Skip to content

Commit fbf374f

Browse files
committed
Track Responsive-Design carousel resize events
1 parent 0f34e57 commit fbf374f

3 files changed

Lines changed: 137 additions & 0 deletions

File tree

experimental/tests.mjs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ export const ExperimentalSuites = freezeSuites([
193193
new BenchmarkTestStep("ReduceWidthIn5Steps", async (page) => {
194194
const widths = [768, 704, 640, 560, 480];
195195
const MATCH_MEDIA_QUERY_BREAKPOINT = 640;
196+
const carouselResizeObservations = page.querySelector(".carousel", ["cooking-app", "main-content", "recipe-carousel"]).observeResizeEvents();
197+
// Seed the baseline width before the synchronous width changes below.
198+
await carouselResizeObservations.ready;
196199

197200
// The matchMedia query is "(max-width: 640px)"
198201
// Starting from a width > 640px, we'll only get 1 event when crossing to <= 640px
@@ -209,6 +212,12 @@ export const ExperimentalSuites = freezeSuites([
209212
}
210213

211214
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
215+
const { count } = carouselResizeObservations;
216+
carouselResizeObservations.disconnect();
217+
if (count)
218+
console.warn(`ReduceWidthIn5Steps: recipe-carousel ResizeObserver observed ${count} distinct width change(s).`);
219+
else
220+
console.warn("ReduceWidthIn5Steps: recipe-carousel ResizeObserver observed 0 distinct width changes; expected width changes during iframe resize.");
212221
}),
213222
new BenchmarkTestStep("ScrollToChatAndSendMessages", async (page) => {
214223
const cvWorkComplete = new Promise((resolve) => {
@@ -252,6 +261,9 @@ export const ExperimentalSuites = freezeSuites([
252261
new BenchmarkTestStep("IncreaseWidthIn5Steps", async (page) => {
253262
const widths = [560, 640, 704, 768, 800];
254263
const MATCH_MEDIA_QUERY_BREAKPOINT = 704;
264+
const carouselResizeObservations = page.querySelector(".carousel", ["cooking-app", "main-content", "recipe-carousel"]).observeResizeEvents();
265+
// Seed the baseline width before the synchronous width changes below.
266+
await carouselResizeObservations.ready;
255267

256268
// The matchMedia query is "(max-width: 640px)"
257269
// Starting from a width <= 640px, we'll get 1 event when crossing back to > 640px.
@@ -268,6 +280,12 @@ export const ExperimentalSuites = freezeSuites([
268280
}
269281

270282
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
283+
const { count } = carouselResizeObservations;
284+
carouselResizeObservations.disconnect();
285+
if (count)
286+
console.warn(`IncreaseWidthIn5Steps: recipe-carousel ResizeObserver observed ${count} distinct width change(s).`);
287+
else
288+
console.warn("IncreaseWidthIn5Steps: recipe-carousel ResizeObserver observed 0 distinct width changes; expected width changes during iframe resize.");
271289
}),
272290
],
273291
},

resources/benchmark-runner.mjs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,46 @@ class PageElement {
163163
this.#node.scrollIntoView(options);
164164
}
165165

166+
observeResizeEvents() {
167+
const contentWindow = this.#node.ownerDocument.defaultView;
168+
const state = {
169+
count: 0,
170+
lastWidth: null,
171+
};
172+
let markReady;
173+
// Resolves on the first delivery so callers can seed a baseline before resizing.
174+
const ready = new Promise((resolve) => {
175+
markReady = resolve;
176+
});
177+
const observer = new contentWindow.ResizeObserver((entries) => {
178+
for (const entry of entries) {
179+
const contentBoxSize = entry.contentBoxSize;
180+
const inlineSize = Array.isArray(contentBoxSize) ? contentBoxSize[0]?.inlineSize : contentBoxSize?.inlineSize;
181+
const width = inlineSize ?? entry.contentRect.width;
182+
// The first delivery seeds the baseline width without counting it as a change.
183+
if (state.lastWidth === null) {
184+
state.lastWidth = width;
185+
markReady();
186+
continue;
187+
}
188+
if (width === state.lastWidth)
189+
continue;
190+
state.count++;
191+
state.lastWidth = width;
192+
}
193+
});
194+
observer.observe(this.#node);
195+
return {
196+
ready,
197+
get count() {
198+
return state.count;
199+
},
200+
disconnect() {
201+
observer.disconnect();
202+
},
203+
};
204+
}
205+
166206
dispatchEvent(eventName, options = NATIVE_OPTIONS, eventType = Event) {
167207
if (eventName === "submit")
168208
// FIXME FireFox doesn't like `new Event('submit')

tests/unittests/benchmark-runner.mjs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,82 @@ describe("BenchmarkRunner", () => {
257257
});
258258
});
259259
});
260+
261+
describe("PageElement", () => {
262+
describe("observeResizeEvents", () => {
263+
async function withPageElement(configureFrame, callback) {
264+
let fixtureNode;
265+
let result;
266+
const runner = new BenchmarkRunner(
267+
[
268+
{
269+
name: "PageElement fixture",
270+
enabled: true,
271+
url: "about:blank",
272+
async prepare(page) {
273+
result = await callback(page.getElementById("resize-target"), fixtureNode);
274+
},
275+
tests: [],
276+
},
277+
],
278+
{}
279+
);
280+
sinon.stub(SuiteRunner.prototype, "_loadFrame").callsFake(async function () {
281+
fixtureNode = configureFrame(this.frame);
282+
});
283+
sinon.stub(SuiteRunner.prototype, "_runSuite").callsFake(async () => {});
284+
await runner.runAllSuites();
285+
return result;
286+
}
287+
288+
// A fake ResizeObserver drives deliveries synchronously for exact-count assertions.
289+
async function createTracker() {
290+
let deliver;
291+
class FakeResizeObserver {
292+
constructor(callback) {
293+
deliver = (...widths) => callback(widths.map((width) => ({ contentBoxSize: [{ inlineSize: width }] })));
294+
}
295+
observe() {}
296+
disconnect() {}
297+
}
298+
return withPageElement(
299+
(frame) => {
300+
Object.defineProperty(frame.contentWindow, "ResizeObserver", { configurable: true, value: FakeResizeObserver });
301+
const node = frame.contentDocument.createElement("div");
302+
node.id = "resize-target";
303+
frame.contentDocument.body.appendChild(node);
304+
return node;
305+
},
306+
(element) => ({
307+
resizeEvents: element.observeResizeEvents(),
308+
deliver: (...widths) => deliver(...widths),
309+
})
310+
);
311+
}
312+
313+
it("treats the first delivery as the baseline without counting it", async () => {
314+
const { resizeEvents, deliver } = await createTracker();
315+
deliver(200);
316+
await resizeEvents.ready;
317+
expect(resizeEvents.count).to.equal(0);
318+
});
319+
320+
it("counts each distinct width change exactly once", async () => {
321+
const { resizeEvents, deliver } = await createTracker();
322+
deliver(200); // seed
323+
deliver(300);
324+
deliver(400);
325+
deliver(500);
326+
expect(resizeEvents.count).to.equal(3);
327+
});
328+
329+
it("does not count deliveries that report the same width", async () => {
330+
const { resizeEvents, deliver } = await createTracker();
331+
deliver(200); // seed
332+
deliver(300);
333+
deliver(300);
334+
deliver(300);
335+
expect(resizeEvents.count).to.equal(1);
336+
});
337+
});
338+
});

0 commit comments

Comments
 (0)