<!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="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="helpers.js"></script>
<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).");
function checkCompletedCantRetry(button) {
button.disabled = true;
promise_test(async t => {
const { response } = await getPaymentRequestResponse();
// sets response.[[complete]] to true.
await response.complete("success");
return promise_rejects_dom(
t,
"InvalidStateError",
response.retry(),
"response.[[complete]] is true, so rejects with InvalidStateError."
);
}, button.textContent.trim());
}
function repeatedCallsToRetry(button) {
button.disabled = true;
promise_test(async t => {
const { response } = await getPaymentRequestResponse();
const retryPromise = response.retry();
await promise_rejects_dom(
t,
"InvalidStateError",
response.retry(),
"Calling retry() again rejects with an InvalidStateError"
);
await retryPromise;
await response.complete("success");
}, button.textContent.trim());
}
function callCompleteWhileRetrying(button) {
button.disabled = true;
promise_test(async t => {
const { response } = await getPaymentRequestResponse();
const retryPromise = response.retry();
const completePromise1 = response.complete("success");
const completePromise2 = response.complete("fail");
assert_not_equals(
completePromise1,
completePromise2,
"complete() must return unique promises"
);
await promise_rejects_dom(
t,
"InvalidStateError",
completePromise1,
"Calling complete() while retrying rejects with an InvalidStateError"
);
await promise_rejects_dom(
t,
"InvalidStateError",
completePromise2,
"Calling complete() while retrying rejects with an InvalidStateError"
);
assert_not_equals(
completePromise1,
completePromise2,
"complete() must return unique promises"
);
await retryPromise;
await response.complete("success");
}, button.textContent.trim());
}
function callingRequestAbortMustNotAbort(button) {
button.disabled = true;
promise_test(async t => {
const { response, request } = await getPaymentRequestResponse();
const retryPromise = response.retry();
await promise_rejects_dom(
t,
"InvalidStateError",
request.abort(),
"Calling request.abort() while retrying rejects with an InvalidStateError"
);
await retryPromise;
await response.complete("success");
}, button.textContent.trim());
}
function canRetryMultipleTimes(button) {
button.disabled = true;
promise_test(async t => {
const { response } = await getPaymentRequestResponse();
assert_equals(
await response.retry(),
undefined,
"Expected undefined as the resolve value"
);
assert_equals(
await response.retry(),
undefined,
"Expected undefined as the resolve value"
);
await response.complete("success");
await promise_rejects_dom(
t,
"InvalidStateError",
response.retry(),
"Calling retry() after complete() rejects with a InvalidStateError"
);
}, button.textContent.trim());
}
function userCanAbortARetry(button) {
button.disabled = true;
promise_test(async t => {
const { response } = await getPaymentRequestResponse();
await promise_rejects_dom(
t,
"AbortError",
response.retry(),
"The user aborting a retry rejects with a AbortError"
);
await promise_rejects_dom(
t,
"InvalidStateError",
response.retry(),
"After the user aborts, response [[complete]] is true so retry() must reject with InvalidStateError"
);
await promise_rejects_dom(
t,
"InvalidStateError",
response.complete("success"),
"After the user aborts, response [[complete]] is true, so complete() rejects with a InvalidStateError"
);
}, button.textContent.trim());
}
function userIsShownErrorsFields(button) {
button.disabled = true;
promise_test(async t => {
const { response, request } = await getPaymentRequestResponse({ requestShipping: true });
const retryPromise = response.retry({
shippingAddress: { city: "Invalid city", addressLine: "Invalid address line" },
});
await retryPromise;
await response.complete("success");
}, button.textContent.trim());
}
function abortTheUpdate(button) {
button.disabled = true;
promise_test(async t => {
const { response, request } = await getPaymentRequestResponse({
requestShipping: true,
});
const shipOptionChangePromise = new Promise(resolve => {
request.onshippingoptionchange = event => {
// causes "abort the update" to run
event.updateWith({ total: "error!" });
resolve();
};
});
const retryPromise = response.retry();
await shipOptionChangePromise;
await promise_rejects_js(
t,
TypeError,
retryPromise,
"retry() aborts with a TypeError, because totals' value is invalid"
);
await promise_rejects_dom(
t,
"InvalidStateError",
response.complete("success"),
"After the user aborts, response [[complete]] is true, so complete() rejects with a InvalidStateError"
);
}, button.textContent.trim());
}
function callingRetryReturnsUniquePromise(button){
button.disabled = true;
promise_test(async t => {
const { response } = await getPaymentRequestResponse();
const retryPromise = response.retry();
const promises = new Set([
retryPromise,
response.retry(),
response.retry(),
]);
assert_equals(promises.size, 3, "Must have three unique objects");
await retryPromise;
await response.complete();
}, button.textContent.trim());
};
</script>
<h2>
Manual Tests for PaymentResponse.retry() - Please run in order!
</h2>
<p>
Click on each button in sequence from top to bottom without refreshing the page.
Each button will bring up the Payment Request UI window.
</p>
<p>
When presented with the payment sheet, use any credit card select to "Pay" multiple times.
</p>
<ol>
<li>
<button onclick="checkCompletedCantRetry(this);">
A completed payment request cannot be retried.
</button>
</li>
<li>
<button onclick="repeatedCallsToRetry(this);">
Calling retry() more than once yield a rejected promise, but the
retryPromise resolves independently.
</button>
</li>
<li>
<button onclick="callCompleteWhileRetrying(this);">
Calling complete() while a retry() is in progress results in an InvalidStateError.
</button>
</li>
<li>
<button onclick="callingRequestAbortMustNotAbort(this);">
Trying to abort() via PaymentRequest is not possible.
</button>
</li>
<li>
<button onclick="canRetryMultipleTimes(this);">
It's possible to retry() multiple times and eventually complete().
After complete(), however, retry() rejects with an InvalidStateError.
</button>
</li>
<li>
<p>
When shown the payment sheet, hit pay once, then abort retrying the payment.
</p>
<button onclick="userCanAbortARetry(this);">
The user aborting retrying a payment causes the retryPromise to reject with AbortError.
Aborting a payment is causes it complete.
</button>
</li>
<li>
<p>
When shown the payment sheet, hit pay once. Check payment sheet for error fields.
Then hit escape or otherwise abort the payment.
</p>
<button onclick="userIsShownErrorsFields(this);">
When retrying, the user is shown error fields to fix.
</button>
</li>
<li>
<p>
When shown the payment sheet, hit pay once.
Then, change the shipping option.
Select to pay again.
</p>
<button onclick="abortTheUpdate(this);">
When "abort the update" occurs because of an update error,
the `retryPromise` is rejected and response.[[complete]] becomes true.
</button>
</li>
<li>
<p>
When shown the payment sheet, hit pay once. Then retry once.
</p>
<button onclick="callingRetryReturnsUniquePromise(this);">
Calling retry() multiple times is always a new object.
</button>
</li>
<li>
<button onclick="done();">
Done!
</button>
</li>
</ol>
<small>
If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">owners</a>.
</small>