Skip to content

Commit

Permalink
Implement TestRendered event for reftests (web-platform-tests#20197)
Browse files Browse the repository at this point in the history
This is described in RFC #34. The TestRendered event is emitted for
reftests with a `reftest-wait` class on the root at the point at which
the screenshot would occurs if that class was not present. It provides
a hook for test authors to dynamically modify the DOM after the
initial layout and paint are complete.
  • Loading branch information
jgraham authored Nov 26, 2019
1 parent 716382f commit 7b20b28
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 49 deletions.
35 changes: 25 additions & 10 deletions docs/writing-tests/reftests.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,31 @@ leaf nodes.)

## Controlling When Comparison Occurs

By default reftest screenshots are taken after the `load` event has
fired, and web fonts (if any) are loaded. In some cases it is
necessary to delay the screenshot later than this, for example because
some DOM manipulation is required to set up the desired test
conditions. To enable this, the test may have a `class="reftest-wait"`
attribute specified on the root element. This will cause the
screenshot to be delayed until the `load` event has fired and the
`reftest-wait` class has been removed from the root element. Note that
in neither case is exact timing of the screenshot guaranteed: it is
only guaranteed to be after those events.
By default, reftest screenshots are taken after the following
conditions are met:

* The `load` event has fired
* Web fonts (if any) are loaded
* Pending paints have completed

In some cases it is necessary to delay the screenshot later than this,
for example because some DOM manipulation is required to set up the
desired test conditions. To enable this, the test may have a
`class="reftest-wait"` attribute specified on the root element. In
this case the harness will run the following sequence of steps:

* Wait for the `load` event to fire and fonts to load.
* Wait for pending paints to complete.
* Fire an event named `TestRendered` at the root element, with the
`bubbles` attribute set to true.
* Wait for the `reftest-wait` class to be removed from the root
element.
* Wait for pending paints to complete.
* Screenshot the viewport.

The `TestRendered` event provides a hook for tests to make
modifications to the test document that are not batched into the
initial layout/paint.

## Fuzzy Matching

Expand Down
14 changes: 14 additions & 0 deletions infrastructure/reftest/reftest_wait_TestRendered.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<html class="reftest-wait">
<title>Test with reftest-wait</title>
<link rel=match href=green.html>
<style>
:root {background-color:red}
</style>
<script>
document.addEventListener("TestRendered",
function() {
document.documentElement.style.backgroundColor = "green";
document.documentElement.className = "";
});
</script>
</html>
2 changes: 1 addition & 1 deletion tools/wptrunner/wptrunner/executors/executormarionette.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ def __init__(self, browser, server_config, timeout_multiplier=1,

with open(os.path.join(here, "reftest.js")) as f:
self.script = f.read()
with open(os.path.join(here, "reftest-wait_marionette.js")) as f:
with open(os.path.join(here, "reftest-wait_webdriver.js")) as f:
self.wait_script = f.read()

def setup(self, runner):
Expand Down
19 changes: 0 additions & 19 deletions tools/wptrunner/wptrunner/executors/reftest-wait_marionette.js

This file was deleted.

48 changes: 29 additions & 19 deletions tools/wptrunner/wptrunner/executors/reftest-wait_webdriver.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
var callback = arguments[arguments.length - 1];
var observer = null;
var root = document.documentElement;

function root_wait() {
if (!root.classList.contains("reftest-wait")) {
observer.disconnect();

if (Document.prototype.hasOwnProperty("fonts")) {
document.fonts.ready.then(ready_for_screenshot);
} else {
// This might take the screenshot too early, depending on whether the
// load event is blocked on fonts being loaded. See:
// https://github.com/w3c/csswg-drafts/issues/1088
ready_for_screenshot();
}
function wait_load() {
if (Document.prototype.hasOwnProperty("fonts")) {
document.fonts.ready.then(wait_paints);
} else {
// This might take the screenshot too early, depending on whether the
// load event is blocked on fonts being loaded. See:
// https://github.com/w3c/csswg-drafts/issues/1088
wait_paints();
}
}

function ready_for_screenshot() {

function wait_paints() {
// As of 2017-04-05, the Chromium web browser exhibits a rendering bug
// (https://bugs.chromium.org/p/chromium/issues/detail?id=708757) that
// produces instability during screen capture. The following use of
Expand All @@ -27,18 +26,29 @@ function ready_for_screenshot() {

requestAnimationFrame(function() {
requestAnimationFrame(function() {
callback();
screenshot_if_ready();
});
});
}

var root = document.documentElement;
var observer = new MutationObserver(root_wait);
function screenshot_if_ready() {
if (root.classList.contains("reftest-wait") &&
observer === null) {
observer = new MutationObserver(wait_paints);
observer.observe(root, {attributes: true});
var event = new Event("TestRendered", {bubbles: true});
root.dispatchEvent(event);
return;
}
if (observer !== null) {
observer.disconnect();
}
callback();
}

observer.observe(root, {attributes: true});

if (document.readyState != "complete") {
addEventListener('load', root_wait);
addEventListener('load', wait_load);
} else {
root_wait();
wait_load();
}

0 comments on commit 7b20b28

Please sign in to comment.