updateWith-call-immediate-manual.https.html (6444B)
1 <!doctype html> 2 <meta charset="utf8"> 3 <link rel="help" href="https://www.w3.org/TR/payment-request/#updatewith()-method"> 4 <link rel="help" href="https://github.com/w3c/payment-request/pull/591"> 5 <title> 6 PaymentRequestUpdateEvent.updateWith() needs to be called immediately 7 </title> 8 <script src="/resources/testharness.js"></script> 9 <script src="/resources/testharnessreport.js"></script> 10 <script> 11 setup({ explicit_done: true, explicit_timeout: true }); 12 const applePay = Object.freeze({ 13 supportedMethods: "https://apple.com/apple-pay", 14 data: { 15 version: 3, 16 merchantIdentifier: "merchant.com.example", 17 countryCode: "US", 18 merchantCapabilities: ["supports3DS"], 19 supportedNetworks: ["visa"], 20 } 21 }); 22 const validMethod = Object.freeze({ supportedMethods: "basic-card" }); 23 const validMethods = Object.freeze([validMethod, applePay]); 24 const validAmount = Object.freeze({ currency: "USD", value: "5.00" }); 25 const validTotal = Object.freeze({ 26 label: "label", 27 amount: validAmount, 28 }); 29 const validShippingOptionA = Object.freeze({ 30 id: "a-shipping-option", 31 label: "A shipping option", 32 amount: validAmount, 33 selected: true, 34 }); 35 const validShippingOptionB = Object.freeze({ 36 id: "b-shipping-option", 37 label: "B shipping option", 38 amount: validAmount, 39 }); 40 const validDetails = Object.freeze({ 41 total: validTotal, 42 shippingOptions: [validShippingOptionA, validShippingOptionB], 43 }); 44 const validOptions = Object.freeze({ 45 requestShipping: true, 46 }); 47 48 function testImmediateUpdate({ textContent: testName }) { 49 promise_test(async t => { 50 const request = new PaymentRequest( 51 validMethods, 52 validDetails, 53 validOptions 54 ); 55 const eventPromise = new Promise((resolve, reject) => { 56 request.addEventListener( 57 "shippingaddresschange", 58 ev => { 59 // Forces updateWith() to be run in the next event loop tick so that 60 // [[waitForUpdate]] is already true when it runs. 61 t.step_timeout(() => { 62 try { 63 ev.updateWith(validDetails); 64 resolve(); // This is bad. 65 } catch (err) { 66 reject(err); // this is good. 67 } 68 }); 69 }, 70 { once: true } 71 ); 72 }); 73 const acceptPromise = request.show(); 74 await promise_rejects_dom( 75 t, 76 "InvalidStateError", 77 eventPromise, 78 "The event loop already spun, so [[waitForUpdate]] is now true" 79 ); 80 const response = await acceptPromise; 81 await response.complete(); 82 }, testName.trim()); 83 } 84 85 function testSubsequentUpdateWithCalls({ textContent: testName }) { 86 promise_test(async t => { 87 const request = new PaymentRequest( 88 validMethods, 89 validDetails, 90 validOptions 91 ); 92 const eventPromise = new Promise((resolve, reject) => { 93 request.addEventListener("shippingaddresschange", async ev => { 94 const p = Promise.resolve(validDetails); 95 ev.updateWith(p); 96 await p; 97 try { 98 ev.updateWith(validDetails); 99 resolve(); // this is bad, we should never get to here. 100 } catch (err) { 101 reject(err); // this is good! 102 } 103 }); 104 }); 105 const responsePromise = request.show(); 106 await promise_rejects_dom( 107 t, 108 "InvalidStateError", 109 eventPromise, 110 "Expected eventPromise to have rejected, because updateWith() was a called twice" 111 ); 112 const response = await responsePromise; 113 await response.complete(); 114 }, testName.trim()); 115 } 116 117 function testRecycleEvents({ textContent: testName }) { 118 promise_test(async t => { 119 const request = new PaymentRequest( 120 validMethods, 121 validDetails, 122 validOptions 123 ); 124 125 // Register both listeners. 126 const addressChangedPromise = new Promise(resolve => { 127 request.addEventListener("shippingaddresschange", resolve, { 128 once: true, 129 }); 130 }); 131 132 const optionChangedPromise = new Promise(resolve => { 133 request.addEventListener("shippingoptionchange", resolve, { 134 once: true, 135 }); 136 }); 137 138 const responsePromise = request.show(); 139 140 // Let's wait for the address to change. 141 const addressChangeEvent = await addressChangedPromise; 142 143 // Sets [[waitingForUpdate]] to true. 144 addressChangeEvent.updateWith(validDetails); 145 146 // Let's wait for the shippingOption. 147 const optionChangeEvent = await optionChangedPromise; 148 149 // Here, we try to be sneaky, and reuse the addressChangeEvent to perform the update. 150 // However, addressChangeEvent [[waitingForUpdate]] is true, so it throws. 151 assert_throws_dom( 152 "InvalidStateError", 153 () => { 154 addressChangeEvent.updateWith(validDetails); 155 }, 156 "addressChangeEvent [[waitingForUpdate]] is true, so it must throw" 157 ); 158 159 // But optionChangeEvent is still usable tho, so... 160 optionChangeEvent.updateWith(validDetails); 161 162 assert_throws_dom( 163 "InvalidStateError", 164 () => { 165 optionChangeEvent.updateWith(validDetails); 166 }, 167 "optionChangeEvent [[waitingForUpdate]] is true, so it must throw" 168 ); 169 170 const response = await responsePromise; 171 await response.complete(); 172 }, testName.trim()); 173 } 174 </script> 175 <h2>updateWith() method</h2> 176 <p> 177 Click on each button in sequence from top to bottom without refreshing the page. 178 Each button will bring up the Payment Request UI window. 179 </p> 180 <p> 181 When the payment sheet is shown, select a different shipping address once. Then pay. 182 </p> 183 <ol> 184 <li id="test-0"> 185 <button onclick="testImmediateUpdate(this);"> 186 updateWith() must be called immediately, otherwise must throw an InvalidStateError. 187 </button> 188 </li> 189 <li id="test-1"> 190 <button onclick="testSubsequentUpdateWithCalls(this);"> 191 Once the event has performed an update, subsequent calls to updateWith() must throw InvalidStateError. 192 </button> 193 </li> 194 <li id="test-2"> 195 <button onclick="testRecycleEvents(this);"> 196 Recycling events must not be possible. 197 </button> When the payment sheet is shown, select a different shipping address once, then change shipping option once. Then pay. 198 </li> 199 <li> 200 <button onclick="done();">Done!</button> 201 </li> 202 </ol> 203 <small> 204 If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> 205 and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. 206 </small>