summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/layout-instability/resources
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/layout-instability/resources')
-rw-r--r--testing/web-platform/tests/layout-instability/resources/slow-image.py6
-rw-r--r--testing/web-platform/tests/layout-instability/resources/test-adapter.js5
-rw-r--r--testing/web-platform/tests/layout-instability/resources/util.js97
3 files changed, 108 insertions, 0 deletions
diff --git a/testing/web-platform/tests/layout-instability/resources/slow-image.py b/testing/web-platform/tests/layout-instability/resources/slow-image.py
new file mode 100644
index 0000000000..d9f09b8bca
--- /dev/null
+++ b/testing/web-platform/tests/layout-instability/resources/slow-image.py
@@ -0,0 +1,6 @@
+import time
+
+def main(request, response):
+ # Sleep for 3s to delay onload.
+ time.sleep(3)
+ return [], b""
diff --git a/testing/web-platform/tests/layout-instability/resources/test-adapter.js b/testing/web-platform/tests/layout-instability/resources/test-adapter.js
new file mode 100644
index 0000000000..3272790f7a
--- /dev/null
+++ b/testing/web-platform/tests/layout-instability/resources/test-adapter.js
@@ -0,0 +1,5 @@
+// Abstracts expectations for reuse in different test frameworks.
+
+cls_expect = (watcher, expectation) => {
+ watcher.checkExpectation(expectation);
+};
diff --git a/testing/web-platform/tests/layout-instability/resources/util.js b/testing/web-platform/tests/layout-instability/resources/util.js
new file mode 100644
index 0000000000..597e2d7d84
--- /dev/null
+++ b/testing/web-platform/tests/layout-instability/resources/util.js
@@ -0,0 +1,97 @@
+// Utilities for Layout Instability tests.
+
+// Returns a promise that is resolved when the specified number of animation
+// frames has occurred.
+waitForAnimationFrames = frameCount => {
+ return new Promise(resolve => {
+ const handleFrame = () => {
+ if (--frameCount <= 0)
+ resolve();
+ else
+ requestAnimationFrame(handleFrame);
+ };
+ requestAnimationFrame(handleFrame);
+ });
+};
+
+// Returns a promise that is resolved when the next animation frame occurs.
+waitForAnimationFrame = () => waitForAnimationFrames(1);
+
+// Helper to compute an expected layout shift score based on an expected impact
+// region and max move distance for a particular animation frame.
+computeExpectedScore = (impactRegionArea, moveDistance) => {
+ const docElement = document.documentElement;
+
+ const viewWidth = docElement.clientWidth;
+ const viewHeight = docElement.clientHeight;
+
+ const viewArea = viewWidth * viewHeight;
+ const viewMaxDim = Math.max(viewWidth, viewHeight);
+
+ const impactFraction = impactRegionArea / viewArea;
+ const distanceFraction = moveDistance / viewMaxDim;
+
+ return impactFraction * distanceFraction;
+};
+
+// An list to record all the entries with startTime and score.
+let watcher_entry_record = [];
+
+// An object that tracks the document cumulative layout shift score.
+// Usage:
+//
+// const watcher = new ScoreWatcher;
+// ...
+// assert_equals(watcher.score, expectedScore);
+//
+// The score reflects only layout shifts that occur after the ScoreWatcher is
+// constructed.
+ScoreWatcher = function() {
+ if (PerformanceObserver.supportedEntryTypes.indexOf("layout-shift") == -1)
+ throw new Error("Layout Instability API not supported");
+ this.score = 0;
+ this.scoreWithInputExclusion = 0;
+ const resetPromise = () => {
+ this.promise = new Promise(resolve => {
+ this.resolve = () => {
+ resetPromise();
+ resolve();
+ }
+ });
+ };
+ resetPromise();
+ const observer = new PerformanceObserver(list => {
+ list.getEntries().forEach(entry => {
+ this.lastEntry = entry;
+ this.score += entry.value;
+ watcher_entry_record.push({startTime: entry.startTime, score: entry.value, hadRecentInput : entry.hadRecentInput});
+ if (!entry.hadRecentInput)
+ this.scoreWithInputExclusion += entry.value;
+ this.resolve();
+ });
+ });
+ observer.observe({entryTypes: ['layout-shift']});
+};
+
+ScoreWatcher.prototype.checkExpectation = function(expectation) {
+ if (expectation.score != undefined)
+ assert_equals(this.score, expectation.score);
+ if (expectation.sources)
+ check_sources(expectation.sources, this.lastEntry.sources);
+};
+
+ScoreWatcher.prototype.get_entry_record = function() {
+ return watcher_entry_record;
+};
+
+check_sources = (expect_sources, actual_sources) => {
+ assert_equals(expect_sources.length, actual_sources.length);
+ let rect_match = (e, a) =>
+ e[0] == a.x && e[1] == a.y && e[2] == a.width && e[3] == a.height;
+ let match = e => a =>
+ e.node === a.node &&
+ rect_match(e.previousRect, a.previousRect) &&
+ rect_match(e.currentRect, a.currentRect);
+ for (let e of expect_sources)
+ assert_true(actual_sources.some(match(e)), e.node + " not found");
+};