Skip to content

Commit 564579a

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

3 files changed

Lines changed: 140 additions & 0 deletions

File tree

experimental/tests.mjs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ import { getTodoText } from "../resources/shared/translations.mjs";
33
import { numberOfItemsToAdd } from "../resources/shared/todomvc-utils.mjs";
44
import { freezeSuites } from "../resources/suites-helper.mjs";
55

6+
function observeRecipeCarouselResizeObservations(page) {
7+
return page.querySelector(".carousel", ["cooking-app", "main-content", "recipe-carousel"]).observeResizeEvents();
8+
}
9+
10+
function reportRecipeCarouselResizeObservations(stepName, resizeObservations) {
11+
const { count } = resizeObservations;
12+
resizeObservations.disconnect();
13+
if (count)
14+
console.warn(`${stepName}: recipe-carousel ResizeObserver observed ${count} distinct width change(s).`);
15+
else
16+
console.warn(`${stepName}: recipe-carousel ResizeObserver observed 0 distinct width changes; expected width changes during iframe resize.`);
17+
}
18+
619
export const ExperimentalSuites = freezeSuites([
720
{
821
name: "TodoMVC-LocalStorage",
@@ -193,6 +206,9 @@ export const ExperimentalSuites = freezeSuites([
193206
new BenchmarkTestStep("ReduceWidthIn5Steps", async (page) => {
194207
const widths = [768, 704, 640, 560, 480];
195208
const MATCH_MEDIA_QUERY_BREAKPOINT = 640;
209+
const carouselResizeObservations = observeRecipeCarouselResizeObservations(page);
210+
// Seed the baseline width before the synchronous width changes below.
211+
await carouselResizeObservations.ready;
196212

197213
// The matchMedia query is "(max-width: 640px)"
198214
// Starting from a width > 640px, we'll only get 1 event when crossing to <= 640px
@@ -209,6 +225,7 @@ export const ExperimentalSuites = freezeSuites([
209225
}
210226

211227
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
228+
reportRecipeCarouselResizeObservations("ReduceWidthIn5Steps", carouselResizeObservations);
212229
}),
213230
new BenchmarkTestStep("ScrollToChatAndSendMessages", async (page) => {
214231
const cvWorkComplete = new Promise((resolve) => {
@@ -252,6 +269,9 @@ export const ExperimentalSuites = freezeSuites([
252269
new BenchmarkTestStep("IncreaseWidthIn5Steps", async (page) => {
253270
const widths = [560, 640, 704, 768, 800];
254271
const MATCH_MEDIA_QUERY_BREAKPOINT = 704;
272+
const carouselResizeObservations = observeRecipeCarouselResizeObservations(page);
273+
// Seed the baseline width before the synchronous width changes below.
274+
await carouselResizeObservations.ready;
255275

256276
// The matchMedia query is "(max-width: 640px)"
257277
// Starting from a width <= 640px, we'll get 1 event when crossing back to > 640px.
@@ -268,6 +288,7 @@ export const ExperimentalSuites = freezeSuites([
268288
}
269289

270290
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
291+
reportRecipeCarouselResizeObservations("IncreaseWidthIn5Steps", carouselResizeObservations);
271292
}),
272293
],
273294
},

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)