このコミットが含まれているのは:
@@ -1,27 +1,30 @@
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
import { Route, Routes } from 'react-router-dom'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { HelmetProvider } from 'react-helmet-async'
|
||||
import { MemoryRouter, Route, Routes } from 'react-router-dom'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { savePostImportSession } from '@/lib/postImportSession'
|
||||
import PostImportReviewPage from '@/pages/posts/PostImportReviewPage'
|
||||
import { buildUser } from '@/test/factories'
|
||||
import { buildPostImportRow } from '@/test/postImportFactories'
|
||||
import { renderWithProviders } from '@/test/render'
|
||||
|
||||
const api = vi.hoisted (() => ({ apiPost: vi.fn () }))
|
||||
const toastApi = vi.hoisted (() => ({ toast: vi.fn () }))
|
||||
const api = vi.hoisted (() => ({
|
||||
apiPost: vi.fn (),
|
||||
}))
|
||||
|
||||
const toastApi = vi.hoisted (() => ({
|
||||
toast: vi.fn (),
|
||||
}))
|
||||
|
||||
const dialogue = vi.hoisted (() => ({
|
||||
form: vi.fn (() => Promise.resolve ()),
|
||||
}))
|
||||
|
||||
vi.mock ('@/lib/api', () => api)
|
||||
vi.mock ('@/components/ui/use-toast', () => toastApi)
|
||||
|
||||
const renderPage = () => renderWithProviders (
|
||||
<Routes>
|
||||
<Route
|
||||
path="/posts/import/:sessionId/review"
|
||||
element={<PostImportReviewPage user={buildUser ()}/>}/>
|
||||
<Route path="/posts/import/:sessionId/result" element={<div>result route</div>}/>
|
||||
</Routes>,
|
||||
{ route: '/posts/import/session/review' })
|
||||
vi.mock ('@/lib/dialogues/useDialogue', () => ({
|
||||
default: () => dialogue,
|
||||
}))
|
||||
|
||||
describe ('PostImportReviewPage', () => {
|
||||
beforeEach (() => {
|
||||
@@ -29,69 +32,43 @@ describe ('PostImportReviewPage', () => {
|
||||
vi.clearAllMocks ()
|
||||
})
|
||||
|
||||
it ('keeps the review route and opens the first invalid row dialogue', async () => {
|
||||
const row = buildPostImportRow ({ attributes: { title: 'title' } })
|
||||
savePostImportSession ('session', { source: row.url, rows: [row], repairMode: 'all' })
|
||||
api.apiPost.mockResolvedValue ({ rows: [buildPostImportRow ({
|
||||
attributes: { title: 'title' },
|
||||
status: 'error',
|
||||
validationErrors: { title: ['タイトルを確認してください.'] } })] })
|
||||
|
||||
renderPage ()
|
||||
fireEvent.click (await screen.findByRole ('button', { name: '取込実行' }))
|
||||
|
||||
expect (await screen.findByText ('投稿を編輯')).toBeInTheDocument ()
|
||||
expect (screen.getByText ('タイトルを確認してください.')).toBeInTheDocument ()
|
||||
expect (api.apiPost).toHaveBeenCalledTimes (1)
|
||||
expect (screen.queryByText ('result route')).not.toBeInTheDocument ()
|
||||
})
|
||||
|
||||
it ('submits existing rows for a formal skipped result and opens result route', async () => {
|
||||
const row = buildPostImportRow ({
|
||||
skipReason: 'existing',
|
||||
existingPostId: 10 })
|
||||
savePostImportSession ('session', { source: row.url, rows: [row], repairMode: 'all' })
|
||||
it ('navigates to the result route after an initial recoverable failure', async () => {
|
||||
savePostImportSession ('review-session', {
|
||||
source: 'https://example.com/post',
|
||||
repairMode: 'all',
|
||||
rows: [buildPostImportRow ({ sourceRow: 1 })] })
|
||||
api.apiPost
|
||||
.mockResolvedValueOnce ({ rows: [row] })
|
||||
.mockResolvedValueOnce ({
|
||||
created: 0,
|
||||
skipped: 1,
|
||||
failed: 0,
|
||||
rows: [{ sourceRow: 1, status: 'skipped', existingPostId: 10 }] })
|
||||
.mockResolvedValueOnce ({
|
||||
rows: [buildPostImportRow ({ sourceRow: 1 })] })
|
||||
.mockResolvedValueOnce ({
|
||||
created: 0,
|
||||
skipped: 0,
|
||||
failed: 1,
|
||||
rows: [{
|
||||
sourceRow: 1,
|
||||
status: 'failed',
|
||||
recoverable: true,
|
||||
errors: { title: ['invalid'] } }] })
|
||||
|
||||
renderPage ()
|
||||
fireEvent.click (await screen.findByRole ('button', { name: '取込実行' }))
|
||||
render (
|
||||
<HelmetProvider>
|
||||
<MemoryRouter initialEntries={['/posts/import/review-session/review']}>
|
||||
<Routes>
|
||||
<Route
|
||||
path="/posts/import/:sessionId/review"
|
||||
element={<PostImportReviewPage user={buildUser ()}/>}/>
|
||||
<Route
|
||||
path="/posts/import/:sessionId/result"
|
||||
element={<div>RESULT ROUTE</div>}/>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</HelmetProvider>)
|
||||
|
||||
fireEvent.click (screen.getByRole ('button', { name: '取込実行' }))
|
||||
|
||||
expect (await screen.findByText ('result route')).toBeInTheDocument ()
|
||||
expect (api.apiPost).toHaveBeenCalledTimes (2)
|
||||
expect (api.apiPost.mock.calls[1]?.[1]).toMatchObject ({
|
||||
rows: [expect.objectContaining ({ sourceRow: 1, url: row.url })] })
|
||||
await waitFor (() => {
|
||||
const saved = JSON.parse (
|
||||
sessionStorage.getItem ('post-import-session:session') ?? '{}')
|
||||
expect (saved.rows[0]).toMatchObject ({
|
||||
importStatus: 'skipped',
|
||||
existingPostId: 10,
|
||||
skipReason: 'existing' })
|
||||
expect (screen.getByText ('RESULT ROUTE')).toBeInTheDocument ()
|
||||
})
|
||||
})
|
||||
|
||||
it ('keeps the dialogue open when save validation fails', async () => {
|
||||
const row = buildPostImportRow ({ attributes: { title: 'title' } })
|
||||
savePostImportSession ('session', { source: row.url, rows: [row], repairMode: 'all' })
|
||||
api.apiPost.mockResolvedValue ({
|
||||
rows: [buildPostImportRow ({
|
||||
sourceRow: row.sourceRow,
|
||||
validationErrors: {
|
||||
originalCreatedAt: ['オリジナルの作成日時の範囲は1分以上必要です.'] } })] })
|
||||
|
||||
renderPage ()
|
||||
fireEvent.click (await screen.findByRole ('button', { name: '編輯' }))
|
||||
fireEvent.click (await screen.findByRole ('button', { name: '編輯内容を保存' }))
|
||||
|
||||
expect (await screen.findByText ('投稿を編輯')).toBeInTheDocument ()
|
||||
expect (screen.getByText ('オリジナルの作成日時の範囲は1分以上必要です.'))
|
||||
.toBeInTheDocument ()
|
||||
expect (api.apiPost).toHaveBeenCalledTimes (1)
|
||||
expect (dialogue.form).not.toHaveBeenCalled ()
|
||||
})
|
||||
})
|
||||
|
||||
新しい課題から参照
ユーザをブロックする