create.tentative.https.html (8458B)
1 <!DOCTYPE html> 2 <title>Digital Credential API tests for create.</title> 3 <link rel="help" href="https://wicg.github.io/digital-credentials/" /> 4 <script src="/common/get-host-info.sub.js"></script> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="/resources/testdriver.js"></script> 8 <script src="/resources/testdriver-vendor.js"></script> 9 10 <body> 11 <iframe id="cross-origin" allow="digital-credentials-create"></iframe> 12 <iframe id="same-origin"></iframe> 13 </body> 14 15 <script type="module"> 16 import { makeCreateOptions, sendMessage, loadIframe } from "./support/helper.js"; 17 18 const iframeSameOrigin = document.querySelector("iframe#same-origin"); 19 const iframeCrossOrigin = document.querySelector("iframe#cross-origin"); 20 21 promise_setup(async () => { 22 const hostInfo = get_host_info(); 23 await Promise.all([ 24 loadIframe( 25 iframeCrossOrigin, 26 `${hostInfo.HTTPS_REMOTE_ORIGIN}/digital-credentials/support/iframe.html` 27 ), 28 loadIframe(iframeSameOrigin, "/digital-credentials/support/iframe.html"), 29 ]); 30 }); 31 32 promise_test(async (t) => { 33 iframeSameOrigin.focus(); 34 for (const global of [window, iframeSameOrigin.contentWindow]) { 35 await promise_rejects_dom( 36 t, 37 "NotSupportedError", 38 global.DOMException, 39 global.navigator.credentials.create() 40 ); 41 42 await promise_rejects_dom( 43 t, 44 "NotSupportedError", 45 global.DOMException, 46 global.navigator.credentials.create({}) 47 ); 48 49 await promise_rejects_dom( 50 t, 51 "NotSupportedError", 52 global.DOMException, 53 global.navigator.credentials.create({ x: "y" }) 54 ); 55 56 await promise_rejects_dom( 57 t, 58 "NotSupportedError", 59 global.DOMException, 60 global.navigator.credentials.create({ x: "y", y: "z" }) 61 ); 62 63 await promise_rejects_dom( 64 t, 65 "NotSupportedError", 66 global.DOMException, 67 global.navigator.credentials.create({ mediation: "required" }) 68 ); 69 70 const abortController = new AbortController(); 71 const { signal } = abortController; 72 73 await promise_rejects_dom( 74 t, 75 "NotSupportedError", 76 global.DOMException, 77 global.navigator.credentials.create({ signal }) 78 ); 79 80 await promise_rejects_dom( 81 t, 82 "NotSupportedError", 83 global.DOMException, 84 global.navigator.credentials.create({ signal, mediation: "required" }) 85 ); 86 } 87 }, "Calling navigator.credentials.create() without a digital member same origin."); 88 89 promise_test(async (t) => { 90 for (const r of [undefined, []]) { 91 const options = { 92 digital: { 93 requests: r 94 }, 95 }; 96 await test_driver.bless("user activation"); 97 await promise_rejects_js(t, TypeError, navigator.credentials.create(options)); 98 } 99 }, "navigator.credentials.create() API rejects if there are no credential request."); 100 101 promise_test(async (t) => { 102 iframeSameOrigin.focus(); 103 const { contentWindow: iframeWindow } = iframeSameOrigin; 104 for (const r of [undefined, []]) { 105 const options = { 106 digital: { 107 requests: r 108 }, 109 }; 110 await test_driver.bless("user activation"); 111 await promise_rejects_js( 112 t, 113 iframeWindow.TypeError, 114 iframeWindow.navigator.credentials.create(options) 115 ); 116 } 117 }, "navigator.credentials.create() API rejects if there are no credential request for same-origin iframe."); 118 119 promise_test(async (t) => { 120 iframeCrossOrigin.focus(); 121 for (const r of [undefined, []]) { 122 const options = { 123 digital: { 124 requests: r 125 }, 126 }; 127 const result = await sendMessage(iframeCrossOrigin, { 128 action: "create", 129 options, 130 }); 131 assert_equals(result.constructor, "TypeError"); 132 } 133 }, "navigator.credentials.create() API rejects if there are no credential request in cross-origin iframe."); 134 135 promise_test(async (t) => { 136 const abortController = new AbortController(); 137 const { signal } = abortController; 138 abortController.abort(); 139 for (const options of [{ signal }, makeCreateOptions({protocol: [], signal})]) { 140 await promise_rejects_dom( 141 t, 142 "AbortError", 143 navigator.credentials.create(options) 144 ); 145 } 146 }, "navigator.credentials.create() promise is rejected if called with an aborted controller."); 147 148 promise_test(async (t) => { 149 iframeSameOrigin.focus(); 150 const { contentWindow: iframeWindow } = iframeSameOrigin; 151 const abortController = new iframeWindow.AbortController(); 152 const { signal } = abortController; 153 abortController.abort(); 154 for (const options of [{ signal }, makeCreateOptions({protocol: [], signal})]) { 155 await test_driver.bless("user activation"); 156 await promise_rejects_dom( 157 t, 158 "AbortError", 159 iframeWindow.DOMException, 160 iframeWindow.navigator.credentials.create(options) 161 ); 162 assert_true( 163 navigator.userActivation.isActive, 164 "User activation is still active." 165 ); 166 } 167 }, "navigator.credentials.create() promise is rejected if called with an aborted controller in same-origin iframe."); 168 169 promise_test(async (t) => { 170 iframeCrossOrigin.focus(); 171 for (const options of [undefined, {}, makeCreateOptions({protocol: []})]) { 172 const result = await sendMessage(iframeCrossOrigin, { 173 abort: "before", 174 action: "create", 175 options, 176 }); 177 assert_equals(result.constructor, "DOMException"); 178 assert_equals(result.name, "AbortError"); 179 } 180 }, "navigator.credentials.create() promise is rejected if called with an aborted signal in cross-origin iframe."); 181 182 promise_test(async (t) => { 183 const abortController = new AbortController(); 184 const { signal } = abortController; 185 const options = makeCreateOptions({ signal }); 186 await test_driver.bless("user activation"); 187 const promise = promise_rejects_dom( 188 t, 189 "AbortError", 190 DOMException, 191 navigator.credentials.create(options) 192 ); 193 abortController.abort(); 194 await promise; 195 }, "navigator.credentials.create() promise is rejected if abort controller is aborted after call to create()."); 196 197 promise_test(async (t) => { 198 iframeCrossOrigin.focus(); 199 const result = await sendMessage(iframeCrossOrigin, { 200 abort: "after", 201 action: "create", 202 needsActivation: true, 203 options: makeCreateOptions(), 204 }); 205 assert_equals(result.constructor, "DOMException"); 206 assert_equals(result.name, "AbortError"); 207 }, "navigator.credentials.create() promise is rejected if abort controller is aborted after call to create() in cross-origin iframe."); 208 209 promise_test(async (t) => { 210 /** @type sequence<CredentialMediationRequirement> */ 211 const mediations = ["silent", "optional", "conditional", "required"]; 212 const abortController = new AbortController(); 213 const { signal } = abortController; 214 abortController.abort(); 215 for (const mediation of mediations) { 216 const requestPromise = navigator.credentials.create({ 217 mediation, 218 signal, 219 }); 220 await promise_rejects_dom(t, "AbortError", requestPromise); 221 } 222 }, "Mediation is implicitly required and hence ignored. Request is aborted regardless."); 223 224 promise_test(async (t) => { 225 await promise_rejects_js( 226 t, 227 TypeError, 228 navigator.credentials.create({digital: {}})); 229 }, "`requests` field is required in the options object."); 230 231 promise_test(async (t) => { 232 const throwingValues = [ 233 BigInt(123), 234 (() => { const o = {}; o.self = o; return o; })(), 235 Symbol("foo") 236 ]; 237 238 for (const badValue of throwingValues) { 239 const options = makeCreateOptions({ data: badValue }); 240 241 await promise_rejects_js( 242 t, 243 TypeError, 244 navigator.credentials.create(options), 245 `Should throw for: ${String(badValue)}` 246 ); 247 } 248 }, "Throws TypeError when request data is not JSON stringifiable."); 249 250 promise_test(async (t) => { 251 const options = { 252 password: document.createElement("form"), 253 }; 254 await promise_rejects_js( 255 t, 256 TypeError, 257 navigator.credentials.create(options), 258 "Should throw for invalid form element" 259 ); 260 }, "Throws when form element does not contain password."); 261 </script>