test_currency_amount_validation.html (8075B)
1 <!DOCTYPE HTML> 2 <meta charset="utf-8"> 3 <!-- 4 https://bugzilla.mozilla.org/show_bug.cgi?id=1367669 5 https://bugzilla.mozilla.org/show_bug.cgi?id=1388661 6 --> 7 <title>Test for PaymentRequest API currency amount validation</title> 8 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> 9 <script src="/tests/SimpleTest/SimpleTest.js"></script> 10 <script> 11 "use strict"; 12 SimpleTest.waitForExplicitFinish(); 13 14 const gUrl = SimpleTest.getTestFileURL( 15 "CurrencyAmountValidationChromeScript.js" 16 ); 17 const gScript = SpecialPowers.loadChromeScript(gUrl); 18 19 function testFailHandler(message) { 20 ok(false, message); 21 } 22 gScript.addMessageListener("test-fail", testFailHandler); 23 24 const defaultMethods = [ 25 { 26 supportedMethods: "basic-card", 27 }, 28 ]; 29 const defaultDetails = { 30 total: { 31 label: "total", 32 amount: { 33 currency: "usd", 34 value: "1.00", 35 }, 36 }, 37 }; 38 39 const specialAmountDetails = { 40 total: { 41 label: "total", 42 amount: { 43 currency: "usd", 44 value: { 45 toString() { 46 throw "42"; 47 }, 48 }, 49 }, 50 }, 51 }; 52 53 const wellFormedCurrencyCodes = [ 54 "BOB", 55 "EUR", 56 "usd", // currency codes are case-insensitive 57 "XdR", 58 "xTs", 59 ]; 60 61 const invalidCurrencyCodes = [ 62 "", 63 "€", 64 "$", 65 "SFr.", 66 "DM", 67 "KR₩", 68 "702", 69 "ßP", 70 "ınr", 71 "invalid", 72 "in", 73 "123", 74 ]; 75 76 const updatedInvalidCurrencyDetails = { 77 total: { 78 label: "Total", 79 amount: { 80 currency: "Invalid", 81 value: "1.00", 82 }, 83 }, 84 }; 85 86 const updatedInvalidAmountDetails = { 87 total: { 88 label: "Total", 89 amount: { 90 currency: "USD", 91 value: "-1.00", 92 }, 93 }, 94 }; 95 96 const invalidAmounts = [ 97 "-", 98 "notdigits", 99 "ALSONOTDIGITS", 100 "10.", 101 ".99", 102 "-10.", 103 "-.99", 104 "10-", 105 "1-0", 106 "1.0.0", 107 "1/3", 108 "", 109 null, 110 " 1.0 ", 111 " 1.0 ", 112 "1.0 ", 113 "USD$1.0", 114 "$1.0", 115 { 116 toString() { 117 return " 1.0"; 118 }, 119 }, 120 undefined, 121 ]; 122 const invalidTotalAmounts = invalidAmounts.concat([ 123 "-1", 124 "-1.0", 125 "-1.00", 126 "-1000.000", 127 ]); 128 129 function updateWithInvalidAmount() { 130 return new Promise((resolve, reject) => { 131 resolve(updatedInvalidAmountDetails); 132 }); 133 } 134 135 async function testWithLowerCaseCurrency() { 136 const payRequest = new PaymentRequest(defaultMethods, defaultDetails); 137 return new Promise(resolve => { 138 gScript.addMessageListener( 139 "check-complete", 140 function checkCompleteHandler() { 141 gScript.removeMessageListener("check-complete", checkCompleteHandler); 142 resolve(); 143 } 144 ); 145 gScript.sendAsyncMessage("check-lower-case-currency"); 146 }); 147 } 148 149 function testWithWellFormedCurrencyCodes() { 150 for (const currency of wellFormedCurrencyCodes) { 151 const details = { 152 total: { 153 label: "Well Formed Currency", 154 amount: { 155 currency, 156 value: "1.00", 157 }, 158 }, 159 }; 160 try { 161 const payRequest = new PaymentRequest(defaultMethods, details); 162 } catch (e) { 163 const msg = `Unexpected error while creating payment request with well-formed currency (${currency}) ${ 164 e.name 165 }`; 166 ok(false, msg); 167 } 168 } 169 } 170 171 function testWithInvalidCurrencyCodes() { 172 for (const invalidCurrency of invalidCurrencyCodes) { 173 const invalidDetails = { 174 total: { 175 label: "Invalid Currency", 176 amount: { 177 currency: invalidCurrency, 178 value: "1.00", 179 }, 180 }, 181 }; 182 try { 183 const payRequest = new PaymentRequest(defaultMethods, invalidDetails); 184 ok( 185 false, 186 `Creating a Payment Request with invalid currency (${invalidCurrency}) must throw.` 187 ); 188 } catch (e) { 189 is( 190 e.name, 191 "RangeError", 192 `Expected rejected with 'RangeError', but got '${e.name}'.` 193 ); 194 } 195 } 196 } 197 198 async function testUpdateWithInvalidCurrency() { 199 const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput( 200 true 201 ); 202 gScript.sendAsyncMessage("set-update-with-invalid-details-ui-service"); 203 const payRequest = new PaymentRequest(defaultMethods, defaultDetails); 204 payRequest.addEventListener("shippingaddresschange", event => { 205 event.updateWith(Promise.resolve(updatedInvalidCurrencyDetails)); 206 }); 207 payRequest.addEventListener("shippingoptionchange", event => { 208 event.updateWith(updatedInvalidCurrencyDetails); 209 }); 210 try { 211 await payRequest.show(); 212 ok(false, "Should have rejected with 'RangeError'"); 213 } catch (err) { 214 is( 215 err.name, 216 "RangeError", 217 `Should be rejected with 'RangeError', but got '${err.name}'.` 218 ); 219 } 220 handler.destruct(); 221 } 222 223 async function testUpdateWithInvalidAmount() { 224 const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput( 225 true 226 ); 227 gScript.sendAsyncMessage("set-update-with-invalid-details-ui-service"); 228 const payRequest = new PaymentRequest(defaultMethods, defaultDetails); 229 payRequest.addEventListener("shippingaddresschange", event => { 230 event.updateWith(updateWithInvalidAmount()); 231 }); 232 payRequest.addEventListener("shippingoptionchange", event => { 233 event.updateWith(updateWithInvalidAmount()); 234 }); 235 try { 236 await payRequest.show(); 237 ok(false, "Should be rejected with 'TypeError'"); 238 } catch (err) { 239 is( 240 err.name, 241 "TypeError", 242 `Should be rejected with 'TypeError', but got ${err.name}.` 243 ); 244 } 245 handler.destruct(); 246 } 247 248 function testSpecialAmount() { 249 try { 250 new PaymentRequest(defaultMethods, specialAmountDetails); 251 ok(false, "Should throw '42', but got resolved."); 252 } catch (e) { 253 is(e, "42", "Expected throw '42'. but got " + e); 254 } 255 } 256 257 function testInvalidTotalAmounts() { 258 for (const invalidAmount of invalidTotalAmounts) { 259 try { 260 const invalidDetails = { 261 total: { 262 label: "", 263 amount: { 264 currency: "USD", 265 value: invalidAmount, 266 }, 267 }, 268 }; 269 new PaymentRequest(defaultMethods, invalidDetails); 270 ok(false, "Should throw 'TypeError', but got resolved."); 271 } catch (err) { 272 is(err.name, "TypeError", `Expected 'TypeError', but got '${err.name}'`); 273 } 274 } 275 } 276 277 function testInvalidAmounts() { 278 for (const invalidAmount of invalidAmounts) { 279 try { 280 new PaymentRequest(defaultMethods, { 281 total: { 282 label: "", 283 amount: { 284 currency: "USD", 285 value: "1.00", 286 }, 287 }, 288 displayItems: [ 289 { 290 label: "", 291 amount: { 292 currency: "USD", 293 value: invalidAmount, 294 }, 295 }, 296 ], 297 }); 298 ok(false, "Should throw 'TypeError', but got resolved."); 299 } catch (err) { 300 is(err.name, "TypeError", `Expected 'TypeError', but got '${err.name}'.`); 301 } 302 } 303 } 304 305 function teardown() { 306 return new Promise(resolve => { 307 gScript.addMessageListener( 308 "teardown-complete", 309 function teardownCompleteHandler() { 310 gScript.removeMessageListener( 311 "teardown-complete", 312 teardownCompleteHandler 313 ); 314 gScript.removeMessageListener("test-fail", testFailHandler); 315 gScript.destroy(); 316 SimpleTest.finish(); 317 resolve(); 318 } 319 ); 320 gScript.sendAsyncMessage("teardown"); 321 }); 322 } 323 324 async function runTests() { 325 try { 326 testInvalidTotalAmounts(); 327 testSpecialAmount(); 328 testInvalidAmounts(); 329 testWithWellFormedCurrencyCodes(); 330 testWithInvalidCurrencyCodes(); 331 await testUpdateWithInvalidAmount(); 332 await testUpdateWithInvalidCurrency(); 333 await testWithLowerCaseCurrency(); 334 await teardown(); 335 } catch (e) { 336 console.error(e); 337 ok(false, "Unexpected error: " + e.name); 338 SimpleTest.finish(); 339 } 340 } 341 342 window.addEventListener("load", () => { 343 SpecialPowers.pushPrefEnv( 344 { 345 set: [["dom.payments.request.enabled", true]], 346 }, 347 runTests 348 ); 349 }); 350 </script> 351 <body> 352 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1367669">Mozilla Bug 1367669</a> 353 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1388661">Mozilla Bug 1388661</a>