blob: 64a411ac5b10f6b2354f023bf5b27ff1ec179689 [file] [log] [blame]
<!doctype html>
<meta charset="utf8">
<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-retry">
<title>
PaymentResponse.prototype.retry() method
</title>
<script src="/js-test-resources/ui-helper.js"></script>
<script src="/resources/payment-request.js"></script>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/helpers.js"></script>
<script>
setUpAndSmokeTest({ explicit_timeout: true });
</script>
<body>
<script>
test(() => {
assert_true(
"retry" in PaymentResponse.prototype,
"retry must be in prototype"
);
assert_true(
PaymentResponse.prototype.retry instanceof Function,
"retry must be a function"
);
}, "PaymentResponse.prototype must have a retry() function (smoke test).");
promise_test(async t => {
const { response } = await getPaymentRequestResponse();
// sets response.[[complete]] to true.
await response.complete("success");
return promise_rejects(
t,
"InvalidStateError",
response.retry({}),
"response.[[complete]] is true, so rejects with InvalidStateError."
);
}, "A completed payment request cannot be retried.");
promise_test(async t => {
const { response } = await getPaymentRequestResponse();
const retryPromise = response.retry({});
await promise_rejects(
t,
"InvalidStateError",
response.retry({}),
"Calling retry() again rejects with an InvalidStateError"
);
internals.mockPaymentCoordinator.acceptPayment();
await retryPromise;
await response.complete("success");
}, "Calling retry() more than once yield a rejected promise, but the retryPromise resolves independently.");
promise_test(async t => {
const { response } = await getPaymentRequestResponse();
const retryPromise = response.retry({});
await promise_rejects(
t,
"InvalidStateError",
response.complete("success"),
"Calling complete() while retrying rejects with an InvalidStateError"
);
internals.mockPaymentCoordinator.acceptPayment();
await retryPromise;
await response.complete("success");
}, "Calling complete() while a retry() is in progress results in an InvalidStateError.");
promise_test(async t => {
const { response, request } = await getPaymentRequestResponse();
const retryPromise = response.retry({});
await promise_rejects(
t,
"InvalidStateError",
request.abort(),
"Calling request.abort() while retrying rejects with an InvalidStateError"
);
internals.mockPaymentCoordinator.acceptPayment();
await retryPromise;
await response.complete("success");
}, "Trying to abort() via PaymentRequest is not possible.");
promise_test(async t => {
const { response } = await getPaymentRequestResponse();
var promise = response.retry({});
internals.mockPaymentCoordinator.acceptPayment();
assert_equals(
await promise,
undefined,
"Expected undefined as the resolve value"
);
promise = response.retry({});
internals.mockPaymentCoordinator.acceptPayment();
assert_equals(
await promise,
undefined,
"Expected undefined as the resolve value"
);
await response.complete("success");
await promise_rejects(
t,
"InvalidStateError",
response.retry({}),
"Calling retry() after complete() rejects with a InvalidStateError"
);
}, "It's possible to retry() multiple times and eventually complete(). After complete(), however, retry() rejects with an InvalidStateError.");
promise_test(async t => {
const { response } = await getPaymentRequestResponse();
var promise = response.retry({});
internals.mockPaymentCoordinator.cancelPayment();
await promise_rejects(
t,
"AbortError",
promise,
"The user aborting a retry rejects with a AbortError"
);
await promise_rejects(
t,
"InvalidStateError",
response.retry({}),
"After the user aborts, response [[complete]] is true so retry() must reject with InvalidStateError"
);
await promise_rejects(
t,
"InvalidStateError",
response.complete("success"),
"After the user aborts, response [[complete]] is true, so complete() rejects with a InvalidStateError"
);
}, "The user aborting retrying a payment causes the retryPromise to reject with AbortError. Aborting a payment is causes it complete.");
promise_test(async t => {
const { response, request } = await getPaymentRequestResponse({ requestShipping: true });
const retryPromise = response.retry({
shippingAddress: { city: "Invalid city", addressLine: "Invalid address line" },
});
const errors = internals.mockPaymentCoordinator.errors;
assert_equals(errors.length, 2, "Must have two errors");
assert_equals(errors[0].code, "shippingContactInvalid", "Must be a 'shippingContactInvalid' error");
assert_equals(errors[0].message, "Invalid address line", "Error message must match");
assert_equals(errors[0].contactField, "addressLines", "Contact field must match");
assert_equals(errors[1].code, "shippingContactInvalid", "Must be a 'shippingContactInvalid' error");
assert_equals(errors[1].message, "Invalid city", "Error message must match");
assert_equals(errors[1].contactField, "locality", "Contact field must match");
internals.mockPaymentCoordinator.acceptPayment();
await retryPromise;
await response.complete("success");
}, "When retrying, the user is shown error fields to fix.");
promise_test(async t => {
const { response, request } = await getPaymentRequestResponse({ requestShipping: true });
// causes "abort the update" to run
const shippingChangedPromise = new Promise(resolve => {
request.onshippingoptionchange = () => {
event.updateWith({ total: { amount: { currency: "USD", value: "INVALID" } }});
resolve();
};
});
const retryPromise = response.retry({});
internals.mockPaymentCoordinator.changeShippingOption("option");
await shippingChangedPromise;
await promise_rejects(
t,
new TypeError(),
retryPromise,
"retry() aborts with a TypeError, because totals' value is invalid"
);
await promise_rejects(
t,
"InvalidStateError",
response.complete("success"),
"After the user aborts, response [[complete]] is true, so complete() rejects with a InvalidStateError"
);
}, 'When "abort the update" occurs because of an update error, the `retryPromise` is rejected and response.[[complete]] becomes true.');
promise_test(async t => {
const { response } = await getPaymentRequestResponse();
const retryPromise = response.retry({});
const promises = new Set([
retryPromise,
response.retry({}).catch(() => {}),
response.retry({}).catch(() => {}),
]);
assert_equals(promises.size, 3, "Must have three unique objects");
internals.mockPaymentCoordinator.acceptPayment();
await retryPromise;
await response.complete();
}, "Calling retry() multiple times is always a new object.");
promise_test(async t => {
const { response, request } = await getPaymentRequestResponse({ requestShipping: true });
const retryPromise = response.retry();
const errors = internals.mockPaymentCoordinator.errors;
assert_equals(errors.length, 1, "Must have one error");
assert_equals(errors[0].code, "unknown", "Must be an 'unknown' error");
assert_equals(errors[0].message, "", "Error message must match");
assert_equals(errors[0].contactField, undefined, "Contact field must match");
internals.mockPaymentCoordinator.acceptPayment();
await retryPromise;
await response.complete("success");
}, "When retrying without errors, the user is shown an `unknown` error.");
promise_test(async t => {
const { response, request } = await getPaymentRequestResponse({ requestShipping: true });
var originalDetails = response.details;
const retryPromise = response.retry();
internals.mockPaymentCoordinator.acceptPayment();
await retryPromise;
assert_not_equals(response.details, originalDetails, "response.details should be a new object after the user accepts a retry");
await response.complete("success");
}, "response.details should be updated after the user accepts a retry.");
</script>