import { fireEvent, screen, waitFor, within } from '@testing-library/react' import { useState } from 'react' import { useLocation, useNavigate } from 'react-router-dom' import { describe, expect, it, vi } from 'vitest' import { useUnsavedChangesGuard } from '@/lib/useUnsavedChangesGuard' import { renderWithProviders } from '@/test/render' const GuardHarness = () => { const [dirty, setDirty] = useState (true) const location = useLocation () const navigate = useNavigate () const discard = vi.fn (() => setDirty (false)) const { allowNextNavigation } = useUnsavedChangesGuard ({ dirty, onDiscard: discard }) return ( <> {location.pathname}{location.search} ) } describe ('useUnsavedChangesGuard', () => { it ('blocks route changes and resets a cancelled transition', async () => { renderWithProviders (, { route: '/current' }) fireEvent.click (screen.getByRole ('button', { name: 'move' })) expect (within (await screen.findByRole ('dialog')).getByText ( '変更が破棄してページ移動しますか?')).toBeInTheDocument () expect (screen.getByLabelText ('location')).toHaveTextContent ('/current') fireEvent.click (screen.getByRole ('button', { name: '取消' })) await waitFor (() => { expect (screen.queryByRole ('dialog')).not.toBeInTheDocument () }) expect (screen.getByLabelText ('location')).toHaveTextContent ('/current') }) it ('proceeds with the blocked transition after discard is confirmed', async () => { renderWithProviders (, { route: '/current' }) fireEvent.click (screen.getByRole ('button', { name: 'move' })) fireEvent.click (await screen.findByRole ('button', { name: '変更を破棄して移動' })) await waitFor (() => { expect (screen.getByLabelText ('location')).toHaveTextContent ('/next?tab=one') }) }) it ('allows only the next navigation without leaving a bypass token', async () => { renderWithProviders (, { route: '/current' }) fireEvent.click (screen.getByRole ('button', { name: 'allowed move' })) await waitFor (() => { expect (screen.getByLabelText ('location')).toHaveTextContent ('/allowed') }) fireEvent.click (screen.getByRole ('button', { name: 'move' })) expect (within (await screen.findByRole ('dialog')).getByText ( '変更が破棄してページ移動しますか?')).toBeInTheDocument () expect (screen.getByLabelText ('location')).toHaveTextContent ('/allowed') }) })