Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,20 @@
descriptor: {name: "geolocation"},
state: "granted",
});

// Ensure any previously set geolocation emulations are cleared.
await test_driver.bidi.emulation.set_geolocation_override(
{coordinates: null});
});

/** Get the current geolocation or error */
function get_current_geolocation() {
return new Promise(
resolve => window.navigator.geolocation.getCurrentPosition(
position => resolve(position.coords.toJSON()),
error => resolve({code: error.code, message: error.message}),
error => resolve({code: error.code}),
// Fail fast if geolocation is not available.
{timeout: 500}
))
}

/** Asserts that relevant coordinate properties match */
/** Asserts that coordinate or error matches the expected value */
function assert_coordinates_match(actual, expected) {
for (const key in expected) {
assert_equals(actual[key], expected[key],
Expand All @@ -49,6 +45,11 @@
};

promise_test(async (t) => {
t.add_cleanup(async () => {
await test_driver.bidi.emulation.set_geolocation_override(
{coordinates: null});
});

// Get the initial geolocation (might be error).
const initial_coords = await get_current_geolocation();

Expand All @@ -66,6 +67,28 @@
await test_driver.bidi.emulation.set_geolocation_override(
{coordinates: null});
// Assert coordinates are set to the original value.
assert_coordinates_match(await get_current_geolocation(), initial_coords);
assert_coordinates_match(await get_current_geolocation(),
initial_coords);
}, "emulate geolocation and clear override");

promise_test(async (t) => {
// Emulate position unavailable.
await test_driver.bidi.emulation.set_geolocation_override({
error: {type: 'positionUnavailable'},
});

// Get the geolocation after setting the override.
const error = await get_current_geolocation();

assert_equals(error.code, 2,
`Geolocation should be unavailable with code POSITION_UNAVAILABLE = 2`);
}, "emulate geolocation position unavailable");

promise_test(async (t) => {
assert_throws_js(Error,
() => test_driver.bidi.emulation.set_geolocation_override({
error: {type: 'positionUnavailable'},
coordinates: SOME_COORDINATES
}), "Setting both `coordinates` and `error` should fail");
}, "cannot set `coordinates` and `error` simultaneously");
</script>
10 changes: 8 additions & 2 deletions resources/testdriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,14 @@
* @param {object} params - Parameters for the command.
* @param {null|object} params.coordinates - The optional
* geolocation coordinates to set. Matches the
* `emulation.GeolocationCoordinates <https://w3c.github.io/webdriver-bidi/#type-emulation-GeolocationCoordinates>`_
* value. If null or omitted, the emulation will be removed.
* `emulation.GeolocationCoordinates <https://w3c.github.io/webdriver-bidi/#commands-emulationsetgeolocationoverride>`_
* value. If null or omitted and the `params.error` is set, the
* emulation will be removed. Mutually exclusive with
* `params.error`.
* @param {object} params.error - The optional
* geolocation error to emulate. Matches the
* `emulation.GeolocationPositionError <https://w3c.github.io/webdriver-bidi/#commands-emulationsetgeolocationoverride>`_
* value. Mutually exclusive with `params.coordinates`.
* @param {null|Array.<(Context)>} [params.contexts] The
* optional contexts parameter specifies which browsing contexts
* to set the geolocation override on. It should be either an
Expand Down
90 changes: 42 additions & 48 deletions tools/wptrunner/wptrunner/executors/asyncactions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# mypy: allow-untyped-defs
from webdriver.bidi.undefined import UNDEFINED

webdriver = None

Expand All @@ -7,6 +8,22 @@ def do_delayed_imports():
global webdriver
import webdriver


def get_browsing_context_id(context):
"""
:param context: Either a string representing the browsing context id, or a
BiDi serialized window proxy object. In the latter case, the value is
extracted from the serialized object.
:return: The browsing context id.
"""
if isinstance(context, str):
return context
elif isinstance(context, webdriver.bidi.protocol.BidiWindow):
# Context can be a serialized WindowProxy.
return context.browsing_context
raise ValueError("Unexpected context type: %s" % context)


class BidiBluetoothHandleRequestDevicePrompt:
name = "bidi.bluetooth.handle_request_device_prompt"

Expand All @@ -16,18 +33,10 @@ def __init__(self, logger, protocol):
self.protocol = protocol

async def __call__(self, payload):
if payload["context"] is None:
if "context" not in payload:
raise ValueError("Missing required parameter: context")

context = payload["context"]
if isinstance(context, str):
pass
elif isinstance(context, webdriver.bidi.protocol.BidiWindow):
# Context can be a serialized WindowProxy.
context = context.browsing_context
else:
raise ValueError("Unexpected context type: %s" % context)

context = get_browsing_context_id(payload["context"])
prompt = payload["prompt"]
accept = payload["accept"]
device = payload["device"]
Expand All @@ -42,17 +51,10 @@ def __init__(self, logger, protocol):
self.protocol = protocol

async def __call__(self, payload):
if payload["context"] is None:
if "context" not in payload:
raise ValueError("Missing required parameter: context")

context = payload["context"]
if isinstance(context, str):
pass
elif isinstance(context, webdriver.bidi.protocol.BidiWindow):
# Context can be a serialized WindowProxy.
context = context.browsing_context
else:
raise ValueError("Unexpected context type: %s" % context)
context = get_browsing_context_id(payload["context"])

state = payload["state"]
return await self.protocol.bidi_bluetooth.simulate_adapter(context,
Expand All @@ -68,17 +70,9 @@ def __init__(self, logger, protocol):
self.protocol = protocol

async def __call__(self, payload):
if payload["context"] is None:
if "context" not in payload:
raise ValueError("Missing required parameter: context")

context = payload["context"]
if isinstance(context, str):
pass
elif isinstance(context, webdriver.bidi.protocol.BidiWindow):
# Context can be a serialized WindowProxy.
context = context.browsing_context
else:
raise ValueError("Unexpected context type: %s" % context)
context = get_browsing_context_id(payload["context"])

address = payload["address"]
name = payload["name"]
Expand All @@ -97,22 +91,29 @@ def __init__(self, logger, protocol):
self.protocol = protocol

async def __call__(self, payload):
coordinates = payload['coordinates']
if "error" in payload and "coordinates" in payload:
raise ValueError(
"Params `error` and `coordinates` are mutually exclusive")

# If `error` is present, set it. Otherwise, do not pass it (error: None).
# Note, unlike `coordinates`, `error` cannot be `UNDEFINED`. It's either
# `None` and it's not passed, or some dict value which is passed.
error = payload['error'] if 'error' in payload else None
# If `error` is present, do not pass `coordinates` (coordinates: UNDEFINED).
# Otherwise, remove emulation (coordinates: None).
coordinates = payload['coordinates'] if 'coordinates' in payload else (
None if error is None else UNDEFINED)

if "contexts" not in payload:
raise ValueError("Missing required parameter: contexts")
contexts = []
for context in payload["contexts"]:
# Context can be either a browsing context id, or a BiDi serialized window. In the latter case, the
# value is extracted from the serialized object.
if isinstance(context, str):
contexts.append(context)
elif isinstance(context, webdriver.bidi.protocol.BidiWindow):
contexts.append(context.browsing_context)
else:
raise ValueError("Unexpected context type: %s" % context)
contexts.append(get_browsing_context_id(context))
if len(contexts) == 0:
raise ValueError("At least one context must be provided")

return await self.protocol.bidi_emulation.set_geolocation_override(
coordinates, contexts)
coordinates, error, contexts)


class BidiSessionSubscribeAction:
Expand All @@ -126,17 +127,10 @@ def __init__(self, logger, protocol):
async def __call__(self, payload):
events = payload["events"]
contexts = None
if payload["contexts"] is not None:
if "contexts" in payload and payload["contexts"] is not None:
contexts = []
for context in payload["contexts"]:
# Context can be either a browsing context id, or a BiDi serialized window. In the latter case, the
# value is extracted from the serialized object.
if isinstance(context, str):
contexts.append(context)
elif isinstance(context, webdriver.bidi.protocol.BidiWindow):
contexts.append(context.browsing_context)
else:
raise ValueError("Unexpected context type: %s" % context)
contexts.append(get_browsing_context_id(context))
return await self.protocol.bidi_events.subscribe(events, contexts)


Expand Down
4 changes: 2 additions & 2 deletions tools/wptrunner/wptrunner/executors/executorwebdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,9 @@ def __init__(self, parent):
def setup(self):
self.webdriver = self.parent.webdriver

async def set_geolocation_override(self, coordinates, contexts):
async def set_geolocation_override(self, coordinates, error, contexts):
return await self.webdriver.bidi_session.emulation.set_geolocation_override(
coordinates=coordinates, contexts=contexts)
coordinates=coordinates, error=error, contexts=contexts)


class WebDriverBidiPermissionsProtocolPart(BidiPermissionsProtocolPart):
Expand Down
8 changes: 6 additions & 2 deletions tools/wptrunner/wptrunner/executors/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
from http.client import HTTPConnection

from abc import ABCMeta, abstractmethod
from typing import Any, Awaitable, Callable, ClassVar, List, Mapping, Optional, Tuple, Type
from typing import Any, Awaitable, Callable, ClassVar, List, Mapping, Optional, \
Tuple, Type, Union

from webdriver.bidi.undefined import Undefined


def merge_dicts(target, source):
Expand Down Expand Up @@ -452,7 +455,8 @@ class BidiEmulationProtocolPart(ProtocolPart):

@abstractmethod
async def set_geolocation_override(self,
coordinates: Optional[Mapping[str, Any]],
coordinates: Optional[Union[Mapping[str, Any], Undefined]],
error: Optional[Mapping[str, Any]],
contexts: List[str]) -> None:
pass

Expand Down
7 changes: 5 additions & 2 deletions tools/wptrunner/wptrunner/testdriver-extra.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,14 @@

window.test_driver_internal.bidi.emulation.set_geolocation_override =
function (params) {
if ('coordinates' in params && 'error' in params) {
throw new Error(
"`coordinates` and `error` are mutually exclusive in set_geolocation_override");
}

return create_action("bidi.emulation.set_geolocation_override", {
// Default to the current window.
contexts: [window],
// Default to no coordinates.
coordinates: null,
...(params ?? {})
});
}
Expand Down