Bug 1549809 - Automated tests for filtering logins. r=MattN
authorJared Wein <jwein@mozilla.com>
Tue, 14 May 2019 20:06:23 +0000
changeset 532682 ae45390d506114e7ffd97d35918e63b6912252e7
parent 532681 2e3ae6f6b1cf59ef0babbb29ce6dff99c880b336
child 532683 22be0302af4d107d45d7715def74909b2a0a7377
push id11270
push userrgurzau@mozilla.com
push dateWed, 15 May 2019 15:07:19 +0000
treeherdermozilla-beta@571bc76da583 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1549809
milestone68.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 1549809 - Automated tests for filtering logins. r=MattN Differential Revision: https://phabricator.services.mozilla.com/D30760
browser/components/aboutlogins/tests/mochitest/mochitest.ini
browser/components/aboutlogins/tests/mochitest/test_login_filter.html
browser/components/aboutlogins/tests/mochitest/test_login_list.html
--- a/browser/components/aboutlogins/tests/mochitest/mochitest.ini
+++ b/browser/components/aboutlogins/tests/mochitest/mochitest.ini
@@ -1,10 +1,12 @@
 [DEFAULT]
 support-files =
    ../../content/aboutLogins.html
+   ../../content/components/login-filter.js
    ../../content/components/login-item.js
    ../../content/components/login-list.js
    ../../content/components/login-list-item.js
    aboutlogins_common.js
 
+[test_login_filter.html]
 [test_login_item.html]
 [test_login_list.html]
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/tests/mochitest/test_login_filter.html
@@ -0,0 +1,128 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test the login-filter component
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test the login-filter component</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="/tests/SimpleTest/EventUtils.js"></script>
+  <script src="login-filter.js"></script>
+  <script src="login-list-item.js"></script>
+  <script src="login-list.js"></script>
+  <script src="aboutlogins_common.js"></script>
+
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <p id="display">
+  </p>
+<div id="content" style="display: none">
+  <iframe id="templateFrame" src="aboutLogins.html"
+          sandbox="allow-same-origin"></iframe>
+</div>
+<pre id="test">
+</pre>
+<script>
+/** Test the login-filter component **/
+
+let gLoginFilter;
+let gLoginList;
+add_task(async function setup() {
+  stubFluentL10n({
+    "count": "count",
+  });
+
+  let templateFrame = document.getElementById("templateFrame");
+  let displayEl = document.getElementById("display");
+  importDependencies(templateFrame, displayEl);
+
+  gLoginFilter = document.createElement("login-filter");
+  displayEl.appendChild(gLoginFilter);
+
+  gLoginList = document.createElement("login-list");
+  displayEl.appendChild(gLoginList);
+});
+
+add_task(async function test_empty_filter() {
+  ok(gLoginFilter, "loginFilter exists");
+  is(gLoginFilter.shadowRoot.querySelector("input").value, "", "Initially empty");
+});
+
+add_task(async function test_input_events() {
+  let filterEvent = null;
+  window.addEventListener("AboutLoginsFilterLogins", event => filterEvent = event);
+  let input = SpecialPowers.wrap(gLoginFilter.shadowRoot.querySelector("input"));
+  input.setUserInput("test");
+  ok(filterEvent, "Filter event received");
+  is(filterEvent.detail, "test", "Event includes input value");
+});
+
+add_task(async function test_list_filtered() {
+  const LOGINS = [{
+    guid: "123456789",
+    hostname: "https://example.com",
+    username: "user1",
+    password: "pass1",
+  }, {
+    guid: "987654321",
+    hostname: "https://example.com",
+    username: "user2",
+    password: "pass2",
+  }];
+  gLoginList.setLogins(LOGINS);
+
+  let tests = [
+    ["", 2],
+    [LOGINS[0].username, 1],
+    [LOGINS[0].username + "-notfound", 0],
+    [LOGINS[0].username.substr(2, 3), 1],
+    ["", 2],
+    // The password is not used for search.
+    [LOGINS[0].password, 0],
+    [LOGINS[0].password + "-notfound", 0],
+    [LOGINS[0].password.substr(2, 3), 0],
+    ["", 2],
+    [LOGINS[0].hostname, 2],
+    [LOGINS[0].hostname + "-notfound", 0],
+    [LOGINS[0].hostname.substr(2, 3), 2],
+    ["", 2],
+    // The guid is not used for search.
+    [LOGINS[0].guid, 0],
+    [LOGINS[0].guid + "-notfound", 0],
+    [LOGINS[0].guid.substr(0, 2), 0],
+    ["", 2],
+  ];
+
+  let loginFilterInput = gLoginFilter.shadowRoot.querySelector("input");
+  loginFilterInput.focus();
+
+  for (let i = 0; i < tests.length; i++) {
+    info("Testcase: " + i);
+
+    let testObj = {
+      testCase: i,
+      query: tests[i][0],
+      resultExpectedCount: tests[i][1],
+    };
+
+    let filterLength = loginFilterInput.value.length;
+    while (filterLength-- > 0) {
+      sendKey("BACK_SPACE");
+    }
+    sendString(testObj.query);
+
+    await SimpleTest.promiseWaitForCondition(() => {
+      return gLoginList.hasAttribute("count") &&
+             +gLoginList.getAttribute("count") == testObj.resultExpectedCount;
+    }, `Waiting for the search result count to update to ${testObj.resultExpectedCount} (tc#${testObj.testCase})`);
+    let count = +gLoginList.getAttribute("count");
+    is(count, testObj.resultExpectedCount,
+       `The login list count should match the expected result (tc#${testObj.testCase})`);
+  }
+});
+</script>
+
+</body>
+</html>
--- a/browser/components/aboutlogins/tests/mochitest/test_login_list.html
+++ b/browser/components/aboutlogins/tests/mochitest/test_login_list.html
@@ -35,16 +35,20 @@ const TEST_LOGIN_1 = {
 const TEST_LOGIN_2 = {
   guid: "987654321",
   hostname: "https://example.com",
   username: "user2",
   password: "pass2",
 };
 
 add_task(async function setup() {
+  stubFluentL10n({
+    "count": "count",
+  });
+
   let templateFrame = document.getElementById("templateFrame");
   let displayEl = document.getElementById("display");
   importDependencies(templateFrame, displayEl);
 
   gLoginList = document.createElement("login-list");
   displayEl.appendChild(gLoginList);
 });
 
@@ -66,16 +70,64 @@ add_task(async function test_populated_l
 
   ok(!loginListItems[0].classList.contains("selected"), "The first item should not be selected by default");
   ok(!loginListItems[1].classList.contains("selected"), "The second item should not be selected by default");
   loginListItems[0].click();
   ok(loginListItems[0].classList.contains("selected"), "The first item should be selected");
   ok(!loginListItems[1].classList.contains("selected"), "The second item should still not be selected");
 });
 
