Files
SlabUploader/frontend/tests/e2e/helpers/draft.ts
Ty 1133201f30 M3 Review→Publish UI (#77): one-column flow, searchable pickers, fig/feat tags, e2e
- Review screen: slug-resolved species/wcat groupers, case-insensitive attrs,
  fig-*/feat-* tag pickers (METADATA-30/40), AI-assist labels + AI bubbles.
- App orchestrator: single writable flow store, capture→review→publish, Resume boot.
- API client (slabs.ts) mirroring live backend routes; Blob name fix.
- Playwright e2e (7 tests, hermetic mock): canvas PNG seed, client-UUID mock,
  SKU blur commit, ready-state reactive fix; expect timeout 4s, 2 workers.
- e2e green 7/7 in 6s; check_requirements + check_coverage PASS.
2026-09-20 14:34:24 -07:00

103 lines
3.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// e2e helper: seed an in-progress device draft (stage='measure', with results) so
// the Resume path restores straight to the Photos-ready Measure step — the
// deterministic driver for the create→review→publish flow (no live camera/canvas
// in e2e). The draft is written to the real idb-keyval DB ('keyval-store'/'keyval',
// key 'slabuploader-draft') exactly as `saveDraft` would, so `resumeProbe` +
// `continueDraft` + `continueToReview` consume it unmodified.
//
// `source.blob` MUST be a decodable image: restoreFromDraft re-derives the mask
// in a Worker that decodes the blob; a failed decode falls back to `mask` (no
// results → the Continue button never renders). A 1×1 white PNG decodes fine and
// yields slabPx:0 (an empty mask) → rederiveMask resolves → stage reaches measure.
import type { Page } from '@playwright/test';
const KEY = 'slabuploader-draft';
export interface SeedMeasure {
sku: string;
lengthIn: number;
thicknessIn: number;
sqft: number;
bdft: number;
widthIn: number;
}
const toEighths = (inches: number) => Math.round(inches * 8);
export async function seedMeasureDraft(
page: Page,
d: SeedMeasure,
): Promise<void> {
const widthEighths = toEighths(d.widthIn);
// Build the decodable source PNG in the page via canvas (a fixed base64 1×1
// gray+alpha PNG trips Chromium's software decoder in headless), then write
// the draft with that blob so restoreFromDraft's worker decode succeeds.
await page.evaluate(
(payload) =>
new Promise<void>((resolve, reject) => {
const c = document.createElement('canvas');
c.width = 1;
c.height = 1;
const ctx = c.getContext('2d')!;
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, 1, 1);
c.toBlob((png) => {
if (!png) { reject(new Error('canvas toBlob returned null')); return; }
const draft = {
version: 1,
stage: 'measure',
batch: [],
calibrationIndex: null,
source: { widthPx: 1, heightPx: 1, blob: png },
maskParams: { sheet: 'green', sensitivity: 50 },
mask: {
slabPx: 0,
bbox: { corners: [], longSidePx: 0, angleDeg: 0 },
slabBounds: { x: 0, y: 0, w: 0, h: 0 },
},
axisBbox: null,
lengthEighths: payload.lengthEighths,
thicknessEighths: payload.thicknessEighths,
results: {
lengthEighths: payload.lengthEighths,
thicknessEighths: payload.thicknessEighths,
scale: 8,
widths: {
min: payload.widthEighths,
max: payload.widthEighths,
avg: payload.widthEighths,
every6In: [],
},
sqft: payload.sqft,
bdft: payload.bdft,
sourceBelowFloor: false,
},
inventoryPng: png,
overlayPng: null,
batchApply: null,
};
const req = indexedDB.open('keyval-store', 1);
req.onupgradeneeded = () => {
const db = req.result;
if (!db.objectStoreNames.contains('keyval')) db.createObjectStore('keyval');
};
req.onsuccess = () => {
const db = req.result;
const tx = db.transaction('keyval', 'readwrite');
tx.objectStore('keyval').put(draft, 'slabuploader-draft');
tx.oncomplete = () => { db.close(); resolve(); };
tx.onerror = () => { db.close(); reject(tx.error); };
};
req.onerror = () => reject(req.error);
}, 'image/png');
}),
{
lengthEighths: toEighths(d.lengthIn),
thicknessEighths: toEighths(d.thicknessIn),
widthEighths,
sqft: d.sqft,
bdft: d.bdft,
},
);
}