import { test, expect } from '@playwright/test'; const SLAB_FIXTURE = 'tests/fixtures/slab-green.jpg'; // E2E smoke: the app serves and the health route responds. test('M1-1 harness smoke: app serves + health ok', async ({ page, request }) => { await page.goto('/'); await expect(page).toHaveTitle(/SlabUploader/); const res = await request.get('/api/health'); expect(res.ok()).toBeTruthy(); const body = await res.json(); expect(body.ok).toBe(true); }); // Regression: the preview canvas must fit the full photo within the // viewport without cropping. Spec: "Preview fits the device width. // Height is capped so Mask and Measure controls stay on screen." test('preview canvas fits full photo without cropping', async ({ page }) => { await page.goto('/'); await page.locator('input[type="file"]').setInputFiles(SLAB_FIXTURE); await page.waitForTimeout(2500); // mask recompute const canvas = page.locator('canvas.preview'); await expect(canvas).toBeVisible(); // Get the canvas's rendered (CSS) size and its intrinsic (pixel) size. const box = await canvas.boundingBox(); const intrinsic = await canvas.evaluate((c) => ({ w: c.width, h: c.height })); // The rendered size must fit within the viewport. const vp = page.viewportSize(); expect(box.width).toBeLessThanOrEqual(vp.width); expect(box.height).toBeLessThanOrEqual(vp.height * 0.55); // 50vh cap + tolerance // Aspect ratio must be preserved (no distortion). const renderedRatio = box.width / box.height; const intrinsicRatio = intrinsic.w / intrinsic.h; expect(renderedRatio).toBeCloseTo(intrinsicRatio, 1); // The canvas must not be clipped by its container. const wrap = page.locator('.preview-wrap'); const wrapBox = await wrap.boundingBox(); expect(box.width).toBeLessThanOrEqual(wrapBox.width + 1); expect(box.height).toBeLessThanOrEqual(wrapBox.height + 1); });