blob: 971622e8a32ee97e3023dbea25c1545b457b664b [file] [log] [blame] [edit]
<!DOCTYPE html>
<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<meta charset="utf-8">
<title>Test for PaymentRequest Constructor</title>
<link rel="help" href="https://w3c.github.io/browser-payment-api/#constructor">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
"use strict";
const basicCard = Object.freeze({ supportedMethods: ["basic-card"] });
const defaultMethods = Object.freeze([basicCard]);
const defaultDetails = Object.freeze({
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
});
test(() => {
const request = new PaymentRequest(defaultMethods, defaultDetails);
assert_true(!!request.id, "must be some truthy value");
}, "If details.id is missing, assign a identifier");
test(() => {
const request1 = new PaymentRequest(defaultMethods, defaultDetails);
const request2 = new PaymentRequest(defaultMethods, defaultDetails);
assert_not_equals(request1.id, request2.id, "UA generated ID must be unique");
}, "If details.id is missing, assign a unique identifier");
test(() => {
const newDetails = Object.assign({}, defaultDetails, { id: "test123" });
const request1 = new PaymentRequest(defaultMethods, newDetails);
const request2 = new PaymentRequest(defaultMethods, newDetails);
assert_equals(request1.id, newDetails.id, `id must be ${newDetails.id}`);
assert_equals(request2.id, newDetails.id, `id must be ${newDetails.id}`);
assert_equals(request1.id, request2.id, "ids need to be the same");
}, "If the same id is provided, then use it");
test(() => {
const newDetails = Object.assign({}, defaultDetails, {
id: "".padStart(1024, "a"),
});
const request = new PaymentRequest(defaultMethods, newDetails);
assert_equals(
request.id,
newDetails.id,
`id must be provided value, even if very long and contain spaces`
);
}, "Use ids even if they are strange");
test(() => {
assert_equals(
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
id: "foo",
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
}
).id,
"foo"
);
}, "Use provided request ID");
test(() => {
assert_throws(
{
name: "TypeError",
},
() => {
new PaymentRequest([], {
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
});
}
);
}, "If the length of the methodData sequence is zero, then throw a TypeError");
test(() => {
assert_throws(
{
name: "TypeError",
},
() => {
new PaymentRequest(
[
{
supportedMethods: [],
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
}
);
}
);
}, "If the length of the paymentMethod.supportedMethods sequence is zero, " + "then throw a TypeError");
test(() => {
let itThrows = false;
try {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
data: ["some-data"],
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
}
);
} catch (err) {
itThrows = true;
}
assert_false(itThrows, "shouldn't throw when using a list");
}, "Method data must be JSON-serializable object (a list in this case)");
test(() => {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
data: {
some: "data",
},
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
}
);
}, "Method data must be JSON-serializable object (an object in this case)");
test(() => {
const recursiveDictionary = {};
recursiveDictionary.foo = recursiveDictionary;
assert_throws(
{
name: "TypeError",
},
() => {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
data: recursiveDictionary,
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
}
);
}
);
assert_throws(
{
name: "TypeError",
},
() => {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
data: "a string",
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
}
);
}
);
assert_throws(
{
name: "TypeError",
},
() => {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
data: null,
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
}
);
},
"Even though null is JSON-serializable, it's not type 'Object' per ES spec"
);
}, "Rethrow any exceptions of JSON-serializing paymentMethod.data into a string");
// process total
const invalidAmounts = [
"-",
"notdigits",
"ALSONOTDIGITS",
"10.",
".99",
"-10.",
"-.99",
"10-",
"1-0",
"1.0.0",
"1/3",
"",
null,
" 1.0 ",
" 1.0 ",
"1.0 ",
"USD$1.0",
"$1.0",
{
toString() {
return " 1.0";
},
},
];
const invalidTotalAmounts = invalidAmounts.concat([
"-1",
"-1.0",
"-1.00",
"-1000.000",
]);
for (const amount of invalidTotalAmounts) {
test(() => {
assert_throws(
{
name: "TypeError",
},
() => {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: amount,
},
},
}
);
}
);
}, `If details.total.amount.value is not a valid decimal monetary value (in this case "${amount}"), then throw a TypeError`);
}
for (const prop in ["displayItems", "shippingOptions", "modifiers"]) {
test(() => {
try {
const details = Object.assign({}, defaultDetails, { [prop]: [] });
new PaymentRequest(defaultMethods, details);
} catch (err) {
assert_true(false, `${prop} can be zero length`);
}
}, `PaymentDetailsBase.${prop} can be 0 length`);
}
test(() => {
assert_throws(
{
name: "TypeError",
},
() => {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "-1.00",
},
},
}
);
}
);
}, "If the first character of details.total.amount.value is U+002D HYPHEN-MINUS, then throw a TypeError");
for (const amount of invalidAmounts) {
test(() => {
assert_throws(
{
name: "TypeError",
},
() => {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
displayItems: [
{
label: "",
amount: {
currency: "USD",
value: amount,
},
},
],
}
);
}
);
}, "For each item in details.displayItems: if item.amount.value is not " + 'a valid decimal monetary value (in this case "' + amount + '"), then throw a TypeError');
}
test(() => {
let itThrows = false;
try {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
data: {
supportedTypes: ["debit"],
},
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
displayItems: [
{
label: "",
amount: {
currency: "USD",
value: "-1000",
},
},
{
label: "",
amount: {
currency: "AUD",
value: "-2000.00",
},
},
],
}
);
} catch (err) {
itThrows = true;
}
assert_false(itThrows, "shouldn't throw when given a negative value");
}, "Negative values are allowed for displayItems.amount.value, irrespective of total amount");
test(() => {
let itThrows = false;
const largeMoney = "1".repeat(510);
try {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: `${largeMoney}.${largeMoney}`,
},
},
displayItems: [
{
label: "",
amount: {
currency: "USD",
value: `-${largeMoney}`,
},
},
{
label: "",
amount: {
currency: "AUD",
value: `-${largeMoney}.${largeMoney}`,
},
},
],
}
);
} catch (err) {
itThrows = true;
}
assert_false(itThrows, "shouldn't throw when given absurd monetary values");
}, "it handles high precision currency values without throwing");
// Process shipping options:
const defaultAmount = Object.freeze({
currency: "USD",
value: "0.0",
});
const defaultShippingOption = Object.freeze({
id: "default",
label: "",
amount: defaultAmount,
selected: false,
});
const defaultShippingOptions = Object.freeze([
Object.assign({}, defaultShippingOption),
]);
for (const amount of invalidAmounts) {
test(() => {
assert_throws(
{
name: "TypeError",
},
() => {
const invalidAmount = Object.assign({}, defaultAmount, {
value: amount,
});
const invalidShippingOption = Object.assign({}, defaultShippingOption, {
amount: invalidAmount,
});
const details = Object.assign({}, defaultDetails, {
shippingOptions: [invalidShippingOption],
});
new PaymentRequest(defaultMethods, details);
}
);
}, "For each option in details.shippingOptions: if option.amount.value is not " + 'a valid decimal monetary value (in this case "' + amount + '"), then throw a TypeError');
}
test(() => {
const shippingOptions = [defaultShippingOption];
const details = Object.assign({}, defaultDetails, { shippingOptions });
const request = new PaymentRequest(defaultMethods, details);
assert_equals(
request.shippingOption,
null,
"request.shippingOption must be null"
);
}, "If there is no selected shipping option, then PaymentRequest.shippingOption remains null");
test(() => {
const selectedOption = Object.assign({}, defaultShippingOption, {
selected: true,
id: "PASS",
});
const shippingOptions = [selectedOption];
const details = Object.assign({}, defaultDetails, { shippingOptions });
const request = new PaymentRequest(defaultMethods, details);
assert_equals(request.shippingOption, "PASS", "selected option must be PASS");
}, "If there is a selected shipping option, then it becomes synchronously selected");
test(() => {
const failOption1 = Object.assign({}, defaultShippingOption, {
selected: true,
id: "FAIL1",
});
const failOption2 = Object.assign({}, defaultShippingOption, {
selected: false,
id: "FAIL2",
});
const passOption = Object.assign({}, defaultShippingOption, {
selected: true,
id: "PASS",
});
const shippingOptions = [failOption1, failOption2, passOption];
const details = Object.assign({}, defaultDetails, { shippingOptions });
const request = new PaymentRequest(defaultMethods, details);
assert_equals(request.shippingOption, "PASS", "selected option must PASS");
}, "If there is a multiple selected shipping options, only the last is selected");
test(() => {
const selectedOption = Object.assign({}, defaultShippingOption, {
selected: true,
});
const unselectedOption = Object.assign({}, defaultShippingOption, {
selected: false,
});
const shippingOptions = [selectedOption, unselectedOption];
const details = Object.assign({}, defaultDetails, { shippingOptions });
const request = new PaymentRequest(defaultMethods, details);
assert_equals(request.shippingOption, null, "selected option must be null");
}, "If there are any duplicate shipping option ids, then there are no shipping options");
// Process payment details modifiers:
for (const amount of invalidTotalAmounts) {
test(() => {
assert_throws(
{
name: "TypeError",
},
() => {
const invalidModifier = {
supportedMethods: ["basic-card"],
total: {
label: "",
amount: {
currency: "USD",
value: amount,
},
},
};
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
modifiers: [invalidModifier],
total: {
label: "",
amount: {
currency: "USD",
value: "1.0",
},
},
}
);
}
);
}, `If modifier.total.amount.value is not a valid decimal monetary value (in this case "${amount}"), then throw a TypeError`);
}
for (const amount of invalidAmounts) {
test(() => {
assert_throws(
{
name: "TypeError",
},
() => {
const invalidModifier = {
supportedMethods: ["basic-card"],
total: {
label: "",
amount: {
currency: "USD",
value: "1.0",
},
},
additionalDisplayItems: [
{
label: "",
amount: {
currency: "USD",
value: amount,
},
},
],
};
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
modifiers: [invalidModifier],
total: {
label: "",
amount: {
currency: "USD",
value: "1.0",
},
},
}
);
}
);
}, `If amount.value of additionalDisplayItems is is not a valid decimal monetary value (in this case "${amount}"), then throw a TypeError`);
}
test(() => {
let itThrows = false;
try {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
modifiers: [
{
supportedMethods: ["basic-card"],
data: ["some-data"],
},
],
}
);
} catch (err) {
itThrows = true;
}
assert_false(itThrows, "shouldn't throw when given a list");
}, "Modifier data must be JSON-serializable object (a list in this case)");
test(() => {
let itThrows = false;
try {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
modifiers: [
{
supportedMethods: ["basic-card"],
data: {
some: "data",
},
},
],
}
);
} catch (err) {
itThrows = true;
}
assert_false(itThrows, "shouldn't throw when given an object value");
}, "Modifier data must be JSON-serializable object (a object in this case)");
test(() => {
const recursiveDictionary = {};
recursiveDictionary.foo = recursiveDictionary;
assert_throws(
{
name: "TypeError",
},
() => {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
modifiers: [
{
supportedMethods: ["basic-card"],
data: recursiveDictionary,
},
],
}
);
}
);
assert_throws(
{
name: "TypeError",
},
() => {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
modifiers: [
{
supportedMethods: ["basic-card"],
data: "a string",
},
],
}
);
}
);
assert_throws(
{
name: "TypeError",
},
() => {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
modifiers: [
{
supportedMethods: ["basic-card"],
data: null,
},
],
}
);
}
);
}, "Rethrow any exceptions of JSON-serializing modifier.data into a string");
//Setting ShippingType attribute during construction
test(() => {
assert_throws(
{
name: "TypeError",
},
() => {
new PaymentRequest(
[
{
supportedMethods: ["basic-card"],
},
],
{
total: {
label: "",
amount: {
currency: "USD",
value: "1.00",
},
},
},
{
shippingType: "invalid",
}
);
}
);
}, "Shipping type should be valid");
test(() => {
const request = new PaymentRequest(defaultMethods, defaultDetails, {});
assert_equals(request.shippingAddress, null, "must be null");
}, "PaymentRequest.shippingAddress must initially be null");
test(() => {
const request1 = new PaymentRequest(defaultMethods, defaultDetails, {});
assert_equals(request1.shippingType, null, "must be null");
const request2 = new PaymentRequest(defaultMethods, defaultDetails, {
requestShipping: false,
});
assert_equals(request2.shippingType, null, "must be null");
}, "If options.requestShipping is not set, then request.shippingType attribute is null.");
test(() => {
// option.shippingType defaults to 'shipping'
const request1 = new PaymentRequest(defaultMethods, defaultDetails, {
requestShipping: true,
});
assert_equals(request1.shippingType, "shipping", "must be shipping");
const request2 = new PaymentRequest(defaultMethods, defaultDetails, {
requestShipping: true,
shippingType: "delivery",
});
assert_equals(request2.shippingType, "delivery", "must be delivery");
}, "If options.requestShipping is true, request.shippingType will be options.shippingType.");
</script>