Skip to content

Commit

Permalink
Implement testdriver features (RFC-214) (#49122)
Browse files Browse the repository at this point in the history
* Add support for testdriver features

# Conflicts:
#	lint.ignore
#	tools/manifest/item.py

* Enable BiDi for Chrome

* lint rules

* lint rules

* indents

* Update simulate_adapter.https.html.ini

* check path correctly

* Address PR comments
  • Loading branch information
sadym-chromium authored Dec 19, 2024
1 parent c433606 commit f3acbb2
Show file tree
Hide file tree
Showing 22 changed files with 591 additions and 27 deletions.
12 changes: 10 additions & 2 deletions docs/writing-tests/testdriver-extension-tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,16 @@ We will leave this unimplemented and override it in another file. Lets do that n
#### WebDriver BiDi
For commands using WebDriver BiDi, add the methods to `window.test_driver.bidi`. Parameters are passed as a single object `params`.
<!-- TODO: add an example link once a first bidi command (probably `test_driver.bidi.permissions.set_permission`) is implemented.-->
For commands using WebDriver BiDi, add the methods to `test_driver.bidi`. Parameters are passed as a single object `params`. For example [`test_driver.bidi.permissions.set_permission`](https://github.com/web-platform-tests/wpt/blob/5ec8ba6d68f27d49a056cbf940e3bc9a8324c538/resources/testdriver.js#L183).
Before calling `test_driver_internal` method, assert the `bidi` testdriver feature is enabled.
```javascript
set_permission: function (params) {
assertBidiIsEnabled();
return window.test_driver_internal.bidi.permissions.set_permission(
params);
}
```
### [tools/wptrunner/wptrunner/testdriver-extra.js](https://github.com/web-platform-tests/wpt/blob/master/tools/wptrunner/wptrunner/testdriver-extra.js)
Expand Down
14 changes: 14 additions & 0 deletions docs/writing-tests/testdriver.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ document when using testdriver from a different context):
The api in `test_driver.bidi` provides access to the
[WebDriver BiDi](https://w3c.github.io/webdriver-bidi) protocol.

### Markup ###

To use WebDriver BiDi, enable the `bidi` feature in `testdriver.js` by adding the
`feature=bidi` query string parameter. Details are in [RFC 214: Add testdriver features](https://github.com/web-platform-tests/rfcs/blob/master/rfcs/testdriver-features.md).
```html
<script src="/resources/testdriver.js?feature=bidi"></script>
```

```javascript
// META: script=/resources/testdriver.js?feature=bidi
```

[Example](https://github.com/web-platform-tests/wpt/blob/aae46926b1fdccd460e1c6eaaf01ca20b941fbce/infrastructure/webdriver/bidi/subscription.html#L6).

### Context ###

A WebDriver BiDi "browsing context" is equivalent to an
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
disabled: https://github.com/web-platform-tests/wpt/issues/47544
disabled:
if product != "chrome": @True
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
disabled: https://github.com/web-platform-tests/wpt/issues/47544
disabled:
if product != "chrome": @True
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
disabled: https://github.com/web-platform-tests/wpt/issues/47544
disabled:
if product != "chrome": @True
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
disabled:
if product != "chrome": @True
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<title>TestDriver bidi.bluetooth.simulate_adapter method</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver.js?feature=bidi"></script>
<script src="/resources/testdriver-vendor.js"></script>

<script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<title>TestDriver bidi.permissions.set_permission method</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver.js?feature=bidi"></script>
<script src="/resources/testdriver-vendor.js"></script>

<script>
Expand Down
4 changes: 2 additions & 2 deletions infrastructure/webdriver/bidi/subscription.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test console log are present</title>
<title>Can subscribe and receive WebDriver BiDi events</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver.js?feature=bidi"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script>
promise_test(async () => {
Expand Down
22 changes: 22 additions & 0 deletions infrastructure/webdriver/bidi/subscription.window.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// META: title=Can subscribe and receive WebDriver BiDi events
// META: script=/resources/testdriver.js?feature=bidi

'use strict';

promise_test(async () => {
const some_message = "SOME MESSAGE";
// Subscribe to `log.entryAdded` BiDi events. This will not add a listener to the page.
await test_driver.bidi.log.entry_added.subscribe();
// Add a listener for the log.entryAdded event. This will not subscribe to the event, so the subscription is
// required before. The cleanup is done automatically after the test is finished.
const log_entry_promise = test_driver.bidi.log.entry_added.once();
// Emit a console.log message.
// Note: Lint rule is disabled in `lint.ignore` file.
console.log(some_message);
// Wait for the log.entryAdded event to be received.
const event = await log_entry_promise;
// Assert the log.entryAdded event has the expected message.
assert_equals(event.args.length, 1);
const event_message = event.args[0];
assert_equals(event_message.value, some_message);
}, "Assert testdriver can subscribe and receive events");
1 change: 1 addition & 0 deletions lint.ignore
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ CONSOLE: webaudio/resources/audit.js:41

# Intentional use of console.*
CONSOLE: infrastructure/webdriver/bidi/subscription.html
CONSOLE: infrastructure/webdriver/bidi/subscription.window.js

# use of console in a public library - annotation-model ensures
# it is not actually used
Expand Down
24 changes: 24 additions & 0 deletions resources/testdriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@
var idCounter = 0;
let testharness_context = null;

const features = (() => {
function getFeatures(scriptSrc) {
try {
const url = new URL(scriptSrc);
return url.searchParams.getAll('feature');
} catch (e) {
return [];
}
}

return getFeatures(document?.currentScript?.src ?? '');
})();

function assertBidiIsEnabled(){
if (!features.includes('bidi')) {
throw new Error(
"`?feature=bidi` is missing when importing testdriver.js but the test is using WebDriver BiDi APIs");
}
}

function getInViewCenterPoint(rect) {
var left = Math.max(0, rect.left);
var right = Math.min(window.innerWidth, rect.right);
Expand Down Expand Up @@ -115,6 +135,7 @@
* is successfully done.
*/
subscribe: async function (params = {}) {
assertBidiIsEnabled();
return window.test_driver_internal.bidi.log.entry_added.subscribe(params);
},
/**
Expand All @@ -127,6 +148,7 @@
* added event listener when called.
*/
on: function (callback) {
assertBidiIsEnabled();
return window.test_driver_internal.bidi.log.entry_added.on(callback);
},
/**
Expand All @@ -137,6 +159,7 @@
* with the event object when the event is emitted.
*/
once: function () {
assertBidiIsEnabled();
return new Promise(resolve => {
const remove_handler = window.test_driver_internal.bidi.log.entry_added.on(
event => {
Expand Down Expand Up @@ -181,6 +204,7 @@
* the permission fails.
*/
set_permission: function (params) {
assertBidiIsEnabled();
return window.test_driver_internal.bidi.permissions.set_permission(
params);
}
Expand Down
83 changes: 75 additions & 8 deletions tools/lint/lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from typing import (Any, Callable, Dict, IO, Iterable, List, Optional, Sequence, Set, Text, Tuple,
Type, TypeVar)

from urllib.parse import urlsplit, urljoin
from urllib.parse import urlsplit, urljoin, parse_qs

try:
from xml.etree import cElementTree as ElementTree
Expand Down Expand Up @@ -523,20 +523,87 @@ def check_parsed(repo_root: Text, path: Text, f: IO[bytes]) -> List[rules.Error]
for element in source_file.root.findall(".//{http://www.w3.org/1999/xhtml}script[@src]"):
src = element.attrib["src"]

def incorrect_path(script: Text, src: Text) -> bool:
return (script == src or
("/%s" % script in src and src != "/resources/%s" % script))
def is_path_correct(script: Text, src: Text) -> bool:
"""
If the `src` relevant to the `script`, check that the `src` is the
correct path for `script`.
:param script: the script name to check the `src` for.
:param src: the included path.
:return: if the `src` irrelevant to the `script`, or if the `src`
path is the correct path.
"""
if script == src:
# The src does not provide the full path.
return False

if "/%s" % script not in src:
# The src is not relevant to the script.
return True

return ("%s" % src).startswith("/resources/%s" % script)

def is_query_string_correct(script: Text, src: Text,
allowed_query_string_params: Dict[str, List[str]]) -> bool:
"""
Checks if the query string in a script tag's `src` is valid.
Specifically, it verifies that the query string parameters and their
values are among those allowed for the given script. It handles vendor
prefixes (parameters or values containing a colon) by allowing them
unconditionally.
:param script: the name of the script (e.g., "testharness.js"). Used
to verify is the given `src` is related to the
script.
:param src: the full `src` attribute value from the script tag.
:param allowed_query_string_params: A dictionary where keys are
allowed parameter names and
values are lists of allowed
values for each parameter.
:return: if the query string is empty or contains only allowed
params.
"""
if not ("%s" % src).startswith("/resources/%s?" % script):
# The src is not related to the script.
return True

if incorrect_path("testharness.js", src):
try:
query_string = urlsplit(urljoin(source_file.url, src)).query
query_string_params = parse_qs(query_string,
keep_blank_values=True)
except ValueError:
# Parsing error means that the query string is incorrect.
return False

for param_name in query_string_params:
if param_name not in allowed_query_string_params:
return False

for param_value in query_string_params[param_name]:
if ':' in param_value:
# Allow for vendor-specific values in query parameters.
continue
if param_value not in allowed_query_string_params[
param_name]:
return False
return True

if (not is_path_correct("testharness.js", src) or
not is_query_string_correct("testharness.js", src, {})):
errors.append(rules.TestharnessPath.error(path))

if incorrect_path("testharnessreport.js", src):
if (not is_path_correct("testharnessreport.js", src) or
not is_query_string_correct("testharnessreport.js", src, {})):
errors.append(rules.TestharnessReportPath.error(path))

if incorrect_path("testdriver.js", src):
if not is_path_correct("testdriver.js", src):
errors.append(rules.TestdriverPath.error(path))
if not is_query_string_correct("testdriver.js", src,
{'feature': ['bidi']}):
errors.append(rules.TestdriverUnsupportedQueryParameter.error(path))

if incorrect_path("testdriver-vendor.js", src):
if (not is_path_correct("testdriver-vendor.js", src) or
not is_query_string_correct("testdriver-vendor.js", src, {})):
errors.append(rules.TestdriverVendorPath.error(path))

script_path = None
Expand Down
5 changes: 5 additions & 0 deletions tools/lint/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,11 @@ class TestdriverPath(Rule):
description = "testdriver.js script seen with incorrect path"


class TestdriverUnsupportedQueryParameter(Rule):
name = "TESTDRIVER-UNSUPPORTED-QUERY-PARAMETER"
description = "testdriver.js script seen with unsupported query parameters"


class TestdriverVendorPath(Rule):
name = "TESTDRIVER-VENDOR-PATH"
description = "testdriver-vendor.js script seen with incorrect path"
Expand Down
Loading

0 comments on commit f3acbb2

Please sign in to comment.