Files
SlabUploader/frontend/tests/e2e/helpers/draft.ts
T
grokbot_ty 1b5ad112cc fix(capture): warn 300–799px PNG floor; hard-block only below 300
Split FLOOR_WARN_PX=800 / FLOOR_BLOCK_PX=300. Warn-band still encodes and
includes the photo (Photos-ready + batchApply ok+warn). Block excludes
siblings and clean cal. CAPTURE-105 / CAPTURE.md / PHOTOS.md updated.
2026-09-26 09:11:34 -07:00

108 lines
4.0 KiB
TypeScript
Raw 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;
/** Woo species leaf; required for Review-ready e2e now that Review has no species picker. */
speciesId?: number | null;
}
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,
species_id: payload.speciesId ?? null,
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,
sourceBelowBlock: false,
sourceBelowWarn: 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,
speciesId: d.speciesId ?? null,
},
);
}