129 行
5.9 KiB
TypeScript
129 行
5.9 KiB
TypeScript
import { act, fireEvent, screen, waitFor } from '@testing-library/react'
|
|
import { Route, Routes, useLocation, useNavigate } from 'react-router-dom'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import HelpDetailPage from '@/pages/help/HelpDetailPage'
|
|
import { helpFixture } from '@/test/helpFixtures'
|
|
import { renderWithProviders } from '@/test/render'
|
|
|
|
import type { ReactElement } from 'react'
|
|
|
|
vi.mock ('@/lib/help', async () => (await import ('@/test/helpFixtures')).helpFixture.api)
|
|
|
|
|
|
const Navigation = () => {
|
|
const navigate = useNavigate ()
|
|
const location = useLocation ()
|
|
return (
|
|
<>
|
|
<output data-testid="location">{location.pathname}{location.search}</output>
|
|
<button onClick={() => navigate ('/ja/help/設定%20手順')}>switch article</button>
|
|
<button onClick={() => navigate ('/ja/help/guide?edition=2026-10-02.3')}>
|
|
switch edition
|
|
</button>
|
|
</>)
|
|
}
|
|
|
|
|
|
const renderDetail = (route = '/ja/help/guide') => renderWithProviders (
|
|
<>
|
|
<Navigation/>
|
|
<Routes>
|
|
<Route path="/ja/help/:identifier" element={<HelpDetailPage/>}/>
|
|
</Routes>
|
|
</>, { route })
|
|
|
|
|
|
describe ('Help details', () => {
|
|
beforeEach (() => helpFixture.reset ())
|
|
|
|
it.each (['2026-10-02', '2026-10-02.2', '2026-10-02.3', '2026-10-02.10']) (
|
|
'displays a date without inventing a time for edition %s', async edition => {
|
|
const { container } = renderDetail (`/ja/help/guide?edition=${ edition }`)
|
|
await screen.findByRole ('heading')
|
|
expect (container).toHaveTextContent ('10月2日')
|
|
expect (container).not.toHaveTextContent ('Invalid Date')
|
|
expect (container.textContent).not.toMatch (/\d{1,2}:\d{2}/)
|
|
const revision = edition.split ('.')[1]
|
|
if (revision != null)
|
|
expect (container).toHaveTextContent (` .${ revision }`)
|
|
expect (helpFixture.modules[`../../help/guide/editions/ja/${ edition }.mdx`])
|
|
.toHaveBeenCalled ()
|
|
})
|
|
|
|
it ('displays current_edition H1 and body even when newer files exist', async () => {
|
|
renderDetail ()
|
|
expect (await screen.findByRole ('heading', { name: 'ホーム第十版' }))
|
|
.toBeInTheDocument ()
|
|
expect (screen.getByText ('タグを検索する 第十版本文')).toBeInTheDocument ()
|
|
expect (screen.queryByText ('未公開限定本文')).not.toBeInTheDocument ()
|
|
await waitFor (() => expect (document.title).toContain ('ホーム第十版 ヘルプ'))
|
|
})
|
|
|
|
it ('uses the historical H1 in both page and document titles', async () => {
|
|
renderDetail ('/ja/help/guide?edition=2026-10-01')
|
|
expect (await screen.findByRole ('heading', { name: '旧ホーム' })).toBeInTheDocument ()
|
|
expect (screen.getByText ('過去限定本文')).toBeInTheDocument ()
|
|
await waitFor (() => expect (document.title).toContain ('旧ホーム ヘルプ'))
|
|
expect (screen.getByText ('(最古)')).toBeInTheDocument ()
|
|
expect (screen.getByRole ('link', { name: '新 >' }))
|
|
.toHaveAttribute ('href', '/ja/help/guide?edition=2026-10-02')
|
|
})
|
|
|
|
it ('navigates .2, .3 and .10 in numeric edition order', async () => {
|
|
renderDetail ('/ja/help/guide?edition=2026-10-02.3')
|
|
await screen.findByRole ('heading', { name: 'ホーム第三版' })
|
|
expect (screen.getByRole ('link', { name: '< 古' }))
|
|
.toHaveAttribute ('href', '/ja/help/guide?edition=2026-10-02.2')
|
|
const next = screen.getByRole ('link', { name: '新 >' })
|
|
expect (next).toHaveAttribute ('href', '/ja/help/guide?edition=2026-10-02.10')
|
|
fireEvent.click (next)
|
|
await screen.findByRole ('heading', { name: 'ホーム第十版' })
|
|
expect (screen.getByText ('(最新)')).toBeInTheDocument ()
|
|
expect (screen.queryByText ('第三版本文')).not.toBeInTheDocument ()
|
|
})
|
|
|
|
it.each (['/ja/help/missing', '/ja/help/guide?edition=missing',
|
|
'/ja/help/guide?edition=2026-02-30', '/ja/help/guide?edition=2026-10-02.2.extra',
|
|
'/ja/help/guide?edition=2026-10-02.11']) (
|
|
'shows NotFound without redirecting %s', async route => {
|
|
renderDetail (route)
|
|
expect (await screen.findByText ('404')).toBeInTheDocument ()
|
|
expect (screen.getByTestId ('location')).toHaveTextContent (route)
|
|
})
|
|
|
|
it ('hides the old body and title immediately while another article loads', async () => {
|
|
let finish!: (value: { default: () => ReactElement }) => void
|
|
const pending = new Promise<{ default: () => ReactElement }> (resolve => {
|
|
finish = resolve
|
|
})
|
|
helpFixture.modules['../../help/設定 手順/editions/ja/2026-10-03.mdx']
|
|
.mockReturnValueOnce (pending)
|
|
renderDetail ()
|
|
await screen.findByRole ('heading', { name: 'ホーム第十版' })
|
|
fireEvent.click (screen.getByRole ('button', { name: 'switch article' }))
|
|
expect (screen.queryByText ('タグを検索する 第十版本文')).not.toBeInTheDocument ()
|
|
await waitFor (() => expect (document.title).not.toContain ('ホーム'))
|
|
await act (async () => finish ({ default: () => <h1>設定</h1> }))
|
|
await screen.findByRole ('heading', { name: '設定' })
|
|
await waitFor (() => expect (document.title).toContain ('設定 ヘルプ'))
|
|
})
|
|
|
|
it ('ignores a late response for the previous edition', async () => {
|
|
let finish!: (value: { default: () => ReactElement }) => void
|
|
const pending = new Promise<{ default: () => ReactElement }> (resolve => {
|
|
finish = resolve
|
|
})
|
|
const loader = helpFixture.modules['../../help/guide/editions/ja/2026-10-02.10.mdx']
|
|
loader.mockReturnValueOnce (pending)
|
|
renderDetail ()
|
|
await waitFor (() => expect (loader).toHaveBeenCalled ())
|
|
fireEvent.click (screen.getByRole ('button', { name: 'switch edition' }))
|
|
await screen.findByRole ('heading', { name: 'ホーム第三版' })
|
|
await act (async () => finish ({ default: () => <h1>遅延した旧本文</h1> }))
|
|
expect (screen.queryByText ('遅延した旧本文')).not.toBeInTheDocument ()
|
|
expect (screen.getByRole ('heading', { name: 'ホーム第三版' })).toBeInTheDocument ()
|
|
await waitFor (() => expect (document.title).toContain ('ホーム第三版 ヘルプ'))
|
|
})
|
|
})
|