diff --git a/web/app/(commonLayout)/marketplace/__tests__/layout.spec.tsx b/web/app/(commonLayout)/marketplace/__tests__/layout.spec.tsx new file mode 100644 index 00000000000..1e18f9f6b3b --- /dev/null +++ b/web/app/(commonLayout)/marketplace/__tests__/layout.spec.tsx @@ -0,0 +1,33 @@ +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { render, screen } from '@testing-library/react' +import { describe, expect, it, vi } from 'vitest' + +vi.mock('../document-title', () => ({ + default: () => marketplace document title, +})) + +describe('marketplace route layout', () => { + it('stays a server module so Flight can stream the async marketplace page', () => { + const source = readFileSync( + resolve(dirname(fileURLToPath(import.meta.url)), '../layout.tsx'), + 'utf8', + ) + + expect(source).not.toMatch(/^['"]use client['"]/) + }) + + it('renders marketplace children and the document title island', async () => { + const { default: MarketplaceLayout } = await import('../layout') + + render( + +

marketplace page

+
, + ) + + expect(screen.getByText('marketplace document title')).toBeInTheDocument() + expect(screen.getByText('marketplace page')).toBeInTheDocument() + }) +}) diff --git a/web/app/(commonLayout)/marketplace/document-title.tsx b/web/app/(commonLayout)/marketplace/document-title.tsx new file mode 100644 index 00000000000..d053ab9cb3c --- /dev/null +++ b/web/app/(commonLayout)/marketplace/document-title.tsx @@ -0,0 +1,12 @@ +'use client' + +import { useTranslation } from 'react-i18next' +import useDocumentTitle from '@/hooks/use-document-title' + +const MarketplaceDocumentTitle = () => { + const { t } = useTranslation() + useDocumentTitle(t(($) => $['mainNav.marketplace'], { ns: 'common' })) + return null +} + +export default MarketplaceDocumentTitle diff --git a/web/app/(commonLayout)/marketplace/layout.tsx b/web/app/(commonLayout)/marketplace/layout.tsx index 5a40a6a3a62..7dbbf002cdd 100644 --- a/web/app/(commonLayout)/marketplace/layout.tsx +++ b/web/app/(commonLayout)/marketplace/layout.tsx @@ -1,12 +1,14 @@ -'use client' - import type { PropsWithChildren } from 'react' -import { useTranslation } from 'react-i18next' -import useDocumentTitle from '@/hooks/use-document-title' +import MarketplaceDocumentTitle from './document-title' +// This route layout must stay a Server Component. A client layout wrapping the +// async marketplace page makes Flight double-resolve streamed children +// (`reason.enqueueModel`) when opening /marketplace from cloud.dify.dev. export default function MarketplaceLayout({ children }: PropsWithChildren) { - const { t } = useTranslation() - useDocumentTitle(t(($) => $['mainNav.marketplace'], { ns: 'common' })) - - return children + return ( + <> + + {children} + + ) }