/** * INS-series — insecure-context regression * * When the app is served over a non-secure origin (http://, used for * on-device phone previews), `window.crypto.randomUUID` is undefined and every * optimistic-id generator that called it crashed `handleAdd`. * * We reproduce that condition by shimming `crypto.randomUUID` to `undefined` * via `addInitScript` on a page that otherwise runs on localhost (where * Playwright has a deterministic OAuth path). Same code path, just with the * API removed. */ import { test, expect } from '@playwright/test'; import { USERS } from '../fixtures/users.js'; import { loginAs } from '../fixtures/login.js'; const SEED_LIST = '/lists/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'; test.describe('Insecure-context resilience', () => { test('INS-01: adding an item works when crypto.randomUUID is undefined', async ({ context, page }) => { await context.addInitScript(() => { // Simulate an insecure origin: randomUUID is gated behind secure // contexts and therefore missing. getRandomValues stays available. Object.defineProperty(window.crypto, 'randomUUID', { configurable: true, value: undefined }); }); // Re-assert the stub after navigation in case the context re-injects. page.on('framenavigated', async () => { await page .evaluate(() => { Object.defineProperty(window.crypto, 'randomUUID', { configurable: true, value: undefined }); }) .catch(() => {}); }); await loginAs(page, USERS.borja); await page.goto(SEED_LIST); // Sanity: the stub stuck — the page sees randomUUID as undefined. const stubbed = await page.evaluate(() => typeof window.crypto.randomUUID); expect(stubbed).toBe('undefined'); const input = page.getByPlaceholder(/add item|añadir producto/i); await expect(input).toBeVisible({ timeout: 10_000 }); const itemName = `INS-01-${Date.now()}`; await input.fill(itemName); await input.press('Enter'); // The row must render — i.e. generateId() returned a valid UUID and the // optimistic insert + server round-trip completed. If the old // crypto.randomUUID() call site is still there, handleAdd throws a // TypeError and the row never appears. await expect( page.locator('[role="listitem"]').filter({ hasText: itemName }).first() ).toBeVisible({ timeout: 5_000 }); }); });