334 lines
16 KiB
CoffeeScript
334 lines
16 KiB
CoffeeScript
define ["jasmine", "js/spec_helpers/create_sinon", "squire"],
|
|
(jasmine, create_sinon, Squire) ->
|
|
|
|
feedbackTpl = readFixtures('system-feedback.underscore')
|
|
assetLibraryTpl = readFixtures('asset-library.underscore')
|
|
assetTpl = readFixtures('asset.underscore')
|
|
pagingHeaderTpl = readFixtures('paging-header.underscore')
|
|
pagingFooterTpl = readFixtures('paging-footer.underscore')
|
|
|
|
describe "Asset view", ->
|
|
beforeEach ->
|
|
setFixtures($("<script>", {id: "asset-tpl", type: "text/template"}).text(assetTpl))
|
|
appendSetFixtures($("<script>", {id: "system-feedback-tpl", type: "text/template"}).text(feedbackTpl))
|
|
appendSetFixtures(sandbox({id: "page-prompt"}))
|
|
|
|
@promptSpies = jasmine.createSpyObj('Prompt.Warning', ["constructor", "show", "hide"])
|
|
@promptSpies.constructor.andReturn(@promptSpies)
|
|
@promptSpies.show.andReturn(@promptSpies)
|
|
|
|
@confirmationSpies = jasmine.createSpyObj('Notification.Confirmation', ["constructor", "show"])
|
|
@confirmationSpies.constructor.andReturn(@confirmationSpies)
|
|
@confirmationSpies.show.andReturn(@confirmationSpies)
|
|
|
|
@savingSpies = jasmine.createSpyObj('Notification.Mini', ["constructor", "show", "hide"])
|
|
@savingSpies.constructor.andReturn(@savingSpies)
|
|
@savingSpies.show.andReturn(@savingSpies)
|
|
|
|
@injector = new Squire()
|
|
@injector.mock("js/views/feedback_prompt", {
|
|
"Warning": @promptSpies.constructor
|
|
})
|
|
@injector.mock("js/views/feedback_notification", {
|
|
"Confirmation": @confirmationSpies.constructor,
|
|
"Mini": @savingSpies.constructor
|
|
})
|
|
runs =>
|
|
@injector.require ["js/models/asset", "js/collections/asset", "js/views/asset"],
|
|
(AssetModel, AssetCollection, AssetView) =>
|
|
@model = new AssetModel
|
|
display_name: "test asset"
|
|
url: 'actual_asset_url'
|
|
portable_url: 'portable_url'
|
|
date_added: 'date'
|
|
thumbnail: null
|
|
id: 'id'
|
|
spyOn(@model, "destroy").andCallThrough()
|
|
spyOn(@model, "save").andCallThrough()
|
|
|
|
@collection = new AssetCollection([@model])
|
|
@collection.url = "assets-url"
|
|
@view = new AssetView({model: @model})
|
|
|
|
waitsFor (=> @view), "AssetView was not created", 1000
|
|
|
|
afterEach ->
|
|
@injector.clean()
|
|
@injector.remove()
|
|
|
|
describe "Basic", ->
|
|
it "should render properly", ->
|
|
@view.render()
|
|
expect(@view.$el).toContainText("test asset")
|
|
|
|
it "should pop a delete confirmation when the delete button is clicked", ->
|
|
@view.render().$(".remove-asset-button").click()
|
|
expect(@promptSpies.constructor).toHaveBeenCalled()
|
|
ctorOptions = @promptSpies.constructor.mostRecentCall.args[0]
|
|
expect(ctorOptions.title).toMatch('Delete File Confirmation')
|
|
# hasn't actually been removed
|
|
expect(@model.destroy).not.toHaveBeenCalled()
|
|
expect(@collection).toContain(@model)
|
|
|
|
describe "AJAX", ->
|
|
it "should destroy itself on confirmation", ->
|
|
requests = create_sinon["requests"](this)
|
|
|
|
@view.render().$(".remove-asset-button").click()
|
|
ctorOptions = @promptSpies.constructor.mostRecentCall.args[0]
|
|
# run the primary function to indicate confirmation
|
|
ctorOptions.actions.primary.click(@promptSpies)
|
|
# AJAX request has been sent, but not yet returned
|
|
expect(@model.destroy).toHaveBeenCalled()
|
|
expect(requests.length).toEqual(1)
|
|
expect(@confirmationSpies.constructor).not.toHaveBeenCalled()
|
|
expect(@collection.contains(@model)).toBeTruthy()
|
|
# return a success response
|
|
requests[0].respond(200)
|
|
expect(@confirmationSpies.constructor).toHaveBeenCalled()
|
|
expect(@confirmationSpies.show).toHaveBeenCalled()
|
|
savingOptions = @confirmationSpies.constructor.mostRecentCall.args[0]
|
|
expect(savingOptions.title).toMatch("Your file has been deleted.")
|
|
expect(@collection.contains(@model)).toBeFalsy()
|
|
|
|
it "should not destroy itself if server errors", ->
|
|
requests = create_sinon["requests"](this)
|
|
|
|
@view.render().$(".remove-asset-button").click()
|
|
ctorOptions = @promptSpies.constructor.mostRecentCall.args[0]
|
|
# run the primary function to indicate confirmation
|
|
ctorOptions.actions.primary.click(@promptSpies)
|
|
# AJAX request has been sent, but not yet returned
|
|
expect(@model.destroy).toHaveBeenCalled()
|
|
# return an error response
|
|
requests[0].respond(404)
|
|
expect(@confirmationSpies.constructor).not.toHaveBeenCalled()
|
|
expect(@collection.contains(@model)).toBeTruthy()
|
|
|
|
it "should lock the asset on confirmation", ->
|
|
requests = create_sinon["requests"](this)
|
|
|
|
@view.render().$(".lock-checkbox").click()
|
|
# AJAX request has been sent, but not yet returned
|
|
expect(@model.save).toHaveBeenCalled()
|
|
expect(requests.length).toEqual(1)
|
|
expect(@savingSpies.constructor).toHaveBeenCalled()
|
|
expect(@savingSpies.show).toHaveBeenCalled()
|
|
savingOptions = @savingSpies.constructor.mostRecentCall.args[0]
|
|
expect(savingOptions.title).toMatch("Saving...")
|
|
expect(@model.get("locked")).toBeFalsy()
|
|
# return a success response
|
|
requests[0].respond(200)
|
|
expect(@savingSpies.hide).toHaveBeenCalled()
|
|
expect(@model.get("locked")).toBeTruthy()
|
|
|
|
it "should not lock the asset if server errors", ->
|
|
requests = create_sinon["requests"](this)
|
|
|
|
@view.render().$(".lock-checkbox").click()
|
|
# return an error response
|
|
requests[0].respond(404)
|
|
# Don't call hide because that closes the notification showing the server error.
|
|
expect(@savingSpies.hide).not.toHaveBeenCalled()
|
|
expect(@model.get("locked")).toBeFalsy()
|
|
|
|
describe "Assets view", ->
|
|
beforeEach ->
|
|
setFixtures($("<script>", {id: "asset-library-tpl", type: "text/template"}).text(assetLibraryTpl))
|
|
appendSetFixtures($("<script>", {id: "asset-tpl", type: "text/template"}).text(assetTpl))
|
|
appendSetFixtures($("<script>", {id: "paging-header-tpl", type: "text/template"}).text(pagingHeaderTpl))
|
|
appendSetFixtures($("<script>", {id: "paging-footer-tpl", type: "text/template"}).text(pagingFooterTpl))
|
|
window.analytics = jasmine.createSpyObj('analytics', ['track'])
|
|
window.course_location_analytics = jasmine.createSpy()
|
|
appendSetFixtures(sandbox({id: "asset_table_body"}))
|
|
|
|
@promptSpies = jasmine.createSpyObj('Prompt.Warning', ["constructor", "show", "hide"])
|
|
@promptSpies.constructor.andReturn(@promptSpies)
|
|
@promptSpies.show.andReturn(@promptSpies)
|
|
|
|
@injector = new Squire()
|
|
@injector.mock("js/views/feedback_prompt", {
|
|
"Warning": @promptSpies.constructor
|
|
})
|
|
|
|
@mockAsset1 = {
|
|
display_name: "test asset 1"
|
|
url: 'actual_asset_url_1'
|
|
portable_url: 'portable_url_1'
|
|
date_added: 'date_1'
|
|
thumbnail: null
|
|
id: 'id_1'
|
|
}
|
|
@mockAsset2 = {
|
|
display_name: "test asset 2"
|
|
url: 'actual_asset_url_2'
|
|
portable_url: 'portable_url_2'
|
|
date_added: 'date_2'
|
|
thumbnail: null
|
|
id: 'id_2'
|
|
}
|
|
@mockAssetsResponse = {
|
|
assets: [ @mockAsset1, @mockAsset2 ],
|
|
start: 0,
|
|
end: 1,
|
|
page: 0,
|
|
pageSize: 5,
|
|
totalCount: 2
|
|
}
|
|
|
|
runs =>
|
|
@injector.require ["js/models/asset", "js/collections/asset", "js/views/assets"],
|
|
(AssetModel, AssetCollection, AssetsView) =>
|
|
@AssetModel = AssetModel
|
|
@collection = new AssetCollection();
|
|
@collection.url = "assets-url"
|
|
@view = new AssetsView
|
|
collection: @collection
|
|
el: $('#asset_table_body')
|
|
@view.render()
|
|
|
|
waitsFor (=> @view), "AssetsView was not created", 1000
|
|
|
|
$.ajax()
|
|
|
|
afterEach ->
|
|
delete window.analytics
|
|
delete window.course_location_analytics
|
|
|
|
@injector.clean()
|
|
@injector.remove()
|
|
|
|
addMockAsset = (requests) ->
|
|
model = new @AssetModel
|
|
display_name: "new asset"
|
|
url: 'new_actual_asset_url'
|
|
portable_url: 'portable_url'
|
|
date_added: 'date'
|
|
thumbnail: null
|
|
id: 'idx'
|
|
@view.addAsset(model)
|
|
create_sinon.respondWithJson(requests,
|
|
{
|
|
assets: [
|
|
@mockAsset1, @mockAsset2,
|
|
{
|
|
display_name: "new asset"
|
|
url: 'new_actual_asset_url'
|
|
portable_url: 'portable_url'
|
|
date_added: 'date'
|
|
thumbnail: null
|
|
id: 'idx'
|
|
}
|
|
],
|
|
start: 0,
|
|
end: 2,
|
|
page: 0,
|
|
pageSize: 5,
|
|
totalCount: 3
|
|
})
|
|
|
|
|
|
describe "Basic", ->
|
|
# Separate setup method to work-around mis-parenting of beforeEach methods
|
|
setup = ->
|
|
requests = create_sinon.requests(this)
|
|
@view.setPage(0)
|
|
create_sinon.respondWithJson(requests, @mockAssetsResponse)
|
|
return requests
|
|
|
|
it "should show a status indicator while loading", ->
|
|
appendSetFixtures('<div class="ui-loading"/>')
|
|
expect($('.ui-loading').is(':visible')).toBe(true)
|
|
setup.call(this)
|
|
expect($('.ui-loading').is(':visible')).toBe(false)
|
|
|
|
it "should hide the status indicator if an error occurs while loading", ->
|
|
requests = create_sinon.requests(this)
|
|
appendSetFixtures('<div class="ui-loading"/>')
|
|
expect($('.ui-loading').is(':visible')).toBe(true)
|
|
@view.setPage(0)
|
|
create_sinon.respondWithError(requests)
|
|
expect($('.ui-loading').is(':visible')).toBe(false)
|
|
|
|
it "should render both assets", ->
|
|
requests = setup.call(this)
|
|
expect(@view.$el).toContainText("test asset 1")
|
|
expect(@view.$el).toContainText("test asset 2")
|
|
|
|
it "should remove the deleted asset from the view", ->
|
|
requests = setup.call(this)
|
|
# Delete the 2nd asset with success from server.
|
|
@view.$(".remove-asset-button")[1].click()
|
|
@promptSpies.constructor.mostRecentCall.args[0].actions.primary.click(@promptSpies)
|
|
req.respond(200) for req in requests
|
|
expect(@view.$el).toContainText("test asset 1")
|
|
expect(@view.$el).not.toContainText("test asset 2")
|
|
|
|
it "does not remove asset if deletion failed", ->
|
|
requests = setup.call(this)
|
|
# 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)
|
|
req.respond(404) for req in requests
|
|
expect(@view.$el).toContainText("test asset 1")
|
|
expect(@view.$el).toContainText("test asset 2")
|
|
|
|
it "adds an asset if asset does not already exist", ->
|
|
requests = setup.call(this)
|
|
addMockAsset.call(this, requests)
|
|
expect(@view.$el).toContainText("new asset")
|
|
expect(@collection.models.length).toBe(3)
|
|
|
|
it "does not add an asset if asset already exists", ->
|
|
setup.call(this)
|
|
spyOn(@collection, "add").andCallThrough()
|
|
model = @collection.models[1]
|
|
@view.addAsset(model)
|
|
expect(@collection.add).not.toHaveBeenCalled()
|
|
|
|
describe "Sorting", ->
|
|
# Separate setup method to work-around mis-parenting of beforeEach methods
|
|
setup = ->
|
|
requests = create_sinon.requests(this)
|
|
@view.setPage(0)
|
|
create_sinon.respondWithJson(requests, @mockAssetsResponse)
|
|
return requests
|
|
|
|
it "should have the correct default sort order", ->
|
|
requests = setup.call(this)
|
|
expect(@view.sortDisplayName()).toBe("Date Added")
|
|
expect(@view.collection.sortDirection).toBe("desc")
|
|
|
|
it "should toggle the sort order when clicking on the currently sorted column", ->
|
|
requests = setup.call(this)
|
|
expect(@view.sortDisplayName()).toBe("Date Added")
|
|
expect(@view.collection.sortDirection).toBe("desc")
|
|
@view.$("#js-asset-date-col").click()
|
|
create_sinon.respondWithJson(requests, @mockAssetsResponse)
|
|
expect(@view.sortDisplayName()).toBe("Date Added")
|
|
expect(@view.collection.sortDirection).toBe("asc")
|
|
@view.$("#js-asset-date-col").click()
|
|
create_sinon.respondWithJson(requests, @mockAssetsResponse)
|
|
expect(@view.sortDisplayName()).toBe("Date Added")
|
|
expect(@view.collection.sortDirection).toBe("desc")
|
|
|
|
it "should switch the sort order when clicking on a different column", ->
|
|
requests = setup.call(this)
|
|
@view.$("#js-asset-name-col").click()
|
|
create_sinon.respondWithJson(requests, @mockAssetsResponse)
|
|
expect(@view.sortDisplayName()).toBe("Name")
|
|
expect(@view.collection.sortDirection).toBe("asc")
|
|
@view.$("#js-asset-name-col").click()
|
|
create_sinon.respondWithJson(requests, @mockAssetsResponse)
|
|
expect(@view.sortDisplayName()).toBe("Name")
|
|
expect(@view.collection.sortDirection).toBe("desc")
|
|
|
|
it "should switch sort to most recent date added when a new asset is added", ->
|
|
requests = setup.call(this)
|
|
@view.$("#js-asset-name-col").click()
|
|
create_sinon.respondWithJson(requests, @mockAssetsResponse)
|
|
addMockAsset.call(this, requests)
|
|
create_sinon.respondWithJson(requests, @mockAssetsResponse)
|
|
expect(@view.sortDisplayName()).toBe("Date Added")
|
|
expect(@view.collection.sortDirection).toBe("desc")
|