Bug 1457835 - Enable ESLint for testing/mochitest (manual changes). r=jmaher
authorMark Banner <standard8@mozilla.com>
Mon, 30 Apr 2018 11:03:44 +0100
changeset 472796 7da7e1e5be4976cd32b3ea79631858e9d8dbd749
parent 472795 53867132bf195f6be61840f0bda1147c73030089
child 472797 00e3fcb6763644069394e2c0f4941a6ef0d8430e
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmaher
bugs1457835
milestone61.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 1457835 - Enable ESLint for testing/mochitest (manual changes). r=jmaher MozReview-Commit-ID: BoGnB1aQxm8
.eslintignore
devtools/client/webconsole/test/fixtures/stubs/pageError.js
testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
testing/mochitest/BrowserTestUtils/content/content-about-page-utils.js
testing/mochitest/BrowserTestUtils/content/content-task.js
testing/mochitest/BrowserTestUtils/content/content-utils.js
testing/mochitest/ShutdownLeaksCollector.jsm
testing/mochitest/browser-test.js
testing/mochitest/chrome-harness.js
testing/mochitest/chrome/.eslintrc.js
testing/mochitest/chrome/test_sanityPluginUtils.html
testing/mochitest/manifestLibrary.js
testing/mochitest/nested_setup.js
testing/mochitest/server.js
testing/mochitest/start_desktop.js
testing/mochitest/tests/browser/browser_async.js
testing/mochitest/tests/browser/browser_browserLoaded_content_loaded.js
testing/mochitest/tests/browser/browser_fail_timeout.js
testing/mochitest/tests/browser/browser_fail_unexpectedTimeout.js
testing/mochitest/tests/browser/browser_getTestFile.js
testing/mochitest/tests/browser/browser_privileges.js
testing/mochitest/tests/browser/browser_requestLongerTimeout.js
testing/mochitest/tests/browser/browser_zz_fail_openwindow.js
tools/lint/eslint/eslint-plugin-mozilla/lib/configs/browser-test.js
--- a/.eslintignore
+++ b/.eslintignore
@@ -345,18 +345,21 @@ servo/**
 # Remote protocol exclusions
 testing/marionette/atom.js
 testing/marionette/legacyaction.js
 testing/marionette/client
 testing/marionette/doc
 testing/marionette/harness
 
 # other testing/ exclusions
-testing/mochitest/**
 # third party modules
+testing/mochitest/tests/Harness_sanity/**
+testing/mochitest/MochiKit/**
+testing/mochitest/tests/MochiKit-1.4.2/**
+testing/mochitest/tests/SimpleTest/**
 testing/modules/ajv-4.1.1.js
 testing/modules/sinon-2.3.2.js
 # octothorpe used for pref file comment causes parsing error
 testing/mozbase/mozprofile/tests/files/prefs_with_comments.js
 testing/talos/talos/scripts/jszip.min.js
 testing/talos/talos/startup_test/sessionrestore/profile/sessionstore.js
 testing/talos/talos/startup_test/sessionrestore/profile-manywindows/sessionstore.js
 testing/talos/talos/tests/canvasmark/**
--- a/devtools/client/webconsole/test/fixtures/stubs/pageError.js
+++ b/devtools/client/webconsole/test/fixtures/stubs/pageError.js
@@ -39,24 +39,24 @@ stubPreparedMessages.set(`ReferenceError
     },
     {
       "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
       "lineNumber": 9,
       "columnNumber": 3,
       "functionName": null
     },
     {
-      "filename": "resource://testing-common/content-task.js line 50 > eval",
+      "filename": "resource://testing-common/content-task.js line 55 > eval",
       "lineNumber": 7,
       "columnNumber": 9,
       "functionName": null
     },
     {
       "filename": "resource://testing-common/content-task.js",
-      "lineNumber": 51,
+      "lineNumber": 56,
       "columnNumber": 20,
       "functionName": null
     }
   ],
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "line": 3,
     "column": 5
@@ -78,24 +78,24 @@ stubPreparedMessages.set(`SyntaxError: r
   "type": "log",
   "helperType": null,
   "level": "error",
   "messageText": "SyntaxError: redeclaration of let a",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"SyntaxError: redeclaration of let a\",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false}",
   "stacktrace": [
     {
-      "filename": "resource://testing-common/content-task.js line 50 > eval",
+      "filename": "resource://testing-common/content-task.js line 55 > eval",
       "lineNumber": 7,
       "columnNumber": 9,
       "functionName": null
     },
     {
       "filename": "resource://testing-common/content-task.js",
-      "lineNumber": 51,
+      "lineNumber": 56,
       "columnNumber": 20,
       "functionName": null
     }
   ],
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 9
@@ -136,24 +136,24 @@ stubPreparedMessages.set(`TypeError long
   "stacktrace": [
     {
       "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
       "lineNumber": 1,
       "columnNumber": 7,
       "functionName": null
     },
     {
-      "filename": "resource://testing-common/content-task.js line 50 > eval",
+      "filename": "resource://testing-common/content-task.js line 55 > eval",
       "lineNumber": 7,
       "columnNumber": 9,
       "functionName": null
     },
     {
       "filename": "resource://testing-common/content-task.js",
-      "lineNumber": 51,
+      "lineNumber": 56,
       "columnNumber": 20,
       "functionName": null
     }
   ],
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 7
@@ -242,24 +242,24 @@ stubPackets.set(`ReferenceError: asdf is
       },
       {
         "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
         "lineNumber": 9,
         "columnNumber": 3,
         "functionName": null
       },
       {
-        "filename": "resource://testing-common/content-task.js line 50 > eval",
+        "filename": "resource://testing-common/content-task.js line 55 > eval",
         "lineNumber": 7,
         "columnNumber": 9,
         "functionName": null
       },
       {
         "filename": "resource://testing-common/content-task.js",
-        "lineNumber": 51,
+        "lineNumber": 56,
         "columnNumber": 20,
         "functionName": null
       }
     ],
     "notes": null
   }
 });
 
@@ -278,24 +278,24 @@ stubPackets.set(`SyntaxError: redeclarat
     "warning": false,
     "error": false,
     "exception": true,
     "strict": false,
     "info": false,
     "private": false,
     "stacktrace": [
       {
-        "filename": "resource://testing-common/content-task.js line 50 > eval",
+        "filename": "resource://testing-common/content-task.js line 55 > eval",
         "lineNumber": 7,
         "columnNumber": 9,
         "functionName": null
       },
       {
         "filename": "resource://testing-common/content-task.js",
-        "lineNumber": 51,
+        "lineNumber": 56,
         "columnNumber": 20,
         "functionName": null
       }
     ],
     "notes": [
       {
         "messageBody": "Previously declared at line 2, column 6",
         "frame": {
@@ -334,24 +334,24 @@ stubPackets.set(`TypeError longString me
     "stacktrace": [
       {
         "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
         "lineNumber": 1,
         "columnNumber": 7,
         "functionName": null
       },
       {
-        "filename": "resource://testing-common/content-task.js line 50 > eval",
+        "filename": "resource://testing-common/content-task.js line 55 > eval",
         "lineNumber": 7,
         "columnNumber": 9,
         "functionName": null
       },
       {
         "filename": "resource://testing-common/content-task.js",
-        "lineNumber": 51,
+        "lineNumber": 56,
         "columnNumber": 20,
         "functionName": null
       }
     ],
     "notes": null
   }
 });
 
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
@@ -4,16 +4,20 @@
 
 /*
  * This module implements a number of utilities useful for browser tests.
  *
  * All asynchronous helper methods should return promises, rather than being
  * callback based.
  */
 
