Skip to content

Commit dc075e0

Browse files
committed
Digital Credentials: Add WebDriver automation and tests
1 parent 4a13b6c commit dc075e0

8 files changed

Lines changed: 218 additions & 1 deletion

File tree

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<!DOCTYPE html>
2+
<title>Digital Credential API: create() webdriver tests.</title>
3+
<link rel="help" href="https://wicg.github.io/digital-credentials/">
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<script src="/resources/testdriver.js?feature=bidi"></script>
7+
<script src="/resources/testdriver-vendor.js"></script>
8+
<body></body>
9+
<script type="module">
10+
import { makeCreateOptions } from "../support/helper.js";
11+
12+
promise_test(async (t) => {
13+
const responseData = "test-create-response-data";
14+
await test_driver.set_virtual_wallet_behavior("respond", "openid4vci", { "value": responseData });
15+
t.add_cleanup(() => test_driver.set_virtual_wallet_behavior("clear"));
16+
await test_driver.bless("user activation");
17+
18+
const options = makeCreateOptions({ protocol: "openid4vci" });
19+
const result = await navigator.credentials.create(options);
20+
21+
assert_equals(result.protocol, "openid4vci");
22+
assert_equals(result.data.value, responseData);
23+
}, "navigator.credentials.create() with respond mode should resolve with the specified data.");
24+
25+
promise_test(async (t) => {
26+
const complexData = { "a": 1, "b": [2, 3], "c": { "d": 4 } };
27+
await test_driver.set_virtual_wallet_behavior("respond", "openid4vci", complexData);
28+
t.add_cleanup(() => test_driver.set_virtual_wallet_behavior("clear"));
29+
await test_driver.bless("user activation");
30+
31+
const options = makeCreateOptions({ protocol: "openid4vci" });
32+
const result = await navigator.credentials.create(options);
33+
34+
assert_equals(result.protocol, "openid4vci");
35+
assert_equals(result.data.a, 1);
36+
assert_array_equals(result.data.b, [2, 3]);
37+
assert_equals(result.data.c.d, 4);
38+
}, "navigator.credentials.create() with complex data response should resolve with structured data.");
39+
40+
promise_test(async (t) => {
41+
await test_driver.set_virtual_wallet_behavior("decline");
42+
t.add_cleanup(() => test_driver.set_virtual_wallet_behavior("clear"));
43+
await test_driver.bless("user activation");
44+
45+
const options = makeCreateOptions({ protocol: "openid4vci" });
46+
await promise_rejects_dom(t, "NotAllowedError", navigator.credentials.create(options));
47+
}, "navigator.credentials.create() with decline mode should reject with NotAllowedError.");
48+
49+
promise_test(async (t) => {
50+
await test_driver.set_virtual_wallet_behavior("wait");
51+
t.add_cleanup(() => test_driver.set_virtual_wallet_behavior("clear"));
52+
await test_driver.bless("user activation");
53+
54+
const controller = new AbortController();
55+
const options = makeCreateOptions({ protocol: "openid4vci", signal: controller.signal });
56+
const promise = navigator.credentials.create(options);
57+
58+
// Give it some time to make sure it's pending.
59+
await new Promise(resolve => t.step_timeout(resolve, 100));
60+
61+
controller.abort();
62+
await promise_rejects_dom(t, "AbortError", promise);
63+
}, "navigator.credentials.create() with wait mode should remain pending until aborted.");
64+
</script>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<!DOCTYPE html>
2+
<title>Digital Credential API: get() webdriver tests.</title>
3+
<link rel="help" href="https://wicg.github.io/digital-credentials/">
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<script src="/resources/testdriver.js?feature=bidi"></script>
7+
<script src="/resources/testdriver-vendor.js"></script>
8+
<body></body>
9+
<script type="module">
10+
import { makeGetOptions } from "../support/helper.js";
11+
12+
promise_test(async (t) => {
13+
const responseData = "test-response-data";
14+
await test_driver.set_virtual_wallet_behavior("respond", "openid4vp-v1-unsigned", { "value": responseData });
15+
t.add_cleanup(() => test_driver.set_virtual_wallet_behavior("clear"));
16+
await test_driver.bless("user activation");
17+
18+
const options = makeGetOptions({ protocol: "openid4vp-v1-unsigned" });
19+
const result = await navigator.credentials.get(options);
20+
21+
assert_equals(result.protocol, "openid4vp-v1-unsigned");
22+
assert_equals(result.data.value, responseData);
23+
}, "navigator.credentials.get() with respond mode should resolve with the specified data.");
24+
25+
promise_test(async (t) => {
26+
const complexData = { "a": 1, "b": [2, 3], "c": { "d": 4 } };
27+
await test_driver.set_virtual_wallet_behavior("respond", "openid4vp-v1-unsigned", complexData);
28+
t.add_cleanup(() => test_driver.set_virtual_wallet_behavior("clear"));
29+
await test_driver.bless("user activation");
30+
31+
const options = makeGetOptions({ protocol: "openid4vp-v1-unsigned" });
32+
const result = await navigator.credentials.get(options);
33+
34+
assert_equals(result.protocol, "openid4vp-v1-unsigned");
35+
assert_equals(result.data.a, 1);
36+
assert_array_equals(result.data.b, [2, 3]);
37+
assert_equals(result.data.c.d, 4);
38+
}, "navigator.credentials.get() with complex data response should resolve with structured data.");
39+
40+
promise_test(async (t) => {
41+
await test_driver.set_virtual_wallet_behavior("decline");
42+
t.add_cleanup(() => test_driver.set_virtual_wallet_behavior("clear"));
43+
await test_driver.bless("user activation");
44+
45+
const options = makeGetOptions({ protocol: "openid4vp-v1-unsigned" });
46+
await promise_rejects_dom(t, "NotAllowedError", navigator.credentials.get(options));
47+
}, "navigator.credentials.get() with decline mode should reject with NotAllowedError.");
48+
49+
promise_test(async (t) => {
50+
await test_driver.set_virtual_wallet_behavior("wait");
51+
t.add_cleanup(() => test_driver.set_virtual_wallet_behavior("clear"));
52+
await test_driver.bless("user activation");
53+
54+
const controller = new AbortController();
55+
const options = makeGetOptions({ protocol: "openid4vp-v1-unsigned", signal: controller.signal });
56+
const promise = navigator.credentials.get(options);
57+
58+
// Give it some time to make sure it's pending.
59+
await new Promise(resolve => t.step_timeout(resolve, 100));
60+
61+
controller.abort();
62+
await promise_rejects_dom(t, "AbortError", promise);
63+
}, "navigator.credentials.get() with wait mode should remain pending until aborted.");
64+
</script>

