From 7cdfb7149a230a2202b7caa557f367acf1e89f59 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Mon, 3 Aug 2026 20:15:42 -0700 Subject: [PATCH] test(e2e): start marquee from an explicit empty lane --- ...orkspace-board-lane-virtualization.spec.ts | 157 ++++-------------- 1 file changed, 34 insertions(+), 123 deletions(-) diff --git a/tests/e2e/workspace-board-lane-virtualization.spec.ts b/tests/e2e/workspace-board-lane-virtualization.spec.ts index 0bc74452965..08240bb8232 100644 --- a/tests/e2e/workspace-board-lane-virtualization.spec.ts +++ b/tests/e2e/workspace-board-lane-virtualization.spec.ts @@ -1,13 +1,6 @@ import { test, expect } from './helpers/orca-app' import { waitForActiveWorktree, waitForSessionReady } from './helpers/store' -type MarqueeStartPoint = { x: number; y: number } | null -type MarqueeStartProbe = { - point: MarqueeStartPoint - geometry: { surfaceLeft: number; laneScrollTop: number; cardLeft: number } | null - blockers: string[] -} - const SEEDED_WORKSPACE_COUNT = 300 const MARQUEE_WORKSPACE_COUNT = 102 const MANY_LANE_COUNT = 21 @@ -315,8 +308,9 @@ test.describe('Workspace board lane virtualization', () => { test('selects the full lane across a single large marquee scroll jump', async ({ orcaPage }) => { const statusId = 'virtual-marquee' + const emptyStatusId = 'virtual-marquee-start' await orcaPage.evaluate( - ({ count, status }) => { + ({ count, emptyStatus, status }) => { const store = window.__store if (!store) { throw new Error('window.__store is not available') @@ -360,10 +354,13 @@ test.describe('Workspace board lane virtualization', () => { }) state.setWorkspaceStatuses([ { id: status, label: 'Virtual marquee' }, - ...state.workspaceStatuses.filter((entry) => entry.id !== status) + { id: emptyStatus, label: 'Marquee start' }, + ...state.workspaceStatuses.filter( + (entry) => entry.id !== status && entry.id !== emptyStatus + ) ]) }, - { count: MARQUEE_WORKSPACE_COUNT, status: statusId } + { count: MARQUEE_WORKSPACE_COUNT, emptyStatus: emptyStatusId, status: statusId } ) await orcaPage.getByRole('button', { name: 'Workspace board' }).click() @@ -373,123 +370,37 @@ test.describe('Workspace board lane virtualization', () => { const laneCards = lane.locator('[data-workspace-board-card-id]') await expect.poll(() => laneCards.count(), { timeout: 15_000 }).toBeGreaterThan(3) const laneScroll = lane.locator('[data-workspace-board-lane-scroll]') - - // Why: the marquee only starts on empty board space — the board ignores a - // pointerdown on a card, and one that lands on anything stacked over the - // board never reaches its handler at all, so the browser text-selects and no - // marquee runs. A precomputed point cannot be trusted: the strip is a few px - // of board padding, and the sheet, sidebar and lane fill all keep resizing it - // after the card-count wait. Ask the live DOM which point is actually empty, - // scanning the strip between the board's left edge and the first card. The - // scan stays in the lane's top rows because the marquee anchors its range in - // content space — starting below the first card would exclude it from the 102. - const probeMarqueeStart = (): Promise => - orcaPage.evaluate((status) => { - const IGNORED = [ - '[data-workspace-board-card-id]', - 'a', - 'button', - 'input', - 'select', - 'textarea', - '[role="button"]', - '[role="menu"]', - '[role="menuitem"]' - ].join(',') - const surface = document.querySelector( - '[data-workspace-board-selection-surface]' - ) - const laneElement = document.querySelector( - `[data-workspace-status="${status}"]` - ) - const scroll = laneElement?.querySelector('[data-workspace-board-lane-scroll]') - const card = laneElement?.querySelector('[data-workspace-board-card-id]') - if (!surface || !scroll || !card) { - return { point: null, geometry: null, blockers: ['board not mounted'] } - } - const surfaceRect = surface.getBoundingClientRect() - const scrollRect = scroll.getBoundingClientRect() - const cardRect = card.getBoundingClientRect() - const geometry = { - surfaceLeft: Math.round(surfaceRect.left), - laneScrollTop: Math.round(scrollRect.top), - cardLeft: Math.round(cardRect.left) - } - const describe = (element: Element | null): string => { - if (!element) { - return 'null' - } - const classes = - typeof element.className === 'string' && element.className.trim().length > 0 - ? `.${element.className.trim().split(/\s+/).slice(0, 3).join('.')}` - : '' - return `${element.tagName.toLowerCase()}${classes}` - } - const blockers: string[] = [] - const top = Math.round(scrollRect.top) - for (let y = top + 6; y <= top + 40; y += 6) { - for ( - let x = Math.ceil(surfaceRect.left) + 2; - x <= Math.floor(cardRect.left) - 2; - x += 2 - ) { - const element = document.elementFromPoint(x, y) - const onSurface = Boolean(element?.closest('[data-workspace-board-selection-surface]')) - const onIgnoredTarget = Boolean(element?.closest(IGNORED)) - if (onSurface && !onIgnoredTarget) { - return { point: { x, y }, geometry, blockers } - } - if (blockers.length < 6) { - blockers.push( - `(${x},${y})=${describe(element)}${onSurface ? ' onSurface' : ''}${ - onIgnoredTarget ? ' ignored' : '' - }` - ) - } - } - } - return { point: null, geometry, blockers } - }, statusId) - - // Why: one empty reading can be a frame mid-relayout. Two consecutive probes - // agreeing on the same point is what proves the strip has stopped moving. - let marqueeStart: MarqueeStartPoint = null - let probe = await probeMarqueeStart() - let previousKey = JSON.stringify(probe.point) - const probeDeadline = Date.now() + 15_000 - while (Date.now() < probeDeadline) { - await orcaPage.waitForTimeout(100) - probe = await probeMarqueeStart() - const key = JSON.stringify(probe.point) - if (probe.point && key === previousKey) { - marqueeStart = probe.point - break - } - previousKey = key - } - - // Why: a viewport that leaves the board no empty space is a layout this test - // cannot drive at all — report that instead of failing on it. - test.skip( - marqueeStart === null, - `no empty board space for the marquee start; geometry=${JSON.stringify( - probe.geometry - )} blockers=${probe.blockers.join(' | ')}` + const emptyLaneScroll = orcaPage.locator( + `[data-workspace-status="${emptyStatusId}"] [data-workspace-board-lane-scroll]` ) - const start = marqueeStart as { x: number; y: number } - const box = await laneScroll.boundingBox() - if (!box) { - throw new Error('Expected the marquee lane to have a bounding box') + const emptyBox = await emptyLaneScroll.boundingBox() + if (!box || !emptyBox) { + throw new Error('Expected the marquee and empty start lanes to have bounding boxes') } - const preflight = await probeMarqueeStart() - expect( - preflight.point, - `marquee start point (${start.x}, ${start.y}) must still be empty board space; ` + - `geometry=${JSON.stringify(preflight.geometry)} blockers=${preflight.blockers.join(' | ')}` - ).not.toBeNull() - await orcaPage.mouse.move(start.x, start.y) + // Why: the sidebar seam overlaps the board edge on CI, so start in a dedicated empty lane. + const startX = Math.round(emptyBox.x + Math.min(24, emptyBox.width / 4)) + const startY = Math.round(box.y + 12) + const startTarget = await orcaPage.evaluate( + ({ x, y }) => { + const element = document.elementFromPoint(x, y) + return { + onSurface: Boolean(element?.closest('[data-workspace-board-selection-surface]')), + onIgnoredTarget: Boolean( + element?.closest('[data-workspace-board-card-id], a, button, input, [role="button"]') + ) + } + }, + { x: startX, y: startY } + ) + expect( + startTarget, + `marquee start point (${startX}, ${startY}) must be empty board space; ` + + `emptyLane=${JSON.stringify(emptyBox)}` + ).toEqual({ onSurface: true, onIgnoredTarget: false }) + + await orcaPage.mouse.move(startX, startY) await orcaPage.mouse.down() await orcaPage.mouse.move(box.x + box.width - 18, box.y + 80, { steps: 4 })