diff --git a/cms/static/coffee/spec/main.coffee b/cms/static/coffee/spec/main.coffee
index 87253d01f1..c5e69aaf24 100644
--- a/cms/static/coffee/spec/main.coffee
+++ b/cms/static/coffee/spec/main.coffee
@@ -283,6 +283,8 @@ while i < testFiles.length
testFiles[i] = '/base/' + testFiles[i] + '.js'
i++
-require testFiles, ->
+# Jasmine has a global stack for creating a tree of specs. We need to load
+# spec files one by one, otherwise some end up getting nested under others.
+requireSerial testFiles, ->
# start test run, once Require.js is done
window.__karma__.start()
diff --git a/cms/static/coffee/spec/main_squire.coffee b/cms/static/coffee/spec/main_squire.coffee
index e2ab5fc35d..a3480f3516 100644
--- a/cms/static/coffee/spec/main_squire.coffee
+++ b/cms/static/coffee/spec/main_squire.coffee
@@ -202,6 +202,9 @@ i = 0
while i < testFiles.length
testFiles[i] = '/base/' + testFiles[i] + '.js'
i++
-require testFiles, ->
+
+# Jasmine has a global stack for creating a tree of specs. We need to load
+# spec files one by one, otherwise some end up getting nested under others.
+requireSerial testFiles, ->
# start test run, once Require.js is done
window.__karma__.start()
diff --git a/cms/static/js/spec/views/xblock_spec.js b/cms/static/js/spec/views/xblock_spec.js
index bcbfabfffa..9777008f05 100644
--- a/cms/static/js/spec/views/xblock_spec.js
+++ b/cms/static/js/spec/views/xblock_spec.js
@@ -62,8 +62,8 @@ define(["jquery", "URI", "common/js/spec_helpers/ajax_helpers", "common/js/compo
mockCssUrl = "mock.css",
headHtml;
postXBlockRequest(requests, [
- ["hash1", { mimetype: "text/css", kind: "text", data: mockCssText }],
- ["hash2", { mimetype: "text/css", kind: "url", data: mockCssUrl }]
+ ["xblock_spec_hash1", { mimetype: "text/css", kind: "text", data: mockCssText }],
+ ["xblock_spec_hash2", { mimetype: "text/css", kind: "url", data: mockCssUrl }]
]);
headHtml = $('head').html();
expect(headHtml).toContain(mockCssText);
@@ -73,7 +73,9 @@ define(["jquery", "URI", "common/js/spec_helpers/ajax_helpers", "common/js/compo
it('can render an xblock with required JavaScript', function() {
var requests = AjaxHelpers.requests(this);
postXBlockRequest(requests, [
- ["hash3", { mimetype: "application/javascript", kind: "text", data: "window.test = 100;" }]
+ ["xblock_spec_hash3", {
+ mimetype: "application/javascript", kind: "text", data: "window.test = 100;"
+ }]
]);
expect(window.test).toBe(100);
});
@@ -82,7 +84,7 @@ define(["jquery", "URI", "common/js/spec_helpers/ajax_helpers", "common/js/compo
var requests = AjaxHelpers.requests(this),
mockHeadTag = "
Test Title";
postXBlockRequest(requests, [
- ["hash4", { mimetype: "text/html", placement: "head", data: mockHeadTag }]
+ ["xblock_spec_hash4", { mimetype: "text/html", placement: "head", data: mockHeadTag }]
]);
expect($('head').html()).toContain(mockHeadTag);
});
@@ -93,7 +95,9 @@ define(["jquery", "URI", "common/js/spec_helpers/ajax_helpers", "common/js/compo
promise;
spyOn(ViewUtils, 'loadJavaScript').and.returnValue($.Deferred().reject().promise());
promise = postXBlockRequest(requests, [
- ["hash5", { mimetype: "application/javascript", kind: "url", data: missingJavaScriptUrl }]
+ ["xblock_spec_hash5", {
+ mimetype: "application/javascript", kind: "url", data: missingJavaScriptUrl
+ }]
]);
expect(promise.isRejected()).toBe(true);
});
diff --git a/cms/static/karma_cms.conf.js b/cms/static/karma_cms.conf.js
index b315e2e962..a33692da00 100644
--- a/cms/static/karma_cms.conf.js
+++ b/cms/static/karma_cms.conf.js
@@ -82,7 +82,9 @@ var libraryFiles = [
{pattern: 'edx-pattern-library/js/afontgarde.js', included: false},
{pattern: 'edx-pattern-library/js/edx-icons.js', included: false},
{pattern: 'edx-pattern-library/js/**/*.js', included: false},
- {pattern: 'edx-ui-toolkit/js/**/*.js', included: false}
+ {pattern: 'edx-ui-toolkit/js/**/*.js', included: false},
+
+ {pattern: 'common/js/utils/require-serial.js', included: true}
];
// Paths to source JavaScript files
diff --git a/cms/static/karma_cms_squire.conf.js b/cms/static/karma_cms_squire.conf.js
index 7d6ec26748..bf6756dd57 100644
--- a/cms/static/karma_cms_squire.conf.js
+++ b/cms/static/karma_cms_squire.conf.js
@@ -68,7 +68,8 @@ var libraryFiles = [
pattern: 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate.js',
included: false
},
- {pattern: 'xmodule_js/common_static/js/vendor/requirejs/text.js', included: false}
+ {pattern: 'xmodule_js/common_static/js/vendor/requirejs/text.js', included: false},
+ {pattern: 'common/js/utils/require-serial.js', included: true}
];
// Paths to source JavaScript files
diff --git a/common/static/common/js/spec/main_requirejs.js b/common/static/common/js/spec/main_requirejs.js
index eebe0a92b0..c859348826 100644
--- a/common/static/common/js/spec/main_requirejs.js
+++ b/common/static/common/js/spec/main_requirejs.js
@@ -178,7 +178,7 @@
testFiles[i] = '/base/' + testFiles[i];
}
- require(testFiles, function () {
+ window.requireSerial(testFiles, function () {
// start test run, once Require.js is done
window.__karma__.start();
});
diff --git a/common/static/common/js/utils/require-serial.js b/common/static/common/js/utils/require-serial.js
new file mode 100644
index 0000000000..c55b6bd628
--- /dev/null
+++ b/common/static/common/js/utils/require-serial.js
@@ -0,0 +1,23 @@
+/*
+Helper function used to require files serially instead of concurrently.
+ */
+(function (window, require) {
+ 'use strict';
+
+ var requireModules = function (paths, callback, modules) {
+ // If all the modules have been loaded, call the callback.
+ if (paths.length === 0) {
+ return callback.apply(null, modules);
+ }
+ // Otherwise load the next one.
+ require([paths.shift()], function(module) {
+ modules.push(module);
+ requireModules(paths, callback, modules);
+ });
+ };
+
+ window.requireSerial = function(paths, callback) {
+ requireModules(paths, callback, []);
+ };
+
+}).call(this, window, require || RequireJS.require);
diff --git a/common/static/karma_common_requirejs.conf.js b/common/static/karma_common_requirejs.conf.js
index 475d869d3c..7590ff4694 100644
--- a/common/static/karma_common_requirejs.conf.js
+++ b/common/static/karma_common_requirejs.conf.js
@@ -46,7 +46,8 @@ var libraryFiles = [
{pattern: 'js/test/i18n.js', included: false},
{pattern: 'coffee/src/jquery.immediateDescendents.js', included: false},
{pattern: 'js/vendor/requirejs/text.js', included: false},
- {pattern: 'js/vendor/sinon-1.17.0.js', included: false}
+ {pattern: 'js/vendor/sinon-1.17.0.js', included: false},
+ {pattern: 'common/js/utils/require-serial.js', included: true}
];
// Paths to source JavaScript files
diff --git a/lms/djangoapps/teams/static/teams/js/spec/teams_tab_factory_spec.js b/lms/djangoapps/teams/static/teams/js/spec/teams_tab_factory_spec.js
index d41cfce9d5..de6b4e104b 100644
--- a/lms/djangoapps/teams/static/teams/js/spec/teams_tab_factory_spec.js
+++ b/lms/djangoapps/teams/static/teams/js/spec/teams_tab_factory_spec.js
@@ -1,6 +1,6 @@
-define(['jquery', 'backbone', 'teams/js/teams_tab_factory',
+define(['jquery', 'backbone', 'teams/js/teams_tab_factory', 'teams/js/views/teams_tab',
'common/js/spec_helpers/page_helpers', 'teams/js/spec_helpers/team_spec_helpers'],
- function($, Backbone, TeamsTabFactory, PageHelpers, TeamSpecHelpers) {
+ function($, Backbone, TeamsTabFactory, TeamsTabView, PageHelpers, TeamSpecHelpers) {
'use strict';
describe("Teams Tab Factory", function() {
@@ -15,6 +15,7 @@ define(['jquery', 'backbone', 'teams/js/teams_tab_factory',
afterEach(function() {
Backbone.history.stop();
+ $(document).off('ajaxError', TeamsTabView.prototype.errorHandler);
});
it('can render the "Teams" tab', function() {
diff --git a/lms/djangoapps/teams/static/teams/js/spec/views/teams_tab_spec.js b/lms/djangoapps/teams/static/teams/js/spec/views/teams_tab_spec.js
index e9f58659dd..2f236433e1 100644
--- a/lms/djangoapps/teams/static/teams/js/spec/views/teams_tab_spec.js
+++ b/lms/djangoapps/teams/static/teams/js/spec/views/teams_tab_spec.js
@@ -56,7 +56,11 @@ define([
spyOn(Logger, 'log');
});
- afterEach(Backbone.history.stop);
+ afterEach(function() {
+ Backbone.history.stop();
+ $(document).off('ajaxError', TeamsTabView.prototype.errorHandler);
+ }
+ );
describe('Navigation', function () {
it('does not render breadcrumbs for the top level tabs', function() {
diff --git a/lms/djangoapps/teams/static/teams/js/views/teams_tab.js b/lms/djangoapps/teams/static/teams/js/views/teams_tab.js
index 7aadb4fed2..69e5241213 100644
--- a/lms/djangoapps/teams/static/teams/js/views/teams_tab.js
+++ b/lms/djangoapps/teams/static/teams/js/views/teams_tab.js
@@ -159,14 +159,7 @@
start: function() {
Backbone.history.start();
- $(document).ajaxError(function (event, xhr) {
- if (xhr.status === 401) {
- TeamUtils.showMessage(gettext("Your request could not be completed. Reload the page and try again."));
- }
- else if (xhr.status === 500) {
- TeamUtils.showMessage(gettext("Your request could not be completed due to a server problem. Reload the page and try again. If the issue persists, click the Help tab to report the problem."));
- }
- });
+ $(document).ajaxError(this.errorHandler);
// Navigate to the default page if there is no history:
// 1. If the user belongs to at least one team, jump to the "My Teams" page
@@ -180,6 +173,20 @@
}
},
+ errorHandler: function(event, xhr) {
+ if (xhr.status === 401) {
+ TeamUtils.showMessage(gettext(
+ "Your request could not be completed. Reload the page and try again."
+ ));
+ }
+ else if (xhr.status === 500) {
+ TeamUtils.showMessage(gettext(
+ "Your request could not be completed due to a server problem. Reload the page" +
+ " and try again. If the issue persists, click the Help tab to report the problem."
+ ));
+ }
+ },
+
render: function() {
this.mainView.setElement(this.$el).render();
TeamUtils.hideMessage();
diff --git a/lms/static/js/spec/main.js b/lms/static/js/spec/main.js
index 2439da0eb8..6bc5f9f481 100644
--- a/lms/static/js/spec/main.js
+++ b/lms/static/js/spec/main.js
@@ -770,7 +770,9 @@
testFiles[i] = '/base/' + testFiles[i];
}
- require(testFiles, function () {
+ // Jasmine has a global stack for creating a tree of specs. We need to load
+ // spec files one by one, otherwise some end up getting nested under others.
+ window.requireSerial(testFiles, function () {
// start test run, once Require.js is done
window.__karma__.start();
});
diff --git a/lms/static/karma_lms.conf.js b/lms/static/karma_lms.conf.js
index 9f8efa3caa..10634dd8da 100644
--- a/lms/static/karma_lms.conf.js
+++ b/lms/static/karma_lms.conf.js
@@ -73,7 +73,8 @@ var libraryFiles = [
{pattern: 'xmodule_js/common_static/js/vendor/slick.core.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/slick.grid.js', included: true},
{pattern: 'xmodule_js/common_static/js/libs/jasmine-waituntil.js', included: true},
- {pattern: 'xmodule_js/common_static/js/libs/jasmine-extensions.js', included: true}
+ {pattern: 'xmodule_js/common_static/js/libs/jasmine-extensions.js', included: true},
+ {pattern: 'common/js/utils/require-serial.js', included: true}
];
// Paths to source JavaScript files