fix: happy-dom version (#26764)

Co-authored-by: lyzno1 <yuanyouhuilyz@gmail.com>
Co-authored-by: lyzno1 <92089059+lyzno1@users.noreply.github.com>
This commit is contained in:
crazywoola
2025-10-11 18:59:31 +08:00
committed by GitHub
parent 30c5b47699
commit 0173496a77
4 changed files with 126 additions and 66 deletions

View File

@@ -13,39 +13,60 @@ import { ThemeProvider } from 'next-themes'
import useTheme from '@/hooks/use-theme'
import { useEffect, useState } from 'react'
const DARK_MODE_MEDIA_QUERY = /prefers-color-scheme:\s*dark/i
// Setup browser environment for testing
const setupMockEnvironment = (storedTheme: string | null, systemPrefersDark = false) => {
// Mock localStorage
const mockStorage = {
getItem: jest.fn((key: string) => {
if (key === 'theme') return storedTheme
return null
}),
setItem: jest.fn(),
removeItem: jest.fn(),
if (typeof window === 'undefined')
return
try {
window.localStorage.clear()
}
catch {
// ignore if localStorage has been replaced by a throwing stub
}
// Mock system theme preference
const mockMatchMedia = jest.fn((query: string) => ({
matches: query.includes('dark') && systemPrefersDark,
media: query,
addListener: jest.fn(),
removeListener: jest.fn(),
}))
if (storedTheme === null)
window.localStorage.removeItem('theme')
else
window.localStorage.setItem('theme', storedTheme)
if (typeof window !== 'undefined') {
Object.defineProperty(window, 'localStorage', {
value: mockStorage,
configurable: true,
})
document.documentElement.removeAttribute('data-theme')
Object.defineProperty(window, 'matchMedia', {
value: mockMatchMedia,
configurable: true,
})
const mockMatchMedia: typeof window.matchMedia = (query: string) => {
const listeners = new Set<(event: MediaQueryListEvent) => void>()
const isDarkQuery = DARK_MODE_MEDIA_QUERY.test(query)
const matches = isDarkQuery ? systemPrefersDark : false
const mediaQueryList: MediaQueryList = {
matches,
media: query,
onchange: null,
addListener: (listener: MediaQueryListListener) => {
listeners.add(listener)
},
removeListener: (listener: MediaQueryListListener) => {
listeners.delete(listener)
},
addEventListener: (_event, listener: EventListener) => {
if (typeof listener === 'function')
listeners.add(listener as MediaQueryListListener)
},
removeEventListener: (_event, listener: EventListener) => {
if (typeof listener === 'function')
listeners.delete(listener as MediaQueryListListener)
},
dispatchEvent: (event: Event) => {
listeners.forEach(listener => listener(event as MediaQueryListEvent))
return true
},
}
return mediaQueryList
}
return { mockStorage, mockMatchMedia }
jest.spyOn(window, 'matchMedia').mockImplementation(mockMatchMedia)
}
// Simulate real page component based on Dify's actual theme usage
@@ -94,7 +115,17 @@ const TestThemeProvider = ({ children }: { children: React.ReactNode }) => (
describe('Real Browser Environment Dark Mode Flicker Test', () => {
beforeEach(() => {
jest.restoreAllMocks()
jest.clearAllMocks()
if (typeof window !== 'undefined') {
try {
window.localStorage.clear()
}
catch {
// ignore when localStorage is replaced with an error-throwing stub
}
document.documentElement.removeAttribute('data-theme')
}
})
describe('Page Refresh Scenario Simulation', () => {
@@ -323,35 +354,40 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
describe('Edge Cases and Error Handling', () => {
test('handles localStorage access errors gracefully', async () => {
// Mock localStorage to throw an error
setupMockEnvironment(null)
const mockStorage = {
getItem: jest.fn(() => {
throw new Error('LocalStorage access denied')
}),
setItem: jest.fn(),
removeItem: jest.fn(),
clear: jest.fn(),
}
if (typeof window !== 'undefined') {
Object.defineProperty(window, 'localStorage', {
value: mockStorage,
configurable: true,
})
}
render(
<TestThemeProvider>
<PageComponent />
</TestThemeProvider>,
)
// Should fallback gracefully without crashing
await waitFor(() => {
expect(screen.getByTestId('theme-indicator')).toBeInTheDocument()
Object.defineProperty(window, 'localStorage', {
value: mockStorage,
configurable: true,
})
// Should default to light theme when localStorage fails
expect(screen.getByTestId('visual-appearance')).toHaveTextContent('Appearance: light')
try {
render(
<TestThemeProvider>
<PageComponent />
</TestThemeProvider>,
)
// Should fallback gracefully without crashing
await waitFor(() => {
expect(screen.getByTestId('theme-indicator')).toBeInTheDocument()
})
// Should default to light theme when localStorage fails
expect(screen.getByTestId('visual-appearance')).toHaveTextContent('Appearance: light')
}
finally {
Reflect.deleteProperty(window, 'localStorage')
}
})
test('handles invalid theme values in localStorage', async () => {
@@ -403,6 +439,8 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
setupMockEnvironment('dark')
expect(window.localStorage.getItem('theme')).toBe('dark')
render(
<TestThemeProvider>
<PerformanceTestComponent />