Bug 1383300 - Move common payment tasks, methods and details to PaymentTestUtils. r=marcosc
☠☠ backed out by ab983c341420 ☠ ☠
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Wed, 18 Oct 2017 15:56:21 -0700
changeset 443413 71b84c8137051478ac2e60dea8c42c4863c7188f
parent 443412 34fa5b4d4d596ae654b3571700afa42c290d7cc7
child 443414 908e2e2a759b9579c74d92950598ea09439c3233
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarcosc
bugs1383300
milestone58.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 1383300 - Move common payment tasks, methods and details to PaymentTestUtils. r=marcosc MozReview-Commit-ID: 2jy2NXALKyd
toolkit/components/payments/moz.build
toolkit/components/payments/test/PaymentTestUtils.jsm
toolkit/components/payments/test/browser/browser.ini
toolkit/components/payments/test/browser/browser_show_dialog.js
toolkit/components/payments/test/browser/head.js
--- a/toolkit/components/payments/moz.build
+++ b/toolkit/components/payments/moz.build
@@ -9,13 +9,15 @@ BROWSER_CHROME_MANIFESTS += ['test/brows
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'WebPayments UI')
 
 EXTRA_COMPONENTS += [
     'payments.manifest',
     'paymentUIService.js',
 ]
 
-EXTRA_JS_MODULES += []
-
 JAR_MANIFESTS += ['jar.mn']
 
 SPHINX_TREES['docs'] = 'docs'
+
+TESTING_JS_MODULES += [
+    'test/PaymentTestUtils.jsm',
+]
copy from toolkit/components/payments/test/browser/head.js
copy to toolkit/components/payments/test/PaymentTestUtils.jsm
--- a/toolkit/components/payments/test/browser/head.js
+++ b/toolkit/components/payments/test/PaymentTestUtils.jsm
@@ -1,90 +1,73 @@
 "use strict";
 
-/* eslint
-  "no-unused-vars": ["error", {
-    vars: "local",
-    args: "none",
-    varsIgnorePattern: "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$",
-  }],
-*/
+this.EXPORTED_SYMBOLS = ["PaymentTestUtils"];
 
