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>
53 lines
2.2 KiB
TypeScript
53 lines
2.2 KiB
TypeScript
/**
|
|
* Regression for the review's CR1: the LightboxModal posted comments to
|
|
* `/upload/{id}/comment` (singular) while the only route is `/comments` (plural),
|
|
* so every comment submitted through the UI 404'd and was silently lost. The
|
|
* earlier "comment → SSE" spec passed by posting via a fetch helper, bypassing
|
|
* the component — a false green. This drives the real component end-to-end.
|
|
*/
|
|
import { test, expect } from '../../fixtures/test';
|
|
import { seedUpload } from '../../helpers/seed';
|
|
import { BASE } from '../../helpers/env';
|
|
|
|
test.describe('Comments — UI round-trip (CR1)', () => {
|
|
test('a comment typed in the lightbox persists to the backend', async ({
|
|
page,
|
|
guest,
|
|
signIn,
|
|
}) => {
|
|
const author = await guest('CommentAuthor');
|
|
const commenter = await guest('Commenter');
|
|
const uploadId = await seedUpload(author.jwt, { caption: 'Comment target' });
|
|
|
|
await signIn(page, commenter);
|
|
await page.goto('/feed');
|
|
|
|
// Open the lightbox. Only one upload exists, so the first open-button is it.
|
|
const imageButton = page.getByRole('button', { name: 'Bild vergrößern' }).first();
|
|
await expect(imageButton).toBeVisible({ timeout: 15_000 });
|
|
await imageButton.click();
|
|
|
|
const lightbox = page.locator('[role="dialog"][aria-labelledby="lightbox-title"]');
|
|
await expect(lightbox).toBeVisible();
|
|
|
|
const text = `Wunderschönes Foto ${Date.now()}`;
|
|
await lightbox.getByPlaceholder(/kommentar/i).fill(text);
|
|
await lightbox.getByRole('button', { name: /senden/i }).click();
|
|
|
|
// The component appends the comment only on a 2xx — with the old singular path
|
|
// it threw and nothing appeared. Assert it's visible in the panel...
|
|
await expect(lightbox.getByText(text)).toBeVisible();
|
|
|
|
// ...and that it actually persisted server-side (the crux CR1 broke).
|
|
await expect
|
|
.poll(async () => {
|
|
const res = await fetch(`${BASE}/api/v1/upload/${uploadId}/comments`, {
|
|
headers: { Authorization: `Bearer ${commenter.jwt}` },
|
|
});
|
|
const body = await res.json();
|
|
return Array.isArray(body) && body.some((c: { body: string }) => c.body === text);
|
|
})
|
|
.toBe(true);
|
|
});
|
|
});
|