94 lines
5.5 KiB
TypeScript
94 lines
5.5 KiB
TypeScript
// shots.layout 'bleed' (the frame fills the image, a corner caption with a text shadow, no device) and
|
|
// shots.orientation 'landscape', plus the Mac App Store size (apple.mac). Pure checks, no browser.
|
|
import { describe, expect, test } from 'bun:test';
|
|
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { type ShipConfig, validateConfig } from '../cli/lib/config';
|
|
import { SHOTS_PAGES } from '../cli/lib/embedded';
|
|
import { checkFrameFolder } from '../cli/shots/check';
|
|
import { renderDims } from '../cli/shots/lib';
|
|
import { checkImage } from '../cli/shots/spec';
|
|
|
|
function png(w: number, h: number): Uint8Array {
|
|
const b = new Uint8Array(8 + 25 + 12);
|
|
b.set([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
const dv = new DataView(b.buffer);
|
|
dv.setUint32(8, 13); b.set([0x49, 0x48, 0x44, 0x52], 12); dv.setUint32(16, w); dv.setUint32(20, h); b[24] = 8; b[25] = 2;
|
|
dv.setUint32(33, 0); b.set([0x49, 0x45, 0x4e, 0x44], 37);
|
|
return b;
|
|
}
|
|
|
|
const base = (shots: Partial<ShipConfig['shots']> = {}, apple: ShipConfig['apple'] = { bundleId: 'com.example.game', mac: true }): ShipConfig => ({
|
|
app: { name: 'Example Game' }, languages: [{ id: 'en', apple: 'en-US', play: 'en-US' }],
|
|
apple, play: { packageName: 'com.example.game', tablets: false },
|
|
shots: { copy: 'c.json', frames: 'frames', out: 'out', layout: 'bleed', orientation: 'landscape',
|
|
scenes: [{ id: 'a', palette: 'night', frames: [{ src: 'a.png' }], corner: 'bl' }], feature: { frame: 'f.png' }, ...shots },
|
|
} as ShipConfig);
|
|
|
|
describe('landscape sizes', () => {
|
|
const dims = (k: string, o?: 'landscape' | 'portrait') => { const d = renderDims(k, { orientation: o }); return { w: d.w, h: d.h }; };
|
|
test('portrait sizes swap, feature, mac and chromebook stay', () => {
|
|
expect(dims('ios69', 'landscape')).toEqual({ w: 2868, h: 1320 });
|
|
expect(dims('ipad13', 'landscape')).toEqual({ w: 2752, h: 2064 });
|
|
expect(dims('play', 'landscape')).toEqual({ w: 1920, h: 1080 });
|
|
expect(dims('ios69')).toEqual({ w: 1320, h: 2868 });
|
|
expect(dims('feature', 'landscape')).toEqual({ w: 1024, h: 500 });
|
|
expect(dims('mac', 'landscape')).toEqual({ w: 2880, h: 1800 });
|
|
expect(dims('chromebook', 'landscape')).toEqual({ w: 1920, h: 1080 });
|
|
});
|
|
test('stores accept the landscape renders', () => {
|
|
for (const [k, w, h] of [['ios69', 2868, 1320], ['ipad13', 2752, 2064], ['mac', 2880, 1800], ['play', 1920, 1080], ['tab7', 1920, 1200], ['tab10', 2560, 1600]] as const)
|
|
expect(checkImage({ format: 'png', w, h, alpha: false, palette: false, bytes: 1000, depth: 8 }, k)).toEqual([]);
|
|
});
|
|
test('orientation landscape needs the bleed layout', () => {
|
|
expect(validateConfig(base({ layout: 'device' })).join(' ')).toContain("needs shots.layout: 'bleed'");
|
|
expect(validateConfig(base()).join(' ')).not.toContain('orientation');
|
|
});
|
|
});
|
|
|
|
describe('bleed layout', () => {
|
|
test('config checks', () => {
|
|
const bad = base({ layout: 'full' as never, orientation: 'sideways' as never, scenes: [{ id: 'a', palette: 'night', frames: [{ src: 'a.png' }], corner: 'mid' as never, captionScale: 3 }] });
|
|
const e = validateConfig(bad).join('\n');
|
|
expect(e).toContain('shots.layout: device | bleed');
|
|
expect(e).toContain('shots.orientation: portrait | landscape');
|
|
expect(e).toContain('corner tl | tr | bl | br');
|
|
expect(e).toContain('captionScale 0.6-1.4');
|
|
});
|
|
test('the pages ship embedded and draw no band behind the caption', () => {
|
|
for (const p of ['template-bleed.html', 'feature-bleed.html']) {
|
|
const html = SHOTS_PAGES[p]!;
|
|
expect(html).toBeTruthy();
|
|
expect(html).toContain('text-shadow');
|
|
// owner rule: legibility from a text shadow and glow, never a dark band, scrim or box behind the words
|
|
const css = html.slice(html.indexOf('<style>'), html.indexOf('</style>'));
|
|
expect(css).not.toMatch(/\.(cap|name)\b[^}]*background/);
|
|
expect(css).not.toMatch(/linear-gradient|scrim|band/);
|
|
}
|
|
});
|
|
test('frames per size at native pixels', () => {
|
|
const dir = mkdtempSync(join(tmpdir(), 'sb-bleed-'));
|
|
try {
|
|
const c = base();
|
|
const put = (rel: string, w: number, h: number) => { mkdirSync(join(dir, rel, '..'), { recursive: true }); writeFileSync(join(dir, rel), png(w, h)); };
|
|
const missing = checkFrameFolder(c, dir, ['en']).map(i => i.file);
|
|
expect(missing).toContain('en/ios69/a.png');
|
|
expect(missing).toContain('en/mac/a.png');
|
|
expect(missing).toContain('en/feature/f.png');
|
|
put('en/ios69/a.png', 2868, 1320); put('en/ios65/a.png', 2778, 1284); put('en/mac/a.png', 2880, 1800);
|
|
put('en/play/a.png', 1920, 1080); put('en/feature/f.png', 2048, 1000);
|
|
expect(checkFrameFolder(c, dir, ['en'])).toEqual([]);
|
|
put('en/play/a.png', 1080, 1920);
|
|
expect(checkFrameFolder(c, dir, ['en'])[0]!.msg).toContain('aspect');
|
|
put('en/play/a.png', 960, 540);
|
|
expect(checkFrameFolder(c, dir, ['en'])[0]!.msg).toContain('native resolution');
|
|
} finally { rmSync(dir, { recursive: true, force: true }); }
|
|
});
|
|
test('render serves the bleed pages and names the size in the URL', () => {
|
|
const src = readFileSync(join(import.meta.dir, '..', 'cli', 'shots', 'render.ts'), 'utf8');
|
|
expect(src).toContain("bleedLayout ? 'template-bleed'");
|
|
expect(src).toContain('&size=${encodeURIComponent(size)}');
|
|
expect(SHOTS_PAGES['template-bleed.html']).toContain('/frames/${encodeURIComponent(LANG)}/${encodeURIComponent(SIZE)}/');
|
|
});
|
|
});
|