Files
edx-platform/cms/static/js/spec/views/assets_squire_spec.js
Michael Terry a34c8c8233 Drop remaining coffee use
This basically commits the transpiled CoffeeScript JS (with minor
cleanup) and removes coffee build support.

A tiny amount of support for xblocks exists, because external users
may have xblocks with coffee. But no coffee in our tree anyway.
2018-04-13 14:10:40 -04:00

429 lines
20 KiB
JavaScript

define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "squire"],
function($, AjaxHelpers, Squire) {
const assetLibraryTpl = readFixtures('asset-library.underscore');
const assetTpl = readFixtures('asset.underscore');
describe("Asset view", function() {
beforeEach(function(done) {
setFixtures($("<script>", {id: "asset-tpl", type: "text/template"}).text(assetTpl));
appendSetFixtures(sandbox({id: "page-prompt"}));
this.promptSpies = jasmine.createSpyObj('Prompt.Warning', ["constructor", "show", "hide"]);
this.promptSpies.constructor.and.returnValue(this.promptSpies);
this.promptSpies.show.and.returnValue(this.promptSpies);
this.confirmationSpies = jasmine.createSpyObj('Notification.Confirmation', ["constructor", "show"]);
this.confirmationSpies.constructor.and.returnValue(this.confirmationSpies);
this.confirmationSpies.show.and.returnValue(this.confirmationSpies);
this.savingSpies = jasmine.createSpyObj('Notification.Mini', ["constructor", "show", "hide"]);
this.savingSpies.constructor.and.returnValue(this.savingSpies);
this.savingSpies.show.and.returnValue(this.savingSpies);
this.injector = new Squire();
this.injector.mock("common/js/components/views/feedback_prompt", {
"Warning": this.promptSpies.constructor
});
this.injector.mock("common/js/components/views/feedback_notification", {
"Confirmation": this.confirmationSpies.constructor,
"Mini": this.savingSpies.constructor
});
this.injector.require(["js/models/asset", "js/collections/asset", "js/views/asset"],
(AssetModel, AssetCollection, AssetView) => {
this.model = new AssetModel({
display_name: "test asset",
url: 'actual_asset_url',
portable_url: 'portable_url',
date_added: 'date',
thumbnail: null,
id: 'id'
});
spyOn(this.model, "destroy").and.callThrough();
spyOn(this.model, "save").and.callThrough();
this.collection = new AssetCollection([this.model]);
this.collection.url = "assets-url";
this.createAssetView = test => {
const view = new AssetView({model: this.model});
const requests = test ? AjaxHelpers["requests"](test) : null;
return {view, requests};
};
return done();
});
});
afterEach(function() {
this.injector.clean();
this.injector.remove();
});
describe("Basic", function() {
it("should render properly", function() {
let requests;
({view: this.view, requests} = this.createAssetView());
this.view.render();
expect(this.view.$el).toContainText("test asset");
});
it("should pop a delete confirmation when the delete button is clicked", function() {
let requests;
({view: this.view, requests} = this.createAssetView());
this.view.render().$(".remove-asset-button").click();
expect(this.promptSpies.constructor).toHaveBeenCalled();
const ctorOptions = this.promptSpies.constructor.calls.mostRecent().args[0];
expect(ctorOptions.title).toMatch('Delete File Confirmation');
// hasn't actually been removed
expect(this.model.destroy).not.toHaveBeenCalled();
expect(this.collection).toContain(this.model);
});
});
describe("AJAX", function() {
it("should destroy itself on confirmation", function() {
let requests;
({view: this.view, requests} = this.createAssetView(this));
this.view.render().$(".remove-asset-button").click();
const ctorOptions = this.promptSpies.constructor.calls.mostRecent().args[0];
// run the primary function to indicate confirmation
ctorOptions.actions.primary.click(this.promptSpies);
// AJAX request has been sent, but not yet returned
expect(this.model.destroy).toHaveBeenCalled();
expect(requests.length).toEqual(1);
expect(this.confirmationSpies.constructor).not.toHaveBeenCalled();
expect(this.collection.contains(this.model)).toBeTruthy();
// return a success response
requests[0].respond(204);
expect(this.confirmationSpies.constructor).toHaveBeenCalled();
expect(this.confirmationSpies.show).toHaveBeenCalled();
const savingOptions = this.confirmationSpies.constructor.calls.mostRecent().args[0];
expect(savingOptions.title).toMatch("Your file has been deleted.");
expect(this.collection.contains(this.model)).toBeFalsy();
});
it("should not destroy itself if server errors", function() {
let requests;
({view: this.view, requests} = this.createAssetView(this));
this.view.render().$(".remove-asset-button").click();
const ctorOptions = this.promptSpies.constructor.calls.mostRecent().args[0];
// run the primary function to indicate confirmation
ctorOptions.actions.primary.click(this.promptSpies);
// AJAX request has been sent, but not yet returned
expect(this.model.destroy).toHaveBeenCalled();
// return an error response
requests[0].respond(404);
expect(this.confirmationSpies.constructor).not.toHaveBeenCalled();
expect(this.collection.contains(this.model)).toBeTruthy();
});
it("should lock the asset on confirmation", function() {
let requests;
({view: this.view, requests} = this.createAssetView(this));
this.view.render().$(".lock-checkbox").click();
// AJAX request has been sent, but not yet returned
expect(this.model.save).toHaveBeenCalled();
expect(requests.length).toEqual(1);
expect(this.savingSpies.constructor).toHaveBeenCalled();
expect(this.savingSpies.show).toHaveBeenCalled();
const savingOptions = this.savingSpies.constructor.calls.mostRecent().args[0];
expect(savingOptions.title).toMatch("Saving");
expect(this.model.get("locked")).toBeFalsy();
// return a success response
requests[0].respond(204);
expect(this.savingSpies.hide).toHaveBeenCalled();
expect(this.model.get("locked")).toBeTruthy();
});
it("should not lock the asset if server errors", function() {
let requests;
({view: this.view, requests} = this.createAssetView(this));
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(this.savingSpies.hide).not.toHaveBeenCalled();
expect(this.model.get("locked")).toBeFalsy();
});
});
});
describe("Assets view", function() {
beforeEach(function(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']);
window.course_location_analytics = jasmine.createSpy();
appendSetFixtures(sandbox({id: "asset_table_body"}));
this.promptSpies = jasmine.createSpyObj('Prompt.Warning', ["constructor", "show", "hide"]);
this.promptSpies.constructor.and.returnValue(this.promptSpies);
this.promptSpies.show.and.returnValue(this.promptSpies);
this.injector = new Squire();
this.injector.mock("common/js/components/views/feedback_prompt", {
"Warning": this.promptSpies.constructor
});
this.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'
};
this.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'
};
this.mockAssetsResponse = {
assets: [ this.mockAsset1, this.mockAsset2 ],
start: 0,
end: 1,
page: 0,
pageSize: 5,
totalCount: 2
};
this.injector.require(["js/models/asset", "js/collections/asset", "js/views/assets"],
(AssetModel, AssetCollection, AssetsView) => {
this.AssetModel = AssetModel;
this.collection = new AssetCollection();
this.collection.url = "assets-url";
this.createAssetsView = test => {
const requests = AjaxHelpers.requests(test);
const view = new AssetsView({
collection: this.collection,
el: $('#asset_table_body')
});
view.render();
return {view, requests};
};
return done();
});
return $.ajax();
});
afterEach(function() {
delete window.analytics;
delete window.course_location_analytics;
this.injector.clean();
this.injector.remove();
});
const addMockAsset = function(requests) {
const model = new this.AssetModel({
display_name: "new asset",
url: 'new_actual_asset_url',
portable_url: 'portable_url',
date_added: 'date',
thumbnail: null,
id: 'idx'
});
this.view.addAsset(model);
return AjaxHelpers.respondWithJson(requests,
{
assets: [
this.mockAsset1, this.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", function() {
// Separate setup method to work-around mis-parenting of beforeEach methods
const setup = function(requests) {
this.view.pagingView.setPage(1);
return AjaxHelpers.respondWithJson(requests, this.mockAssetsResponse);
};
$.fn.fileupload = () => '';
const clickEvent = html_selector => $(html_selector).click();
it("should show upload modal on clicking upload asset button", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
spyOn(this.view, "showUploadModal");
setup.call(this, requests);
expect(this.view.showUploadModal).not.toHaveBeenCalled();
this.view.showUploadModal(clickEvent(".upload-button"));
expect(this.view.showUploadModal).toHaveBeenCalled();
});
it("should show file selection menu on choose file button", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
spyOn(this.view, "showFileSelectionMenu");
setup.call(this, requests);
expect(this.view.showFileSelectionMenu).not.toHaveBeenCalled();
this.view.showFileSelectionMenu(clickEvent(".choose-file-button"));
expect(this.view.showFileSelectionMenu).toHaveBeenCalled();
});
it("should hide upload modal on clicking close button", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
spyOn(this.view, "hideModal");
setup.call(this, requests);
expect(this.view.hideModal).not.toHaveBeenCalled();
this.view.hideModal(clickEvent(".close-button"));
expect(this.view.hideModal).toHaveBeenCalled();
});
it("should show a status indicator while loading", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
appendSetFixtures('<div class="ui-loading"/>');
expect($('.ui-loading').is(':visible')).toBe(true);
setup.call(this, requests);
expect($('.ui-loading').is(':visible')).toBe(false);
});
it("should hide the status indicator if an error occurs while loading", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
appendSetFixtures('<div class="ui-loading"/>');
expect($('.ui-loading').is(':visible')).toBe(true);
this.view.pagingView.setPage(1);
AjaxHelpers.respondWithError(requests);
expect($('.ui-loading').is(':visible')).toBe(false);
});
it("should render both assets", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
setup.call(this, requests);
expect(this.view.$el).toContainText("test asset 1");
expect(this.view.$el).toContainText("test asset 2");
});
it("should remove the deleted asset from the view", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
AjaxHelpers.respondWithJson(requests, this.mockAssetsResponse);
setup.call(this, requests);
// Delete the 2nd asset with success from server.
this.view.$(".remove-asset-button")[1].click();
this.promptSpies.constructor.calls.mostRecent().args[0].actions.primary.click(this.promptSpies);
AjaxHelpers.respondWithNoContent(requests);
expect(this.view.$el).toContainText("test asset 1");
expect(this.view.$el).not.toContainText("test asset 2");
});
it("does not remove asset if deletion failed", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
setup.call(this, requests);
// Delete the 2nd asset, but mimic a failure from the server.
this.view.$(".remove-asset-button")[1].click();
this.promptSpies.constructor.calls.mostRecent().args[0].actions.primary.click(this.promptSpies);
AjaxHelpers.respondWithError(requests);
expect(this.view.$el).toContainText("test asset 1");
expect(this.view.$el).toContainText("test asset 2");
});
it("adds an asset if asset does not already exist", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
setup.call(this, requests);
addMockAsset.call(this, requests);
expect(this.view.$el).toContainText("new asset");
expect(this.collection.models.length).toBe(3);
});
it("does not add an asset if asset already exists", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
setup.call(this, requests);
spyOn(this.collection, "add").and.callThrough();
const model = this.collection.models[1];
this.view.addAsset(model);
expect(this.collection.add).not.toHaveBeenCalled();
});
});
describe("Sorting", function() {
// Separate setup method to work-around mis-parenting of beforeEach methods
const setup = function(requests) {
this.view.pagingView.setPage(1);
return AjaxHelpers.respondWithJson(requests, this.mockAssetsResponse);
};
it("should have the correct default sort order", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
setup.call(this, requests);
expect(this.view.pagingView.sortDisplayName()).toBe("Date Added");
expect(this.view.collection.sortDirection).toBe("desc");
});
it("should toggle the sort order when clicking on the currently sorted column", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
setup.call(this, requests);
expect(this.view.pagingView.sortDisplayName()).toBe("Date Added");
expect(this.view.collection.sortDirection).toBe("desc");
this.view.$("#js-asset-date-col").click();
AjaxHelpers.respondWithJson(requests, this.mockAssetsResponse);
expect(this.view.pagingView.sortDisplayName()).toBe("Date Added");
expect(this.view.collection.sortDirection).toBe("asc");
this.view.$("#js-asset-date-col").click();
AjaxHelpers.respondWithJson(requests, this.mockAssetsResponse);
expect(this.view.pagingView.sortDisplayName()).toBe("Date Added");
expect(this.view.collection.sortDirection).toBe("desc");
});
it("should switch the sort order when clicking on a different column", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
setup.call(this, requests);
this.view.$("#js-asset-name-col").click();
AjaxHelpers.respondWithJson(requests, this.mockAssetsResponse);
expect(this.view.pagingView.sortDisplayName()).toBe("Name");
expect(this.view.collection.sortDirection).toBe("asc");
this.view.$("#js-asset-name-col").click();
AjaxHelpers.respondWithJson(requests, this.mockAssetsResponse);
expect(this.view.pagingView.sortDisplayName()).toBe("Name");
expect(this.view.collection.sortDirection).toBe("desc");
});
it("should switch sort to most recent date added when a new asset is added", function() {
let requests;
({view: this.view, requests} = this.createAssetsView(this));
setup.call(this, requests);
this.view.$("#js-asset-name-col").click();
AjaxHelpers.respondWithJson(requests, this.mockAssetsResponse);
addMockAsset.call(this, requests);
AjaxHelpers.respondWithJson(requests, this.mockAssetsResponse);
expect(this.view.pagingView.sortDisplayName()).toBe("Date Added");
expect(this.view.collection.sortDirection).toBe("desc");
});
});
});
});