Bug 1463390 [wpt PR 11101] - Add tests for PaymentResponse.retry(), a=testonly
authorMarcos Cáceres <marcos@marcosc.com>
Wed, 15 Aug 2018 10:03:13 +0000
changeset 431769 2b2c564b2e13accaa97fff7dd8264904c641bc77
parent 431768 f33bd8e46c771d7a11ba95a27ec2e930e67f4619
child 431770 faf93e2708549068e4286fad80edb2869d30ce9e
push id34451
push userebalazs@mozilla.com
push dateThu, 16 Aug 2018 09:25:15 +0000
treeherdermozilla-central@161817e6d127 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1463390, 11101
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1463390 [wpt PR 11101] - Add tests for PaymentResponse.retry(), a=testonly Automatic update from web-platform-testsAdd tests for PaymentResponse.retry() (#11101) * Add tests for PaymentResponse.retry() * Tests for https://github.com/w3c/payment-request/pull/715 * wip * remove unnecessary comment * test retry() rejects if doc not fully active * Provide better instructions * fix copy/pasta * test 'abort the update' * Calling retry() multiple times is always a new object. * Generalize test for retry and complete -- wpt-commits: 2f996e750b6bf5c6c64b693aca154198241ae9e5 wpt-pr: 11101
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/payment-request/payment-response/rejects_if_not_active-manual.https.html
testing/web-platform/tests/payment-request/payment-response/retry-method-manual.https.html
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -13292,22 +13292,34 @@
     ]
    ],
    "payment-request/payment-response/payerPhone-attribute-manual.https.html": [
     [
      "/payment-request/payment-response/payerPhone-attribute-manual.https.html",
      {}
     ]
    ],
+   "payment-request/payment-response/rejects_if_not_active-manual.https.html": [
+    [
+     "/payment-request/payment-response/rejects_if_not_active-manual.https.html",
+     {}
+    ]
+   ],
    "payment-request/payment-response/requestId-attribute-manual.https.html": [
     [
      "/payment-request/payment-response/requestId-attribute-manual.https.html",
      {}
     ]
    ],