+add_task(async function test_filtered_list() {
+  is(gLoginList.shadowRoot.querySelectorAll("login-list-item:not([hidden])").length, 2, "Both logins should be visible");
+  let countSpan = gLoginList.shadowRoot.querySelector(".count");
+  is(countSpan.textContent, "2", "Count should match full list length");
+  window.dispatchEvent(new CustomEvent("AboutLoginsFilterLogins", {
+    bubbles: true,
+    composed: true,
+    detail: "user1",
+  }));
+  is(countSpan.textContent, "1", "Count should match result amount");
+  let loginListItems = gLoginList.shadowRoot.querySelectorAll("login-list-item");
+  is(loginListItems[0].shadowRoot.querySelector(".username").textContent, "user1", "user1 is expected first");
+  ok(!loginListItems[0].hidden, "user1 should remain visible");
+  ok(loginListItems[1].hidden, "user2 should be hidden");
+  window.dispatchEvent(new CustomEvent("AboutLoginsFilterLogins", {
+    bubbles: true,
+    composed: true,
+    detail: "user2",
+  }));
+  is(countSpan.textContent, "1", "Count should match result amount");
+  ok(loginListItems[0].hidden, "user1 should be hidden");
+  ok(!loginListItems[1].hidden, "user2 should be visible");
+  window.dispatchEvent(new CustomEvent("AboutLoginsFilterLogins", {
+    bubbles: true,
+    composed: true,
+    detail: "user",
+  }));
+  is(countSpan.textContent, "2", "Count should match result amount");
+  ok(!loginListItems[0].hidden, "user1 should be visible");
+  ok(!loginListItems[1].hidden, "user2 should be visible");
+  window.dispatchEvent(new CustomEvent("AboutLoginsFilterLogins", {
+    bubbles: true,
+    composed: true,
+    detail: "foo",
+  }));
+  is(countSpan.textContent, "0", "Count should match result amount");
+  ok(loginListItems[0].hidden, "user1 should be hidden");
+  ok(loginListItems[1].hidden, "user2 should be hidden");
+  window.dispatchEvent(new CustomEvent("AboutLoginsFilterLogins", {
+    bubbles: true,
+    composed: true,
+    detail: "",
+  }));
+  is(countSpan.textContent, "2", "Count should be reset to full list length");
+  ok(!loginListItems[0].hidden, "user1 should be visible");
+  ok(!loginListItems[1].hidden, "user2 should be visible");
+});
+
 add_task(async function test_login_modified() {
   let modifiedLogin = Object.assign(TEST_LOGIN_1, {username: "user11"});
   gLoginList.loginModified(modifiedLogin);
   await asyncElementRendered();
   let loginListItems = gLoginList.shadowRoot.querySelectorAll("login-list-item");
   is(loginListItems.length, 2, "Both logins should be displayed");
   is(loginListItems[0].getAttribute("guid"), TEST_LOGIN_1.guid, "login-list-item should have correct guid attribute");
   is(loginListItems[0].shadowRoot.querySelector(".hostname").textContent, TEST_LOGIN_1.hostname,