Skip to content

Commit

Permalink
Add testdriver_features and use in Chrome
Browse files Browse the repository at this point in the history
* Add testdriver_features
* Update doc
* Enable bidi in Chrome based on `testdriver_features` containing `bidi`
* Enable `infrastructure/webdriver/bidi/subscription.html` for chrome
* Enable `infrastructure/testdriver/bidi/permissions/set_permission.https.html` for chrome
* Enable `infrastructure/testdriver/bidi/bluetooth/simulate_adapter.https.html` for Chrome
  • Loading branch information
sadym-chromium committed Nov 20, 2024
1 parent 4110ada commit bdf6e5f
Show file tree
Hide file tree
Showing 20 changed files with 280 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 @@ -32,6 +32,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
`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 @@ -122,6 +122,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
28 changes: 21 additions & 7 deletions tools/lint/lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,20 +523,34 @@ 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,
allow_query_params: bool = False) -> bool:
if script == src:
# The src does not provide the full path.
return False

if incorrect_path("testharness.js", src):
if "/%s" % script not in src:
# The src is not related to the script.
return True

if src == "/resources/%s" % script:
# The src is properly included.
return True

# Check if the script is properly included with query parameters.
return allow_query_params and ("%s" % src).startswith(
"/resources/%s?" % script)

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

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

if incorrect_path("testdriver.js", src):
if not is_path_correct("testdriver.js", src, True):
errors.append(rules.TestdriverPath.error(path))

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

script_path = None
Expand Down
6 changes: 6 additions & 0 deletions tools/manifest/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ def timeout(self) -> Optional[Text]:
def pac(self) -> Optional[Text]:
return self._extras.get("pac")

@property
def testdriver_features(self) -> Optional[List[Text]]:
return self._extras.get("testdriver_features")

@property
def testdriver(self) -> Optional[Text]:
return self._extras.get("testdriver")
Expand All @@ -183,6 +187,8 @@ def to_json(self) -> Tuple[Optional[Text], Dict[Text, Any]]:
rv[-1]["timeout"] = self.timeout
if self.pac is not None:
rv[-1]["pac"] = self.pac
if self.testdriver_features is not None:
rv[-1]["testdriver_features"] = self.testdriver_features
if self.testdriver:
rv[-1]["testdriver"] = self.testdriver
if self.jsshell:
Expand Down
56 changes: 54 additions & 2 deletions tools/manifest/sourcefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from io import BytesIO
from typing import (Any, BinaryIO, Callable, Deque, Dict, Iterable, List,
Optional, Pattern, Set, Text, Tuple, TypedDict, Union, cast)
from urllib.parse import urljoin
from urllib.parse import urlparse, parse_qs, urljoin

try:
from xml.etree import cElementTree as ElementTree
Expand Down Expand Up @@ -723,7 +723,15 @@ def testdriver_nodes(self) -> List[ElementTree.Element]:
"""List of ElementTree Elements corresponding to nodes representing a
testdriver.js script"""
assert self.root is not None
return self.root.findall(".//{http://www.w3.org/1999/xhtml}script[@src='/resources/testdriver.js']")
# `xml.etree.ElementTree.findall` has a limited support of xPath, so
# explicit filter is required.
return [node for node in
self.root.findall(".//{http://www.w3.org/1999/xhtml}script")
if node.attrib.get('src',
"") == '/resources/testdriver.js'
or
node.attrib.get('src', "").startswith(
'/resources/testdriver.js?')]

@cached_property
def has_testdriver(self) -> Optional[bool]:
Expand All @@ -733,6 +741,46 @@ def has_testdriver(self) -> Optional[bool]:
return None
return bool(self.testdriver_nodes)

def ___get_testdriver_include_path(self):
if self.script_metadata:
for (meta, content) in self.script_metadata:
if meta.strip() == 'script' and content.startswith(
'/resources/testdriver.js'):
return content.strip()

if self.root is None:
return None

for node in self.testdriver_nodes:
if "src" in node.attrib:
return node.attrib.get("src")

return None


@cached_property
def testdriver_features(self) -> Optional[Text]:
"""
List of requested testdriver features.
"""

testdriver_include_url = self.___get_testdriver_include_path()

if testdriver_include_url is None:
return None

# Parse the URL
parsed_url = urlparse(testdriver_include_url)
# Extract query parameters
query_params = parse_qs(parsed_url.query)
# Get the values for the 'feature' parameter
feature_values = query_params.get('feature', [])

if len(feature_values) > 0:
return feature_values

return None

@cached_property
def reftest_nodes(self) -> List[ElementTree.Element]:
"""List of ElementTree Elements corresponding to nodes representing a
Expand Down Expand Up @@ -984,6 +1032,7 @@ def manifest_items(self) -> Tuple[Text, List[ManifestItem]]:
global_variant_url(self.rel_url, suffix) + variant,
timeout=self.timeout,
pac=self.pac,
testdriver_features=self.testdriver_features,
jsshell=jsshell,
script_metadata=self.script_metadata
)
Expand All @@ -1002,6 +1051,7 @@ def manifest_items(self) -> Tuple[Text, List[ManifestItem]]:
test_url + variant,
timeout=self.timeout,
pac=self.pac,
testdriver_features=self.testdriver_features,
script_metadata=self.script_metadata
)
for variant in self.test_variants
Expand All @@ -1018,6 +1068,7 @@ def manifest_items(self) -> Tuple[Text, List[ManifestItem]]:
test_url + variant,
timeout=self.timeout,
pac=self.pac,
testdriver_features=self.testdriver_features,
script_metadata=self.script_metadata
)
for variant in self.test_variants
Expand Down Expand Up @@ -1045,6 +1096,7 @@ def manifest_items(self) -> Tuple[Text, List[ManifestItem]]:
url,
timeout=self.timeout,
pac=self.pac,
testdriver_features=self.testdriver_features,
testdriver=testdriver,
script_metadata=self.script_metadata
))
Expand Down
10 changes: 7 additions & 3 deletions tools/manifest/tests/test_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,16 +294,20 @@ def test_update_from_json_modified():
# Reload it from JSON
m = manifest.Manifest.from_json("/", json_str)

# Update it with timeout="long"
s2 = SourceFileWithTest("test1", "1"*40, item.TestharnessTest, timeout="long", pac="proxy.pac")
testdriver_features = ['feature_1', 'feature_2']

# Update timeout, pac and testdriver_features
s2 = SourceFileWithTest("test1", "1" * 40, item.TestharnessTest, timeout="long", pac="proxy.pac",
testdriver_features=testdriver_features)
tree, sourcefile_mock = tree_and_sourcefile_mocks([(s2, None, True)])
with mock.patch("tools.manifest.manifest.SourceFile", side_effect=sourcefile_mock):
m.update(tree)
json_str = m.to_json()
assert json_str == {
'items': {'testharness': {'test1': [
"1"*40,
(None, {'timeout': 'long', 'pac': 'proxy.pac'})
(None, {'timeout': 'long', 'pac': 'proxy.pac',
'testdriver_features': testdriver_features})
]}},
'url_base': '/',
'version': 8
Expand Down
Loading

0 comments on commit bdf6e5f

Please sign in to comment.