dd011207a0
Card 2 (deterministic core) + Card 3 (capture+mask geometry): - src/lib/core/math.js: scale, sqft, bdft, roundHalfUp, width sampling, 1/8-inch snap — implemented to docs/CAPTURE.md identities. - src/lib/core/mask.js: keySheet green/black/auto chroma keys + maskStats. - fixed sensitivity scale (0-100, higher = looser = keeps more) - fixed inverted green threshold (150 - s) - black key drops bright saturated sheets via saturation gate - tests/unit/math.test.js: corrected 3 wrong expected values to match the spec (sqft 21x12in = 1.75 not 17.5; bdft 3.06; sample avg 11.25). - tests/e2e/mask.spec.js: skeleton, skipped pending M1-5 live UI. Full unit suite green: 24/24. E2e collects clean.
43 lines
2.0 KiB
JavaScript
43 lines
2.0 KiB
JavaScript
import { test, expect } from '@playwright/test';
|
|
|
|
// M1-3: Playwright skeleton for the Mask state.
|
|
//
|
|
// The live Mask UI lands in card M1-5, so these tests are written but .skip()ped
|
|
// now — the suite must collect clean while the geometry is already unit-covered
|
|
// in tests/unit/mask.test.js (keySheet / maskStats are pure and DOM-free).
|
|
//
|
|
// Spec ground truth (docs/CAPTURE.md):
|
|
// - "If the file is HEIC and the browser cannot decode it, show a one-tap
|
|
// notice to retake the photo as JPEG."
|
|
// - "Approve is blocked when the mask has no slab pixels. User retakes."
|
|
|
|
test.describe('Mask state (M1-5 live UI)', () => {
|
|
test.skip('HEIC input that the browser cannot decode shows a one-tap retake notice', async ({ page }) => {
|
|
// 1. Land in the Mask state with a photo loaded (M1-5 provides this).
|
|
await page.goto('/');
|
|
// 2. Provide an HEIC file the browser cannot decode.
|
|
// (Test fixture path to be added with the M1-5 capture UI.)
|
|
// const input = page.locator('input[type="file"]');
|
|
// await input.setInputFiles('tests/fixtures/slab.heic');
|
|
// 3. The app must surface a one-tap 'retake as JPEG' notice — never a
|
|
// silent crash, never a server-side decode.
|
|
// const notice = page.getByRole('alert', { name: /retake/i });
|
|
// await expect(notice).toBeVisible();
|
|
// await expect(notice).toHaveText(/retake as JPEG/i);
|
|
// 4. Tapping it returns to Add photo.
|
|
// await notice.tap();
|
|
// await expect(page.getByRole('button', { name: /add photo/i })).toBeVisible();
|
|
});
|
|
|
|
test.skip('Approve is blocked while the mask has no slab pixels', async ({ page }) => {
|
|
// 1. Load a Mask whose keying removes every pixel (e.g. photo of sheet only).
|
|
await page.goto('/');
|
|
// 2. Approve must be disabled — the mask has no slab pixels.
|
|
// const approve = page.getByRole('button', { name: /approve/i });
|
|
// await expect(approve).toBeDisabled();
|
|
// 3. User retakes: after a photo with a real slab is keyed, approve enables.
|
|
// await page.getByRole('button', { name: /retake/i }).tap();
|
|
// await expect(approve).toBeEnabled();
|
|
});
|
|
});
|