From a69bd09b01eb4f47d0819f0b340c4526ff2bdfae Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Wed, 26 Aug 2026 16:30:28 +1000 Subject: [PATCH] test(desktop): stabilize chip-wrap measurement against row re-render The composer Buzz chip smoke test forced the sent message row's container to 220px with a one-shot inline style, then measured line fragments in a separate evaluate. When the relay echo re-creates the optimistic row between those steps, the style is lost and the chip never wraps, failing CI (all three attempts on Desktop Smoke E2E shard 3). Apply the width and measure atomically inside an expect.poll, and re-apply it when reading fragment rects for the tooltip checks, so a late re-render can no longer strand the measurement. Co-Authored-By: Claude Fable 5 Signed-off-by: Matt Toohey --- desktop/tests/e2e/navigation.spec.ts | 66 +++++++++++++++------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/desktop/tests/e2e/navigation.spec.ts b/desktop/tests/e2e/navigation.spec.ts index 90ae8cc64f..9f05fc96d6 100644 --- a/desktop/tests/e2e/navigation.spec.ts +++ b/desktop/tests/e2e/navigation.spec.ts @@ -602,43 +602,47 @@ test("composer Buzz chip labels wrap without orphaning their icons", async ({ }); await expect(sentChip).toBeVisible(); await expect(sentChip).toHaveClass(/wrapping-inline-chip/); - await sentChip.evaluate((element) => { - const container = element.parentElement; - if (container) container.style.width = "220px"; - }); - const fragmentMetrics = await sentChip.evaluate((element) => { - const chipStyle = getComputedStyle(element); - const rects = Array.from(element.getClientRects(), (rect) => ({ - bottom: rect.bottom, - height: rect.height, - left: rect.left, - right: rect.right, - top: rect.top, - width: rect.width, - })).filter((rect) => rect.width > 0 && rect.height > 0); - const fragmentTops = Array.from( - new Set(rects.map((rect) => Math.round(rect.top))), - ).sort((a, b) => a - b); - return { - boxDecorationBreak: - chipStyle.getPropertyValue("box-decoration-break") || - chipStyle.getPropertyValue("-webkit-box-decoration-break"), - fragmentStep: - fragmentTops.length > 1 ? fragmentTops[1] - fragmentTops[0] : null, - lineHeight: Number.parseFloat(chipStyle.lineHeight), - rects, - }; - }); - expect(fragmentMetrics.boxDecorationBreak).toBe("clone"); - expect(fragmentMetrics.lineHeight).toBe(22); - expect(fragmentMetrics.fragmentStep).toBe(22); - expect(fragmentMetrics.rects.length).toBeGreaterThanOrEqual(2); + // The optimistic message row can be re-created when the relay echo lands, + // dropping any inline style applied before the swap — apply the width and + // measure in one evaluate, polling until the chip settles into wrapped + // fragments with the expected cloned-box metrics. + await expect + .poll(() => + sentChip.evaluate((element) => { + const container = element.parentElement; + if (container) container.style.width = "220px"; + const chipStyle = getComputedStyle(element); + const rects = Array.from(element.getClientRects()).filter( + (rect) => rect.width > 0 && rect.height > 0, + ); + const fragmentTops = Array.from( + new Set(rects.map((rect) => Math.round(rect.top))), + ).sort((a, b) => a - b); + return { + boxDecorationBreak: + chipStyle.getPropertyValue("box-decoration-break") || + chipStyle.getPropertyValue("-webkit-box-decoration-break"), + fragmentStep: + fragmentTops.length > 1 ? fragmentTops[1] - fragmentTops[0] : null, + lineHeight: Number.parseFloat(chipStyle.lineHeight), + wrapped: rects.length >= 2, + }; + }), + ) + .toMatchObject({ + boxDecorationBreak: "clone", + fragmentStep: 22, + lineHeight: 22, + wrapped: true, + }); const tooltip = page.getByRole("tooltip"); await sentChip.focus(); await expect(tooltip).toBeVisible(); const positionOverFragment = async (index: number) => { const fragment = await sentChip.evaluate((element, fragmentIndex) => { + const container = element.parentElement; + if (container) container.style.width = "220px"; const rects = Array.from(element.getClientRects()).filter( (rect) => rect.width > 0 && rect.height > 0, );