Files
EventSnap/e2e/specs/09-mobile/touch-targets.spec.ts
fabi bbdfae09a0 chore(e2e): add ESLint + Prettier; fix real findings; dedupe BASE
The Playwright suite had no linter and no formatter — only tsc. Add flat-config ESLint
(typescript-eslint, type-aware) and Prettier (2-space, matching the suite's style).

Rules keep the ones that catch real TEST bugs and drop the noise:
  - no-floating-promises KEPT — an un-awaited request/assertion can let a test end before it runs,
    passing vacuously. It caught one: the SSE reader loop in sse-listener is now explicitly `void`.
  - no-unused-vars KEPT — caught three dead bindings (an unused adminToken fixture arg, an unused
    `api` arg, an unused JPEG_MAGIC import), all removed.
  - no-explicit-any OFF — all test code; `any` is the honest type for an untyped res.json() body or
    a page.evaluate() return.
  - no-empty-pattern OFF — Playwright's dependency-free fixtures are `async ({}, use) => {}`.

Refactor: `const BASE = process.env.E2E_FRONTEND_URL ?? '...'` was redeclared verbatim in 23
files — extracted to helpers/env.ts and imported, so a port/scheme change is one edit not a sweep.

Then `prettier --write`. Verified: eslint clean, tsc clean, prettier clean, desktop suite 210
passed / 1 skipped. (One mobile spec flaked once under retries:0 — a pre-existing cross-test
reflow-timing vector from the flakiness audit, not this change: the each-key edit is stable across
16 isolated runs and a clean full mobile re-run.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 20:45:59 +02:00

68 lines
2.7 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.
/**
* Phase 3 mobile — touch target sizing audit.
*
* Apple HIG says ≥ 44×44 pt. Material says ≥ 48×48 dp. We assert ≥ 44 px
* on critical interactive elements at mobile viewports (the device
* descriptor used by this project sets `deviceScaleFactor` but
* `boundingBox` returns CSS pixels, so the threshold is in CSS px).
*
* Each test grabs the bounding box for the locator and asserts both
* dimensions are ≥ 44. Fails surface with the actual sizing so the
* mismatch is obvious.
*/
import { test, expect } from '../../fixtures/test';
const MIN_TOUCH = 44;
async function assertTouchTarget(box: { width: number; height: number } | null, name: string) {
if (!box) throw new Error(`${name} not visible — no bounding box`);
expect
.soft(box.width, `${name} width ≥ ${MIN_TOUCH}px (got ${box.width})`)
.toBeGreaterThanOrEqual(MIN_TOUCH);
expect
.soft(box.height, `${name} height ≥ ${MIN_TOUCH}px (got ${box.height})`)
.toBeGreaterThanOrEqual(MIN_TOUCH);
}
test.describe('Mobile — touch target audit', () => {
test('bottom nav links and FAB are ≥ 44×44 px on /feed', async ({ page, guest, signIn }) => {
const g = await guest('Touchy');
await signIn(page, g);
await page.goto('/feed');
const galerie = page.getByRole('link', { name: 'Galerie' });
const fab = page.getByRole('button', { name: 'Hochladen' });
const konto = page.getByRole('link', { name: 'Konto' });
await expect(galerie).toBeVisible();
await assertTouchTarget(await galerie.boundingBox(), 'Galerie nav link');
await assertTouchTarget(await fab.boundingBox(), 'Upload FAB');
await assertTouchTarget(await konto.boundingBox(), 'Konto nav link');
});
test('join submit and PIN-modal buttons are ≥ 44×44 px', async ({ page }) => {
await page.goto('/join');
const submit = page.getByTestId('join-submit');
await assertTouchTarget(await submit.boundingBox(), 'Join submit button');
});
test('admin login submit button is ≥ 44×44 px', async ({ page }) => {
await page.goto('/admin/login');
const submit = page.getByTestId('admin-login-submit');
await assertTouchTarget(await submit.boundingBox(), 'Admin login submit');
});
test('PIN copy + continue buttons in the PIN modal are ≥ 44×44 px', async ({ page }) => {
await page.goto('/join');
await page.getByTestId('join-name-input').fill('TouchTargetPinModal');
await page.getByTestId('join-submit').click();
await expect(page.getByTestId('pin-modal')).toBeVisible();
await assertTouchTarget(await page.getByTestId('pin-copy').boundingBox(), 'PIN copy button');
await assertTouchTarget(
await page.getByTestId('continue-to-feed').boundingBox(),
'Continue-to-feed button'
);
});
});