AI: address review on PR #465
このコミットが含まれているのは:
@@ -1,11 +1,13 @@
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { act, fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { textContentOf } from '@/test/text'
|
||||
import PostEditForm from '@/components/PostEditForm'
|
||||
import { buildPost, buildTag } from '@/test/factories'
|
||||
import { renderWithProviders as render } from '@/test/render'
|
||||
|
||||
import type { DialogueAPI } from '@/lib/dialogues/useDialogue'
|
||||
|
||||
const postsApi = vi.hoisted (() => ({
|
||||
updatePost: vi.fn (),
|
||||
}))
|
||||
@@ -18,18 +20,27 @@ const toastApi = vi.hoisted (() => ({
|
||||
toast: vi.fn (),
|
||||
}))
|
||||
|
||||
const dialogue = vi.hoisted (() => ({
|
||||
confirm: vi.fn<DialogueAPI['confirm']> (),
|
||||
choice: vi.fn () }))
|
||||
|
||||
vi.mock ('@/lib/posts', () => postsApi)
|
||||
vi.mock ('@/lib/api', () => api)
|
||||
vi.mock ('@/components/ui/use-toast', () => toastApi)
|
||||
vi.mock ('@/lib/dialogues/useDialogue', async importOriginal => ({
|
||||
...await importOriginal<typeof import ('@/lib/dialogues/useDialogue')> (),
|
||||
default: () => ({
|
||||
choice: vi.fn (),
|
||||
}),
|
||||
}))
|
||||
...await importOriginal<typeof import ('@/lib/dialogues/useDialogue')> (),
|
||||
default: () => dialogue }))
|
||||
|
||||
describe ('PostEditForm', () => {
|
||||
beforeEach (() => {
|
||||
vi.resetAllMocks ()
|
||||
})
|
||||
|
||||
it ('submits edited post fields with the current base version', async () => {
|
||||
const confirm = vi.fn<(value: boolean) => void> ()
|
||||
dialogue.confirm.mockReturnValueOnce (new Promise<boolean> (resolve => {
|
||||
confirm.mockImplementation (resolve)
|
||||
}))
|
||||
const onSave = vi.fn ()
|
||||
const post = buildPost ({
|
||||
id: 8,
|
||||
@@ -55,6 +66,16 @@ describe ('PostEditForm', () => {
|
||||
fireEvent.change (parentIds, { target: { value: '3 4' } })
|
||||
fireEvent.submit (screen.getByRole ('button', { name: '更新' }).closest ('form')!)
|
||||
|
||||
expect (postsApi.updatePost).not.toHaveBeenCalled ()
|
||||
expect (dialogue.confirm).toHaveBeenCalledTimes (1)
|
||||
const difference = textContentOf (
|
||||
dialogue.confirm.mock.calls[0][0].description)
|
||||
expect (difference).toContain ('oldnew')
|
||||
expect (difference).toContain ('23 4')
|
||||
expect (difference).not.toContain ('general-tag')
|
||||
|
||||
await act (async () => { confirm (true) })
|
||||
|
||||
await waitFor (() => {
|
||||
expect (postsApi.updatePost).toHaveBeenCalledWith (
|
||||
expect.objectContaining ({
|
||||
@@ -71,6 +92,28 @@ describe ('PostEditForm', () => {
|
||||
expect (textContentOf (toastApi.toast.mock.calls[0][0].description)).toBe ('更新しました.')
|
||||
})
|
||||
|
||||
it ('keeps edited fields without submitting when confirmation is cancelled', async () => {
|
||||
const onSave = vi.fn ()
|
||||
dialogue.confirm.mockResolvedValueOnce (false)
|
||||
render (<PostEditForm post={buildPost ()} onSave={onSave}/>)
|
||||
|
||||
const [title, parentIds] = screen.getAllByRole ('textbox')
|
||||
fireEvent.change (title, { target: { value: 'new' } })
|
||||
fireEvent.change (parentIds, { target: { value: '3 4' } })
|
||||
const button = screen.getByRole ('button', { name: '更新' })
|
||||
fireEvent.submit (button.closest ('form')!)
|
||||
|
||||
await waitFor (() => {
|
||||
expect (button).toBeEnabled ()
|
||||
})
|
||||
expect (dialogue.confirm).toHaveBeenCalledTimes (1)
|
||||
expect (postsApi.updatePost).not.toHaveBeenCalled ()
|
||||
expect (onSave).not.toHaveBeenCalled ()
|
||||
expect (toastApi.toast).not.toHaveBeenCalled ()
|
||||
expect (title).toHaveValue ('new')
|
||||
expect (parentIds).toHaveValue ('3 4')
|
||||
})
|
||||
|
||||
it ('preserves duration while the video tag is temporarily removed', () => {
|
||||
const post = buildPost ({
|
||||
videoMs: 180_500,
|
||||
@@ -100,6 +143,7 @@ describe ('PostEditForm', () => {
|
||||
'shows deduplicated original-created endpoint errors on the shared datetime field',
|
||||
async () => {
|
||||
const post = buildPost ()
|
||||
dialogue.confirm.mockResolvedValueOnce (true)
|
||||
api.isApiError.mockReturnValue (true)
|
||||
postsApi.updatePost.mockRejectedValueOnce ({
|
||||
response: {
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする