test(web): share one sweep and settle the control's resize
Review follow-ups on the column-overflow scenario: - Memoize the sweep so the golden and the assertions consume the same readings, which is what its contract already claimed; two runs could disagree if a resize settled differently between them. - Settle the column width before the mutation control measures. The test arrives from 1680 alone and from the sweep's 600 in a full run, and the frame eases its column tracks, so an immediate read can report the previous viewport's bleed. - Name the wheel delta, assert the bleed stays inside it, and compare the travelled distance rounded: a clamp or a sub-pixel would otherwise read as a broken fix.
This commit is contained in:
@@ -49,6 +49,8 @@ const MODE = webSnapshotMode()
|
||||
const WIDTHS = [1680, 1200, 1000, 800, 600]
|
||||
/** Element id of the mutation control's injected sheet, so the test can take it back out. */
|
||||
const CONTROL_STYLE_ID = 'dsh-column-overflow-control'
|
||||
/** Horizontal wheel delta per gesture; must exceed the widest bleed the sweep can produce. */
|
||||
const WHEEL_DELTA = 300
|
||||
|
||||
/** One viewport stop: whether the glow bleeds past the column, and whether that bleed scrolls. */
|
||||
interface ColumnMetrics {
|
||||
@@ -124,7 +126,7 @@ async function wheelHorizontally(page: Page): Promise<number> {
|
||||
return { x: box.left + box.width / 2, y: box.top + 60 }
|
||||
})
|
||||
await page.mouse.move(origin.x, origin.y)
|
||||
await page.mouse.wheel(300, 0)
|
||||
await page.mouse.wheel(WHEEL_DELTA, 0)
|
||||
// A fixed settle, then two frames. Polling for a settled value cannot be
|
||||
// used here — the value under test is 0, which a poll starting at 0 accepts
|
||||
// before the gesture has had any chance to move it — so the wait is
|
||||
@@ -190,28 +192,45 @@ describe('web e2e: the conversation column scrolls on one axis', () => {
|
||||
})
|
||||
|
||||
/**
|
||||
* Sweep the stops once and hand the readings to every assertion below, so
|
||||
* the golden and the assertions describe the same measurement rather than
|
||||
* two runs that could disagree.
|
||||
* Resize to a viewport and read the column once its width stops moving.
|
||||
*
|
||||
* The glow rides the hero box, which rides the column, and the frame eases
|
||||
* its column tracks over `--ds-transition-duration-slow`: reading straight
|
||||
* after a resize can report the previous viewport's relation, or a width
|
||||
* caught mid-transition.
|
||||
* @param width - viewport width to settle at.
|
||||
* @returns the column's readings at that width.
|
||||
*/
|
||||
const settleAt = async (width: number): Promise<ColumnMetrics> => {
|
||||
await page.setViewportSize({ width, height: 900 })
|
||||
let previous = -1
|
||||
await expect.poll(async () => {
|
||||
const current = (await measureColumn(page, width)).columnWidth
|
||||
const settled = current === previous
|
||||
previous = current
|
||||
return settled
|
||||
}, { timeout: 10_000 }).toBe(true)
|
||||
return measureColumn(page, width)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sweep the stops once per run and hand the SAME readings to every assertion
|
||||
* below, so the golden and the assertions describe one measurement instead of
|
||||
* two runs that could disagree. Memoized rather than re-run per test: the
|
||||
* gestures below move the viewport, and a second sweep would be a second
|
||||
* chance for a resize to settle differently.
|
||||
* @returns the stops in {@link WIDTHS} order.
|
||||
*/
|
||||
const sweep = async (): Promise<ColumnStop[]> => {
|
||||
const stops: ColumnStop[] = []
|
||||
for (const width of WIDTHS) {
|
||||
await page.setViewportSize({ width, height: 900 })
|
||||
// The glow rides the hero box, which rides the column, and the column's
|
||||
// track animates: settle on a column width that stops moving, or a stop
|
||||
// gets read mid-transition and reports the previous viewport's relation.
|
||||
let previous = -1
|
||||
await expect.poll(async () => {
|
||||
const current = (await measureColumn(page, width)).columnWidth
|
||||
const settled = current === previous
|
||||
previous = current
|
||||
return settled
|
||||
}, { timeout: 10_000 }).toBe(true)
|
||||
stops.push({ ...await measureColumn(page, width), scrollLeftAfterWheel: await wheelHorizontally(page) })
|
||||
}
|
||||
return stops
|
||||
let swept: Promise<ColumnStop[]> | undefined
|
||||
const sweep = (): Promise<ColumnStop[]> => {
|
||||
swept ??= (async () => {
|
||||
const stops: ColumnStop[] = []
|
||||
for (const width of WIDTHS) {
|
||||
stops.push({ ...await settleAt(width), scrollLeftAfterWheel: await wheelHorizontally(page) })
|
||||
}
|
||||
return stops
|
||||
})()
|
||||
return swept
|
||||
}
|
||||
|
||||
it('never scrolls horizontally, at any width the glow bleeds past', async () => {
|
||||
@@ -242,7 +261,10 @@ describe('web e2e: the conversation column scrolls on one axis', () => {
|
||||
// that a one-axis scroller computes to `auto` — and shows the same gesture,
|
||||
// at the same timing, carrying the column to the full bleed. Without it a
|
||||
// `scrollLeft` of 0 could equally mean the wheel never arrived.
|
||||
await page.setViewportSize({ width: 1200, height: 900 })
|
||||
// Settle the resize first: this test runs at 1680 on its own and after the
|
||||
// sweep's 600 in a full run, and an unsettled column reports the previous
|
||||
// viewport's bleed.
|
||||
await settleAt(1200)
|
||||
// Injected with an id rather than through `addStyleTag`, so the teardown
|
||||
// below can take the sheet out again by selector: it must not outlive this
|
||||
// test, or the golden ends up reading the control.
|
||||
@@ -255,8 +277,16 @@ describe('web e2e: the conversation column scrolls on one axis', () => {
|
||||
try {
|
||||
const before = await measureColumn(page, 1200)
|
||||
expect(before.overflowX).toBe('auto')
|
||||
expect(await wheelHorizontally(page)).toBe(before.bleedRange)
|
||||
expect(before.bleedRange).toBeGreaterThan(0)
|
||||
// The gesture has to be able to reach the far edge, or the equality below
|
||||
// would fail on the clamp and read as a broken fix. Stated as its own
|
||||
// assertion so that failure names itself.
|
||||
expect(before.bleedRange).toBeLessThan(WHEEL_DELTA)
|
||||
// Rounded: `scrollLeft` is fractional under a fractional layout while
|
||||
// `scrollWidth - clientWidth` is integral, and the claim is that the
|
||||
// column travelled the whole bleed — not that two engines agree on a
|
||||
// sub-pixel.
|
||||
expect(Math.round(await wheelHorizontally(page))).toBe(before.bleedRange)
|
||||
} finally {
|
||||
await page.evaluate((id: string) => {
|
||||
document.getElementById(id)?.remove()
|
||||
|
||||
Reference in New Issue
Block a user