このコミットが含まれているのは:
@@ -1,21 +1,12 @@
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
import { fireEvent, screen } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import PostImportSourcePage from '@/pages/posts/PostImportSourcePage'
|
||||
import { buildUser } from '@/test/factories'
|
||||
import { buildPostImportRow } from '@/test/postImportFactories'
|
||||
import { renderWithProviders } from '@/test/render'
|
||||
|
||||
const api = vi.hoisted (() => ({
|
||||
apiPost: vi.fn (),
|
||||
isApiError: vi.fn () }))
|
||||
|
||||
const router = vi.hoisted (() => ({ navigate: vi.fn () }))
|
||||
|
||||
const toastApi = vi.hoisted (() => ({ toast: vi.fn () }))
|
||||
|
||||
vi.mock ('@/lib/api', () => api)
|
||||
vi.mock ('@/components/ui/use-toast', () => toastApi)
|
||||
vi.mock ('react-router-dom', async importOriginal => ({
|
||||
...await importOriginal<typeof import('react-router-dom')> (),
|
||||
useNavigate: () => router.navigate }))
|
||||
@@ -24,71 +15,53 @@ describe ('PostImportSourcePage', () => {
|
||||
beforeEach (() => {
|
||||
sessionStorage.clear ()
|
||||
vi.clearAllMocks ()
|
||||
api.isApiError.mockReturnValue (false)
|
||||
})
|
||||
|
||||
it ('shows no empty error initially and validates only after Next is pressed', () => {
|
||||
it ('validates an empty source only after Next is pressed', () => {
|
||||
renderWithProviders (<PostImportSourcePage user={buildUser ()}/>)
|
||||
|
||||
expect (screen.getByRole ('heading', { name: '広場に投稿を追加' })).toBeInTheDocument ()
|
||||
expect (screen.queryByText ('投稿インポート')).not.toBeInTheDocument ()
|
||||
expect (screen.queryByText ('URL を入力してください.')).not.toBeInTheDocument ()
|
||||
|
||||
fireEvent.click (screen.getByRole ('button', { name: '次へ' }))
|
||||
|
||||
expect (screen.getByText ('URL を入力してください.')).toBeInTheDocument ()
|
||||
expect (api.apiPost).not.toHaveBeenCalled ()
|
||||
expect (router.navigate).not.toHaveBeenCalled ()
|
||||
})
|
||||
|
||||
it ('shows frontend URL issues with line numbers without requesting preview', () => {
|
||||
it ('reports frontend URL issues with original line numbers', () => {
|
||||
renderWithProviders (<PostImportSourcePage user={buildUser ()}/>)
|
||||
const input = screen.getByRole ('textbox', { name: '' })
|
||||
|
||||
fireEvent.change (input, {
|
||||
target: { value: '\nftp://example.com/file\nhttps://example.com/valid' } })
|
||||
expect (screen.queryByText (/2 行目/)).not.toBeInTheDocument ()
|
||||
fireEvent.click (screen.getByRole ('button', { name: '次へ' }))
|
||||
|
||||
expect (screen.getByText (/2 行目: HTTP または HTTPS/)).toBeInTheDocument ()
|
||||
expect (screen.getByText ('ftp://example.com/file')).toBeInTheDocument ()
|
||||
expect (input).toHaveAttribute ('aria-invalid', 'true')
|
||||
expect (input.getAttribute ('aria-describedby')).toContain ('post-import-source-issues')
|
||||
expect (api.apiPost).not.toHaveBeenCalled ()
|
||||
})
|
||||
|
||||
it ('shows backend URL errors against original input without a session', async () => {
|
||||
api.apiPost.mockResolvedValue ({
|
||||
rows: [buildPostImportRow ({
|
||||
sourceRow: 2,
|
||||
url: 'https://example.com/canonical',
|
||||
status: 'error',
|
||||
validationErrors: { url: ['URL が重複しています.'] } })] })
|
||||
renderWithProviders (<PostImportSourcePage user={buildUser ()}/>)
|
||||
|
||||
fireEvent.change (screen.getByRole ('textbox', { name: '' }), {
|
||||
target: { value: '\nhttps://example.com/original' } })
|
||||
fireEvent.click (screen.getByRole ('button', { name: '次へ' }))
|
||||
|
||||
expect (await screen.findByText ('2 行目: URL が重複しています.')).toBeInTheDocument ()
|
||||
expect (screen.getAllByText ('https://example.com/original')).toHaveLength (2)
|
||||
expect (router.navigate).not.toHaveBeenCalled ()
|
||||
expect (Array.from ({ length: sessionStorage.length }, (_, index) =>
|
||||
sessionStorage.key (index))).not.toContainEqual(
|
||||
expect.stringMatching (/^post-import-session:/))
|
||||
})
|
||||
|
||||
it ('navigates to /posts/new with query state after a successful preview', async () => {
|
||||
api.apiPost.mockResolvedValue ({ rows: [buildPostImportRow ()] })
|
||||
it ('navigates with individually encoded URLs without calling an API', () => {
|
||||
renderWithProviders (<PostImportSourcePage user={buildUser ()}/>)
|
||||
|
||||
fireEvent.change (screen.getByRole ('textbox', { name: '' }), {
|
||||
target: { value: 'https://example.com/post' } })
|
||||
target: {
|
||||
value: 'https://example.com/one+a\nhttps://example.com/two?value=b+c' } })
|
||||
fireEvent.click (screen.getByRole ('button', { name: '次へ' }))
|
||||
|
||||
await waitFor (() => {
|
||||
expect (router.navigate).toHaveBeenCalledWith (
|
||||
expect.stringMatching (/^\/posts\/new\?/))
|
||||
})
|
||||
expect (Array.from ({ length: sessionStorage.length }, (_, index) =>
|
||||
sessionStorage.key (index))).not.toContainEqual(
|
||||
expect.stringMatching (/^post-import-session:/))
|
||||
expect (router.navigate).toHaveBeenCalledWith (
|
||||
'/posts/new?urls=https%3A%2F%2Fexample.com%2Fone%2Ba'
|
||||
+ '+https%3A%2F%2Fexample.com%2Ftwo%3Fvalue%3Db%2Bc')
|
||||
})
|
||||
|
||||
it ('disables Next when the encoded request target reaches 4096 bytes', () => {
|
||||
renderWithProviders (<PostImportSourcePage user={buildUser ()}/>)
|
||||
const input = screen.getByRole ('textbox', { name: '' })
|
||||
|
||||
fireEvent.change (input, {
|
||||
target: { value: `https://example.com/${ 'a'.repeat (4_100) }` } })
|
||||
|
||||
expect (screen.getByRole ('button', { name: '次へ' })).toBeDisabled ()
|
||||
})
|
||||
})
|
||||
|
||||
新しい課題から参照
ユーザをブロックする