// @vitest-environment jsdom import { describe, it, expect, vi, afterEach } from 'vitest'; import { render, screen, cleanup, fireEvent, waitFor } from '@testing-library/react'; import PasswordGate from '../PasswordGate'; /** * The gate treated every non-ok answer as a wrong password. After ten attempts * the rate limit answers 429 — and the visitor who finally typed the right * password was told it was wrong, and had it wiped from the field. */ afterEach(() => { cleanup(); vi.unstubAllGlobals(); }); async function submit(response: Response, type?: 'journal') { vi.stubGlobal( 'fetch', vi.fn(async () => response), ); render(); const input = screen.getByLabelText('Enter password') as HTMLInputElement; fireEvent.change(input, { target: { value: 'right-password' } }); fireEvent.submit(input.closest('form')!); const alert = await screen.findByRole('alert'); await waitFor(() => expect(input.disabled).toBe(false)); return { alert, input }; } describe('PasswordGate refusals', () => { it('says a wrong password is wrong, and clears the field', async () => { const { alert, input } = await submit(new Response(null, { status: 401 })); expect(alert.textContent).toBe('Incorrect password. Please try again.'); expect(input.value).toBe(''); }); it('says a rate limit is a rate limit, with the wait, and keeps the password', async () => { const { alert, input } = await submit( new Response(null, { status: 429, headers: { 'Retry-After': '42' } }), ); expect(alert.textContent).toBe('Too many attempts. Please try again in 42 seconds.'); expect(alert.textContent).not.toContain('Incorrect'); expect(input.value).toBe('right-password'); }); it('copes with a rate limit that names no wait', async () => { const { alert } = await submit(new Response(null, { status: 429 })); expect(alert.textContent).toBe('Too many attempts. Please try again later.'); }); it('reports a server failure as a failure, not a wrong password', async () => { const { alert, input } = await submit(new Response(null, { status: 500 })); expect(alert.textContent).toBe('Unable to verify password. Please try again later.'); expect(input.value).toBe('right-password'); }); }); describe('PasswordGate subtitle', () => { it('calls a journal entry a journal entry, not a gallery', () => { render(); expect(screen.getByText('This journal entry is password-protected.')).toBeTruthy(); expect(screen.queryByText('This gallery is password-protected.')).toBeNull(); }); });