mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
Fix type error 5 (#27139)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@ -160,8 +160,7 @@ describe('Navigation Utilities', () => {
|
|||||||
page: 1,
|
page: 1,
|
||||||
limit: '',
|
limit: '',
|
||||||
keyword: 'test',
|
keyword: 'test',
|
||||||
empty: null,
|
filter: '',
|
||||||
undefined,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(path).toBe('/datasets/123/documents?page=1&keyword=test')
|
expect(path).toBe('/datasets/123/documents?page=1&keyword=test')
|
||||||
|
|||||||
@ -39,28 +39,38 @@ const setupMockEnvironment = (storedTheme: string | null, systemPrefersDark = fa
|
|||||||
const isDarkQuery = DARK_MODE_MEDIA_QUERY.test(query)
|
const isDarkQuery = DARK_MODE_MEDIA_QUERY.test(query)
|
||||||
const matches = isDarkQuery ? systemPrefersDark : false
|
const matches = isDarkQuery ? systemPrefersDark : false
|
||||||
|
|
||||||
|
const handleAddListener = (listener: (event: MediaQueryListEvent) => void) => {
|
||||||
|
listeners.add(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRemoveListener = (listener: (event: MediaQueryListEvent) => void) => {
|
||||||
|
listeners.delete(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAddEventListener = (_event: string, listener: EventListener) => {
|
||||||
|
if (typeof listener === 'function')
|
||||||
|
listeners.add(listener as (event: MediaQueryListEvent) => void)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRemoveEventListener = (_event: string, listener: EventListener) => {
|
||||||
|
if (typeof listener === 'function')
|
||||||
|
listeners.delete(listener as (event: MediaQueryListEvent) => void)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDispatchEvent = (event: Event) => {
|
||||||
|
listeners.forEach(listener => listener(event as MediaQueryListEvent))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
const mediaQueryList: MediaQueryList = {
|
const mediaQueryList: MediaQueryList = {
|
||||||
matches,
|
matches,
|
||||||
media: query,
|
media: query,
|
||||||
onchange: null,
|
onchange: null,
|
||||||
addListener: (listener: MediaQueryListListener) => {
|
addListener: handleAddListener,
|
||||||
listeners.add(listener)
|
removeListener: handleRemoveListener,
|
||||||
},
|
addEventListener: handleAddEventListener,
|
||||||
removeListener: (listener: MediaQueryListListener) => {
|
removeEventListener: handleRemoveEventListener,
|
||||||
listeners.delete(listener)
|
dispatchEvent: handleDispatchEvent,
|
||||||
},
|
|
||||||
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 mediaQueryList
|
||||||
@ -69,6 +79,121 @@ const setupMockEnvironment = (storedTheme: string | null, systemPrefersDark = fa
|
|||||||
jest.spyOn(window, 'matchMedia').mockImplementation(mockMatchMedia)
|
jest.spyOn(window, 'matchMedia').mockImplementation(mockMatchMedia)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to create timing page component
|
||||||
|
const createTimingPageComponent = (
|
||||||
|
timingData: Array<{ phase: string; timestamp: number; styles: { backgroundColor: string; color: string } }>,
|
||||||
|
) => {
|
||||||
|
const recordTiming = (phase: string, styles: { backgroundColor: string; color: string }) => {
|
||||||
|
timingData.push({
|
||||||
|
phase,
|
||||||
|
timestamp: performance.now(),
|
||||||
|
styles,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const TimingPageComponent = () => {
|
||||||
|
const [mounted, setMounted] = useState(false)
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const isDark = mounted ? theme === 'dark' : false
|
||||||
|
|
||||||
|
const currentStyles = {
|
||||||
|
backgroundColor: isDark ? '#1f2937' : '#ffffff',
|
||||||
|
color: isDark ? '#ffffff' : '#000000',
|
||||||
|
}
|
||||||
|
|
||||||
|
recordTiming(mounted ? 'CSR' : 'Initial', currentStyles)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-testid="timing-page"
|
||||||
|
style={currentStyles}
|
||||||
|
>
|
||||||
|
<div data-testid="timing-status">
|
||||||
|
Phase: {mounted ? 'CSR' : 'Initial'} | Theme: {theme} | Visual: {isDark ? 'dark' : 'light'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return TimingPageComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create CSS test component
|
||||||
|
const createCSSTestComponent = (
|
||||||
|
cssStates: Array<{ className: string; timestamp: number }>,
|
||||||
|
) => {
|
||||||
|
const recordCSSState = (className: string) => {
|
||||||
|
cssStates.push({
|
||||||
|
className,
|
||||||
|
timestamp: performance.now(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSSTestComponent = () => {
|
||||||
|
const [mounted, setMounted] = useState(false)
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const isDark = mounted ? theme === 'dark' : false
|
||||||
|
|
||||||
|
const className = `min-h-screen ${isDark ? 'bg-gray-900 text-white' : 'bg-white text-black'}`
|
||||||
|
|
||||||
|
recordCSSState(className)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-testid="css-component"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<div data-testid="css-classes">Classes: {className}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return CSSTestComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create performance test component
|
||||||
|
const createPerformanceTestComponent = (
|
||||||
|
performanceMarks: Array<{ event: string; timestamp: number }>,
|
||||||
|
) => {
|
||||||
|
const recordPerformanceMark = (event: string) => {
|
||||||
|
performanceMarks.push({ event, timestamp: performance.now() })
|
||||||
|
}
|
||||||
|
|
||||||
|
const PerformanceTestComponent = () => {
|
||||||
|
const [mounted, setMounted] = useState(false)
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
|
recordPerformanceMark('component-render')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
recordPerformanceMark('mount-start')
|
||||||
|
setMounted(true)
|
||||||
|
recordPerformanceMark('mount-complete')
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (theme)
|
||||||
|
recordPerformanceMark('theme-available')
|
||||||
|
}, [theme])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-testid="performance-test">
|
||||||
|
Mounted: {mounted.toString()} | Theme: {theme || 'loading'}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return PerformanceTestComponent
|
||||||
|
}
|
||||||
|
|
||||||
// Simulate real page component based on Dify's actual theme usage
|
// Simulate real page component based on Dify's actual theme usage
|
||||||
const PageComponent = () => {
|
const PageComponent = () => {
|
||||||
const [mounted, setMounted] = useState(false)
|
const [mounted, setMounted] = useState(false)
|
||||||
@ -227,39 +352,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
|
|||||||
setupMockEnvironment('dark')
|
setupMockEnvironment('dark')
|
||||||
|
|
||||||
const timingData: Array<{ phase: string; timestamp: number; styles: any }> = []
|
const timingData: Array<{ phase: string; timestamp: number; styles: any }> = []
|
||||||
|
const TimingPageComponent = createTimingPageComponent(timingData)
|
||||||
const TimingPageComponent = () => {
|
|
||||||
const [mounted, setMounted] = useState(false)
|
|
||||||
const { theme } = useTheme()
|
|
||||||
const isDark = mounted ? theme === 'dark' : false
|
|
||||||
|
|
||||||
// Record timing and styles for each render phase
|
|
||||||
const currentStyles = {
|
|
||||||
backgroundColor: isDark ? '#1f2937' : '#ffffff',
|
|
||||||
color: isDark ? '#ffffff' : '#000000',
|
|
||||||
}
|
|
||||||
|
|
||||||
timingData.push({
|
|
||||||
phase: mounted ? 'CSR' : 'Initial',
|
|
||||||
timestamp: performance.now(),
|
|
||||||
styles: currentStyles,
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setMounted(true)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-testid="timing-page"
|
|
||||||
style={currentStyles}
|
|
||||||
>
|
|
||||||
<div data-testid="timing-status">
|
|
||||||
Phase: {mounted ? 'CSR' : 'Initial'} | Theme: {theme} | Visual: {isDark ? 'dark' : 'light'}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<TestThemeProvider>
|
<TestThemeProvider>
|
||||||
@ -295,33 +388,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
|
|||||||
setupMockEnvironment('dark')
|
setupMockEnvironment('dark')
|
||||||
|
|
||||||
const cssStates: Array<{ className: string; timestamp: number }> = []
|
const cssStates: Array<{ className: string; timestamp: number }> = []
|
||||||
|
const CSSTestComponent = createCSSTestComponent(cssStates)
|
||||||
const CSSTestComponent = () => {
|
|
||||||
const [mounted, setMounted] = useState(false)
|
|
||||||
const { theme } = useTheme()
|
|
||||||
const isDark = mounted ? theme === 'dark' : false
|
|
||||||
|
|
||||||
// Simulate Tailwind CSS class application
|
|
||||||
const className = `min-h-screen ${isDark ? 'bg-gray-900 text-white' : 'bg-white text-black'}`
|
|
||||||
|
|
||||||
cssStates.push({
|
|
||||||
className,
|
|
||||||
timestamp: performance.now(),
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setMounted(true)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-testid="css-component"
|
|
||||||
className={className}
|
|
||||||
>
|
|
||||||
<div data-testid="css-classes">Classes: {className}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<TestThemeProvider>
|
<TestThemeProvider>
|
||||||
@ -413,34 +480,12 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
|
|||||||
test('verifies ThemeProvider position fix reduces initialization delay', async () => {
|
test('verifies ThemeProvider position fix reduces initialization delay', async () => {
|
||||||
const performanceMarks: Array<{ event: string; timestamp: number }> = []
|
const performanceMarks: Array<{ event: string; timestamp: number }> = []
|
||||||
|
|
||||||
const PerformanceTestComponent = () => {
|
|
||||||
const [mounted, setMounted] = useState(false)
|
|
||||||
const { theme } = useTheme()
|
|
||||||
|
|
||||||
performanceMarks.push({ event: 'component-render', timestamp: performance.now() })
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
performanceMarks.push({ event: 'mount-start', timestamp: performance.now() })
|
|
||||||
setMounted(true)
|
|
||||||
performanceMarks.push({ event: 'mount-complete', timestamp: performance.now() })
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (theme)
|
|
||||||
performanceMarks.push({ event: 'theme-available', timestamp: performance.now() })
|
|
||||||
}, [theme])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div data-testid="performance-test">
|
|
||||||
Mounted: {mounted.toString()} | Theme: {theme || 'loading'}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
setupMockEnvironment('dark')
|
setupMockEnvironment('dark')
|
||||||
|
|
||||||
expect(window.localStorage.getItem('theme')).toBe('dark')
|
expect(window.localStorage.getItem('theme')).toBe('dark')
|
||||||
|
|
||||||
|
const PerformanceTestComponent = createPerformanceTestComponent(performanceMarks)
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<TestThemeProvider>
|
<TestThemeProvider>
|
||||||
<PerformanceTestComponent />
|
<PerformanceTestComponent />
|
||||||
|
|||||||
@ -70,14 +70,18 @@ describe('Unified Tags Editing - Pure Logic Tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('Fallback Logic (from layout-main.tsx)', () => {
|
describe('Fallback Logic (from layout-main.tsx)', () => {
|
||||||
|
type Tag = { id: string; name: string }
|
||||||
|
type AppDetail = { tags: Tag[] }
|
||||||
|
type FallbackResult = { tags?: Tag[] } | null
|
||||||
|
// no-op
|
||||||
it('should trigger fallback when tags are missing or empty', () => {
|
it('should trigger fallback when tags are missing or empty', () => {
|
||||||
const appDetailWithoutTags = { tags: [] }
|
const appDetailWithoutTags: AppDetail = { tags: [] }
|
||||||
const appDetailWithTags = { tags: [{ id: 'tag1' }] }
|
const appDetailWithTags: AppDetail = { tags: [{ id: 'tag1', name: 't' }] }
|
||||||
const appDetailWithUndefinedTags = { tags: undefined as any }
|
const appDetailWithUndefinedTags: { tags: Tag[] | undefined } = { tags: undefined }
|
||||||
|
|
||||||
// This simulates the condition in layout-main.tsx
|
// This simulates the condition in layout-main.tsx
|
||||||
const shouldFallback1 = !appDetailWithoutTags.tags || appDetailWithoutTags.tags.length === 0
|
const shouldFallback1 = appDetailWithoutTags.tags.length === 0
|
||||||
const shouldFallback2 = !appDetailWithTags.tags || appDetailWithTags.tags.length === 0
|
const shouldFallback2 = appDetailWithTags.tags.length === 0
|
||||||
const shouldFallback3 = !appDetailWithUndefinedTags.tags || appDetailWithUndefinedTags.tags.length === 0
|
const shouldFallback3 = !appDetailWithUndefinedTags.tags || appDetailWithUndefinedTags.tags.length === 0
|
||||||
|
|
||||||
expect(shouldFallback1).toBe(true) // Empty array should trigger fallback
|
expect(shouldFallback1).toBe(true) // Empty array should trigger fallback
|
||||||
@ -86,24 +90,26 @@ describe('Unified Tags Editing - Pure Logic Tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should preserve tags when fallback succeeds', () => {
|
it('should preserve tags when fallback succeeds', () => {
|
||||||
const originalAppDetail = { tags: [] as any[] }
|
const originalAppDetail: AppDetail = { tags: [] }
|
||||||
const fallbackResult = { tags: [{ id: 'tag1', name: 'fallback-tag' }] }
|
const fallbackResult: { tags?: Tag[] } = { tags: [{ id: 'tag1', name: 'fallback-tag' }] }
|
||||||
|
|
||||||
// This simulates the successful fallback in layout-main.tsx
|
// This simulates the successful fallback in layout-main.tsx
|
||||||
if (fallbackResult?.tags)
|
const tags = fallbackResult.tags
|
||||||
originalAppDetail.tags = fallbackResult.tags
|
if (tags)
|
||||||
|
originalAppDetail.tags = tags
|
||||||
|
|
||||||
expect(originalAppDetail.tags).toEqual(fallbackResult.tags)
|
expect(originalAppDetail.tags).toEqual(fallbackResult.tags)
|
||||||
expect(originalAppDetail.tags.length).toBe(1)
|
expect(originalAppDetail.tags.length).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should continue with empty tags when fallback fails', () => {
|
it('should continue with empty tags when fallback fails', () => {
|
||||||
const originalAppDetail: { tags: any[] } = { tags: [] }
|
const originalAppDetail: AppDetail = { tags: [] }
|
||||||
const fallbackResult: { tags?: any[] } | null = null
|
const fallbackResult = null as FallbackResult
|
||||||
|
|
||||||
// This simulates fallback failure in layout-main.tsx
|
// This simulates fallback failure in layout-main.tsx
|
||||||
if (fallbackResult?.tags)
|
const tags: Tag[] | undefined = fallbackResult && 'tags' in fallbackResult ? fallbackResult.tags : undefined
|
||||||
originalAppDetail.tags = fallbackResult.tags
|
if (tags)
|
||||||
|
originalAppDetail.tags = tags
|
||||||
|
|
||||||
expect(originalAppDetail.tags).toEqual([])
|
expect(originalAppDetail.tags).toEqual([])
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user