mirror of
https://github.com/langgenius/dify.git
synced 2026-09-03 15:27:49 +08:00
Gate iframe install on plugin.install, keep the 15s deadline off package downloads, abort cancelled collection fetches, bound /templates prefetch, and stop caching partial template-collection failures. Revert unrelated Main Nav order and icon styling, remove the unused performance E2E bundle, and expose a standalone Marketplace entry instead of Knip path aliases.
134 lines
4.0 KiB
TypeScript
134 lines
4.0 KiB
TypeScript
import { QueryClient } from '@tanstack/react-query'
|
|
|
|
let queryClient: QueryClient
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
getSystemFeatures: vi.fn(),
|
|
headers: vi.fn(async () => new Headers()),
|
|
}))
|
|
|
|
vi.mock('@/features/system-features/server', () => ({
|
|
getSystemFeaturesQueryClient: () => queryClient,
|
|
prefetchSystemFeatures: async () => {
|
|
const queryOptions = {
|
|
queryKey: ['console', 'system-features'],
|
|
queryFn: mocks.getSystemFeatures,
|
|
retry: false,
|
|
}
|
|
if (!queryClient.getQueryState(queryOptions.queryKey))
|
|
await queryClient.prefetchQuery(queryOptions)
|
|
return queryClient.getQueryData(queryOptions.queryKey)
|
|
},
|
|
}))
|
|
|
|
vi.mock('@/env', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('@/env')>()
|
|
|
|
return {
|
|
...actual,
|
|
getDatasetMap: () => ({}),
|
|
}
|
|
})
|
|
|
|
vi.mock('@/i18n-config/server', () => ({
|
|
getLocaleOnServer: async () => 'en-US',
|
|
}))
|
|
|
|
vi.mock('@/next/headers', () => ({
|
|
headers: mocks.headers,
|
|
}))
|
|
|
|
describe('Root layout System Features bootstrap', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
mocks.headers.mockResolvedValue(new Headers())
|
|
queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
|
|
})
|
|
|
|
it('caches the resolved System Features for dehydration', async () => {
|
|
mocks.getSystemFeatures.mockResolvedValue({
|
|
branding: {
|
|
application_title: 'Acme AI',
|
|
enabled: true,
|
|
},
|
|
deployment_edition: 'CLOUD',
|
|
})
|
|
const { default: RootLayout, generateMetadata } = await import('../layout')
|
|
|
|
await expect(RootLayout({ children: <div>App</div> })).resolves.toBeDefined()
|
|
await expect(generateMetadata()).resolves.toMatchObject({
|
|
title: {
|
|
default: 'Acme AI',
|
|
template: '%s - Acme AI',
|
|
},
|
|
})
|
|
|
|
expect(mocks.getSystemFeatures).toHaveBeenCalledTimes(1)
|
|
expect(queryClient.getQueryData(['console', 'system-features'])).toEqual({
|
|
branding: {
|
|
application_title: 'Acme AI',
|
|
enabled: true,
|
|
},
|
|
deployment_edition: 'CLOUD',
|
|
})
|
|
})
|
|
|
|
it('points the icons at the branding favicon when one is configured', async () => {
|
|
mocks.getSystemFeatures.mockResolvedValue({
|
|
branding: {
|
|
application_title: 'Acme AI',
|
|
enabled: true,
|
|
favicon: 'https://cdn.example.com/brand.ico',
|
|
},
|
|
deployment_edition: 'CLOUD',
|
|
})
|
|
const { generateMetadata } = await import('../layout')
|
|
|
|
await expect(generateMetadata()).resolves.toMatchObject({
|
|
icons: {
|
|
icon: 'https://cdn.example.com/brand.ico',
|
|
apple: 'https://cdn.example.com/brand.ico',
|
|
},
|
|
})
|
|
})
|
|
|
|
it('falls back to the static favicon without branding', async () => {
|
|
mocks.getSystemFeatures.mockResolvedValue({
|
|
branding: { enabled: false },
|
|
deployment_edition: 'CLOUD',
|
|
})
|
|
const { generateMetadata } = await import('../layout')
|
|
|
|
await expect(generateMetadata()).resolves.toMatchObject({
|
|
icons: { icon: '/favicon.ico' },
|
|
})
|
|
})
|
|
|
|
it('falls back to the static favicon when branding is enabled without one', async () => {
|
|
mocks.getSystemFeatures.mockResolvedValue({
|
|
branding: { application_title: 'Acme AI', enabled: true, favicon: '' },
|
|
deployment_edition: 'CLOUD',
|
|
})
|
|
const { generateMetadata } = await import('../layout')
|
|
|
|
await expect(generateMetadata()).resolves.toMatchObject({
|
|
icons: { icon: '/favicon.ico' },
|
|
})
|
|
})
|
|
|
|
it('renders the client recovery path when the server prefetch fails', async () => {
|
|
mocks.getSystemFeatures.mockRejectedValue(new Error('system features unavailable'))
|
|
const { default: RootLayout, generateMetadata } = await import('../layout')
|
|
|
|
await expect(RootLayout({ children: <div>App</div> })).resolves.toBeDefined()
|
|
await expect(generateMetadata()).resolves.toMatchObject({
|
|
title: {
|
|
default: 'Dify',
|
|
template: '%s - Dify',
|
|
},
|
|
})
|
|
|
|
expect(queryClient.getQueryData(['console', 'system-features'])).toBeUndefined()
|
|
})
|
|
})
|