summaryrefslogtreecommitdiffstats
path: root/toolkit/components/formautofill/test
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/formautofill/test')
-rw-r--r--toolkit/components/formautofill/test/browser/.eslintrc.js7
-rw-r--r--toolkit/components/formautofill/test/browser/browser.ini10
-rw-r--r--toolkit/components/formautofill/test/browser/browser_infrastructure.js48
-rw-r--r--toolkit/components/formautofill/test/browser/browser_ui_requestAutocomplete.js48
-rw-r--r--toolkit/components/formautofill/test/browser/head.js18
-rw-r--r--toolkit/components/formautofill/test/browser/loader.js38
-rw-r--r--toolkit/components/formautofill/test/chrome/.eslintrc.js7
-rw-r--r--toolkit/components/formautofill/test/chrome/chrome.ini17
-rw-r--r--toolkit/components/formautofill/test/chrome/head.js15
-rw-r--r--toolkit/components/formautofill/test/chrome/loader.js116
-rw-r--r--toolkit/components/formautofill/test/chrome/loader_parent.js77
-rw-r--r--toolkit/components/formautofill/test/chrome/test_infrastructure.html8
-rw-r--r--toolkit/components/formautofill/test/chrome/test_infrastructure.js61
-rw-r--r--toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.html9
-rw-r--r--toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.js26
-rw-r--r--toolkit/components/formautofill/test/head_common.js245
-rw-r--r--toolkit/components/formautofill/test/loader_common.js120
-rw-r--r--toolkit/components/formautofill/test/xpcshell/.eslintrc.js7
-rw-r--r--toolkit/components/formautofill/test/xpcshell/head.js23
-rw-r--r--toolkit/components/formautofill/test/xpcshell/loader.js46
-rw-r--r--toolkit/components/formautofill/test/xpcshell/test_infrastructure.js48
-rw-r--r--toolkit/components/formautofill/test/xpcshell/test_integration.js72
-rw-r--r--toolkit/components/formautofill/test/xpcshell/xpcshell.ini12
23 files changed, 1078 insertions, 0 deletions
diff --git a/toolkit/components/formautofill/test/browser/.eslintrc.js b/toolkit/components/formautofill/test/browser/.eslintrc.js
new file mode 100644
index 000000000..7c8021192
--- /dev/null
+++ b/toolkit/components/formautofill/test/browser/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/mochitest/browser.eslintrc.js"
+ ]
+};
diff --git a/toolkit/components/formautofill/test/browser/browser.ini b/toolkit/components/formautofill/test/browser/browser.ini
new file mode 100644
index 000000000..dff9c3381
--- /dev/null
+++ b/toolkit/components/formautofill/test/browser/browser.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+# The following files starting with ".." are installed in the current folder.
+support-files =
+ ../head_common.js
+ ../loader_common.js
+ head.js
+ loader.js
+
+[browser_infrastructure.js]
+[browser_ui_requestAutocomplete.js]
diff --git a/toolkit/components/formautofill/test/browser/browser_infrastructure.js b/toolkit/components/formautofill/test/browser/browser_infrastructure.js
new file mode 100644
index 000000000..af27cfdb5
--- /dev/null
+++ b/toolkit/components/formautofill/test/browser/browser_infrastructure.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests the local testing infrastructure.
+ */
+
+"use strict";
+
+/**
+ * Tests the truth assertion function.
+ */
+add_task(function* test_assert_truth() {
+ Assert.ok(1 != 2);
+});
+
+/**
+ * Tests the equality assertion function.
+ */
+add_task(function* test_assert_equality() {
+ Assert.equal(1 + 1, 2);
+});
+
+/**
+ * Uses some of the utility functions provided by the framework.
+ */
+add_task(function* test_utility_functions() {
+ // The "print" function is useful to log information that is not known before.
+ let randomString = "R" + Math.floor(Math.random() * 10);
+ Output.print("The random contents will be '" + randomString + "'.");
+
+ // Create the text file with the random contents.
+ let path = yield TestUtils.getTempFile("test-infrastructure.txt");
+ yield OS.File.writeAtomic(path, new TextEncoder().encode(randomString));
+
+ // Test a few utility functions.
+ yield TestUtils.waitForTick();
+ yield TestUtils.waitMs(50);
+
+ let promiseMyNotification = TestUtils.waitForNotification("my-topic");
+ Services.obs.notifyObservers(null, "my-topic", "");
+ yield promiseMyNotification;
+
+ // Check the file size. The file will be deleted automatically later.
+ Assert.equal((yield OS.File.stat(path)).size, randomString.length);
+});
+
+add_task(terminationTaskFn);
diff --git a/toolkit/components/formautofill/test/browser/browser_ui_requestAutocomplete.js b/toolkit/components/formautofill/test/browser/browser_ui_requestAutocomplete.js
new file mode 100644
index 000000000..2a7b58f12
--- /dev/null
+++ b/toolkit/components/formautofill/test/browser/browser_ui_requestAutocomplete.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests the requestAutocomplete user interface.
+ */
+
+"use strict";
+
+/**
+ * Open the requestAutocomplete UI and test that selecting a profile results in
+ * the correct data being sent back to the opener.
+ */
+add_task(function* test_select_profile() {
+ // Request an e-mail address.
+ let { uiWindow, promiseResult } = yield FormAutofillTest.showUI(
+ TestData.requestEmailOnly);
+
+ // Accept the dialog.
+ let acceptButton = uiWindow.document.getElementById("accept");
+ EventUtils.synthesizeMouseAtCenter(acceptButton, {}, uiWindow);
+
+ let result = yield promiseResult;
+ Assert.equal(result.fields.length, 1);
+ Assert.equal(result.fields[0].section, "");
+ Assert.equal(result.fields[0].addressType, "");
+ Assert.equal(result.fields[0].contactType, "");
+ Assert.equal(result.fields[0].fieldName, "email");
+ Assert.equal(result.fields[0].value, "email@example.org");
+});
+
+/**
+ * Open the requestAutocomplete UI and cancel the dialog.
+ */
+add_task(function* test_cancel() {
+ // Request an e-mail address.
+ let { uiWindow, promiseResult } = yield FormAutofillTest.showUI(
+ TestData.requestEmailOnly);
+
+ // Cancel the dialog.
+ let acceptButton = uiWindow.document.getElementById("cancel");
+ EventUtils.synthesizeMouseAtCenter(acceptButton, {}, uiWindow);
+
+ let result = yield promiseResult;
+ Assert.ok(result.canceled);
+});
+
+add_task(terminationTaskFn);
diff --git a/toolkit/components/formautofill/test/browser/head.js b/toolkit/components/formautofill/test/browser/head.js
new file mode 100644
index 000000000..882f3fd5e
--- /dev/null
+++ b/toolkit/components/formautofill/test/browser/head.js
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Initialization specific to Form Autofill mochitest-browser tests.
+ */
+
+"use strict";
+
+// We cannot start initialization from "loader.js" like we do in the xpcshell
+// and mochitest-chrome frameworks, thus we load the script here.
+Services.scriptloader.loadSubScript(getRootDirectory(gTestPath) + "loader.js",
+ this);
+
+// The testing framework is fully initialized at this point, you can add
+// mochitest-browser specific test initialization here. If you need shared
+// functions or initialization that are not specific to mochitest-browser,
+// consider adding them to "head_common.js" in the parent folder instead.
diff --git a/toolkit/components/formautofill/test/browser/loader.js b/toolkit/components/formautofill/test/browser/loader.js
new file mode 100644
index 000000000..bfd5b9ee0
--- /dev/null
+++ b/toolkit/components/formautofill/test/browser/loader.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Infrastructure for the mochitest-browser tests located in this folder.
+ *
+ * See "loader_common.js" in the parent folder for a general overview.
+ *
+ * Unless you are adding new features to the framework, you shouldn't have to
+ * modify this file. Use "head_common.js" or "head.js" for shared code.
+ */
+
+"use strict";
+
+Services.scriptloader.loadSubScript(getRootDirectory(gTestPath) +
+ "loader_common.js", this);
+
+// Define output functions so they look the same across all frameworks.
+var Output = {
+ print: info,
+};
+
+// Define assertion functions so they look the same across all frameworks.
+var Assert = {
+ ok: _mochitestAssert.ok,
+ equal: _mochitestAssert.equal,
+};
+
+// Define task registration functions, see description in "loader_common.js".
+var add_task_in_parent_process = add_task;
+var add_task_in_child_process = function () {};
+var add_task_in_both_processes = add_task;
+
+Services.scriptloader.loadSubScript(getRootDirectory(gTestPath) +
+ "head_common.js", this);
+
+// Reminder: unless you are adding new features to the framework, you shouldn't
+// have to modify this file. Use "head_common.js" or "head.js" for shared code.
diff --git a/toolkit/components/formautofill/test/chrome/.eslintrc.js b/toolkit/components/formautofill/test/chrome/.eslintrc.js
new file mode 100644
index 000000000..8c0f4f574
--- /dev/null
+++ b/toolkit/components/formautofill/test/chrome/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/mochitest/chrome.eslintrc.js"
+ ]
+};
diff --git a/toolkit/components/formautofill/test/chrome/chrome.ini b/toolkit/components/formautofill/test/chrome/chrome.ini
new file mode 100644
index 000000000..67b7869af
--- /dev/null
+++ b/toolkit/components/formautofill/test/chrome/chrome.ini
@@ -0,0 +1,17 @@
+[DEFAULT]
+# The following files starting with ".." are installed in the current folder.
+support-files =
+ ../head_common.js
+ ../loader_common.js
+ head.js
+ test_infrastructure.js
+ test_requestAutocomplete_cancel.js
+ loader_parent.js
+ loader.js
+
+# For each test defined below, the associated JavaScript file must be declared
+# in the list above. This is required because a "support-files" declaration on
+# the individual test would override the global list instead of adding entries.
+
+[test_infrastructure.html]
+[test_requestAutocomplete_cancel.html]
diff --git a/toolkit/components/formautofill/test/chrome/head.js b/toolkit/components/formautofill/test/chrome/head.js
new file mode 100644
index 000000000..4110d5e7c
--- /dev/null
+++ b/toolkit/components/formautofill/test/chrome/head.js
@@ -0,0 +1,15 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Initialization specific to Form Autofill mochitest-chrome tests.
+ *
+ * This file is loaded by "loader.js".
+ */
+
+"use strict";
+
+// The testing framework is fully initialized at this point, you can add
+// mochitest-chrome specific test initialization here. If you need shared
+// functions or initialization that are not specific to mochitest-chrome,
+// consider adding them to "head_common.js" in the parent folder instead.
diff --git a/toolkit/components/formautofill/test/chrome/loader.js b/toolkit/components/formautofill/test/chrome/loader.js
new file mode 100644
index 000000000..25b0e6ea3
--- /dev/null
+++ b/toolkit/components/formautofill/test/chrome/loader.js
@@ -0,0 +1,116 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Infrastructure for the mochitest-chrome tests located in this folder.
+ *
+ * See "loader_common.js" in the parent folder for a general overview.
+ *
+ * Unless you are adding new features to the framework, you shouldn't have to
+ * modify this file. Use "head_common.js" or "head.js" for shared code.
+ */
+
+"use strict";
+
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm", this);
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochikit/content/tests/SimpleTest/SimpleTest.js", this);
+
+var sharedUrl = SimpleTest.getTestFileURL("loader_common.js");
+Services.scriptloader.loadSubScript(sharedUrl, this);
+
+var parentScript = SpecialPowers.loadChromeScript(
+ SimpleTest.getTestFileURL("loader_parent.js"));
+
+// Replace the extension of the loaded HTML file with ".js"
+var testUrl = location.href.replace(/\.\w+$/, ".js");
+
+// Start loading the test script in the parent process.
+var promiseParentInitFinished = new Promise(function (resolve) {
+ parentScript.addMessageListener("finish_load_in_parent", resolve);
+});
+parentScript.sendAsyncMessage("start_load_in_parent", { testUrl: testUrl });
+
+// Define output functions so they look the same across all frameworks.
+var Output = {
+ print: info,
+};
+
+// Define assertion functions so they look the same across all frameworks.
+var Assert = {
+ ok: _mochitestAssert.ok,
+ equal: _mochitestAssert.equal,
+};
+
+var executeSoon = SimpleTest.executeSoon;
+
+var gTestTasks = [];
+
+// Define task registration functions, see description in "loader_common.js".
+function add_task(taskFn) {
+ gTestTasks.push([taskFn, "content", taskFn.name]);
+}
+function add_task_in_parent_process(taskFn, taskIdOverride) {
+ let taskId = taskIdOverride || getTaskId(Components.stack.caller);
+ gTestTasks.push([taskFn, "parent", taskId]);
+}
+function add_task_in_both_processes(taskFn) {
+ // We need to define a task ID based on our direct caller.
+ add_task_in_parent_process(taskFn, getTaskId(Components.stack.caller));
+ add_task(taskFn);
+}
+var add_task_in_child_process = add_task;
+
+window.addEventListener("load", function onLoad() {
+ window.removeEventListener("load", onLoad);
+
+ Task.spawn(function* () {
+ try {
+ for (let [taskFn, taskType, taskId] of gTestTasks) {
+ if (taskType == "content") {
+ // This is a normal task executed in the current process.
+ info("Running " + taskFn.name);
+ yield Task.spawn(taskFn);
+ } else {
+ // This is a task executed in the parent process.
+ info("Running task in parent process: " + taskFn.name);
+ let promiseFinished = new Promise(function (resolve) {
+ parentScript.addMessageListener("finish_task_" + taskId, resolve);
+ });
+ parentScript.sendAsyncMessage("start_task_" + taskId);
+ yield promiseFinished;
+ info("Finished task in parent process: " + taskFn.name);
+ }
+ }
+ } catch (ex) {
+ ok(false, ex);
+ }
+
+ SimpleTest.finish();
+ });
+});
+
+// Wait for the test script to be loaded in the parent process. This means that
+// test tasks are registered and ready, but have not been executed yet.
+add_task(function* wait_loading_in_parent_process() {
+ yield promiseParentInitFinished;
+});
+
+var headUrl = SimpleTest.getTestFileURL("head_common.js");
+Services.scriptloader.loadSubScript(headUrl, this);
+
+Output.print("Loading test file: " + testUrl);
+Services.scriptloader.loadSubScript(testUrl, this);
+
+// Register the execution of termination tasks after all other tasks.
+add_task(terminationTaskFn);
+add_task_in_parent_process(terminationTaskFn, terminationTaskFn.name);
+
+SimpleTest.waitForExplicitFinish();
+
+// Reminder: unless you are adding new features to the framework, you shouldn't
+// have to modify this file. Use "head_common.js" or "head.js" for shared code.
diff --git a/toolkit/components/formautofill/test/chrome/loader_parent.js b/toolkit/components/formautofill/test/chrome/loader_parent.js
new file mode 100644
index 000000000..bf823218e
--- /dev/null
+++ b/toolkit/components/formautofill/test/chrome/loader_parent.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Infrastructure for the mochitest-chrome tests located in this folder, always
+ * executed in the parent process.
+ *
+ * See "loader_common.js" in the parent folder for a general overview.
+ *
+ * Unless you are adding new features to the framework, you shouldn't have to
+ * modify this file. Use "head_common.js" or "head.js" for shared code.
+ */
+
+"use strict";
+
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm", this);
+
+var sharedUrl = "chrome://mochitests/content/chrome/" +
+ "toolkit/components/formautofill/test/chrome/loader_common.js";
+Services.scriptloader.loadSubScript(sharedUrl, this);
+
+// Define output functions so they look the same across all frameworks. Since
+// we don't have an output function available here, we report as TEST-PASS.
+var Output = {
+ print: message => assert.ok(true, message),
+};
+
+// Define assertion functions so they look the same across all frameworks.
+var Assert = {
+ ok: assert.ok,
+ equal: assert.equal,
+};
+
+// Define task registration functions, see description in "loader_common.js".
+function add_task_in_parent_process(taskFn, taskIdOverride) {
+ let taskId = taskIdOverride || getTaskId(Components.stack.caller);
+ Output.print("Registering in the parent process: " + taskId);
+ addMessageListener("start_task_" + taskId, function () {
+ Task.spawn(function* () {
+ try {
+ Output.print("Running in the parent process " + taskId);
+ yield Task.spawn(taskFn);
+ } catch (ex) {
+ assert.ok(false, ex);
+ }
+
+ sendAsyncMessage("finish_task_" + taskId, {});
+ });
+ });
+}
+var add_task = function () {};
+var add_task_in_child_process = function () {};
+var add_task_in_both_processes = add_task_in_parent_process;
+
+// We need to wait for the child process to send us the path of the test file
+// to load before we can actually start loading it.
+var context = this;
+addMessageListener("start_load_in_parent", function (message) {
+ Output.print("Starting loading infrastructure in parent process.");
+ let headUrl = "chrome://mochitests/content/chrome/" +
+ "toolkit/components/formautofill/test/chrome/head_common.js";
+ Services.scriptloader.loadSubScript(headUrl, context);
+
+ Services.scriptloader.loadSubScript(message.testUrl, context);
+
+ // Register the execution of termination tasks after all other tasks.
+ add_task_in_parent_process(terminationTaskFn, terminationTaskFn.name);
+
+ Output.print("Finished loading infrastructure in parent process.");
+ sendAsyncMessage("finish_load_in_parent", {});
+});
+
+// Reminder: unless you are adding new features to the framework, you shouldn't
+// have to modify this file. Use "head_common.js" or "head.js" for shared code.
diff --git a/toolkit/components/formautofill/test/chrome/test_infrastructure.html b/toolkit/components/formautofill/test/chrome/test_infrastructure.html
new file mode 100644
index 000000000..54f417f77
--- /dev/null
+++ b/toolkit/components/formautofill/test/chrome/test_infrastructure.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
+<script type="application/javascript;version=1.7" src="loader.js"></script>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<p id="paragraph">Paragraph contents.</p>
+
+</body></html>
diff --git a/toolkit/components/formautofill/test/chrome/test_infrastructure.js b/toolkit/components/formautofill/test/chrome/test_infrastructure.js
new file mode 100644
index 000000000..c3b0b43ff
--- /dev/null
+++ b/toolkit/components/formautofill/test/chrome/test_infrastructure.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests the local testing infrastructure.
+ */
+
+"use strict";
+
+/**
+ * Tests the truth assertion function.
+ */
+add_task(function* test_assert_truth() {
+ Assert.ok(1 != 2);
+});
+
+/**
+ * Tests the equality assertion function.
+ */
+add_task(function* test_assert_equality() {
+ Assert.equal(1 + 1, 2);
+});
+
+/**
+ * Uses some of the utility functions provided by the framework.
+ */
+add_task(function* test_utility_functions() {
+ // The "print" function is useful to log information that is not known before.
+ let randomString = "R" + Math.floor(Math.random() * 10);
+ Output.print("The random contents will be '" + randomString + "'.");
+
+ // Create the text file with the random contents.
+ let path = yield TestUtils.getTempFile("test-infrastructure.txt");
+ yield OS.File.writeAtomic(path, new TextEncoder().encode(randomString));
+
+ // Test a few utility functions.
+ yield TestUtils.waitForTick();
+ yield TestUtils.waitMs(50);
+
+ let promiseMyNotification = TestUtils.waitForNotification("my-topic");
+ Services.obs.notifyObservers(null, "my-topic", "");
+ yield promiseMyNotification;
+
+ // Check the file size. The file will be deleted automatically later.
+ Assert.equal((yield OS.File.stat(path)).size, randomString.length);
+});
+
+/**
+ * This type of test has access to the content declared above.
+ */
+add_task(function* test_content() {
+ Assert.equal($("paragraph").innerHTML, "Paragraph contents.");
+
+ let promiseMyEvent = TestUtils.waitForEvent($("paragraph"), "MyEvent");
+
+ let event = document.createEvent("CustomEvent");
+ event.initCustomEvent("MyEvent", true, false, {});
+ $("paragraph").dispatchEvent(event);
+
+ yield promiseMyEvent;
+});
diff --git a/toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.html b/toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.html
new file mode 100644
index 000000000..8ae7ffd4b
--- /dev/null
+++ b/toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
+<script type="application/javascript;version=1.7" src="loader.js"></script>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<form id="form">
+</form>
+
+</body></html>
diff --git a/toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.js b/toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.js
new file mode 100644
index 000000000..1ee12bd9a
--- /dev/null
+++ b/toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.js
@@ -0,0 +1,26 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests the response sent when requestAutocomplete is canceled by the user.
+ */
+
+"use strict";
+
+/**
+ * The requestAutocomplete UI will not be displayed during these tests.
+ */
+add_task_in_parent_process(function* test_cancel_init() {
+ FormAutofillTest.requestAutocompleteResponse = { canceled: true };
+});
+
+/**
+ * Tests the case where the feature is canceled.
+ */
+add_task(function* test_cancel() {
+ let promise = TestUtils.waitForEvent($("form"), "autocompleteerror");
+ $("form").requestAutocomplete();
+ let errorEvent = yield promise;
+
+ Assert.equal(errorEvent.reason, "cancel");
+});
diff --git a/toolkit/components/formautofill/test/head_common.js b/toolkit/components/formautofill/test/head_common.js
new file mode 100644
index 000000000..82b87e4a6
--- /dev/null
+++ b/toolkit/components/formautofill/test/head_common.js
@@ -0,0 +1,245 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Initialization of Form Autofill tests shared between all frameworks.
+ *
+ * A copy of this file is installed in each of the framework subfolders, this
+ * means it becomes a sibling of the test files in the final layout. This is
+ * determined by how manifest "support-files" installation works.
+ */
+
+"use strict";
+
+// The requestAutocomplete framework is available at this point, you can add
+// mochitest-chrome specific test initialization here. If you need shared
+// functions or initialization that are not specific to mochitest-chrome,
+// consider adding them to "head_common.js" in the parent folder instead.
+
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
+ "resource://gre/modules/DownloadPaths.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+ "resource://gre/modules/FileUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FormAutofill",
+ "resource://gre/modules/FormAutofill.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+ "resource://gre/modules/NetUtil.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "OS",
+ "resource://gre/modules/osfile.jsm");
+
+/* --- Global helpers --- */
+
+// Some of these functions are already implemented in other parts of the source
+// tree, see bug 946708 about sharing more code.
+
+var TestUtils = {
+ /**
+ * Waits for at least one tick of the event loop. This means that all pending
+ * events at the time of this call will have been processed. Other events may
+ * be processed before the returned promise is resolved.
+ *
+ * @return {Promise}
+ * @resolves When pending events have been processed.
+ * @rejects Never.
+ */
+ waitForTick: function () {
+ return new Promise(resolve => executeSoon(resolve));
+ },
+
+ /**
+ * Waits for the specified timeout.
+ *
+ * @param aTimeMs
+ * Minimum time to wait from the moment of this call, in milliseconds.
+ * The actual wait may be longer, due to system timer resolution and
+ * pending events being processed before the promise is resolved.
+ *
+ * @return {Promise}
+ * @resolves When the specified time has passed.
+ * @rejects Never.
+ */
+ waitMs: function (aTimeMs) {
+ return new Promise(resolve => setTimeout(resolve, aTimeMs));
+ },
+
+ /**
+ * Allows waiting for an observer notification once.
+ *
+ * @param aTopic
+ * Notification topic to observe.
+ *
+ * @return {Promise}
+ * @resolves The array [aSubject, aData] from the observed notification.
+ * @rejects Never.
+ */
+ waitForNotification: function (aTopic) {
+ Output.print("Waiting for notification: '" + aTopic + "'.");
+
+ return new Promise(resolve => Services.obs.addObserver(
+ function observe(aSubject, aTopic, aData) {
+ Services.obs.removeObserver(observe, aTopic);
+ resolve([aSubject, aData]);
+ }, aTopic, false));
+ },
+
+ /**
+ * Waits for a DOM event on the specified target.
+ *
+ * @param aTarget
+ * The DOM EventTarget on which addEventListener should be called.
+ * @param aEventName
+ * String with the name of the event.
+ * @param aUseCapture
+ * This parameter is passed to the addEventListener call.
+ *
+ * @return {Promise}
+ * @resolves The arguments from the observed event.
+ * @rejects Never.
+ */
+ waitForEvent: function (aTarget, aEventName, aUseCapture = false) {
+ Output.print("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
+
+ return new Promise(resolve => aTarget.addEventListener(aEventName,
+ function onEvent(...aArgs) {
+ aTarget.removeEventListener(aEventName, onEvent, aUseCapture);
+ resolve(...aArgs);
+ }, aUseCapture));
+ },
+
+ // While the previous test file should have deleted all the temporary files it
+ // used, on Windows these might still be pending deletion on the physical file
+ // system. Thus, start from a new base number every time, to make a collision
+ // with a file that is still pending deletion highly unlikely.
+ _fileCounter: Math.floor(Math.random() * 1000000),
+
+ /**
+ * Returns a reference to a temporary file, that is guaranteed not to exist,
+ * and to have never been created before.
+ *
+ * @param aLeafName
+ * Suggested leaf name for the file to be created.
+ *
+ * @return {Promise}
+ * @resolves Path of a non-existent file in a temporary directory.
+ *
+ * @note It is not enough to delete the file if it exists, or to delete the
+ * file after calling nsIFile.createUnique, because on Windows the
+ * delete operation in the file system may still be pending, preventing
+ * a new file with the same name to be created.
+ */
+ getTempFile: Task.async(function* (aLeafName) {
+ // Prepend a serial number to the extension in the suggested leaf name.
+ let [base, ext] = DownloadPaths.splitBaseNameAndExtension(aLeafName);
+ let leafName = base + "-" + this._fileCounter + ext;
+ this._fileCounter++;
+
+ // Get a file reference under the temporary directory for this test file.
+ let path = OS.Path.join(OS.Constants.Path.tmpDir, leafName);
+ Assert.ok(!(yield OS.File.exists(path)));
+
+ // Ensure the file is deleted whe the test terminates.
+ add_termination_task(function* () {
+ if (yield OS.File.exists(path)) {
+ yield OS.File.remove(path);
+ }
+ });
+
+ return path;
+ }),
+};
+
+/* --- Local helpers --- */
+
+var FormAutofillTest = {
+ /**
+ * Stores the response that the next call to the mock requestAutocomplete UI
+ * will return to the requester, or null to enable displaying the default UI.
+ */
+ requestAutocompleteResponse: null,
+
+ /**
+ * Displays the requestAutocomplete user interface using the specified data.
+ *
+ * @param aFormAutofillData
+ * Serializable object containing the set of requested fields.
+ *
+ * @return {Promise}
+ * @resolves An object with the following properties:
+ * {
+ * uiWindow: Reference to the initialized window.
+ * promiseResult: Promise resolved by the UI when it closes.
+ * }
+ */
+ showUI: Task.async(function* (aFormAutofillData) {
+ Output.print("Opening UI with data: " + JSON.stringify(aFormAutofillData));
+
+ // Wait for the initialization event before opening the window.
+ let promiseUIWindow =
+ TestUtils.waitForNotification("formautofill-window-initialized");
+ let ui = yield FormAutofill.integration.createRequestAutocompleteUI(
+ aFormAutofillData);
+ let promiseResult = ui.show();
+
+ // The window is the subject of the observer notification.
+ return {
+ uiWindow: (yield promiseUIWindow)[0],
+ promiseResult: promiseResult,
+ };
+ }),
+};
+
+var TestData = {
+ /**
+ * Autofill UI request for the e-mail field only.
+ */
+ get requestEmailOnly() {
+ return {
+ sections: [{
+ name: "",
+ addressSections: [{
+ addressType: "",
+ fields: [{
+ fieldName: "email",
+ contactType: "",
+ }],
+ }],
+ }],
+ };
+ },
+};
+
+/* --- Initialization and termination functions common to all tests --- */
+
+add_task_in_parent_process(function* () {
+ // If required, we return a mock response instead of displaying the UI.
+ let mockIntegrationFn = base => ({
+ createRequestAutocompleteUI: Task.async(function* () {
+ // Call the base method to display the UI if override is not requested.
+ if (FormAutofillTest.requestAutocompleteResponse === null) {
+ return yield base.createRequestAutocompleteUI.apply(this, arguments);
+ }
+
+ // Return a mock RequestAutocompleteUI object.
+ return {
+ show: Task.async(function* () {
+ let response = FormAutofillTest.requestAutocompleteResponse;
+ Output.print("Mock UI response: " + JSON.stringify(response));
+ return response;
+ }),
+ };
+ }),
+ });
+
+ FormAutofill.registerIntegration(mockIntegrationFn);
+ add_termination_task(function* () {
+ FormAutofill.unregisterIntegration(mockIntegrationFn);
+ });
+});
+
+add_task_in_both_processes(function* () {
+ // We must manually enable the feature while testing.
+ Services.prefs.setBoolPref("dom.forms.requestAutocomplete", true);
+ add_termination_task(function* () {
+ Services.prefs.clearUserPref("dom.forms.requestAutocomplete");
+ });
+});
diff --git a/toolkit/components/formautofill/test/loader_common.js b/toolkit/components/formautofill/test/loader_common.js
new file mode 100644
index 000000000..340586b65
--- /dev/null
+++ b/toolkit/components/formautofill/test/loader_common.js
@@ -0,0 +1,120 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Infrastructure common to the test frameworks located in subfolders.
+ *
+ * A copy of this file is installed in each of the framework subfolders, this
+ * means it becomes a sibling of the test files in the final layout. This is
+ * determined by how manifest "support-files" installation works.
+ *
+ * Unless you are adding new features to the framework, you shouldn't have to
+ * modify this file. Use "head_common.js" or the "head.js" file of each
+ * framework for shared code.
+ */
+
+"use strict";
+
+/*
+ * --------------------
+ * FRAMEWORK OVERVIEW
+ * --------------------
+ *
+ * This framework is designed in such a way that test can be written in similar
+ * ways in the xpcshell, mochitest-chrome, and mochitest-browser frameworks,
+ * both when tests are running in the parent process or in a content process.
+ *
+ * There are some basic self-documenting assertion and output functions:
+ *
+ * Assert.ok(actualValue);
+ * Assert.is(actualValue, expectedValue);
+ * Output.print(string);
+ *
+ * Test cases and initialization functions are declared in shared head files
+ * ("head_common.js" and "head.js") as well as individual test files. When
+ * tests run in an Elecrolysis (e10s) environment, they are executed in both
+ * processes at first. Normally, at this point only the registration of test
+ * cases happen. When everything has finished loading, tests are started and
+ * appropriately synchronized between processes.
+ *
+ * Tests can be declared using the add_task syntax:
+ *
+ * add_task(function* test_something () { ... });
+ * This adds a test either in the parent process or child process:
+ * - Parent: xpcshell, mochitest-chrome --disable-e10s, mochitest-browser
+ * - Child: mochitest-chrome with e10s
+ * In the future, these might run in the child process for "xpcshell".
+ *
+ * add_task_in_parent_process(function* test_something () { ... });
+ * This test runs in the parent process, but the child process will wait for
+ * its completion before continuing with the next task. This wait currently
+ * happens only in mochitest-chrome with e10s, in other frameworks that run
+ * only in the parent process this is the same as a normal add_task.
+ *
+ * add_task_in_child_process(function* test_something () { ... });
+ * This test runs only in the child process. This means that the test is not
+ * run unless this is an e10s test, currently mochitest-chrome with e10s.
+ *
+ * add_task_in_both_processes(function* test_something () { ... });
+ * Useful for initialization that must be done both in the parent and the
+ * child, like setting preferences.
+ *
+ * add_termination_task(function* () { ... });
+ * Registers a new asynchronous termination task. This is executed after all
+ * test cases in the file finished, and always in the same process where the
+ * termination task is registered.
+ */
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+ "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
+
+var gTerminationTasks = [];
+
+/**
+ * None of the testing frameworks support asynchronous termination functions, so
+ * this task must be registered later, after the other "add_task" calls.
+ *
+ * Even xpcshell doesn't support calling "add_task" in the "tail.js" file,
+ * because it registers the task but does not wait for its termination,
+ * potentially leading to intermittent failures in subsequent tests.
+ */
+function* terminationTaskFn() {
+ for (let taskFn of gTerminationTasks) {
+ try {
+ yield Task.spawn(taskFn);
+ } catch (ex) {
+ Output.print(ex);
+ Assert.ok(false);
+ }
+ }
+}
+
+function add_termination_task(taskFn) {
+ gTerminationTasks.push(taskFn);
+}
+
+/**
+ * Returns a unique identifier used for synchronizing the given test task
+ * between the parent and child processes.
+ */
+function getTaskId(stackFrame) {
+ return stackFrame.filename + ":" + stackFrame.lineNumber;
+}
+
+// This is a shared helper for mochitest-chrome and mochitest-browser.
+var _mochitestAssert = {
+ ok: function (actual) {
+ let stack = Components.stack.caller;
+ ok(actual, "[" + stack.name + " : " + stack.lineNumber + "] " + actual +
+ " == true");
+ },
+ equal: function (actual, expected) {
+ let stack = Components.stack.caller;
+ is(actual, expected, "[" + stack.name + " : " + stack.lineNumber + "] " +
+ actual + " == " + expected);
+ },
+};
+
+// Reminder: unless you are adding new features to the framework, you shouldn't
+// have to modify this file. Use "head_common.js" or "head.js" for shared code.
diff --git a/toolkit/components/formautofill/test/xpcshell/.eslintrc.js b/toolkit/components/formautofill/test/xpcshell/.eslintrc.js
new file mode 100644
index 000000000..d35787cd2
--- /dev/null
+++ b/toolkit/components/formautofill/test/xpcshell/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/xpcshell/xpcshell.eslintrc.js"
+ ]
+};
diff --git a/toolkit/components/formautofill/test/xpcshell/head.js b/toolkit/components/formautofill/test/xpcshell/head.js
new file mode 100644
index 000000000..1cee023f2
--- /dev/null
+++ b/toolkit/components/formautofill/test/xpcshell/head.js
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Initialization specific to Form Autofill xpcshell tests.
+ *
+ * This file is loaded by "loader.js".
+ */
+
+"use strict";
+
+// The testing framework is fully initialized at this point, you can add
+// xpcshell specific test initialization here. If you need shared functions or
+// initialization that are not specific to xpcshell, consider adding them to
+// "head_common.js" in the parent folder instead.
+
+add_task_in_parent_process(function* test_xpcshell_initialize_profile() {
+ // We need to send the profile-after-change notification manually to the
+ // startup component to ensure it has been initialized.
+ Cc["@mozilla.org/formautofill/startup;1"]
+ .getService(Ci.nsIObserver)
+ .observe(null, "profile-after-change", "");
+});
diff --git a/toolkit/components/formautofill/test/xpcshell/loader.js b/toolkit/components/formautofill/test/xpcshell/loader.js
new file mode 100644
index 000000000..449989c8a
--- /dev/null
+++ b/toolkit/components/formautofill/test/xpcshell/loader.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Infrastructure for the xpcshell tests located in this folder.
+ *
+ * See "loader_common.js" in the parent folder for a general overview.
+ *
+ * Unless you are adding new features to the framework, you shouldn't have to
+ * modify this file. Use "head_common.js" or "head.js" for shared code.
+ */
+
+"use strict";
+
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm", this);
+
+Services.scriptloader.loadSubScript(
+ Services.io.newFileURI(do_get_file("loader_common.js")).spec, this);
+
+// Define output functions so they look the same across all frameworks.
+var Output = {
+ print: do_print,
+};
+
+var executeSoon = do_execute_soon;
+var setTimeout = (fn, delay) => do_timeout(delay, fn);
+
+// Define task registration functions, see description in "loader_common.js".
+var add_task_in_parent_process = add_task;
+var add_task_in_child_process = function () {};
+var add_task_in_both_processes = add_task;
+
+Services.scriptloader.loadSubScript(
+ Services.io.newFileURI(do_get_file("head_common.js")).spec, this);
+
+// Tests are always run asynchronously and with the profile loaded.
+function run_test() {
+ do_get_profile();
+ run_next_test();
+}
+
+// Reminder: unless you are adding new features to the framework, you shouldn't
+// have to modify this file. Use "head_common.js" or "head.js" for shared code.
diff --git a/toolkit/components/formautofill/test/xpcshell/test_infrastructure.js b/toolkit/components/formautofill/test/xpcshell/test_infrastructure.js
new file mode 100644
index 000000000..af27cfdb5
--- /dev/null
+++ b/toolkit/components/formautofill/test/xpcshell/test_infrastructure.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests the local testing infrastructure.
+ */
+
+"use strict";
+
+/**
+ * Tests the truth assertion function.
+ */
+add_task(function* test_assert_truth() {
+ Assert.ok(1 != 2);
+});
+
+/**
+ * Tests the equality assertion function.
+ */
+add_task(function* test_assert_equality() {
+ Assert.equal(1 + 1, 2);
+});
+
+/**
+ * Uses some of the utility functions provided by the framework.
+ */
+add_task(function* test_utility_functions() {
+ // The "print" function is useful to log information that is not known before.
+ let randomString = "R" + Math.floor(Math.random() * 10);
+ Output.print("The random contents will be '" + randomString + "'.");
+
+ // Create the text file with the random contents.
+ let path = yield TestUtils.getTempFile("test-infrastructure.txt");
+ yield OS.File.writeAtomic(path, new TextEncoder().encode(randomString));
+
+ // Test a few utility functions.
+ yield TestUtils.waitForTick();
+ yield TestUtils.waitMs(50);
+
+ let promiseMyNotification = TestUtils.waitForNotification("my-topic");
+ Services.obs.notifyObservers(null, "my-topic", "");
+ yield promiseMyNotification;
+
+ // Check the file size. The file will be deleted automatically later.
+ Assert.equal((yield OS.File.stat(path)).size, randomString.length);
+});
+
+add_task(terminationTaskFn);
diff --git a/toolkit/components/formautofill/test/xpcshell/test_integration.js b/toolkit/components/formautofill/test/xpcshell/test_integration.js
new file mode 100644
index 000000000..7707f3880
--- /dev/null
+++ b/toolkit/components/formautofill/test/xpcshell/test_integration.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests overriding the FormAutofillIntegration module functions.
+ */
+
+"use strict";
+
+/**
+ * The requestAutocomplete UI will not be displayed during these tests.
+ */
+add_task_in_parent_process(function* test_initialize() {
+ FormAutofillTest.requestAutocompleteResponse = { canceled: true };
+});
+
+/**
+ * Registers and unregisters an integration override function.
+ */
+add_task(function* test_integration_override() {
+ let overrideCalled = false;
+
+ let newIntegrationFn = base => ({
+ createRequestAutocompleteUI: Task.async(function* () {
+ overrideCalled = true;
+ return yield base.createRequestAutocompleteUI.apply(this, arguments);
+ }),
+ });
+
+ FormAutofill.registerIntegration(newIntegrationFn);
+ try {
+ let ui = yield FormAutofill.integration.createRequestAutocompleteUI({});
+ let result = yield ui.show();
+ Assert.ok(result.canceled);
+ } finally {
+ FormAutofill.unregisterIntegration(newIntegrationFn);
+ }
+
+ Assert.ok(overrideCalled);
+});
+
+/**
+ * Registers an integration override function that throws an exception, and
+ * ensures that this does not block other functions from being registered.
+ */
+add_task(function* test_integration_override_error() {
+ let overrideCalled = false;
+
+ let errorIntegrationFn = base => { throw "Expected error." };
+
+ let newIntegrationFn = base => ({
+ createRequestAutocompleteUI: Task.async(function* () {
+ overrideCalled = true;
+ return yield base.createRequestAutocompleteUI.apply(this, arguments);
+ }),
+ });
+
+ FormAutofill.registerIntegration(errorIntegrationFn);
+ FormAutofill.registerIntegration(newIntegrationFn);
+ try {
+ let ui = yield FormAutofill.integration.createRequestAutocompleteUI({});
+ let result = yield ui.show();
+ Assert.ok(result.canceled);
+ } finally {
+ FormAutofill.unregisterIntegration(errorIntegrationFn);
+ FormAutofill.unregisterIntegration(newIntegrationFn);
+ }
+
+ Assert.ok(overrideCalled);
+});
+
+add_task(terminationTaskFn);
diff --git a/toolkit/components/formautofill/test/xpcshell/xpcshell.ini b/toolkit/components/formautofill/test/xpcshell/xpcshell.ini
new file mode 100644
index 000000000..711c03399
--- /dev/null
+++ b/toolkit/components/formautofill/test/xpcshell/xpcshell.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+head = loader.js head.js
+tail =
+skip-if = toolkit == 'android'
+# The following files starting with ".." are installed in the current folder.
+# However, they cannot be referenced directly in the "head" directive above.
+support-files =
+ ../head_common.js
+ ../loader_common.js
+
+[test_infrastructure.js]
+[test_integration.js]