From fbf374f991e3aa00a1a5ca2b4615a7fcbe897a4f Mon Sep 17 00:00:00 2001 From: issack john Date: Tue, 2 Jun 2026 13:22:19 -0700 Subject: [PATCH] Track Responsive-Design carousel resize events --- experimental/tests.mjs | 18 +++++++ resources/benchmark-runner.mjs | 40 ++++++++++++++ tests/unittests/benchmark-runner.mjs | 79 ++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) diff --git a/experimental/tests.mjs b/experimental/tests.mjs index 136fb7cd5..8a0b184a5 100644 --- a/experimental/tests.mjs +++ b/experimental/tests.mjs @@ -193,6 +193,9 @@ export const ExperimentalSuites = freezeSuites([ new BenchmarkTestStep("ReduceWidthIn5Steps", async (page) => { const widths = [768, 704, 640, 560, 480]; const MATCH_MEDIA_QUERY_BREAKPOINT = 640; + const carouselResizeObservations = page.querySelector(".carousel", ["cooking-app", "main-content", "recipe-carousel"]).observeResizeEvents(); + // Seed the baseline width before the synchronous width changes below. + await carouselResizeObservations.ready; // The matchMedia query is "(max-width: 640px)" // Starting from a width > 640px, we'll only get 1 event when crossing to <= 640px @@ -209,6 +212,12 @@ export const ExperimentalSuites = freezeSuites([ } await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))); + const { count } = carouselResizeObservations; + carouselResizeObservations.disconnect(); + if (count) + console.warn(`ReduceWidthIn5Steps: recipe-carousel ResizeObserver observed ${count} distinct width change(s).`); + else + console.warn("ReduceWidthIn5Steps: recipe-carousel ResizeObserver observed 0 distinct width changes; expected width changes during iframe resize."); }), new BenchmarkTestStep("ScrollToChatAndSendMessages", async (page) => { const cvWorkComplete = new Promise((resolve) => { @@ -252,6 +261,9 @@ export const ExperimentalSuites = freezeSuites([ new BenchmarkTestStep("IncreaseWidthIn5Steps", async (page) => { const widths = [560, 640, 704, 768, 800]; const MATCH_MEDIA_QUERY_BREAKPOINT = 704; + const carouselResizeObservations = page.querySelector(".carousel", ["cooking-app", "main-content", "recipe-carousel"]).observeResizeEvents(); + // Seed the baseline width before the synchronous width changes below. + await carouselResizeObservations.ready; // The matchMedia query is "(max-width: 640px)" // Starting from a width <= 640px, we'll get 1 event when crossing back to > 640px. @@ -268,6 +280,12 @@ export const ExperimentalSuites = freezeSuites([ } await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))); + const { count } = carouselResizeObservations; + carouselResizeObservations.disconnect(); + if (count) + console.warn(`IncreaseWidthIn5Steps: recipe-carousel ResizeObserver observed ${count} distinct width change(s).`); + else + console.warn("IncreaseWidthIn5Steps: recipe-carousel ResizeObserver observed 0 distinct width changes; expected width changes during iframe resize."); }), ], }, diff --git a/resources/benchmark-runner.mjs b/resources/benchmark-runner.mjs index 6a77a1cfd..e6724f5b4 100644 --- a/resources/benchmark-runner.mjs +++ b/resources/benchmark-runner.mjs @@ -163,6 +163,46 @@ class PageElement { this.#node.scrollIntoView(options); } + observeResizeEvents() { + const contentWindow = this.#node.ownerDocument.defaultView; + const state = { + count: 0, + lastWidth: null, + }; + let markReady; + // Resolves on the first delivery so callers can seed a baseline before resizing. + const ready = new Promise((resolve) => { + markReady = resolve; + }); + const observer = new contentWindow.ResizeObserver((entries) => { + for (const entry of entries) { + const contentBoxSize = entry.contentBoxSize; + const inlineSize = Array.isArray(contentBoxSize) ? contentBoxSize[0]?.inlineSize : contentBoxSize?.inlineSize; + const width = inlineSize ?? entry.contentRect.width; + // The first delivery seeds the baseline width without counting it as a change. + if (state.lastWidth === null) { + state.lastWidth = width; + markReady(); + continue; + } + if (width === state.lastWidth) + continue; + state.count++; + state.lastWidth = width; + } + }); + observer.observe(this.#node); + return { + ready, + get count() { + return state.count; + }, + disconnect() { + observer.disconnect(); + }, + }; + } + dispatchEvent(eventName, options = NATIVE_OPTIONS, eventType = Event) { if (eventName === "submit") // FIXME FireFox doesn't like `new Event('submit') diff --git a/tests/unittests/benchmark-runner.mjs b/tests/unittests/benchmark-runner.mjs index 16d765e2a..6fc574736 100644 --- a/tests/unittests/benchmark-runner.mjs +++ b/tests/unittests/benchmark-runner.mjs @@ -257,3 +257,82 @@ describe("BenchmarkRunner", () => { }); }); }); + +describe("PageElement", () => { + describe("observeResizeEvents", () => { + async function withPageElement(configureFrame, callback) { + let fixtureNode; + let result; + const runner = new BenchmarkRunner( + [ + { + name: "PageElement fixture", + enabled: true, + url: "about:blank", + async prepare(page) { + result = await callback(page.getElementById("resize-target"), fixtureNode); + }, + tests: [], + }, + ], + {} + ); + sinon.stub(SuiteRunner.prototype, "_loadFrame").callsFake(async function () { + fixtureNode = configureFrame(this.frame); + }); + sinon.stub(SuiteRunner.prototype, "_runSuite").callsFake(async () => {}); + await runner.runAllSuites(); + return result; + } + + // A fake ResizeObserver drives deliveries synchronously for exact-count assertions. + async function createTracker() { + let deliver; + class FakeResizeObserver { + constructor(callback) { + deliver = (...widths) => callback(widths.map((width) => ({ contentBoxSize: [{ inlineSize: width }] }))); + } + observe() {} + disconnect() {} + } + return withPageElement( + (frame) => { + Object.defineProperty(frame.contentWindow, "ResizeObserver", { configurable: true, value: FakeResizeObserver }); + const node = frame.contentDocument.createElement("div"); + node.id = "resize-target"; + frame.contentDocument.body.appendChild(node); + return node; + }, + (element) => ({ + resizeEvents: element.observeResizeEvents(), + deliver: (...widths) => deliver(...widths), + }) + ); + } + + it("treats the first delivery as the baseline without counting it", async () => { + const { resizeEvents, deliver } = await createTracker(); + deliver(200); + await resizeEvents.ready; + expect(resizeEvents.count).to.equal(0); + }); + + it("counts each distinct width change exactly once", async () => { + const { resizeEvents, deliver } = await createTracker(); + deliver(200); // seed + deliver(300); + deliver(400); + deliver(500); + expect(resizeEvents.count).to.equal(3); + }); + + it("does not count deliveries that report the same width", async () => { + const { resizeEvents, deliver } = await createTracker(); + deliver(200); // seed + deliver(300); + deliver(300); + deliver(300); + expect(resizeEvents.count).to.equal(1); + }); + }); +});