mirror of https://github.com/langgenius/dify.git
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:
parent
30c5b47699
commit
0173496a77
|
|
@ -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 />
|
||||
|
|
|
|||
|
|
@ -160,7 +160,11 @@ const config: Config = {
|
|||
testEnvironment: '@happy-dom/jest-environment',
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
testEnvironmentOptions: {
|
||||
// Match happy-dom's default to ensure Node.js environment resolution
|
||||
// This prevents ESM packages like uuid from using browser exports
|
||||
customExportConditions: ['node', 'node-addons'],
|
||||
},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
|
@ -189,10 +193,10 @@ const config: Config = {
|
|||
// transform: undefined,
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
// transformIgnorePatterns: [
|
||||
// "/node_modules/",
|
||||
// "\\.pnp\\.[^\\/]+$"
|
||||
// ],
|
||||
// For pnpm: allow transforming uuid ESM package
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!(.pnpm|uuid))',
|
||||
],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@
|
|||
"@babel/core": "^7.28.3",
|
||||
"@chromatic-com/storybook": "^3.1.0",
|
||||
"@eslint-react/eslint-plugin": "^1.15.0",
|
||||
"@happy-dom/jest-environment": "^17.4.4",
|
||||
"@happy-dom/jest-environment": "^20.0.0",
|
||||
"@mdx-js/loader": "^3.1.0",
|
||||
"@mdx-js/react": "^3.1.0",
|
||||
"@next/bundle-analyzer": "15.5.4",
|
||||
|
|
|
|||
|
|
@ -345,8 +345,8 @@ importers:
|
|||
specifier: ^1.15.0
|
||||
version: 1.52.3(eslint@9.35.0(jiti@2.6.0))(ts-api-utils@2.1.0(typescript@5.8.3))(typescript@5.8.3)
|
||||
'@happy-dom/jest-environment':
|
||||
specifier: ^17.4.4
|
||||
version: 17.6.3
|
||||
specifier: ^20.0.0
|
||||
version: 20.0.0(@jest/environment@29.7.0)(@jest/fake-timers@29.7.0)(@jest/types@29.6.3)(jest-mock@29.7.0)(jest-util@29.7.0)
|
||||
'@mdx-js/loader':
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0(acorn@8.15.0)(webpack@5.100.2(esbuild@0.25.0)(uglify-js@3.19.3))
|
||||
|
|
@ -1644,9 +1644,15 @@ packages:
|
|||
'@formatjs/intl-localematcher@0.5.10':
|
||||
resolution: {integrity: sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==}
|
||||
|
||||
'@happy-dom/jest-environment@17.6.3':
|
||||
resolution: {integrity: sha512-HXuHKvpHLo9/GQ/yKMmKFyS1AYL2t9pL67+GfpYZfOAb29qD80EMozi50zRZk82KmNRBcA2A0/ErjpOwUxJrNg==}
|
||||
'@happy-dom/jest-environment@20.0.0':
|
||||
resolution: {integrity: sha512-dUyMDNJzPDFopSDyzKdbeYs8z9B4jLj9kXnru8TjYdGeLsQKf+6r0lq/9T2XVcu04QFxXMykt64A+KjsaJTaNA==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
peerDependencies:
|
||||
'@jest/environment': '>=25.0.0'
|
||||
'@jest/fake-timers': '>=25.0.0'
|
||||
'@jest/types': '>=25.0.0'
|
||||
jest-mock: '>=25.0.0'
|
||||
jest-util: '>=25.0.0'
|
||||
|
||||
'@headlessui/react@2.2.1':
|
||||
resolution: {integrity: sha512-daiUqVLae8CKVjEVT19P/izW0aGK0GNhMSAeMlrDebKmoVZHcRRwbxzgtnEadUVDXyBsWo9/UH4KHeniO+0tMg==}
|
||||
|
|
@ -3416,6 +3422,9 @@ packages:
|
|||
'@types/node@18.15.0':
|
||||
resolution: {integrity: sha512-z6nr0TTEOBGkzLGmbypWOGnpSpSIBorEhC4L+4HeQ2iezKCi4f77kyslRwvHeNitymGQ+oFyIWGP96l/DPSV9w==}
|
||||
|
||||
'@types/node@20.19.20':
|
||||
resolution: {integrity: sha512-2Q7WS25j4pS1cS8yw3d6buNCVJukOTeQ39bAnwR6sOJbaxvyCGebzTMypDFN82CxBLnl+lSWVdCCWbRY6y9yZQ==}
|
||||
|
||||
'@types/papaparse@5.3.16':
|
||||
resolution: {integrity: sha512-T3VuKMC2H0lgsjI9buTB3uuKj3EMD2eap1MOuEQuBQ44EnDx/IkGhU6EwiTf9zG3za4SKlmwKAImdDKdNnCsXg==}
|
||||
|
||||
|
|
@ -3475,6 +3484,9 @@ packages:
|
|||
'@types/uuid@9.0.8':
|
||||
resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==}
|
||||
|
||||
'@types/whatwg-mimetype@3.0.2':
|
||||
resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==}
|
||||
|
||||
'@types/yargs-parser@21.0.3':
|
||||
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
|
||||
|
||||
|
|
@ -5542,8 +5554,8 @@ packages:
|
|||
hachure-fill@0.5.2:
|
||||
resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==}
|
||||
|
||||
happy-dom@17.6.3:
|
||||
resolution: {integrity: sha512-UVIHeVhxmxedbWPCfgS55Jg2rDfwf2BCKeylcPSqazLz5w3Kri7Q4xdBJubsr/+VUzFLh0VjIvh13RaDA2/Xug==}
|
||||
happy-dom@20.0.0:
|
||||
resolution: {integrity: sha512-GkWnwIFxVGCf2raNrxImLo397RdGhLapj5cT3R2PT7FwL62Ze1DROhzmYW7+J3p9105DYMVenEejEbnq5wA37w==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
has-flag@4.0.0:
|
||||
|
|
@ -8246,6 +8258,9 @@ packages:
|
|||
engines: {node: '>=0.8.0'}
|
||||
hasBin: true
|
||||
|
||||
undici-types@6.21.0:
|
||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||
|
||||
unicode-canonical-property-names-ecmascript@2.0.1:
|
||||
resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
@ -8472,10 +8487,6 @@ packages:
|
|||
webidl-conversions@4.0.2:
|
||||
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
|
||||
|
||||
webidl-conversions@7.0.0:
|
||||
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
webpack-bundle-analyzer@4.10.1:
|
||||
resolution: {integrity: sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
|
|
@ -10105,12 +10116,12 @@ snapshots:
|
|||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@happy-dom/jest-environment@17.6.3':
|
||||
'@happy-dom/jest-environment@20.0.0(@jest/environment@29.7.0)(@jest/fake-timers@29.7.0)(@jest/types@29.6.3)(jest-mock@29.7.0)(jest-util@29.7.0)':
|
||||
dependencies:
|
||||
'@jest/environment': 29.7.0
|
||||
'@jest/fake-timers': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
happy-dom: 17.6.3
|
||||
happy-dom: 20.0.0
|
||||
jest-mock: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
|
||||
|
|
@ -12201,6 +12212,10 @@ snapshots:
|
|||
|
||||
'@types/node@18.15.0': {}
|
||||
|
||||
'@types/node@20.19.20':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/papaparse@5.3.16':
|
||||
dependencies:
|
||||
'@types/node': 18.15.0
|
||||
|
|
@ -12255,6 +12270,8 @@ snapshots:
|
|||
|
||||
'@types/uuid@9.0.8': {}
|
||||
|
||||
'@types/whatwg-mimetype@3.0.2': {}
|
||||
|
||||
'@types/yargs-parser@21.0.3': {}
|
||||
|
||||
'@types/yargs@17.0.33':
|
||||
|
|
@ -14709,9 +14726,10 @@ snapshots:
|
|||
|
||||
hachure-fill@0.5.2: {}
|
||||
|
||||
happy-dom@17.6.3:
|
||||
happy-dom@20.0.0:
|
||||
dependencies:
|
||||
webidl-conversions: 7.0.0
|
||||
'@types/node': 20.19.20
|
||||
'@types/whatwg-mimetype': 3.0.2
|
||||
whatwg-mimetype: 3.0.0
|
||||
|
||||
has-flag@4.0.0: {}
|
||||
|
|
@ -18125,6 +18143,8 @@ snapshots:
|
|||
|
||||
uglify-js@3.19.3: {}
|
||||
|
||||
undici-types@6.21.0: {}
|
||||
|
||||
unicode-canonical-property-names-ecmascript@2.0.1: {}
|
||||
|
||||
unicode-match-property-ecmascript@2.0.0:
|
||||
|
|
@ -18351,8 +18371,6 @@ snapshots:
|
|||
|
||||
webidl-conversions@4.0.2: {}
|
||||
|
||||
webidl-conversions@7.0.0: {}
|
||||
|
||||
webpack-bundle-analyzer@4.10.1:
|
||||
dependencies:
|
||||
'@discoveryjs/json-ext': 0.5.7
|
||||
|
|
|
|||
Loading…
Reference in New Issue