Files
immich-folio/components/__tests__/PasswordGate.test.tsx
Ralksta ad8b09700b release: v0.19.0
Release v0.19.0. See the `## [0.19.0] — 2026-09-29` section of
[CHANGELOG.md](https://github.com/ralksta/immich-folio/blob/dev/CHANGELOG.md)
for the full list.

Highlights: client proofing links (#683), custom content pages (#738),
asset picker search (#740), concurrent-edit detection (#742), env-locked
settings (#739), an accessibility baseline (#741), three security fixes
with advisories (#737, #755, #759), and a pre-release quality pass
(#780–#797).

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ralf <rohlenbo79@gmail.com>
Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com>
Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com>
Co-authored-by: Lance Martin <lancetaylor95@gmail.com>
2026-09-29 22:50:30 +02:00

66 lines
2.6 KiB
TypeScript

// @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(<PasswordGate slug="polen" title="Polen" type={type} />);
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(<PasswordGate slug="tagebuch" title="Journal" type="journal" />);
expect(screen.getByText('This journal entry is password-protected.')).toBeTruthy();
expect(screen.queryByText('This gallery is password-protected.')).toBeNull();
});
});