Bug 1183884 - Change manual testing infrastructure. r=mikedeboer
authorAndrei Oprea <andrei.br92@gmail.com>
Mon, 27 Jul 2015 10:11:00 -0400
changeset 286669 91164a877f8a9a284547677a5cddfe9ef1d0ee37
parent 286668 321d5f9703299d535fcee0843a1402c85b61c48d
child 286670 e29a16b9eee01b80ff541d1e87df59bb6557d25f
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmikedeboer
bugs1183884
milestone42.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 1183884 - Change manual testing infrastructure. r=mikedeboer
browser/components/loop/.eslintignore
browser/components/loop/.gitignore
browser/components/loop/README.txt
browser/components/loop/run-all-loop-tests.sh
browser/components/loop/test/desktop-local/conversation_test.js
browser/components/loop/test/karma/karma.conf.base.js
browser/components/loop/test/karma/karma.coverage.desktop.js
browser/components/loop/test/karma/karma.coverage.shared_standalone.js
browser/components/loop/test/karma/stubs.js
browser/components/loop/test/package.json
browser/components/loop/test/shared/activeRoomStore_test.js
browser/components/loop/test/shared/mixins_test.js
browser/components/loop/test/shared/validate_test.js
browser/components/loop/test/standalone/webapp_test.js
--- a/browser/components/loop/.eslintignore
+++ b/browser/components/loop/.eslintignore
@@ -6,16 +6,19 @@ modules/MozLoopWorker.js
 modules/MozLoopAPI.jsm
 # Libs we don't need to check
 content/libs
 content/shared/libs
 standalone/content/libs
 standalone/node_modules
 # Libs we don't need to check
 test/shared/vendor
+# Coverage files
+test/coverage
+test/node_modules
 # These are generated react files that we don't need to check
 content/js/contacts.js
 content/js/conversation.js
 content/js/conversationViews.js
 content/js/panel.js
 content/js/roomViews.js
 content/js/feedbackViews.js
 content/shared/js/textChatView.js
--- a/browser/components/loop/.gitignore
+++ b/browser/components/loop/.gitignore
@@ -1,1 +1,2 @@
 .module-cache
+test/coverage
--- a/browser/components/loop/README.txt
+++ b/browser/components/loop/README.txt
@@ -51,16 +51,27 @@ If you install eslint and the react plug
 
   npm install -g eslint
   npm install -g eslint-plugin-react
 
 You can also run it by hand in the browser/components/loop directory:
 
   eslint --ext .js --ext .jsx --ext .jsm .
 
+Test coverage
+=============
+Initial setup
+  cd test
+  npm install
+
+To run
+  npm run build-coverage
+
+It will create a `coverage` folder under test/
+
 Front-End Unit Tests
 ====================
 The unit tests for Loop reside in three directories:
 
 - test/desktop-local
 - test/shared
 - test/standalone
 
--- a/browser/components/loop/run-all-loop-tests.sh
+++ b/browser/components/loop/run-all-loop-tests.sh
@@ -2,31 +2,42 @@
 # Run from topsrcdir, no args
 
 if [ "$1" == "--help" ]; then
   echo "Usage: ./run-all-loop-tests.sh [options]"
   echo "    --skip-e10s  Skips the e10s tests"
   exit 0;
 fi
 
+# Causes script to abort immediately if error code is not checked.
 set -e
 
 # Main tests
 
 LOOPDIR=browser/components/loop
 ESLINT=standalone/node_modules/.bin/eslint
 if [ -x "${LOOPDIR}/${ESLINT}" ]; then
   echo 'running eslint; see http://eslint.org/docs/rules/ for error info'
   (cd ${LOOPDIR} && ./${ESLINT} --ext .js --ext .jsm --ext .jsx .)
   if [ $? != 0 ]; then
     exit 1;
   fi
   echo 'eslint run finished.'
 fi
 
+# Build tests coverage.
+MISSINGDEPSMSG="\nMake sure all dependencies are up to date by running
+'npm install' inside the 'browser/components/loop/test/' directory.\n"
+(
+cd ${LOOPDIR}/test
+if ! npm run-script build-coverage ; then
+  echo $MISSINGDEPSMSG && exit 1
+fi
+)
+
 ./mach xpcshell-test ${LOOPDIR}/
 ./mach marionette-test ${LOOPDIR}/manifest.ini
 
 # The browser_parsable_css.js can fail if we add some css that isn't parsable.
 #
 # The check to make sure that the media devices can be used in Loop without
 # prompting is in browser_devices_get_user_media_about_urls.js. It's possible
 # to mess this up with CSP handling, and probably other changes, too.
