import { act, fireEvent, render, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { AppModeEnum, Theme } from '@/types/app' import Doc from '../doc' // The vitest mdx-stub plugin makes .mdx files parseable; these mocks replace vi.mock('../template/template.en.mdx', () => ({ default: (_props: Record) =>
, })) vi.mock('../template/template.zh.mdx', () => ({ default: (_props: Record) =>
, })) vi.mock('../template/template.ja.mdx', () => ({ default: (_props: Record) =>
, })) vi.mock('../template/template_chat.en.mdx', () => ({ default: (_props: Record) =>
, })) vi.mock('../template/template_chat.zh.mdx', () => ({ default: (_props: Record) =>
, })) vi.mock('../template/template_chat.ja.mdx', () => ({ default: (_props: Record) =>
, })) vi.mock('../template/template_advanced_chat.en.mdx', () => ({ default: (_props: Record) =>
, })) vi.mock('../template/template_advanced_chat.zh.mdx', () => ({ default: (_props: Record) =>
, })) vi.mock('../template/template_advanced_chat.ja.mdx', () => ({ default: (_props: Record) =>
, })) vi.mock('../template/template_workflow.en.mdx', () => ({ default: (_props: Record) =>
, })) vi.mock('../template/template_workflow.zh.mdx', () => ({ default: (_props: Record) =>
, })) vi.mock('../template/template_workflow.ja.mdx', () => ({ default: (_props: Record) =>
, })) const mockLocale = vi.fn().mockReturnValue('en-US') vi.mock('@/context/i18n', () => ({ useLocale: () => mockLocale(), })) const mockTheme = vi.fn().mockReturnValue(Theme.light) vi.mock('@/hooks/use-theme', () => ({ default: () => ({ theme: mockTheme() }), })) vi.mock('@/i18n-config/language', () => ({ LanguagesSupported: ['en-US', 'zh-Hans', 'zh-Hant', 'pt-BR', 'es-ES', 'fr-FR', 'de-DE', 'ja-JP'], })) describe('Doc', () => { const makeAppDetail = (mode: AppModeEnum, variables: Array<{ key: string, name: string }> = []) => ({ mode, model_config: { configs: { prompt_variables: variables, }, }, }) beforeEach(() => { vi.clearAllMocks() mockLocale.mockReturnValue('en-US') mockTheme.mockReturnValue(Theme.light) Object.defineProperty(window, 'matchMedia', { writable: true, value: vi.fn().mockReturnValue({ matches: false }), }) }) describe('template selection by app mode', () => { it.each([ [AppModeEnum.CHAT, 'template-chat-en'], [AppModeEnum.AGENT_CHAT, 'template-chat-en'], [AppModeEnum.ADVANCED_CHAT, 'template-advanced-chat-en'], [AppModeEnum.WORKFLOW, 'template-workflow-en'], [AppModeEnum.COMPLETION, 'template-completion-en'], ])('should render correct EN template for mode %s', (mode, testId) => { render() expect(screen.getByTestId(testId)).toBeInTheDocument() }) }) describe('template selection by locale', () => { it('should render ZH template when locale is zh-Hans', () => { mockLocale.mockReturnValue('zh-Hans') render() expect(screen.getByTestId('template-chat-zh')).toBeInTheDocument() }) it('should render JA template when locale is ja-JP', () => { mockLocale.mockReturnValue('ja-JP') render() expect(screen.getByTestId('template-chat-ja')).toBeInTheDocument() }) it('should fall back to EN template for unsupported locales', () => { mockLocale.mockReturnValue('fr-FR') render() expect(screen.getByTestId('template-completion-en')).toBeInTheDocument() }) it('should render ZH advanced-chat template', () => { mockLocale.mockReturnValue('zh-Hans') render() expect(screen.getByTestId('template-advanced-chat-zh')).toBeInTheDocument() }) it('should render JA workflow template', () => { mockLocale.mockReturnValue('ja-JP') render() expect(screen.getByTestId('template-workflow-ja')).toBeInTheDocument() }) }) describe('null/undefined appDetail', () => { it('should render nothing when appDetail has no mode', () => { render() expect(screen.queryByTestId('template-completion-en')).not.toBeInTheDocument() expect(screen.queryByTestId('template-chat-en')).not.toBeInTheDocument() }) it('should render nothing when appDetail is null', () => { render() expect(screen.queryByTestId('template-completion-en')).not.toBeInTheDocument() }) }) describe('TOC toggle', () => { it('should show collapsed TOC button by default on small screens', () => { render() expect(screen.getByLabelText('Open table of contents')).toBeInTheDocument() }) it('should show expanded TOC on wide screens', () => { Object.defineProperty(window, 'matchMedia', { writable: true, value: vi.fn().mockReturnValue({ matches: true }), }) render() expect(screen.getByText('appApi.develop.toc')).toBeInTheDocument() expect(screen.getByLabelText('Close')).toBeInTheDocument() }) it('should expand TOC when toggle button is clicked', async () => { render() const toggleBtn = screen.getByLabelText('Open table of contents') await act(async () => { fireEvent.click(toggleBtn) }) expect(screen.getByText('appApi.develop.toc')).toBeInTheDocument() }) it('should collapse TOC when close button is clicked', async () => { Object.defineProperty(window, 'matchMedia', { writable: true, value: vi.fn().mockReturnValue({ matches: true }), }) render() const closeBtn = screen.getByLabelText('Close') await act(async () => { fireEvent.click(closeBtn) }) expect(screen.getByLabelText('Open table of contents')).toBeInTheDocument() }) }) describe('dark theme', () => { it('should apply prose-invert class in dark mode', () => { mockTheme.mockReturnValue(Theme.dark) const { container } = render() const article = container.querySelector('article') expect(article?.className).toContain('prose-invert') }) it('should not apply prose-invert class in light mode', () => { mockTheme.mockReturnValue(Theme.light) const { container } = render() const article = container.querySelector('article') expect(article?.className).not.toContain('prose-invert') }) }) describe('article structure', () => { it('should render article with prose classes', () => { const { container } = render() const article = container.querySelector('article') expect(article).toBeInTheDocument() expect(article?.className).toContain('prose') }) it('should render flex layout wrapper', () => { const { container } = render() expect(container.querySelector('.flex')).toBeInTheDocument() }) }) })