このコミットが含まれているのは:
@@ -155,7 +155,7 @@ RSpec.describe 'Counts table API', type: :request do
|
|||||||
result = query(tags: 'renamed-alpha')
|
result = query(tags: 'renamed-alpha')
|
||||||
expect(result['versions'].first['tags'].first['name']).to eq('alpha')
|
expect(result['versions'].first['tags'].first['name']).to eq('alpha')
|
||||||
expect(versions(query(tags: 'renamed-alpha', definition: 'latest',
|
expect(versions(query(tags: 'renamed-alpha', definition: 'latest',
|
||||||
at: '2024-01-31T16:00:00Z', current: '0'))).to eq([2])
|
at: '2024-01-31T16:00:00Z'))).to eq([2])
|
||||||
expect(versions(query(tags: 'not:alpha-alias'))).to eq([3])
|
expect(versions(query(tags: 'not:alpha-alias'))).to eq([3])
|
||||||
expect(versions(query(tags: 'renamed-alpha nico:beta'))).to eq([1])
|
expect(versions(query(tags: 'renamed-alpha nico:beta'))).to eq([1])
|
||||||
expect(versions(query(tags: 'renamed-alpha nico:beta', match: 'any'))).to eq([1, 2, 4])
|
expect(versions(query(tags: 'renamed-alpha nico:beta', match: 'any'))).to eq([1, 2, 4])
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { DndContext, DragOverlay, KeyboardSensor, MouseSensor, TouchSensor,
|
|||||||
closestCenter, useSensor, useSensors } from '@dnd-kit/core'
|
closestCenter, useSensor, useSensors } from '@dnd-kit/core'
|
||||||
import { SortableContext, arrayMove, horizontalListSortingStrategy,
|
import { SortableContext, arrayMove, horizontalListSortingStrategy,
|
||||||
sortableKeyboardCoordinates } from '@dnd-kit/sortable'
|
sortableKeyboardCoordinates } from '@dnd-kit/sortable'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useId, useRef, useState } from 'react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import { CSS } from '@dnd-kit/utilities'
|
import { CSS } from '@dnd-kit/utilities'
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ const SortableColumns = ({ ids, fixed = [], name, onChange, children }: {
|
|||||||
onChange: (ids: string[]) => void; children: (ids: string[]) => ReactNode },
|
onChange: (ids: string[]) => void; children: (ids: string[]) => ReactNode },
|
||||||
) => {
|
) => {
|
||||||
const [mounted, setMounted] = useState (false)
|
const [mounted, setMounted] = useState (false)
|
||||||
|
const scope = useId ()
|
||||||
useEffect (() => setMounted (true), [])
|
useEffect (() => setMounted (true), [])
|
||||||
const [active, setActive] = useState<string | null> (null)
|
const [active, setActive] = useState<string | null> (null)
|
||||||
const drag = useRef<{ id: string; ids: string[] } | null> (null)
|
const drag = useRef<{ id: string; ids: string[] } | null> (null)
|
||||||
@@ -35,7 +36,7 @@ const SortableColumns = ({ ids, fixed = [], name, onChange, children }: {
|
|||||||
drag.current = null
|
drag.current = null
|
||||||
setActive (null)
|
setActive (null)
|
||||||
}
|
}
|
||||||
return <DndContext sensors={sensors} collisionDetection={closestCenter}
|
return <DndContext id={scope} sensors={sensors} collisionDetection={closestCenter}
|
||||||
accessibility={{ announcements: {
|
accessibility={{ announcements: {
|
||||||
onDragStart: event => name (String (event.active.id)).text,
|
onDragStart: event => name (String (event.active.id)).text,
|
||||||
onDragOver: event => event.over == null ? undefined : name (String (event.over.id)).text,
|
onDragOver: event => event.over == null ? undefined : name (String (event.over.id)).text,
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ const SortableList = (
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<DndContext
|
<DndContext
|
||||||
|
id={scope}
|
||||||
sensors={sensors}
|
sensors={sensors}
|
||||||
collisionDetection={args => {
|
collisionDetection={args => {
|
||||||
const hits = pointerWithin (args)
|
const hits = pointerWithin (args)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { hydrateRoot } from 'react-dom/client'
|
|||||||
import { renderToString } from 'react-dom/server'
|
import { renderToString } from 'react-dom/server'
|
||||||
import { HelmetProvider } from 'react-helmet-async'
|
import { HelmetProvider } from 'react-helmet-async'
|
||||||
import { createMemoryRouter, RouterProvider, useLocation } from 'react-router-dom'
|
import { createMemoryRouter, RouterProvider, useLocation } from 'react-router-dom'
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
|
|
||||||
import CountsResourcePage from '@/pages/CountsResourcePage'
|
import CountsResourcePage from '@/pages/CountsResourcePage'
|
||||||
import DialogueProvider from '@/components/dialogues/DialogueProvider'
|
import DialogueProvider from '@/components/dialogues/DialogueProvider'
|
||||||
@@ -37,7 +37,7 @@ const execute = () => fireEvent.submit (
|
|||||||
const openConditions = () => fireEvent.click (
|
const openConditions = () => fireEvent.click (
|
||||||
screen.getByText ('集計条件').closest ('button')!)
|
screen.getByText ('集計条件').closest ('button')!)
|
||||||
const resourceInput = () => within (
|
const resourceInput = () => within (
|
||||||
screen.getByText ('抽出条件', { selector: 'legend' }).closest ('fieldset')!,
|
screen.getByRole ('group', { name: '抽出条件' }),
|
||||||
).getByRole ('textbox', { name: 'Id.' })
|
).getByRole ('textbox', { name: 'Id.' })
|
||||||
const renderPage = (extra = '') => renderWithProviders (<>
|
const renderPage = (extra = '') => renderWithProviders (<>
|
||||||
<CountsResourcePage resource="posts"/><Location/>
|
<CountsResourcePage resource="posts"/><Location/>
|
||||||
@@ -62,6 +62,10 @@ beforeEach (() => {
|
|||||||
fetchCountsDefinition.mockReset ().mockResolvedValue (postDefinition)
|
fetchCountsDefinition.mockReset ().mockResolvedValue (postDefinition)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const hydrationCleanup = new Set<() => void> ()
|
||||||
|
afterEach (() => {
|
||||||
|
for (const dispose of hydrationCleanup) dispose ()
|
||||||
|
})
|
||||||
|
|
||||||
describe ('Counts execution and URL state', () => {
|
describe ('Counts execution and URL state', () => {
|
||||||
it ('renders sortable shells on the server even when JSDOM provides document', () => {
|
it ('renders sortable shells on the server even when JSDOM provides document', () => {
|
||||||
@@ -102,23 +106,42 @@ describe ('Counts execution and URL state', () => {
|
|||||||
const container = document.createElement ('div')
|
const container = document.createElement ('div')
|
||||||
container.innerHTML = html
|
container.innerHTML = html
|
||||||
document.body.appendChild (container)
|
document.body.appendChild (container)
|
||||||
|
const handles = [...container.querySelectorAll ('[aria-roledescription="sortable"]')]
|
||||||
|
.map (node => ({ node, description: node.getAttribute ('aria-describedby') }))
|
||||||
const recoverable = vi.fn ()
|
const recoverable = vi.fn ()
|
||||||
const root = hydrateRoot (container, page, { onRecoverableError: recoverable })
|
const root = hydrateRoot (container, page, { onRecoverableError: recoverable })
|
||||||
|
const dispose = () => {
|
||||||
|
if (!hydrationCleanup.delete (dispose)) return
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
act (() => root.unmount ())
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
container.remove ()
|
||||||
|
router.dispose ()
|
||||||
|
client.clear ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hydrationCleanup.add (dispose)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
expect (handles.length).toBeGreaterThan (0)
|
||||||
await waitForResult (client)
|
await waitForResult (client)
|
||||||
expect (fetchCounts).toHaveBeenCalledTimes (1)
|
expect (fetchCounts).toHaveBeenCalledTimes (1)
|
||||||
expect (fetchCounts.mock.calls[0][1].get ('resource_id')).toBe ('7')
|
expect (fetchCounts.mock.calls[0][1].get ('resource_id')).toBe ('7')
|
||||||
await waitFor (() => expect (currentParams ().get ('execution_at')).toBe (executionAt))
|
await waitFor (() => expect (currentParams ().get ('execution_at')).toBe (executionAt))
|
||||||
expect (currentParams ().get ('resource_id')).toBe ('7')
|
expect (currentParams ().get ('resource_id')).toBe ('7')
|
||||||
|
for (const { node, description } of handles)
|
||||||
|
{
|
||||||
|
expect (node.getAttribute ('aria-describedby')).toBe (description)
|
||||||
|
expect (document.getElementById (description!)).not.toBeNull ()
|
||||||
|
}
|
||||||
expect (recoverable).not.toHaveBeenCalled ()
|
expect (recoverable).not.toHaveBeenCalled ()
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
await act (async () => root.unmount ())
|
dispose ()
|
||||||
container.remove ()
|
|
||||||
router.dispose ()
|
|
||||||
client.clear ()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
import { act, render, screen, within } from '@testing-library/react'
|
import { act, render, screen, waitFor, within } from '@testing-library/react'
|
||||||
import { HelmetProvider } from 'react-helmet-async'
|
import { HelmetProvider } from 'react-helmet-async'
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
|
|
||||||
import { apiGet } from '@/lib/api'
|
import { apiGet } from '@/lib/api'
|
||||||
|
import { helpKeys } from '@/lib/queryKeys'
|
||||||
import App, { pageRoutes } from '@/App'
|
import App, { pageRoutes } from '@/App'
|
||||||
import { helpFixture } from '@/test/helpFixtures'
|
import { helpFixture } from '@/test/helpFixtures'
|
||||||
|
|
||||||
@@ -37,6 +38,13 @@ describe ('Help global edition history', () => {
|
|||||||
<QueryClientProvider client={client}>
|
<QueryClientProvider client={client}>
|
||||||
<HelmetProvider><App/></HelmetProvider>
|
<HelmetProvider><App/></HelmetProvider>
|
||||||
</QueryClientProvider>)
|
</QueryClientProvider>)
|
||||||
|
// Wait for the query started by the actual route, separately from DOM discovery.
|
||||||
|
await waitFor (() => expect (client.getQueryCache ().find ({
|
||||||
|
queryKey: helpKeys.changes ('ja') })).toBeDefined ())
|
||||||
|
const changes = client.getQueryCache ().find ({ queryKey: helpKeys.changes ('ja') })
|
||||||
|
await act (async () => { await changes!.promise })
|
||||||
|
expect (changes!.state.status).toBe ('success')
|
||||||
|
expect (changes!.state.data).toHaveLength (6)
|
||||||
const table = await screen.findByRole ('table')
|
const table = await screen.findByRole ('table')
|
||||||
const links = within (table).getAllByRole ('link')
|
const links = within (table).getAllByRole ('link')
|
||||||
const editions = links.map (link => link.getAttribute ('href'))
|
const editions = links.map (link => link.getAttribute ('href'))
|
||||||
|
|||||||
新しいイシューから参照
ユーザーをブロックする