Verse of the day with picture backgrounds, shareable image, and study covers
Build and Push Docker Image / build-and-push (push) Failing after 21s

- Verse of the day: card over a picture (a different one each day, or chosen),
  12 drawn scenes plus photos from public/verse-backgrounds, and "Share as
  image" (also on the Reader's verse panel) that saves or shares a PNG with the
  same background, a theme look, or a photo from the device
- Study cards get a cover banner named for the book, coloured by kind of book
- Outline "Build from my notes": abbreviations like "A.D. 63." no longer end a
  sentence

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-06 09:21:44 -04:00
1 parent d2bbf1feb4
commit aabd4267b8
15 files changed
+826 -8

No files matched your search

+65
View File
@@ -0,0 +1,65 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi, test, expect, beforeEach } from 'vitest';
vi.mock('../utils/verseCard.js', async (orig) => ({
...(await orig()),
renderVerseCard: vi.fn(async () => new Blob(['png'], { type: 'image/png' })),
shareOrSaveImage: vi.fn(async () => 'saved'),
}));
vi.mock('./Toasts.jsx', () => ({ useToast: () => ({ toast: vi.fn() }) }));
import VerseShareImage from './VerseShareImage.jsx';
import { renderVerseCard, shareOrSaveImage } from '../utils/verseCard.js';
beforeEach(() => {
localStorage.clear();
vi.clearAllMocks();
URL.createObjectURL = vi.fn(() => 'blob:card');
URL.revokeObjectURL = vi.fn();
});
const props = { text: 'For God so loved the world.', reference: 'John 3:16', translation: 'BSB' };
test('previews the card, redraws when the look changes, and remembers the look', async () => {
const user = userEvent.setup();
render(<VerseShareImage {...props} />);
expect(await screen.findByAltText(/John 3:16 \(BSB\) as a picture/)).toBeInTheDocument();
expect(renderVerseCard).toHaveBeenLastCalledWith(expect.objectContaining({ themeId: 'paper', reference: 'John 3:16' }));
await user.click(screen.getByRole('button', { name: 'Dark' }));
await screen.findByAltText(/as a picture/);
expect(renderVerseCard).toHaveBeenLastCalledWith(expect.objectContaining({ themeId: 'dark' }));
expect(localStorage.getItem('verse-card-style')).toBe('dark');
});
test('Save/Share sends the image under a file name from the reference', async () => {
const user = userEvent.setup();
render(<VerseShareImage {...props} />);
await screen.findByAltText(/as a picture/);
await user.click(screen.getByRole('button', { name: /save image|share image/i }));
expect(shareOrSaveImage).toHaveBeenCalledWith(expect.any(Blob), 'john-3-16.png', expect.objectContaining({ title: 'John 3:16' }));
});
test('says so if the picture can\'t be drawn', async () => {
renderVerseCard.mockRejectedValueOnce(new Error('This browser can’t draw the image.'));
render(<VerseShareImage {...props} />);
expect(await screen.findByRole('alert')).toHaveTextContent(/can’t draw/);
});
test('a background picture can be chosen, and is passed to the card; the plain looks come back with Plain', async () => {
const user = userEvent.setup();
render(<VerseShareImage {...props} />);
await screen.findByAltText(/as a picture/);
expect(screen.getByRole('group', { name: /image style/i })).toBeInTheDocument();
await user.selectOptions(screen.getByLabelText('Image background'), 'sea');
await screen.findByAltText(/as a picture/);
expect(renderVerseCard).toHaveBeenLastCalledWith(expect.objectContaining({ background: { kind: 'scene', id: 'sea' } }));
expect(screen.queryByRole('group', { name: /image style/i })).toBeNull(); // theme looks only apply to the plain card
await user.selectOptions(screen.getByLabelText('Image background'), 'plain');
expect(renderVerseCard).toHaveBeenLastCalledWith(expect.objectContaining({ background: null }));
});
test('the picture of the day is used when the Home card passes it in', async () => {
render(<VerseShareImage {...props} defaultBackground="auto" />);
await screen.findByAltText(/as a picture/);
expect(renderVerseCard).toHaveBeenLastCalledWith(expect.objectContaining({ background: expect.objectContaining({ kind: 'scene' }) }));
});