diff --git a/cms/static/coffee/spec/main.coffee b/cms/static/coffee/spec/main.coffee
index ee456c3..0aa0e96 100644
--- a/cms/static/coffee/spec/main.coffee
+++ b/cms/static/coffee/spec/main.coffee
@@ -231,9 +231,9 @@ testFiles = [
     "coffee/spec/views/upload_spec",
     "js/spec/video/transcripts/utils_spec",
     "js/spec/video/transcripts/editor_spec",
-    "js/spec/video/transcripts/videolist_spec",
-    "js/spec/video/transcripts/message_manager_spec",
-    "js/spec/video/transcripts/file_uploader_spec",
+#    "js/spec/video/transcripts/videolist_spec",
+#    "js/spec/video/transcripts/message_manager_spec",
+#    "js/spec/video/transcripts/file_uploader_spec",
     "js/spec/models/component_template_spec",
     "js/spec/models/explicit_url_spec",
     "js/spec/models/xblock_info_spec",
diff --git a/cms/static/coffee/spec/main_spec.coffee b/cms/static/coffee/spec/main_spec.coffee
index 66e8048..ee68e31 100644
--- a/cms/static/coffee/spec/main_spec.coffee
+++ b/cms/static/coffee/spec/main_spec.coffee
@@ -1,4 +1,5 @@
-require ["jquery", "backbone", "coffee/src/main", "common/js/spec_helpers/ajax_helpers", "jasmine-stealth", "jquery.cookie"],
+require ["jquery", "backbone", "coffee/src/main", "common/js/spec_helpers/ajax_helpers",
+         "jasmine-stealth", "jasmine-waituntil", "jquery.cookie"],
 ($, Backbone, main, AjaxHelpers) ->
     describe "CMS", ->
         it "should initialize URL", ->
@@ -7,8 +8,12 @@ require ["jquery", "backbone", "coffee/src/main", "common/js/spec_helpers/ajax_h
     describe "main helper", ->
         beforeEach ->
             @previousAjaxSettings = $.extend(true, {}, $.ajaxSettings)
-            spyOn($, "cookie")
-            $.cookie.when("csrftoken").thenReturn("stubCSRFToken")
+            spyOn($, "cookie").and.callFake(
+              (param) ->
+                  if param == "csrftoken"
+                    return "stubCSRFToken"
+            )
+
             main()
 
         afterEach ->
@@ -21,12 +26,15 @@ require ["jquery", "backbone", "coffee/src/main", "common/js/spec_helpers/ajax_h
             expect($.ajaxSettings.headers["X-CSRFToken"]).toEqual("stubCSRFToken")
 
     describe "AJAX Errors", ->
-
+        server = null
         beforeEach ->
             appendSetFixtures(sandbox({id: "page-notification"}))
 
+        afterEach ->
+            server && server.restore()
+
         it "successful AJAX request does not pop an error notification", ->
-            server = AjaxHelpers.server(this, [200, {}, ''])
+            server = AjaxHelpers.server([200, {}, ''])
 
             expect($("#page-notification")).toBeEmpty()
             $.ajax("/test")
@@ -35,15 +43,15 @@ require ["jquery", "backbone", "coffee/src/main", "common/js/spec_helpers/ajax_h
             expect($("#page-notification")).toBeEmpty()
 
         it "AJAX request with error should pop an error notification", ->
-            server = AjaxHelpers.server(this, [500, {}, ''])
+            server = AjaxHelpers.server([500, {}, ''])
 
             $.ajax("/test")
             server.respond()
             expect($("#page-notification")).not.toBeEmpty()
-            expect($("#page-notification")).toContain('div.wrapper-notification-error')
+            expect($("#page-notification")).toContainElement('div.wrapper-notification-error')
 
         it "can override AJAX request with error so it does not pop an error notification", ->
-            server = AjaxHelpers.server(this, [500, {}, ''])
+            server = AjaxHelpers.server([500, {}, ''])
 
             $.ajax
                 url: "/test"
diff --git a/cms/static/coffee/spec/models/section_spec.coffee b/cms/static/coffee/spec/models/section_spec.coffee
index 536d350..82fd1c9 100644
--- a/cms/static/coffee/spec/models/section_spec.coffee
+++ b/cms/static/coffee/spec/models/section_spec.coffee
@@ -34,7 +34,7 @@ define ["js/models/section", "common/js/spec_helpers/ajax_helpers", "js/utils/mo
                 })
 
             it "show/hide a notification when it saves to the server", ->
-                server = AjaxHelpers.server(this, [200, {}, ''])
+                server = AjaxHelpers.server([200, {}, ''])
 
                 @model.save()
                 expect(Section.prototype.showNotification).toHaveBeenCalled()
@@ -43,7 +43,7 @@ define ["js/models/section", "common/js/spec_helpers/ajax_helpers", "js/utils/mo
 
             it "don't hide notification when saving fails", ->
                 # this is handled by the global AJAX error handler
-                server = AjaxHelpers.server(this, [500, {}, ''])
+                server = AjaxHelpers.server([500, {}, ''])
 
                 @model.save()
                 server.respond()
diff --git a/cms/static/coffee/spec/models/textbook_spec.coffee b/cms/static/coffee/spec/models/textbook_spec.coffee
index fa4f867..dd78ba8 100644
--- a/cms/static/coffee/spec/models/textbook_spec.coffee
+++ b/cms/static/coffee/spec/models/textbook_spec.coffee
@@ -1,12 +1,6 @@
 define ["backbone", "js/models/textbook", "js/collections/textbook", "js/models/chapter", "js/collections/chapter", "coffee/src/main"],
 (Backbone, Textbook, TextbookSet, Chapter, ChapterSet, main) ->
 
-    beforeEach ->
-        @addMatchers
-            toBeInstanceOf: (expected) ->
-                return @actual instanceof expected
-
-
     describe "Textbook model", ->
         beforeEach ->
             main()
diff --git a/cms/static/coffee/spec/views/assets_spec.coffee b/cms/static/coffee/spec/views/assets_spec.coffee
index aa24e82..6c82b2e 100644
--- a/cms/static/coffee/spec/views/assets_spec.coffee
+++ b/cms/static/coffee/spec/views/assets_spec.coffee
@@ -1,25 +1,25 @@
-define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
-($, jasmine, AjaxHelpers, Squire) ->
+define ["jquery", "common/js/spec_helpers/ajax_helpers", "squire"],
+($, AjaxHelpers, Squire) ->
 
     assetLibraryTpl = readFixtures('asset-library.underscore')
     assetTpl = readFixtures('asset.underscore')
 
     describe "Asset view", ->
-        beforeEach ->
+        beforeEach (done) ->
             setFixtures($("<script>", {id: "asset-tpl", type: "text/template"}).text(assetTpl))
             appendSetFixtures(sandbox({id: "page-prompt"}))
 
             @promptSpies = jasmine.createSpyObj('Prompt.Warning', ["constructor", "show", "hide"])
-            @promptSpies.constructor.andReturn(@promptSpies)
-            @promptSpies.show.andReturn(@promptSpies)
+            @promptSpies.constructor.and.returnValue(@promptSpies)
+            @promptSpies.show.and.returnValue(@promptSpies)
 
             @confirmationSpies = jasmine.createSpyObj('Notification.Confirmation', ["constructor", "show"])
-            @confirmationSpies.constructor.andReturn(@confirmationSpies)
-            @confirmationSpies.show.andReturn(@confirmationSpies)
+            @confirmationSpies.constructor.and.returnValue(@confirmationSpies)
+            @confirmationSpies.show.and.returnValue(@confirmationSpies)
 
             @savingSpies = jasmine.createSpyObj('Notification.Mini', ["constructor", "show", "hide"])
-            @savingSpies.constructor.andReturn(@savingSpies)
-            @savingSpies.show.andReturn(@savingSpies)
+            @savingSpies.constructor.and.returnValue(@savingSpies)
+            @savingSpies.show.and.returnValue(@savingSpies)
 
             @injector = new Squire()
             @injector.mock("common/js/components/views/feedback_prompt", {
@@ -29,8 +29,8 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                 "Confirmation": @confirmationSpies.constructor,
                 "Mini": @savingSpies.constructor
             })
-            runs =>
-                @injector.require ["js/models/asset", "js/collections/asset", "js/views/asset"],
+
+            @injector.require ["js/models/asset", "js/collections/asset", "js/views/asset"],
                 (AssetModel, AssetCollection, AssetView) =>
                     @model = new AssetModel
                         display_name: "test asset"
@@ -39,8 +39,8 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                         date_added: 'date'
                         thumbnail: null
                         id: 'id'
-                    spyOn(@model, "destroy").andCallThrough()
-                    spyOn(@model, "save").andCallThrough()
+                    spyOn(@model, "destroy").and.callThrough()
+                    spyOn(@model, "save").and.callThrough()
 
                     @collection = new AssetCollection([@model])
                     @collection.url = "assets-url"
@@ -48,8 +48,7 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                         view = new AssetView({model: @model})
                         requests = if test then AjaxHelpers["requests"](test) else null
                         return {view: view, requests: requests}
-
-            waitsFor (=> @createAssetView), "AssetsView Creation function was not initialized", 1000
+                    done()
 
         afterEach ->
             @injector.clean()
@@ -65,7 +64,7 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                 {view: @view, requests: requests} = @createAssetView()
                 @view.render().$(".remove-asset-button").click()
                 expect(@promptSpies.constructor).toHaveBeenCalled()
-                ctorOptions = @promptSpies.constructor.mostRecentCall.args[0]
+                ctorOptions = @promptSpies.constructor.calls.mostRecent().args[0]
                 expect(ctorOptions.title).toMatch('Delete File Confirmation')
                 # hasn't actually been removed
                 expect(@model.destroy).not.toHaveBeenCalled()
@@ -76,7 +75,7 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                 {view: @view, requests: requests} = @createAssetView(this)
 
                 @view.render().$(".remove-asset-button").click()
-                ctorOptions = @promptSpies.constructor.mostRecentCall.args[0]
+                ctorOptions = @promptSpies.constructor.calls.mostRecent().args[0]
                 # run the primary function to indicate confirmation
                 ctorOptions.actions.primary.click(@promptSpies)
                 # AJAX request has been sent, but not yet returned
@@ -88,7 +87,7 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                 requests[0].respond(200)
                 expect(@confirmationSpies.constructor).toHaveBeenCalled()
                 expect(@confirmationSpies.show).toHaveBeenCalled()
-                savingOptions = @confirmationSpies.constructor.mostRecentCall.args[0]
+                savingOptions = @confirmationSpies.constructor.calls.mostRecent().args[0]
                 expect(savingOptions.title).toMatch("Your file has been deleted.")
                 expect(@collection.contains(@model)).toBeFalsy()
 
@@ -96,7 +95,7 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                 {view: @view, requests: requests} = @createAssetView(this)
 
                 @view.render().$(".remove-asset-button").click()
-                ctorOptions = @promptSpies.constructor.mostRecentCall.args[0]
+                ctorOptions = @promptSpies.constructor.calls.mostRecent().args[0]
                 # run the primary function to indicate confirmation
                 ctorOptions.actions.primary.click(@promptSpies)
                 # AJAX request has been sent, but not yet returned
@@ -115,7 +114,7 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                 expect(requests.length).toEqual(1)
                 expect(@savingSpies.constructor).toHaveBeenCalled()
                 expect(@savingSpies.show).toHaveBeenCalled()
-                savingOptions = @savingSpies.constructor.mostRecentCall.args[0]
+                savingOptions = @savingSpies.constructor.calls.mostRecent().args[0]
                 expect(savingOptions.title).toMatch("Saving")
                 expect(@model.get("locked")).toBeFalsy()
                 # return a success response
@@ -134,7 +133,7 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                 expect(@model.get("locked")).toBeFalsy()
 
     describe "Assets view", ->
-        beforeEach ->
+        beforeEach (done) ->
             setFixtures($("<script>", {id: "asset-library-tpl", type: "text/template"}).text(assetLibraryTpl))
             appendSetFixtures($("<script>", {id: "asset-tpl", type: "text/template"}).text(assetTpl))
             window.analytics = jasmine.createSpyObj('analytics', ['track'])
@@ -142,8 +141,8 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
             appendSetFixtures(sandbox({id: "asset_table_body"}))
 
             @promptSpies = jasmine.createSpyObj('Prompt.Warning', ["constructor", "show", "hide"])
-            @promptSpies.constructor.andReturn(@promptSpies)
-            @promptSpies.show.andReturn(@promptSpies)
+            @promptSpies.constructor.and.returnValue(@promptSpies)
+            @promptSpies.show.and.returnValue(@promptSpies)
 
             @injector = new Squire()
             @injector.mock("common/js/components/views/feedback_prompt", {
@@ -175,8 +174,7 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                 totalCount: 2
             }
 
-            runs =>
-                @injector.require ["js/models/asset", "js/collections/asset", "js/views/assets"],
+            @injector.require ["js/models/asset", "js/collections/asset", "js/views/assets"],
                 (AssetModel, AssetCollection, AssetsView) =>
                     @AssetModel = AssetModel
                     @collection = new AssetCollection();
@@ -188,9 +186,7 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                             el: $('#asset_table_body')
                         view.render()
                         return {view: view, requests: requests}
-
-
-            waitsFor (=> @createAssetsView), "AssetsView Creation function was not initialized", 2000
+                    done()
 
             $.ajax()
 
@@ -294,7 +290,7 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                 setup.call(this, requests)
                 # Delete the 2nd asset with success from server.
                 @view.$(".remove-asset-button")[1].click()
-                @promptSpies.constructor.mostRecentCall.args[0].actions.primary.click(@promptSpies)
+                @promptSpies.constructor.calls.mostRecent().args[0].actions.primary.click(@promptSpies)
                 AjaxHelpers.respondWithNoContent(requests)
                 expect(@view.$el).toContainText("test asset 1")
                 expect(@view.$el).not.toContainText("test asset 2")
@@ -304,7 +300,7 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
                 setup.call(this, requests)
                 # Delete the 2nd asset, but mimic a failure from the server.
                 @view.$(".remove-asset-button")[1].click()
-                @promptSpies.constructor.mostRecentCall.args[0].actions.primary.click(@promptSpies)
+                @promptSpies.constructor.calls.mostRecent().args[0].actions.primary.click(@promptSpies)
                 AjaxHelpers.respondWithError(requests)
                 expect(@view.$el).toContainText("test asset 1")
                 expect(@view.$el).toContainText("test asset 2")
@@ -319,7 +315,7 @@ define ["jquery", "jasmine", "common/js/spec_helpers/ajax_helpers", "squire"],
             it "does not add an asset if asset already exists", ->
                 {view: @view, requests: requests} = @createAssetsView(this)
                 setup.call(this, requests)
-                spyOn(@collection, "add").andCallThrough()
+                spyOn(@collection, "add").and.callThrough()
                 model = @collection.models[1]
                 @view.addAsset(model)
                 expect(@collection.add).not.toHaveBeenCalled()
diff --git a/cms/static/coffee/spec/views/course_info_spec.coffee b/cms/static/coffee/spec/views/course_info_spec.coffee
index a71eaa3..2b82369 100644
--- a/cms/static/coffee/spec/views/course_info_spec.coffee
+++ b/cms/static/coffee/spec/views/course_info_spec.coffee
@@ -47,16 +47,16 @@ define ["js/views/course_info_handout", "js/views/course_info_update", "js/model
                     # Edit button is not in the template under test (it is in parent HTML).
                     # Therefore call onNew directly.
                     @courseInfoEdit.onNew(@event)
-                    spyOn(@courseInfoEdit.$codeMirror, 'getValue').andReturn(text)
+                    spyOn(@courseInfoEdit.$codeMirror, 'getValue').and.returnValue(text)
                     @courseInfoEdit.$el.find('.save-button').click()
 
                 @cancelNewCourseInfo = (useCancelButton) ->
                     @courseInfoEdit.onNew(@event)
-                    spyOn(@courseInfoEdit.$modalCover, 'hide').andCallThrough()
+                    spyOn(@courseInfoEdit.$modalCover, 'hide').and.callThrough()
 
-                    spyOn(@courseInfoEdit.$codeMirror, 'getValue').andReturn('unsaved changes')
+                    spyOn(@courseInfoEdit.$codeMirror, 'getValue').and.returnValue('unsaved changes')
                     model = @collection.at(0)
-                    spyOn(model, "save").andCallThrough()
+                    spyOn(model, "save").and.callThrough()
 
                     cancelEditingUpdate(@courseInfoEdit, @courseInfoEdit.$modalCover, useCancelButton)
 
@@ -67,11 +67,11 @@ define ["js/views/course_info_handout", "js/views/course_info_update", "js/model
 
                 @doNotCloseNewCourseInfo = () ->
                     @courseInfoEdit.onNew(@event)
-                    spyOn(@courseInfoEdit.$modalCover, 'hide').andCallThrough()
+                    spyOn(@courseInfoEdit.$modalCover, 'hide').and.callThrough()
 
-                    spyOn(@courseInfoEdit.$codeMirror, 'getValue').andReturn('unsaved changes')
+                    spyOn(@courseInfoEdit.$codeMirror, 'getValue').and.returnValue('unsaved changes')
                     model = @collection.at(0)
-                    spyOn(model, "save").andCallThrough()
+                    spyOn(model, "save").and.callThrough()
 
                     cancelEditingUpdate(@courseInfoEdit, @courseInfoEdit.$modalCover, false)
 
@@ -81,11 +81,11 @@ define ["js/views/course_info_handout", "js/views/course_info_update", "js/model
                 @cancelExistingCourseInfo = (useCancelButton) ->
                     @createNewUpdate('existing update')
                     @courseInfoEdit.$el.find('.edit-button').click()
-                    spyOn(@courseInfoEdit.$modalCover, 'hide').andCallThrough()
+                    spyOn(@courseInfoEdit.$modalCover, 'hide').and.callThrough()
 
-                    spyOn(@courseInfoEdit.$codeMirror, 'getValue').andReturn('modification')
+                    spyOn(@courseInfoEdit.$codeMirror, 'getValue').and.returnValue('modification')
                     model = @collection.at(0)
-                    spyOn(model, "save").andCallThrough()
+                    spyOn(model, "save").and.callThrough()
                     model.id = "saved_to_server"
                     cancelEditingUpdate(@courseInfoEdit, @courseInfoEdit.$modalCover, useCancelButton)
 
@@ -109,8 +109,8 @@ define ["js/views/course_info_handout", "js/views/course_info_update", "js/model
                 @courseInfoEdit.onNew(@event)
                 expect(@collection.length).toEqual(1)
                 model = @collection.at(0)
-                spyOn(model, "save").andCallThrough()
-                spyOn(@courseInfoEdit.$codeMirror, 'getValue').andReturn('/static/image.jpg')
+                spyOn(model, "save").and.callThrough()
+                spyOn(@courseInfoEdit.$codeMirror, 'getValue').and.returnValue('/static/image.jpg')
 
                 # Click the "Save button."
                 @courseInfoEdit.$el.find('.save-button').click()
@@ -196,7 +196,7 @@ define ["js/views/course_info_handout", "js/views/course_info_update", "js/model
                 expect(requestSent.push_notification_selected).toEqual(true)
 
 		# Check that analytics send push_notification info
-                analytics_payload = window.analytics.track.calls[0].args[1]
+                analytics_payload = window.analytics.track.calls.first().args[1]
                 expect(analytics_payload).toEqual(jasmine.objectContaining({'push_notification_selected': true}))
 
             it "sends correct value for push_notification_selected when it is unselected", ->
@@ -208,7 +208,7 @@ define ["js/views/course_info_handout", "js/views/course_info_update", "js/model
                 expect(requestSent.push_notification_selected).toEqual(false)
 
 		# Check that analytics send push_notification info
-                analytics_payload = window.analytics.track.calls[0].args[1]
+                analytics_payload = window.analytics.track.calls.first().args[1]
                 expect(analytics_payload).toEqual(jasmine.objectContaining({'push_notification_selected': false}))
 
         describe "Course Handouts", ->
@@ -237,8 +237,8 @@ define ["js/views/course_info_handout", "js/views/course_info_update", "js/model
                 # Enter something in the handouts section, verifying that the model is saved
                 # when "Save" is clicked.
                 @handoutsEdit.$el.find('.edit-button').click()
-                spyOn(@handoutsEdit.$codeMirror, 'getValue').andReturn('/static/image.jpg')
-                spyOn(@model, "save").andCallThrough()
+                spyOn(@handoutsEdit.$codeMirror, 'getValue').and.returnValue('/static/image.jpg')
+                spyOn(@model, "save").and.callThrough()
                 @handoutsEdit.$el.find('.save-button').click()
                 expect(@model.save).toHaveBeenCalled()
 
@@ -251,7 +251,7 @@ define ["js/views/course_info_handout", "js/views/course_info_update", "js/model
             it "does rewrite links after edit", ->
                 # Edit handouts and save.
                 @handoutsEdit.$el.find('.edit-button').click()
-                spyOn(@handoutsEdit.$codeMirror, 'getValue').andReturn('/static/image.jpg')
+                spyOn(@handoutsEdit.$codeMirror, 'getValue').and.returnValue('/static/image.jpg')
                 @handoutsEdit.$el.find('.save-button').click()
 
                 # Verify preview text.
diff --git a/cms/static/coffee/spec/views/module_edit_spec.coffee b/cms/static/coffee/spec/views/module_edit_spec.coffee
index 2c5ac48..a631c4d 100644
--- a/cms/static/coffee/spec/views/module_edit_spec.coffee
+++ b/cms/static/coffee/spec/views/module_edit_spec.coffee
@@ -29,7 +29,7 @@ define ["jquery", "common/js/components/utils/view_utils", "js/spec_helpers/edit
         </ul>
         """
         edit_helpers.installEditTemplates(true);
-        spyOn($, 'ajax').andReturn(@moduleData)
+        spyOn($, 'ajax').and.returnValue(@moduleData)
 
         @moduleEdit = new ModuleEdit(
           el: $(".component")
@@ -62,7 +62,7 @@ define ["jquery", "common/js/components/utils/view_utils", "js/spec_helpers/edit
             spyOn(@moduleEdit, 'loadDisplay')
             spyOn(@moduleEdit, 'delegateEvents')
             spyOn($.fn, 'append')
-            spyOn(ViewUtils, 'loadJavaScript').andReturn($.Deferred().resolve().promise());
+            spyOn(ViewUtils, 'loadJavaScript').and.returnValue($.Deferred().resolve().promise());
 
             window.MockXBlock = (runtime, element) ->
               return { }
@@ -70,7 +70,7 @@ define ["jquery", "common/js/components/utils/view_utils", "js/spec_helpers/edit
             window.loadedXBlockResources = undefined
 
             @moduleEdit.render()
-            $.ajax.mostRecentCall.args[0].success(
+            $.ajax.calls.mostRecent().args[0].success(
               html: '<div>Response html</div>'
               resources: [
                 ['hash1', {kind: 'text', mimetype: 'text/css', data: 'inline-css'}],
@@ -120,7 +120,7 @@ define ["jquery", "common/js/components/utils/view_utils", "js/spec_helpers/edit
 
             mockXBlockEditorHtml = readFixtures('mock/mock-xblock-editor.underscore')
 
-            $.ajax.mostRecentCall.args[0].success(
+            $.ajax.calls.mostRecent().args[0].success(
               html: mockXBlockEditorHtml
               resources: [
                 ['hash1', {kind: 'text', mimetype: 'text/css', data: 'inline-css'}],
@@ -161,14 +161,14 @@ define ["jquery", "common/js/components/utils/view_utils", "js/spec_helpers/edit
             expect($.fn.append).not.toHaveBeenCalledWith('not-head-html')
 
           it "doesn't reload resources", ->
-            count = $('head').append.callCount
-            $.ajax.mostRecentCall.args[0].success(
+            count = $('head').append.calls.count()
+            $.ajax.calls.mostRecent().args[0].success(
               html: '<div>Response html 2</div>'
               resources: [
                 ['hash1', {kind: 'text', mimetype: 'text/css', data: 'inline-css'}],
               ]
             )
-            expect($('head').append.callCount).toBe(count)
+            expect($('head').append.calls.count()).toBe(count)
 
         describe "loadDisplay", ->
           beforeEach ->
@@ -177,4 +177,4 @@ define ["jquery", "common/js/components/utils/view_utils", "js/spec_helpers/edit
 
           it "loads the .xmodule-display inside the module editor", ->
             expect(XBlock.initializeBlock).toHaveBeenCalled()
-            expect(XBlock.initializeBlock.mostRecentCall.args[0]).toBe($('.xblock-student_view'))
+            expect(XBlock.initializeBlock.calls.mostRecent().args[0].get(0)).toBe($('.xblock-student_view').get(0))
diff --git a/cms/static/coffee/spec/views/textbook_spec.coffee b/cms/static/coffee/spec/views/textbook_spec.coffee
index 0a326b2..01e6134 100644
--- a/cms/static/coffee/spec/views/textbook_spec.coffee
+++ b/cms/static/coffee/spec/views/textbook_spec.coffee
@@ -5,16 +5,6 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
     "js/spec_helpers/modal_helpers", "jasmine-stealth"],
 (Textbook, Chapter, ChapterSet, Course, TextbookSet, ShowTextbook, EditTextbook, ListTextbooks, EditChapter, Prompt, Notification, ViewUtils, AjaxHelpers, modal_helpers) ->
 
-    beforeEach ->
-        # remove this when we upgrade jasmine-jquery
-        @addMatchers
-            toContainText: (text) ->
-                trimmedText = $.trim(@actual.text())
-                if text and $.isFunction(text.test)
-                    return text.test(trimmedText)
-                else
-                    return trimmedText.indexOf(text) != -1;
-
     describe "ShowTextbook", ->
         tpl = readFixtures('show-textbook.underscore')
 
@@ -23,12 +13,12 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
             appendSetFixtures(sandbox({id: "page-notification"}))
             appendSetFixtures(sandbox({id: "page-prompt"}))
             @model = new Textbook({name: "Life Sciences", id: "0life-sciences"})
-            spyOn(@model, "destroy").andCallThrough()
+            spyOn(@model, "destroy").and.callThrough()
             @collection = new TextbookSet([@model])
             @view = new ShowTextbook({model: @model})
 
-            @promptSpies = spyOnConstructor(Prompt, "Warning", ["show", "hide"])
-            @promptSpies.show.andReturn(@promptSpies)
+            @promptSpies = jasmine.stealth.spyOnConstructor(Prompt, "Warning", ["show", "hide"])
+            @promptSpies.show.and.returnValue(@promptSpies)
             window.course = new Course({
                 id: "5",
                 name: "Course Name",
@@ -53,7 +43,7 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
             it "should pop a delete confirmation when the delete button is clicked", ->
                 @view.render().$(".delete").click()
                 expect(@promptSpies.constructor).toHaveBeenCalled()
-                ctorOptions = @promptSpies.constructor.mostRecentCall.args[0]
+                ctorOptions = @promptSpies.constructor.calls.mostRecent().args[0]
                 expect(ctorOptions.title).toMatch(/Life Sciences/)
                 # hasn't actually been removed
                 expect(@model.destroy).not.toHaveBeenCalled()
@@ -73,9 +63,9 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
 
         describe "AJAX", ->
             beforeEach ->
-                @savingSpies = spyOnConstructor(Notification, "Mini",
+                @savingSpies = jasmine.stealth.spyOnConstructor(Notification, "Mini",
                     ["show", "hide"])
-                @savingSpies.show.andReturn(@savingSpies)
+                @savingSpies.show.and.returnValue(@savingSpies)
                 CMS.URL.TEXTBOOKS = "/textbooks"
 
             afterEach ->
@@ -85,7 +75,7 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
                 requests = AjaxHelpers["requests"](this)
 
                 @view.render().$(".delete").click()
-                ctorOptions = @promptSpies.constructor.mostRecentCall.args[0]
+                ctorOptions = @promptSpies.constructor.calls.mostRecent().args[0]
                 # run the primary function to indicate confirmation
                 ctorOptions.actions.primary.click(@promptSpies)
                 # AJAX request has been sent, but not yet returned
@@ -94,7 +84,7 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
                 expect(@savingSpies.constructor).toHaveBeenCalled()
                 expect(@savingSpies.show).toHaveBeenCalled()
                 expect(@savingSpies.hide).not.toHaveBeenCalled()
-                savingOptions = @savingSpies.constructor.mostRecentCall.args[0]
+                savingOptions = @savingSpies.constructor.calls.mostRecent().args[0]
                 expect(savingOptions.title).toMatch(/Deleting/)
                 # return a success response
                 requests[0].respond(200)
@@ -114,7 +104,7 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
                 @collection = new TextbookSet()
                 @collection.add(@model)
                 @view = new EditTextbook({model: @model})
-                spyOn(@view, 'render').andCallThrough()
+                spyOn(@view, 'render').and.callThrough()
 
             it "should render properly", ->
                 @view.render()
@@ -209,10 +199,16 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
             expect(ViewUtils.setScrollOffset).toHaveBeenCalledWith($sectionEl, 0)
 
         it "should focus first input element of newly added textbook", ->
-            spyOn(jQuery.fn, 'focus').andCallThrough()
-            @addMatchers
-                toHaveBeenCalledOnJQueryObject: (actual, expected) ->
-                        pass: actual.calls && actual.calls.mostRecent() && actual.calls.mostRecent().object[0] == expected[0]
+            spyOn(jQuery.fn, 'focus').and.callThrough()
+            jasmine.addMatchers
+                toHaveBeenCalledOnJQueryObject: () ->
+                    return {
+                        compare: (actual, expected) ->
+                            return {
+                                pass: actual.calls && actual.calls.mostRecent() &&
+                                  actual.calls.mostRecent().object[0] == expected[0]
+                            }
+                        }
             @view.$(".new-button").click()
             $inputEl = @view.$el.find('section:last input:first')
             expect($inputEl.length).toEqual(1)
@@ -229,13 +225,13 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
 #        beforeEach ->
 #            setFixtures($("<script>", {id: "no-textbooks-tpl", type: "text/template"}).text(noTextbooksTpl))
 #            @showSpies = spyOnConstructor("ShowTextbook", ["render"])
-#            @showSpies.render.andReturn(@showSpies) # equivalent of `return this`
+#            @showSpies.render.and.returnValue(@showSpies) # equivalent of `return this`
 #            showEl = $("<li>")
 #            @showSpies.$el = showEl
 #            @showSpies.el = showEl.get(0)
 #            @editSpies = spyOnConstructor("EditTextbook", ["render"])
 #            editEl = $("<li>")
-#            @editSpies.render.andReturn(@editSpies)
+#            @editSpies.render.and.returnValue(@editSpies)
 #            @editSpies.$el = editEl
 #            @editSpies.el= editEl.get(0)
 #
@@ -304,7 +300,7 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
             @collection = new ChapterSet()
             @collection.add(@model)
             @view = new EditChapter({model: @model})
-            spyOn(@view, "remove").andCallThrough()
+            spyOn(@view, "remove").and.callThrough()
             CMS.URL.UPLOAD_ASSET = "/upload"
             window.course = new Course({name: "abcde"})
 
@@ -324,10 +320,10 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
 
 #        it "can open an upload dialog", ->
 #            uploadSpies = spyOnConstructor("UploadDialog", ["show", "el"])
-#            uploadSpies.show.andReturn(uploadSpies)
+#            uploadSpies.show.and.returnValue(uploadSpies)
 #
 #            @view.render().$(".action-upload").click()
-#            ctorOptions = uploadSpies.constructor.mostRecentCall.args[0]
+#            ctorOptions = uploadSpies.constructor.calls.mostRecent().args[0]
 #            expect(ctorOptions.model.get('title')).toMatch(/abcde/)
 #            expect(typeof ctorOptions.onSuccess).toBe('function')
 #            expect(uploadSpies.show).toHaveBeenCalled()
diff --git a/cms/static/coffee/spec/views/upload_spec.coffee b/cms/static/coffee/spec/views/upload_spec.coffee
index 07d9708..06964f8 100644
--- a/cms/static/coffee/spec/views/upload_spec.coffee
+++ b/cms/static/coffee/spec/views/upload_spec.coffee
@@ -18,16 +18,16 @@ define ["js/models/uploads", "js/views/uploads", "js/models/chapter", "common/js
               onSuccess: (response) =>
                 dialogResponse.push(response.response)
             )
-            spyOn(@view, 'remove').andCallThrough()
+            spyOn(@view, 'remove').and.callThrough()
 
             # create mock file input, so that we aren't subject to browser restrictions
             @mockFiles = []
             mockFileInput = jasmine.createSpy('mockFileInput')
             mockFileInput.files = @mockFiles
             jqMockFileInput = jasmine.createSpyObj('jqMockFileInput', ['get', 'replaceWith'])
-            jqMockFileInput.get.andReturn(mockFileInput)
+            jqMockFileInput.get.and.returnValue(mockFileInput)
             realMethod = @view.$
-            spyOn(@view, "$").andCallFake (selector) ->
+            spyOn(@view, "$").and.callFake (selector) ->
                 if selector == "input[type=file]"
                     jqMockFileInput
                 else
@@ -41,7 +41,7 @@ define ["js/models/uploads", "js/views/uploads", "js/models/chapter", "common/js
         describe "Basic", ->
             it "should render without a file selected", ->
                 @view.render()
-                expect(@view.$el).toContain("input[type=file]")
+                expect(@view.$el).toContainElement("input[type=file]")
                 expect(@view.$(".action-upload")).toHaveClass("disabled")
 
             it "should render with a PDF selected", ->
@@ -49,8 +49,8 @@ define ["js/models/uploads", "js/views/uploads", "js/models/chapter", "common/js
                 @mockFiles.push(file)
                 @model.set("selectedFile", file)
                 @view.render()
-                expect(@view.$el).toContain("input[type=file]")
-                expect(@view.$el).not.toContain("#upload_error")
+                expect(@view.$el).toContainElement("input[type=file]")
+                expect(@view.$el).not.toContainElement("#upload_error")
                 expect(@view.$(".action-upload")).not.toHaveClass("disabled")
 
             it "should render an error with an invalid file type selected", ->
@@ -58,8 +58,8 @@ define ["js/models/uploads", "js/views/uploads", "js/models/chapter", "common/js
                 @mockFiles.push(file)
                 @model.set("selectedFile", file)
                 @view.render()
-                expect(@view.$el).toContain("input[type=file]")
-                expect(@view.$el).toContain("#upload_error")
+                expect(@view.$el).toContainElement("input[type=file]")
+                expect(@view.$el).toContainElement("#upload_error")
                 expect(@view.$(".action-upload")).toHaveClass("disabled")
 
             it "should render an error with an invalid file type after a correct file type selected", ->
@@ -70,12 +70,12 @@ define ["js/models/uploads", "js/views/uploads", "js/models/chapter", "common/js
 
                 event.target = {"files": [correctFile]}
                 @view.selectFile(event)
-                expect(@view.$el).toContain("input[type=file]")
-                expect(@view.$el).not.toContain("#upload_error")
+                expect(@view.$el).toContainElement("input[type=file]")
+                expect(@view.$el).not.toContainElement("#upload_error")
                 expect(@view.$(".action-upload")).not.toHaveClass("disabled")
 
                 realMethod = @model.set
-                spyOn(@model, "set").andCallFake (data) ->
+                spyOn(@model, "set").and.callFake (data) ->
                   if data.selectedFile != undefined
                     this.attributes.selectedFile = data.selectedFile
                     this.changed = {}
@@ -84,8 +84,8 @@ define ["js/models/uploads", "js/views/uploads", "js/models/chapter", "common/js
 
                 event.target = {"files": [inCorrectFile]}
                 @view.selectFile(event)
-                expect(@view.$el).toContain("input[type=file]")
-                expect(@view.$el).toContain("#upload_error")
+                expect(@view.$el).toContainElement("input[type=file]")
+                expect(@view.$el).toContainElement("#upload_error")
                 expect(@view.$(".action-upload")).toHaveClass("disabled")
 
         describe "Uploads", ->
diff --git a/cms/static/js/certificates/spec/custom_matchers.js b/cms/static/js/certificates/spec/custom_matchers.js
index 64b7e9f..43b11bf 100644
--- a/cms/static/js/certificates/spec/custom_matchers.js
+++ b/cms/static/js/certificates/spec/custom_matchers.js
@@ -3,29 +3,21 @@
 
 define(['jquery'], function($) { // jshint ignore:line
     'use strict';
-    return function (that) {
-        that.addMatchers({
-
-            toContainText: function (text) {
-                // Assert the value being tested has text which matches the provided text
-                var trimmedText = $.trim($(this.actual).text());
-                if (text && $.isFunction(text.test)) {
-                    return text.test(trimmedText);
-                } else {
-                    return trimmedText.indexOf(text) !== -1;
-                }
-            },
-
-            toBeCorrectValuesInModel: function (values) {
+    return function () {
+        jasmine.addMatchers({
+            toBeCorrectValuesInModel: function () {
                 // Assert the value being tested has key values which match the provided values
-                return _.every(values, function (value, key) {
-                    return this.actual.get(key) === value;
-                }.bind(this));
-            },
+                return {
+                    compare: function (actual, values) {
+                        var passed = _.every(values, function (value, key) {
+                            return actual.get(key) === value;
+                        }.bind(this));
 
-            toBeInstanceOf: function(expected) {
-                // Assert the type of the value being tested matches the provided type
-                return this.actual instanceof expected;
+                        return {
+                            pass: passed
+                        };
+                    }
+                };
             }
         });
     };
diff --git a/cms/static/js/certificates/spec/views/certificate_details_spec.js b/cms/static/js/certificates/spec/views/certificate_details_spec.js
index daec1ec..2552e52 100644
--- a/cms/static/js/certificates/spec/views/certificate_details_spec.js
+++ b/cms/static/js/certificates/spec/views/certificate_details_spec.js
@@ -47,27 +47,6 @@ function(_, Course, CertificatesCollection, CertificateModel, CertificateDetails
         ViewHelpers.verifyPromptHidden(promptSpy);
     };
 
-    beforeEach(function() {
-        window.course = new Course({
-            id: '5',
-            name: 'Course Name',
-            url_name: 'course_name',
-            org: 'course_org',
-            num: 'course_num',
-            revision: 'course_rev'
-        });
-        window.certWebPreview = new CertificatePreview({
-            course_modes: ['honor', 'test'],
-            certificate_web_view_url: '/users/1/courses/orgX/009/2016'
-        });
-        window.CMS.User = {isGlobalStaff: true};
-    });
-
-    afterEach(function() {
-        delete window.course;
-        delete window.CMS.User;
-    });
-
     describe('Certificate Details Spec:', function() {
         var setValuesToInputs = function (view, values) {
             _.each(values, function (value, selector) {
@@ -81,6 +60,20 @@ function(_, Course, CertificatesCollection, CertificateModel, CertificateDetails
         beforeEach(function() {
             TemplateHelpers.installTemplates(['certificate-details', 'signatory-details', 'signatory-editor', 'signatory-actions'], true);
 
+            window.course = new Course({
+                id: '5',
+                name: 'Course Name',
+                url_name: 'course_name',
+                org: 'course_org',
+                num: 'course_num',
+                revision: 'course_rev'
+            });
+            window.certWebPreview = new CertificatePreview({
+                course_modes: ['honor', 'test'],
+                certificate_web_view_url: '/users/1/courses/orgX/009/2016'
+            });
+            window.CMS.User = {isGlobalStaff: true};
+
             this.newModelOptions = {add: true};
             this.model = new CertificateModel({
                 name: 'Test Name',
@@ -97,7 +90,13 @@ function(_, Course, CertificatesCollection, CertificateModel, CertificateDetails
                 model: this.model
             });
             appendSetFixtures(this.view.render().el);
-            CustomMatchers(this); // jshint ignore:line
+            CustomMatchers(); // jshint ignore:line
+        });
+
+        afterEach(function() {
+            delete window.course;
+            delete window.certWebPreview;
+            delete window.CMS.User;
         });
 
         describe('The Certificate Details view', function() {
diff --git a/cms/static/js/certificates/spec/views/certificate_editor_spec.js b/cms/static/js/certificates/spec/views/certificate_editor_spec.js
index 3893986..f2780d0 100644
--- a/cms/static/js/certificates/spec/views/certificate_editor_spec.js
+++ b/cms/static/js/certificates/spec/views/certificate_editor_spec.js
@@ -18,7 +18,7 @@ function(_, Course, CertificateModel, SignatoryModel, CertificatesCollection, Ce
          Notification, AjaxHelpers, TemplateHelpers, ViewHelpers, ValidationHelpers, CustomMatchers) {
     'use strict';
 
-    var MAX_SIGNATORIES = 100;
+    var MAX_SIGNATORIES_LIMIT = 10;
     var SELECTORS = {
         detailsView: '.certificate-details',
         editView: '.certificate-edit',
@@ -76,23 +76,6 @@ function(_, Course, CertificateModel, SignatoryModel, CertificatesCollection, Ce
         AjaxHelpers.respondWithJson(requests, {asset: {url: file_path}});
     };
 
-    beforeEach(function() {
-        window.course = new Course({
-            id: '5',
-            name: 'Course Name',
-            url_name: 'course_name',
-            org: 'course_org',
-            num: 'course_num',
-            revision: 'course_rev'
-        });
-        window.CMS.User = {isGlobalStaff: true};
-    });
-
-    afterEach(function() {
-        delete window.course;
-        delete window.CMS.User;
-    });
-
     describe('Certificate editor view', function() {
         var setValuesToInputs = function (view, values) {
             _.each(values, function (value, selector) {
@@ -109,6 +92,16 @@ function(_, Course, CertificateModel, SignatoryModel, CertificatesCollection, Ce
         beforeEach(function() {
             TemplateHelpers.installTemplates(['certificate-editor', 'signatory-editor'], true);
 
+            window.course = new Course({
+                id: '5',
+                name: 'Course Name',
+                url_name: 'course_name',
+                org: 'course_org',
+                num: 'course_num',
+                revision: 'course_rev'
+            });
+            window.CMS.User = {isGlobalStaff: true};
+
             this.newModelOptions = {add: true};
             this.model = new CertificateModel({
                 name: 'Test Name',
@@ -122,10 +115,16 @@ function(_, Course, CertificateModel, SignatoryModel, CertificatesCollection, Ce
             });
             this.model.set('id', 0);
             this.view = new CertificateEditorView({
-                model: this.model
+                model: this.model,
+                max_signatories_limit: MAX_SIGNATORIES_LIMIT
             });
             appendSetFixtures(this.view.render().el);
-            CustomMatchers(this); // jshint ignore:line
+            CustomMatchers(); // jshint ignore:line
+        });
+
+        afterEach(function() {
+            delete window.course;
+            delete window.CMS.User;
         });
 
         describe('Basic', function () {
@@ -198,8 +197,8 @@ function(_, Course, CertificateModel, SignatoryModel, CertificatesCollection, Ce
                 expect(this.collection.length).toBe(1);
             });
 
-            it('user can only add signatories up to max 100', function() {
-                for(var i = 1; i < MAX_SIGNATORIES ; i++) {
+            it('user can only add signatories up to limit', function() {
+                for(var i = 1; i < MAX_SIGNATORIES_LIMIT ; i++) {
                     this.view.$(SELECTORS.addSignatoryButton).click();
                 }
                 expect(this.view.$(SELECTORS.addSignatoryButton)).toHaveClass('disableClick');
@@ -215,7 +214,7 @@ function(_, Course, CertificateModel, SignatoryModel, CertificatesCollection, Ce
 
             it('user can add signatories when signatory reached the upper limit But after deleting a signatory',
                 function() {
-                    for(var i = 1; i < MAX_SIGNATORIES ; i++) {
+                    for(var i = 1; i < MAX_SIGNATORIES_LIMIT ; i++) {
                         this.view.$(SELECTORS.addSignatoryButton).click();
                     }
                     expect(this.view.$(SELECTORS.addSignatoryButton)).toHaveClass('disableClick');
@@ -274,7 +273,7 @@ function(_, Course, CertificateModel, SignatoryModel, CertificatesCollection, Ce
                 var signatory = this.model.get('signatories').at(0);
                 var signatory_url = '/certificates/signatory';
                 signatory.url = signatory_url;
-                spyOn(signatory, "isNew").andReturn(false);
+                spyOn(signatory, "isNew").and.returnValue(false);
                 var text = 'Delete "'+ signatory.get('name') +'" from the list of signatories?';
                 clickDeleteItem(this, text, SELECTORS.signatoryDeleteButton + ':first', signatory_url);
                 expect(this.model.get('signatories').length).toEqual(total_signatories - 1);
@@ -283,7 +282,7 @@ function(_, Course, CertificateModel, SignatoryModel, CertificatesCollection, Ce
             it('can cancel deletion of signatories', function() {
                 this.view.$(SELECTORS.addSignatoryButton).click();
                 var signatory = this.model.get('signatories').at(0);
-                spyOn(signatory, "isNew").andReturn(false);
+                spyOn(signatory, "isNew").and.returnValue(false);
                 // add one more signatory
                 this.view.$(SELECTORS.addSignatoryButton).click();
                 var total_signatories = this.model.get('signatories').length;
diff --git a/cms/static/js/certificates/spec/views/certificate_preview_spec.js b/cms/static/js/certificates/spec/views/certificate_preview_spec.js
index e8bcd6b..2c03c7b 100644
--- a/cms/static/js/certificates/spec/views/certificate_preview_spec.js
+++ b/cms/static/js/certificates/spec/views/certificate_preview_spec.js
@@ -18,23 +18,6 @@ function(_, $, Course, CertificatePreview, TemplateHelpers, ViewHelpers, AjaxHel
         preview_certificate: '.preview-certificate-link'
     };
 
-    beforeEach(function() {
-        window.course = new Course({
-            id: '5',
-            name: 'Course Name',
-            url_name: 'course_name',
-            org: 'course_org',
-            num: 'course_num',
-            revision: 'course_rev'
-        });
-        window.CMS.User = {isGlobalStaff: true};
-    });
-
-    afterEach(function() {
-        delete window.course;
-        delete window.CMS.User;
-    });
-
     describe('Certificate Web Preview Spec:', function() {
 
         var selectDropDownByText = function ( element, value ) {
@@ -45,8 +28,18 @@ function(_, $, Course, CertificatePreview, TemplateHelpers, ViewHelpers, AjaxHel
         };
 
         beforeEach(function() {
-            TemplateHelpers.installTemplate('certificate-web-preview', true);
             appendSetFixtures('<div class="preview-certificate nav-actions"></div>');
+
+            window.course = new Course({
+                id: '5',
+                name: 'Course Name',
+                url_name: 'course_name',
+                org: 'course_org',
+                num: 'course_num',
+                revision: 'course_rev'
+            });
+            window.CMS.User = {isGlobalStaff: true};
+
             this.view = new CertificatePreview({
                 el: $('.preview-certificate'),
                 course_modes: ['test1', 'test2', 'test3'],
@@ -57,6 +50,11 @@ function(_, $, Course, CertificatePreview, TemplateHelpers, ViewHelpers, AjaxHel
             appendSetFixtures(this.view.render().el);
         });
 
+        afterEach(function() {
+            delete window.course;
+            delete window.CMS.User;
+        });
+
         describe('Certificate preview', function() {
             it('course mode event should call when user choose a new mode', function () {
                 spyOn(this.view, 'courseModeChanged');
@@ -122,7 +120,7 @@ function(_, $, Course, CertificatePreview, TemplateHelpers, ViewHelpers, AjaxHel
 
             it('certificate web preview should be removed when method "remove" called', function () {
                 this.view.remove();
-                expect(this.view.el.innerHTML).toContain("");
+                expect(this.view.el.innerHTML).toBe('');
             });
 
             it('method "show" should call the render function', function () {
diff --git a/cms/static/js/certificates/spec/views/certificates_list_spec.js b/cms/static/js/certificates/spec/views/certificates_list_spec.js
index d5437af..f0ad00e 100644
--- a/cms/static/js/certificates/spec/views/certificates_list_spec.js
+++ b/cms/static/js/certificates/spec/views/certificates_list_spec.js
@@ -27,33 +27,28 @@ function(_, Course, CertificatesCollection, CertificateModel, CertificateDetails
         newCertificateButton: '.new-button'
     };
 
-    beforeEach(function() {
-        window.course = new Course({
-            id: '5',
-            name: 'Course Name',
-            url_name: 'course_name',
-            org: 'course_org',
-            num: 'course_num',
-            revision: 'course_rev'
-        });
-        window.certWebPreview = new CertificatePreview({
-            course_modes: ['honor', 'test'],
-            certificate_web_view_url: '/users/1/courses/orgX/009/2016'
-        });
-    });
-
-    afterEach(function() {
-        delete window.course;
-    });
-
     describe('Certificates list view', function() {
         var emptyMessage = 'You have not created any certificates yet.';
 
         beforeEach(function() {
             TemplateHelpers.installTemplates(
-                ['certificate-editor', 'certificate-edit', 'list']
+                ['certificate-editor', 'list']
             );
 
+            window.course = new Course({
+                id: '5',
+                name: 'Course Name',
+                url_name: 'course_name',
+                org: 'course_org',
+                num: 'course_num',
+                revision: 'course_rev'
+            });
+            window.certWebPreview = new CertificatePreview({
+                course_modes: ['honor', 'test'],
+                certificate_web_view_url: '/users/1/courses/orgX/009/2016'
+            });
+            window.CMS.User = {isGlobalStaff: true};
+
             this.model = new CertificateModel({
                 course_title: 'Test Course Title Override'
             }, {add: true});
@@ -61,11 +56,18 @@ function(_, Course, CertificatesCollection, CertificateModel, CertificateDetails
             this.collection = new CertificatesCollection([], {
                 certificateUrl: '/certificates/'+ window.course.id
             });
+            this.model.set('id', 0);
             this.view = new CertificatesListView({
                 collection: this.collection
             });
             appendSetFixtures(this.view.render().el);
-            CustomMatchers(this); // jshint ignore:line
+            CustomMatchers(); // jshint ignore:line
+        });
+
+        afterEach(function() {
+            delete window.course;
+            delete window.certWebPreview;
+            delete window.CMS.User;
         });
 
         describe('empty template', function () {
diff --git a/cms/static/js/certificates/views/certificate_details.js b/cms/static/js/certificates/views/certificate_details.js
index c1333c0..44823c5 100644
--- a/cms/static/js/certificates/views/certificate_details.js
+++ b/cms/static/js/certificates/views/certificate_details.js
@@ -9,9 +9,11 @@ define([ // jshint ignore:line
     'js/certificates/models/signatory',
     'js/certificates/views/signatory_details',
     'common/js/components/utils/view_utils',
-    'jquery.smoothScroll'
+    'jquery.smoothScroll',
+    'text!templates/certificate-details.underscore'
 ],
-function($, _, str, gettext, BaseView, SignatoryModel, SignatoryDetailsView, ViewUtils) {
+function($, _, str, gettext, BaseView, SignatoryModel, SignatoryDetailsView, ViewUtils, smoothScroll,
+         certificateDetailsTemplate) {
     'use strict';
     var CertificateDetailsView = BaseView.extend({
         tagName: 'div',
@@ -31,7 +33,6 @@ function($, _, str, gettext, BaseView, SignatoryModel, SignatoryDetailsView, Vie
         initialize: function() {
             // Set up the initial state of the attributes set for this model instance
             this.showDetails = true;
-            this.template = this.loadTemplate('certificate-details');
             this.listenTo(this.model, 'change', this.render);
         },
 
@@ -61,7 +62,7 @@ function($, _, str, gettext, BaseView, SignatoryModel, SignatoryDetailsView, Vie
                 index: this.model.collection.indexOf(this.model),
                 showDetails: this.showDetails || showDetails || false
             });
-            this.$el.html(this.template(attrs));
+            this.$el.html(_.template(certificateDetailsTemplate)(attrs));
             if(this.showDetails || showDetails) {
                 var self = this;
                 this.model.get("signatories").each(function (modelSignatory) {
diff --git a/cms/static/js/certificates/views/certificate_editor.js b/cms/static/js/certificates/views/certificate_editor.js
index 11cc32a..cff8eb1 100644
--- a/cms/static/js/certificates/views/certificate_editor.js
+++ b/cms/static/js/certificates/views/certificate_editor.js
@@ -7,10 +7,11 @@ define([ // jshint ignore:line
     'gettext',
     'js/views/list_item_editor',
     'js/certificates/models/signatory',
-    'js/certificates/views/signatory_editor'
+    'js/certificates/views/signatory_editor',
+    'text!templates/certificate-editor.underscore'
 ],
 function($, _, Backbone, gettext,
-         ListItemEditorView, SignatoryModel, SignatoryEditorView) {
+         ListItemEditorView, SignatoryModel, SignatoryEditorView, certificateEditorTemplate) {
     'use strict';
 
     // If signatories limit is required to specific value then we can change it.
@@ -41,14 +42,15 @@ function($, _, Backbone, gettext,
             ].join(' ');
         },
 
-        initialize: function() {
+        initialize: function(options) {
             // Set up the initial state of the attributes set for this model instance
             _.bindAll(this, "onSignatoryRemoved", "clearErrorMessage");
+            this.max_signatories_limit = options.max_signatories_limit || MAX_SIGNATORIES_LIMIT;
+            this.template = _.template(certificateEditorTemplate);
             this.eventAgg = _.extend({}, Backbone.Events);
             this.eventAgg.bind("onSignatoryRemoved", this.onSignatoryRemoved);
             this.eventAgg.bind("onSignatoryUpdated", this.clearErrorMessage);
             ListItemEditorView.prototype.initialize.call(this);
-            this.template = this.loadTemplate('certificate-editor');
         },
 
         onSignatoryRemoved: function() {
@@ -87,7 +89,7 @@ function($, _, Backbone, gettext,
 
         disableAddSignatoryButton: function() {
             // Disable the 'Add Signatory' link if the constraint has been met.
-            if(this.$(".signatory-edit-list > div.signatory-edit").length >= MAX_SIGNATORIES_LIMIT) {
+            if(this.$(".signatory-edit-list > div.signatory-edit").length >= this.max_signatories_limit) {
                 this.$(".action-add-signatory").addClass("disableClick");
             }
         },
diff --git a/cms/static/js/certificates/views/certificate_preview.js b/cms/static/js/certificates/views/certificate_preview.js
index a86ef20..70bf997 100644
--- a/cms/static/js/certificates/views/certificate_preview.js
+++ b/cms/static/js/certificates/views/certificate_preview.js
@@ -7,9 +7,10 @@ define([ // jshint ignore:line
     'gettext',
     'js/views/baseview',
     'common/js/components/utils/view_utils',
-    'common/js/components/views/feedback_notification'
+    'common/js/components/views/feedback_notification',
+    "text!templates/certificate-web-preview.underscore"
 ],
-function(_, gettext, BaseView, ViewUtils, NotificationView) {
+function(_, gettext, BaseView, ViewUtils, NotificationView, certificateWebPreviewTemplate) {
     'use strict';
     var CertificateWebPreview = BaseView.extend({
         el: $(".preview-certificate"),
@@ -23,11 +24,10 @@ function(_, gettext, BaseView, ViewUtils, NotificationView) {
             this.certificate_web_view_url = options.certificate_web_view_url;
             this.certificate_activation_handler_url = options.certificate_activation_handler_url;
             this.is_active = options.is_active;
-            this.template = this.loadTemplate('certificate-web-preview');
         },
 
         render: function () {
-            this.$el.html(this.template({
+            this.$el.html(_.template(certificateWebPreviewTemplate)({
                 course_modes: this.course_modes,
                 certificate_web_view_url: this.certificate_web_view_url,
                 is_active: this.is_active
diff --git a/cms/static/js/certificates/views/signatory_details.js b/cms/static/js/certificates/views/signatory_details.js
index 4f2e341..1f2385a 100644
--- a/cms/static/js/certificates/views/signatory_details.js
+++ b/cms/static/js/certificates/views/signatory_details.js
@@ -9,9 +9,12 @@ define([ // jshint ignore:line
     'js/utils/templates',
     'common/js/components/utils/view_utils',
     'js/views/baseview',
-    'js/certificates/views/signatory_editor'
+    'js/certificates/views/signatory_editor',
+    'text!templates/signatory-details.underscore',
+    'text!templates/signatory-actions.underscore'
 ],
-function ($, _, str, Backbone, gettext, TemplateUtils, ViewUtils, BaseView, SignatoryEditorView) {
+function ($, _, str, Backbone, gettext, TemplateUtils, ViewUtils, BaseView, SignatoryEditorView,
+          signatoryDetailsTemplate, signatoryActionsTemplate) {
     'use strict';
     var SignatoryDetailsView = BaseView.extend({
         tagName: 'div',
@@ -39,8 +42,6 @@ function ($, _, str, Backbone, gettext, TemplateUtils, ViewUtils, BaseView, Sign
                 isEditingAllCollections: false,
                 eventAgg: this.eventAgg
             });
-            this.template = this.loadTemplate('signatory-details');
-            this.signatory_action_template = this.loadTemplate('signatory-actions');
         },
 
         loadTemplate: function(name) {
@@ -52,7 +53,7 @@ function ($, _, str, Backbone, gettext, TemplateUtils, ViewUtils, BaseView, Sign
             // Retrieve the edit view for this model
             if (event && event.preventDefault) { event.preventDefault(); }
             this.$el.html(this.edit_view.render());
-            $(this.signatory_action_template()).appendTo(this.el);
+            $(_.template(signatoryActionsTemplate)()).appendTo(this.el);
             this.edit_view.delegateEvents();
             this.delegateEvents();
         },
@@ -93,7 +94,7 @@ function ($, _, str, Backbone, gettext, TemplateUtils, ViewUtils, BaseView, Sign
             var attributes = $.extend({}, this.model.attributes, {
                 signatory_number: this.model.collection.indexOf(this.model) + 1
             });
-            return $(this.el).html(this.template(attributes));
+            return $(this.el).html(_.template(signatoryDetailsTemplate)(attributes));
         }
     });
     return SignatoryDetailsView;
diff --git a/cms/static/js/certificates/views/signatory_editor.js b/cms/static/js/certificates/views/signatory_editor.js
index d446ea6..e1166ad 100644
--- a/cms/static/js/certificates/views/signatory_editor.js
+++ b/cms/static/js/certificates/views/signatory_editor.js
@@ -10,10 +10,12 @@ define([ // jshint ignore:line
     'common/js/components/views/feedback_prompt',
     'common/js/components/views/feedback_notification',
     'js/models/uploads',
-    'js/views/uploads'
+    'js/views/uploads',
+    'text!templates/signatory-editor.underscore'
 ],
 function ($, _, Backbone, gettext,
-          TemplateUtils, ViewUtils, PromptView, NotificationView, FileUploadModel, FileUploadDialog) {
+          TemplateUtils, ViewUtils, PromptView, NotificationView, FileUploadModel, FileUploadDialog,
+          signatoryEditorTemplate) {
     'use strict';
     var SignatoryEditorView = Backbone.View.extend({
         tagName: 'div',
@@ -41,7 +43,6 @@ function ($, _, Backbone, gettext,
             this.model.bind('change', this.render);
             this.eventAgg = options.eventAgg;
             this.isEditingAllCollections = options.isEditingAllCollections;
-            this.template = this.loadTemplate('signatory-editor');
         },
 
         getModelIndex: function(givenModel) {
@@ -77,7 +78,7 @@ function ($, _, Backbone, gettext,
                 is_editing_all_collections: this.isEditingAllCollections,
                 total_saved_signatories: this.getTotalSignatoriesOnServer()
             });
-            return $(this.el).html(this.template(attributes));
+            return $(this.el).html(_.template(signatoryEditorTemplate)(attributes));
         },
 
         setSignatoryName: function(event) {
diff --git a/cms/static/js/spec/models/group_configuration_spec.js b/cms/static/js/spec/models/group_configuration_spec.js
index f047fbb..5a441b8 100644
--- a/cms/static/js/spec/models/group_configuration_spec.js
+++ b/cms/static/js/spec/models/group_configuration_spec.js
@@ -1,41 +1,29 @@
 define([
     'backbone', 'coffee/src/main', 'js/models/group_configuration',
     'js/models/group', 'js/collections/group', 'squire'
-], function(
-    Backbone, main, GroupConfigurationModel, GroupModel, GroupCollection, Squire
-) {
+], function (Backbone, main, GroupConfigurationModel, GroupModel, GroupCollection, Squire) {
     'use strict';
-    beforeEach(function() {
-      this.addMatchers({
-        toBeInstanceOf: function(expected) {
-          return this.actual instanceof expected;
-        },
-        toBeEmpty: function() {
-            return this.actual.length === 0;
-        }
-      });
-    });
 
-    describe('GroupConfigurationModel', function() {
-        beforeEach(function() {
+    describe('GroupConfigurationModel', function () {
+        beforeEach(function () {
             main();
             this.model = new GroupConfigurationModel();
         });
 
-        describe('Basic', function() {
-            it('should have an empty name by default', function() {
+        describe('Basic', function () {
+            it('should have an empty name by default', function () {
                 expect(this.model.get('name')).toEqual('');
             });
 
-            it('should have an empty description by default', function() {
+            it('should have an empty description by default', function () {
                 expect(this.model.get('description')).toEqual('');
             });
 
-            it('should not show groups by default', function() {
+            it('should not show groups by default', function () {
                 expect(this.model.get('showGroups')).toBeFalsy();
             });
 
-            it('should have a collection with 2 groups by default', function() {
+            it('should have a collection with 2 groups by default', function () {
                 var groups = this.model.get('groups');
 
                 expect(groups).toBeInstanceOf(GroupCollection);
@@ -43,11 +31,11 @@ define([
                 expect(groups.at(1).get('name')).toBe('Group B');
             });
 
-            it('should have an empty usage by default', function() {
-                expect(this.model.get('usage')).toBeEmpty();
+            it('should have an empty usage by default', function () {
+                expect(this.model.get('usage').length).toBe(0);
             });
 
-            it('should be able to reset itself', function() {
+            it('should be able to reset itself', function () {
                 var originalName = 'Original Name',
                     model = new GroupConfigurationModel({name: originalName});
                 model.set({name: 'New Name'});
@@ -56,18 +44,18 @@ define([
                 expect(model.get('name')).toEqual(originalName);
             });
 
-            it('should be dirty after it\'s been changed', function() {
+            it('should be dirty after it\'s been changed', function () {
                 this.model.set('name', 'foobar');
 
                 expect(this.model.isDirty()).toBeTruthy();
             });
 
             describe('should not be dirty', function () {
-                it('by default', function() {
+                it('by default', function () {
                     expect(this.model.isDirty()).toBeFalsy();
                 });
 
-                it('after calling setOriginalAttributes', function() {
+                it('after calling setOriginalAttributes', function () {
                     this.model.set('name', 'foobar');
                     this.model.setOriginalAttributes();
 
@@ -76,13 +64,13 @@ define([
             });
         });
 
-        describe('Input/Output', function() {
-            var deepAttributes = function(obj) {
+        describe('Input/Output', function () {
+            var deepAttributes = function (obj) {
                 if (obj instanceof Backbone.Model) {
                     return deepAttributes(obj.attributes);
                 } else if (obj instanceof Backbone.Collection) {
                     return obj.map(deepAttributes);
-                } else if (_.isObject(obj)) {
+                } else if ($.isPlainObject(obj)) {
                     var attributes = {};
 
                     for (var prop in obj) {
@@ -96,7 +84,7 @@ define([
                 }
             };
 
-            it('should match server model to client model', function() {
+            it('should match server model to client model', function () {
                 var serverModelSpec = {
                         'id': 10,
                         'name': 'My Group Configuration',
@@ -139,32 +127,32 @@ define([
                         'usage': []
                     },
                     model = new GroupConfigurationModel(
-                        serverModelSpec, { parse: true }
+                        serverModelSpec, {parse: true}
                     );
 
                 expect(deepAttributes(model)).toEqual(clientModelSpec);
-                expect(model.toJSON()).toEqual(serverModelSpec);
+                expect(JSON.parse(JSON.stringify(model))).toEqual(serverModelSpec);
             });
         });
 
-        describe('Validation', function() {
-            it('requires a name', function() {
-                var model = new GroupConfigurationModel({ name: '' });
+        describe('Validation', function () {
+            it('requires a name', function () {
+                var model = new GroupConfigurationModel({name: ''});
 
                 expect(model.isValid()).toBeFalsy();
             });
 
-            it('can pass validation', function() {
+            it('can pass validation', function () {
                 // Note that two groups - Group A and Group B - are
                 // created by default.
-                var model = new GroupConfigurationModel({ name: 'foo' });
+                var model = new GroupConfigurationModel({name: 'foo'});
 
                 expect(model.isValid()).toBeTruthy();
             });
 
-            it('requires at least one group', function() {
-                var group1 = new GroupModel({ name: 'Group A' }),
-                    model = new GroupConfigurationModel({ name: 'foo', groups: [] });
+            it('requires at least one group', function () {
+                var group1 = new GroupModel({name: 'Group A'}),
+                    model = new GroupConfigurationModel({name: 'foo', groups: []});
 
                 expect(model.isValid()).toBeFalsy();
 
@@ -172,21 +160,21 @@ define([
                 expect(model.isValid()).toBeTruthy();
             });
 
-            it('requires a valid group', function() {
-                var model = new GroupConfigurationModel({ name: 'foo', groups: [{ name: '' }] });
+            it('requires a valid group', function () {
+                var model = new GroupConfigurationModel({name: 'foo', groups: [{name: ''}]});
 
                 expect(model.isValid()).toBeFalsy();
             });
 
-            it('requires all groups to be valid', function() {
-                var model = new GroupConfigurationModel({ name: 'foo', groups: [{ name: 'Group A' }, { name: '' }] });
+            it('requires all groups to be valid', function () {
+                var model = new GroupConfigurationModel({name: 'foo', groups: [{name: 'Group A'}, {name: ''}]});
 
                 expect(model.isValid()).toBeFalsy();
             });
 
-            it('requires all groups to have unique names', function() {
+            it('requires all groups to have unique names', function () {
                 var model = new GroupConfigurationModel({
-                    name: 'foo', groups: [{ name: 'Group A' }, { name: 'Group A' }]
+                    name: 'foo', groups: [{name: 'Group A'}, {name: 'Group A'}]
                 });
 
                 expect(model.isValid()).toBeFalsy();
@@ -194,91 +182,92 @@ define([
         });
     });
 
-    describe('GroupModel', function() {
-        beforeEach(function() {
+    describe('GroupModel', function () {
+        beforeEach(function () {
             this.collection = new GroupCollection([{}]);
             this.model = this.collection.at(0);
         });
 
-        describe('Basic', function() {
-            it('should have an empty name by default', function() {
+        describe('Basic', function () {
+            it('should have an empty name by default', function () {
                 expect(this.model.get('name')).toEqual('');
             });
 
-            it('should be empty by default', function() {
+            it('should be empty by default', function () {
                 expect(this.model.isEmpty()).toBeTruthy();
             });
         });
 
-        describe('Validation', function() {
-            it('requires a name', function() {
-                var model = new GroupModel({ name: '' });
+        describe('Validation', function () {
+            it('requires a name', function () {
+                var model = new GroupModel({name: ''});
 
                 expect(model.isValid()).toBeFalsy();
             });
 
-            it('can pass validation', function() {
-                var model = new GroupConfigurationModel({ name: 'foo' });
+            it('can pass validation', function () {
+                var model = new GroupConfigurationModel({name: 'foo'});
 
                 expect(model.isValid()).toBeTruthy();
             });
         });
     });
 
-    describe('GroupCollection', function() {
-        beforeEach(function() {
+    describe('GroupCollection', function () {
+        beforeEach(function () {
             this.collection = new GroupCollection();
         });
 
-        it('is empty by default', function() {
+        it('is empty by default', function () {
             expect(this.collection.isEmpty()).toBeTruthy();
         });
 
-        it('is empty if all groups are empty', function() {
-            this.collection.add([{ name: '' }, { name: '' }, { name: '' }]);
+        it('is empty if all groups are empty', function () {
+            this.collection.add([{name: ''}, {name: ''}, {name: ''}]);
 
             expect(this.collection.isEmpty()).toBeTruthy();
         });
 
-        it('is not empty if a group is not empty', function() {
+        it('is not empty if a group is not empty', function () {
             this.collection.add([
-                { name: '' }, { name: 'full' }, { name: '' }
+                {name: ''}, {name: 'full'}, {name: ''}
             ]);
 
             expect(this.collection.isEmpty()).toBeFalsy();
         });
 
         describe('getGroupId', function () {
-            var collection, injector, mockGettext, initializeGroupModel;
+            var collection, injector, mockGettext, initializeGroupModel, cleanUp;
 
             mockGettext = function (returnedValue) {
                 var injector = new Squire();
 
                 injector.mock('gettext', function () {
-                    return function () { return returnedValue; };
+                    return function () {
+                        return returnedValue;
+                    };
                 });
 
                 return injector;
             };
 
-            initializeGroupModel = function (dict, that) {
-                runs(function() {
-                    injector = mockGettext(dict);
-                    injector.require(['js/collections/group'],
-                    function(GroupCollection) {
+            initializeGroupModel = function (dict) {
+                var deferred = $.Deferred();
+
+                injector = mockGettext(dict);
+                injector.require(['js/collections/group'],
+                    function (GroupCollection) {
                         collection = new GroupCollection();
+                        deferred.resolve(collection);
                     });
-                });
 
-                waitsFor(function() {
-                    return collection;
-                }, 'GroupModel was not instantiated', 500);
+                return deferred.promise();
+            };
 
-                that.after(function () {
-                    collection = null;
-                    injector.clean();
-                    injector.remove();
-                });
+            cleanUp = function () {
+                collection = null;
+                injector.clean();
+                injector.remove();
             };
 
             it('returns correct ids', function () {
@@ -294,34 +283,46 @@ define([
                 expect(collection.getGroupId(475279)).toBe('AAAAZ');
             });
 
-            it('just 1 character in the dictionary', function () {
-                initializeGroupModel('1', this);
-                runs(function() {
-                    expect(collection.getGroupId(0)).toBe('1');
-                    expect(collection.getGroupId(1)).toBe('11');
-                    expect(collection.getGroupId(5)).toBe('111111');
-                });
+            it('just 1 character in the dictionary', function (done) {
+                initializeGroupModel('1')
+                    .then(function (collection) {
+                        expect(collection.getGroupId(0)).toBe('1');
+                        expect(collection.getGroupId(1)).toBe('11');
+                        expect(collection.getGroupId(5)).toBe('111111');
+                    })
+                    .always(function () {
+                        cleanUp();
+                        done();
+                    });
             });
 
-            it('allow to use unicode characters in the dict', function () {
-                initializeGroupModel('ö诶úeœ', this);
-                runs(function() {
-                    expect(collection.getGroupId(0)).toBe('ö');
-                    expect(collection.getGroupId(1)).toBe('诶');
-                    expect(collection.getGroupId(5)).toBe('öö');
-                    expect(collection.getGroupId(29)).toBe('œœ');
-                    expect(collection.getGroupId(30)).toBe('ööö');
-                    expect(collection.getGroupId(43)).toBe('öúe');
-                });
+            it('allow to use unicode characters in the dict', function (done) {
+                initializeGroupModel('ö诶úeœ')
+                    .then(function (collection) {
+                        expect(collection.getGroupId(0)).toBe('ö');
+                        expect(collection.getGroupId(1)).toBe('诶');
+                        expect(collection.getGroupId(5)).toBe('öö');
+                        expect(collection.getGroupId(29)).toBe('œœ');
+                        expect(collection.getGroupId(30)).toBe('ööö');
+                        expect(collection.getGroupId(43)).toBe('öúe');
+                    })
+                    .always(function () {
+                        cleanUp();
+                        done();
+                    });
             });
 
-            it('return initial value if dictionary is empty', function () {
-                initializeGroupModel('', this);
-                runs(function() {
-                    expect(collection.getGroupId(0)).toBe('0');
-                    expect(collection.getGroupId(5)).toBe('5');
-                    expect(collection.getGroupId(30)).toBe('30');
-                });
+            it('return initial value if dictionary is empty', function (done) {
+                initializeGroupModel('')
+                    .then(function (collection) {
+                        expect(collection.getGroupId(0)).toBe('0');
+                        expect(collection.getGroupId(5)).toBe('5');
+                        expect(collection.getGroupId(30)).toBe('30');
+                    })
+                    .always(function () {
+                        cleanUp();
+                        done();
+                    });
             });
         });
     });
diff --git a/cms/static/js/spec/utils/drag_and_drop_spec.js b/cms/static/js/spec/utils/drag_and_drop_spec.js
index e53a07e..cd06179 100644
--- a/cms/static/js/spec/utils/drag_and_drop_spec.js
+++ b/cms/static/js/spec/utils/drag_and_drop_spec.js
@@ -40,7 +40,7 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
                         left: $ele.offset().left
                     });
                     destination = ContentDragger.findDestination($ele, 1);
-                    expect(destination.ele).toBe($('#unit-2'));
+                    expect(destination.ele).toEqual($('#unit-2'));
                     expect(destination.attachMethod).toBe('before');
                 });
                 it("can drag and drop across section boundaries, with special handling for single sibling", function () {
@@ -52,17 +52,17 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
                         left: $ele.offset().left
                     });
                     destination = ContentDragger.findDestination($ele, 1);
-                    expect(destination.ele).toBe($unit4);
+                    expect(destination.ele).toEqual($unit4);
                     expect(destination.attachMethod).toBe('after');
                     destination = ContentDragger.findDestination($ele, -1);
-                    expect(destination.ele).toBe($unit4);
+                    expect(destination.ele).toEqual($unit4);
                     expect(destination.attachMethod).toBe('before');
                     $ele.offset({
                         top: $unit4.offset().top + $unit4.height() + 1,
                         left: $ele.offset().left
                     });
                     destination = ContentDragger.findDestination($ele, 0);
-                    expect(destination.ele).toBe($unit4);
+                    expect(destination.ele).toEqual($unit4);
                     expect(destination.attachMethod).toBe('after');
                     $unit0 = $('#unit-0');
                     $ele.offset({
@@ -70,7 +70,7 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
                         left: $ele.offset().left
                     });
                     destination = ContentDragger.findDestination($ele, 0);
-                    expect(destination.ele).toBe($unit0);
+                    expect(destination.ele).toEqual($unit0);
                     expect(destination.attachMethod).toBe('before');
                 });
                 it("can drop before the first element, even if element being dragged is\nslightly before the first element", function () {
@@ -81,7 +81,7 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
                         left: $ele.offset().left
                     });
                     destination = ContentDragger.findDestination($ele, -1);
-                    expect(destination.ele).toBe($('#subsection-0'));
+                    expect(destination.ele).toEqual($('#subsection-0'));
                     expect(destination.attachMethod).toBe('before');
                 });
                 it("can drag and drop across section boundaries, with special handling for last element", function () {
@@ -92,14 +92,14 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
                         left: $ele.offset().left
                     });
                     destination = ContentDragger.findDestination($ele, -1);
-                    expect(destination.ele).toBe($('#unit-3'));
+                    expect(destination.ele).toEqual($('#unit-3'));
                     expect(destination.attachMethod).toBe('after');
                     $ele.offset({
                         top: $('#unit-3').offset().top + 4,
                         left: $ele.offset().left
                     });
                     destination = ContentDragger.findDestination($ele, -1);
-                    expect(destination.ele).toBe($('#unit-3'));
+                    expect(destination.ele).toEqual($('#unit-3'));
                     expect(destination.attachMethod).toBe('before');
                 });
                 it("can drop past the last element, even if element being dragged is\nslightly before/taller then the last element", function () {
@@ -110,7 +110,7 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
                         left: $ele.offset().left
                     });
                     destination = ContentDragger.findDestination($ele, 1);
-                    expect(destination.ele).toBe($('#subsection-4'));
+                    expect(destination.ele).toEqual($('#subsection-4'));
                     expect(destination.attachMethod).toBe('after');
                 });
                 it("can drag into an empty list", function () {
@@ -121,7 +121,7 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
                         left: $ele.offset().left
                     });
                     destination = ContentDragger.findDestination($ele, 1);
-                    expect(destination.ele).toBe($('#subsection-list-3'));
+                    expect(destination.ele).toEqual($('#subsection-list-3'));
                     expect(destination.attachMethod).toBe('prepend');
                 });
                 it("reports a null destination on a failed drag", function () {
@@ -146,8 +146,8 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
                         left: $ele.offset().left
                     });
                     destination = ContentDragger.findDestination($ele, 1);
-                    expect(destination.ele).toBe($('#subsection-list-2'));
-                    expect(destination.parentList).toBe($('#subsection-2'));
+                    expect(destination.ele).toEqual($('#subsection-list-2'));
+                    expect(destination.parentList).toEqual($('#subsection-2'));
                     expect(destination.attachMethod).toBe('prepend');
                 });
             });
@@ -176,7 +176,7 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
             });
             describe("onDragMove", function () {
                 beforeEach(function () {
-                    this.redirectSpy = spyOn(window, 'scrollBy').andCallThrough();
+                    this.redirectSpy = spyOn(window, 'scrollBy').and.callThrough();
                 });
                 it("adds the correct CSS class to the drop destination", function () {
                     var $ele, dragX, dragY;
@@ -239,7 +239,7 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
                     this.reorderSpy = spyOn(ContentDragger, 'handleReorder');
                 });
                 afterEach(function () {
-                    this.reorderSpy.reset();
+                    this.reorderSpy.calls.reset();
                 });
                 it("calls handleReorder on a successful drag", function () {
                     ContentDragger.dragState.dropDestination = $('#unit-2');
@@ -279,7 +279,7 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
                     expect($('#subsection-1')).not.toHaveClass('expand-on-drop');
                 });
                 it("expands a collapsed element when something is dropped in it", function () {
-                    expandElementSpy = spyOn(ContentDragger, 'expandElement').andCallThrough();
+                    var expandElementSpy = spyOn(ContentDragger, 'expandElement').and.callThrough();
                     expect(expandElementSpy).not.toHaveBeenCalled();
                     expect($('#subsection-2').data('ensureChildrenRendered')).not.toHaveBeenCalled();
 
@@ -301,8 +301,8 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
             });
             describe("AJAX", function () {
                 beforeEach(function () {
-                    this.savingSpies = spyOnConstructor(Notification, "Mini", ["show", "hide"]);
-                    this.savingSpies.show.andReturn(this.savingSpies);
+                    this.savingSpies = jasmine.stealth.spyOnConstructor(Notification, "Mini", ["show", "hide"]);
+                    this.savingSpies.show.and.returnValue(this.savingSpies);
                     this.clock = sinon.useFakeTimers();
                 });
                 afterEach(function () {
@@ -327,7 +327,7 @@ define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notificat
                     expect(this.savingSpies.constructor).toHaveBeenCalled();
                     expect(this.savingSpies.show).toHaveBeenCalled();
                     expect(this.savingSpies.hide).not.toHaveBeenCalled();
-                    savingOptions = this.savingSpies.constructor.mostRecentCall.args[0];
+                    savingOptions = this.savingSpies.constructor.calls.mostRecent().args[0];
                     expect(savingOptions.title).toMatch(/Saving/);
                     expect($('#unit-1')).toHaveClass('was-dropped');
                     expect(request.requestBody).toEqual('{"children":["fourth-unit-id","first-unit-id"]}');
diff --git a/cms/static/js/spec/utils/handle_iframe_binding_spec.js b/cms/static/js/spec/utils/handle_iframe_binding_spec.js
index 5311950..cec051d 100644
--- a/cms/static/js/spec/utils/handle_iframe_binding_spec.js
+++ b/cms/static/js/spec/utils/handle_iframe_binding_spec.js
@@ -39,7 +39,9 @@ function ($, _, IframeBinding) {
             //after calling iframeBinding function: src url of iframes should have "wmode=transparent" in its querystring
             //and embed objects should have "wmode='transparent'" as an attribute
             expect(iframe_html).toContain('<iframe src="http://www.youtube.com/embed/NHd27UvY-lw?wmode=transparent"');
-            expect(iframe_html).toContain('<embed wmode="transparent" type="application/x-shockwave-flash" src="http://www.youtube.com/embed/NHd27UvY-lw"');
+            expect(iframe_html).toContainHtml(
+              '<embed wmode="transparent" type="application/x-shockwave-flash"' +
+              ' src="http://www.youtube.com/embed/NHd27UvY-lw"');
         });
 
         it("does not modify src url of DOM iframe if it is empty", function () {
diff --git a/cms/static/js/spec/video/file_uploader_editor_spec.js b/cms/static/js/spec/video/file_uploader_editor_spec.js
index c4348f4..34fcfdb 100644
--- a/cms/static/js/spec/video/file_uploader_editor_spec.js
+++ b/cms/static/js/spec/video/file_uploader_editor_spec.js
@@ -30,61 +30,104 @@ function ($, _, Squire) {
 
         var createPromptSpy = function (name) {
             var spy = jasmine.createSpyObj(name, ['constructor', 'show', 'hide']);
-            spy.constructor.andReturn(spy);
-            spy.show.andReturn(spy);
-            spy.extend = jasmine.createSpy().andReturn(spy.constructor);
+            spy.constructor.and.returnValue(spy);
+            spy.show.and.returnValue(spy);
+            spy.extend = jasmine.createSpy().and.returnValue(spy.constructor);
 
             return spy;
         };
 
-        beforeEach(function () {
+        beforeEach(function (done) {
             self = this;
 
-            this.addMatchers({
-                assertValueInView: function(expected) {
-                    var value = this.actual.getValueFromEditor();
-                    return this.env.equals_(value, expected);
+            jasmine.addMatchers({
+                assertValueInView: function() {
+                    return {
+                        compare: function (actual, expected) {
+                            var value = actual.getValueFromEditor(),
+                            passed = _.isEqual(value, expected);
+
+                            return {
+                                pass: passed,
+                                message: 'Expected ' + actual + (passed ? '' : ' not') + ' to equal ' + expected
+                            };
+                        }
+                    };
                 },
-                assertCanUpdateView: function (expected) {
-                    var view = this.actual,
-                        value;
-
-                    view.setValueInEditor(expected);
-                    value = view.getValueFromEditor();
-
-                    return this.env.equals_(value, expected);
+                assertCanUpdateView: function () {
+                    return {
+                        compare: function (actual, expected) {
+                            var view = actual,
+                                value,
+                                passed;
+
+                            view.setValueInEditor(expected);
+                            value = view.getValueFromEditor();
+
+                            passed = _.isEqual(value, expected);
+
+                            return {
+                                pass: passed,
+                                message: 'Expected ' + actual + (passed ? '' : ' not') + ' to equal ' + expected
+                            };
+                        }
+                    };
                 },
-                assertClear: function (modelValue) {
-                    var env = this.env,
-                        view = this.actual,
-                        model = view.model;
-
-                    return model.getValue() === null &&
-                           env.equals_(model.getDisplayValue(), modelValue) &&
-                           env.equals_(view.getValueFromEditor(), modelValue);
+                assertClear: function () {
+                    return {
+                        compare: function (actual, modelValue) {
+                            var view = actual,
+                                model = view.model,
+                                passed;
+
+                            passed = model.getValue() === null &&
+                                _.isEqual(model.getDisplayValue(), modelValue) &&
+                                _.isEqual(view.getValueFromEditor(), modelValue);
+
+                            return {
+                                pass: passed
+                            };
+                        }
+                    };
                 },
-                assertUpdateModel: function (originalValue, newValue) {
-                    var env = this.env,
-                        view = this.actual,
-                        model = view.model,
-                        expectOriginal;
-
-                    view.setValueInEditor(newValue);
-                    expectOriginal = env.equals_(model.getValue(), originalValue);
-                    view.updateModel();
-
-                    return expectOriginal &&
-                           env.equals_(model.getValue(), newValue);
+                assertUpdateModel: function () {
+                    return {
+                        compare: function (actual, originalValue, newValue) {
+                            var view = actual,
+                                model = view.model,
+                                expectOriginal,
+                                passed;
+
+                            view.setValueInEditor(newValue);
+                            expectOriginal = _.isEqual(model.getValue(), originalValue);
+                            view.updateModel();
+
+                            passed = expectOriginal &&
+                                _.isEqual(model.getValue(), newValue);
+
+                            return {
+                                pass: passed
+                            };
+                        }
+                    };
                 },
-                verifyButtons: function (upload, download, index) {
-                    var view = this.actual,
-                        uploadBtn = view.$('.upload-setting'),
-                        downloadBtn = view.$('.download-setting');
-
-                    upload = upload ? uploadBtn.length : !uploadBtn.length;
-                    download = download ? downloadBtn.length : !downloadBtn.length;
-
-                    return upload && download;
+                verifyButtons: function () {
+                    return {
+                        compare: function (actual, upload, download) {
+                            var view = actual,
+                                uploadBtn = view.$('.upload-setting'),
+                                downloadBtn = view.$('.download-setting'),
+                                passed;
+
+                            upload = upload ? uploadBtn.length : !uploadBtn.length;
+                            download = download ? downloadBtn.length : !downloadBtn.length;
+                            passed = upload && download;
+
+                            return {
+                                pass: passed
+                            };
+                        }
+                    };
                 }
             });
 
@@ -107,22 +150,18 @@ function ($, _, Squire) {
             injector.mock('js/views/video/transcripts/metadata_videolist');
             injector.mock('js/views/video/translations_editor');
 
-            runs(function() {
-                injector.require([
+            injector.require([
                     'js/models/metadata', 'js/views/metadata'
                 ],
-                function(MetadataModel, MetadataView) {
+                function (MetadataModel, MetadataView) {
                     var model = new MetadataModel($.extend(true, {}, modelStub));
                     self.view = new MetadataView.FileUploader({
                         model: model,
                         locator: locator
                     });
-                });
-            });
 
-            waitsFor(function() {
-                return self.view;
-            }, 'FileUploader was not created', 2000);
+                    done();
+                });
         });
 
         afterEach(function () {
@@ -150,7 +189,7 @@ function ($, _, Squire) {
             expect(this.uploadSpies.constructor).toHaveBeenCalled();
             expect(this.uploadSpies.show).toHaveBeenCalled();
 
-            options = this.uploadSpies.constructor.mostRecentCall.args[0];
+            options = this.uploadSpies.constructor.calls.mostRecent().args[0];
             options.onSuccess({
                 'asset': {
                     'url': 'http://example.org/test_3'
diff --git a/cms/static/js/spec/video/transcripts/editor_spec.js b/cms/static/js/spec/video/transcripts/editor_spec.js
index d489050..d2b711c 100644
--- a/cms/static/js/spec/video/transcripts/editor_spec.js
+++ b/cms/static/js/spec/video/transcripts/editor_spec.js
@@ -3,8 +3,7 @@ define(
         "jquery", "backbone", "underscore",
         "js/views/video/transcripts/utils", "js/views/video/transcripts/editor",
         "js/views/metadata", "js/models/metadata", "js/collections/metadata",
-        "underscore.string", "xmodule", "js/views/video/transcripts/metadata_videolist",
-        "jasmine-jquery"
+        "underscore.string", "xmodule", "js/views/video/transcripts/metadata_videolist"
     ],
 function ($, Backbone, _, Utils, Editor, MetadataView, MetadataModel, MetadataCollection, _str) {
     describe('Transcripts.Editor', function () {
@@ -43,6 +42,13 @@ function ($, Backbone, _, Utils, Editor, MetadataView, MetadataModel, MetadataCo
             },
             transcripts, container;
 
+        var waitsForDisplayName = function (collection) {
+            return jasmine.waitUntil(function () {
+                var displayNameValue = collection[0].getValue();
+                return displayNameValue !== '' && displayNameValue !== 'video_id';
+            });
+        };
+
         beforeEach(function () {
             var tpl = sandbox({
                     'class': 'wrapper-comp-settings basic_metadata_edit',
@@ -60,7 +66,6 @@ function ($, Backbone, _, Utils, Editor, MetadataView, MetadataModel, MetadataCo
         });
 
         describe('Test initialization', function () {
-
             beforeEach(function () {
                 spyOn(MetadataView, 'Editor');
 
@@ -158,27 +163,23 @@ function ($, Backbone, _, Utils, Editor, MetadataView, MetadataModel, MetadataCo
             });
 
             describe('Test Advanced to Basic synchronization', function () {
-                it('Correct data', function () {
+                it('Correct data', function (done) {
                     transcripts.syncBasicTab(metadataCollection, metadataView);
-
                     var collection = transcripts.collection.models;
 
-                    waitsFor(function() {
-                        var displayNameValue = collection[0].getValue();
-                        return (displayNameValue !== "" && displayNameValue != "video_id");
-                    }, "Defaults never loaded", 1000);
-
-                    runs(function() {
-                        var displayNameValue = collection[0].getValue(),
-                            videoUrlValue = collection[1].getValue();
-
-                        expect(displayNameValue).toEqual('default');
-                        expect(videoUrlValue).toEqual([
-                            'http://youtu.be/OEoXaMPEzfM',
-                            'default.mp4',
-                            'default.webm'
-                        ]);
-                    });
+                    waitsForDisplayName(collection)
+                        .then(function () {
+                            var displayNameValue = collection[0].getValue(),
+                                videoUrlValue = collection[1].getValue();
+
+                            expect(displayNameValue).toEqual('default');
+                            expect(videoUrlValue).toEqual([
+                                'http://youtu.be/OEoXaMPEzfM',
+                                'default.mp4',
+                                'default.webm'
+                            ]);
+                        })
+                        .always(done);
                 });
 
                 it('If metadataCollection is not defined', function () {
@@ -219,31 +220,26 @@ function ($, Backbone, _, Utils, Editor, MetadataView, MetadataModel, MetadataCo
             });
 
             describe('Test Basic to Advanced synchronization', function () {
-                it('Correct data', function () {
+                it('Correct data', function (done) {
                     transcripts.syncAdvancedTab(metadataCollection);
 
                     var collection = metadataCollection.models;
-
-                    waitsFor(function() {
-                        var displayNameValue = collection[0].getValue();
-                        return (displayNameValue !== "" && displayNameValue != "video_id");
-                    }, "Defaults never loaded", 1000);
-
-                    runs(function() {
-
-                        var displayNameValue = collection[0].getValue();
-                        var subValue = collection[1].getValue();
-                        var html5SourcesValue = collection[2].getValue();
-                        var youtubeValue = collection[3].getValue();
-
-                        expect(displayNameValue).toEqual('display value');
-                        expect(subValue).toEqual('default');
-                        expect(html5SourcesValue).toEqual([
-                            'video.mp4',
-                            'video.webm'
-                        ]);
-                        expect(youtubeValue).toEqual('12345678901');
-                    });
+                    waitsForDisplayName(collection)
+                        .then(function () {
+                            var displayNameValue = collection[0].getValue();
+                            var subValue = collection[1].getValue();
+                            var html5SourcesValue = collection[2].getValue();
+                            var youtubeValue = collection[3].getValue();
+
+                            expect(displayNameValue).toEqual('display value');
+                            expect(subValue).toEqual('default');
+                            expect(html5SourcesValue).toEqual([
+                                'video.mp4',
+                                'video.webm'
+                            ]);
+                            expect(youtubeValue).toEqual('12345678901');
+                        })
+                        .always(done);
                 });
 
                 it('metadataCollection is not defined', function () {
@@ -307,8 +303,7 @@ function ($, Backbone, _, Utils, Editor, MetadataView, MetadataModel, MetadataCo
                     transcripts.syncAdvancedTab(metadataCollection);
                     transcripts.syncAdvancedTab(metadataCollection);
                     transcripts.syncAdvancedTab(metadataCollection);
-
-                    expect(subModel.setValue.calls.length).toEqual(1);
+                    expect(subModel.setValue.calls.count()).toEqual(1);
                 });
 
             });
diff --git a/cms/static/js/spec/video/transcripts/file_uploader_spec.js b/cms/static/js/spec/video/transcripts/file_uploader_spec.js
index 59d46ea..41f159a 100644
--- a/cms/static/js/spec/video/transcripts/file_uploader_spec.js
+++ b/cms/static/js/spec/video/transcripts/file_uploader_spec.js
@@ -2,7 +2,7 @@ define(
     [
         "jquery", "underscore",
         "js/views/video/transcripts/utils", "js/views/video/transcripts/file_uploader",
-        "xmodule", "jquery.form", "jasmine-jquery"
+        "xmodule", "jquery.form"
     ],
 function ($, _, Utils, FileUploader) {
     // TODO: fix TNL-559 Intermittent failures of Transcript FileUploader JS tests
@@ -43,7 +43,7 @@ function ($, _, Utils, FileUploader) {
                 .append('<div class="transcripts-file-uploader" />')
                 .append('<a class="setting-upload" href="#">Upload</a>');
 
-            spyOn(FileUploader.prototype, 'render').andCallThrough();
+            spyOn(FileUploader.prototype, 'render').and.callThrough();
 
             view = new FileUploader({
                 el: $container,
@@ -61,7 +61,7 @@ function ($, _, Utils, FileUploader) {
         describe('Render', function () {
 
             beforeEach(function () {
-                spyOn(_, 'template').andCallThrough();
+                spyOn(_, 'template').and.callThrough();
             });
 
             it('Template doesn\'t exist', function () {
@@ -138,7 +138,7 @@ function ($, _, Utils, FileUploader) {
             });
 
             it('Valid File Type - error should be hided', function () {
-                spyOn(view, 'checkExtValidity').andReturn(true);
+                spyOn(view, 'checkExtValidity').and.returnValue(true);
 
                 view.$input.change();
 
@@ -148,7 +148,7 @@ function ($, _, Utils, FileUploader) {
             });
 
             it('Invalid File Type - error should be shown', function () {
-                spyOn(view, 'checkExtValidity').andReturn(false);
+                spyOn(view, 'checkExtValidity').and.returnValue(false);
 
                 view.$input.change();
 
@@ -189,7 +189,7 @@ function ($, _, Utils, FileUploader) {
         it('xhrProgressHandler', function () {
             var percent = 26;
 
-            spyOn($.fn, 'width').andCallThrough();
+            spyOn($.fn, 'width').and.callThrough();
 
             view.xhrProgressHandler(null, null, null, percent);
             expect(view.$progress.width).toHaveBeenCalledWith(percent + '%');
@@ -209,7 +209,7 @@ function ($, _, Utils, FileUploader) {
                 view.xhrCompleteHandler(xhr);
 
                 expect(view.$progress).toHaveClass('is-invisible');
-                expect(view.options.messenger.render.mostRecentCall.args[0])
+                expect(view.options.messenger.render.calls.mostRecent().args[0])
                     .toEqual('uploaded');
                 expect(Utils.Storage.set)
                     .toHaveBeenCalledWith('sub', 'test');
diff --git a/cms/static/js/spec/video/transcripts/message_manager_spec.js b/cms/static/js/spec/video/transcripts/message_manager_spec.js
index d929457..2324f68 100644
--- a/cms/static/js/spec/video/transcripts/message_manager_spec.js
+++ b/cms/static/js/spec/video/transcripts/message_manager_spec.js
@@ -2,7 +2,7 @@ define(
     [
         "jquery", "underscore",
         "js/views/video/transcripts/utils", "js/views/video/transcripts/message_manager",
-        "js/views/video/transcripts/file_uploader", "sinon", "jasmine-jquery",
+        "js/views/video/transcripts/file_uploader", "sinon",
         "xmodule"
     ],
 function ($, _, Utils, MessageManager, FileUploader, sinon) {
@@ -67,10 +67,10 @@ function ($, _, Utils, MessageManager, FileUploader, sinon) {
         });
 
         // Disabled 2/6/14 after intermittent failure in master
-        xdescribe('Render', function () {
+        describe('Render', function () {
 
             beforeEach(function () {
-                spyOn(_,'template').andCallThrough();
+                spyOn(_,'template').and.callThrough();
                 spyOn(fileUploader, 'render');
             });
 
@@ -101,7 +101,7 @@ function ($, _, Utils, MessageManager, FileUploader, sinon) {
             beforeEach(function () {
                 view.render('found');
                 spyOn(view, 'hideError');
-                spyOn($.fn, 'html').andCallThrough();
+                spyOn($.fn, 'html').and.callThrough();
                 $error = view.$el.find('.transcripts-error-message');
                 $buttons = view.$el.find('.wrapper-transcripts-buttons');
             });
@@ -147,10 +147,10 @@ function ($, _, Utils, MessageManager, FileUploader, sinon) {
         $.each(handlers, function(key, value) {
              it(key, function () {
                 var eventObj = jasmine.createSpyObj('event', ['preventDefault']);
-                spyOn($.fn, 'data').andReturn('video_id');
+                spyOn($.fn, 'data').and.returnValue('video_id');
                 spyOn(view, 'processCommand');
                 view[key](eventObj);
-                expect(view.processCommand.mostRecentCall.args).toEqual(value);
+                expect(view.processCommand.calls.mostRecent().args).toEqual(value);
              });
         });
 
@@ -162,7 +162,7 @@ function ($, _, Utils, MessageManager, FileUploader, sinon) {
 
             beforeEach(function () {
                 view.render('found');
-                spyOn(Utils, 'command').andCallThrough();
+                spyOn(Utils, 'command').and.callThrough();
                 spyOn(view, 'render');
                 spyOn(view, 'showError');
 
@@ -174,35 +174,19 @@ function ($, _, Utils, MessageManager, FileUploader, sinon) {
                 sinonXhr.restore();
             });
 
-            var assertCommand = function (config, expectFunc) {
-                var flag = false,
-                    defaults = {
+            var assertCommand = function (config) {
+                var defaults = {
                         action: 'replace',
                         errorMessage: 'errorMessage',
                         extraParamas: void(0)
                     };
-                    args = $.extend({}, defaults, config);
+                var args = $.extend({}, defaults, config);
 
-                runs(function() {
-                    view
-                        .processCommand(
-                            args.action,
-                            args.errorMessage,
-                            args.extraParamas
-                        )
-                        .always(function () { flag = true; });
-                });
-
-                waitsFor(function() {
-                    return flag;
-                }, "Ajax Timeout", 750);
-
-
-                runs(expectFunc);
+                return view
+                    .processCommand(args.action, args.errorMessage, args.extraParamas);
             };
 
-            it('Invoke without extraParamas', function () {
-
+            it('Invoke without extraParamas', function (done) {
                 sinonXhr.respondWith([
                     200,
                     { "Content-Type": "application/json"},
@@ -212,9 +196,8 @@ function ($, _, Utils, MessageManager, FileUploader, sinon) {
                     })
                 ]);
 
-                assertCommand(
-                    { },
-                    function() {
+                assertCommand({})
+                    .then(function () {
                         expect(Utils.command).toHaveBeenCalledWith(
                             action,
                             view.component_locator,
@@ -222,15 +205,14 @@ function ($, _, Utils, MessageManager, FileUploader, sinon) {
                             void(0)
                         );
                         expect(view.showError).not.toHaveBeenCalled();
-                        expect(view.render.mostRecentCall.args[0])
+                        expect(view.render.calls.mostRecent().args[0])
                             .toEqual('found');
                         expect(Utils.Storage.set).toHaveBeenCalled();
-                    }
-                );
+                    })
+                    .always(done);
             });
 
-            it('Invoke with extraParamas', function () {
-
+            it('Invoke with extraParamas', function (done) {
                 sinonXhr.respondWith([
                     200,
                     { "Content-Type": "application/json"},
@@ -242,9 +224,8 @@ function ($, _, Utils, MessageManager, FileUploader, sinon) {
 
                 view.processCommand(action, errorMessage, extraParamas);
 
-                assertCommand(
-                    { extraParamas : extraParamas },
-                    function () {
+                assertCommand({extraParamas : extraParamas})
+                    .then(function () {
                         expect(Utils.command).toHaveBeenCalledWith(
                             action,
                             view.component_locator,
@@ -254,20 +235,16 @@ function ($, _, Utils, MessageManager, FileUploader, sinon) {
                             }
                         );
                         expect(view.showError).not.toHaveBeenCalled();
-                        expect(view.render.mostRecentCall.args[0])
-                            .toEqual('found');
+                        expect(view.render.calls.mostRecent().args[0]).toEqual('found');
                         expect(Utils.Storage.set).toHaveBeenCalled();
-                    }
-                );
+                    })
+                    .always(done);
             });
 
-            it('Fail', function () {
-
+            it('Fail', function (done) {
                 sinonXhr.respondWith([400, {}, '']);
-
-                assertCommand(
-                    { },
-                    function () {
+                assertCommand({})
+                    .then(function () {
                         expect(Utils.command).toHaveBeenCalledWith(
                             action,
                             view.component_locator,
@@ -277,8 +254,8 @@ function ($, _, Utils, MessageManager, FileUploader, sinon) {
                         expect(view.showError).toHaveBeenCalled();
                         expect(view.render).not.toHaveBeenCalled();
                         expect(Utils.Storage.set).not.toHaveBeenCalled();
-                    }
-                );
+                    })
+                    .always(done);
             });
         });
 
diff --git a/cms/static/js/spec/video/transcripts/utils_spec.js b/cms/static/js/spec/video/transcripts/utils_spec.js
index 6658c95..14f8c92 100644
--- a/cms/static/js/spec/video/transcripts/utils_spec.js
+++ b/cms/static/js/spec/video/transcripts/utils_spec.js
@@ -2,7 +2,7 @@ define(
     [
         'jquery', 'underscore',
         'js/views/video/transcripts/utils',
-        'underscore.string', 'xmodule', 'jasmine-jquery'
+        'underscore.string', 'xmodule'
     ],
 function ($, _, Utils, _str) {
 'use strict';
diff --git a/cms/static/js/spec/video/transcripts/videolist_spec.js b/cms/static/js/spec/video/transcripts/videolist_spec.js
index 51a6e00..7ea7ec5 100644
--- a/cms/static/js/spec/video/transcripts/videolist_spec.js
+++ b/cms/static/js/spec/video/transcripts/videolist_spec.js
@@ -5,7 +5,7 @@ define(
         'js/views/video/transcripts/metadata_videolist', 'js/models/metadata',
         'js/views/abstract_editor',
         'common/js/spec_helpers/ajax_helpers',
-        'xmodule', 'jasmine-jquery'
+        'xmodule'
     ],
 function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
     'use strict';
@@ -53,6 +53,19 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
             }),
             MessageManager, messenger;
 
+
+        var createMockAjaxServer = function () {
+            var mockServer = AjaxHelpers.server(
+                [
+                    200,
+                    { 'Content-Type': 'application/json'},
+                    response
+                ]
+            );
+            mockServer.autoRespond = true;
+            return mockServer;
+        };
+
         beforeEach(function () {
             var tpl = sandbox({
                     'class': 'component',
@@ -70,9 +83,12 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
                 ).text(videoListEntryTemplate)
             );
 
-            spyOn(Utils, 'command').andCallThrough();
-            spyOn(abstractEditor, 'initialize').andCallThrough();
-            spyOn(abstractEditor, 'render').andCallThrough();
+            // create mock server
+            this.mockServer = createMockAjaxServer();
+
+            spyOn(Utils, 'command').and.callThrough();
+            spyOn(abstractEditor, 'initialize').and.callThrough();
+            spyOn(abstractEditor, 'render').and.callThrough();
             spyOn(console, 'error');
 
             messenger = jasmine.createSpyObj('MessageManager',[
@@ -80,7 +96,7 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
             ]);
 
             $.each(messenger, function(index, method) {
-                 method.andReturn(messenger);
+                 method.and.returnValue(messenger);
             });
 
             MessageManager = function () {
@@ -89,40 +105,54 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
                 return messenger;
             };
 
-            this.addMatchers({
-                assertValueInView: function(expected) {
-                    var actualValue = this.actual.getValueFromEditor();
-                    return this.env.equals_(actualValue, expected);
-                },
-                assertCanUpdateView: function (expected) {
-                    var actual = this.actual,
-                        actualValue;
-
-                    actual.setValueInEditor(expected);
-                    actualValue = actual.getValueFromEditor();
+            jasmine.addMatchers({
+                assertValueInView: function() {
+                    return {
+                        compare: function (actual, expected) {
+                            var actualValue = actual.getValueFromEditor(),
+                            passed = _.isEqual(actualValue, expected);
 
-                    return this.env.equals_(actualValue, expected);
+                            return {
+                                pass: passed
+                            };
+                        }
+                    };
                 },
-                assertIsCorrectVideoList: function (expected) {
-                    var actualValue = this.actual.getVideoObjectsList();
-
-                    return this.env.equals_(actualValue, expected);
+                assertCanUpdateView: function () {
+                    return {
+                        compare: function (actual, expected) {
+                            var actualValue,
+                                passed;
+
+                            actual.setValueInEditor(expected);
+                            actualValue = actual.getValueFromEditor();
+                            passed = _.isEqual(actualValue, expected);
+
+                            return {
+                                pass: passed
+                            };
+                        }
+                    };
+                },
+                assertIsCorrectVideoList: function () {
+                    return {
+                        compare: function (actual, expected) {
+                            var actualValue = actual.getVideoObjectsList(),
+                            passed = _.isEqual(actualValue, expected);
+
+                            return {
+                                pass: passed
+                            };
+                        }
+                    };
                 }
             });
         });
 
-        var createMockAjaxServer = function (test) {
-            var mockServer = AjaxHelpers.server(
-                test,
-                [
-                    200,
-                    { 'Content-Type': 'application/json'},
-                    response
-                ]
-            );
-            mockServer.autoRespond = true;
-            return mockServer;
-        };
+        afterEach(function () {
+            // restore mock server
+            this.mockServer.restore();
+        });
 
         var createVideoListView = function () {
             var model = new MetadataModel(modelStub);
@@ -133,37 +163,25 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
             });
         };
 
-        var waitsForResponse = function (mockServer, expectFunc, prep) {
-            var flag = false;
-
-            if (prep) {
-                runs(prep);
-            }
-
-            waitsFor(function() {
+        var waitsForResponse = function (mockServer) {
+            return jasmine.waitUntil(function () {
                 var requests = mockServer.requests,
                     len = requests.length;
 
-                if (len && requests[0].readyState === 4) {
-                    flag = true;
-                }
-
-                return flag;
-            }, 'Ajax Timeout', 750);
-
-            runs(expectFunc);
+                return len && requests[0].readyState === 4;
+            });
         };
 
 
-        it('Initialize', function () {
-            var mockServer = createMockAjaxServer(this),
-                view = createVideoListView();
-            waitsForResponse(mockServer, function () {
-                expect(abstractEditor.initialize).toHaveBeenCalled();
-                expect(messenger.initialize).toHaveBeenCalled();
-                expect(view.component_locator).toBe(component_locator);
-                expect(view.$el).toHandle('input');
-            });
+        it('Initialize', function (done) {
+            var view = createVideoListView();
+            waitsForResponse(this.mockServer)
+              .then(function () {
+                  expect(abstractEditor.initialize).toHaveBeenCalled();
+                  expect(messenger.initialize).toHaveBeenCalled();
+                  expect(view.component_locator).toBe(component_locator);
+                  expect(view.$el).toHandle('input');
+              }).always(done);
         });
 
         describe('Render', function () {
@@ -178,23 +196,23 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
                     expect(messenger.render).toHaveBeenCalled();
                 },
                 resetSpies = function(mockServer) {
-                    abstractEditor.render.reset();
-                    Utils.command.reset();
-                    messenger.render.reset();
+                    abstractEditor.render.calls.reset();
+                    Utils.command.calls.reset();
+                    messenger.render.calls.reset();
                     mockServer.requests.length = 0;
                 };
 
-            it('is rendered in correct way', function () {
-                var mockServer = createMockAjaxServer(this);
+            it('is rendered in correct way', function (done) {
                 createVideoListView();
-                waitsForResponse(mockServer, function () {
-                    assertToHaveBeenRendered(videoList);
-                });
+                waitsForResponse(this.mockServer)
+                  .then(function () {
+                      assertToHaveBeenRendered(videoList);
+                  })
+                  .always(done);
             });
 
-            it('is rendered with opened extra videos bar', function () {
-                var mockServer = createMockAjaxServer(this),
-                    view = createVideoListView();
+            it('is rendered with opened extra videos bar', function (done) {
+                var view = createVideoListView();
                 var videoListLength = [
                         {
                             mode: 'youtube',
@@ -215,41 +233,35 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
                         }
                     ];
 
-                spyOn(view, 'getVideoObjectsList').andReturn(videoListLength);
+                spyOn(view, 'getVideoObjectsList').and.returnValue(videoListLength);
                 spyOn(view, 'openExtraVideosBar');
 
-                waitsForResponse(
-                    mockServer,
-                    function () {
+                resetSpies(this.mockServer);
+                view.render();
+
+                waitsForResponse(this.mockServer)
+                    .then(function () {
                         assertToHaveBeenRendered(videoListLength);
-                        view.getVideoObjectsList.andReturn(videoListLength);
+                        view.getVideoObjectsList.and.returnValue(videoListLength);
                         expect(view.openExtraVideosBar).toHaveBeenCalled();
-                    },
-                    function () {
-                        resetSpies(mockServer);
+                    })
+                    .then(_.bind(function () {
+                        resetSpies(this.mockServer);
+                        view.openExtraVideosBar.calls.reset();
+                        view.getVideoObjectsList.and.returnValue(videoListHtml5mode);
                         view.render();
-                    }
-                );
 
-                waitsForResponse(
-                    mockServer,
-                    function () {
-                        assertToHaveBeenRendered(videoListHtml5mode);
-                        expect(view.openExtraVideosBar).toHaveBeenCalled();
-                    },
-                    function () {
-                        resetSpies(mockServer);
-                        view.openExtraVideosBar.reset();
-                        view.getVideoObjectsList.andReturn(videoListHtml5mode);
-                        view.render();
-                    }
-                );
+                        return waitsForResponse(this.mockServer)
+                            .then(function () {
+                                assertToHaveBeenRendered(videoListHtml5mode);
+                                expect(view.openExtraVideosBar).toHaveBeenCalled();
+                            }).then(done);
+                    }, this));
 
             });
 
-            it('is rendered without opened extra videos bar', function () {
-                var mockServer = createMockAjaxServer(this),
-                    view = createVideoListView(),
+            it('is rendered without opened extra videos bar', function (done) {
+                var view = createVideoListView(),
                     videoList = [
                         {
                             mode: 'youtube',
@@ -258,44 +270,40 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
                         }
                     ];
 
-                spyOn(view, 'getVideoObjectsList').andReturn(videoList);
+                spyOn(view, 'getVideoObjectsList').and.returnValue(videoList);
                 spyOn(view, 'closeExtraVideosBar');
 
-                waitsForResponse(
-                    mockServer,
-                    function () {
-                        assertToHaveBeenRendered(videoList);
-                        expect(view.closeExtraVideosBar).toHaveBeenCalled();
-                    },
-                    function () {
-                        resetSpies(mockServer);
-                        view.render();
-                    }
-                );
-            });
+                resetSpies(this.mockServer);
+                view.render();
 
+                waitsForResponse(this.mockServer)
+                  .then(function () {
+                      assertToHaveBeenRendered(videoList);
+                      expect(view.closeExtraVideosBar).toHaveBeenCalled();
+                  })
+                  .always(done);
+            });
         });
 
         describe('isUniqOtherVideos', function () {
-            it('Unique data - return true', function () {
-                var mockServer = createMockAjaxServer(this),
-                    view = createVideoListView(),
+            it('Unique data - return true', function (done) {
+                var view = createVideoListView(),
                     data = videoList.concat([{
                         mode: 'html5',
                         type: 'other',
                         video: 'pxxZrg'
                     }]);
-                waitsForResponse(mockServer, function () {
-                    var result = view.isUniqOtherVideos(data);
-
-                    expect(result).toBe(true);
-                });
 
+                waitsForResponse(this.mockServer)
+                  .then(function () {
+                      var result = view.isUniqOtherVideos(data);
+                      expect(result).toBe(true);
+                  })
+                  .always(done);
             });
 
-            it('Not Unique data - return false', function () {
-                var mockServer = createMockAjaxServer(this),
-                    view = createVideoListView(),
+            it('Not Unique data - return false', function (done) {
+                var view = createVideoListView(),
                     data = [
                         {
                             mode: 'html5',
@@ -323,30 +331,31 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
                             video: '12345678901'
                         }
                     ];
-                waitsForResponse(mockServer, function () {
-                    var result = view.isUniqOtherVideos(data);
 
-                    expect(result).toBe(false);
-                });
+                waitsForResponse(this.mockServer)
+                  .then(function () {
+                      var result = view.isUniqOtherVideos(data);
+                      expect(result).toBe(false);
+                  })
+                  .always(done);
             });
         });
 
         describe('isUniqVideoTypes', function () {
-
-            it('Unique data - return true', function () {
-                var mockServer = createMockAjaxServer(this),
-                    view = createVideoListView(),
+            it('Unique data - return true', function (done) {
+                var view = createVideoListView(),
                     data = videoList;
-                waitsForResponse(mockServer, function () {
-                    var result = view.isUniqVideoTypes(data);
-                    expect(result).toBe(true);
-                });
 
+                waitsForResponse(this.mockServer)
+                  .then(function () {
+                      var result = view.isUniqVideoTypes(data);
+                      expect(result).toBe(true);
+                  })
+                  .always(done);
             });
 
-            it('Not Unique data - return false', function () {
-                var mockServer = createMockAjaxServer(this),
-                    view = createVideoListView(),
+            it('Not Unique data - return false', function (done) {
+                var view = createVideoListView(),
                     data = [
                         {
                             mode: 'html5',
@@ -369,18 +378,19 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
                             video: '12345678901'
                         }
                     ];
-                waitsForResponse(mockServer, function () {
-                    var result = view.isUniqVideoTypes(data);
 
-                    expect(result).toBe(false);
-                });
+                waitsForResponse(this.mockServer)
+                  .then(function () {
+                      var result = view.isUniqVideoTypes(data);
+                      expect(result).toBe(false);
+                  })
+                  .always(done);
             });
         });
 
         describe('checkIsUniqVideoTypes', function () {
-            it('Error is shown', function () {
-                var mockServer = createMockAjaxServer(this),
-                    view = createVideoListView(),
+            it('Error is shown', function (done) {
+                var view = createVideoListView(),
                     data = [
                         {
                             mode: 'html5',
@@ -404,125 +414,137 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
                         }
                     ];
 
-                waitsForResponse(mockServer, function () {
-                    var result = view.checkIsUniqVideoTypes(data);
+                waitsForResponse(this.mockServer)
+                  .then(function () {
+                      var result = view.checkIsUniqVideoTypes(data);
 
-                    expect(messenger.showError).toHaveBeenCalled();
-                    expect(result).toBe(false);
-                });
+                      expect(messenger.showError).toHaveBeenCalled();
+                      expect(result).toBe(false);
+                  })
+                  .always(done);
             });
 
-            it('All works okay if arguments are not passed', function () {
-                var mockServer = createMockAjaxServer(this),
-                    view = createVideoListView();
-                spyOn(view, 'getVideoObjectsList').andReturn(videoList);
-                waitsForResponse(mockServer, function () {
-                    var result = view.checkIsUniqVideoTypes();
+            it('All works okay if arguments are not passed', function (done) {
+                var view = createVideoListView();
+                spyOn(view, 'getVideoObjectsList').and.returnValue(videoList);
 
-                    expect(view.getVideoObjectsList).toHaveBeenCalled();
-                    expect(messenger.showError).not.toHaveBeenCalled();
-                    expect(result).toBe(true);
-                });
+                waitsForResponse(this.mockServer)
+                  .then(function () {
+                      var result = view.checkIsUniqVideoTypes();
+
+                      expect(view.getVideoObjectsList).toHaveBeenCalled();
+                      expect(messenger.showError).not.toHaveBeenCalled();
+                      expect(result).toBe(true);
+                  })
+                  .always(done);
             });
         });
 
         describe('checkValidity', function () {
-            it('Error message is shown', function () {
-                var mockServer = createMockAjaxServer(this),
-                    view = createVideoListView();
-                spyOn(view, 'checkIsUniqVideoTypes').andReturn(true);
-                waitsForResponse(mockServer, function () {
-                    var data = { mode: 'incorrect' },
+            it('Error message is shown', function (done) {
+                var view = createVideoListView();
+                spyOn(view, 'checkIsUniqVideoTypes').and.returnValue(true);
+
+                waitsForResponse(this.mockServer)
+                  .then(function () {
+                      var data = {mode: 'incorrect'},
                         result = view.checkValidity(data, true);
 
-                    expect(messenger.showError).toHaveBeenCalled();
-                    expect(view.checkIsUniqVideoTypes).toHaveBeenCalled();
-                    expect(result).toBe(false);
-                });
+                      expect(messenger.showError).toHaveBeenCalled();
+                      expect(view.checkIsUniqVideoTypes).toHaveBeenCalled();
+                      expect(result).toBe(false);
+                  })
+                  .always(done);
             });
 
-            it('Error message is shown when flag is not passed', function () {
-                var mockServer = createMockAjaxServer(this),
-                    view = createVideoListView();
-                spyOn(view, 'checkIsUniqVideoTypes').andReturn(true);
-                waitsForResponse(mockServer, function () {
-                    var data = { mode: 'incorrect' },
+            it('Error message is shown when flag is not passed', function (done) {
+                var view = createVideoListView();
+                spyOn(view, 'checkIsUniqVideoTypes').and.returnValue(true);
+
+                waitsForResponse(this.mockServer)
+                  .then(function () {
+                      var data = {mode: 'incorrect'},
                         result = view.checkValidity(data);
 
-                    expect(messenger.showError).not.toHaveBeenCalled();
-                    expect(view.checkIsUniqVideoTypes).toHaveBeenCalled();
-                    expect(result).toBe(true);
-                });
+                      expect(messenger.showError).not.toHaveBeenCalled();
+                      expect(view.checkIsUniqVideoTypes).toHaveBeenCalled();
+                      expect(result).toBe(true);
+                  }).always(done);
             });
 
-            it('All works okay if correct data is passed', function () {
-                var mockServer = createMockAjaxServer(this),
-                    view = createVideoListView();
-                spyOn(view, 'checkIsUniqVideoTypes').andReturn(true);
-                waitsForResponse(mockServer, function () {
-                    var data = videoList,
+            it('All works okay if correct data is passed', function (done) {
+                var view = createVideoListView();
+                spyOn(view, 'checkIsUniqVideoTypes').and.returnValue(true);
+
+                waitsForResponse(this.mockServer)
+                  .then(function () {
+                      var data = videoList,
                         result = view.checkValidity(data);
 
-                    expect(messenger.showError).not.toHaveBeenCalled();
-                    expect(view.checkIsUniqVideoTypes).toHaveBeenCalled();
-                    expect(result).toBe(true);
-                });
+                      expect(messenger.showError).not.toHaveBeenCalled();
+                      expect(view.checkIsUniqVideoTypes).toHaveBeenCalled();
+                      expect(result).toBe(true);
+                  })
+                  .always(done);
             });
         });
 
-        it('openExtraVideosBar', function () {
-            var mockServer = createMockAjaxServer(this),
-                view = createVideoListView();
-            waitsForResponse(mockServer, function () {
-                view.$extraVideosBar.removeClass('is-visible');
-
-                view.openExtraVideosBar();
-                expect(view.$extraVideosBar).toHaveClass('is-visible');
-            });
+        it('openExtraVideosBar', function (done) {
+            var view = createVideoListView();
+            waitsForResponse(this.mockServer)
+              .then(function () {
+                  view.$extraVideosBar.removeClass('is-visible');
+                  view.openExtraVideosBar();
+                  expect(view.$extraVideosBar).toHaveClass('is-visible');
+              })
+              .always(done);
         });
 
-        it('closeExtraVideosBar', function () {
-            var mockServer = createMockAjaxServer(this),
-                view = createVideoListView();
-            waitsForResponse(mockServer, function () {
-                view.$extraVideosBar.addClass('is-visible');
-                view.closeExtraVideosBar();
+        it('closeExtraVideosBar', function (done) {
+            var view = createVideoListView();
+            waitsForResponse(this.mockServer)
+              .then(function () {
+                  view.$extraVideosBar.addClass('is-visible');
+                  view.closeExtraVideosBar();
 
-                expect(view.$extraVideosBar).not.toHaveClass('is-visible');
-            });
+                  expect(view.$extraVideosBar).not.toHaveClass('is-visible');
+              })
+              .always(done);
         });
 
-        it('toggleExtraVideosBar', function () {
-            var mockServer = createMockAjaxServer(this),
-                view = createVideoListView();
-            waitsForResponse(mockServer, function () {
-                view.$extraVideosBar.addClass('is-visible');
-                view.toggleExtraVideosBar();
-                expect(view.$extraVideosBar).not.toHaveClass('is-visible');
-                view.toggleExtraVideosBar();
-                expect(view.$extraVideosBar).toHaveClass('is-visible');
-            });
+        it('toggleExtraVideosBar', function (done) {
+            var view = createVideoListView();
+            waitsForResponse(this.mockServer)
+              .then(function () {
+                  view.$extraVideosBar.addClass('is-visible');
+                  view.toggleExtraVideosBar();
+                  expect(view.$extraVideosBar).not.toHaveClass('is-visible');
+                  view.toggleExtraVideosBar();
+                  expect(view.$extraVideosBar).toHaveClass('is-visible');
+              })
+              .always(done);
         });
 
-        it('getValueFromEditor', function () {
-            var mockServer = createMockAjaxServer(this),
-                view = createVideoListView();
-            waitsForResponse(mockServer, function () {
-                expect(view).assertValueInView(modelStub.value);
-            });
+        it('getValueFromEditor', function (done) {
+            var view = createVideoListView();
+            waitsForResponse(this.mockServer)
+              .then(function () {
+                  expect(view).assertValueInView(modelStub.value);
+              })
+              .always(done);
         });
 
-        it('setValueInEditor', function () {
-            var mockServer = createMockAjaxServer(this),
-                view = createVideoListView();
-            waitsForResponse(mockServer, function () {
-                expect(view).assertCanUpdateView(['abc.mp4']);
-            });
+        it('setValueInEditor', function (done) {
+            var view = createVideoListView();
+            waitsForResponse(this.mockServer)
+              .then(function () {
+                  expect(view).assertCanUpdateView(['abc.mp4']);
+              })
+              .always(done);
         });
 
-        it('getVideoObjectsList', function () {
-            var mockServer = createMockAjaxServer(this),
-                view = createVideoListView();
+        it('getVideoObjectsList', function (done) {
+            var view = createVideoListView();
             var value = [
                 {
                     mode: 'youtube',
@@ -541,36 +563,39 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
                 }
             ];
 
-            waitsForResponse(mockServer, function () {
-                view.setValueInEditor([
-                    'http://youtu.be/12345678901',
-                    'video.mp4',
-                    'http://goo.gl/pxxZrg',
-                    'video'
-                ]);
-                expect(view).assertIsCorrectVideoList(value);
-            });
+            waitsForResponse(this.mockServer)
+              .then(function () {
+                  view.setValueInEditor([
+                      'http://youtu.be/12345678901',
+                      'video.mp4',
+                      'http://goo.gl/pxxZrg',
+                      'video'
+                  ]);
+                  expect(view).assertIsCorrectVideoList(value);
+              })
+              .always(done);
         });
 
         describe('getPlaceholders', function () {
 
-            it('All works okay if empty values are passed', function () {
-                var mockServer = createMockAjaxServer(this),
-                    view = createVideoListView(),
+            it('All works okay if empty values are passed', function (done) {
+                var view = createVideoListView(),
                     defaultPlaceholders = view.placeholders;
-                waitsForResponse(mockServer, function () {
-                    var result = view.getPlaceholders([]),
-                    expectedResult = _.values(defaultPlaceholders).reverse();
 
-                    expect(result).toEqual(expectedResult);
-                });
+                waitsForResponse(this.mockServer)
+                  .then(function () {
+                      var result = view.getPlaceholders([]),
+                        expectedResult = _.values(defaultPlaceholders).reverse();
+
+                      expect(result).toEqual(expectedResult);
+                  })
+                  .always(done);
             });
 
             it('On filling less than 3 fields, remaining fields should have ' +
 'placeholders for video types that were not filled yet',
-                function () {
-                    var mockServer = createMockAjaxServer(this),
-                        view = createVideoListView(),
+                function (done) {
+                    var view = createVideoListView(),
                         defaultPlaceholders = view.placeholders;
                     var dataDict = {
                         youtube: {
@@ -598,14 +623,17 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
                             ]
                         }
                     };
-                defaultPlaceholders = view.placeholders;
-                    waitsForResponse(mockServer, function () {
-                        $.each(dataDict, function(index, val) {
-                            var result = view.getPlaceholders(val.value);
-
-                            expect(result).toEqual(val.expectedResult);
-                        });
-                    });
+
+                    defaultPlaceholders = view.placeholders;
+                    waitsForResponse(this.mockServer)
+                      .then(function () {
+                          $.each(dataDict, function (index, val) {
+                              var result = view.getPlaceholders(val.value);
+
+                              expect(result).toEqual(val.expectedResult);
+                          });
+                      })
+                      .always(done);
                 }
             );
         });
@@ -614,9 +642,9 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
             var eventObject;
 
             var resetSpies = function (view) {
-                messenger.hideError.reset();
-                view.updateModel.reset();
-                view.closeExtraVideosBar.reset();
+                messenger.hideError.calls.reset();
+                view.updateModel.calls.reset();
+                view.closeExtraVideosBar.calls.reset();
             };
 
             var setUp = function (view) {
@@ -628,100 +656,104 @@ function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
                 spyOn($.fn, 'hasClass');
                 spyOn($.fn, 'addClass');
                 spyOn($.fn, 'removeClass');
-                spyOn($.fn, 'prop').andCallThrough();
+                spyOn($.fn, 'prop').and.callThrough();
                 spyOn(_, 'isEqual');
 
                 resetSpies(view);
             };
 
             it('Field has invalid value - nothing should happen',
-                function () {
-                    var mockServer = createMockAjaxServer(this),
-                        view = createVideoListView();
+                function (done) {
+                    var view = createVideoListView();
                     setUp(view);
-                    $.fn.hasClass.andReturn(false);
-                    view.checkValidity.andReturn(false);
-
-                    waitsForResponse(mockServer, function () {
-                        view.inputHandler(eventObject);
-                        expect(messenger.hideError).not.toHaveBeenCalled();
-                        expect(view.updateModel).not.toHaveBeenCalled();
-                        expect(view.closeExtraVideosBar).not.toHaveBeenCalled();
-                        expect($.fn.prop).toHaveBeenCalledWith(
-                            'disabled', true
-                        );
-                        expect($.fn.addClass).toHaveBeenCalledWith(
-                            'is-disabled'
-                        );
-                    });
+                    $.fn.hasClass.and.returnValue(false);
+                    view.checkValidity.and.returnValue(false);
+
+                    waitsForResponse(this.mockServer)
+                        .then(function () {
+                            view.inputHandler(eventObject);
+                            expect(messenger.hideError).not.toHaveBeenCalled();
+                            expect(view.updateModel).not.toHaveBeenCalled();
+                            expect(view.closeExtraVideosBar).not.toHaveBeenCalled();
+                            expect($.fn.prop).toHaveBeenCalledWith(
+                                'disabled', true
+                            );
+                            expect($.fn.addClass).toHaveBeenCalledWith(
+                                'is-disabled'
+                            );
+                        })
+                        .always(done);
                 }
             );
 
             it('Main field has invalid value - extra Videos Bar is closed',
-                function () {
-                    var mockServer = createMockAjaxServer(this),
-                        view = createVideoListView();
+                function (done) {
+                    var view = createVideoListView();
                     setUp(view);
-                    $.fn.hasClass.andReturn(true);
-                    view.checkValidity.andReturn(false);
-
-                    waitsForResponse(mockServer, function () {
-                        view.inputHandler(eventObject);
-                        expect(messenger.hideError).not.toHaveBeenCalled();
-                        expect(view.updateModel).not.toHaveBeenCalled();
-                        expect(view.closeExtraVideosBar).toHaveBeenCalled();
-                        expect($.fn.prop).toHaveBeenCalledWith(
-                            'disabled', true
-                        );
-                        expect($.fn.addClass).toHaveBeenCalledWith(
-                            'is-disabled'
-                        );
-                    });
+                    $.fn.hasClass.and.returnValue(true);
+                    view.checkValidity.and.returnValue(false);
+
+                    waitsForResponse(this.mockServer)
+                        .then(function () {
+                            view.inputHandler(eventObject);
+                            expect(messenger.hideError).not.toHaveBeenCalled();
+                            expect(view.updateModel).not.toHaveBeenCalled();
+                            expect(view.closeExtraVideosBar).toHaveBeenCalled();
+                            expect($.fn.prop).toHaveBeenCalledWith(
+                                'disabled', true
+                            );
+                            expect($.fn.addClass).toHaveBeenCalledWith(
+                                'is-disabled'
+                            );
+                        })
+                        .always(done);
                 }
             );
 
             it('Model is updated if value is valid',
-                function () {
-                    var mockServer = createMockAjaxServer(this),
-                        view = createVideoListView();
+                function (done) {
+                    var view = createVideoListView();
                     setUp(view);
-                    view.checkValidity.andReturn(true);
-                    _.isEqual.andReturn(false);
-
-                    waitsForResponse(mockServer, function () {
-                        view.inputHandler(eventObject);
-                        expect(messenger.hideError).not.toHaveBeenCalled();
-                        expect(view.updateModel).toHaveBeenCalled();
-                        expect(view.closeExtraVideosBar).not.toHaveBeenCalled();
-                        expect($.fn.prop).toHaveBeenCalledWith(
-                            'disabled', false
-                        );
-                        expect($.fn.removeClass).toHaveBeenCalledWith(
-                            'is-disabled'
-                        );
-                    });
+                    view.checkValidity.and.returnValue(true);
+                    _.isEqual.and.returnValue(false);
+
+                    waitsForResponse(this.mockServer)
+                        .then(function () {
+                            view.inputHandler(eventObject);
+                            expect(messenger.hideError).not.toHaveBeenCalled();
+                            expect(view.updateModel).toHaveBeenCalled();
+                            expect(view.closeExtraVideosBar).not.toHaveBeenCalled();
+                            expect($.fn.prop).toHaveBeenCalledWith(
+                                'disabled', false
+                            );
+                            expect($.fn.removeClass).toHaveBeenCalledWith(
+                                'is-disabled'
+                            );
+                        })
+                        .always(done);
                 }
             );
 
             it('Corner case: Error is hided',
-                function () {
-                    var mockServer = createMockAjaxServer(this),
-                        view = createVideoListView();
+                function (done) {
+                    var view = createVideoListView();
                     setUp(view);
-                    view.checkValidity.andReturn(true);
-                    _.isEqual.andReturn(true);
-                    waitsForResponse(mockServer, function () {
-                        view.inputHandler(eventObject);
-                        expect(messenger.hideError).toHaveBeenCalled();
-                        expect(view.updateModel).not.toHaveBeenCalled();
-                        expect(view.closeExtraVideosBar).not.toHaveBeenCalled();
-                        expect($.fn.prop).toHaveBeenCalledWith(
-                            'disabled', false
-                        );
-                        expect($.fn.removeClass).toHaveBeenCalledWith(
-                            'is-disabled'
-                        );
-                    });
+                    view.checkValidity.and.returnValue(true);
+                    _.isEqual.and.returnValue(true);
+                    waitsForResponse(this.mockServer)
+                        .then(function () {
+                            view.inputHandler(eventObject);
+                            expect(messenger.hideError).toHaveBeenCalled();
+                            expect(view.updateModel).not.toHaveBeenCalled();
+                            expect(view.closeExtraVideosBar).not.toHaveBeenCalled();
+                            expect($.fn.prop).toHaveBeenCalledWith(
+                                'disabled', false
+                            );
+                            expect($.fn.removeClass).toHaveBeenCalledWith(
+                                'is-disabled'
+                            );
+                        })
+                        .always(done);
                 }
             );
 
diff --git a/cms/static/js/spec/video/translations_editor_spec.js b/cms/static/js/spec/video/translations_editor_spec.js
index 2bc212d..b86cc91 100644
--- a/cms/static/js/spec/video/translations_editor_spec.js
+++ b/cms/static/js/spec/video/translations_editor_spec.js
@@ -45,82 +45,133 @@ function ($, _, Squire) {
 
         var createPromptSpy = function (name) {
             var spy = jasmine.createSpyObj(name, ['constructor', 'show', 'hide']);
-            spy.constructor.andReturn(spy);
-            spy.show.andReturn(spy);
-            spy.extend = jasmine.createSpy().andReturn(spy.constructor);
+            spy.constructor.and.returnValue(spy);
+            spy.show.and.returnValue(spy);
+            spy.extend = jasmine.createSpy().and.returnValue(spy.constructor);
 
             return spy;
         };
 
-        beforeEach(function () {
+        beforeEach(function (done) {
             self = this;
 
-            this.addMatchers({
-                assertValueInView: function(expected) {
-                    var value = this.actual.getValueFromEditor();
-                    return this.env.equals_(value, expected);
+            jasmine.addMatchers({
+                assertValueInView: function() {
+                    return {
+                        compare: function (actual, expected) {
+                            var value = actual.getValueFromEditor();
+                            var passed = _.isEqual(value, expected);
+
+                            return {
+                                pass: passed,
+                                message: 'Expected ' + actual + (passed ? '' : ' not') + ' to equal ' + expected
+                            };
+                        }
+                    };
                 },
-                assertCanUpdateView: function (expected) {
-                    var view = this.actual,
-                        value;
-
-                    view.setValueInEditor(expected);
-                    value = view.getValueFromEditor();
-
-                    return this.env.equals_(value, expected);
+                assertCanUpdateView: function () {
+                    return {
+                        compare: function (actual, expected) {
+                            var view = actual,
+                                value,
+                                passed;
+
+                            view.setValueInEditor(expected);
+                            value = view.getValueFromEditor();
+                            passed = _.isEqual(value, expected);
+
+                            return {
+                                pass: passed,
+                                message: 'Expected ' + actual + (passed ? '' : ' not') + ' to equal ' + expected
+                            };
+                        }
+                    };
                 },
-                assertClear: function (modelValue) {
-                    var env = this.env,
-                        view = this.actual,
-                        model = view.model;
-
-                    return model.getValue() === null &&
-                           env.equals_(model.getDisplayValue(), modelValue) &&
-                           env.equals_(view.getValueFromEditor(), modelValue);
+                assertClear: function () {
+                    return {
+                        compare: function (actual, modelValue) {
+                            var view = actual,
+                                model = view.model,
+                                passed;
+
+                            passed = model.getValue() === null &&
+                                _.isEqual(model.getDisplayValue(), modelValue) &&
+                                _.isEqual(view.getValueFromEditor(), modelValue);
+
+                            return {
+                                pass: passed
+                            };
+                        }
+                    };
                 },
-                assertUpdateModel: function (originalValue, newValue) {
-                    var env = this.env,
-                        view = this.actual,
-                        model = view.model,
-                        expectOriginal;
-
-                    view.setValueInEditor(newValue);
-                    expectOriginal = env.equals_(model.getValue(), originalValue);
-                    view.updateModel();
-
-                    return expectOriginal &&
-                           env.equals_(model.getValue(), newValue);
+                assertUpdateModel: function () {
+                    return {
+                        compare: function (actual, originalValue, newValue) {
+                            var view = actual,
+                                model = view.model,
+                                expectOriginal,
+                                passed;
+
+                            view.setValueInEditor(newValue);
+                            expectOriginal = _.isEqual(model.getValue(), originalValue);
+                            view.updateModel();
+
+                            passed = expectOriginal &&
+                                _.isEqual(model.getValue(), newValue);
+
+                            return {
+                                pass: passed
+                            };
+                        }
+                    };
                 },
-                verifyKeysUnique: function (initial, expected, testData) {
-                    var env = this.env,
-                        view = this.actual,
-                        item, value;
-
-                    view.setValueInEditor(initial);
-                    view.updateModel();
-                    view.$el.find('.create-setting').click();
-                    item = view.$el.find('.list-settings-item').last();
-                    item.find('select').val(testData.key);
-                    item.find('input:hidden').val(testData.value);
-                    value = view.getValueFromEditor();
-
-                    return env.equals_(value, expected);
+                verifyKeysUnique: function () {
+                    return {
+                        compare: function (actual, initial, expected, testData) {
+                            var view = this.actual,
+                                item,
+                                value,
+                                passed;
+
+                            view.setValueInEditor(initial);
+                            view.updateModel();
+                            view.$el.find('.create-setting').click();
+                            item = view.$el.find('.list-settings-item').last();
+                            item.find('select').val(testData.key);
+                            item.find('input:hidden').val(testData.value);
+                            value = view.getValueFromEditor();
+
+                            passed = _.isEqual(value, expected);
+
+                            return {
+                                pass: passed
+                            };
+                        }
+                    };
                 },
-                verifyButtons: function (upload, download, remove, index) {
-                    var view = this.actual,
-                        items = view.$el.find('.list-settings-item'),
-                        item  = index ? items.eq(index) : items.last(),
-                        uploadBtn = item.find('.upload-setting'),
-                        downloadBtn = item.find('.download-setting'),
-                        removeBtn = item.find('.remove-setting');
-
-
-                    upload = upload ? uploadBtn.length : !uploadBtn.length;
-                    download = download ? downloadBtn.length : !downloadBtn.length;
-                    remove = remove ? removeBtn.length : !removeBtn.length;
-
-                    return upload && download && remove;
-
+                verifyButtons: function () {
+                    return {
+                        compare: function (actual, upload, download, remove, index) {
+                            var view = this.actual,
+                                items = view.$el.find('.list-settings-item'),
+                                item = index ? items.eq(index) : items.last(),
+                                uploadBtn = item.find('.upload-setting'),
+                                downloadBtn = item.find('.download-setting'),
+                                removeBtn = item.find('.remove-setting'),
+                                passed;
+
+
+                            upload = upload ? uploadBtn.length : !uploadBtn.length;
+                            download = download ? downloadBtn.length : !downloadBtn.length;
+                            remove = remove ? removeBtn.length : !removeBtn.length;
+
+                            passed = upload && download && remove;
+
+                            return {
+                                pass: passed
+                            };
+                        }
+                    };
                 }
             });
 
@@ -141,19 +192,15 @@ function ($, _, Squire) {
                 return self.uploadSpies;
             });
 
-            runs(function() {
-                injector.require([
+            injector.require([
                     'js/models/metadata', 'js/views/video/translations_editor'
                 ],
-                function(MetadataModel, Translations) {
+                function (MetadataModel, Translations) {
                     var model = new MetadataModel($.extend(true, {}, modelStub));
                     self.view = new Translations({model: model});
-                });
-            });
 
-            waitsFor(function() {
-                return self.view;
-            }, 'VideoTranslations was not created', 1000);
+                    done();
+                });
         });
 
         afterEach(function () {
@@ -198,7 +245,7 @@ function ($, _, Squire) {
             expect(this.uploadSpies.constructor).toHaveBeenCalled();
             expect(this.uploadSpies.show).toHaveBeenCalled();
 
-            options = this.uploadSpies.constructor.mostRecentCall.args[0];
+            options = this.uploadSpies.constructor.calls.mostRecent().args[0];
             options.onSuccess({'filename': 'zh.srt'});
 
             expect(this.view).verifyButtons(true, true, true);
diff --git a/cms/static/js/spec/views/active_video_upload_list_spec.js b/cms/static/js/spec/views/active_video_upload_list_spec.js
index bd1b7fd..1b985e2 100644
--- a/cms/static/js/spec/views/active_video_upload_list_spec.js
+++ b/cms/static/js/spec/views/active_video_upload_list_spec.js
@@ -1,5 +1,11 @@
 define(
-    ["jquery", "js/models/active_video_upload", "js/views/active_video_upload_list", "common/js/spec_helpers/template_helpers", "mock-ajax", "jasmine-jquery"],
+    [
+        "jquery",
+        "js/models/active_video_upload",
+        "js/views/active_video_upload_list",
+        "common/js/spec_helpers/template_helpers",
+        "mock-ajax"
+    ],
     function($, ActiveVideoUpload, ActiveVideoUploadListView, TemplateHelpers) {
         "use strict";
         var concurrentUploadLimit = 2;
@@ -16,8 +22,7 @@ define(
                     uploadButton: this.uploadButton
                 });
                 this.view.render();
-                jasmine.Ajax.useMock();
-                clearAjaxRequests();
+                jasmine.Ajax.install();
                 this.globalAjaxError = jasmine.createSpy();
                 $(document).ajaxError(this.globalAjaxError);
             });
@@ -25,15 +30,16 @@ define(
             // Remove window unload handler triggered by the upload requests
             afterEach(function() {
                 $(window).off("beforeunload");
+                jasmine.Ajax.uninstall();
             });
 
             it("should trigger file selection when either the upload button or the drop zone is clicked", function() {
                 var clickSpy = jasmine.createSpy();
-                clickSpy.andCallFake(function(event) { event.preventDefault(); });
+                clickSpy.and.callFake(function(event) { event.preventDefault(); });
                 this.view.$(".js-file-input").on("click", clickSpy);
                 this.view.$(".file-drop-area").click();
                 expect(clickSpy).toHaveBeenCalled();
-                clickSpy.reset();
+                clickSpy.calls.reset();
                 this.uploadButton.click();
                 expect(clickSpy).toHaveBeenCalled();
             });
@@ -47,17 +53,16 @@ define(
             };
 
             var getSentRequests = function() {
-                return _.filter(
-                    ajaxRequests,
-                    function(request) { return request.readyState > 0; }
-                );
+                return jasmine.Ajax.requests.filter(function (request) {
+                    return request.readyState > 0;
+                });
             };
 
             _.each(
                 [
                     {desc: "a single file", numFiles: 1},
                     {desc: "multiple files", numFiles: concurrentUploadLimit},
-                    {desc: "more files than upload limit", numFiles: concurrentUploadLimit + 1},
+                    {desc: "more files than upload limit", numFiles: concurrentUploadLimit + 1}
                 ],
                 function(caseInfo) {
                     var fileNames = _.map(
@@ -71,7 +76,7 @@ define(
                             // security reasons, so we must mock the access mechanism
                             // that jQuery-File-Upload uses to retrieve it.
                             var realProp = $.prop;
-                            spyOn($, "prop").andCallFake(function(el, propName) {
+                            spyOn($, "prop").and.callFake(function(el, propName) {
                                 if (arguments.length == 2 && propName == "files") {
                                     return _.map(
                                         fileNames,
@@ -82,7 +87,7 @@ define(
                                 }
                             });
                             this.view.$(".js-file-input").change();
-                            this.request = mostRecentAjaxRequest();
+                            this.request = jasmine.Ajax.requests.mostRecent();
                         });
 
                         it("should trigger the correct request", function() {
@@ -99,14 +104,14 @@ define(
                         });
 
                         it("should trigger the global AJAX error handler on server error", function() {
-                            this.request.response({status: 500});
+                            this.request.respondWith({status: 500});
                             expect(this.globalAjaxError).toHaveBeenCalled();
                         });
 
                         describe("and successful server response", function() {
                             beforeEach(function() {
-                                clearAjaxRequests();
-                                this.request.response({
+                                jasmine.Ajax.requests.reset();
+                                this.request.respondWith({
                                     status: 200,
                                     responseText: JSON.stringify({
                                         files: _.map(
@@ -141,7 +146,6 @@ define(
                             });
 
                             it("should display upload status and progress", function() {
-                                var spec = this;
                                 expect(this.$uploadElems.length).toEqual(caseInfo.numFiles);
                                 this.$uploadElems.each(function(i, uploadElem) {
                                     var $uploadElem = $(uploadElem);
@@ -154,7 +158,7 @@ define(
                                             ActiveVideoUpload.STATUS_QUEUED :
                                             ActiveVideoUpload.STATUS_UPLOADING
                                     );
-                                    expect($uploadElem.find(".video-detail-progress").attr("value")).toEqual(0);
+                                    expect($uploadElem.find(".video-detail-progress").val()).toEqual(0);
                                     expect($uploadElem).not.toHaveClass("success");
                                     expect($uploadElem).not.toHaveClass("error");
                                     expect($uploadElem.hasClass("queued")).toEqual(queued);
@@ -187,12 +191,12 @@ define(
                                         progressValue: 0,
                                         presentClass: "error",
                                         absentClass: "success"
-                                    },
+                                    }
                                 ],
                                 function(subCaseInfo) {
                                     describe("and upload " + subCaseInfo.desc, function() {
                                         beforeEach(function() {
-                                            getSentRequests()[0].response({status: subCaseInfo.responseStatus});
+                                            getSentRequests()[0].respondWith({status: subCaseInfo.responseStatus});
                                         });
 
                                         it("should update status and progress", function() {
@@ -202,7 +206,7 @@ define(
                                                 subCaseInfo.statusText
                                             );
                                             expect(
-                                                $uploadElem.find(".video-detail-progress").attr("value")
+                                                $uploadElem.find(".video-detail-progress").val()
                                             ).toEqual(subCaseInfo.progressValue);
                                             expect($uploadElem).toHaveClass(subCaseInfo.presentClass);
                                             expect($uploadElem).not.toHaveClass(subCaseInfo.absentClass);
diff --git a/cms/static/js/spec/views/assets_spec.js b/cms/static/js/spec/views/assets_spec.js
index 87c5006..921c2b7 100644
--- a/cms/static/js/spec/views/assets_spec.js
+++ b/cms/static/js/spec/views/assets_spec.js
@@ -16,7 +16,7 @@ define([ "jquery", "common/js/spec_helpers/ajax_helpers", "URI", "js/views/asset
                 appendSetFixtures(uploadModalTpl);
                 appendSetFixtures(sandbox({ id: "asset_table_body" }));
 
-                spyOn($.fn, "fileupload").andReturn("");
+                spyOn($.fn, "fileupload").and.returnValue("");
 
                 var collection = new AssetCollection();
                 collection.url = "assets-url";
@@ -181,7 +181,7 @@ define([ "jquery", "common/js/spec_helpers/ajax_helpers", "URI", "js/views/asset
                 it('uploads file properly', function () {
                     var requests = setup.call(this);
                     expect(assetsView).toBeDefined();
-                    spyOn(assetsView, "addAsset").andCallFake(function () {
+                    spyOn(assetsView, "addAsset").and.callFake(function () {
                         assetsView.collection.add(mockAssetUploadResponse.asset);
                         assetsView.pagingView.renderPageItems();
                         assetsView.pagingView.setPage(0);
diff --git a/cms/static/js/spec/views/baseview_spec.js b/cms/static/js/spec/views/baseview_spec.js
index 4d88992..b88a91c 100644
--- a/cms/static/js/spec/views/baseview_spec.js
+++ b/cms/static/js/spec/views/baseview_spec.js
@@ -13,8 +13,8 @@ define(["jquery", "underscore", "js/views/baseview", "js/utils/handle_iframe_bin
 
                     spyOn(baseViewPrototype, 'initialize');
                     spyOn(baseViewPrototype, 'beforeRender');
-                    spyOn(baseViewPrototype, 'render').andCallThrough();
-                    spyOn(baseViewPrototype, 'afterRender').andCallThrough();
+                    spyOn(baseViewPrototype, 'render').and.callThrough();
+                    spyOn(baseViewPrototype, 'afterRender').and.callThrough();
                 });
 
                 afterEach(function () {
diff --git a/cms/static/js/spec/views/group_configuration_spec.js b/cms/static/js/spec/views/group_configuration_spec.js
index 9309e04..b049f2f 100644
--- a/cms/static/js/spec/views/group_configuration_spec.js
+++ b/cms/static/js/spec/views/group_configuration_spec.js
@@ -125,7 +125,7 @@ define([
         ViewHelpers.verifyNotificationShowing(notificationSpy, /Deleting/);
         expect($(listItemView)).toExist();
     };
-    var assertCannotDeleteUsed = function (that, toolTipText, warningText){
+    var assertCannotDeleteUsed = function (that, toolTipText, warningText) {
         setUsageInfo(that.model);
         that.view.render();
         expect(that.view.$(SELECTORS.note)).toHaveAttr(
@@ -153,36 +153,69 @@ define([
             revision: 'course_rev'
         });
 
-        this.addMatchers({
-            toContainText: function(text) {
-                var trimmedText = $.trim(this.actual.text());
-
-                if (text && $.isFunction(text.test)) {
-                    return text.test(trimmedText);
-                } else {
-                    return trimmedText.indexOf(text) !== -1;
-                }
+        jasmine.addMatchers({
+            toContainText: function() {
+                return {
+                    compare: function (actual, text) {
+                        var trimmedText = $.trim(actual.text()),
+                            passed;
+
+                        if (text && $.isFunction(text.test)) {
+                            passed = text.test(trimmedText);
+                        } else {
+                            passed = trimmedText.indexOf(text) !== -1;
+                        }
+
+                        return {
+                            pass: passed
+                        };
+                    }
+                };
             },
-            toBeCorrectValuesInInputs: function (values) {
-                var expected = {
-                    name: this.actual.$(SELECTORS.inputName).val(),
-                    description: this.actual
-                        .$(SELECTORS.inputDescription).val()
+            toBeCorrectValuesInInputs: function () {
+                return {
+                    compare: function (actual, values) {
+                        var expected = {
+                            name: actual.$(SELECTORS.inputName).val(),
+                            description: actual
+                                .$(SELECTORS.inputDescription).val()
+                        };
+
+                        var passed =  _.isEqual(values, expected);
+
+                        return {
+                            pass: passed
+                        };
+                    }
                 };
-
-                return _.isEqual(values, expected);
             },
-            toBeCorrectValuesInModel: function (values) {
-                return _.every(values, function (value, key) {
-                    return this.actual.get(key) === value;
-                }.bind(this));
+            toBeCorrectValuesInModel: function () {
+                return {
+                    compare: function (actual, values) {
+                        var passed = _.every(values, function (value, key) {
+                            return actual.get(key) === value;
+                        }.bind(this));
+
+                        return {
+                            pass: passed
+                        };
+                    }
+                };
             },
-            toHaveDefaultNames: function (values) {
-                var actualValues = $.map(this.actual, function (item) {
-                    return $(item).val();
-                });
-
-                return _.isEqual(actualValues, values);
+            toHaveDefaultNames: function () {
+                return {
+                    compare: function (actual, values) {
+                        var actualValues = $.map(actual, function (item) {
+                            return $(item).val();
+                        });
+
+                        var passed = _.isEqual(actualValues, values);
+
+                        return {
+                            pass: passed
+                        };
+                    }
+                };
             }
         });
     });
@@ -389,7 +422,7 @@ define([
             groups = this.model.get('groups');
             expect(groups.length).toBe(3);
             expect(groups.at(2).get('name')).toBe('Group C');
-            expect(this.view.$el).not.toExist();
+            expect(this.view.$el).not.toBeInDOM();
         });
 
         it('does not hide saving message if failure', function() {
@@ -421,7 +454,7 @@ define([
         });
 
         it('should be removed on cancel if it is a new item', function() {
-            spyOn(this.model, 'isNew').andReturn(true);
+            spyOn(this.model, 'isNew').and.returnValue(true);
             setValuesToInputs(this.view, {
                 inputName: 'New Configuration',
                 inputDescription: 'New Description'
@@ -454,18 +487,23 @@ define([
                 name: 'New Configuration'
             });
             // Error message disappear
-            expect(this.view.$(SELECTORS.errorMessage)).not.toExist();
+            expect(this.view.$(SELECTORS.errorMessage)).not.toBeInDOM();
             AjaxHelpers.expectNoRequests(requests);
         });
 
-        it('should have appropriate class names on focus/blur', function () {
+        it('should have appropriate class names on focus/blur', function (done) {
             var groupInput = this.view.$(SELECTORS.inputGroupName).first(),
                 groupFields = this.view.$(SELECTORS.groupFields);
 
             groupInput.focus();
-            expect(groupFields).toHaveClass('is-focused');
-            groupInput.blur();
-            expect(groupFields).not.toHaveClass('is-focused');
+            jasmine.waitUntil(function() {
+                return groupFields.hasClass('is-focused');
+            }).then(function () {
+                groupInput.blur();
+                jasmine.waitUntil(function() {
+                    return !groupFields.hasClass('is-focused');
+                }).then(done);
+            });
         });
 
         describe('removes all newly created groups on cancel', function () {
@@ -957,13 +995,13 @@ define([
             expect(this.model).toBeCorrectValuesInModel({
                 name: 'New Content Group'
             });
-            expect(this.view.$el).not.toExist();
+            expect(this.view.$el).not.toBeInDOM();
         });
 
         it('does not hide saving message if failure', function() {
             var requests = AjaxHelpers.requests(this),
                 notificationSpy = ViewHelpers.createNotificationSpy();
-            this.view.$(SELECTORS.inputName).val('New Content Group')
+            this.view.$(SELECTORS.inputName).val('New Content Group');
 
             ViewHelpers.submitAndVerifyFormError(this.view, requests, notificationSpy)
         });
diff --git a/cms/static/js/spec/views/login_studio_spec.js b/cms/static/js/spec/views/login_studio_spec.js
index 2fb44c2..b5c752f 100644
--- a/cms/static/js/spec/views/login_studio_spec.js
+++ b/cms/static/js/spec/views/login_studio_spec.js
@@ -12,7 +12,7 @@ function($, LoginFactory, AjaxHelpers, ViewUtils) {
         });
 
         it('disable the submit button once it is clicked', function() {
-            spyOn(ViewUtils, 'redirect').andCallFake(function(){});
+            spyOn(ViewUtils, 'redirect').and.callFake(function(){});
             var requests = AjaxHelpers.requests(this);
             expect(submitButton).not.toHaveClass('is-disabled');
             submitButton.click();
diff --git a/cms/static/js/spec/views/modals/base_modal_spec.js b/cms/static/js/spec/views/modals/base_modal_spec.js
index 6d752ee..5878ce0 100644
--- a/cms/static/js/spec/views/modals/base_modal_spec.js
+++ b/cms/static/js/spec/views/modals/base_modal_spec.js
@@ -31,14 +31,13 @@ define(["jquery", "underscore", "js/views/modals/base_modal", "js/spec_helpers/m
                     expect(ModelHelpers.isShowingModal(modal)).toBeTruthy();
                 });
 
-                it('sends focus to the modal window after show is called', function() {
+                it('sends focus to the modal window after show is called', function(done) {
                     showMockModal();
-                    waitsFor(function () {
-                        // This is the implementation of "toBeFocused". However, simply calling that method
-                        // with no wait seems to be flaky.
+
+                    jasmine.waitUntil(function() {
                         var modalWindow = ModelHelpers.getModalWindow(modal);
-                        return $(modalWindow)[0] === $(modalWindow)[0].ownerDocument.activeElement;
-                    }, 'Modal Window did not get focus', 5000);
+                        return ($(modalWindow)[0] === $(modalWindow)[0].ownerDocument.activeElement);
+                    }).then(done);
                 });
 
                 it('is removed after hide is called', function () {
diff --git a/cms/static/js/spec/views/modals/validation_error_modal_spec.js b/cms/static/js/spec/views/modals/validation_error_modal_spec.js
index 7a722fd..605abad 100644
--- a/cms/static/js/spec/views/modals/validation_error_modal_spec.js
+++ b/cms/static/js/spec/views/modals/validation_error_modal_spec.js
@@ -51,7 +51,7 @@ define(['jquery', 'underscore', 'js/spec_helpers/validation_helpers', 'js/views/
                 ValidationHelpers.checkErrorContents(modal, errorObjects);
             });
 
-            it('run callback when undo changes button is clicked', function () {
+            it('run callback when undo changes button is clicked', function (done) {
                 var errorObjects = [
                     {
                         model: {display_name: 'test_attribute1'},
@@ -64,7 +64,7 @@ define(['jquery', 'underscore', 'js/spec_helpers/validation_helpers', 'js/views/
                 ];
 
                 var callback = function() {
-                    return true;
+                    done();
                 };
 
                 // Show Modal and click undo changes
@@ -72,15 +72,8 @@ define(['jquery', 'underscore', 'js/spec_helpers/validation_helpers', 'js/views/
                 expect(ValidationHelpers.isShowingModal(modal)).toBeTruthy();
                 ValidationHelpers.undoChanges(modal);
 
-                // Wait for the callback to be fired
-                waitsFor(function () {
-                    return callback();
-                }, 'the callback to be called', 5000);
-
                 // After checking callback fire, check modal hide
-                runs(function () {
-                    expect(ValidationHelpers.isShowingModal(modal)).toBe(false);
-                });
+                expect(ValidationHelpers.isShowingModal(modal)).toBe(false);
             });
         });
     });
diff --git a/cms/static/js/spec/views/paged_container_spec.js b/cms/static/js/spec/views/paged_container_spec.js
index fd55c3e..282ae33 100644
--- a/cms/static/js/spec/views/paged_container_spec.js
+++ b/cms/static/js/spec/views/paged_container_spec.js
@@ -74,7 +74,10 @@ define(["jquery", "underscore", "common/js/spec_helpers/ajax_helpers", "URI", "j
             var pagingContainer;
 
             beforeEach(function () {
-                pagingContainer = new MockPagingView({page_size: PAGE_SIZE});
+                pagingContainer = new MockPagingView({
+                    page_size: PAGE_SIZE,
+                    page: jasmine.createSpyObj('page', ['updatePreviewButton', 'renderAddXBlockComponents'])
+                });
             });
 
             describe("Container", function () {
@@ -546,7 +549,7 @@ define(["jquery", "underscore", "common/js/spec_helpers/ajax_helpers", "URI", "j
                         mockXBlockView.model.id = 'mock-location';
                         pagingContainer.refresh(mockXBlockView, true);
                         expect(pagingContainer.render).toHaveBeenCalled();
-                        expect(pagingContainer.render.mostRecentCall.args[0].force_render).toEqual('mock-location');
+                        expect(pagingContainer.render.calls.mostRecent().args[0].force_render).toEqual('mock-location');
                     });
                 });
             });
diff --git a/cms/static/js/spec/views/pages/container_spec.js b/cms/static/js/spec/views/pages/container_spec.js
index fe78638..43d36f2 100644
--- a/cms/static/js/spec/views/pages/container_spec.js
+++ b/cms/static/js/spec/views/pages/container_spec.js
@@ -70,7 +70,7 @@ define(["jquery", "underscore", "underscore.string", "common/js/spec_helpers/aja
                     containerPage.render();
                     respondWithHtml(html);
                     AjaxHelpers.expectJsonRequest(requests, 'GET', '/xblock/locator-container');
-                    AjaxHelpers.respondWithJson(requests, options);
+                    AjaxHelpers.respondWithJson(requests, options || {});
                 };
 
                 handleContainerPageRefresh = function(requests) {
diff --git a/cms/static/js/spec/views/pages/container_subviews_spec.js b/cms/static/js/spec/views/pages/container_subviews_spec.js
index 744b991..1d5385b 100644
--- a/cms/static/js/spec/views/pages/container_subviews_spec.js
+++ b/cms/static/js/spec/views/pages/container_subviews_spec.js
@@ -1,9 +1,10 @@
 define(["jquery", "underscore", "underscore.string", "common/js/spec_helpers/ajax_helpers",
         "common/js/spec_helpers/template_helpers", "js/spec_helpers/edit_helpers",
         "common/js/components/views/feedback_prompt", "js/views/pages/container",
-        "js/views/pages/container_subviews", "js/models/xblock_info", "js/views/utils/xblock_utils"],
+        "js/views/pages/container_subviews", "js/models/xblock_info", "js/views/utils/xblock_utils",
+        'js/models/course'],
     function ($, _, str, AjaxHelpers, TemplateHelpers, EditHelpers, Prompt, ContainerPage, ContainerSubviews,
-              XBlockInfo, XBlockUtils) {
+              XBlockInfo, XBlockUtils, Course) {
         var VisibilityState = XBlockUtils.VisibilityState;
 
         describe("Container Subviews", function() {
@@ -14,12 +15,26 @@ define(["jquery", "underscore", "underscore.string", "common/js/spec_helpers/aja
                 mockContainerXBlockHtml = readFixtures('mock/mock-empty-container-xblock.underscore');
 
             beforeEach(function () {
+                window.course = new Course({
+                    id: '5',
+                    name: 'Course Name',
+                    url_name: 'course_name',
+                    org: 'course_org',
+                    num: 'course_num',
+                    revision: 'course_rev'
+                });
+
                 TemplateHelpers.installTemplate('xblock-string-field-editor');
                 TemplateHelpers.installTemplate('publish-xblock');
                 TemplateHelpers.installTemplate('publish-history');
                 TemplateHelpers.installTemplate('unit-outline');
                 TemplateHelpers.installTemplate('container-message');
                 appendSetFixtures(mockContainerPage);
+                requests = AjaxHelpers.requests(this);
+            });
+
+            afterEach(function() {
+                delete window.course;
             });
 
             defaultXBlockInfo = {
@@ -39,7 +54,6 @@ define(["jquery", "underscore", "underscore.string", "common/js/spec_helpers/aja
             };
 
             createContainerPage = function (test, options) {
-                requests = AjaxHelpers.requests(test);
                 model = new XBlockInfo(createXBlockInfo(options), { parse: true });
                 containerPage = new ContainerPage({
                     model: model,
@@ -135,7 +149,7 @@ define(["jquery", "underscore", "underscore.string", "common/js/spec_helpers/aja
 
                     // Confirm the discard.
                     expect(promptSpies.constructor).toHaveBeenCalled();
-                    promptSpies.constructor.mostRecentCall.args[0].actions.primary.click(promptSpies);
+                    promptSpies.constructor.calls.mostRecent().args[0].actions.primary.click(promptSpies);
 
                     AjaxHelpers.expectJsonRequest(requests, "POST", "/xblock/locator-container",
                         {"publish": "discard_changes"}
@@ -152,8 +166,8 @@ define(["jquery", "underscore", "underscore.string", "common/js/spec_helpers/aja
                 };
 
                 beforeEach(function() {
-                    promptSpies = spyOnConstructor(Prompt, "Warning", ["show", "hide"]);
-                    promptSpies.show.andReturn(this.promptSpies);
+                    promptSpies = jasmine.stealth.spyOnConstructor(Prompt, "Warning", ["show", "hide"]);
+                    promptSpies.show.and.returnValue(this.promptSpies);
                 });
 
                 it('renders correctly with private content', function () {
@@ -268,7 +282,7 @@ define(["jquery", "underscore", "underscore.string", "common/js/spec_helpers/aja
                     var notificationSpy, renderPageSpy, numRequests;
                     createContainerPage(this);
                     notificationSpy = EditHelpers.createNotificationSpy();
-                    renderPageSpy = spyOn(containerPage.xblockPublisher, 'renderPage').andCallThrough();
+                    renderPageSpy = spyOn(containerPage.xblockPublisher, 'renderPage').and.callThrough();
 
                     sendDiscardChangesToServer();
                     numRequests = requests.length;
@@ -287,7 +301,7 @@ define(["jquery", "underscore", "underscore.string", "common/js/spec_helpers/aja
                 it('does not fetch if discard changes fails', function () {
                     var renderPageSpy, numRequests;
                     createContainerPage(this);
-                    renderPageSpy = spyOn(containerPage.xblockPublisher, 'renderPage').andCallThrough();
+                    renderPageSpy = spyOn(containerPage.xblockPublisher, 'renderPage').and.callThrough();
 
                     sendDiscardChangesToServer();
 
@@ -309,7 +323,7 @@ define(["jquery", "underscore", "underscore.string", "common/js/spec_helpers/aja
 
                     // Click cancel to confirmation.
                     expect(promptSpies.constructor).toHaveBeenCalled();
-                    promptSpies.constructor.mostRecentCall.args[0].actions.secondary.click(promptSpies);
+                    promptSpies.constructor.calls.mostRecent().args[0].actions.secondary.click(promptSpies);
                     AjaxHelpers.expectNoRequests(requests);
                     expect(containerPage.$(discardChangesButtonCss)).not.toHaveClass('is-disabled');
                 });
diff --git a/cms/static/js/spec/views/pages/course_outline_spec.js b/cms/static/js/spec/views/pages/course_outline_spec.js
index 2db0878..e8db1e1 100644
--- a/cms/static/js/spec/views/pages/course_outline_spec.js
+++ b/cms/static/js/spec/views/pages/course_outline_spec.js
@@ -1,14 +1,15 @@
 define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/utils/view_utils", "js/views/pages/course_outline",
         "js/models/xblock_outline_info", "js/utils/date_utils", "js/spec_helpers/edit_helpers",
-        "common/js/spec_helpers/template_helpers"],
-    function($, AjaxHelpers, ViewUtils, CourseOutlinePage, XBlockOutlineInfo, DateUtils, EditHelpers, TemplateHelpers) {
+        "common/js/spec_helpers/template_helpers", 'js/models/course',],
+    function($, AjaxHelpers, ViewUtils, CourseOutlinePage, XBlockOutlineInfo, DateUtils,
+             EditHelpers, TemplateHelpers, Course) {
 
         describe("CourseOutlinePage", function() {
             var createCourseOutlinePage, displayNameInput, model, outlinePage, requests,
                 getItemsOfType, getItemHeaders, verifyItemsExpanded, expandItemsAndVerifyState,
                 collapseItemsAndVerifyState, createMockCourseJSON, createMockSectionJSON, createMockSubsectionJSON,
                 verifyTypePublishable, mockCourseJSON, mockEmptyCourseJSON, mockSingleSectionCourseJSON,
-                createMockVerticalJSON, createMockIndexJSON, mockCourseEntranceExamJSON
+                createMockVerticalJSON, createMockIndexJSON, mockCourseEntranceExamJSON,
                 mockOutlinePage = readFixtures('mock/mock-course-outline-page.underscore'),
                 mockRerunNotification = readFixtures('mock/mock-course-rerun-notification.underscore');
 
@@ -214,6 +215,15 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
             };
 
             beforeEach(function () {
+                window.course = new Course({
+                    id: '5',
+                    name: 'Course Name',
+                    url_name: 'course_name',
+                    org: 'course_org',
+                    num: 'course_num',
+                    revision: 'course_rev'
+                });
+
                 EditHelpers.installMockAnalytics();
                 EditHelpers.installViewTemplates();
                 TemplateHelpers.installTemplates([
@@ -252,6 +262,7 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
                 $("#start_date").datepicker( "destroy" );
                 $("#due_date").datepicker( "destroy" );
                 $('.ui-datepicker').remove();
+                delete window.course;
             });
 
             describe('Initial display', function() {
@@ -346,8 +357,8 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
 
                 it('can start reindex of a course', function() {
                     createCourseOutlinePage(this, mockSingleSectionCourseJSON);
-                    var reindexSpy = spyOn(outlinePage, 'startReIndex').andCallThrough();
-                    var successSpy = spyOn(outlinePage, 'onIndexSuccess').andCallThrough();
+                    var reindexSpy = spyOn(outlinePage, 'startReIndex').and.callThrough();
+                    var successSpy = spyOn(outlinePage, 'onIndexSuccess').and.callThrough();
                     var reindexButton = outlinePage.$('.button.button-reindex');
                     var test_url = '/course/5/search_reindex';
                     reindexButton.attr('href', test_url)
@@ -360,11 +371,11 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
 
                 it('shows an error message when reindexing fails', function() {
                     createCourseOutlinePage(this, mockSingleSectionCourseJSON);
-                    var reindexSpy = spyOn(outlinePage, 'startReIndex').andCallThrough();
-                    var errorSpy = spyOn(outlinePage, 'onIndexError').andCallThrough();
+                    var reindexSpy = spyOn(outlinePage, 'startReIndex').and.callThrough();
+                    var errorSpy = spyOn(outlinePage, 'onIndexError').and.callThrough();
                     var reindexButton = outlinePage.$('.button.button-reindex');
                     var test_url = '/course/5/search_reindex';
-                    reindexButton.attr('href', test_url)
+                    reindexButton.attr('href', test_url);
                     reindexButton.trigger('click');
                     AjaxHelpers.expectJsonRequest(requests, 'GET', test_url);
                     AjaxHelpers.respondWithError(requests, 500, createMockIndexJSON(false));
diff --git a/cms/static/js/spec/views/pages/group_configurations_spec.js b/cms/static/js/spec/views/pages/group_configurations_spec.js
index 52c5316..03e8397 100644
--- a/cms/static/js/spec/views/pages/group_configurations_spec.js
+++ b/cms/static/js/spec/views/pages/group_configurations_spec.js
@@ -40,9 +40,15 @@ define([
                 'content-group-editor', 'group-edit', 'list'
             ]);
 
-            this.addMatchers({
+            jasmine.addMatchers({
                 toBeExpanded: function () {
-                    return Boolean($('a.group-toggle.hide-groups', $(this.actual)).length);
+                    return {
+                        compare: function (actual) {
+                            return {
+                                pass: Boolean($('a.group-toggle.hide-groups', $(actual)).length)
+                            };
+                        }
+                    };
                 }
             });
         });
@@ -67,7 +73,7 @@ define([
             });
 
             it('should focus and expand if its id is part of url hash', function() {
-                spyOn(this.view, 'getLocationHash').andReturn('#0');
+                spyOn(this.view, 'getLocationHash').and.returnValue('#0');
                 this.view.render();
                 // We cannot use .toBeFocused due to flakiness.
                 expect($.fn.focus).toHaveBeenCalled();
@@ -75,14 +81,14 @@ define([
             });
 
             it('should not focus on any experiment configuration if url hash is empty', function() {
-                spyOn(this.view, 'getLocationHash').andReturn('');
+                spyOn(this.view, 'getLocationHash').and.returnValue('');
                 this.view.render();
                 expect($.fn.focus).not.toHaveBeenCalled();
                 expect(this.view.$(groupConfigItemClassName)).not.toBeExpanded();
             });
 
             it('should not focus on any experiment configuration if url hash contains wrong id', function() {
-                spyOn(this.view, 'getLocationHash').andReturn('#1');
+                spyOn(this.view, 'getLocationHash').and.returnValue('#1');
                 this.view.render();
                 expect($.fn.focus).not.toHaveBeenCalled();
                 expect(this.view.$(groupConfigItemClassName)).not.toBeExpanded();
diff --git a/cms/static/js/spec/views/pages/library_users_spec.js b/cms/static/js/spec/views/pages/library_users_spec.js
index 3a747d9..1c77948 100644
--- a/cms/static/js/spec/views/pages/library_users_spec.js
+++ b/cms/static/js/spec/views/pages/library_users_spec.js
@@ -22,7 +22,7 @@ function ($, AjaxHelpers, ViewHelpers, ManageUsersFactory, ViewUtils) {
         describe("read-write access", function() {
             var mockHTML = readFixtures('mock/mock-manage-users-lib.underscore');
 
-            beforeEach(function () {
+            beforeEach(function (done) {
                 ViewHelpers.installMockAnalytics();
                 setFixtures(mockHTML);
                 appendSetFixtures($("<script>", { id: "team-member-tpl", type: "text/template"}).text(team_member_fixture));
@@ -37,9 +37,10 @@ function ($, AjaxHelpers, ViewHelpers, ManageUsersFactory, ViewUtils) {
                     10000,
                     true
                 );
-                waitsFor(function(){
-                   return $(".ui-loading").length === 0;
-                }, "Waiting for backbone render to happen", 1000);
+
+                jasmine.waitUntil(function() {
+                    return ($(".ui-loading").length === 0);
+                }).then(done);
             });
 
             afterEach(function () {
diff --git a/cms/static/js/spec/views/previous_video_upload_spec.js b/cms/static/js/spec/views/previous_video_upload_spec.js
index 0244b7f..63e965a 100644
--- a/cms/static/js/spec/views/previous_video_upload_spec.js
+++ b/cms/static/js/spec/views/previous_video_upload_spec.js
@@ -47,7 +47,7 @@ define(
 
             it("should render created timestamp correctly", function() {
                 var fakeDate = "fake formatted date";
-                spyOn(Date.prototype, "toLocaleString").andCallFake(
+                spyOn(Date.prototype, "toLocaleString").and.callFake(
                     function(locales, options) {
                         expect(locales).toEqual([]);
                         expect(options.timeZone).toEqual("UTC");
diff --git a/cms/static/js/spec/views/unit_outline_spec.js b/cms/static/js/spec/views/unit_outline_spec.js
index c033018..2f5b5ef 100644
--- a/cms/static/js/spec/views/unit_outline_spec.js
+++ b/cms/static/js/spec/views/unit_outline_spec.js
@@ -1,6 +1,8 @@
 define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/spec_helpers/template_helpers",
-        "common/js/spec_helpers/view_helpers", "common/js/components/utils/view_utils", "js/views/unit_outline", "js/models/xblock_info"],
-    function ($, AjaxHelpers, TemplateHelpers, ViewHelpers, ViewUtils, UnitOutlineView, XBlockInfo) {
+        "common/js/spec_helpers/view_helpers", "common/js/components/utils/view_utils", "js/models/course",
+        "js/views/unit_outline", "js/models/xblock_info"],
+    function ($, AjaxHelpers, TemplateHelpers, ViewHelpers, ViewUtils,
+              Course, UnitOutlineView, XBlockInfo) {
 
         describe("UnitOutlineView", function() {
             var createUnitOutlineView, createMockXBlockInfo,
@@ -71,6 +73,14 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/spec_helpers
             };
 
             beforeEach(function () {
+                window.course = new Course({
+                    id: '5',
+                    name: 'Course Name',
+                    url_name: 'course_name',
+                    org: 'course_org',
+                    num: 'course_num',
+                    revision: 'course_rev'
+                });
                 ViewHelpers.installMockAnalytics();
                 ViewHelpers.installViewTemplates();
                 TemplateHelpers.installTemplate('unit-outline');
@@ -78,6 +88,7 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/spec_helpers
             });
 
             afterEach(function () {
+                delete window.course;
                 ViewHelpers.removeMockAnalytics();
             });
 
diff --git a/cms/static/js/spec/views/xblock_spec.js b/cms/static/js/spec/views/xblock_spec.js
index 755d790..bcbfabf 100644
--- a/cms/static/js/spec/views/xblock_spec.js
+++ b/cms/static/js/spec/views/xblock_spec.js
@@ -91,7 +91,7 @@ define(["jquery", "URI", "common/js/spec_helpers/ajax_helpers", "common/js/compo
                     var requests = AjaxHelpers.requests(this),
                         missingJavaScriptUrl = "no_such_file.js",
                         promise;
-                    spyOn(ViewUtils, 'loadJavaScript').andReturn($.Deferred().reject().promise());
+                    spyOn(ViewUtils, 'loadJavaScript').and.returnValue($.Deferred().reject().promise());
                     promise = postXBlockRequest(requests, [
                         ["hash5", { mimetype: "application/javascript", kind: "url", data: missingJavaScriptUrl }]
                     ]);
@@ -99,7 +99,7 @@ define(["jquery", "URI", "common/js/spec_helpers/ajax_helpers", "common/js/compo
                 });
 
                 it('Triggers an event to the runtime when a notification-action-button is clicked', function () {
-                    var notifySpy = spyOn(xblockView, "notifyRuntime").andCallThrough();
+                    var notifySpy = spyOn(xblockView, "notifyRuntime").and.callThrough();
 
                     postXBlockRequest(AjaxHelpers.requests(this), []);
                     xblockView.$el.find(".notification-action-button").click();
diff --git a/cms/static/js/views/experiment_group_edit.js b/cms/static/js/views/experiment_group_edit.js
index 2a5f55e..c513b1a 100644
--- a/cms/static/js/views/experiment_group_edit.js
+++ b/cms/static/js/views/experiment_group_edit.js
@@ -3,9 +3,9 @@
  * It is expected to be backed by a Group model.
  */
 define([
-    'js/views/baseview'
+    'js/views/baseview', 'underscore', 'underscore.string', 'gettext', 'text!templates/group-edit.underscore'
 ],
-function(BaseView) {
+function(BaseView, _, str, gettext, groupEditTemplate) {
     'use strict';
     var ExperimentGroupEditView = BaseView.extend({
         tagName: 'li',
@@ -22,7 +22,6 @@ function(BaseView) {
         },
 
         initialize: function() {
-            this.template = this.loadTemplate('group-edit');
             this.listenTo(this.model, 'change', this.render);
         },
 
@@ -30,7 +29,7 @@ function(BaseView) {
             var collection = this.model.collection,
                 index = collection.indexOf(this.model);
 
-            this.$el.html(this.template({
+            this.$el.html(_.template(groupEditTemplate)({
                 name: this.model.get('name'),
                 allocation: this.getAllocation(),
                 index: index,
diff --git a/cms/static/js/views/license.js b/cms/static/js/views/license.js
index 3e8f9a1..f5a17d9 100644
--- a/cms/static/js/views/license.js
+++ b/cms/static/js/views/license.js
@@ -1,4 +1,8 @@
-define(["js/views/baseview", "underscore"], function(BaseView, _) {
+define([
+    "js/views/baseview",
+    "underscore",
+    "text!templates/license-selector.underscore"
+], function(BaseView, _, licenseSelectorTemplate) {
     var defaultLicenseInfo = {
         "all-rights-reserved": {
             "name": gettext("All Rights Reserved"),
@@ -55,7 +59,6 @@ define(["js/views/baseview", "underscore"], function(BaseView, _) {
         initialize: function(options) {
             this.licenseInfo = options.licenseInfo || defaultLicenseInfo;
             this.showPreview = !!options.showPreview; // coerce to boolean
-            this.template = this.loadTemplate("license-selector");
 
             // Rerender when the model changes
             this.listenTo(this.model, 'change', this.render);
@@ -79,7 +82,7 @@ define(["js/views/baseview", "underscore"], function(BaseView, _) {
         },
 
         render: function() {
-            this.$el.html(this.template({
+            this.$el.html(_.template(licenseSelectorTemplate)({
                 model: this.model.attributes,
                 licenseString: this.model.toString() || "",
                 licenseInfo: this.licenseInfo,
diff --git a/cms/static/js_test.yml b/cms/static/js_test.yml
index 92a8dcf..22a4e56 100644
--- a/cms/static/js_test.yml
+++ b/cms/static/js_test.yml
@@ -48,10 +48,9 @@ lib_paths:
     - xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill.js
     - xmodule_js/common_static/js/vendor/sinon-1.17.0.js
     - xmodule_js/common_static/js/vendor/Squire.js
-    - xmodule_js/common_static/js/vendor/jasmine-jquery.js
-    - xmodule_js/common_static/js/vendor/jasmine-stealth.js
+    - xmodule_js/common_static/js/libs/jasmine-stealth.js
+    - xmodule_js/common_static/js/libs/jasmine-waituntil.js
     - xmodule_js/common_static/js/vendor/jasmine-imagediff.js
-    - xmodule_js/common_static/js/vendor/jasmine.async.js
     - xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js
     - xmodule_js/common_static/js/vendor/jQuery-File-Upload/js
     - xmodule_js/src/xmodule.js
diff --git a/cms/static/js_test_squire.yml b/cms/static/js_test_squire.yml
index 1122188..d65f44e 100644
--- a/cms/static/js_test_squire.yml
+++ b/cms/static/js_test_squire.yml
@@ -45,10 +45,7 @@ lib_paths:
     - xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill.js
     - xmodule_js/common_static/js/vendor/sinon-1.17.0.js
     - xmodule_js/common_static/js/vendor/Squire.js
-    - xmodule_js/common_static/js/vendor/jasmine-jquery.js
-    - xmodule_js/common_static/js/vendor/jasmine-stealth.js
     - xmodule_js/common_static/js/vendor/jasmine-imagediff.js
-    - xmodule_js/common_static/js/vendor/jasmine.async.js
     - xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js
     - xmodule_js/common_static/js/vendor/domReady.js
     - xmodule_js/common_static/js/vendor/URI.min.js
diff --git a/cms/static/karma_cms.conf.js b/cms/static/karma_cms.conf.js
index 871eed5..5c5d2fe 100644
--- a/cms/static/karma_cms.conf.js
+++ b/cms/static/karma_cms.conf.js
@@ -78,6 +78,7 @@ var files = [
     {pattern: 'xmodule_js/common_static/js/vendor/requirejs/text.js', included: false},
 
     // Paths to source JavaScript files
+    {pattern: 'xmodule_js/common_static/js/libs/jasmine-extensions.js', included: true, nocache: true},
     {pattern: 'coffee/src/**/*.js', included: false, nocache: true},
     {pattern: 'js/**/*.js', included: false, nocache: true},
     {pattern: 'js/certificates/**/*.js', included: false, nocache: true},
diff --git a/cms/static/karma_cms_squire.conf.js b/cms/static/karma_cms_squire.conf.js
index bd89960..501133a 100644
--- a/cms/static/karma_cms_squire.conf.js
+++ b/cms/static/karma_cms_squire.conf.js
@@ -70,6 +70,7 @@ var files = [
     {pattern: 'xmodule_js/common_static/js/vendor/requirejs/text.js', included: false},
 
     // Paths to source JavaScript files
+    {pattern: 'xmodule_js/common_static/js/libs/jasmine-extensions.js', included: true, nocache: true},
     {pattern: 'coffee/src/**/*.js', included: false, nocache: true},
     {pattern: 'js/collections/**/*.js', included: false, nocache: true},
     {pattern: 'js/models/**/*.js', included: false, nocache: true},
diff --git a/cms/templates/js/mock/mock-xmodule-editor-with-custom-tabs.underscore b/cms/templates/js/mock/mock-xmodule-editor-with-custom-tabs.underscore
index b37459e..c27d8f0 100644
--- a/cms/templates/js/mock/mock-xmodule-editor-with-custom-tabs.underscore
+++ b/cms/templates/js/mock/mock-xmodule-editor-with-custom-tabs.underscore
@@ -1,6 +1,6 @@
 <div class="xblock xblock-studio_view xmodule_edit xmodule_WrapperDescriptor" data-runtime-class="StudioRuntime"
      data-init="XBlockToXModuleShim" data-runtime-version="1" data-usage-id="i4x:;_;_edX;_mock"
-     data-type="VerticalDescriptor" tabindex="0">
+     data-type="MockDescriptor" tabindex="0">
     <div class="wrapper-comp-editor is-active" id="editor-tab" data-base-asset-url="/c4x/AndyA/ABT101/asset/">
 <section class="editor-with-tabs">
      <div class="edit-header">
diff --git a/cms/templates/js/mock/mock-xmodule-settings-only-editor.underscore b/cms/templates/js/mock/mock-xmodule-settings-only-editor.underscore
index 325cc06..5013204 100644
--- a/cms/templates/js/mock/mock-xmodule-settings-only-editor.underscore
+++ b/cms/templates/js/mock/mock-xmodule-settings-only-editor.underscore
@@ -1,4 +1,4 @@
-<div class="xblock xblock-studio_view xmodule_edit xmodule_WrapperDescriptor" data-runtime-class="StudioRuntime" data-init="XBlockToXModuleShim" data-runtime-version="1" data-usage-id="i4x:;_;_AndyA;_ABT101;_wrapper;_wrapper_l1_poll" data-type="VerticalDescriptor" tabindex="0">
+<div class="xblock xblock-studio_view xmodule_edit xmodule_WrapperDescriptor" data-runtime-class="StudioRuntime" data-init="XBlockToXModuleShim" data-runtime-version="1" data-usage-id="i4x:;_;_AndyA;_ABT101;_wrapper;_wrapper_l1_poll" data-type="MockDescriptor" tabindex="0">
     <section class="sequence-edit">
         <script id="metadata-editor-tpl" type="text/template">
             <ul class="list-input settings-list">
diff --git a/common/static/js/libs/jasmine-extensions.js b/common/static/js/libs/jasmine-extensions.js
index 47b9561..155504d 100644
--- a/common/static/js/libs/jasmine-extensions.js
+++ b/common/static/js/libs/jasmine-extensions.js
@@ -10,7 +10,13 @@
 
 (function(root, factory) {
     /* jshint strict: false */
-    factory(root, root.jQuery);
+    if (typeof define === 'function' && define.amd) {
+        require(['jquery'], function ($) {
+            factory(root, $);
+        });
+    } else {
+        factory(root, root.jQuery);
+    }
 }((function() {
     /* jshint strict: false */
     return this;
@@ -20,7 +26,9 @@
     // Add custom Jasmine matchers.
     beforeEach(function() {
 
-        jasmine.addMatchers(window.imagediff.jasmine);
+        if (window.imagediff) {
+            jasmine.addMatchers(window.imagediff.jasmine);
+        }
 
         jasmine.addMatchers({
             toHaveAttrs: function() {
@@ -60,6 +68,16 @@
                         };
                     }
                 };
+            },
+            toBeInstanceOf: function() {
+                // Assert the type of the value being tested matches the provided type
+                return {
+                    compare: function (actual, expected) {
+                        return {
+                            pass: actual instanceof expected
+                        };
+                    }
+                };
             }
         });
     });
diff --git a/pavelib/utils/envs.py b/pavelib/utils/envs.py
index b458499..a1d16e9 100644
--- a/pavelib/utils/envs.py
+++ b/pavelib/utils/envs.py
@@ -122,8 +122,8 @@ class Env(object):
     KARMA_CONFIG_FILES = [
         # REPO_ROOT / 'lms/static/karma_lms.conf.js',
         # REPO_ROOT / 'lms/static/karma_lms_coffee.conf.js',
-        # REPO_ROOT / 'cms/static/karma_cms.conf.js',
-        # REPO_ROOT / 'cms/static/karma_cms_squire.conf.js',
+        REPO_ROOT / 'cms/static/karma_cms.conf.js',
+        REPO_ROOT / 'cms/static/karma_cms_squire.conf.js',
         REPO_ROOT / 'common/lib/xmodule/xmodule/js/karma_xmodule.conf.js',
         REPO_ROOT / 'common/static/karma_common.conf.js',
         REPO_ROOT / 'common/static/karma_common_requirejs.conf.js',
@@ -132,8 +132,8 @@ class Env(object):
     JS_TEST_ID_KEYS = [
         # 'lms',
         # 'lms-coffee',
-        # 'cms',
-        # 'cms-squire',
+        'cms',
+        'cms-squire',
         'xmodule',
         'common',
         'common-requirejs'