-const BLANK_PAGE_URL = "https://example.com/browser/toolkit/components/" +
-                       "payments/test/browser/blank_page.html";
-const PREF_PAYMENT_ENABLED = "dom.payments.request.enabled";
-const paymentUISrv = Cc["@mozilla.org/dom/payments/payment-ui-service;1"]
-                     .getService().wrappedJSObject;
-
+const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 
-/**
- * Return the container (e.g. dialog or overlay) that the payment request contents are shown in.
- * This abstracts away the details of the widget used so that this can more earily transition from a
- * dialog to another kind of overlay.
- * Consumers shouldn't rely on a dialog window being returned.
- * @returns {Promise}
- */
-async function getPaymentWidget() {
-  let win;
-  await BrowserTestUtils.waitForCondition(() => {
-    win = Services.wm.getMostRecentWindow(null);
-    return win.name.startsWith(paymentUISrv.REQUEST_ID_PREFIX);
-  }, "payment dialog should be the most recent");
-
-  return win;
-}
+this.PaymentTestUtils = {
+  /**
+   * Common content tasks functions to be used with ContentTask.spawn.
+   */
+  ContentTasks: {
+    /* eslint-env mozilla/frame-script */
+    /**
+     * Create a new payment request and cache it as `rq`.
+     *
+     * @param {Object} args
+     * @param {PaymentMethodData[]} methodData
+     * @param {PaymentDetailsInit} details
+     * @param {PaymentOptions} options
+     */
+    createRequest: ({methodData, details, options}) => {
+      const rq = new content.PaymentRequest(methodData, details, options);
+      content.rq = rq; // assign it so we can retrieve it later
+    },
 
-async function getPaymentFrame(widget) {
-  return widget.document.getElementById("paymentRequestFrame");
-}
+    /**
+     * Create a new payment request cached as `rq` and then show it.
+     *
+     * @param {Object} args
+     * @param {PaymentMethodData[]} methodData
+     * @param {PaymentDetailsInit} details
+     * @param {PaymentOptions} options
+     */
+    createAndShowRequest: ({methodData, details, options}) => {
+      const rq = new content.PaymentRequest(methodData, details, options);
+      content.rq = rq; // assign it so we can retrieve it later
+      rq.show();
+    },
 
-async function waitForMessageFromWidget(messageType, widget = null) {
-  info("waitForMessageFromWidget: " + messageType);
-  return new Promise(resolve => {
-    Services.mm.addMessageListener("paymentContentToChrome", function onMessage({data, target}) {
-      if (data.messageType != messageType) {
-        return;
-      }
-      if (widget && widget != target) {
-        return;
-      }
-      resolve();
-      info(`Got ${messageType} from widget`);
-      Services.mm.removeMessageListener("paymentContentToChrome", onMessage);
-    });
-  });
-}
-
-async function waitForWidgetReady(widget = null) {
-  return waitForMessageFromWidget("paymentDialogReady", widget);
-}
-
-function spawnPaymentDialogTask(paymentDialogFrame, taskFn, args = null) {
-  return ContentTask.spawn(paymentDialogFrame.frameLoader, args, taskFn);
-}
-
-/**
- * Common content tasks functions to be used with ContentTask.spawn.
- */
-let ContentTasks = {
-  createAndShowRequest: async ({methodData, details, options}) => {
-    let rq = new content.PaymentRequest(methodData, details, options);
-    content.rq = rq; // assign it so we can retrieve it later
-    rq.show();
+    /**
+     * Click the cancel button
+     *
+     * Don't await on this task since the cancel can close the dialog before
+     * ContentTask can resolve the promise.
+     *
+     * @returns {undefined}
+     */
+    manuallyClickCancel: () => {
+      content.document.getElementById("cancel").click();
+    },
   },
 
   /**
-   * Click the cancel button
-   *
-   * Don't await on this task since the cancel can close the dialog before
-   * ContentTask can resolve the promise.
-   *
-   * @returns {undefined}
+   * Common PaymentMethodData for testing
    */
-  manuallyClickCancel: () => {
-    content.document.getElementById("cancel").click();
+  MethodData: {
+    basicCard: {
+      supportedMethods: "basic-card",
+    },
+  },
+
+  /**
+   * Common PaymentDetailsInit for testing
+   */
+  Details: {
+    total60USD: {
+      total: {
+        label: "Total due",
+        amount: { currency: "USD", value: "60.00" },
+      },
+    },
   },
 };
-
-
-add_task(async function setup_head() {
-  await SpecialPowers.pushPrefEnv({set: [[PREF_PAYMENT_ENABLED, true]]});
-});
--- a/toolkit/components/payments/test/browser/browser.ini
+++ b/toolkit/components/payments/test/browser/browser.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
 head = head.js
+prefs =
+  dom.payments.request.enabled=true
+skip-if = !e10s # Bug 1365964 - Payment Request isn't implemented for non-e10s
 support-files =
   blank_page.html
 
 [browser_request_summary.js]
 [browser_show_dialog.js]
-# Bug 1365964 - Payment Request isn't implemented for non-e10s
-skip-if = !e10s
--- a/toolkit/components/payments/test/browser/browser_show_dialog.js
+++ b/toolkit/components/payments/test/browser/browser_show_dialog.js
@@ -1,27 +1,20 @@
 "use strict";
 
-const methodData = [{
-  supportedMethods: ["basic-card"],
-}];
-const details = {
-  total: {
-    label: "Total due",
-    amount: { currency: "USD", value: "60.00" },
-  },
-};
+const methodData = [PTU.MethodData.basicCard];
+const details = PTU.Details.total60USD;
 
 add_task(async function test_show_abort_dialog() {
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: BLANK_PAGE_URL,
   }, async browser => {
     // start by creating a PaymentRequest, and show it
-    await ContentTask.spawn(browser, {methodData, details}, ContentTasks.createAndShowRequest);
+    await ContentTask.spawn(browser, {methodData, details}, PTU.ContentTasks.createAndShowRequest);
 
     // get a reference to the UI dialog and the requestId
     let win = await getPaymentWidget();
     let requestId = paymentUISrv.requestIdForWindow(win);
     ok(requestId, "requestId should be defined");
     is(win.closed, false, "dialog should not be closed");
 
     // abort the payment request
@@ -32,27 +25,26 @@ add_task(async function test_show_abort_
 
 add_task(async function test_show_manualAbort_dialog() {
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: BLANK_PAGE_URL,
   }, async browser => {
     let dialogReadyPromise = waitForWidgetReady();
     // start by creating a PaymentRequest, and show it
-    await ContentTask.spawn(browser, {methodData, details}, ContentTasks.createAndShowRequest);
+    await ContentTask.spawn(browser, {methodData, details}, PTU.ContentTasks.createAndShowRequest);
 
     // get a reference to the UI dialog and the requestId
     let [win] = await Promise.all([getPaymentWidget(), dialogReadyPromise]);
     ok(win, "Got payment widget");
     let requestId = paymentUISrv.requestIdForWindow(win);
     ok(requestId, "requestId should be defined");
     is(win.closed, false, "dialog should not be closed");
 
     // abort the payment request manually
     let frame = await getPaymentFrame(win);
     ok(frame, "Got payment frame");
     await dialogReadyPromise;
     info("dialog ready");
-    spawnPaymentDialogTask(frame, ContentTasks.manuallyClickCancel);
-
+    spawnPaymentDialogTask(frame, PTU.ContentTasks.manuallyClickCancel);
     await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
   });
 });
--- a/toolkit/components/payments/test/browser/head.js
+++ b/toolkit/components/payments/test/browser/head.js
@@ -3,22 +3,34 @@
 /* eslint
   "no-unused-vars": ["error", {
     vars: "local",
     args: "none",
     varsIgnorePattern: "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$",
   }],
 */
 
-const BLANK_PAGE_URL = "https://example.com/browser/toolkit/components/" +
-                       "payments/test/browser/blank_page.html";
-const PREF_PAYMENT_ENABLED = "dom.payments.request.enabled";
+
+const BLANK_PAGE_PATH = "/browser/toolkit/components/payments/test/browser/blank_page.html";
+const BLANK_PAGE_URL = "https://example.com" + BLANK_PAGE_PATH;
+
+const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"]
+                     .getService(Ci.nsIPaymentRequestService);
 const paymentUISrv = Cc["@mozilla.org/dom/payments/payment-ui-service;1"]
                      .getService().wrappedJSObject;
+const {PaymentTestUtils: PTU} = Cu.import("resource://testing-common/PaymentTestUtils.jsm", {});
 
+function getPaymentRequests() {
+  let requestsEnum = paymentSrv.enumerate();
+  let requests = [];
+  while (requestsEnum.hasMoreElements()) {
+    requests.push(requestsEnum.getNext().QueryInterface(Ci.nsIPaymentRequest));
+  }
+  return requests;
+}
 
 /**
  * Return the container (e.g. dialog or overlay) that the payment request contents are shown in.
  * This abstracts away the details of the widget used so that this can more earily transition from a
  * dialog to another kind of overlay.
  * Consumers shouldn't rely on a dialog window being returned.
  * @returns {Promise}
  */
@@ -31,17 +43,17 @@ async function getPaymentWidget() {
 
   return win;
 }
 
 async function getPaymentFrame(widget) {
   return widget.document.getElementById("paymentRequestFrame");
 }
 
-async function waitForMessageFromWidget(messageType, widget = null) {
+function waitForMessageFromWidget(messageType, widget = null) {
   info("waitForMessageFromWidget: " + messageType);
   return new Promise(resolve => {
     Services.mm.addMessageListener("paymentContentToChrome", function onMessage({data, target}) {
       if (data.messageType != messageType) {
         return;
       }
       if (widget && widget != target) {
         return;
@@ -56,35 +68,67 @@ async function waitForMessageFromWidget(
 async function waitForWidgetReady(widget = null) {
   return waitForMessageFromWidget("paymentDialogReady", widget);
 }
 
 function spawnPaymentDialogTask(paymentDialogFrame, taskFn, args = null) {
   return ContentTask.spawn(paymentDialogFrame.frameLoader, args, taskFn);
 }
 
-/**
- * Common content tasks functions to be used with ContentTask.spawn.
- */
-let ContentTasks = {
-  createAndShowRequest: async ({methodData, details, options}) => {
-    let rq = new content.PaymentRequest(methodData, details, options);
-    content.rq = rq; // assign it so we can retrieve it later
-    rq.show();
-  },
+async function withMerchantTab({browser = gBrowser, url = BLANK_PAGE_URL} = {
+  browser: gBrowser,
+  url: BLANK_PAGE_URL,
+}, taskFn) {
+  await BrowserTestUtils.withNewTab({
+    gBrowser: browser,
+    url,
+  }, taskFn);
+
+  paymentSrv.cleanup(); // Temporary measure until bug 1408234 is fixed.
+
+  await new Promise(resolve => {
+    SpecialPowers.exactGC(resolve);
+  });
+}
+
+function withNewDialogFrame(requestId, taskFn) {
+  async function dialogTabTask(dialogBrowser) {
+    let paymentRequestFrame = dialogBrowser.contentDocument.getElementById("paymentRequestFrame");
+    await taskFn(paymentRequestFrame);
+  }
 
-  /**
-   * Click the cancel button
-   *
-   * Don't await on this task since the cancel can close the dialog before
-   * ContentTask can resolve the promise.
-   *
-   * @returns {undefined}
-   */
-  manuallyClickCancel: () => {
-    content.document.getElementById("cancel").click();
-  },
-};
+  let args = {
+    gBrowser,
+    url: `chrome://payments/content/paymentDialog.xhtml?requestId=${requestId}`,
+  };
+  return BrowserTestUtils.withNewTab(args, dialogTabTask);
+}
+
+function spawnTaskInNewDialog(requestId, contentTaskFn, args = null) {
+  return withNewDialogFrame(requestId, async function spawnTaskInNewDialog_tabTask(reqFrame) {
+    await spawnPaymentDialogTask(reqFrame, contentTaskFn, args);
+  });
+}
 
+async function spawnInDialogForMerchantTask(merchantTaskFn, dialogTaskFn, taskArgs, {
+  origin = "https://example.com",
+} = {
+  origin: "https://example.com",
+}) {
+  await withMerchantTab({
+    url: origin + BLANK_PAGE_PATH,
+  }, async merchBrowser => {
+    await ContentTask.spawn(merchBrowser, taskArgs, PTU.ContentTasks.createRequest);
+
+    const requests = getPaymentRequests();
+    is(requests.length, 1, "Should have one payment request");
+    let request = requests[0];
+    ok(!!request.requestId, "Got a payment request with an ID");
+
+    await spawnTaskInNewDialog(request.requestId, dialogTaskFn, taskArgs);
+  });
+}
 
 add_task(async function setup_head() {
-  await SpecialPowers.pushPrefEnv({set: [[PREF_PAYMENT_ENABLED, true]]});
+  SimpleTest.registerCleanupFunction(function cleanup() {
+    paymentSrv.cleanup();
+  });
 });