diff options
Diffstat (limited to 'testing/web-platform/tests/layout-instability/resources')
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"); +}; |