65 lines
4.3 KiB
TypeScript
65 lines
4.3 KiB
TypeScript
// Regression: an app-style palette with a string bg ('#15171C' or 'linear-gradient(...)') rendered a WHITE background
|
|
// (the template indexed bg[0..2] of a string), so white ink headlines vanished. Now the bg goes through palette.ts
|
|
// bgCss, and `shots render` samples the pixels behind the title (titleContrast) for `shots check`.
|
|
import { afterAll, describe, expect, test } from 'bun:test';
|
|
import { readFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { DEFAULT_PHONE, DEFAULT_TABLET } from '../../cli/shots/lib';
|
|
import { bgCss, contrastIssues, photoCss } from '../../cli/shots/palette';
|
|
import { titleContrast } from '../../cli/shots/render';
|
|
import { AFTER_ALL_MS, launchTestBrowser, scaled } from '../helpers/browser';
|
|
|
|
const shared = await launchTestBrowser('playwright-core');
|
|
const { browser } = shared;
|
|
if (!browser) console.warn(`palette bg render test skipped: no Chromium installed (${shared.skipWhy}).`);
|
|
afterAll(() => shared.close(), AFTER_ALL_MS);
|
|
|
|
const PNG = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==', 'base64');
|
|
const html = readFileSync(join(import.meta.dir, '..', '..', 'cli', 'shots', 'template-app.html'), 'utf8');
|
|
|
|
async function render(bg: unknown, ink: string, photo = false): Promise<{ contrast: Awaited<ReturnType<typeof titleContrast>>; bgImage: string; bgColor: string }> {
|
|
const page = await browser.newPage({ viewport: { width: 540, height: 960 } });
|
|
try {
|
|
const pal = { bg, glow: '#93c5fd', ink, shadow: 'rgba(0,0,0,.4)' };
|
|
const css = photo ? photoCss('/bgimg/0', 0.6) + ', ' + bgCss(bg as never) : bgCss(bg as never);
|
|
const job = { style: 'app', phone: DEFAULT_PHONE, tablet: DEFAULT_TABLET, landscape: { w: 1440, h: 900, bar: 0 }, side: 'left', logo: false, appName: 'Demo',
|
|
fonts: { title: 'X', body: 'Y' }, number: 1, scene: { id: 'a', frames: [{ src: 'a.png' }] },
|
|
palette: { ...pal, bgCss: css, bgTone: photo ? '#000000' : undefined, ...(photo ? { photo: { overlay: 0.6 } } : {}) },
|
|
copy: { eyebrow: 'Notes', title: ['Calm notes', 'for busy days'], sub: 'Synced everywhere.' } };
|
|
await page.route('http://t.test/**', (r: { request(): { url(): string }; fulfill(o: unknown): Promise<void> }) => {
|
|
const u = new URL(r.request().url());
|
|
if (u.pathname.endsWith('.html')) return r.fulfill({ contentType: 'text/html', body: html });
|
|
if (u.pathname === '/job.json') return r.fulfill({ contentType: 'application/json', body: JSON.stringify(job) });
|
|
if (u.pathname.startsWith('/frames/') || u.pathname.startsWith('/bgimg/')) return r.fulfill({ contentType: 'image/png', body: PNG });
|
|
return r.fulfill({ contentType: 'text/css', body: '' });
|
|
});
|
|
await page.goto('http://t.test/template-app.html?s=a&w=540&h=960&lang=en');
|
|
await page.waitForFunction("document.body.dataset.ready === '1'", null, { timeout: 15000 });
|
|
const st = JSON.parse(await page.evaluate("JSON.stringify((s => ({ i: s.backgroundImage, c: s.backgroundColor }))(getComputedStyle(document.getElementById('c'))))")) as { i: string; c: string };
|
|
return { contrast: await titleContrast(page, 540, 960), bgImage: st.i, bgColor: st.c };
|
|
} finally { await page.close(); }
|
|
}
|
|
|
|
describe.skipIf(!browser)('app template: palette bg forms and the contrast guard', () => {
|
|
test('a flat colour string renders that colour, white ink passes', async () => {
|
|
const r = await render('#15171C', '#ffffff');
|
|
expect(r.bgColor).toBe('rgb(21, 23, 28)');
|
|
expect(r.contrast.length).toBeGreaterThan(0);
|
|
expect(contrastIssues(r.contrast)).toEqual([]);
|
|
}, scaled(30_000));
|
|
test('a gradient string passes through', async () => {
|
|
const r = await render('linear-gradient(160deg, #0b1736, #1d4ed8)', '#ffffff');
|
|
expect(r.bgImage).toContain('linear-gradient');
|
|
expect(contrastIssues(r.contrast)).toEqual([]);
|
|
}, scaled(30_000));
|
|
test('white ink on a white bg is flagged', async () => {
|
|
const r = await render(['#ffffff', '#fafafa', '#ffffff'], '#ffffff');
|
|
expect(contrastIssues(r.contrast).map(i => i.code)).toContain('contrast');
|
|
}, scaled(30_000));
|
|
test('a photo under the overlay keeps white ink legible', async () => {
|
|
const r = await render('#ffffff', '#ffffff', true);
|
|
expect(r.bgImage).toContain('/bgimg/0');
|
|
expect(contrastIssues(r.contrast)).toEqual([]);
|
|
}, scaled(30_000));
|
|
});
|
|
|