+// This file uses ContentTask & frame scripts, where these are available.
+/* global addEventListener, removeEventListener, sendAsyncMessage,
+          addMessageListener, removeMessageListener, privateNoteIntentionalCrash */
+
 "use strict";
 
 var EXPORTED_SYMBOLS = [
   "BrowserTestUtils",
 ];
 
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -354,18 +358,16 @@ var BrowserTestUtils = {
    *        Whether NS_BINDING_ABORTED stops 'count' as 'real' stops
    *        (e.g. caused by the stop button or equivalent APIs)
    *
    * @return {Promise}
    * @resolves When STATE_STOP reaches the tab's progress listener
    */
   browserStopped(browser, expectedURI, checkAborts = false) {
     return new Promise(resolve => {
-      const kDocStopFlags = Ci.nsIWebProgressListener.STATE_IS_NETWORK |
-                            Ci.nsIWebProgressListener.STATE_STOP;
       let wpl = {
         onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
           dump("Saw state " + aStateFlags.toString(16) + " and status " + aStatus.toString(16) + "\n");
           if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
               aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
               (checkAborts || aStatus != Cr.NS_BINDING_ABORTED) &&
               aWebProgress.isTopLevel) {
             let chan = aRequest.QueryInterface(Ci.nsIChannel);
@@ -903,16 +905,17 @@ var BrowserTestUtils = {
    */
   waitForContentEvent(browser, eventName, capture = false, checkFn, wantsUntrusted = false) {
     let parameters = {
       eventName,
       capture,
       checkFnSource: checkFn ? checkFn.toSource() : null,
       wantsUntrusted,
     };
+    /* eslint-disable no-eval */
     return ContentTask.spawn(browser, parameters,
         function({ eventName, capture, checkFnSource, wantsUntrusted }) {
           let checkFn;
           if (checkFnSource) {
             checkFn = eval(`(() => (${checkFnSource}))()`);
           }
           return new Promise((resolve, reject) => {
             addEventListener(eventName, function listener(event) {
@@ -924,16 +927,17 @@ var BrowserTestUtils = {
               } catch (e) {
                 completion = () => reject(e);
               }
               removeEventListener(eventName, listener, capture);
               completion();
             }, capture, wantsUntrusted);
           });
         });
+    /* eslint-enable no-eval */
   },
 
   /**
    * Adds a content event listener on the given browser
    * element. Similar to waitForContentEvent, but the listener will
    * fire until it is removed. A callable object is returned that,
    * when called, removes the event listener. Note that this function
    * works even if the browser's frameloader is swapped.
@@ -972,16 +976,17 @@ var BrowserTestUtils = {
                           autoremove = true) {
     let id = gListenerId++;
     let checkFnSource = checkFn ? encodeURIComponent(escape(checkFn.toSource())) : "";
 
     // To correctly handle frameloader swaps, we load a frame script
     // into all tabs but ignore messages from the ones not related to
     // |browser|.
 
+    /* eslint-disable no-eval */
     function frameScript(id, eventName, useCapture, checkFnSource, wantsUntrusted) {
       let checkFn;
       if (checkFnSource) {
         checkFn = eval(`(() => (${unescape(checkFnSource)}))()`);
       }
 
       function listener(event) {
         if (checkFn && !checkFn(event)) {
@@ -993,16 +998,17 @@ var BrowserTestUtils = {
         if (msg.data == id) {
           removeMessageListener("ContentEventListener:Remove", removeListener);
           removeEventListener(eventName, listener, useCapture, wantsUntrusted);
         }
       }
       addMessageListener("ContentEventListener:Remove", removeListener);
       addEventListener(eventName, listener, useCapture, wantsUntrusted);
     }
+    /* eslint-enable no-eval */
 
     let frameScriptSource =
         `data:,(${frameScript.toString()})(${id}, "${eventName}", ${useCapture}, "${checkFnSource}", ${wantsUntrusted})`;
 
     let mm = Services.mm;
 
     function runListener(msg) {
       if (msg.data == id && msg.target == browser) {
@@ -1269,34 +1275,36 @@ var BrowserTestUtils = {
       dies();
     };
 
     let expectedPromises = [];
 
     let crashCleanupPromise = new Promise((resolve, reject) => {
       let observer = (subject, topic, data) => {
         if (topic != "ipc:content-shutdown") {
-          return reject("Received incorrect observer topic: " + topic);
+          reject("Received incorrect observer topic: " + topic);
+          return;
         }
         if (!(subject instanceof Ci.nsIPropertyBag2)) {
-          return reject("Subject did not implement nsIPropertyBag2");
+          reject("Subject did not implement nsIPropertyBag2");
+          return;
         }
         // we might see this called as the process terminates due to previous tests.
         // We are only looking for "abnormal" exits...
         if (!subject.hasKey("abnormal")) {
           dump("\nThis is a normal termination and isn't the one we are looking for...\n");
           return;
         }
 
         let dumpID;
         if (AppConstants.MOZ_CRASHREPORTER) {
           dumpID = subject.getPropertyAsAString("dumpID");
           if (!dumpID) {
-            return reject("dumpID was not present despite crash reporting " +
-                          "being enabled");
+            reject("dumpID was not present despite crash reporting being enabled");
+            return;
           }
         }
 
         let removalPromise = Promise.resolve();
 
         if (dumpID) {
           removalPromise = Services.crashmanager.ensureCrashIsPresent(dumpID)
                                                 .then(() => {
--- a/testing/mochitest/BrowserTestUtils/content/content-about-page-utils.js
+++ b/testing/mochitest/BrowserTestUtils/content/content-about-page-utils.js
@@ -1,8 +1,10 @@
+/* eslint-env mozilla/frame-script */
+
 "use strict";
 
 var Cm = Components.manager;
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
--- a/testing/mochitest/BrowserTestUtils/content/content-task.js
+++ b/testing/mochitest/BrowserTestUtils/content/content-task.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* eslint-env mozilla/frame-script */
+
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Task.jsm", this);
 ChromeUtils.import("resource://testing-common/ContentTaskUtils.jsm", this);
 const AssertCls = ChromeUtils.import("resource://testing-common/Assert.jsm", null).Assert;
 
 addMessageListener("content-task:spawn", function(msg) {
   let id = msg.data.id;
@@ -24,34 +26,37 @@ addMessageListener("content-task:spawn",
     sendAsyncMessage("content-task:test-result", {
       id,
       condition: !err,
       name: err ? err.message : message,
       stack: getStack(err ? err.stack : stack)
     });
   });
 
+  /* eslint-disable no-unused-vars */
   var ok = Assert.ok.bind(Assert);
   var is = Assert.equal.bind(Assert);
   var isnot = Assert.notEqual.bind(Assert);
 
   function todo(expr, name) {
     sendAsyncMessage("content-task:test-todo", {id, expr, name});
   }
 
   function info(name) {
     sendAsyncMessage("content-task:test-info", {id, name});
   }
+  /* eslint-enable no-unused-vars */
 
   try {
     let runnablestr = `
       (() => {
         return (${source});
       })();`;
 
+    // eslint-disable-next-line no-eval
     let runnable = eval(runnablestr);
     let iterator = runnable.call(this, msg.data.arg);
     Task.spawn(iterator).then((val) => {
       sendAsyncMessage("content-task:complete", {
         id,
         result: val,
       });
     }, (e) => {
--- a/testing/mochitest/BrowserTestUtils/content/content-utils.js
+++ b/testing/mochitest/BrowserTestUtils/content/content-utils.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* eslint-env mozilla/frame-script */
+
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 addEventListener("DOMContentLoaded", function(event) {
   let subframe = event.target != content.document;
   // For error page, internalURL is 'about:neterror?...', and visibleURL
   // is the original URL.
@@ -16,9 +18,8 @@ addEventListener("DOMContentLoaded", fun
 }, true);
 
 addEventListener("load", function(event) {
   let subframe = event.target != content.document;
   sendAsyncMessage("browser-test-utils:loadEvent",
     {subframe, internalURL: event.target.documentURI,
      visibleURL: content.document.location.href});
 }, true);
-
--- a/testing/mochitest/ShutdownLeaksCollector.jsm
+++ b/testing/mochitest/ShutdownLeaksCollector.jsm
@@ -9,17 +9,17 @@ var EXPORTED_SYMBOLS = ["ContentCollecto
 
 // This listens for the message "browser-test:collect-request". When it gets it,
 // it runs some GCs and CCs, then prints out a message indicating the collections
 // are complete. Mochitest uses this information to determine when windows and
 // docshells should be destroyed.
 
 var ContentCollector = {
   init() {
-      let processType = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).processType;
+      let processType = Services.appinfo.processType;
       if (processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
         // In the main process, we handle triggering collections in browser-test.js
         return;
       }
 
     Services.cpmm.addMessageListener("browser-test:collect-request", this);
   },
 
@@ -49,16 +49,16 @@ var ContentCollector = {
           }, 1000);
         });
 
         break;
     }
   },
 
   finish() {
-    let pid = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).processID;
+    let pid = Services.appinfo.processID;
     dump("Completed ShutdownLeaks collections in process " + pid + "\n");
 
     Services.cpmm.removeMessageListener("browser-test:collect-request", this);
   },
 
 };
 ContentCollector.init();
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -1,9 +1,14 @@
 /* -*- js-indent-level: 2; tab-width: 2; indent-tabs-mode: nil -*- */
+
+/* eslint-env mozilla/browser-window */
+/* import-globals-from chrome-harness.js */
+/* import-globals-from mochitest-e10s-utils.js */
+
 // Test timeout (seconds)
 var gTimeoutSeconds = 45;
 var gConfig;
 var gSaveInstrumentationData = null;
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
@@ -78,28 +83,32 @@ function testInit() {
                   createInstance(Ci.nsISupportsString);
     sstring.data = location.search;
 
     Services.ww.openWindow(window, "chrome://mochikit/content/browser-harness.xul", "browserTest",
                            "chrome,centerscreen,dialog=no,resizable,titlebar,toolbar=no,width=800,height=600", sstring);
   } else {
     // This code allows us to redirect without requiring specialpowers for chrome and a11y tests.
     let messageHandler = function(m) {
+      // eslint-disable-next-line no-undef
       messageManager.removeMessageListener("chromeEvent", messageHandler);
       var url = m.json.data;
 
       // Window is the [ChromeWindow] for messageManager, so we need content.window
       // Currently chrome tests are run in a content window instead of a ChromeWindow
+      // eslint-disable-next-line no-undef
       var webNav = content.window.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIWebNavigation);
       webNav.loadURI(url, null, null, null, null);
     };
 
     var listener = 'data:,function doLoad(e) { var data=e.detail&&e.detail.data;removeEventListener("contentEvent", function (e) { doLoad(e); }, false, true);sendAsyncMessage("chromeEvent", {"data":data}); };addEventListener("contentEvent", function (e) { doLoad(e); }, false, true);';
+    // eslint-disable-next-line no-undef
     messageManager.addMessageListener("chromeEvent", messageHandler);
+    // eslint-disable-next-line no-undef
     messageManager.loadFrameScript(listener, true);
   }
   if (gConfig.e10s) {
     e10s_init();
 
     let processCount = prefs.getIntPref("dom.ipc.processCount", 1);
     if (processCount > 1) {
       // Currently starting a content process is slow, to aviod timeouts, let's
@@ -519,17 +528,17 @@ Tester.prototype = {
       }
     }
     // graphics test window is already gone, just call callback immediately
     aCallback();
   },
 
   waitForWindowsState: function Tester_waitForWindowsState(aCallback) {
     let timedOut = this.currentTest && this.currentTest.timedOut;
-    let startTime = Date.now();
+    // eslint-disable-next-line no-nested-ternary
     let baseMsg = timedOut ? "Found a {elt} after previous test timed out"
                            : this.currentTest ? "Found an unexpected {elt} at the end of test run"
                                               : "Found an unexpected {elt}";
 
     // Remove stale tabs
     if (this.currentTest && window.gBrowser && gBrowser.tabs.length > 1) {
       while (gBrowser.tabs.length > 1) {
         let lastTab = gBrowser.tabContainer.lastChild;
@@ -543,17 +552,17 @@ Tester.prototype = {
           }));
         }
         gBrowser.removeTab(lastTab);
       }
     }
 
     // Replace the last tab with a fresh one
     if (window.gBrowser) {
-      let newTab = gBrowser.addTab("about:blank", { skipAnimation: true });
+      gBrowser.addTab("about:blank", { skipAnimation: true });
       gBrowser.removeTab(gBrowser.selectedTab, { skipPermitUnload: true });
       gBrowser.stop();
     }
 
     // Remove stale windows
     this.structuredLogger.info("checking window state");
     let windowsEnum = Services.wm.getEnumerator(null);
     while (windowsEnum.hasMoreElements()) {
@@ -599,17 +608,17 @@ Tester.prototype = {
 
     TabDestroyObserver.destroy();
     Services.console.unregisterListener(this);
 
     // It's important to terminate the module to avoid crashes on shutdown.
     this.PromiseTestUtils.uninit();
 
     // In the main process, we print the ShutdownLeaksCollector message here.
-    let pid = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).processID;
+    let pid = Services.appinfo.processID;
     dump("Completed ShutdownLeaks collections in process " + pid + "\n");
 
     this.structuredLogger.info("TEST-START | Shutdown");
 
     if (this.tests.length) {
       let e10sMode = gMultiProcessBrowser ? "e10s" : "non-e10s";
       this.structuredLogger.info("Browser Chrome Test Summary");
       this.structuredLogger.info("Passed:  " + passCount);
@@ -735,16 +744,17 @@ Tester.prototype = {
         }
       }, this);
 
       // Clear document.popupNode.  The test could have set it to a custom value
       // for its own purposes, nulling it out it will go back to the default
       // behavior of returning the last opened popup.
       document.popupNode = null;
 
+      // eslint-disable-next-line no-undef
       await new Promise(resolve => SpecialPowers.flushPrefEnv(resolve));
 
       if (gConfig.cleanupCrashes) {
         let gdir = Services.dirsvc.get("UAppData", Ci.nsIFile);
         gdir.append("Crash Reports");
         gdir.append("pending");
         if (gdir.exists()) {
           let entries = gdir.directoryEntries;
@@ -831,19 +841,17 @@ Tester.prototype = {
             name: "We expect at least one assertion to fail because this" +
                   " test file is marked as fail-if in the manifest.",
             todo: true,
           }));
         }
       }
 
       // Dump memory stats for main thread.
-      if (Cc["@mozilla.org/xre/runtime;1"]
-          .getService(Ci.nsIXULRuntime)
-          .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
+      if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
         this.MemoryStats.dump(this.currentTestIndex,
                               this.currentTest.path,
                               gConfig.dumpOutputDirectory,
                               gConfig.dumpAboutMemoryAfterTest,
                               gConfig.dumpDMDAfterTest);
       }
 
       // Note the test run time
@@ -1045,17 +1053,16 @@ Tester.prototype = {
       this._scriptLoader.loadSubScript(this.currentTest.path, scope);
       // Run the test
       this.lastStartTime = Date.now();
       if (this.currentTest.scope.__tasks) {
         // This test consists of tasks, added via the `add_task()` API.
         if ("test" in this.currentTest.scope) {
           throw "Cannot run both a add_task test and a normal test at the same time.";
         }
-        let Promise = this.Promise;
         let PromiseTestUtils = this.PromiseTestUtils;
 
         // Allow for a task to be skipped; we need only use the structured logger
         // for this, whilst deactivating log buffering to ensure that messages
         // are always printed to stdout.
         let skipTask = (task) => {
           let logger = this.structuredLogger;
           logger.deactivateBuffering();
@@ -1391,16 +1398,17 @@ function testScope(aTester, aTest, expec
   // into this sandbox.
   //
   // Otherwise, load test files directly into the testScope instance.
   if (aTest.usesUnsafeCPOWs) {
     let sandbox = this._createSandbox();
     Cu.permitCPOWsInScope(sandbox);
     return sandbox;
   }
+  return this;
 }
 
 function decorateTaskFn(fn) {
   fn = fn.bind(this);
   fn.skip = () => fn.__skipMe = true;
   fn.only = () => this.__runOnlyThisTask = fn;
   return fn;
 }
--- a/testing/mochitest/chrome-harness.js
+++ b/testing/mochitest/chrome-harness.js
@@ -1,28 +1,31 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
 
-ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+/* import-globals-from manifestLibrary.js */
+
+// Defined in browser-test.js
+/* global gTestPath */
 
 /*
  * getChromeURI converts a URL to a URI
  *
  * url: string of a URL (http://mochi.test/test.html)
  * returns: a nsiURI object representing the given URL
  *
  */
 function getChromeURI(url) {
-  var ios = Cc["@mozilla.org/network/io-service;1"].
-              getService(Ci.nsIIOService);
-  return ios.newURI(url);
+  return Services.io.newURI(url);
 }
 
 /*
  * Convert a URL (string) into a nsIURI or NSIJARURI
  * This is intended for URL's that are on a file system
  * or in packaged up in an extension .jar file
  *
  * url: a string of a url on the local system(http://localhost/blah.html)
@@ -100,19 +103,17 @@ function getJar(uri) {
  * input:
  *  jar: a nsIJARURI object with the jarfile and jarentry (path in jar file)
  *
  * output;
  *  all files and subdirectories inside jarentry will be extracted to TmpD/mochikit.tmp
  *  we will return the location of /TmpD/mochikit.tmp* so you can reference the files locally
  */
 function extractJarToTmp(jar) {
-  var tmpdir = Cc["@mozilla.org/file/directory_service;1"]
-                      .getService(Ci.nsIProperties)
-                      .get("ProfD", Ci.nsIFile);
+  var tmpdir = Services.dirsvc.get("ProfD", Ci.nsIFile);
   tmpdir.append("mochikit.tmp");
   // parseInt is used because octal escape sequences cause deprecation warnings
   // in strict mode (which is turned on in debug builds)
   tmpdir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0777", 8));
 
   var zReader = Cc["@mozilla.org/libjar/zip-reader;1"].
                   createInstance(Ci.nsIZipReader);
 
@@ -212,19 +213,17 @@ function buildRelativePath(jarentryname,
   }
 
   return targetFile;
 }
 
 function readConfig(filename) {
   filename = filename || "testConfig.js";
 
-  var fileLocator = Cc["@mozilla.org/file/directory_service;1"].
-                    getService(Ci.nsIProperties);
-  var configFile = fileLocator.get("ProfD", Ci.nsIFile);
+  var configFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
   configFile.append(filename);
 
   if (!configFile.exists())
     return {};
 
   var fileInStream = Cc["@mozilla.org/network/file-input-stream;1"].
                      createInstance(Ci.nsIFileInputStream);
   fileInStream.init(configFile, -1, 0, 0);
@@ -232,17 +231,17 @@ function readConfig(filename) {
   var str = NetUtil.readInputStreamToString(fileInStream, fileInStream.available());
   fileInStream.close();
   return JSON.parse(str);
 }
 
 function getTestList(params, callback) {
   var baseurl = "chrome://mochitests/content";
   if (window.parseQueryString) {
-    params = parseQueryString(location.search.substring(1), true);
+    params = window.parseQueryString(location.search.substring(1), true);
   }
   if (!params.baseurl) {
     params.baseurl = baseurl;
   }
 
   var config = readConfig();
   for (var p in params) {
     if (params[p] == 1) {
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/chrome/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+  "extends": [
+    "plugin:mozilla/chrome-test"
+  ]
+};
--- a/testing/mochitest/chrome/test_sanityPluginUtils.html
+++ b/testing/mochitest/chrome/test_sanityPluginUtils.html
@@ -15,17 +15,17 @@
 <!-- load the test plugin defined at $(DIST)/bin/plugins/Test.plugin/ -->
 <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
 <script class="testbody" type="text/javascript">
 info("\nProfile::PluginUtilsLoadTime: " + (loadTime - start) + "\n");
 function starttest() {
   SimpleTest.waitForExplicitFinish();
   var startTime = new Date();
   // increase the runtime of the test so it is detectible, otherwise we get 0-1ms
-  runtimes = 100;
+  let runtimes = 100;
   function runTest(plugin) {
     is(plugin.version, "1.0.0.0", "Make sure version is correct");
     is(plugin.name, "Test Plug-in");
   }
   while (runtimes > 0) {
     ok(PluginUtils.withTestPlugin(runTest), "Test plugin should be found");
     --runtimes;
   }
--- a/testing/mochitest/manifestLibrary.js
+++ b/testing/mochitest/manifestLibrary.js
@@ -1,51 +1,51 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 function parseTestManifest(testManifest, params, callback) {
-  var links = {};
-  var paths = [];
+  let links = {};
+  let paths = [];
 
   // Support --test-manifest format for mobile
   if ("runtests" in testManifest || "excludetests" in testManifest) {
     callback(testManifest);
     return;
   }
 
   // For mochitest-chrome and mochitest-browser-chrome harnesses, we
   // define tests as links[testname] = true.
   // For mochitest-plain, we define lists as an array of testnames.
-  for (var obj of testManifest.tests) {
-    var path = obj.path;
+  for (let obj of testManifest.tests) {
+    let path = obj.path;
     // Note that obj.disabled may be "". We still want to skip in that case.
     if ("disabled" in obj) {
       dump("TEST-SKIPPED | " + path + " | " + obj.disabled + "\n");
       continue;
     }
     if (params.testRoot != "tests" && params.testRoot !== undefined) {
-      name = params.baseurl + "/" + params.testRoot + "/" + path;
+      let name = params.baseurl + "/" + params.testRoot + "/" + path;
       links[name] = {"test": {"url": name, "expected": obj.expected, "uses-unsafe-cpows": obj["uses-unsafe-cpows"]}};
     } else {
-      name = params.testPrefix + path;
+      let name = params.testPrefix + path;
       paths.push({"test": {"url": name, "expected": obj.expected, "uses-unsafe-cpows": obj["uses-unsafe-cpows"]}});
     }
   }
   if (paths.length > 0) {
     callback(paths);
   } else {
     callback(links);
   }
 }
 
 function getTestManifest(url, params, callback) {
-  var req = new XMLHttpRequest();
+  let req = new XMLHttpRequest();
   req.open("GET", url);
   req.onload = function() {
     if (req.readyState == 4) {
       if (req.status == 200) {
         try {
           parseTestManifest(JSON.parse(req.responseText), params, callback);
         } catch (e) {
           dump("TEST-UNEXPECTED-FAIL: manifestLibrary.js | error parsing " + url + " (" + e + ")\n");
@@ -69,21 +69,19 @@ function getTestManifest(url, params, ca
  parameters:
    filter = json object of runtests | excludetests
    testList = array of test names to run
    runOnly = use runtests vs excludetests in case both are defined
  returns:
    filtered version of testList
 */
 function filterTests(filter, testList, runOnly) {
-
-  var filteredTests = [];
-  var removedTests = [];
-  var runtests = {};
-  var excludetests = {};
+  let filteredTests = [];
+  let runtests = {};
+  let excludetests = {};
 
   if (filter == null) {
     return testList;
   }
 
   if ("runtests" in filter) {
     runtests = filter.runtests;
   }
@@ -93,30 +91,32 @@ function filterTests(filter, testList, r
   if (!("runtests" in filter) && !("excludetests" in filter)) {
     if (runOnly == "true") {
       runtests = filter;
     } else {
       excludetests = filter;
     }
   }
 
-  var testRoot = config.testRoot || "tests";
+  // eslint-disable-next-line no-undef
+  let testRoot = config.testRoot || "tests";
   // Start with testList, and put everything that's in 'runtests' in
   // filteredTests.
   if (Object.keys(runtests).length) {
-    for (var i = 0; i < testList.length; i++) {
+    for (let i = 0; i < testList.length; i++) {
+      let testpath;
       if ((testList[i] instanceof Object) && ("test" in testList[i])) {
-        var testpath = testList[i].test.url;
+        testpath = testList[i].test.url;
       } else {
-        var testpath = testList[i];
+        testpath = testList[i];
       }
-      var tmppath = testpath.replace(/^\//, "");
-      for (var f in runtests) {
+      let tmppath = testpath.replace(/^\//, "");
+      for (let f in runtests) {
         // Remove leading /tests/ if exists
-        file = f.replace(/^\//, "");
+        let file = f.replace(/^\//, "");
         file = file.replace(/^tests\//, "");
 
         // Match directory or filename, testList has <testroot>/<path>
         if (tmppath.match(testRoot + "/" + file) != null) {
           filteredTests.push(testpath);
           break;
         }
       }
@@ -126,28 +126,29 @@ function filterTests(filter, testList, r
   }
 
   // Continue with filteredTests, and deselect everything that's in
   // excludedtests.
   if (!Object.keys(excludetests).length) {
     return filteredTests;
   }
 
-  var refilteredTests = [];
-  for (var i = 0; i < filteredTests.length; i++) {
-    var found = false;
+  let refilteredTests = [];
+  for (let i = 0; i < filteredTests.length; i++) {
+    let found = false;
+    let testpath;
     if ((filteredTests[i] instanceof Object) && ("test" in filteredTests[i])) {
-      var testpath = filteredTests[i].test.url;
+      testpath = filteredTests[i].test.url;
     } else {
-      var testpath = filteredTests[i];
+      testpath = filteredTests[i];
     }
-    var tmppath = testpath.replace(/^\//, "");
-    for (var f in excludetests) {
+    let tmppath = testpath.replace(/^\//, "");
+    for (let f in excludetests) {
       // Remove leading /tests/ if exists
-      file = f.replace(/^\//, "");
+      let file = f.replace(/^\//, "");
       file = file.replace(/^tests\//, "");
 
       // Match directory or filename, testList has <testroot>/<path>
       if (tmppath.match(testRoot + "/" + file) != null) {
         found = true;
         break;
       }
     }
--- a/testing/mochitest/nested_setup.js
+++ b/testing/mochitest/nested_setup.js
@@ -1,8 +1,10 @@
+
+/* global SpecialPowers */
 
 var gTestURL = "";
 
 function addPermissions() {
   SpecialPowers.pushPermissions(
     [{ type: "browser", allow: true, context: document }],
     addPreferences);
 }
--- a/testing/mochitest/server.js
+++ b/testing/mochitest/server.js
@@ -1,30 +1,47 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-// Note that the server script itself already defines Cc, Ci, and Cr for us,
-// and because they're constants it's not safe to redefine them.  Scope leakage
-// sucks.
+// We expect these to be defined in the global scope by runtest.py.
+/* global __LOCATION__, _PROFILE_PATH, _SERVER_PORT, _SERVER_ADDR, _DISPLAY_RESULTS,
+          _TEST_PREFIX */
+// Defined by xpcshell
+/* global quit */
+
+/* import-globals-from ../../netwerk/test/httpserver/httpd.js */
 
 // Disable automatic network detection, so tests work correctly when
 // not connected to a network.
+// eslint-disable-next-line mozilla/use-services
 var ios = Cc["@mozilla.org/network/io-service;1"]
             .getService(Ci.nsIIOService);
 ios.manageOfflineStatus = false;
 ios.offline = false;
 
 var server; // for use in the shutdown handler, if necessary
 
 //
 // HTML GENERATION
 //
+/* global A, ABBR, ACRONYM, ADDRESS, APPLET, AREA, B, BASE,
+          BASEFONT, BDO, BIG, BLOCKQUOTE, BODY, BR, BUTTON,
+          CAPTION, CENTER, CITE, CODE, COL, COLGROUP, DD,
+          DEL, DFN, DIR, DIV, DL, DT, EM, FIELDSET, FONT,
+          FORM, FRAME, FRAMESET, H1, H2, H3, H4, H5, H6,
+          HEAD, HR, HTML, I, IFRAME, IMG, INPUT, INS,
+          ISINDEX, KBD, LABEL, LEGEND, LI, LINK, MAP, MENU,
+          META, NOFRAMES, NOSCRIPT, OBJECT, OL, OPTGROUP,
+          OPTION, P, PARAM, PRE, Q, S, SAMP, SCRIPT,
+          SELECT, SMALL, SPAN, STRIKE, STRONG, STYLE, SUB,
+          SUP, TABLE, TBODY, TD, TEXTAREA, TFOOT, TH, THEAD,
+          TITLE, TR, TT, U, UL, VAR */
 var tags = ["A", "ABBR", "ACRONYM", "ADDRESS", "APPLET", "AREA", "B", "BASE",
             "BASEFONT", "BDO", "BIG", "BLOCKQUOTE", "BODY", "BR", "BUTTON",
             "CAPTION", "CENTER", "CITE", "CODE", "COL", "COLGROUP", "DD",
             "DEL", "DFN", "DIR", "DIV", "DL", "DT", "EM", "FIELDSET", "FONT",
             "FORM", "FRAME", "FRAMESET", "H1", "H2", "H3", "H4", "H5", "H6",
             "HEAD", "HR", "HTML", "I", "IFRAME", "IMG", "INPUT", "INS",
             "ISINDEX", "KBD", "LABEL", "LEGEND", "LI", "LINK", "MAP", "MENU",
             "META", "NOFRAMES", "NOSCRIPT", "OBJECT", "OL", "OPTGROUP",
@@ -542,19 +559,19 @@ function jsonArrayOfTestFiles(links) {
 
   return "[" + testFiles.join(",\n") + "]";
 }
 
 /**
  * Produce a normal directory listing.
  */
 function regularListing(metadata, response) {
-  var [links, count] = list(metadata.path,
-                            metadata.getProperty("directory"),
-                            false);
+  var [links] = list(metadata.path,
+                     metadata.getProperty("directory"),
+                     false);
   response.write(
     HTML(
       HEAD(
         TITLE("mochitest index ", metadata.path)
       ),
       BODY(
         BR(),
         A({href: ".."}, "Up a level"),
--- a/testing/mochitest/start_desktop.js
+++ b/testing/mochitest/start_desktop.js
@@ -1,15 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+// Defined by Marionette.
+/* global __webDriverArguments */
 const flavor  = __webDriverArguments[0].flavor;
 const url = __webDriverArguments[0].testUrl;
 
+// eslint-disable-next-line mozilla/use-services
 let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
           .getService(Ci.nsIWindowMediator);
 let win = wm.getMostRecentWindow("navigator:browser");
 
 // mochikit's bootstrap.js has set up a listener for this event. It's
 // used so bootstrap.js knows which flavor and url to load.
 let ev = new CustomEvent("mochitest-load", {"detail": [flavor, url]});
 win.dispatchEvent(ev);
--- a/testing/mochitest/tests/browser/browser_async.js
+++ b/testing/mochitest/tests/browser/browser_async.js
@@ -1,8 +1,9 @@
 function test() {
   waitForExplicitFinish();
   function done() {
     ok(true, "timeout ran");
     finish();
   }
+  // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
   setTimeout(done, 500);
 }
--- a/testing/mochitest/tests/browser/browser_browserLoaded_content_loaded.js
+++ b/testing/mochitest/tests/browser/browser_browserLoaded_content_loaded.js
@@ -31,17 +31,16 @@ add_task(async function() {
   let browsers = tabURLs.map(u => BrowserTestUtils.addTab(gBrowser, u).linkedBrowser);
 
   // wait for promises to settle
   await Promise.all((function* () {
     for (let b of browsers) {
       yield BrowserTestUtils.browserLoaded(b);
     }
   })());
-  let expected = "Expected all promised browsers to have loaded.";
   for (const browser of browsers) {
     await isDOMLoaded(browser);
   }
   // cleanup
   browsers
     .map(browser => gBrowser.getTabForBrowser(browser))
     .forEach(tab => gBrowser.removeTab(tab));
 });
--- a/testing/mochitest/tests/browser/browser_fail_timeout.js
+++ b/testing/mochitest/tests/browser/browser_fail_timeout.js
@@ -1,8 +1,9 @@
 function test() {
   function end() {
     ok(false, "should have timed out");
     finish();
   }
   waitForExplicitFinish();
+  // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
   setTimeout(end, 40000);
 }
--- a/testing/mochitest/tests/browser/browser_fail_unexpectedTimeout.js
+++ b/testing/mochitest/tests/browser/browser_fail_unexpectedTimeout.js
@@ -2,11 +2,13 @@ function test() {
   function message() {
     info("This should delay timeout");
   }
   function end() {
     ok(true, "Should have not timed out, but notified long running test");
     finish();
   }
   waitForExplicitFinish();
+  // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
   setTimeout(message, 20000);
+  // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
   setTimeout(end, 40000);
 }
--- a/testing/mochitest/tests/browser/browser_getTestFile.js
+++ b/testing/mochitest/tests/browser/browser_getTestFile.js
@@ -1,10 +1,10 @@
 function test() {
-  let {Promise} = ChromeUtils.import("resource://gre/modules/Promise.jsm");
+  let {Promise} = ChromeUtils.import("resource://gre/modules/Promise.jsm", {});
   ChromeUtils.import("resource://gre/modules/osfile.jsm");
   let decoder = new TextDecoder();
 
   waitForExplicitFinish();
 
   SimpleTest.doesThrow(function() {
     getTestFilePath("/browser_getTestFile.js");
   }, "getTestFilePath rejects absolute paths");
--- a/testing/mochitest/tests/browser/browser_privileges.js
+++ b/testing/mochitest/tests/browser/browser_privileges.js
@@ -1,14 +1,15 @@
 function test() {
   // simple test to confirm we have chrome privileges
   let hasPrivileges = true;
 
   // this will throw an exception if we are not running with privileges
   try {
+    // eslint-disable-next-line no-unused-vars, mozilla/use-services
     var prefs = Cc["@mozilla.org/preferences-service;1"].
                 getService(Ci.nsIPrefBranch);
   } catch (e) {
     hasPrivileges = false;
   }
 
   // if we get here, we must have chrome privileges
   ok(hasPrivileges, "running with chrome privileges");
--- a/testing/mochitest/tests/browser/browser_requestLongerTimeout.js
+++ b/testing/mochitest/tests/browser/browser_requestLongerTimeout.js
@@ -1,9 +1,10 @@
 function test() {
   requestLongerTimeout(2);
   function end() {
     ok(true, "should not time out");
     finish();
   }
   waitForExplicitFinish();
+  // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
   setTimeout(end, 40000);
 }
--- a/testing/mochitest/tests/browser/browser_zz_fail_openwindow.js
+++ b/testing/mochitest/tests/browser/browser_zz_fail_openwindow.js
@@ -3,10 +3,11 @@ function test() {
   function done() {
     ok(true, "timeout ran");
     finish();
   }
 
   ok(OpenBrowserWindow(), "opened browser window");
   // and didn't close it!
 
+  // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
   setTimeout(done, 10000);
 }
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/browser-test.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/browser-test.js
@@ -40,16 +40,17 @@ module.exports = {
     "ignoreAllUncaughtExceptions": false,
     "info": false,
     "is": false,
     "isnot": false,
     "ok": false,
     "privateNoteIntentionalCrash": false,
     "registerCleanupFunction": false,
     "requestLongerTimeout": false,
+    "setExpectedFailuresForSelfTest": false,
     "todo": false,
     "todo_is": false,
     "todo_isnot": false,
     "waitForClipboard": false,
     "waitForExplicitFinish": false,
     "waitForFocus": false
   },