このコミットが含まれているのは:
2026-10-31 20:44:28 +09:00
コミット 14853a8c5f
4個のファイルの変更、91行の追加、15行の削除
+65 -1
ファイルの表示
@@ -1,8 +1,16 @@
import { act, fireEvent, screen, waitFor } from '@testing-library/react'
import { useLocation } from 'react-router-dom'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { hydrateRoot } from 'react-dom/client'
import { renderToString } from 'react-dom/server'
import { HelmetProvider } from 'react-helmet-async'
import { MemoryRouter, useLocation } from 'react-router-dom'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import CountsResourcePage from '@/pages/CountsResourcePage'
import DialogueProvider from '@/components/dialogues/DialogueProvider'
import { countsKeys } from '@/lib/queryKeys'
import { KeyboardShortcutsProvider } from '@/lib/useKeyboardShortcuts'
import { UnsavedChangesGuardProvider } from '@/lib/useUnsavedChangesGuard'
import { detailResult, executionAt, postDefinition } from '@/test/fixtures/counts'
import { renderWithProviders } from '@/test/render'
@@ -32,6 +40,50 @@ beforeEach (() => {
describe ('Counts execution and URL state', () => {
it ('hydrates the server shell before fetching results with the URL conditions', async () => {
const client = new QueryClient ({
defaultOptions: { queries: { retry: false } } })
client.setQueryData (countsKeys.definition ('posts', 'ja'), postDefinition)
const page = <QueryClientProvider client={client}>
<HelmetProvider><MemoryRouter initialEntries={[
'/ja/counts/posts?mode=details&timezone=UTC&resource_id=7']}>
<DialogueProvider><UnsavedChangesGuardProvider>
<KeyboardShortcutsProvider>
<CountsResourcePage resource="posts"/>
</KeyboardShortcutsProvider>
</UnsavedChangesGuardProvider></DialogueProvider>
</MemoryRouter></HelmetProvider>
</QueryClientProvider>
const html = renderToString (page)
expect (fetchCounts).not.toHaveBeenCalled ()
expect (html).toContain ('counts-targets')
expect (html).not.toContain ('Recorded title')
expect (html).not.toContain ('counts-grid')
const resultKey = client.getQueryCache ().getAll ().find (query =>
query.queryKey[0] === 'counts' && query.queryKey[1] === 'posts')!.queryKey
client.setQueryData (resultKey, detailResult)
expect (renderToString (page)).not.toContain ('Recorded title')
client.removeQueries ({ queryKey: resultKey, exact: true })
const container = document.createElement ('div')
container.innerHTML = html
document.body.appendChild (container)
const recoverable = vi.fn ()
const root = hydrateRoot (container, page, { onRecoverableError: recoverable })
try
{
await screen.findByText ('Recorded title')
expect (fetchCounts).toHaveBeenCalledTimes (1)
expect (fetchCounts.mock.calls[0][1].get ('resource_id')).toBe ('7')
expect (recoverable).not.toHaveBeenCalled ()
}
finally
{
await act (async () => root.unmount ())
container.remove ()
client.clear ()
}
})
it ('enriches the initial URL without a second query or removing the page shell', async () => {
renderPage ()
const heading = screen.getByRole ('heading', { name: '広場集計' })
@@ -44,6 +96,18 @@ describe ('Counts execution and URL state', () => {
expect (currentParams ().get ('limit')).toBe ('100')
})
it ('retries an initial CSR failure with the URL filter intact', async () => {
fetchCounts.mockRejectedValueOnce (new Error ('offline'))
renderPage ('&resource_id=7')
await screen.findByRole ('alert')
expect (screen.queryByText ('Recorded title')).not.toBeInTheDocument ()
expect (currentParams ().get ('resource_id')).toBe ('7')
execute ()
await screen.findByText ('Recorded title')
expect (fetchCounts).toHaveBeenCalledTimes (2)
expect (fetchCounts.mock.calls[1][1].get ('resource_id')).toBe ('7')
})
it ('keeps edits local until execution then commits the server timestamp and filters together',
async () => {
renderPage ()