From 7d630a09c90181464f544406ff48fe63b87567ea Mon Sep 17 00:00:00 2001 From: Adam Wright Date: Wed, 11 Nov 2026 22:18:21 +0000 Subject: [PATCH] e2e: the tissue drag follows the page, not the clock 'a dragged tissue counts as chosen' failed three times in one CI run. Its trace shows why: the test measured the selected list while the analysis panel was still opening, the panel then pushed the form down about 170px, and the drop landed on the heading above the list. The drag also relied on fixed pauses (150 ms, then 20 ms a step). The drag now waits on what the page shows: pressed on Colon as it is now until CDK's preview carries it, then moved to the selected list as it is now until CDK's placeholder is in it, re-measured if the page moved, and released only then. No fixed pause is left. Checked: 24 of 24 runs eight at once with the browser's CPU slowed sixfold. Aimed off the list, the old test releases and fails as CI did; the new one does not release. Co-Authored-By: Claude Opus 5.5 --- e2e/analysis-results.spec.ts | 46 ++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/e2e/analysis-results.spec.ts b/e2e/analysis-results.spec.ts index d7952ea4..7ebfdd40 100644 --- a/e2e/analysis-results.spec.ts +++ b/e2e/analysis-results.spec.ts @@ -1,4 +1,4 @@ -import { type Page, type Download } from '@playwright/test'; +import { type Page, type Download, type Locator } from '@playwright/test'; import { test, expect } from './support/backend'; import { readFileSync } from 'node:fs'; @@ -184,21 +184,37 @@ test.describe('Analysis results', () => { ]; const colon = available.getByText('Colon', { exact: true }); await colon.waitFor({ timeout: READY }); - const from = await colon.boundingBox(); - const to = await selected.boundingBox(); - if (!from || !to) throw new Error('the tissue lists are not on screen'); - // A drag as a person makes one: press, pause, then move in small steps. - // CDK does not start a drag from a single jump of the pointer. - const [x0, y0] = [from.x + from.width / 2, from.y + from.height / 2]; - const [x1, y1] = [to.x + to.width / 2, to.y + 30]; - await colon.hover(); - await page.mouse.down(); - await page.waitForTimeout(150); - for (let step = 1; step <= 25; step++) { - await page.mouse.move(x0 + ((x1 - x0) * step) / 25, y0 + ((y1 - y0) * step) / 25); - await page.waitForTimeout(20); - } + // The drag follows what the page shows, not the clock. The panel can still + // be opening when the drag begins, moving the lists down under the + // pointer: a target measured once, before the drag, was then the heading + // above the selected list, and the drop went nowhere (CI, Nov 2026). + const centre = async (target: Locator) => { + const box = await target.boundingBox(); + if (!box) throw new Error('not on screen'); + return { x: box.x + box.width / 2, y: box.y + box.height / 2 }; + }; + // CDK starts a drag once the pointer has moved a few pixels, which its + // preview shows. Pressed again, on Colon as it is now, until it has. + const preview = page.locator('.cdk-drag-preview'); + await expect(async () => { + await page.mouse.up(); + // The list scrolls, and Colon starts below its fold. + await colon.scrollIntoViewIfNeeded(); + const start = await centre(colon); + await page.mouse.move(start.x, start.y); + await page.mouse.down(); + await page.mouse.move(start.x + 10, start.y + 10, { steps: 5 }); + await expect(preview).toHaveText('Colon', { timeout: 500 }); + }).toPass({ timeout: READY }); + // Over the selected list as it is now; measured again until CDK places the + // tissue in it, which its placeholder shows. + await expect(async () => { + const over = await centre(selected); + await page.mouse.move(over.x, over.y, { steps: 10 }); + await expect(selected.locator('.cdk-drag-placeholder')).toBeAttached({ timeout: 500 }); + }).toPass({ timeout: READY }); await page.mouse.up(); + await expect(preview).toHaveCount(0); await expect(selected.getByText('Colon', { exact: true })).toBeVisible(); await page.getByRole('button', { name: /^Next$/ }).click();