このコミットが含まれているのは:
2026-06-22 07:57:17 +09:00
コミット d2e69b72da
12個のファイルの変更301行の追加10行の削除
+23 -1
ファイルの表示
@@ -1,9 +1,11 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { apiPost } from '@/lib/api'
import { apiGet, apiPost } from '@/lib/api'
import {
buildGekanatorQuestions,
expectedAnswerForQuestion,
fetchGekanatorPosts,
fetchGekanatorQuestions,
learnedSemanticSideForPost,
questionIdForCondition,
restoreGekanatorQuestion,
@@ -24,6 +26,7 @@ vi.mock('@/lib/api', () => ({
}))
const mockedApiPost = vi.mocked(apiPost)
const mockedApiGet = vi.mocked(apiGet)
const post = (overrides: Partial<Post> = {}): Post => ({
id: 1,
@@ -43,6 +46,24 @@ const post = (overrides: Partial<Post> = {}): Post => ({
...overrides,
})
describe('Gekanator API functions', () => {
it('returns posts from the Gekanator posts endpoint', async () => {
const posts = [post()]
mockedApiGet.mockResolvedValueOnce({ posts })
await expect(fetchGekanatorPosts()).resolves.toEqual(posts)
expect(mockedApiGet).toHaveBeenCalledWith('/gekanator/posts')
})
it('returns questions from the Gekanator questions endpoint', async () => {
const questions: StoredGekanatorQuestion[] = []
mockedApiGet.mockResolvedValueOnce({ questions })
await expect(fetchGekanatorQuestions()).resolves.toEqual(questions)
expect(mockedApiGet).toHaveBeenCalledWith('/gekanator/questions')
})
})
describe('expectedAnswerForQuestion', () => {
it('returns a direct example answer when present', () => {
const question: StoredGekanatorQuestion = {
@@ -126,6 +147,7 @@ describe('expectedAnswerForQuestion', () => {
postCount: 1,
createdAt: '2026-06-10T00:00:00.000Z',
updatedAt: '2026-06-10T00:00:00.000Z',
deprecatedAt: null,
hasWiki: false,
hasDeerjikists: false,
materialId: null,
+14
ファイルの表示
@@ -58,6 +58,20 @@ describe ('tags API functions', () => {
)
})
it.each ([
[true, '1'],
[false, '0'],
] as const) ('maps deprecated=%s to %s', async (deprecated, expected) => {
api.apiGet.mockResolvedValueOnce ({ tags: [], count: 0 })
await fetchTags ({ ...baseParams, deprecated })
expect (api.apiGet).toHaveBeenCalledWith (
'/tags',
{ params: expect.objectContaining ({ deprecated: expected }) },
)
})
it ('returns null when tag fetches fail', async () => {
api.apiGet.mockRejectedValueOnce (new Error ('missing'))
api.apiGet.mockRejectedValueOnce (new Error ('missing'))
+17 -3
ファイルの表示
@@ -14,13 +14,22 @@ vi.mock ('@/lib/tags', () => tagsApi)
describe ('TagListPage', () => {
it ('loads tags from URL filters and renders the results table', async () => {
tagsApi.fetchTags.mockResolvedValueOnce ({
tags: [buildTag ({ id: 7, name: '虹夏', category: 'character', postCount: 99 })],
tags: [buildTag ({
id: 7,
name: '虹夏',
category: 'character',
postCount: 99,
deprecatedAt: '2026-06-01T00:00:00.000Z',
})],
count: 1,
})
renderWithProviders (
<TagListPage/>,
{ route: '/tags?name=%E8%99%B9&category=character&page=3&post_count_gte=5' },
{
route:
'/tags?name=%E8%99%B9&category=character&page=3&post_count_gte=5&deprecated=1',
},
)
await waitFor (() => {
@@ -30,6 +39,7 @@ describe ('TagListPage', () => {
category: 'character',
page: 3,
postCountGTE: 5,
deprecated: true,
}),
)
})
@@ -38,6 +48,8 @@ describe ('TagListPage', () => {
'/tags/7',
)
expect (screen.getAllByText ('キャラクター').length).toBeGreaterThan (0)
expect (screen.getAllByRole ('combobox')[1]).toHaveValue ('1')
expect (screen.getAllByText ('廃止')).toHaveLength (2)
})
it ('navigates to a normalized search URL on submit', async () => {
@@ -46,7 +58,9 @@ describe ('TagListPage', () => {
renderWithProviders (<TagListPage/>, { route: '/tags' })
fireEvent.change (screen.getByRole ('textbox'), { target: { value: '虹夏' } })
fireEvent.change (screen.getByRole ('combobox'), { target: { value: 'character' } })
fireEvent.change (screen.getAllByRole ('combobox')[0], {
target: { value: 'character' },
})
fireEvent.submit (screen.getByRole ('button', { name: '検索' }).closest ('form')!)
await waitFor (() => {
+2 -1
ファイルの表示
@@ -27,5 +27,6 @@
"@/*": ["*"]
}
},
"include": ["src"]
"include": ["src"],
"exclude": ["src/**/*.test.ts", "src/**/*.test.tsx", "src/test"]
}