diff --git a/tools/webdriver/webdriver/bidi/client.py b/tools/webdriver/webdriver/bidi/client.py index d4cc78588e9fd8..8ba93bb7c2affe 100644 --- a/tools/webdriver/webdriver/bidi/client.py +++ b/tools/webdriver/webdriver/bidi/client.py @@ -90,6 +90,7 @@ def __init__(self, # Modules. # For each module, have a property representing that module + self.bluetooth = modules.Bluetooth(self) self.browser = modules.Browser(self) self.browsing_context = modules.BrowsingContext(self) self.input = modules.Input(self) diff --git a/tools/webdriver/webdriver/bidi/modules/__init__.py b/tools/webdriver/webdriver/bidi/modules/__init__.py index 0a2ef500c42ce0..81dd9e6e6f628b 100644 --- a/tools/webdriver/webdriver/bidi/modules/__init__.py +++ b/tools/webdriver/webdriver/bidi/modules/__init__.py @@ -1,5 +1,6 @@ # flake8: noqa +from .bluetooth import Bluetooth from .browser import Browser from .browsing_context import BrowsingContext from .input import Input diff --git a/tools/webdriver/webdriver/bidi/modules/bluetooth.py b/tools/webdriver/webdriver/bidi/modules/bluetooth.py new file mode 100644 index 00000000000000..1013d28e8c644f --- /dev/null +++ b/tools/webdriver/webdriver/bidi/modules/bluetooth.py @@ -0,0 +1,21 @@ +from typing import Any, Mapping + +from ._module import BidiModule, command + + +class Bluetooth(BidiModule): + """ + Represents bluetooth automation module specified in + https://webbluetoothcg.github.io/web-bluetooth/#automated-testing + """ + + @command + def simulate_adapter(self, context: str, state: str) -> Mapping[str, Any]: + """ + Represents a command `bluetooth.simulateAdapter` specified in + https://webbluetoothcg.github.io/web-bluetooth/#bluetooth-simulateAdapter-command + """ + return { + "context": context, + "state": state + } diff --git a/webdriver/tests/bidi/external/__init__.py b/webdriver/tests/bidi/external/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/webdriver/tests/bidi/external/bluetooth/__init__.py b/webdriver/tests/bidi/external/bluetooth/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/webdriver/tests/bidi/external/bluetooth/simulate_adapter/__init__.py b/webdriver/tests/bidi/external/bluetooth/simulate_adapter/__init__.py new file mode 100644 index 00000000000000..ea1a7e6f232d42 --- /dev/null +++ b/webdriver/tests/bidi/external/bluetooth/simulate_adapter/__init__.py @@ -0,0 +1,18 @@ +from webdriver.bidi.modules.script import ContextTarget + + +async def get_bluetooth_availability(bidi_session, context): + result = await bidi_session.script.evaluate( + expression="navigator.bluetooth.getAvailability()", + target=ContextTarget(context["context"]), await_promise=True, ) + return result['value'] + + +async def set_simulate_adapter(bidi_session, context, test_page, state): + # Navigate to a page, as bluetooth is not guaranteed to work on + # `about:blank`. + await bidi_session.browsing_context.navigate(context=context['context'], + url=test_page, wait="complete") + + await bidi_session.bluetooth.simulate_adapter(context=context["context"], + state=state) diff --git a/webdriver/tests/bidi/external/bluetooth/simulate_adapter/context.py b/webdriver/tests/bidi/external/bluetooth/simulate_adapter/context.py new file mode 100644 index 00000000000000..ef56a33219ce0e --- /dev/null +++ b/webdriver/tests/bidi/external/bluetooth/simulate_adapter/context.py @@ -0,0 +1,19 @@ +import pytest + +from . import get_bluetooth_availability, set_simulate_adapter + +pytestmark = pytest.mark.asyncio + + +async def test_contexts_are_isolated(bidi_session, top_context, test_page): + another_browsing_context = await bidi_session.browsing_context.create( + type_hint="tab") + + await set_simulate_adapter(bidi_session, top_context, test_page, + "powered-on") + await set_simulate_adapter(bidi_session, another_browsing_context, + test_page, "absent") + + assert await get_bluetooth_availability(bidi_session, top_context) is True + assert await get_bluetooth_availability(bidi_session, + another_browsing_context) is False diff --git a/webdriver/tests/bidi/external/bluetooth/simulate_adapter/invalid.py b/webdriver/tests/bidi/external/bluetooth/simulate_adapter/invalid.py new file mode 100644 index 00000000000000..8fe2538873b8ba --- /dev/null +++ b/webdriver/tests/bidi/external/bluetooth/simulate_adapter/invalid.py @@ -0,0 +1,31 @@ +import pytest +import webdriver.bidi.error as error + +pytestmark = pytest.mark.asyncio + + +@pytest.mark.parametrize("state", [None, False, 42, {}, []]) +async def test_state_invalid_type(bidi_session, top_context, state): + with pytest.raises(error.InvalidArgumentException): + await bidi_session.bluetooth.simulate_adapter( + context=top_context["context"], state=state) + + +@pytest.mark.parametrize("state", ["", "invalid"]) +async def test_state_invalid_value(bidi_session, top_context, state): + with pytest.raises(error.InvalidArgumentException): + await bidi_session.bluetooth.simulate_adapter( + context=top_context["context"], state=state) + + +@pytest.mark.parametrize("context", [None, False, 42, {}, []]) +async def test_context_invalid_type(bidi_session, context): + with pytest.raises(error.InvalidArgumentException): + await bidi_session.bluetooth.simulate_adapter( + context=context, state="powered-on") + + +async def test_context_unknown_value(bidi_session): + with pytest.raises(error.NoSuchFrameException): + await bidi_session.bluetooth.simulate_adapter( + context="UNKNOWN_CONTEXT", state="powered-on") diff --git a/webdriver/tests/bidi/external/bluetooth/simulate_adapter/state.py b/webdriver/tests/bidi/external/bluetooth/simulate_adapter/state.py new file mode 100644 index 00000000000000..ad21326081e077 --- /dev/null +++ b/webdriver/tests/bidi/external/bluetooth/simulate_adapter/state.py @@ -0,0 +1,31 @@ +import pytest + +from . import get_bluetooth_availability, set_simulate_adapter + +pytestmark = pytest.mark.asyncio + + +@pytest.mark.parametrize("state,availability", + [("absent", False), ("powered-off", True), + ("powered-on", True)]) +async def test_state(bidi_session, top_context, test_page, state, availability): + await set_simulate_adapter(bidi_session, top_context, test_page, state) + assert await get_bluetooth_availability(bidi_session, + top_context) == availability + + +@pytest.mark.parametrize("state_1,availability_1", + [("absent", False), ("powered-off", True), + ("powered-on", True)]) +@pytest.mark.parametrize("state_2,availability_2", + [("absent", False), ("powered-off", True), + ("powered-on", True)]) +async def test_set_twice(bidi_session, top_context, test_page, state_1, + availability_1, state_2, availability_2): + await set_simulate_adapter(bidi_session, top_context, test_page, state_1) + assert await get_bluetooth_availability(bidi_session, + top_context) == availability_1 + + await set_simulate_adapter(bidi_session, top_context, test_page, state_2) + assert await get_bluetooth_availability(bidi_session, + top_context) == availability_2