From 7234ebc1977748c4766ed1f39f7c8110648caab5 Mon Sep 17 00:00:00 2001 From: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com> Date: Tue, 28 Jul 2026 14:58:34 +0200 Subject: [PATCH] test(e2e): de-flake the tasks + marketplace journeys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two intermittent failures in "Local E2E Journeys" (both against localhost, unrelated to any product change): - tasks-journey: `getByText('E2E erledigt')` matched 2 elements under strict mode — the completion note renders in two panes (list + detail), the same case the empty state already handled with `.first()`. Add `.first()` here too. - marketplace-checkout-journey: read-after-write race — after the UI confirms the answer, a single API re-fetch sometimes read the question status still 'open'. Replace with `expect.poll(...).toBe('answered')` so it waits for the write to reflect. No product code changed; scoped to the two observed flakes (other journeys use unique or scoped locators and initial-state asserts, left as-is). Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/e2e/marketplace-checkout-journey.spec.ts | 14 ++++++++++++-- tests/e2e/tasks-journey.spec.ts | 5 ++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/e2e/marketplace-checkout-journey.spec.ts b/tests/e2e/marketplace-checkout-journey.spec.ts index 7f5ede18c..50061a335 100644 --- a/tests/e2e/marketplace-checkout-journey.spec.ts +++ b/tests/e2e/marketplace-checkout-journey.spec.ts @@ -96,8 +96,18 @@ test.describe('Marketplace dual-persona checkout journey', () => { await publishAnswer.click() await expect(page.getByText(testAnswer)).toBeVisible({ timeout: 15000 }) - const questionsAfterAnswer = await fetchListingQuestions(page.request, listingId) - expect(questionsAfterAnswer.find((q) => q.question === testQuestion)?.status).toBe('answered') + // The UI confirms the answer above, but the question-status write can lag the + // read by a beat — poll the API until it reflects 'answered' instead of a + // single racy fetch (which flaked with status still 'open'). + await expect + .poll( + async () => { + const qs = await fetchListingQuestions(page.request, listingId) + return qs.find((q) => q.question === testQuestion)?.status + }, + { timeout: 15000 }, + ) + .toBe('answered') // 2c. Seller cannot buy own listing (must run while listing is still active) await loginWithCredentials( diff --git a/tests/e2e/tasks-journey.spec.ts b/tests/e2e/tasks-journey.spec.ts index 40c196be6..cb6f9f5da 100644 --- a/tests/e2e/tasks-journey.spec.ts +++ b/tests/e2e/tasks-journey.spec.ts @@ -65,7 +65,10 @@ test.describe('Admin tasks staff journey', () => { await expect(page.getByRole('heading', { name: 'Erledigungen (1)' })).toBeVisible({ timeout: 15000, }) - await expect(page.getByText('E2E erledigt')).toBeVisible() + // .first(): like the empty state above, the completion note renders in two + // panes (list + detail), so a bare getByText matches 2 elements once both + // panes have hydrated — which flaked under strict mode. + await expect(page.getByText('E2E erledigt').first()).toBeVisible() if (hasDualPersonaCredentials()) { await loginWithCredentials(page, '/dashboard', USER_TEST_EMAIL, USER_TEST_PASSWORD)