summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/browser/browser_autoFill_placeholder.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/tests/browser/browser_autoFill_placeholder.js')
-rw-r--r--browser/components/urlbar/tests/browser/browser_autoFill_placeholder.js1017
1 files changed, 1017 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/browser/browser_autoFill_placeholder.js b/browser/components/urlbar/tests/browser/browser_autoFill_placeholder.js
new file mode 100644
index 0000000000..6fcd664de0
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_autoFill_placeholder.js
@@ -0,0 +1,1017 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This test makes sure that the autofill placeholder value is autofilled
+// correctly. The placeholder is a string that we immediately autofill when a
+// search starts and before its first result arrives in order to prevent text
+// flicker in the input.
+//
+// Because this test specifically checks autofill *before* searches complete, we
+// can't use promiseAutocompleteResultPopup() or other helpers that wait for
+// searches to complete. Instead the test uses fireInputEvent() to trigger
+// placeholder autofill and then immediately checks autofill status.
+
+"use strict";
+
+// Allow more time for verify mode.
+requestLongerTimeout(5);
+
+add_setup(async function () {
+ await cleanUp();
+});
+
+// Basic origin autofill test.
+add_task(async function origin() {
+ await addVisits("http://example.com/");
+
+ await search({
+ searchString: "ex",
+ valueBefore: "ex",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+ await search({
+ searchString: "exa",
+ valueBefore: "example.com/",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+ await search({
+ searchString: "EXAM",
+ valueBefore: "EXAMple.com/",
+ valueAfter: "EXAMple.com/",
+ placeholderAfter: "EXAMple.com/",
+ });
+ await search({
+ searchString: "eXaMp",
+ valueBefore: "eXaMple.com/",
+ valueAfter: "eXaMple.com/",
+ placeholderAfter: "eXaMple.com/",
+ });
+ await search({
+ searchString: "exampL",
+ valueBefore: "exampLe.com/",
+ valueAfter: "exampLe.com/",
+ placeholderAfter: "exampLe.com/",
+ });
+ await search({
+ searchString: "example.com",
+ valueBefore: "example.com/",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+ await search({
+ searchString: "example.com/",
+ valueBefore: "example.com/",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+
+ await cleanUp();
+});
+
+// Basic URL autofill test.
+add_task(async function url() {
+ await addVisits("http://example.com/aaa/bbb/ccc");
+
+ await search({
+ searchString: "example.com/a",
+ valueBefore: "example.com/a",
+ valueAfter: "example.com/aaa/",
+ placeholderAfter: "example.com/aaa/",
+ });
+ await search({
+ searchString: "EXAmple.com/aA",
+ valueBefore: "EXAmple.com/aAa/",
+ valueAfter: "EXAmple.com/aAa/",
+ placeholderAfter: "EXAmple.com/aAa/",
+ });
+ await search({
+ searchString: "example.com/aAa",
+ valueBefore: "example.com/aAa/",
+ valueAfter: "example.com/aAa/",
+ placeholderAfter: "example.com/aAa/",
+ });
+ await search({
+ searchString: "example.com/aaa/",
+ valueBefore: "example.com/aaa/",
+ valueAfter: "example.com/aaa/",
+ placeholderAfter: "example.com/aaa/",
+ });
+ await search({
+ searchString: "example.com/aaa/b",
+ valueBefore: "example.com/aaa/b",
+ valueAfter: "example.com/aaa/bbb/",
+ placeholderAfter: "example.com/aaa/bbb/",
+ });
+ await search({
+ searchString: "example.com/aAa/bB",
+ valueBefore: "example.com/aAa/bBb/",
+ valueAfter: "example.com/aAa/bBb/",
+ placeholderAfter: "example.com/aAa/bBb/",
+ });
+ await search({
+ searchString: "example.com/aAa/bBb",
+ valueBefore: "example.com/aAa/bBb/",
+ valueAfter: "example.com/aAa/bBb/",
+ placeholderAfter: "example.com/aAa/bBb/",
+ });
+ await search({
+ searchString: "example.com/aaa/bbb/",
+ valueBefore: "example.com/aaa/bbb/",
+ valueAfter: "example.com/aaa/bbb/",
+ placeholderAfter: "example.com/aaa/bbb/",
+ });
+ await search({
+ searchString: "example.com/aaa/bbb/c",
+ valueBefore: "example.com/aaa/bbb/c",
+ valueAfter: "example.com/aaa/bbb/ccc",
+ placeholderAfter: "example.com/aaa/bbb/ccc",
+ });
+ await search({
+ searchString: "example.com/aAa/bBb/cC",
+ valueBefore: "example.com/aAa/bBb/cCc",
+ valueAfter: "example.com/aAa/bBb/cCc",
+ placeholderAfter: "example.com/aAa/bBb/cCc",
+ });
+ await search({
+ searchString: "example.com/aaa/bbb/ccc",
+ valueBefore: "example.com/aaa/bbb/ccc",
+ valueAfter: "example.com/aaa/bbb/ccc",
+ placeholderAfter: "example.com/aaa/bbb/ccc",
+ });
+
+ await cleanUp();
+});
+
+// Basic adaptive history autofill test.
+add_task(async function adaptiveHistory() {
+ UrlbarPrefs.set("autoFill.adaptiveHistory.enabled", true);
+
+ await addVisits("http://example.com/test");
+ await UrlbarUtils.addToInputHistory("http://example.com/test", "exa");
+
+ await search({
+ searchString: "exa",
+ valueBefore: "exa",
+ valueAfter: "example.com/test",
+ placeholderAfter: "example.com/test",
+ });
+ await search({
+ searchString: "EXAM",
+ valueBefore: "EXAMple.com/test",
+ valueAfter: "EXAMple.com/test",
+ placeholderAfter: "EXAMple.com/test",
+ });
+ await search({
+ searchString: "eXaMpLe",
+ valueBefore: "eXaMpLe.com/test",
+ valueAfter: "eXaMpLe.com/test",
+ placeholderAfter: "eXaMpLe.com/test",
+ });
+ await search({
+ searchString: "example.",
+ valueBefore: "example.com/test",
+ valueAfter: "example.com/test",
+ placeholderAfter: "example.com/test",
+ });
+ await search({
+ searchString: "example.c",
+ valueBefore: "example.com/test",
+ valueAfter: "example.com/test",
+ placeholderAfter: "example.com/test",
+ });
+ await search({
+ searchString: "example.com",
+ valueBefore: "example.com/test",
+ valueAfter: "example.com/test",
+ placeholderAfter: "example.com/test",
+ });
+ await search({
+ searchString: "example.com/",
+ valueBefore: "example.com/test",
+ valueAfter: "example.com/test",
+ placeholderAfter: "example.com/test",
+ });
+ await search({
+ searchString: "example.com/T",
+ valueBefore: "example.com/Test",
+ valueAfter: "example.com/Test",
+ placeholderAfter: "example.com/Test",
+ });
+ await search({
+ searchString: "eXaMple.com/tE",
+ valueBefore: "eXaMple.com/tEst",
+ valueAfter: "eXaMple.com/tEst",
+ placeholderAfter: "eXaMple.com/tEst",
+ });
+ await search({
+ searchString: "example.com/tes",
+ valueBefore: "example.com/test",
+ valueAfter: "example.com/test",
+ placeholderAfter: "example.com/test",
+ });
+ await search({
+ searchString: "example.com/test",
+ valueBefore: "example.com/test",
+ valueAfter: "example.com/test",
+ placeholderAfter: "example.com/test",
+ });
+
+ UrlbarPrefs.clear("autoFill.adaptiveHistory.enabled");
+ await cleanUp();
+});
+
+// Search engine token alias test (aliases that start with "@").
+add_task(async function tokenAlias() {
+ // We have built-in engine aliases that may conflict with the one we choose
+ // here in terms of autofill, so be careful and choose a weird alias.
+ await SearchTestUtils.installSearchExtension({ keyword: "@__example" });
+
+ await search({
+ searchString: "@__ex",
+ valueBefore: "@__ex",
+ valueAfter: "@__example ",
+ placeholderAfter: "@__example ",
+ });
+ await search({
+ searchString: "@__exa",
+ valueBefore: "@__example ",
+ valueAfter: "@__example ",
+ placeholderAfter: "@__example ",
+ });
+ await search({
+ searchString: "@__EXAM",
+ valueBefore: "@__EXAMple ",
+ valueAfter: "@__EXAMple ",
+ placeholderAfter: "@__EXAMple ",
+ });
+ await search({
+ searchString: "@__eXaMp",
+ valueBefore: "@__eXaMple ",
+ valueAfter: "@__eXaMple ",
+ placeholderAfter: "@__eXaMple ",
+ });
+ await search({
+ searchString: "@__exampl",
+ valueBefore: "@__example ",
+ valueAfter: "@__example ",
+ placeholderAfter: "@__example ",
+ });
+
+ await cleanUp();
+});
+
+// The placeholder should not be used for a search that does not autofill, and
+// it should be cleared after the search completes.
+add_task(async function noAutofill() {
+ await addVisits("http://example.com/");
+
+ // Do an initial search that triggers autofill so that the placeholder has an
+ // initial value.
+ await search({
+ searchString: "ex",
+ valueBefore: "ex",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+
+ // Search with a string that does not match the placeholder. Placeholder
+ // autofill shouldn't happen.
+ await search({
+ searchString: "moz",
+ valueBefore: "moz",
+ valueAfter: "moz",
+ placeholderAfter: "",
+ });
+
+ // Search for "ex" again. It should be autofilled when the search completes
+ // but the placeholder will not be autofilled.
+ await search({
+ searchString: "ex",
+ valueBefore: "ex",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+
+ // Continue with a series of searches that should all use the placeholder.
+ await search({
+ searchString: "exa",
+ valueBefore: "example.com/",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+ await search({
+ searchString: "EXAM",
+ valueBefore: "EXAMple.com/",
+ valueAfter: "EXAMple.com/",
+ placeholderAfter: "EXAMple.com/",
+ });
+ await search({
+ searchString: "eXaMp",
+ valueBefore: "eXaMple.com/",
+ valueAfter: "eXaMple.com/",
+ placeholderAfter: "eXaMple.com/",
+ });
+ await search({
+ searchString: "exampl",
+ valueBefore: "example.com/",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+
+ await cleanUp();
+});
+
+// The placeholder should not be used for a search that autofills a different
+// value.
+add_task(async function differentAutofill() {
+ await addVisits("http://mozilla.org/", "http://example.com/");
+
+ // Do an initial search that triggers autofill so that the placeholder has an
+ // initial value.
+ await search({
+ searchString: "moz",
+ valueBefore: "moz",
+ valueAfter: "mozilla.org/",
+ placeholderAfter: "mozilla.org/",
+ });
+
+ // Search with a string that does not match the placeholder but does trigger
+ // autofill. Placeholder autofill shouldn't happen.
+ await search({
+ searchString: "ex",
+ valueBefore: "ex",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+
+ // Continue with a series of searches that should all use the placeholder.
+ await search({
+ searchString: "exa",
+ valueBefore: "example.com/",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+ await search({
+ searchString: "EXAm",
+ valueBefore: "EXAmple.com/",
+ valueAfter: "EXAmple.com/",
+ placeholderAfter: "EXAmple.com/",
+ });
+
+ // Search for "moz" again. It should be autofilled. Placeholder autofill
+ // shouldn't happen.
+ await search({
+ searchString: "moz",
+ valueBefore: "moz",
+ valueAfter: "mozilla.org/",
+ placeholderAfter: "mozilla.org/",
+ });
+
+ // Continue with a series of searches that should all use the placeholder.
+ await search({
+ searchString: "mozi",
+ valueBefore: "mozilla.org/",
+ valueAfter: "mozilla.org/",
+ placeholderAfter: "mozilla.org/",
+ });
+ await search({
+ searchString: "MOZil",
+ valueBefore: "MOZilla.org/",
+ valueAfter: "MOZilla.org/",
+ placeholderAfter: "MOZilla.org/",
+ });
+
+ await cleanUp();
+});
+
+// The placeholder should not be used for a search that uses a bookmark keyword
+// even when the keyword matches the placeholder, and the placeholder should be
+// cleared after the search completes.
+add_task(async function bookmarkKeyword() {
+ // Add a visit to example.com.
+ await addVisits("https://example.com/");
+
+ // Add a bookmark keyword that is a prefix of example.com.
+ await PlacesUtils.keywords.insert({
+ keyword: "ex",
+ url: "https://somekeyword.com/",
+ });
+
+ // Do an initial search that triggers autofill for the visit so that the
+ // placeholder has an initial value of "example.com/".
+ await search({
+ searchString: "e",
+ valueBefore: "e",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+
+ // Do a search that matches the bookmark keyword. The placeholder from the
+ // search above should be autofilled since the autofill placeholder
+ // ("example.com/") starts with the keyword ("ex"), but then when the bookmark
+ // result arrives, the autofilled value and placeholder should be cleared.
+ await search({
+ searchString: "ex",
+ valueBefore: "example.com/",
+ valueAfter: "ex",
+ placeholderAfter: "",
+ });
+
+ // Do another search that simulates the user continuing to type "example". No
+ // placeholder should be autofilled, but once the autofill result arrives for
+ // the visit, "example.com/" should be autofilled.
+ await search({
+ searchString: "exa",
+ valueBefore: "exa",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+
+ await PlacesUtils.keywords.remove("ex");
+ await cleanUp();
+});
+
+// The placeholder should not be used for a search that doesn't match its URI
+// fragment. This task uses a URL whose path is "/".
+add_task(async function noURIFragmentMatch1() {
+ await addVisits("https://example.com/#TEST");
+
+ const testData = [
+ {
+ desc: "Autofill example.com/#TEST then search for example.com/#Te",
+ searches: [
+ {
+ searchString: "example.com/#T",
+ valueBefore: "example.com/#T",
+ valueAfter: "example.com/#TEST",
+ placeholderAfter: "example.com/#TEST",
+ },
+ {
+ searchString: "example.com/#Te",
+ valueBefore: "example.com/#Te",
+ valueAfter: "example.com/#Te",
+ placeholderAfter: "",
+ },
+ ],
+ },
+ {
+ desc: "Autofill https://example.com/#TEST then search for https://example.com/#Te",
+ searches: [
+ {
+ searchString: "https://example.com/#T",
+ valueBefore: "https://example.com/#T",
+ valueAfter: "https://example.com/#TEST",
+ placeholderAfter: "https://example.com/#TEST",
+ },
+ {
+ searchString: "https://example.com/#Te",
+ valueBefore: "https://example.com/#Te",
+ valueAfter: "https://example.com/#Te",
+ placeholderAfter: "",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/#TEST then search for example.com/",
+ searches: [
+ {
+ searchString: "example.com/#T",
+ valueBefore: "example.com/#T",
+ valueAfter: "example.com/#TEST",
+ placeholderAfter: "example.com/#TEST",
+ },
+ {
+ searchString: "example.com/",
+ valueBefore: "example.com/",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ },
+ ],
+ },
+ ];
+
+ for (const { desc, searches } of testData) {
+ info("Running subtest: " + desc);
+
+ for (let i = 0; i < searches.length; i++) {
+ info("Doing search at index " + i);
+ await search(searches[i]);
+ }
+
+ // Clear the placeholder for the next subtest.
+ info("Doing extra search to clear placeholder");
+ await search({
+ searchString: "no match",
+ valueBefore: "no match",
+ valueAfter: "no match",
+ placeholderAfter: "",
+ });
+ }
+
+ await cleanUp();
+});
+
+// The placeholder should not be used for a search that doesn't match its URI
+// fragment. This task uses a URL whose path is "/foo".
+add_task(async function noURIFragmentMatch2() {
+ await addVisits("https://example.com/foo#TEST");
+
+ const testData = [
+ {
+ desc: "Autofill example.com/foo#TEST then search for example.com/foo#Te",
+ searches: [
+ {
+ searchString: "example.com/foo#T",
+ valueBefore: "example.com/foo#T",
+ valueAfter: "example.com/foo#TEST",
+ placeholderAfter: "example.com/foo#TEST",
+ },
+ {
+ searchString: "example.com/foo#Te",
+ valueBefore: "example.com/foo#Te",
+ valueAfter: "example.com/foo#Te",
+ placeholderAfter: "",
+ },
+ ],
+ },
+ {
+ desc: "Autofill https://example.com/foo#TEST then search for https://example.com/foo#Te",
+ searches: [
+ {
+ searchString: "https://example.com/foo#T",
+ valueBefore: "https://example.com/foo#T",
+ valueAfter: "https://example.com/foo#TEST",
+ placeholderAfter: "https://example.com/foo#TEST",
+ },
+ {
+ searchString: "https://example.com/foo#Te",
+ valueBefore: "https://example.com/foo#Te",
+ valueAfter: "https://example.com/foo#Te",
+ placeholderAfter: "",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/foo#TEST then search for example.com/",
+ searches: [
+ {
+ searchString: "example.com/foo#T",
+ valueBefore: "example.com/foo#T",
+ valueAfter: "example.com/foo#TEST",
+ placeholderAfter: "example.com/foo#TEST",
+ },
+ {
+ searchString: "example.com/",
+ valueBefore: "example.com/",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ },
+ ],
+ },
+ ];
+
+ for (const { desc, searches } of testData) {
+ info("Running subtest: " + desc);
+
+ for (let i = 0; i < searches.length; i++) {
+ info("Doing search at index " + i);
+ await search(searches[i]);
+ }
+
+ // Clear the placeholder for the next subtest.
+ info("Doing extra search to clear placeholder");
+ await search({
+ searchString: "no match",
+ valueBefore: "no match",
+ valueAfter: "no match",
+ placeholderAfter: "",
+ });
+ }
+
+ await cleanUp();
+});
+
+// The placeholder should not be used for a search that does not autofill its
+// URL path.
+add_task(async function noPathMatch() {
+ await addVisits("http://example.com/shallow/deep/file");
+
+ const testData = [
+ {
+ desc: "Autofill example.com/shallow/ then search for exam",
+ searches: [
+ {
+ searchString: "example.com/s",
+ valueBefore: "example.com/s",
+ valueAfter: "example.com/shallow/",
+ placeholderAfter: "example.com/shallow/",
+ },
+ {
+ searchString: "exam",
+ valueBefore: "exam",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/shallow/ then search for example.com/",
+ searches: [
+ {
+ searchString: "example.com/s",
+ valueBefore: "example.com/s",
+ valueAfter: "example.com/shallow/",
+ placeholderAfter: "example.com/shallow/",
+ },
+ {
+ searchString: "example.com/",
+ valueBefore: "example.com/",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/shallow/deep/ then search for exam",
+ searches: [
+ {
+ searchString: "example.com/shallow/d",
+ valueBefore: "example.com/shallow/d",
+ valueAfter: "example.com/shallow/deep/",
+ placeholderAfter: "example.com/shallow/deep/",
+ },
+ {
+ searchString: "exam",
+ valueBefore: "exam",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/shallow/deep/ then search for example.com/",
+ searches: [
+ {
+ searchString: "example.com/shallow/d",
+ valueBefore: "example.com/shallow/d",
+ valueAfter: "example.com/shallow/deep/",
+ placeholderAfter: "example.com/shallow/deep/",
+ },
+ {
+ searchString: "example.com/",
+ valueBefore: "example.com/",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/shallow/deep/ then search for example.com/s",
+ searches: [
+ {
+ searchString: "example.com/shallow/d",
+ valueBefore: "example.com/shallow/d",
+ valueAfter: "example.com/shallow/deep/",
+ placeholderAfter: "example.com/shallow/deep/",
+ },
+ {
+ searchString: "example.com/s",
+ valueBefore: "example.com/s",
+ valueAfter: "example.com/shallow/",
+ placeholderAfter: "example.com/shallow/",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/shallow/deep/ then search for example.com/shallow/",
+ searches: [
+ {
+ searchString: "example.com/shallow/d",
+ valueBefore: "example.com/shallow/d",
+ valueAfter: "example.com/shallow/deep/",
+ placeholderAfter: "example.com/shallow/deep/",
+ },
+ {
+ searchString: "example.com/shallow/",
+ valueBefore: "example.com/shallow/",
+ valueAfter: "example.com/shallow/",
+ placeholderAfter: "example.com/shallow/",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/shallow/deep/file then search for exam",
+ searches: [
+ {
+ searchString: "example.com/shallow/deep/f",
+ valueBefore: "example.com/shallow/deep/f",
+ valueAfter: "example.com/shallow/deep/file",
+ placeholderAfter: "example.com/shallow/deep/file",
+ },
+ {
+ searchString: "exam",
+ valueBefore: "exam",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/shallow/deep/file then search for example.com/",
+ searches: [
+ {
+ searchString: "example.com/shallow/deep/f",
+ valueBefore: "example.com/shallow/deep/f",
+ valueAfter: "example.com/shallow/deep/file",
+ placeholderAfter: "example.com/shallow/deep/file",
+ },
+ {
+ searchString: "example.com/",
+ valueBefore: "example.com/",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/shallow/deep/file then search for example.com/s",
+ searches: [
+ {
+ searchString: "example.com/shallow/deep/f",
+ valueBefore: "example.com/shallow/deep/f",
+ valueAfter: "example.com/shallow/deep/file",
+ placeholderAfter: "example.com/shallow/deep/file",
+ },
+ {
+ searchString: "example.com/s",
+ valueBefore: "example.com/s",
+ valueAfter: "example.com/shallow/",
+ placeholderAfter: "example.com/shallow/",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/shallow/deep/file then search for example.com/shallow/",
+ searches: [
+ {
+ searchString: "example.com/shallow/deep/f",
+ valueBefore: "example.com/shallow/deep/f",
+ valueAfter: "example.com/shallow/deep/file",
+ placeholderAfter: "example.com/shallow/deep/file",
+ },
+ {
+ searchString: "example.com/shallow/",
+ valueBefore: "example.com/shallow/",
+ valueAfter: "example.com/shallow/",
+ placeholderAfter: "example.com/shallow/",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/shallow/deep/file then search for example.com/shallow/d",
+ searches: [
+ {
+ searchString: "example.com/shallow/deep/f",
+ valueBefore: "example.com/shallow/deep/f",
+ valueAfter: "example.com/shallow/deep/file",
+ placeholderAfter: "example.com/shallow/deep/file",
+ },
+ {
+ searchString: "example.com/shallow/d",
+ valueBefore: "example.com/shallow/d",
+ valueAfter: "example.com/shallow/deep/",
+ placeholderAfter: "example.com/shallow/deep/",
+ },
+ ],
+ },
+ {
+ desc: "Autofill example.com/shallow/deep/file then search for example.com/shallow/deep/",
+ searches: [
+ {
+ searchString: "example.com/shallow/deep/f",
+ valueBefore: "example.com/shallow/deep/f",
+ valueAfter: "example.com/shallow/deep/file",
+ placeholderAfter: "example.com/shallow/deep/file",
+ },
+ {
+ searchString: "example.com/shallow/deep/fi",
+ valueBefore: "example.com/shallow/deep/file",
+ valueAfter: "example.com/shallow/deep/file",
+ placeholderAfter: "example.com/shallow/deep/file",
+ },
+ {
+ searchString: "example.com/shallow/deep/",
+ valueBefore: "example.com/shallow/deep/",
+ valueAfter: "example.com/shallow/deep/",
+ placeholderAfter: "example.com/shallow/deep/",
+ },
+ ],
+ },
+ ];
+
+ for (const { desc, searches } of testData) {
+ info("Running subtest: " + desc);
+
+ for (let i = 0; i < searches.length; i++) {
+ info("Doing search at index " + i);
+ await search(searches[i]);
+ }
+
+ // Clear the placeholder for the next subtest.
+ info("Doing extra search to clear placeholder");
+ await search({
+ searchString: "no match",
+ valueBefore: "no match",
+ valueAfter: "no match",
+ placeholderAfter: "",
+ });
+ }
+
+ await cleanUp();
+});
+
+// An adaptive history placeholder should not be used for a search that does not
+// autofill it.
+add_task(async function noAdaptiveHistoryMatch() {
+ UrlbarPrefs.set("autoFill.adaptiveHistory.enabled", true);
+
+ await addVisits("http://example.com/test");
+ await UrlbarUtils.addToInputHistory("http://example.com/test", "exam");
+
+ // Search for a longer string than the adaptive history input. Adaptive
+ // history autofill should be triggered.
+ await search({
+ searchString: "example",
+ valueBefore: "example",
+ valueAfter: "example.com/test",
+ placeholderAfter: "example.com/test",
+ });
+
+ // Search for the same string as the adaptive history input. The placeholder
+ // from the previous search should be used and adaptive history autofill
+ // should be triggered.
+ await search({
+ searchString: "exam",
+ valueBefore: "example.com/test",
+ valueAfter: "example.com/test",
+ placeholderAfter: "example.com/test",
+ });
+
+ // Search for a shorter string than the adaptive history input. The
+ // placeholder from the previous search should not be used since the search
+ // string is shorter than the adaptive history input.
+ await search({
+ searchString: "ex",
+ valueBefore: "ex",
+ valueAfter: "example.com/",
+ placeholderAfter: "example.com/",
+ });
+
+ UrlbarPrefs.clear("autoFill.adaptiveHistory.enabled");
+ await cleanUp();
+});
+
+/**
+ * This function does the following:
+ *
+ * 1. Starts a search with `searchString` but doesn't wait for it to complete.
+ * 2. Compares the input value to `valueBefore`. If anything is autofilled at
+ * this point, it will be due to the placeholder.
+ * 3. Waits for the search to complete.
+ * 4. Compares the input value to `valueAfter`. If anything is autofilled at
+ * this point, it will be due to the autofill result fetched by the search.
+ * 5. Compares the placeholder to `placeholderAfter`.
+ *
+ * @param {object} options
+ * The options object.
+ * @param {string} options.searchString
+ * The search string.
+ * @param {string} options.valueBefore
+ * The expected input value before the search completes.
+ * @param {string} options.valueAfter
+ * The expected input value after the search completes.
+ * @param {string} options.placeholderAfter
+ * The expected placeholder value after the search completes.
+ * @returns {Promise}
+ */
+async function search({
+ searchString,
+ valueBefore,
+ valueAfter,
+ placeholderAfter,
+}) {
+ info(
+ "Searching: " +
+ JSON.stringify({
+ searchString,
+ valueBefore,
+ valueAfter,
+ placeholderAfter,
+ })
+ );
+
+ await SimpleTest.promiseFocus(window);
+ gURLBar.inputField.focus();
+
+ // Set the input value and move the caret to the end to simulate the user
+ // typing. It's important the caret is at the end because otherwise autofill
+ // won't happen.
+ gURLBar.value = searchString;
+ gURLBar.inputField.setSelectionRange(
+ searchString.length,
+ searchString.length
+ );
+
+ // Placeholder autofill is done on input, so fire an input event. We can't use
+ // `promiseAutocompleteResultPopup()` or other helpers that wait for the
+ // search to complete because we are specifically checking placeholder
+ // autofill before the search completes.
+ UrlbarTestUtils.fireInputEvent(window);
+
+ // Check the input value and selection immediately, before waiting on the
+ // search to complete.
+ Assert.equal(
+ gURLBar.value,
+ valueBefore,
+ "gURLBar.value before the search completes"
+ );
+ Assert.equal(
+ gURLBar.selectionStart,
+ searchString.length,
+ "gURLBar.selectionStart before the search completes"
+ );
+ Assert.equal(
+ gURLBar.selectionEnd,
+ valueBefore.length,
+ "gURLBar.selectionEnd before the search completes"
+ );
+
+ // Wait for the search to complete.
+ info("Waiting for the search to complete");
+ await UrlbarTestUtils.promiseSearchComplete(window);
+
+ // Check the final value after the results arrived.
+ Assert.equal(
+ gURLBar.value,
+ valueAfter,
+ "gURLBar.value after the search completes"
+ );
+ Assert.equal(
+ gURLBar.selectionStart,
+ searchString.length,
+ "gURLBar.selectionStart after the search completes"
+ );
+ Assert.equal(
+ gURLBar.selectionEnd,
+ valueAfter.length,
+ "gURLBar.selectionEnd after the search completes"
+ );
+
+ // Check the placeholder.
+ if (placeholderAfter) {
+ Assert.ok(
+ gURLBar._autofillPlaceholder,
+ "gURLBar._autofillPlaceholder exists after the search completes"
+ );
+ Assert.strictEqual(
+ gURLBar._autofillPlaceholder.value,
+ placeholderAfter,
+ "gURLBar._autofillPlaceholder.value after the search completes"
+ );
+ } else {
+ Assert.strictEqual(
+ gURLBar._autofillPlaceholder,
+ null,
+ "gURLBar._autofillPlaceholder does not exist after the search completes"
+ );
+ }
+
+ // Check the first result.
+ let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ Assert.equal(
+ !!details.autofill,
+ !!placeholderAfter,
+ "First result is an autofill result iff a placeholder is expected"
+ );
+}
+
+/**
+ * Adds enough visits to URLs so their origins start autofilling.
+ *
+ * @param {...string} urls The URLs to add visits to.
+ */
+async function addVisits(...urls) {
+ for (let url of urls) {
+ for (let i = 0; i < 5; i++) {
+ await PlacesTestUtils.addVisits(url);
+ }
+ }
+}
+
+async function cleanUp() {
+ await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur());
+ await PlacesUtils.bookmarks.eraseEverything();
+ await PlacesUtils.history.clear();
+}