Prøven, der skal holde udgaverne adskilt, stod grøn gennem to merges, mens
34 opgaveruter blev betjent af noter-udgaven. Den fejlede ikke: den
sammenligner routeren med en nedskreven liste, og listen var skrevet ud
fra de ruter, nogen havde tænkt på. Den er en DRIFT-kontrol, og den kan
ikke være en fuldstændighedskontrol. Det står nu i prøven selv.
Lukket: CalDAV-træet (8 ruter, alle læser eller skriver en VTODO),
.well-known/caldav (en påstand om at der er en CalDAV-server), ICS-feedet
og de to ruter til dets token, de fire opgave-eksporter, Todoist- og
CSV-importen, skabelonerne, sektionerne, de seks AI-opgaveruter og
indbakke-krogen. Plus fem af ni MCP-værktøjer — endepunktet er der
stadig, værktøjslisten er kortere.
Tre blev afgjort ved at læse frem for ved navnet: en skabelon laver
opgaver (CreateProjectFromTemplate), `section_id` står på `tasks` og
findes ikke på `notes`, og add_comment hænger på en opgave.
Men den dyre halvdel var fladen. En vandring gennem hver side, der kan
nås i noter-udgaven, med konsollen og svarene aflæst, fandt SYV brud —
fire af dem fra den første port:
sidebjælken GET /filters 404 ved hver navigation, ædt af .catch()
sidebjælken GET /labels 404 ved hver navigation, ædt af .catch()
notifikationer GET /ai/plan 404, stille
notesbogens side GET /tasks kastede → status = 'failed'
notesbogens side GET /sections kastede → samme fejlskærm
indstillinger GET /templates toast med fejlbesked på skærmen
integrationer GET /feed 404, stille
Den fjerde er den, der har kostet noget: /projekt/{id} er notesbogens
side, og kaldene samles i et Promise.all, så én 404 tog hele siden med.
Den har vist en fejlskærm frem for noter siden porten blev sat. Ingen
prøve åbnede en notesbog i den udgave, og .catch(() => {}) sørgede for,
at resten ikke sagde noget. "Fladen følger udgaven" var sandt om menuen
og falsk om hentningerne.
Vagten for opgaver står i butikken frem for ved kaldestederne: der var
tre, og den fjerde blev glemt — hvilket er præcis sådan, den slap
igennem.
Og rettelsen lavede selv en fejl, som vandringen fangede: da opgavekaldet
holdt op med at fejle, kom en uendelig effekt-løkke frem, som 404'eren
havde holdt låg på. En ubetinget tilskrivning til app.tasks får
projektsidens effekt til at kalde tilbage. 404'eren var låget på en
løkke, ikke bare støj.
Også: -timeout 20m i begge CI-workflows. internal/httpapi tog 664s her og
498s i CI på #95, mod Go's standardgrænse på 600 — og når den krydses, er
beskeden "panic: test timed out after 10m0s" plus et goroutine-dump, som
læser som en deadlock i koden frem for som en suite, der blev for stor.
watchForTrouble udskilt til web/e2e/trouble.js, så udgave-suiten kan bruge
den samme.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
39 lines
1.6 KiB
JavaScript
39 lines
1.6 KiB
JavaScript
/**
|
|
* Collects anything that went wrong quietly: a thrown exception, a console error,
|
|
* or an API call that came back a failure.
|
|
*
|
|
* Failed responses are recorded with their URL and status. "A console error
|
|
* happened" is not an actionable test failure — "PATCH /api/v1/tasks/x returned
|
|
* 500" is.
|
|
*
|
|
* Udskilt fra smoke.spec.js, så udgave-suiten kan bruge den samme — og den
|
|
* fortjente flytningen med det samme: lukker man en rute, fladen stadig kalder,
|
|
* står der en 404 på en side, der ser helt rigtig ud, og det her er det eneste,
|
|
* der ser den.
|
|
*/
|
|
export function watchForTrouble(page) {
|
|
const trouble = [];
|
|
page.on('console', (message) => {
|
|
// The browser logs its own line for every failed fetch. It carries no URL,
|
|
// so it is noise next to the response listener below.
|
|
if (message.type() === 'error' && !message.text().includes('Failed to load resource')) {
|
|
trouble.push(message.text());
|
|
}
|
|
});
|
|
page.on('pageerror', (error) => trouble.push(String(error)));
|
|
page.on('response', (response) => {
|
|
const url = response.url();
|
|
if (!url.includes('/api/') || response.ok()) return;
|
|
|
|
// A 401 from /auth/me is the app asking "am I signed in?" and being told no,
|
|
// which is the correct answer on the sign-in page and after signing out.
|
|
// Every other non-ok response is worth failing over — that is what this
|
|
// watcher is for.
|
|
const path = new URL(url).pathname;
|
|
if (path.endsWith('/auth/me') && response.status() === 401) return;
|
|
|
|
trouble.push(`${response.request().method()} ${path} → ${response.status()}`);
|
|
});
|
|
return trouble;
|
|
}
|