| <!DOCTYPE html> | |
| <title>Digital Credential tests.</title> | |
| <link rel="help" href="https://wicg.github.io/digital-credentials/" /> | |
| <script src="/common/get-host-info.sub.js"></script> | |
| <script src="/resources/testharness.js"></script> | |
| <script src="/resources/testharnessreport.js"></script> | |
| <script src="/resources/testdriver.js"></script> | |
| <script src="/resources/testdriver-vendor.js"></script> | |
| <body> | |
| <iframe id="same-origin"></iframe> | |
| <iframe id="cross-origin" allow="digital-credentials-get"></iframe> | |
| </body> | |
| <script type="module"> | |
| import { makeGetOptions, sendMessage, loadIframe } from "./support/helper.js"; | |
| const iframeSameOrigin = document.querySelector("iframe#same-origin"); | |
| const iframeCrossOrigin = document.querySelector("iframe#cross-origin"); | |
| promise_setup(async () => { | |
| const hostInfo = get_host_info(); | |
| await Promise.all([ | |
| loadIframe( | |
| iframeCrossOrigin, | |
| `${hostInfo.HTTPS_REMOTE_ORIGIN}/digital-credentials/support/iframe.html` | |
| ), | |
| loadIframe(iframeSameOrigin, "/digital-credentials/support/iframe.html"), | |
| ]); | |
| }); | |
| promise_test(async (t) => { | |
| const invalidData = [null, undefined, "", 123, true, false]; | |
| for (const data of invalidData) { | |
| const options = { | |
| digital: { | |
| requests: [ | |
| { | |
| data, | |
| protocol: "openid4vp", | |
| }, | |
| ], | |
| }, | |
| }; | |
| await promise_rejects_js( | |
| t, | |
| TypeError, | |
| navigator.credentials.get(options) | |
| ); | |
| } | |
| }, "Type conversion happens on the data member of the DigitalCredentialGetRequest object."); | |
| promise_test(async (t) => { | |
| iframeSameOrigin.focus(); | |
| for (const global of [window, iframeSameOrigin.contentWindow]) { | |
| await promise_rejects_dom( | |
| t, | |
| "NotSupportedError", | |
| global.DOMException, | |
| global.navigator.credentials.get() | |
| ); | |
| await promise_rejects_dom( | |
| t, | |
| "NotSupportedError", | |
| global.DOMException, | |
| global.navigator.credentials.get({}) | |
| ); | |
| await promise_rejects_dom( | |
| t, | |
| "NotSupportedError", | |
| global.DOMException, | |
| global.navigator.credentials.get({ x: "y" }) | |
| ); | |
| await promise_rejects_dom( | |
| t, | |
| "NotSupportedError", | |
| global.DOMException, | |
| global.navigator.credentials.get({ x: "y", y: "z" }) | |
| ); | |
| await promise_rejects_dom( | |
| t, | |
| "NotSupportedError", | |
| global.DOMException, | |
| global.navigator.credentials.get({ mediation: "required" }) | |
| ); | |
| const abortController = new AbortController(); | |
| const { signal } = abortController; | |
| await promise_rejects_dom( | |
| t, | |
| "NotSupportedError", | |
| global.DOMException, | |
| global.navigator.credentials.get({ signal }) | |
| ); | |
| await promise_rejects_dom( | |
| t, | |
| "NotSupportedError", | |
| global.DOMException, | |
| global.navigator.credentials.get({ signal, mediation: "required" }) | |
| ); | |
| } | |
| }, "Calling navigator.credentials.get() without a digital member same origin."); | |
| promise_test(async (t) => { | |
| for (const request of [undefined, []]) { | |
| const options = makeGetOptions(request); | |
| await test_driver.bless("user activation"); | |
| await promise_rejects_js( | |
| t, | |
| TypeError, | |
| navigator.credentials.get(options) | |
| ); | |
| } | |
| }, "navigator.credentials.get() API rejects if there are no credential request."); | |
| promise_test(async (t) => { | |
| iframeSameOrigin.focus(); | |
| const { contentWindow: iframeWindow } = iframeSameOrigin; | |
| for (const request of [undefined, []]) { | |
| const options = makeGetOptions(request); | |
| await test_driver.bless("user activation"); | |
| await promise_rejects_js( | |
| t, | |
| iframeWindow.TypeError, | |
| iframeWindow.navigator.credentials.get(options) | |
| ); | |
| } | |
| }, "navigator.credentials.get() API rejects if there are no credential request for same-origin iframe."); | |
| promise_test(async (t) => { | |
| iframeCrossOrigin.focus(); | |
| for (const request of [undefined, []]) { | |
| const options = makeGetOptions(request); | |
| const result = await sendMessage(iframeCrossOrigin, { | |
| action: "get", | |
| options, | |
| }); | |
| assert_equals(result.constructor, "TypeError"); | |
| } | |
| }, "navigator.credentials.get() API rejects if there are no credential request in cross-origin iframe."); | |
| promise_test(async (t) => { | |
| const abortController = new AbortController(); | |
| const { signal } = abortController; | |
| abortController.abort(); | |
| for (const options of [{ signal }, { signal, ...makeGetOptions([]) }]) { | |
| await promise_rejects_dom( | |
| t, | |
| "AbortError", | |
| navigator.credentials.get(options) | |
| ); | |
| } | |
| }, "navigator.credentials.get() promise is rejected if called with an aborted controller."); | |
| promise_test(async (t) => { | |
| iframeSameOrigin.focus(); | |
| const { contentWindow: iframeWindow } = iframeSameOrigin; | |
| const abortController = new iframeWindow.AbortController(); | |
| const { signal } = abortController; | |
| abortController.abort(); | |
| for (const options of [{ signal }, { signal, ...makeGetOptions([]) }]) { | |
| await test_driver.bless("user activation"); | |
| await promise_rejects_dom( | |
| t, | |
| "AbortError", | |
| iframeWindow.DOMException, | |
| iframeWindow.navigator.credentials.get(options) | |
| ); | |
| assert_true( | |
| navigator.userActivation.isActive, | |
| "User activation is still active." | |
| ); | |
| } | |
| }, "navigator.credentials.get() promise is rejected if called with an aborted controller in same-origin iframe."); | |
| promise_test(async (t) => { | |
| iframeCrossOrigin.focus(); | |
| for (const options of [undefined, {}, makeGetOptions([])]) { | |
| const result = await sendMessage(iframeCrossOrigin, { | |
| abort: "before", | |
| action: "get", | |
| options, | |
| }); | |
| assert_equals(result.constructor, "DOMException"); | |
| assert_equals(result.name, "AbortError"); | |
| } | |
| }, "navigator.credentials.get() promise is rejected if called with an aborted signal in cross-origin iframe."); | |
| promise_test(async (t) => { | |
| const abortController = new AbortController(); | |
| const { signal } = abortController; | |
| const options = makeGetOptions("openid4vp"); | |
| options.signal = signal; | |
| await test_driver.bless("user activation"); | |
| const promise = promise_rejects_dom( | |
| t, | |
| "AbortError", | |
| DOMException, | |
| navigator.credentials.get(options) | |
| ); | |
| abortController.abort(); | |
| await promise; | |
| }, "navigator.credentials.get() promise is rejected if abort controller is aborted after call to get()."); | |
| promise_test(async (t) => { | |
| iframeCrossOrigin.focus(); | |
| const result = await sendMessage(iframeCrossOrigin, { | |
| abort: "after", | |
| action: "get", | |
| needsActivation: true, | |
| options: makeGetOptions("openid4vp"), | |
| }); | |
| assert_equals(result.constructor, "DOMException"); | |
| assert_equals(result.name, "AbortError"); | |
| }, "navigator.credentials.get() promise is rejected if abort controller is aborted after call to get() in cross-origin iframe."); | |
| promise_test(async (t) => { | |
| /** @type sequence<CredentialMediationRequirement> */ | |
| const disallowedMediations = ["conditional", "optional", "silent"]; | |
| for (const mediation of disallowedMediations) { | |
| const options = makeGetOptions("default", mediation); | |
| await promise_rejects_js( | |
| t, | |
| TypeError, | |
| navigator.credentials.get(options) | |
| ); | |
| } | |
| }, "Mediation is required to get a DigitalCredential."); | |
| </script> |