resources/testdriver.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1985,6 +1985,24 @@
19851985
return window.test_driver_internal.reset_fedcm_cooldown(context);
19861986
},
19871987

1988+
/**
1989+
* Sets the behavior for the virtual wallet.
1990+
*
1991+
* Matches the `Set Virtual Wallet Behavior`
1992+
* WebDriver BiDi command in Digital Credentials spec.
1993+
*
1994+
* @param {String} action - The action to take ("decline", "respond", "wait", "clear").
1995+
* @param {String} [protocol=null] - The protocol requested (required for "respond").
1996+
* @param {Object} [response=null] - The response data (optional for "respond").
1997+
* @param {WindowProxy} [context=null] - Browsing context in which to run the call.
1998+
*
1999+
* @returns {Promise} Fulfilled after the behavior has been set.
2000+
*/
2001+
set_virtual_wallet_behavior: function(action, protocol=null, response=null, context=null) {
2002+
return window.test_driver_internal.set_virtual_wallet_behavior(action, protocol, response, context);
2003+
},
2004+
2005+
19882006
/**
19892007
* Creates a virtual sensor for use with the Generic Sensors APIs.
19902008
*
@@ -2695,6 +2713,11 @@
26952713
throw new Error("reset_fedcm_cooldown() is not implemented by testdriver-vendor.js");
26962714
},
26972715

2716+
async set_virtual_wallet_behavior(action, protocol=null, response=null, context=null) {
2717+
throw new Error("set_virtual_wallet_behavior() is not implemented by testdriver-vendor.js");
2718+
},
2719+
2720+
26982721
async create_virtual_sensor(sensor_type, sensor_params, context=null) {
26992722
throw new Error("create_virtual_sensor() is not implemented by testdriver-vendor.js");
27002723
},

tools/wptrunner/wptrunner/executors/actions.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,21 @@ def __call__(self, payload):
441441
return self.protocol.fedcm.reset_fedcm_cooldown()
442442

443443

444+
class SetVirtualWalletBehaviorAction:
445+
name = "set_virtual_wallet_behavior"
446+
447+
def __init__(self, logger, protocol):
448+
self.logger = logger
449+
self.protocol = protocol
450+
451+
def __call__(self, payload):
452+
action = payload["action"]
453+
protocol = payload.get("protocol")
454+
response = payload.get("response")
455+
self.logger.debug("Setting virtual wallet behavior to %s" % action)
456+
return self.protocol.digital_credentials.set_virtual_wallet_behavior(action, protocol, response)
457+
458+
444459
class CreateVirtualSensorAction:
445460
name = "create_virtual_sensor"
446461

@@ -653,6 +668,7 @@ def __call__(self, payload):
653668
GetFedCMDialogTypeAction,
654669
SetFedCMDelayEnabledAction,
655670
ResetFedCMCooldownAction,
671+
SetVirtualWalletBehaviorAction,
656672
CreateVirtualSensorAction,
657673
UpdateVirtualSensorAction,
658674
RemoveVirtualSensorAction,

tools/wptrunner/wptrunner/executors/asyncactions.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,22 @@ async def __call__(self, payload):
342342
embedded_origin)
343343

344344

345+
class BidiDigitalCredentialsSetVirtualWalletBehaviorAction:
346+
name = "set_virtual_wallet_behavior"
347+
348+
def __init__(self, logger, protocol):
349+
do_delayed_imports()
350+
self.logger = logger
351+
self.protocol = protocol
352+
353+
async def __call__(self, payload):
354+
action = payload["action"]
355+
protocol = payload.get("protocol")
356+
response = payload.get("response")
357+
self.logger.debug("Setting virtual wallet behavior to %s" % action)
358+
return await self.protocol.digital_credentials.set_virtual_wallet_behavior(action, protocol, response)
359+
360+
345361
async_actions = [
346362
BidiBluetoothHandleRequestDevicePrompt,
347363
BidiBluetoothSimulateAdapterAction,
@@ -362,4 +378,4 @@ async def __call__(self, payload):
362378
BidiPermissionsSetPermissionAction,
363379
BidiSessionSubscribeAction,
364380
BidiSessionUnsubscribeAction,
365-
BidiPermissionsSetPermissionAction]
381+
BidiDigitalCredentialsSetVirtualWalletBehaviorAction]

tools/wptrunner/wptrunner/executors/executorwebdriver.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# mypy: allow-untyped-defs
22

33
import asyncio
4+
import logging
45
import json
56
import os
67
import socket
@@ -36,6 +37,7 @@
3637
SPCTransactionsProtocolPart,
3738
RPHRegistrationsProtocolPart,
3839
FedCMProtocolPart,
40+
DigitalCredentialsProtocolPart,
3941
VirtualSensorProtocolPart,
4042
BidiBluetoothProtocolPart,
4143
BidiBrowsingContextProtocolPart,
@@ -965,6 +967,17 @@ def clear_device_posture(self):
965967
return self.webdriver.send_session_command("DELETE", "deviceposture")
966968

967969

970+
class WebDriverDigitalCredentialsPart(DigitalCredentialsProtocolPart):
971+
def setup(self):
972+
self.webdriver = self.parent.webdriver
973+
self.logger = logging.getLogger(__name__)
974+
975+
def set_virtual_wallet_behavior(self, action, protocol=None, response=None):
976+
# Placeholder implementation
977+
self.logger.debug(f"Placeholder set_virtual_wallet_behavior called with action={action}")
978+
pass
979+
980+
968981
class WebDriverStorageProtocolPart(StorageProtocolPart):
969982
def setup(self):
970983
self.webdriver = self.parent.webdriver
@@ -1056,6 +1069,7 @@ class WebDriverProtocol(Protocol):
10561069
WebDriverSPCTransactionsProtocolPart,
10571070
WebDriverRPHRegistrationsProtocolPart,
10581071
WebDriverFedCMProtocolPart,
1072+
WebDriverDigitalCredentialsPart,
10591073
WebDriverDebugProtocolPart,
10601074
WebDriverVirtualSensorPart,
10611075
WebDriverDevicePostureProtocolPart,

tools/wptrunner/wptrunner/executors/protocol.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,22 @@ def reset_fedcm_cooldown(self):
11451145
pass
11461146

11471147

1148+
class DigitalCredentialsProtocolPart(ProtocolPart):
1149+
"""Protocol part for Digital Credentials"""
1150+
__metaclass__ = ABCMeta
1151+
1152+
name = "digital_credentials"
1153+
1154+
@abstractmethod
1155+
def set_virtual_wallet_behavior(self, action, protocol=None, response=None):
1156+
"""Set the virtual wallet behavior
1157+
1158+
:param str action: The action to take ("decline", "respond", "wait", "clear")
1159+
:param str protocol: The protocol requested (required for "respond")
1160+
:param dict response: The response data (optional for "respond")"""
1161+
pass
1162+
1163+
11481164
class PrintProtocolPart(ProtocolPart):
11491165
"""Protocol part for rendering to a PDF."""
11501166
__metaclass__ = ABCMeta

tools/wptrunner/wptrunner/testdriver-extra.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,10 @@
655655
return create_context_action("reset_fedcm_cooldown", context, {});
656656
};
657657

658+
window.test_driver_internal.set_virtual_wallet_behavior = function(action, protocol=null, response=null, context=null) {
659+
return create_context_action("set_virtual_wallet_behavior", context, {action, protocol, response});
660+
};
661+
658662
window.test_driver_internal.create_virtual_sensor = function(sensor_type, sensor_params={}, context=null) {
659663
return create_context_action("create_virtual_sensor", context, {sensor_type, sensor_params});
660664
};

0 commit comments

Comments
 (0)