--- a/browser/components/loop/test/desktop-local/conversation_test.js
+++ b/browser/components/loop/test/desktop-local/conversation_test.js
@@ -73,38 +73,40 @@ describe("loop.conversation", function()
 
   afterEach(function() {
     loop.shared.mixins.setRootObject(window);
     delete navigator.mozLoop;
     sandbox.restore();
   });
 
   describe("#init", function() {
+    var OTRestore;
     beforeEach(function() {
       sandbox.stub(React, "render");
       sandbox.stub(document.mozL10n, "initialize");
 
       sandbox.stub(loop.shared.models.ConversationModel.prototype,
         "initialize");
 
       sandbox.stub(loop.Dispatcher.prototype, "dispatch");
 
       sandbox.stub(loop.shared.utils,
         "locationData").returns({
           hash: "#42",
           pathname: "/"
         });
 
+      OTRestore = window.OT;
       window.OT = {
         overrideGuidStorage: sinon.stub()
       };
     });
 
     afterEach(function() {
-      delete window.OT;
+      window.OT = OTRestore;
     });
 
     it("should initialize L10n", function() {
       loop.conversation.init();
 
       sinon.assert.calledOnce(document.mozL10n.initialize);
       sinon.assert.calledWithExactly(document.mozL10n.initialize,
         navigator.mozLoop);
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/karma/karma.conf.base.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env node */
+
+module.exports = function(config) {
+  "use strict";
+
+  return {
+
+    // Base path that will be used to resolve all patterns (eg. files, exclude).
+    basePath: "../../",
+
+    // List of files / patterns to load in the browser.
+    files: [],
+
+    // List of files to exclude.
+    exclude: [
+    ],
+
+    // Frameworks to use.
+    // Available frameworks: https://npmjs.org/browse/keyword/karma-adapter .
+    frameworks: ["mocha"],
+
+    // Test results reporter to use.
+    // Possible values: "dots", "progress".
+    // Available reporters: https://npmjs.org/browse/keyword/karma-reporter .
+    reporters: ["progress", "coverage"],
+
+    coverageReporter: {
+      type: "html",
+      dir: "test/coverage/"
+    },
+
+    // Web server port.
+    port: 9876,
+
+    // Enable / disable colors in the output (reporters and logs).
+    colors: true,
+
+    // Level of logging.
+    // Possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG.
+    logLevel: config.LOG_INFO,
+
+    // Enable / disable watching file and executing tests whenever any file changes.
+    autoWatch: false,
+
+    // Start these browsers
+    // Available browser launchers: https://npmjs.org/browse/keyword/karma-launcher .
+    browsers: ["Firefox"],
+
+    // Continuous Integration mode.
+    // If true, Karma captures browsers, runs the tests and exits.
+    singleRun: true,
+
+    // Capture console output.
+    client: {
+      captureConsole: true
+    },
+
+    plugins: [
+      "karma-coverage",
+      "karma-mocha",
+      "karma-firefox-launcher"
+    ]
+  };
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/karma/karma.coverage.desktop.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env node */
+
+module.exports = function(config) {
+  "use strict";
+
+  var baseConfig = require("./karma.conf.base.js")(config);
+
+  // List of files / patterns to load in the browser.
+  baseConfig.files = baseConfig.files.concat([
+    "content/libs/l10n.js",
+    "content/shared/libs/react-0.12.2.js",
+    "content/shared/libs/jquery-2.1.4.js",
+    "content/shared/libs/lodash-3.9.3.js",
+    "content/shared/libs/backbone-1.2.1.js",
+    "test/shared/vendor/*.js",
+    "test/karma/stubs.js", // Stub out DOM event listener due to races.
+    "content/shared/js/utils.js",
+    "content/shared/js/models.js",
+    "content/shared/js/mixins.js",
+    "content/shared/js/websocket.js",
+    "content/shared/js/actions.js",
+    "content/shared/js/otSdkDriver.js",
+    "content/shared/js/validate.js",
+    "content/shared/js/dispatcher.js",
+    "content/shared/js/store.js",
+    "content/shared/js/conversationStore.js",
+    "content/shared/js/roomStates.js",
+    "content/shared/js/fxOSActiveRoomStore.js",
+    "content/shared/js/activeRoomStore.js",
+    "content/shared/js/views.js",
+    "content/shared/js/textChatStore.js",
+    "content/shared/js/textChatView.js",
+    "content/js/feedbackViews.js",
+    "content/js/client.js",
+    "content/js/conversationAppStore.js",
+    "content/js/roomStore.js",
+    "content/js/roomViews.js",
+    "content/js/conversationViews.js",
+    "content/js/conversation.js",
+    "test/desktop-local/*.js"
+  ]);
+
+  // List of files to exclude.
+  baseConfig.exclude = baseConfig.exclude.concat([
+    "test/desktop-local/panel_test.js",
+    "test/desktop-local/contacts_test.js"
+  ]);
+
+  // Preprocess matching files before serving them to the browser.
+  // Available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor .
+  baseConfig.preprocessors = {
+    "content/js/*.js": ["coverage"]
+  };
+
+  baseConfig.coverageReporter.dir = "test/coverage/desktop";
+
+  config.set(baseConfig);
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/karma/karma.coverage.shared_standalone.js
@@ -0,0 +1,59 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env node */
+
+module.exports = function(config) {
+  "use strict";
+
+  var baseConfig = require("./karma.conf.base.js")(config);
+
+  // List of files / patterns to load in the browser.
+  baseConfig.files = baseConfig.files.concat([
+    "standalone/content/libs/l10n-gaia-02ca67948fe8.js",
+    "content/shared/libs/jquery-2.1.4.js",
+    "content/shared/libs/lodash-3.9.3.js",
+    "content/shared/libs/backbone-1.2.1.js",
+    "content/shared/libs/react-0.12.2.js",
+    "content/shared/libs/sdk.js",
+    "test/shared/vendor/*.js",
+    "content/shared/js/utils.js",
+    "content/shared/js/store.js",
+    "content/shared/js/models.js",
+    "content/shared/js/mixins.js",
+    "content/shared/js/crypto.js",
+    "content/shared/js/websocket.js",
+    "content/shared/js/validate.js",
+    "content/shared/js/actions.js",
+    "content/shared/js/dispatcher.js",
+    "content/shared/js/otSdkDriver.js",
+    "content/shared/js/roomStates.js",
+    "content/shared/js/fxOSActiveRoomStore.js",
+    "content/shared/js/activeRoomStore.js",
+    "content/shared/js/conversationStore.js",
+    "content/shared/js/views.js",
+    "content/shared/js/textChatStore.js",
+    "content/shared/js/textChatView.js",
+    "standalone/content/js/multiplexGum.js",
+    "standalone/content/js/standaloneAppStore.js",
+    "standalone/content/js/standaloneClient.js",
+    "standalone/content/js/standaloneMozLoop.js",
+    "standalone/content/js/fxOSMarketplace.js",
+    "standalone/content/js/standaloneRoomViews.js",
+    "standalone/content/js/standaloneMetricsStore.js",
+    "standalone/content/js/webapp.js",
+    "test/shared/*.js",
+    "test/standalone/*.js"
+  ]);
+
+  // Preprocess matching files before serving them to the browser.
+  // Available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor .
+  baseConfig.preprocessors = {
+    "content/shared/js/*.js": ["coverage"],
+    "standalone/content/js/*.js": ["coverage"]
+  };
+
+  baseConfig.coverageReporter.dir = "test/coverage/shared_standalone";
+
+  config.set(baseConfig);
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/karma/stubs.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Used for desktop coverage tests because triggering methods on
+// DOMContentLoaded proved to lead to race conditions.
+
+sinon.stub(document, "addEventListener");
+console.log("[stubs.js] addEventListener stubbed to prevent race conditions");
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/package.json
@@ -0,0 +1,21 @@
+{
+  "name": "FirefoxHello",
+  "version": "0.0.1",
+  "description": "Firefox Hello test coverage",
+  "main": "index.js",
+  "directories": {
+    "test": "test"
+  },
+  "devDependencies": {
+    "istanbul": "^0.3.17",
+    "karma": "^0.12.37",
+    "karma-coverage": "^0.4.2",
+    "karma-firefox-launcher": "^0.1.6",
+    "karma-mocha": "^0.2.0"
+  },
+  "scripts": {
+    "build-coverage-shared": "./node_modules/.bin/karma start karma/karma.coverage.shared_standalone.js",
+    "build-coverage-desktop": "./node_modules/.bin/karma start karma/karma.coverage.desktop.js",
+    "build-coverage": "npm run build-coverage-desktop && npm run build-coverage-shared"
+  }
+}
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -6,18 +6,18 @@ describe("loop.store.ActiveRoomStore", f
 
   var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
   var ROOM_STATES = loop.store.ROOM_STATES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
   var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
-  var sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver;
-  var fakeMultiplexGum;
+  var sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver, fakeMultiplexGum;
+  var standaloneMediaRestore;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     sandbox.useFakeTimers();
 
     dispatcher = new loop.Dispatcher();
     sandbox.stub(dispatcher, "dispatch");
     sandbox.stub(window, "close");
@@ -47,31 +47,33 @@ describe("loop.store.ActiveRoomStore", f
       forceDisconnectAll: sinon.stub().callsArg(0),
       retryPublishWithoutVideo: sinon.stub(),
       startScreenShare: sinon.stub(),
       switchAcquiredWindow: sinon.stub(),
       endScreenShare: sinon.stub().returns(true)
     };
 
     fakeMultiplexGum = {
-        reset: sandbox.spy()
+      reset: sandbox.spy()
     };
 
+    standaloneMediaRestore = loop.standaloneMedia;
     loop.standaloneMedia = {
       multiplexGum: fakeMultiplexGum
     };
 
     store = new loop.store.ActiveRoomStore(dispatcher, {
       mozLoop: fakeMozLoop,
       sdkDriver: fakeSdkDriver
     });
   });
 
   afterEach(function() {
     sandbox.restore();
+    loop.standaloneMedia = standaloneMediaRestore;
   });
 
   describe("#constructor", function() {
     it("should throw an error if mozLoop is missing", function() {
       expect(function() {
         new loop.store.ActiveRoomStore(dispatcher);
       }).to.Throw(/mozLoop/);
     });
--- a/browser/components/loop/test/shared/mixins_test.js
+++ b/browser/components/loop/test/shared/mixins_test.js
@@ -426,17 +426,18 @@ describe("loop.shared.mixins", function(
   describe("loop.shared.mixins.AudioMixin", function() {
     var view, fakeAudio, TestComp;
 
     beforeEach(function() {
       navigator.mozLoop = {
         doNotDisturb: true,
         getAudioBlob: sinon.spy(function(name, callback) {
           callback(null, new Blob([new ArrayBuffer(10)], {type: "audio/ogg"}));
-        })
+        }),
+        getLoopPref: sandbox.stub()
       };
 
       fakeAudio = {
         play: sinon.spy(),
         pause: sinon.spy(),
         removeAttribute: sinon.spy()
       };
       sandbox.stub(window, "Audio").returns(fakeAudio);
--- a/browser/components/loop/test/shared/validate_test.js
+++ b/browser/components/loop/test/shared/validate_test.js
@@ -12,16 +12,23 @@ describe("Validator", function() {
     return validator.validate.bind(validator, values);
   }
 
   // test types
   function X(){}
   function Y(){}
 
   describe("#validate", function() {
+    function mozRTCSessionDescription() {}
+    var mozRTC;
+
+    beforeEach(function() {
+      mozRTC = new mozRTCSessionDescription();
+    });
+
     it("should check for a single required dependency when no option passed",
       function() {
         expect(create({x: Number}, {}))
           .to.Throw(TypeError, /missing required x$/);
       });
 
     it("should check for a missing required dependency, undefined passed",
       function() {
@@ -62,17 +69,17 @@ describe("Validator", function() {
     });
 
     it("should check for a custom constructor dependency", function() {
       expect(create({foo: X}, {foo: null})).to.Throw(
         TypeError, /invalid dependency: foo; expected X, got null$/);
     });
 
     it("should check for a native constructor dependency", function() {
-      expect(create({foo: mozRTCSessionDescription}, {foo: "x"}))
+      expect(create({foo: mozRTC}, {foo: "x"}))
         .to.Throw(TypeError,
                   /invalid dependency: foo; expected mozRTCSessionDescription/);
     });
 
     it("should check for a null dependency", function() {
       expect(create({foo: null}, {foo: "x"})).to.Throw(
         TypeError, /invalid dependency: foo; expected null, got String$/);
     });
--- a/browser/components/loop/test/standalone/webapp_test.js
+++ b/browser/components/loop/test/standalone/webapp_test.js
@@ -42,21 +42,32 @@ describe("loop.webapp", function() {
     };
   });
 
   afterEach(function() {
     sandbox.restore();
   });
 
   describe("#init", function() {
+    var loopConfigRestore;
+
     beforeEach(function() {
       sandbox.stub(React, "render");
+      loopConfigRestore = loop.config;
+      loop.config = {
+        feedbackApiUrl: "http://fake.invalid",
+        serverUrl: "http://fake.invalid"
+      };
       sandbox.stub(loop.Dispatcher.prototype, "dispatch");
     });
 
+    afterEach(function() {
+      loop.config = loopConfigRestore;
+    });
+
     it("should create the WebappRootView", function() {
       loop.webapp.init();
 
       sinon.assert.calledOnce(React.render);
       sinon.assert.calledWith(React.render,
         sinon.match(function(value) {
           return TestUtils.isCompositeComponentElement(value,
             loop.webapp.WebappRootView);