payment-request-ctor-currency-code-checks.https.sub.html (9707B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <title>Test currency code usage in PaymentRequest Constructor</title> 4 <link rel="help" href="https://w3c.github.io/browser-payment-api/#constructor"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script> 8 const defaultMethods = [ 9 Object.freeze({ 10 supportedMethods: "https://{{domains[nonexistent]}}", 11 }), 12 ]; 13 const defaultAmount = Object.freeze({ 14 currency: "USD", 15 value: "1.00", 16 }); 17 const defaultTotal = Object.freeze({ 18 label: "Total", 19 amount: defaultAmount, 20 }); 21 22 const defaultDetails = Object.freeze({ 23 total: Object.freeze({ 24 label: "", 25 amount: defaultAmount, 26 }), 27 }); 28 29 // The following are the same set of valid/invalid codes that are used in 30 // the ECMAScript Internationalization API Specification (ECMA-402) test suite. 31 const wellFormedCurrencyCodes = [ 32 "BOB", 33 "EUR", 34 "usd", // currency codes are case-insensitive 35 "XdR", 36 "xTs", 37 ]; 38 39 const invalidCurrencyCodes = [ 40 "", 41 "€", 42 "$", 43 "SFr.", 44 "DM", 45 "KR₩", 46 "702", 47 "ßP", 48 "ınr", 49 ]; 50 51 const RANGE_ERROR = RangeError; 52 53 const invalidAmount = Object.freeze({ 54 currency: "¡INVALID!", 55 value: "1.00", 56 }); 57 58 const invalidTotal = { 59 total: { 60 label: "Invalid total", 61 amount: invalidAmount, 62 }, 63 }; 64 65 // Ensure we don't get false positives 66 function smokeTest() { 67 new PaymentRequest(defaultMethods, invalidTotal); 68 } 69 70 // Process the total: 71 test(() => { 72 assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); 73 for (const validCurrency of wellFormedCurrencyCodes) { 74 const amount = { 75 currency: validCurrency, 76 value: "1.00", 77 }; 78 const total = { 79 label: "Total", 80 amount, 81 }; 82 const details = { 83 total, 84 }; 85 try { 86 new PaymentRequest(defaultMethods, details); 87 } catch (err) { 88 assert_unreached( 89 `Unexpected exception for details.total.amount "${validCurrency}": ${err.message}` 90 ); 91 } 92 } 93 }, "Check and canonicalize valid details.total.amount"); 94 95 test(() => { 96 assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); 97 for (const invalidCurrency of invalidCurrencyCodes) { 98 const amount = { 99 currency: invalidCurrency, 100 value: "1.00", 101 }; 102 const total = { 103 label: "Total", 104 amount, 105 }; 106 const details = { 107 total, 108 }; 109 assert_throws_js( 110 RANGE_ERROR, 111 () => { 112 new PaymentRequest(defaultMethods, details); 113 }, 114 `Expected RangeError for details.total.amount given ("${invalidCurrency}").` 115 ); 116 } 117 }, "Check and canonicalize invalid details.total.amount and rethrow any exceptions."); 118 119 // If the displayItems member of details is present, then for each item in details.displayItems: 120 test(() => { 121 assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); 122 const displayItems = []; 123 for (const validCurrency of wellFormedCurrencyCodes) { 124 const amount = { 125 currency: validCurrency, 126 value: "123", 127 }; 128 const displayItem = { 129 amount, 130 label: "valid currency", 131 }; 132 const details = { 133 total: defaultTotal, 134 displayItems: [displayItem], 135 }; 136 try { 137 new PaymentRequest(defaultMethods, details); 138 } catch (err) { 139 assert_unreached( 140 `Unexpected error with displayItem that had a valid currency "${validCurrency}": ${err.message}` 141 ); 142 } 143 displayItems.push(displayItem); 144 } 145 // Let's make sure it doesn't throw given a list of valid displayItems 146 try { 147 const details = Object.assign({}, defaultDetails, { displayItems }); 148 new PaymentRequest(defaultMethods, details); 149 } catch (err) { 150 assert_unreached( 151 `Unexpected error with multiple valid displayItems: ${err.message}` 152 ); 153 } 154 }, "Check and canonicalize valid details.displayItems amount"); 155 156 test(() => { 157 assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); 158 for (const invalidCurrency of invalidCurrencyCodes) { 159 const amount = { 160 currency: invalidCurrency, 161 value: "123", 162 }; 163 const displayItem = { 164 amount, 165 label: "invalid currency", 166 }; 167 const details = { 168 total: defaultTotal, 169 displayItems: [displayItem], 170 }; 171 assert_throws_js( 172 RANGE_ERROR, 173 () => { 174 new PaymentRequest(defaultMethods, details); 175 }, 176 `Expected RangeError with invalid displayItem currency "${invalidCurrency}".` 177 ); 178 } 179 }, "Check and canonicalize invalid details.displayItems amount and rethrow RangeError."); 180 181 // Process shipping options: 182 test(() => { 183 assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); 184 const shippingOptions = []; 185 for (const validCurrency of wellFormedCurrencyCodes) { 186 const shippingOption = { 187 id: `test` + Math.random(), 188 label: "shipping option", 189 amount: { currency: validCurrency, value: "5.00" }, 190 selected: !shippingOptions.length, 191 }; 192 const details = { 193 total: defaultTotal, 194 shippingOptions: [shippingOption], 195 }; 196 try { 197 new PaymentRequest(defaultMethods, details, { requestShipping: true }); 198 } catch (err) { 199 assert_unreached( 200 `Unexpected exception with valid shippingOption currency code "${validCurrency}": ${err.message}.` 201 ); 202 } 203 shippingOptions.push(shippingOption); 204 } 205 try { 206 const details = Object.assign({}, defaultDetails, { shippingOptions }); 207 new PaymentRequest(defaultMethods, details, { requestShipping: true }); 208 } catch (err) { 209 assert_unreached( 210 `Unexpected error with multiple valid shppingOptions: ${err.message}.` 211 ); 212 } 213 }, "Check and canonicalize valid details.shippingOptions amount."); 214 215 test(() => { 216 assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); 217 for (const invalidCurrency of invalidCurrencyCodes) { 218 const shippingOption = { 219 id: "test", 220 label: "shipping option", 221 amount: { currency: invalidCurrency, value: "5.00" }, 222 selected: true, 223 }; 224 const details = { 225 total: defaultTotal, 226 shippingOptions: [shippingOption], 227 }; 228 assert_throws_js( 229 RANGE_ERROR, 230 () => { 231 new PaymentRequest(defaultMethods, details, { requestShipping: true }); 232 }, 233 `Expected RangeError with invalid shippingOption currency code "${invalidCurrency}".` 234 ); 235 } 236 }, "Check and canonicalize invalid details.shippingOptions amount and rethrow RangeError."); 237 238 // Process payment details modifiers: 239 test(() => { 240 assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); 241 for (const validCurrency of wellFormedCurrencyCodes) { 242 const modifier = { 243 supportedMethods: "https://{{domains[nonexistent]}}", 244 total: { 245 label: "Total due", 246 amount: { currency: validCurrency, value: "68.00" }, 247 }, 248 }; 249 const details = { 250 total: defaultTotal, 251 modifiers: [modifier], 252 }; 253 try { 254 new PaymentRequest(defaultMethods, details); 255 } catch (err) { 256 assert_unreached( 257 `Unexpected error with valid modifier currency code "${validCurrency}": ${err.message}` 258 ); 259 } 260 } 261 }, "Check and canonicalize valid modifiers[n].total amount."); 262 263 test(() => { 264 assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); 265 for (const invalidCurrency of invalidCurrencyCodes) { 266 const modifier = { 267 supportedMethods: "https://{{domains[nonexistent]}}", 268 total: { 269 label: "Total due", 270 amount: { currency: invalidCurrency, value: "68.00" }, 271 }, 272 }; 273 const details = { 274 total: defaultTotal, 275 modifiers: [modifier], 276 }; 277 assert_throws_js( 278 RANGE_ERROR, 279 () => { 280 new PaymentRequest(defaultMethods, details); 281 }, 282 `Expected RangeError with invalid modifier currency code "${invalidCurrency}".` 283 ); 284 } 285 }, "Check and canonicalize invalid modifiers[n].total amount and rethrow RangeError."); 286 287 // Process payment details modifiers: 288 test(() => { 289 assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); 290 for (const validCurrency of wellFormedCurrencyCodes) { 291 const additionalItem = { 292 label: "additionalItem", 293 amount: { currency: validCurrency, value: "3.00" }, 294 }; 295 const modifier = { 296 supportedMethods: "https://{{domains[nonexistent]}}", 297 total: defaultTotal, 298 additionalDisplayItems: [additionalItem], 299 }; 300 const details = { 301 total: defaultTotal, 302 modifiers: [modifier], 303 }; 304 try { 305 new PaymentRequest(defaultMethods, details); 306 } catch (err) { 307 assert_unreached( 308 `Unexpected error with valid additionalDisplayItems[n] currency code "${validCurrency}": ${err.message}` 309 ); 310 } 311 } 312 }, "Check and canonicalize valid modifiers[n].additionaDisplayItem amount."); 313 314 test(() => { 315 assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); 316 for (const invalidCurrency of invalidCurrencyCodes) { 317 const additionalItem = { 318 label: "additionalItem", 319 amount: { currency: invalidCurrency, value: "3.00" }, 320 }; 321 const modifier = { 322 supportedMethods: "https://{{domains[nonexistent]}}", 323 total: defaultTotal, 324 additionalDisplayItems: [additionalItem], 325 }; 326 const details = { 327 total: defaultTotal, 328 modifiers: [modifier], 329 }; 330 assert_throws_js( 331 RANGE_ERROR, 332 () => { 333 new PaymentRequest(defaultMethods, details); 334 }, 335 `Expected RangeError with invalid additionalDisplayItems[n] currency code "${invalidCurrency}".` 336 ); 337 } 338 }, "Check and canonicalize invalid modifiers[n].additionaDisplayItem amount and rethrow RangeError."); 339 </script>