Build and Push Docker Image / build-and-push (push) Successful in 6m47s
- Admin: sign-up Open / Invite only / Closed with single-use invite links - Present mode for scripts (auto-scroll) and outlines (step through, pace vs planned minutes) - Series: group studies on the dashboard, filter by series, calendar file for a study's sessions - Lesson handout: printable page per session (passage, outline, questions, writing lines) - Save a chunk's notes as your own study template (synced) - Voice dictation for notes and scripts (browser speech recognition) - Reading progress: map of the 66 books and a calendar of days read, synced and merged across devices - Export highlights and bookmarks as Markdown or Word, grouped by named colour - Verse of the day push to the ntfy topic (needs IQBIBLE_API_KEY) - Sharing: co-teacher editing via single-use invite links; read-only links can expire and take a passcode Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
51 lines
2.6 KiB
React
51 lines
2.6 KiB
React
import { render, screen } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { vi, test, expect } from 'vitest';
|
|
import LinkRules, { expiryText } from './LinkRules.jsx';
|
|
import SharedPasscodeForm from './SharedPasscodeForm.jsx';
|
|
|
|
test('says when a link expires and whether it has a passcode', () => {
|
|
const now = Date.UTC(2026, 9, 6);
|
|
expect(expiryText(null, now)).toBe('Never expires');
|
|
expect(expiryText(now - 1, now)).toBe('Expired');
|
|
expect(expiryText(now + 3 * 86_400_000, now)).toMatch(/3 days left/);
|
|
render(<LinkRules expiresAt={null} hasPasscode busy={false} onSave={vi.fn()} />);
|
|
expect(screen.getByText(/Needs a passcode/)).toBeInTheDocument();
|
|
});
|
|
|
|
test('only the settings you change are sent', async () => {
|
|
const onSave = vi.fn(async () => true);
|
|
const user = userEvent.setup();
|
|
render(<LinkRules expiresAt={null} hasPasscode={false} busy={false} onSave={onSave} />);
|
|
const save = screen.getByRole('button', { name: 'Save settings' });
|
|
expect(save).toBeDisabled();
|
|
await user.selectOptions(screen.getByLabelText('Stop working'), '7');
|
|
await user.click(save);
|
|
expect(onSave).toHaveBeenLastCalledWith({ expiresInDays: 7 }); // no passcode key: the passcode is left alone
|
|
await user.type(screen.getByLabelText(/Passcode/), ' moses ');
|
|
await user.click(screen.getByRole('button', { name: 'Save settings' }));
|
|
expect(onSave).toHaveBeenLastCalledWith({ passcode: 'moses' });
|
|
});
|
|
|
|
test('a passcode can be removed, and "Never" clears the expiry', async () => {
|
|
const onSave = vi.fn(async () => true);
|
|
const user = userEvent.setup();
|
|
render(<LinkRules expiresAt={Date.now() + 86_400_000} hasPasscode busy={false} onSave={onSave} />);
|
|
await user.click(screen.getByRole('button', { name: 'Remove passcode' }));
|
|
expect(onSave).toHaveBeenLastCalledWith({ passcode: null });
|
|
await user.selectOptions(screen.getByLabelText('Stop working'), 'Never');
|
|
await user.click(screen.getByRole('button', { name: 'Save settings' }));
|
|
expect(onSave).toHaveBeenLastCalledWith({ expiresInDays: null });
|
|
});
|
|
|
|
test('the viewer\'s passcode form submits what was typed and shows a wrong-passcode message', async () => {
|
|
const onSubmit = vi.fn();
|
|
const user = userEvent.setup();
|
|
render(<SharedPasscodeForm error="That passcode is not right." onSubmit={onSubmit} />);
|
|
expect(screen.getByRole('alert')).toHaveTextContent(/not right/);
|
|
expect(screen.getByRole('button', { name: 'Open' })).toBeDisabled();
|
|
await user.type(screen.getByLabelText('Passcode'), 'moses');
|
|
await user.click(screen.getByRole('button', { name: 'Open' }));
|
|
expect(onSubmit).toHaveBeenCalledWith('moses');
|
|
});
|