Build and Push Docker Image / build-and-push (push) Failing after 19s
Reader: plan strip with a dot per chapter, plan sheet (day picker, chapter ticks, translation, streak, other plans), end-of-day card that steps through the day and rolls into the next, and chapter-level auto-marking. Each plan remembers its own translation (BSB, ESV, NLT, CSB, NASB, NET) and the Reader follows it while a plan is open. Plans home redesigned in the reader's themes with a Continue card, in-progress plans, a filterable catalog, and sheets for details and the custom-plan builder. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
72 lines
3.4 KiB
React
72 lines
3.4 KiB
React
import { render, screen, fireEvent, within } from '@testing-library/react';
|
|
import { vi, test, expect, beforeEach } from 'vitest';
|
|
import { getPlanSchedule, formatDayReadings } from '../utils/readingPlans.js';
|
|
|
|
const fn = () => vi.fn();
|
|
const ctx = {};
|
|
vi.mock('../context/AppContext.js', () => ({ useApp: () => ctx }));
|
|
import ReadingPlansPage from './ReadingPlansPage.jsx';
|
|
|
|
const record = (over = {}) => ({
|
|
id: 'p1', planId: 'gospels-30', startedAt: Date.now(), completedDays: [0, 1], chaptersRead: {}, completedAt: null,
|
|
lastActivityAt: Date.now(), translation: 'NLT', ...over,
|
|
});
|
|
|
|
beforeEach(() => {
|
|
Object.assign(ctx, {
|
|
goHome: fn(), planProgress: [], customPlans: [], readingActivity: [],
|
|
startPlan: fn(), toggleDay: fn(), restartPlan: fn(), removePlanProgress: fn(), recalibratePace: fn(), setPlanTranslation: fn(),
|
|
addCustomPlan: fn(), removeCustomPlan: fn(), updateCustomPlan: fn(),
|
|
openPlanReading: fn(), studyReadingPlanDay: fn(), studyBuildBusy: false,
|
|
readingPlanSyncStatus: '', readingPlanSyncError: '', retryReadingPlanSync: fn(),
|
|
readerTheme: 'paper', setReaderTheme: fn(), readerTranslation: 'BSB',
|
|
});
|
|
});
|
|
|
|
test('with no plans, the catalog is shown and there is no Continue card', () => {
|
|
render(<ReadingPlansPage />);
|
|
expect(screen.queryByText('Continue reading')).toBeNull();
|
|
expect(screen.getByRole('button', { name: /The Gospels in 30 Days/ })).toBeTruthy();
|
|
});
|
|
|
|
test('Continue reading opens the plan at its next unread day, in the plan translation', () => {
|
|
ctx.planProgress = [record()];
|
|
render(<ReadingPlansPage />);
|
|
const day2 = getPlanSchedule('gospels-30')[2];
|
|
expect(screen.getAllByText(new RegExp(formatDayReadings(day2)))[0]).toBeTruthy();
|
|
expect(screen.getAllByText('NLT').length).toBeGreaterThan(0);
|
|
fireEvent.click(screen.getByText('Continue reading'));
|
|
expect(ctx.openPlanReading).toHaveBeenCalledWith('p1', 2);
|
|
});
|
|
|
|
test('starting a catalog plan passes the chosen translation', () => {
|
|
render(<ReadingPlansPage />);
|
|
fireEvent.click(screen.getByRole('button', { name: /The Gospels in 30 Days/ }));
|
|
fireEvent.change(screen.getByRole('combobox'), { target: { value: 'CSB' } });
|
|
fireEvent.click(screen.getByRole('button', { name: 'Start plan' }));
|
|
expect(ctx.startPlan).toHaveBeenCalledWith('gospels-30', 'CSB');
|
|
});
|
|
|
|
test('every reader translation can be chosen for a plan', () => {
|
|
render(<ReadingPlansPage />);
|
|
fireEvent.click(screen.getByRole('button', { name: /The Gospels in 30 Days/ }));
|
|
const options = within(screen.getByRole('combobox')).getAllByRole('option').map((o) => o.value);
|
|
expect(options).toEqual(expect.arrayContaining(['BSB', 'ESV', 'NLT', 'CSB', 'NASB', 'NET']));
|
|
});
|
|
|
|
test('an in-progress plan: change its translation, jump into a specific day', () => {
|
|
ctx.planProgress = [record()];
|
|
render(<ReadingPlansPage />);
|
|
fireEvent.click(screen.getAllByRole('button', { name: /The Gospels in 30 Days/ })[0]);
|
|
fireEvent.change(screen.getByRole('combobox'), { target: { value: 'ESV' } });
|
|
expect(ctx.setPlanTranslation).toHaveBeenCalledWith('p1', 'ESV');
|
|
fireEvent.click(screen.getAllByText('Read')[3]); // "Read" on Day 4
|
|
expect(ctx.openPlanReading).toHaveBeenCalledWith('p1', 3);
|
|
});
|
|
|
|
test('categories filter the catalog', () => {
|
|
render(<ReadingPlansPage />);
|
|
fireEvent.click(screen.getByRole('button', { name: 'Old Testament' }));
|
|
expect(screen.queryByRole('button', { name: /The Gospels in 30 Days/ })).toBeNull();
|
|
});
|