+   "payment-request/payment-response/retry-method-manual.https.html": [
+    [
+     "/payment-request/payment-response/retry-method-manual.https.html",
+     {}
+    ]
+   ],
    "payment-request/payment-response/shippingAddress-attribute-manual.https.html": [
     [
      "/payment-request/payment-response/shippingAddress-attribute-manual.https.html",
      {}
     ]
    ],
    "payment-request/payment-response/shippingOption-attribute-manual.https.html": [
     [
@@ -616394,20 +616406,28 @@
   "payment-request/payment-response/payerName-attribute-manual.https.html": [
    "44d741ae45cb7f8f34fa1550b47b49cf3e0c450b",
    "manual"
   ],
   "payment-request/payment-response/payerPhone-attribute-manual.https.html": [
    "85a44a819cd8555e032520f887cf89508bdb98b6",
    "manual"
   ],
+  "payment-request/payment-response/rejects_if_not_active-manual.https.html": [
+   "60dd9655dd5bafcdb7768b929696cb9104214b29",
+   "manual"
+  ],
   "payment-request/payment-response/requestId-attribute-manual.https.html": [
    "ddb1e0d831dac65cd2f5dc64c7218a45b2e154a8",
    "manual"
   ],
+  "payment-request/payment-response/retry-method-manual.https.html": [
+   "288a32b386d621551dd43a46240290d7de33a390",
+   "manual"
+  ],
   "payment-request/payment-response/shippingAddress-attribute-manual.https.html": [
    "92bac2bb5e5e270cedfb5b84bc8c8998df46265e",
    "manual"
   ],
   "payment-request/payment-response/shippingOption-attribute-manual.https.html": [
    "687d3a52de9f6bfdb10456147586008b18683eb2",
    "manual"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/payment-request/payment-response/rejects_if_not_active-manual.https.html
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<link rel="help" href="https://w3c.github.io/payment-request/#retry-method">
+<title>PaymentResponse retry() rejects if doc is not fully active</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-retry">
+<body>
+<script>
+setup({ explicit_done: true, explicit_timeout: true });
+const validMethod = Object.freeze({
+  supportedMethods: "basic-card",
+});
+const validMethods = Object.freeze([validMethod]);
+const validAmount = Object.freeze({
+  currency: "USD",
+  value: "5.00",
+});
+const validTotal = Object.freeze({
+  label: "Total due",
+  amount: validAmount,
+});
+const validDetails = Object.freeze({
+  total: validTotal,
+});
+
+function getLoadedPaymentResponse(iframe, url) {
+  return new Promise(resolve => {
+    iframe.addEventListener(
+      "load",
+      async () => {
+        const { PaymentRequest } = iframe.contentWindow;
+        const response = await new PaymentRequest(
+          validMethods,
+          validDetails
+        ).show();
+        resolve(response);
+      },
+      { once: true }
+    );
+    iframe.src = url;
+  });
+}
+
+function methodNotFullyActive(button, method, ...args) {
+  const text = button.textContent.trim();
+  promise_test(async t => {
+    const iframe = document.createElement("iframe");
+    iframe.allowPaymentRequest = true;
+    document.body.appendChild(iframe);
+
+    // We first got to page1.html, grab a PaymentResponse instance.
+    const response = await getLoadedPaymentResponse(
+      iframe,
+      "/payment-request/resources/page1.html"
+    );
+    // We navigate the iframe again, putting response's document into an inactive state.
+    await new Promise(resolve => {
+      iframe.addEventListener("load", resolve);
+      iframe.src = "/payment-request/resources/page2.html";
+    });
+    // Now, response's relevant global object's document is no longer active.
+    // So, promise needs to reject appropriately.
+    const promise = response[methodName](...args);
+    await promise_rejects(
+      t,
+      "AbortError",
+      promise,
+      "Inactive document, so must throw AbortError"
+    );
+    // We are done, so clean up.
+    iframe.remove();
+  }, text);
+}
+
+function methodBecomesNotFullyActive(button, methodName, ...args) {
+  const text = button.textContent.trim();
+  promise_test(async t => {
+    const iframe = document.createElement("iframe");
+    iframe.allowPaymentRequest = true;
+    document.body.appendChild(iframe);
+
+    // We first got to page1.html, grab a PaymentResponse instance.
+    const response = await getLoadedPaymentResponse(
+      iframe,
+      "/payment-request/resources/page1.html"
+    );
+
+    // we get the promise from page1.html, while it's active!
+    const promise = response[methodName](...args);
+
+    // We navigate the iframe again, putting response's document into an inactive state.
+    await new Promise(resolve => {
+      iframe.addEventListener("load", resolve);
+      iframe.src = "/payment-request/resources/page2.html";
+    });
+
+    // Now, response's relevant global object's document is no longer active.
+    // So, promise needs to reject appropriately.
+    await promise_rejects(
+      t,
+      "AbortError",
+      promise,
+      "Inactive document, so must throw AbortError"
+    );
+    // We are done, so clean up.
+    iframe.remove();
+  }, text);
+}
+</script>
+<section>
+  <p>
+    For each test, when the payment sheet is shown, select a payment method and hit "Pay".
+  </p>
+  <h2>retry() and document active state</h2>
+  <p>Manual Tests for PaymentResponse.retry() - Please run in order!</p>
+  <ol>
+    <li>
+      <button onclick="methodNotFullyActive(this, 'retry', {});">
+        retry()'s retryPromise rejects if document is not fully active.
+      </button>
+    </li>
+    <li>
+      <button onclick="methodBecomesNotFullyActive(this, 'retry', {});">
+        retry()'s retryPromise rejects if the document becomes not fully active.
+      </button>
+    </li>
+  </ol>
+  <h2>complete() and document active state</h2>
+  <p>Manual Tests for PaymentResponse.complete() - Please run in order!</p>
+  <ol>
+    <li>
+      <button onclick="methodNotFullyActive(this, 'complete', 'success');">
+        complete()'s completePromise rejects if document is not fully active.
+      </button>
+    </li>
+    <li>
+      <button onclick="methodBecomesNotFullyActive(this, 'complete', 'success');">
+        complete()'s completePromise rejects if the document becomes not fully active.
+      </button>
+    </li>
+    <li>
+      <button onclick="done();">Done</button>
+    </li>
+  </ol>
+</section>
+<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">suggested reviewers</a>.
+</small>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/payment-request/payment-response/retry-method-manual.https.html
@@ -0,0 +1,262 @@
+<!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(
+      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(
+      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({});
+    await promise_rejects(
+      t,
+      "InvalidStateError",
+      response.complete("success"),
+      "Calling complete() while retrying rejects with an InvalidStateError"
+    );
+    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(
+      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(
+      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(
+      t,
+      "AbortError",
+      response.retry({}),
+      "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"
+    );
+  }, button.textContent.trim());
+}
+
+function abortTheUpdate(button) {
+  button.disabled = true;
+  promise_test(async t => {
+    const { response, request } = await getPaymentRequestResponse();
+    // 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({});
+    await shippingChangedPromise;
+    await promise_rejects(
+      t,
+      "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"
+    );
+  }, button.textContent.trim());
+}
+
+function callingRetryReturnsUniquePromise(button){
+  button.disabled = true;
+  promise_test(async t => {
+    const request = new PaymentRequest(defaultMethods, defaultDetails);
+    const retryPromise = request.retry();
+    const promises = new Set([
+      retryPromise,
+      request.retry(),
+      request.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 retry once.
+    </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/w3c/web-platform-tests/issues">file a bug</a>
+  and tag one of the <a href="https://github.com/w3c/web-platform-tests/blob/master/payment-request/OWNERS">owners</a>.
+</small>