CoffeeScript tests migration: Decaffeinate files

This is running decaffeinate, with no additional cleanup.
This commit is contained in:
David Ormsbee
2017-08-17 20:09:37 -04:00
committed by Calen Pennington
parent 5c64da2f63
commit 0880502f26
30 changed files with 5897 additions and 4850 deletions

View File

@@ -1,10 +1,20 @@
define ["js/models/course"], (Course) ->
describe "Course", ->
describe "basic", ->
beforeEach ->
@model = new Course({
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(["js/models/course"], Course =>
describe("Course", () =>
describe("basic", function() {
beforeEach(function() {
return this.model = new Course({
name: "Greek Hero"
})
});
});
it "should take a name argument", ->
expect(@model.get("name")).toEqual("Greek Hero")
return it("should take a name argument", function() {
return expect(this.model.get("name")).toEqual("Greek Hero");
});
})
)
);

View File

@@ -1,59 +1,74 @@
define ["js/models/metadata"], (Metadata) ->
describe "Metadata", ->
it "knows when the value has not been modified", ->
model = new Metadata(
{'value': 'original', 'explicitly_set': false})
expect(model.isModified()).toBeFalsy()
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(["js/models/metadata"], Metadata =>
describe("Metadata", function() {
it("knows when the value has not been modified", function() {
let model = new Metadata(
{'value': 'original', 'explicitly_set': false});
expect(model.isModified()).toBeFalsy();
model = new Metadata(
{'value': 'original', 'explicitly_set': true})
model.setValue('original')
expect(model.isModified()).toBeFalsy()
{'value': 'original', 'explicitly_set': true});
model.setValue('original');
return expect(model.isModified()).toBeFalsy();
});
it "knows when the value has been modified", ->
model = new Metadata(
{'value': 'original', 'explicitly_set': false})
model.setValue('original')
expect(model.isModified()).toBeTruthy()
it("knows when the value has been modified", function() {
let model = new Metadata(
{'value': 'original', 'explicitly_set': false});
model.setValue('original');
expect(model.isModified()).toBeTruthy();
model = new Metadata(
{'value': 'original', 'explicitly_set': true})
model.setValue('modified')
expect(model.isModified()).toBeTruthy()
{'value': 'original', 'explicitly_set': true});
model.setValue('modified');
return expect(model.isModified()).toBeTruthy();
});
it "tracks when values have been explicitly set", ->
model = new Metadata(
{'value': 'original', 'explicitly_set': false})
expect(model.isExplicitlySet()).toBeFalsy()
model.setValue('original')
expect(model.isExplicitlySet()).toBeTruthy()
it("tracks when values have been explicitly set", function() {
const model = new Metadata(
{'value': 'original', 'explicitly_set': false});
expect(model.isExplicitlySet()).toBeFalsy();
model.setValue('original');
return expect(model.isExplicitlySet()).toBeTruthy();
});
it "has both 'display value' and a 'value' methods", ->
model = new Metadata(
{'value': 'default', 'explicitly_set': false})
expect(model.getValue()).toBeNull
expect(model.getDisplayValue()).toBe('default')
model.setValue('modified')
expect(model.getValue()).toBe('modified')
expect(model.getDisplayValue()).toBe('modified')
it("has both 'display value' and a 'value' methods", function() {
const model = new Metadata(
{'value': 'default', 'explicitly_set': false});
expect(model.getValue()).toBeNull;
expect(model.getDisplayValue()).toBe('default');
model.setValue('modified');
expect(model.getValue()).toBe('modified');
return expect(model.getDisplayValue()).toBe('modified');
});
it "has a clear method for reverting to the default", ->
model = new Metadata(
{'value': 'original', 'default_value' : 'default', 'explicitly_set': true})
model.clear()
expect(model.getValue()).toBeNull
expect(model.getDisplayValue()).toBe('default')
expect(model.isExplicitlySet()).toBeFalsy()
it("has a clear method for reverting to the default", function() {
const model = new Metadata(
{'value': 'original', 'default_value' : 'default', 'explicitly_set': true});
model.clear();
expect(model.getValue()).toBeNull;
expect(model.getDisplayValue()).toBe('default');
return expect(model.isExplicitlySet()).toBeFalsy();
});
it "has a getter for field name", ->
model = new Metadata({'field_name': 'foo'})
expect(model.getFieldName()).toBe('foo')
it("has a getter for field name", function() {
const model = new Metadata({'field_name': 'foo'});
return expect(model.getFieldName()).toBe('foo');
});
it "has a getter for options", ->
model = new Metadata({'options': ['foo', 'bar']})
expect(model.getOptions()).toEqual(['foo', 'bar'])
it("has a getter for options", function() {
const model = new Metadata({'options': ['foo', 'bar']});
return expect(model.getOptions()).toEqual(['foo', 'bar']);
});
it "has a getter for type", ->
model = new Metadata({'type': 'Integer'})
expect(model.getType()).toBe(Metadata.INTEGER_TYPE)
return it("has a getter for type", function() {
const model = new Metadata({'type': 'Integer'});
return expect(model.getType()).toBe(Metadata.INTEGER_TYPE);
});
})
);

View File

@@ -1,50 +1,67 @@
define ["js/models/section", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "js/utils/module"], (Section, AjaxHelpers, ModuleUtils) ->
describe "Section", ->
describe "basic", ->
beforeEach ->
@model = new Section({
id: 42
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(["js/models/section", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "js/utils/module"], (Section, AjaxHelpers, ModuleUtils) =>
describe("Section", function() {
describe("basic", function() {
beforeEach(function() {
return this.model = new Section({
id: 42,
name: "Life, the Universe, and Everything"
})
});
});
it "should take an id argument", ->
expect(@model.get("id")).toEqual(42)
it("should take an id argument", function() {
return expect(this.model.get("id")).toEqual(42);
});
it "should take a name argument", ->
expect(@model.get("name")).toEqual("Life, the Universe, and Everything")
it("should take a name argument", function() {
return expect(this.model.get("name")).toEqual("Life, the Universe, and Everything");
});
it "should have a URL set", ->
expect(@model.url()).toEqual(ModuleUtils.getUpdateUrl(42))
it("should have a URL set", function() {
return expect(this.model.url()).toEqual(ModuleUtils.getUpdateUrl(42));
});
it "should serialize to JSON correctly", ->
expect(@model.toJSON()).toEqual({
return it("should serialize to JSON correctly", function() {
return expect(this.model.toJSON()).toEqual({
metadata:
{
display_name: "Life, the Universe, and Everything"
}
})
});
});
});
describe "XHR", ->
beforeEach ->
spyOn(Section.prototype, 'showNotification')
spyOn(Section.prototype, 'hideNotification')
@model = new Section({
id: 42
return describe("XHR", function() {
beforeEach(function() {
spyOn(Section.prototype, 'showNotification');
spyOn(Section.prototype, 'hideNotification');
return this.model = new Section({
id: 42,
name: "Life, the Universe, and Everything"
})
});
});
it "show/hide a notification when it saves to the server", ->
server = AjaxHelpers.server([200, {"Content-Type": "application/json"}, "{}"])
it("show/hide a notification when it saves to the server", function() {
const server = AjaxHelpers.server([200, {"Content-Type": "application/json"}, "{}"]);
@model.save()
expect(Section.prototype.showNotification).toHaveBeenCalled()
server.respond()
expect(Section.prototype.hideNotification).toHaveBeenCalled()
this.model.save();
expect(Section.prototype.showNotification).toHaveBeenCalled();
server.respond();
return expect(Section.prototype.hideNotification).toHaveBeenCalled();
});
it "don't hide notification when saving fails", ->
# this is handled by the global AJAX error handler
server = AjaxHelpers.server([500, {"Content-Type": "application/json"}, "{}"])
return it("don't hide notification when saving fails", function() {
// this is handled by the global AJAX error handler
const server = AjaxHelpers.server([500, {"Content-Type": "application/json"}, "{}"]);
@model.save()
server.respond()
expect(Section.prototype.hideNotification).not.toHaveBeenCalled()
this.model.save();
server.respond();
return expect(Section.prototype.hideNotification).not.toHaveBeenCalled();
});
});
})
);

View File

@@ -1,39 +1,52 @@
define ["js/models/settings/course_grader"], (CourseGrader) ->
describe "CourseGraderModel", ->
describe "parseWeight", ->
it "converts a float to an integer", ->
model = new CourseGrader({weight: 7.0001, min_count: 3.67, drop_count: 1.88}, {parse:true})
expect(model.get('weight')).toBe(7)
expect(model.get('min_count')).toBe(4)
expect(model.get('drop_count')).toBe(2)
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(["js/models/settings/course_grader"], CourseGrader =>
describe("CourseGraderModel", () =>
describe("parseWeight", function() {
it("converts a float to an integer", function() {
const model = new CourseGrader({weight: 7.0001, min_count: 3.67, drop_count: 1.88}, {parse:true});
expect(model.get('weight')).toBe(7);
expect(model.get('min_count')).toBe(4);
return expect(model.get('drop_count')).toBe(2);
});
it "converts float value of weight to an integer with rounding", ->
model = new CourseGrader({weight: 28.999999999999996}, {parse:true})
expect(model.get('weight')).toBe(29)
it("converts float value of weight to an integer with rounding", function() {
const model = new CourseGrader({weight: 28.999999999999996}, {parse:true});
return expect(model.get('weight')).toBe(29);
});
it "converts a string to an integer", ->
model = new CourseGrader({weight: '7.0001', min_count: '3.67', drop_count: '1.88'}, {parse:true})
expect(model.get('weight')).toBe(7)
expect(model.get('min_count')).toBe(4)
expect(model.get('drop_count')).toBe(2)
it("converts a string to an integer", function() {
const model = new CourseGrader({weight: '7.0001', min_count: '3.67', drop_count: '1.88'}, {parse:true});
expect(model.get('weight')).toBe(7);
expect(model.get('min_count')).toBe(4);
return expect(model.get('drop_count')).toBe(2);
});
it "does a no-op for integers", ->
model = new CourseGrader({weight: 7, min_count: 3, drop_count: 1}, {parse:true})
expect(model.get('weight')).toBe(7)
expect(model.get('min_count')).toBe(3)
expect(model.get('drop_count')).toBe(1)
it("does a no-op for integers", function() {
const model = new CourseGrader({weight: 7, min_count: 3, drop_count: 1}, {parse:true});
expect(model.get('weight')).toBe(7);
expect(model.get('min_count')).toBe(3);
return expect(model.get('drop_count')).toBe(1);
});
it "gives validation error if min_count is less than 1 or drop_count is NaN", ->
model = new CourseGrader()
errors = model.validate({min_count: 0, drop_count: ''}, {validate:true})
expect(errors.min_count).toBe('Please enter an integer greater than 0.')
expect(errors.drop_count).toBe('Please enter non-negative integer.')
# don't allow negative integers
errors = model.validate({min_count: -12, drop_count: -1}, {validate:true})
expect(errors.min_count).toBe('Please enter an integer greater than 0.')
expect(errors.drop_count).toBe('Please enter non-negative integer.')
# don't allow floats
errors = model.validate({min_count: 12.2, drop_count: 1.5}, {validate:true})
expect(errors.min_count).toBe('Please enter an integer greater than 0.')
expect(errors.drop_count).toBe('Please enter non-negative integer.')
return it("gives validation error if min_count is less than 1 or drop_count is NaN", function() {
const model = new CourseGrader();
let errors = model.validate({min_count: 0, drop_count: ''}, {validate:true});
expect(errors.min_count).toBe('Please enter an integer greater than 0.');
expect(errors.drop_count).toBe('Please enter non-negative integer.');
// don't allow negative integers
errors = model.validate({min_count: -12, drop_count: -1}, {validate:true});
expect(errors.min_count).toBe('Please enter an integer greater than 0.');
expect(errors.drop_count).toBe('Please enter non-negative integer.');
// don't allow floats
errors = model.validate({min_count: 12.2, drop_count: 1.5}, {validate:true});
expect(errors.min_count).toBe('Please enter an integer greater than 0.');
return expect(errors.drop_count).toBe('Please enter non-negative integer.');
});
})
)
);

View File

@@ -1,36 +1,52 @@
define ["underscore", "js/models/settings/course_grading_policy"], (_, CourseGradingPolicy) ->
describe "CourseGradingPolicy", ->
beforeEach ->
@model = new CourseGradingPolicy()
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(["underscore", "js/models/settings/course_grading_policy"], (_, CourseGradingPolicy) =>
describe("CourseGradingPolicy", function() {
beforeEach(function() {
return this.model = new CourseGradingPolicy();
});
describe "parse", ->
it "sets a null grace period to 00:00", ->
attrs = @model.parse(grace_period: null)
expect(attrs.grace_period).toEqual(
describe("parse", () =>
it("sets a null grace period to 00:00", function() {
const attrs = this.model.parse({grace_period: null});
return expect(attrs.grace_period).toEqual({
hours: 0,
minutes: 0
)
});
})
);
describe "parseGracePeriod", ->
it "parses a time in HH:MM format", ->
time = @model.parseGracePeriod("07:19")
expect(time).toEqual(
describe("parseGracePeriod", function() {
it("parses a time in HH:MM format", function() {
const time = this.model.parseGracePeriod("07:19");
return expect(time).toEqual({
hours: 7,
minutes: 19
)
});
});
it "returns null on an incorrectly formatted string", ->
expect(@model.parseGracePeriod("asdf")).toBe(null)
expect(@model.parseGracePeriod("7:19")).toBe(null)
expect(@model.parseGracePeriod("1000:00")).toBe(null)
return it("returns null on an incorrectly formatted string", function() {
expect(this.model.parseGracePeriod("asdf")).toBe(null);
expect(this.model.parseGracePeriod("7:19")).toBe(null);
return expect(this.model.parseGracePeriod("1000:00")).toBe(null);
});
});
describe "validate", ->
it "enforces that the passing grade is <= the minimum grade to receive credit if credit is enabled", ->
@model.set({minimum_grade_credit: 0.8, grace_period: '01:00', is_credit_course: true})
@model.set('grade_cutoffs', [0.9], validate: true)
expect(_.keys(@model.validationError)).toContain('minimum_grade_credit')
return describe("validate", function() {
it("enforces that the passing grade is <= the minimum grade to receive credit if credit is enabled", function() {
this.model.set({minimum_grade_credit: 0.8, grace_period: '01:00', is_credit_course: true});
this.model.set('grade_cutoffs', [0.9], {validate: true});
return expect(_.keys(this.model.validationError)).toContain('minimum_grade_credit');
});
it "does not enforce the passing grade limit in non-credit courses", ->
@model.set({minimum_grade_credit: 0.8, grace_period: '01:00', is_credit_course: false})
@model.set({grade_cutoffs: [0.9]}, validate: true)
expect(@model.validationError).toBe(null)
return it("does not enforce the passing grade limit in non-credit courses", function() {
this.model.set({minimum_grade_credit: 0.8, grace_period: '01:00', is_credit_course: false});
this.model.set({grade_cutoffs: [0.9]}, {validate: true});
return expect(this.model.validationError).toBe(null);
});
});
})
);

View File

@@ -1,78 +1,98 @@
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS203: Remove `|| {}` from converted for-own loops
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define ["backbone", "js/models/textbook", "js/collections/textbook", "js/models/chapter", "js/collections/chapter", "cms/js/main"],
(Backbone, Textbook, TextbookSet, Chapter, ChapterSet, main) ->
define(["backbone", "js/models/textbook", "js/collections/textbook", "js/models/chapter", "js/collections/chapter", "cms/js/main"],
function(Backbone, Textbook, TextbookSet, Chapter, ChapterSet, main) {
describe "Textbook model", ->
beforeEach ->
main()
@model = new Textbook()
CMS.URL.TEXTBOOKS = "/textbooks"
describe("Textbook model", function() {
beforeEach(function() {
main();
this.model = new Textbook();
return CMS.URL.TEXTBOOKS = "/textbooks";
});
afterEach ->
delete CMS.URL.TEXTBOOKS
afterEach(() => delete CMS.URL.TEXTBOOKS);
describe "Basic", ->
it "should have an empty name by default", ->
expect(@model.get("name")).toEqual("")
describe("Basic", function() {
it("should have an empty name by default", function() {
return expect(this.model.get("name")).toEqual("");
});
it "should not show chapters by default", ->
expect(@model.get("showChapters")).toBeFalsy()
it("should not show chapters by default", function() {
return expect(this.model.get("showChapters")).toBeFalsy();
});
it "should have a ChapterSet with one chapter by default", ->
chapters = @model.get("chapters")
expect(chapters).toBeInstanceOf(ChapterSet)
expect(chapters.length).toEqual(1)
expect(chapters.at(0).isEmpty()).toBeTruthy()
it("should have a ChapterSet with one chapter by default", function() {
const chapters = this.model.get("chapters");
expect(chapters).toBeInstanceOf(ChapterSet);
expect(chapters.length).toEqual(1);
return expect(chapters.at(0).isEmpty()).toBeTruthy();
});
it "should be empty by default", ->
expect(@model.isEmpty()).toBeTruthy()
it("should be empty by default", function() {
return expect(this.model.isEmpty()).toBeTruthy();
});
it "should have a URL root", ->
urlRoot = _.result(@model, 'urlRoot')
expect(urlRoot).toBeTruthy()
it("should have a URL root", function() {
const urlRoot = _.result(this.model, 'urlRoot');
return expect(urlRoot).toBeTruthy();
});
it "should be able to reset itself", ->
@model.set("name", "foobar")
@model.reset()
expect(@model.get("name")).toEqual("")
it("should be able to reset itself", function() {
this.model.set("name", "foobar");
this.model.reset();
return expect(this.model.get("name")).toEqual("");
});
it "should not be dirty by default", ->
expect(@model.isDirty()).toBeFalsy()
it("should not be dirty by default", function() {
return expect(this.model.isDirty()).toBeFalsy();
});
it "should be dirty after it's been changed", ->
@model.set("name", "foobar")
expect(@model.isDirty()).toBeTruthy()
it("should be dirty after it's been changed", function() {
this.model.set("name", "foobar");
return expect(this.model.isDirty()).toBeTruthy();
});
it "should not be dirty after calling setOriginalAttributes", ->
@model.set("name", "foobar")
@model.setOriginalAttributes()
expect(@model.isDirty()).toBeFalsy()
return it("should not be dirty after calling setOriginalAttributes", function() {
this.model.set("name", "foobar");
this.model.setOriginalAttributes();
return expect(this.model.isDirty()).toBeFalsy();
});
});
describe "Input/Output", ->
deepAttributes = (obj) ->
if obj instanceof Backbone.Model
deepAttributes(obj.attributes)
else if obj instanceof Backbone.Collection
obj.map(deepAttributes);
else if _.isArray(obj)
_.map(obj, deepAttributes);
else if _.isObject(obj)
attributes = {};
for own prop, val of obj
attributes[prop] = deepAttributes(val)
attributes
else
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 (_.isArray(obj)) {
return _.map(obj, deepAttributes);
} else if (_.isObject(obj)) {
const attributes = {};
for (let prop of Object.keys(obj || {})) {
const val = obj[prop];
attributes[prop] = deepAttributes(val);
}
return attributes;
} else {
return obj;
}
};
it "should match server model to client model", ->
serverModelSpec = {
return it("should match server model to client model", function() {
const serverModelSpec = {
"tab_title": "My Textbook",
"chapters": [
{"title": "Chapter 1", "url": "/ch1.pdf"},
{"title": "Chapter 2", "url": "/ch2.pdf"},
]
}
clientModelSpec = {
};
const clientModelSpec = {
"name": "My Textbook",
"showChapters": false,
"editing": false,
@@ -86,113 +106,142 @@ define ["backbone", "js/models/textbook", "js/collections/textbook", "js/models/
"order": 2
}
]
}
};
model = new Textbook(serverModelSpec, {parse: true})
expect(deepAttributes(model)).toEqual(clientModelSpec)
expect(model.toJSON()).toEqual(serverModelSpec)
const model = new Textbook(serverModelSpec, {parse: true});
expect(deepAttributes(model)).toEqual(clientModelSpec);
return expect(model.toJSON()).toEqual(serverModelSpec);
});
});
describe "Validation", ->
it "requires a name", ->
model = new Textbook({name: ""})
expect(model.isValid()).toBeFalsy()
return describe("Validation", function() {
it("requires a name", function() {
const model = new Textbook({name: ""});
return expect(model.isValid()).toBeFalsy();
});
it "requires at least one chapter", ->
model = new Textbook({name: "foo"})
model.get("chapters").reset()
expect(model.isValid()).toBeFalsy()
it("requires at least one chapter", function() {
const model = new Textbook({name: "foo"});
model.get("chapters").reset();
return expect(model.isValid()).toBeFalsy();
});
it "requires a valid chapter", ->
chapter = new Chapter()
chapter.isValid = -> false
model = new Textbook({name: "foo"})
model.get("chapters").reset([chapter])
expect(model.isValid()).toBeFalsy()
it("requires a valid chapter", function() {
const chapter = new Chapter();
chapter.isValid = () => false;
const model = new Textbook({name: "foo"});
model.get("chapters").reset([chapter]);
return expect(model.isValid()).toBeFalsy();
});
it "requires all chapters to be valid", ->
chapter1 = new Chapter()
chapter1.isValid = -> true
chapter2 = new Chapter()
chapter2.isValid = -> false
model = new Textbook({name: "foo"})
model.get("chapters").reset([chapter1, chapter2])
expect(model.isValid()).toBeFalsy()
it("requires all chapters to be valid", function() {
const chapter1 = new Chapter();
chapter1.isValid = () => true;
const chapter2 = new Chapter();
chapter2.isValid = () => false;
const model = new Textbook({name: "foo"});
model.get("chapters").reset([chapter1, chapter2]);
return expect(model.isValid()).toBeFalsy();
});
it "can pass validation", ->
chapter = new Chapter()
chapter.isValid = -> true
model = new Textbook({name: "foo"})
model.get("chapters").reset([chapter])
expect(model.isValid()).toBeTruthy()
return it("can pass validation", function() {
const chapter = new Chapter();
chapter.isValid = () => true;
const model = new Textbook({name: "foo"});
model.get("chapters").reset([chapter]);
return expect(model.isValid()).toBeTruthy();
});
});
});
describe "Textbook collection", ->
beforeEach ->
CMS.URL.TEXTBOOKS = "/textbooks"
@collection = new TextbookSet()
describe("Textbook collection", function() {
beforeEach(function() {
CMS.URL.TEXTBOOKS = "/textbooks";
return this.collection = new TextbookSet();
});
afterEach ->
delete CMS.URL.TEXTBOOKS
afterEach(() => delete CMS.URL.TEXTBOOKS);
it "should have a url set", ->
url = _.result(@collection, 'url')
expect(url).toEqual("/textbooks")
return it("should have a url set", function() {
const url = _.result(this.collection, 'url');
return expect(url).toEqual("/textbooks");
});
});
describe "Chapter model", ->
beforeEach ->
@model = new Chapter()
describe("Chapter model", function() {
beforeEach(function() {
return this.model = new Chapter();
});
describe "Basic", ->
it "should have a name by default", ->
expect(@model.get("name")).toEqual("")
describe("Basic", function() {
it("should have a name by default", function() {
return expect(this.model.get("name")).toEqual("");
});
it "should have an asset_path by default", ->
expect(@model.get("asset_path")).toEqual("")
it("should have an asset_path by default", function() {
return expect(this.model.get("asset_path")).toEqual("");
});
it "should have an order by default", ->
expect(@model.get("order")).toEqual(1)
it("should have an order by default", function() {
return expect(this.model.get("order")).toEqual(1);
});
it "should be empty by default", ->
expect(@model.isEmpty()).toBeTruthy()
return it("should be empty by default", function() {
return expect(this.model.isEmpty()).toBeTruthy();
});
});
describe "Validation", ->
it "requires a name", ->
model = new Chapter({name: "", asset_path: "a.pdf"})
expect(model.isValid()).toBeFalsy()
return describe("Validation", function() {
it("requires a name", function() {
const model = new Chapter({name: "", asset_path: "a.pdf"});
return expect(model.isValid()).toBeFalsy();
});
it "requires an asset_path", ->
model = new Chapter({name: "a", asset_path: ""})
expect(model.isValid()).toBeFalsy()
it("requires an asset_path", function() {
const model = new Chapter({name: "a", asset_path: ""});
return expect(model.isValid()).toBeFalsy();
});
it "can pass validation", ->
model = new Chapter({name: "a", asset_path: "a.pdf"})
expect(model.isValid()).toBeTruthy()
return it("can pass validation", function() {
const model = new Chapter({name: "a", asset_path: "a.pdf"});
return expect(model.isValid()).toBeTruthy();
});
});
});
describe "Chapter collection", ->
beforeEach ->
@collection = new ChapterSet()
return describe("Chapter collection", function() {
beforeEach(function() {
return this.collection = new ChapterSet();
});
it "is empty by default", ->
expect(@collection.isEmpty()).toBeTruthy()
it("is empty by default", function() {
return expect(this.collection.isEmpty()).toBeTruthy();
});
it "is empty if all chapters are empty", ->
@collection.add([{}, {}, {}])
expect(@collection.isEmpty()).toBeTruthy()
it("is empty if all chapters are empty", function() {
this.collection.add([{}, {}, {}]);
return expect(this.collection.isEmpty()).toBeTruthy();
});
it "is not empty if a chapter is not empty", ->
@collection.add([{}, {name: "full"}, {}])
expect(@collection.isEmpty()).toBeFalsy()
it("is not empty if a chapter is not empty", function() {
this.collection.add([{}, {name: "full"}, {}]);
return expect(this.collection.isEmpty()).toBeFalsy();
});
it "should have a nextOrder function", ->
expect(@collection.nextOrder()).toEqual(1)
@collection.add([{}])
expect(@collection.nextOrder()).toEqual(2)
@collection.add([{}])
expect(@collection.nextOrder()).toEqual(3)
# verify that it doesn't just return an incrementing value each time
expect(@collection.nextOrder()).toEqual(3)
# try going back one
@collection.remove(@collection.last())
expect(@collection.nextOrder()).toEqual(2)
return it("should have a nextOrder function", function() {
expect(this.collection.nextOrder()).toEqual(1);
this.collection.add([{}]);
expect(this.collection.nextOrder()).toEqual(2);
this.collection.add([{}]);
expect(this.collection.nextOrder()).toEqual(3);
// verify that it doesn't just return an incrementing value each time
expect(this.collection.nextOrder()).toEqual(3);
// try going back one
this.collection.remove(this.collection.last());
return expect(this.collection.nextOrder()).toEqual(2);
});
});
});

View File

@@ -1,71 +1,93 @@
define ["js/models/uploads"], (FileUpload) ->
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(["js/models/uploads"], FileUpload =>
describe "FileUpload", ->
beforeEach ->
@model = new FileUpload()
describe("FileUpload", function() {
beforeEach(function() {
return this.model = new FileUpload();
});
it "is unfinished by default", ->
expect(@model.get("finished")).toBeFalsy()
it("is unfinished by default", function() {
return expect(this.model.get("finished")).toBeFalsy();
});
it "is not uploading by default", ->
expect(@model.get("uploading")).toBeFalsy()
it("is not uploading by default", function() {
return expect(this.model.get("uploading")).toBeFalsy();
});
it "is valid by default", ->
expect(@model.isValid()).toBeTruthy()
it("is valid by default", function() {
return expect(this.model.isValid()).toBeTruthy();
});
it "is valid for text files by default", ->
file = {"type": "text/plain", "name": "filename.txt"}
@model.set("selectedFile", file);
expect(@model.isValid()).toBeTruthy()
it("is valid for text files by default", function() {
const file = {"type": "text/plain", "name": "filename.txt"};
this.model.set("selectedFile", file);
return expect(this.model.isValid()).toBeTruthy();
});
it "is valid for PNG files by default", ->
file = {"type": "image/png", "name": "filename.png"}
@model.set("selectedFile", file);
expect(@model.isValid()).toBeTruthy()
it("is valid for PNG files by default", function() {
const file = {"type": "image/png", "name": "filename.png"};
this.model.set("selectedFile", file);
return expect(this.model.isValid()).toBeTruthy();
});
it "can accept a file type when explicitly set", ->
file = {"type": "image/png", "name": "filename.png"}
@model.set("mimeTypes": ["image/png"])
@model.set("selectedFile", file)
expect(@model.isValid()).toBeTruthy()
it("can accept a file type when explicitly set", function() {
const file = {"type": "image/png", "name": "filename.png"};
this.model.set({"mimeTypes": ["image/png"]});
this.model.set("selectedFile", file);
return expect(this.model.isValid()).toBeTruthy();
});
it "can accept a file format when explicitly set", ->
file = {"type": "", "name": "filename.png"}
@model.set("fileFormats": ["png"])
@model.set("selectedFile", file)
expect(@model.isValid()).toBeTruthy()
it("can accept a file format when explicitly set", function() {
const file = {"type": "", "name": "filename.png"};
this.model.set({"fileFormats": ["png"]});
this.model.set("selectedFile", file);
return expect(this.model.isValid()).toBeTruthy();
});
it "can accept multiple file types", ->
file = {"type": "image/gif", "name": "filename.gif"}
@model.set("mimeTypes": ["image/png", "image/jpeg", "image/gif"])
@model.set("selectedFile", file)
expect(@model.isValid()).toBeTruthy()
it("can accept multiple file types", function() {
const file = {"type": "image/gif", "name": "filename.gif"};
this.model.set({"mimeTypes": ["image/png", "image/jpeg", "image/gif"]});
this.model.set("selectedFile", file);
return expect(this.model.isValid()).toBeTruthy();
});
it "can accept multiple file formats", ->
file = {"type": "image/gif", "name": "filename.gif"}
@model.set("fileFormats": ["png", "jpeg", "gif"])
@model.set("selectedFile", file)
expect(@model.isValid()).toBeTruthy()
it("can accept multiple file formats", function() {
const file = {"type": "image/gif", "name": "filename.gif"};
this.model.set({"fileFormats": ["png", "jpeg", "gif"]});
this.model.set("selectedFile", file);
return expect(this.model.isValid()).toBeTruthy();
});
describe "fileTypes", ->
it "returns a list of the uploader's file types", ->
@model.set('mimeTypes', ['image/png', 'application/json'])
@model.set('fileFormats', ['gif', 'srt'])
expect(@model.fileTypes()).toEqual(['PNG', 'JSON', 'GIF', 'SRT'])
describe("fileTypes", () =>
it("returns a list of the uploader's file types", function() {
this.model.set('mimeTypes', ['image/png', 'application/json']);
this.model.set('fileFormats', ['gif', 'srt']);
return expect(this.model.fileTypes()).toEqual(['PNG', 'JSON', 'GIF', 'SRT']);
})
);
describe "formatValidTypes", ->
it "returns a map of formatted file types and extensions", ->
@model.set('mimeTypes', ['image/png', 'image/jpeg', 'application/json'])
formatted = @model.formatValidTypes()
expect(formatted).toEqual(
return describe("formatValidTypes", function() {
it("returns a map of formatted file types and extensions", function() {
this.model.set('mimeTypes', ['image/png', 'image/jpeg', 'application/json']);
const formatted = this.model.formatValidTypes();
return expect(formatted).toEqual({
fileTypes: 'PNG, JPEG or JSON',
fileExtensions: '.png, .jpeg or .json'
)
});
});
it "does not format with only one mime type", ->
@model.set('mimeTypes', ['application/pdf'])
formatted = @model.formatValidTypes()
expect(formatted).toEqual(
return it("does not format with only one mime type", function() {
this.model.set('mimeTypes', ['application/pdf']);
const formatted = this.model.formatValidTypes();
return expect(formatted).toEqual({
fileTypes: 'PDF',
fileExtensions: '.pdf'
)
});
});
});
})
);

View File

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

View File

@@ -1,298 +1,339 @@
define ["js/views/course_info_handout", "js/views/course_info_update", "js/models/module_info",
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(["js/views/course_info_handout", "js/views/course_info_update", "js/models/module_info",
"js/collections/course_update", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers"],
(CourseInfoHandoutsView, CourseInfoUpdateView, ModuleInfo, CourseUpdateCollection, AjaxHelpers) ->
(CourseInfoHandoutsView, CourseInfoUpdateView, ModuleInfo, CourseUpdateCollection, AjaxHelpers) =>
describe "Course Updates and Handouts", ->
courseInfoPage = """
<div class="course-info-wrapper">
<div class="main-column window">
<article class="course-updates" id="course-update-view">
<ol class="update-list" id="course-update-list"></ol>
</article>
</div>
<div class="sidebar window course-handouts" id="course-handouts-view"></div>
</div>
<div class="modal-cover"></div>
"""
describe("Course Updates and Handouts", function() {
const courseInfoPage = `\
<div class="course-info-wrapper">
<div class="main-column window">
<article class="course-updates" id="course-update-view">
<ol class="update-list" id="course-update-list"></ol>
</article>
</div>
<div class="sidebar window course-handouts" id="course-handouts-view"></div>
</div>
<div class="modal-cover"></div>\
`;
beforeEach ->
window.analytics = jasmine.createSpyObj('analytics', ['track'])
window.course_location_analytics = jasmine.createSpy()
beforeEach(function() {
window.analytics = jasmine.createSpyObj('analytics', ['track']);
return window.course_location_analytics = jasmine.createSpy();
});
afterEach ->
delete window.analytics
delete window.course_location_analytics
afterEach(function() {
delete window.analytics;
return delete window.course_location_analytics;
});
describe "Course Updates without Push notification", ->
courseInfoTemplate = readFixtures('course_info_update.underscore')
describe("Course Updates without Push notification", function() {
const courseInfoTemplate = readFixtures('course_info_update.underscore');
beforeEach ->
setFixtures($("<script>", {id: "course_info_update-tpl", type: "text/template"}).text(courseInfoTemplate))
appendSetFixtures courseInfoPage
beforeEach(function() {
let cancelEditingUpdate;
setFixtures($("<script>", {id: "course_info_update-tpl", type: "text/template"}).text(courseInfoTemplate));
appendSetFixtures(courseInfoPage);
@collection = new CourseUpdateCollection()
@collection.url = 'course_info_update/'
@courseInfoEdit = new CourseInfoUpdateView({
this.collection = new CourseUpdateCollection();
this.collection.url = 'course_info_update/';
this.courseInfoEdit = new CourseInfoUpdateView({
el: $('.course-updates'),
collection: @collection,
collection: this.collection,
base_asset_url : 'base-asset-url/'
})
});
@courseInfoEdit.render()
this.courseInfoEdit.render();
@event = {
preventDefault : () -> 'no op'
}
this.event = {
preventDefault() { return 'no op'; }
};
@createNewUpdate = (text) ->
# 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').and.returnValue(text)
@courseInfoEdit.$el.find('.save-button').click()
this.createNewUpdate = function(text) {
// Edit button is not in the template under test (it is in parent HTML).
// Therefore call onNew directly.
this.courseInfoEdit.onNew(this.event);
spyOn(this.courseInfoEdit.$codeMirror, 'getValue').and.returnValue(text);
return this.courseInfoEdit.$el.find('.save-button').click();
};
@cancelNewCourseInfo = (useCancelButton) ->
@courseInfoEdit.onNew(@event)
spyOn(@courseInfoEdit.$modalCover, 'hide').and.callThrough()
this.cancelNewCourseInfo = function(useCancelButton) {
this.courseInfoEdit.onNew(this.event);
spyOn(this.courseInfoEdit.$modalCover, 'hide').and.callThrough();
spyOn(@courseInfoEdit.$codeMirror, 'getValue').and.returnValue('unsaved changes')
model = @collection.at(0)
spyOn(model, "save").and.callThrough()
spyOn(this.courseInfoEdit.$codeMirror, 'getValue').and.returnValue('unsaved changes');
const model = this.collection.at(0);
spyOn(model, "save").and.callThrough();
cancelEditingUpdate(@courseInfoEdit, @courseInfoEdit.$modalCover, useCancelButton)
cancelEditingUpdate(this.courseInfoEdit, this.courseInfoEdit.$modalCover, useCancelButton);
expect(@courseInfoEdit.$modalCover.hide).toHaveBeenCalled()
expect(model.save).not.toHaveBeenCalled()
previewContents = @courseInfoEdit.$el.find('.update-contents').html()
expect(previewContents).not.toEqual('unsaved changes')
expect(this.courseInfoEdit.$modalCover.hide).toHaveBeenCalled();
expect(model.save).not.toHaveBeenCalled();
const previewContents = this.courseInfoEdit.$el.find('.update-contents').html();
return expect(previewContents).not.toEqual('unsaved changes');
};
@doNotCloseNewCourseInfo = () ->
@courseInfoEdit.onNew(@event)
spyOn(@courseInfoEdit.$modalCover, 'hide').and.callThrough()
this.doNotCloseNewCourseInfo = function() {
this.courseInfoEdit.onNew(this.event);
spyOn(this.courseInfoEdit.$modalCover, 'hide').and.callThrough();
spyOn(@courseInfoEdit.$codeMirror, 'getValue').and.returnValue('unsaved changes')
model = @collection.at(0)
spyOn(model, "save").and.callThrough()
spyOn(this.courseInfoEdit.$codeMirror, 'getValue').and.returnValue('unsaved changes');
const model = this.collection.at(0);
spyOn(model, "save").and.callThrough();
cancelEditingUpdate(@courseInfoEdit, @courseInfoEdit.$modalCover, false)
cancelEditingUpdate(this.courseInfoEdit, this.courseInfoEdit.$modalCover, false);
expect(model.save).not.toHaveBeenCalled()
expect(@courseInfoEdit.$modalCover.hide).not.toHaveBeenCalled()
expect(model.save).not.toHaveBeenCalled();
return expect(this.courseInfoEdit.$modalCover.hide).not.toHaveBeenCalled();
};
@cancelExistingCourseInfo = (useCancelButton) ->
@createNewUpdate('existing update')
@courseInfoEdit.$el.find('.edit-button').click()
spyOn(@courseInfoEdit.$modalCover, 'hide').and.callThrough()
this.cancelExistingCourseInfo = function(useCancelButton) {
this.createNewUpdate('existing update');
this.courseInfoEdit.$el.find('.edit-button').click();
spyOn(this.courseInfoEdit.$modalCover, 'hide').and.callThrough();
spyOn(@courseInfoEdit.$codeMirror, 'getValue').and.returnValue('modification')
model = @collection.at(0)
spyOn(model, "save").and.callThrough()
model.id = "saved_to_server"
cancelEditingUpdate(@courseInfoEdit, @courseInfoEdit.$modalCover, useCancelButton)
spyOn(this.courseInfoEdit.$codeMirror, 'getValue').and.returnValue('modification');
const model = this.collection.at(0);
spyOn(model, "save").and.callThrough();
model.id = "saved_to_server";
cancelEditingUpdate(this.courseInfoEdit, this.courseInfoEdit.$modalCover, useCancelButton);
expect(@courseInfoEdit.$modalCover.hide).toHaveBeenCalled()
expect(model.save).not.toHaveBeenCalled()
previewContents = @courseInfoEdit.$el.find('.update-contents').html()
expect(previewContents).toEqual('existing update')
expect(this.courseInfoEdit.$modalCover.hide).toHaveBeenCalled();
expect(model.save).not.toHaveBeenCalled();
const previewContents = this.courseInfoEdit.$el.find('.update-contents').html();
return expect(previewContents).toEqual('existing update');
};
@testInvalidDateValue = (value) ->
@courseInfoEdit.onNew(@event)
expect(@courseInfoEdit.$el.find('.save-button').hasClass("is-disabled")).toEqual(false)
@courseInfoEdit.$el.find('input.date').val(value).trigger("change")
expect(@courseInfoEdit.$el.find('.save-button').hasClass("is-disabled")).toEqual(true)
@courseInfoEdit.$el.find('input.date').val("01/01/16").trigger("change")
expect(@courseInfoEdit.$el.find('.save-button').hasClass("is-disabled")).toEqual(false)
this.testInvalidDateValue = function(value) {
this.courseInfoEdit.onNew(this.event);
expect(this.courseInfoEdit.$el.find('.save-button').hasClass("is-disabled")).toEqual(false);
this.courseInfoEdit.$el.find('input.date').val(value).trigger("change");
expect(this.courseInfoEdit.$el.find('.save-button').hasClass("is-disabled")).toEqual(true);
this.courseInfoEdit.$el.find('input.date').val("01/01/16").trigger("change");
return expect(this.courseInfoEdit.$el.find('.save-button').hasClass("is-disabled")).toEqual(false);
};
cancelEditingUpdate = (update, modalCover, useCancelButton) ->
if useCancelButton
update.$el.find('.cancel-button').click()
else
modalCover.click()
return cancelEditingUpdate = function(update, modalCover, useCancelButton) {
if (useCancelButton) {
return update.$el.find('.cancel-button').click();
} else {
return modalCover.click();
}
};
});
it "does send expected data on save", ->
requests = AjaxHelpers["requests"](this)
it("does send expected data on save", function() {
const requests = AjaxHelpers["requests"](this);
# Create a new update, verifying that the model is created
# in the collection and save is called.
expect(@collection.isEmpty()).toBeTruthy()
@courseInfoEdit.onNew(@event)
expect(@collection.length).toEqual(1)
model = @collection.at(0)
spyOn(model, "save").and.callThrough()
spyOn(@courseInfoEdit.$codeMirror, 'getValue').and.returnValue('/static/image.jpg')
// Create a new update, verifying that the model is created
// in the collection and save is called.
expect(this.collection.isEmpty()).toBeTruthy();
this.courseInfoEdit.onNew(this.event);
expect(this.collection.length).toEqual(1);
const model = this.collection.at(0);
spyOn(model, "save").and.callThrough();
spyOn(this.courseInfoEdit.$codeMirror, 'getValue').and.returnValue('/static/image.jpg');
# Click the "Save button."
@courseInfoEdit.$el.find('.save-button').click()
expect(model.save).toHaveBeenCalled()
// Click the "Save button."
this.courseInfoEdit.$el.find('.save-button').click();
expect(model.save).toHaveBeenCalled();
# Verify push_notification_selected is set to false.
requestSent = JSON.parse(requests[requests.length - 1].requestBody)
expect(requestSent.push_notification_selected).toEqual(false)
// Verify push_notification_selected is set to false.
const requestSent = JSON.parse(requests[requests.length - 1].requestBody);
expect(requestSent.push_notification_selected).toEqual(false);
# Verify the link is not rewritten when saved.
expect(requestSent.content).toEqual('/static/image.jpg')
// Verify the link is not rewritten when saved.
expect(requestSent.content).toEqual('/static/image.jpg');
# Verify that analytics are sent
expect(window.analytics.track).toHaveBeenCalled()
// Verify that analytics are sent
return expect(window.analytics.track).toHaveBeenCalled();
});
it "does rewrite links for preview", ->
# Create a new update.
@createNewUpdate('/static/image.jpg')
it("does rewrite links for preview", function() {
// Create a new update.
this.createNewUpdate('/static/image.jpg');
# Verify the link is rewritten for preview purposes.
previewContents = @courseInfoEdit.$el.find('.update-contents').html()
expect(previewContents).toEqual('base-asset-url/image.jpg')
// Verify the link is rewritten for preview purposes.
const previewContents = this.courseInfoEdit.$el.find('.update-contents').html();
return expect(previewContents).toEqual('base-asset-url/image.jpg');
});
it "shows static links in edit mode", ->
@createNewUpdate('/static/image.jpg')
it("shows static links in edit mode", function() {
this.createNewUpdate('/static/image.jpg');
# Click edit and verify CodeMirror contents.
@courseInfoEdit.$el.find('.edit-button').click()
expect(@courseInfoEdit.$codeMirror.getValue()).toEqual('/static/image.jpg')
// Click edit and verify CodeMirror contents.
this.courseInfoEdit.$el.find('.edit-button').click();
return expect(this.courseInfoEdit.$codeMirror.getValue()).toEqual('/static/image.jpg');
});
it "removes newly created course info on cancel", ->
@cancelNewCourseInfo(true)
it("removes newly created course info on cancel", function() {
return this.cancelNewCourseInfo(true);
});
it "do not close new course info on click outside modal", ->
@doNotCloseNewCourseInfo()
it("do not close new course info on click outside modal", function() {
return this.doNotCloseNewCourseInfo();
});
it "does not remove existing course info on cancel", ->
@cancelExistingCourseInfo(true)
it("does not remove existing course info on cancel", function() {
return this.cancelExistingCourseInfo(true);
});
it "does not remove existing course info on click outside modal", ->
@cancelExistingCourseInfo(false)
it("does not remove existing course info on click outside modal", function() {
return this.cancelExistingCourseInfo(false);
});
it "does not allow updates to be saved with an invalid date", ->
@testInvalidDateValue("Marchtober 40, 2048")
it("does not allow updates to be saved with an invalid date", function() {
return this.testInvalidDateValue("Marchtober 40, 2048");
});
it "does not allow updates to be saved with a blank date", ->
@testInvalidDateValue("")
return it("does not allow updates to be saved with a blank date", function() {
return this.testInvalidDateValue("");
});
});
describe "Course Updates WITH Push notification", ->
courseInfoTemplate = readFixtures('course_info_update.underscore')
describe("Course Updates WITH Push notification", function() {
const courseInfoTemplate = readFixtures('course_info_update.underscore');
beforeEach ->
setFixtures($("<script>", {id: "course_info_update-tpl", type: "text/template"}).text(courseInfoTemplate))
appendSetFixtures courseInfoPage
@collection = new CourseUpdateCollection()
@collection.url = 'course_info_update/'
@courseInfoEdit = new CourseInfoUpdateView({
beforeEach(function() {
setFixtures($("<script>", {id: "course_info_update-tpl", type: "text/template"}).text(courseInfoTemplate));
appendSetFixtures(courseInfoPage);
this.collection = new CourseUpdateCollection();
this.collection.url = 'course_info_update/';
this.courseInfoEdit = new CourseInfoUpdateView({
el: $('.course-updates'),
collection: @collection,
collection: this.collection,
base_asset_url : 'base-asset-url/',
push_notification_enabled : true
})
@courseInfoEdit.render()
@event = {preventDefault : () -> 'no op'}
@courseInfoEdit.onNew(@event)
});
this.courseInfoEdit.render();
this.event = {preventDefault() { return 'no op'; }};
return this.courseInfoEdit.onNew(this.event);
});
it "shows push notification checkbox as selected by default", ->
expect(@courseInfoEdit.$el.find('.toggle-checkbox')).toBeChecked()
it("shows push notification checkbox as selected by default", function() {
return expect(this.courseInfoEdit.$el.find('.toggle-checkbox')).toBeChecked();
});
it "sends correct default value for push_notification_selected", ->
requests = AjaxHelpers.requests(this);
@courseInfoEdit.$el.find('.save-button').click()
requestSent = JSON.parse(requests[requests.length - 1].requestBody)
expect(requestSent.push_notification_selected).toEqual(true)
it("sends correct default value for push_notification_selected", function() {
const requests = AjaxHelpers.requests(this);
this.courseInfoEdit.$el.find('.save-button').click();
const requestSent = JSON.parse(requests[requests.length - 1].requestBody);
expect(requestSent.push_notification_selected).toEqual(true);
# Check that analytics send push_notification info
analytics_payload = window.analytics.track.calls.first().args[1]
expect(analytics_payload).toEqual(jasmine.objectContaining({'push_notification_selected': true}))
// Check that analytics send push_notification info
const analytics_payload = window.analytics.track.calls.first().args[1];
return expect(analytics_payload).toEqual(jasmine.objectContaining({'push_notification_selected': true}));
});
it "sends correct value for push_notification_selected when it is unselected", ->
requests = AjaxHelpers.requests(this);
# unselect push notification
@courseInfoEdit.$el.find('.toggle-checkbox').attr('checked', false);
@courseInfoEdit.$el.find('.save-button').click()
requestSent = JSON.parse(requests[requests.length - 1].requestBody)
expect(requestSent.push_notification_selected).toEqual(false)
return it("sends correct value for push_notification_selected when it is unselected", function() {
const requests = AjaxHelpers.requests(this);
// unselect push notification
this.courseInfoEdit.$el.find('.toggle-checkbox').attr('checked', false);
this.courseInfoEdit.$el.find('.save-button').click();
const requestSent = JSON.parse(requests[requests.length - 1].requestBody);
expect(requestSent.push_notification_selected).toEqual(false);
# Check that analytics send push_notification info
analytics_payload = window.analytics.track.calls.first().args[1]
expect(analytics_payload).toEqual(jasmine.objectContaining({'push_notification_selected': false}))
// Check that analytics send push_notification info
const analytics_payload = window.analytics.track.calls.first().args[1];
return expect(analytics_payload).toEqual(jasmine.objectContaining({'push_notification_selected': false}));
});
});
describe "Course Handouts", ->
handoutsTemplate = readFixtures('course_info_handouts.underscore')
return describe("Course Handouts", function() {
const handoutsTemplate = readFixtures('course_info_handouts.underscore');
beforeEach ->
setFixtures($("<script>", {id: "course_info_handouts-tpl", type: "text/template"}).text(handoutsTemplate))
appendSetFixtures courseInfoPage
beforeEach(function() {
setFixtures($("<script>", {id: "course_info_handouts-tpl", type: "text/template"}).text(handoutsTemplate));
appendSetFixtures(courseInfoPage);
@model = new ModuleInfo({
this.model = new ModuleInfo({
id: 'handouts-id',
data: '/static/fromServer.jpg'
})
});
@handoutsEdit = new CourseInfoHandoutsView({
this.handoutsEdit = new CourseInfoHandoutsView({
el: $('#course-handouts-view'),
model: @model,
model: this.model,
base_asset_url: 'base-asset-url/'
});
@handoutsEdit.render()
return this.handoutsEdit.render();
});
it "saves <ol></ol> when content left empty", ->
requests = AjaxHelpers["requests"](this)
it("saves <ol></ol> when content left empty", function() {
const requests = AjaxHelpers["requests"](this);
# Enter empty string in the handouts section, verifying that the model
# is saved with '<ol></ol>' instead of the empty string
@handoutsEdit.$el.find('.edit-button').click()
spyOn(@handoutsEdit.$codeMirror, 'getValue').and.returnValue('')
spyOn(@model, "save").and.callThrough()
@handoutsEdit.$el.find('.save-button').click()
expect(@model.save).toHaveBeenCalled()
// Enter empty string in the handouts section, verifying that the model
// is saved with '<ol></ol>' instead of the empty string
this.handoutsEdit.$el.find('.edit-button').click();
spyOn(this.handoutsEdit.$codeMirror, 'getValue').and.returnValue('');
spyOn(this.model, "save").and.callThrough();
this.handoutsEdit.$el.find('.save-button').click();
expect(this.model.save).toHaveBeenCalled();
contentSaved = JSON.parse(requests[requests.length - 1].requestBody).data
expect(contentSaved).toEqual('<ol></ol>')
const contentSaved = JSON.parse(requests[requests.length - 1].requestBody).data;
return expect(contentSaved).toEqual('<ol></ol>');
});
it "does not rewrite links on save", ->
requests = AjaxHelpers["requests"](this)
it("does not rewrite links on save", function() {
const requests = AjaxHelpers["requests"](this);
# 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').and.returnValue('/static/image.jpg')
spyOn(@model, "save").and.callThrough()
@handoutsEdit.$el.find('.save-button').click()
expect(@model.save).toHaveBeenCalled()
// Enter something in the handouts section, verifying that the model is saved
// when "Save" is clicked.
this.handoutsEdit.$el.find('.edit-button').click();
spyOn(this.handoutsEdit.$codeMirror, 'getValue').and.returnValue('/static/image.jpg');
spyOn(this.model, "save").and.callThrough();
this.handoutsEdit.$el.find('.save-button').click();
expect(this.model.save).toHaveBeenCalled();
contentSaved = JSON.parse(requests[requests.length - 1].requestBody).data
expect(contentSaved).toEqual('/static/image.jpg')
const contentSaved = JSON.parse(requests[requests.length - 1].requestBody).data;
return expect(contentSaved).toEqual('/static/image.jpg');
});
it "does rewrite links in initial content", ->
expect(@handoutsEdit.$preview.html().trim()).toBe('base-asset-url/fromServer.jpg')
it("does rewrite links in initial content", function() {
return expect(this.handoutsEdit.$preview.html().trim()).toBe('base-asset-url/fromServer.jpg');
});
it "does rewrite links after edit", ->
# Edit handouts and save.
@handoutsEdit.$el.find('.edit-button').click()
spyOn(@handoutsEdit.$codeMirror, 'getValue').and.returnValue('/static/image.jpg')
@handoutsEdit.$el.find('.save-button').click()
it("does rewrite links after edit", function() {
// Edit handouts and save.
this.handoutsEdit.$el.find('.edit-button').click();
spyOn(this.handoutsEdit.$codeMirror, 'getValue').and.returnValue('/static/image.jpg');
this.handoutsEdit.$el.find('.save-button').click();
# Verify preview text.
expect(@handoutsEdit.$preview.html().trim()).toBe('base-asset-url/image.jpg')
// Verify preview text.
return expect(this.handoutsEdit.$preview.html().trim()).toBe('base-asset-url/image.jpg');
});
it "shows static links in edit mode", ->
# Click edit and verify CodeMirror contents.
@handoutsEdit.$el.find('.edit-button').click()
expect(@handoutsEdit.$codeMirror.getValue().trim()).toEqual('/static/fromServer.jpg')
it("shows static links in edit mode", function() {
// Click edit and verify CodeMirror contents.
this.handoutsEdit.$el.find('.edit-button').click();
return expect(this.handoutsEdit.$codeMirror.getValue().trim()).toEqual('/static/fromServer.jpg');
});
it "can open course handouts with bad html on edit", ->
# Enter some bad html in handouts section, verifying that the
# model/handoutform opens when "Edit" is clicked
return it("can open course handouts with bad html on edit", function() {
// Enter some bad html in handouts section, verifying that the
// model/handoutform opens when "Edit" is clicked
@model = new ModuleInfo({
this.model = new ModuleInfo({
id: 'handouts-id',
data: '<p><a href="[URL OF FILE]>[LINK TEXT]</a></p>'
})
@handoutsEdit = new CourseInfoHandoutsView({
});
this.handoutsEdit = new CourseInfoHandoutsView({
el: $('#course-handouts-view'),
model: @model,
model: this.model,
base_asset_url: 'base-asset-url/'
});
@handoutsEdit.render()
this.handoutsEdit.render();
expect($('.edit-handouts-form').is(':hidden')).toEqual(true)
@handoutsEdit.$el.find('.edit-button').click()
expect(@handoutsEdit.$codeMirror.getValue()).toEqual('<p><a href="[URL OF FILE]>[LINK TEXT]</a></p>')
expect($('.edit-handouts-form').is(':hidden')).toEqual(false)
expect($('.edit-handouts-form').is(':hidden')).toEqual(true);
this.handoutsEdit.$el.find('.edit-button').click();
expect(this.handoutsEdit.$codeMirror.getValue()).toEqual('<p><a href="[URL OF FILE]>[LINK TEXT]</a></p>');
return expect($('.edit-handouts-form').is(':hidden')).toEqual(false);
});
});
})
);

View File

@@ -1,30 +1,39 @@
define ["js/models/metadata", "js/collections/metadata", "js/views/metadata", "cms/js/main"],
(MetadataModel, MetadataCollection, MetadataView, main) ->
verifyInputType = (input, expectedType) ->
# Some browsers (e.g. FireFox) do not support the "number"
# input type. We can accept a "text" input instead
# and still get acceptable behavior in the UI.
if expectedType == 'number' and input.type != 'number'
expectedType = 'text'
expect(input.type).toBe(expectedType)
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(["js/models/metadata", "js/collections/metadata", "js/views/metadata", "cms/js/main"],
function(MetadataModel, MetadataCollection, MetadataView, main) {
const verifyInputType = function(input, expectedType) {
// Some browsers (e.g. FireFox) do not support the "number"
// input type. We can accept a "text" input instead
// and still get acceptable behavior in the UI.
if ((expectedType === 'number') && (input.type !== 'number')) {
expectedType = 'text';
}
return expect(input.type).toBe(expectedType);
};
describe "Test Metadata Editor", ->
editorTemplate = readFixtures('metadata-editor.underscore')
numberEntryTemplate = readFixtures('metadata-number-entry.underscore')
stringEntryTemplate = readFixtures('metadata-string-entry.underscore')
optionEntryTemplate = readFixtures('metadata-option-entry.underscore')
listEntryTemplate = readFixtures('metadata-list-entry.underscore')
dictEntryTemplate = readFixtures('metadata-dict-entry.underscore')
return describe("Test Metadata Editor", function() {
const editorTemplate = readFixtures('metadata-editor.underscore');
const numberEntryTemplate = readFixtures('metadata-number-entry.underscore');
const stringEntryTemplate = readFixtures('metadata-string-entry.underscore');
const optionEntryTemplate = readFixtures('metadata-option-entry.underscore');
const listEntryTemplate = readFixtures('metadata-list-entry.underscore');
const dictEntryTemplate = readFixtures('metadata-dict-entry.underscore');
beforeEach ->
setFixtures($("<script>", {id: "metadata-editor-tpl", type: "text/template"}).text(editorTemplate))
appendSetFixtures($("<script>", {id: "metadata-number-entry", type: "text/template"}).text(numberEntryTemplate))
appendSetFixtures($("<script>", {id: "metadata-string-entry", type: "text/template"}).text(stringEntryTemplate))
appendSetFixtures($("<script>", {id: "metadata-option-entry", type: "text/template"}).text(optionEntryTemplate))
appendSetFixtures($("<script>", {id: "metadata-list-entry", type: "text/template"}).text(listEntryTemplate))
appendSetFixtures($("<script>", {id: "metadata-dict-entry", type: "text/template"}).text(dictEntryTemplate))
beforeEach(function() {
setFixtures($("<script>", {id: "metadata-editor-tpl", type: "text/template"}).text(editorTemplate));
appendSetFixtures($("<script>", {id: "metadata-number-entry", type: "text/template"}).text(numberEntryTemplate));
appendSetFixtures($("<script>", {id: "metadata-string-entry", type: "text/template"}).text(stringEntryTemplate));
appendSetFixtures($("<script>", {id: "metadata-option-entry", type: "text/template"}).text(optionEntryTemplate));
appendSetFixtures($("<script>", {id: "metadata-list-entry", type: "text/template"}).text(listEntryTemplate));
return appendSetFixtures($("<script>", {id: "metadata-dict-entry", type: "text/template"}).text(dictEntryTemplate));
});
genericEntry = {
const genericEntry = {
default_value: 'default value',
display_name: "Display Name",
explicitly_set: true,
@@ -33,9 +42,9 @@ define ["js/models/metadata", "js/collections/metadata", "js/views/metadata", "c
options: [],
type: MetadataModel.GENERIC_TYPE,
value: "Word cloud"
}
};
selectEntry = {
const selectEntry = {
default_value: "answered",
display_name: "Show Answer",
explicitly_set: false,
@@ -48,9 +57,9 @@ define ["js/models/metadata", "js/collections/metadata", "js/views/metadata", "c
],
type: MetadataModel.SELECT_TYPE,
value: "always"
}
};
integerEntry = {
const integerEntry = {
default_value: 6,
display_name: "Inputs",
explicitly_set: false,
@@ -59,9 +68,9 @@ define ["js/models/metadata", "js/collections/metadata", "js/views/metadata", "c
options: {min: 1},
type: MetadataModel.INTEGER_TYPE,
value: 5
}
};
floatEntry = {
const floatEntry = {
default_value: 2.7,
display_name: "Weight",
explicitly_set: true,
@@ -70,9 +79,9 @@ define ["js/models/metadata", "js/collections/metadata", "js/views/metadata", "c
options: {min: 1.3, max:100.2, step:0.1},
type: MetadataModel.FLOAT_TYPE,
value: 10.2
}
};
listEntry = {
const listEntry = {
default_value: ["a thing", "another thing"],
display_name: "List",
explicitly_set: false,
@@ -81,9 +90,9 @@ define ["js/models/metadata", "js/collections/metadata", "js/views/metadata", "c
options: [],
type: MetadataModel.LIST_TYPE,
value: ["the first display value", "the second"]
}
};
timeEntry = {
const timeEntry = {
default_value: "00:00:00",
display_name: "Time",
explicitly_set: true,
@@ -92,9 +101,9 @@ define ["js/models/metadata", "js/collections/metadata", "js/views/metadata", "c
options: [],
type: MetadataModel.RELATIVE_TIME_TYPE,
value: "12:12:12"
}
};
dictEntry = {
const dictEntry = {
default_value: {
'en': 'English',
'ru': 'Русский'
@@ -110,13 +119,13 @@ define ["js/models/metadata", "js/collections/metadata", "js/views/metadata", "c
'ua': 'Українська',
'fr': 'Français'
}
}
};
# Test for the editor that creates the individual views.
describe "MetadataView.Editor creates editors for each field", ->
beforeEach ->
@model = new MetadataCollection(
// Test for the editor that creates the individual views.
describe("MetadataView.Editor creates editors for each field", function() {
beforeEach(function() {
return this.model = new MetadataCollection(
[
integerEntry,
floatEntry,
@@ -139,36 +148,40 @@ define ["js/models/metadata", "js/collections/metadata", "js/views/metadata", "c
timeEntry,
dictEntry
]
)
);
});
it "creates child views on initialize, and sorts them alphabetically", ->
view = new MetadataView.Editor({collection: @model})
childModels = view.collection.models
expect(childModels.length).toBe(8)
# Be sure to check list view as well as other input types
childViews = view.$el.find('.setting-input, .list-settings')
expect(childViews.length).toBe(8)
it("creates child views on initialize, and sorts them alphabetically", function() {
const view = new MetadataView.Editor({collection: this.model});
const childModels = view.collection.models;
expect(childModels.length).toBe(8);
// Be sure to check list view as well as other input types
const childViews = view.$el.find('.setting-input, .list-settings');
expect(childViews.length).toBe(8);
verifyEntry = (index, display_name, type) ->
expect(childModels[index].get('display_name')).toBe(display_name)
verifyInputType(childViews[index], type)
const verifyEntry = function(index, display_name, type) {
expect(childModels[index].get('display_name')).toBe(display_name);
return verifyInputType(childViews[index], type);
};
verifyEntry(0, 'Display Name', 'text')
verifyEntry(1, 'Inputs', 'number')
verifyEntry(2, 'List', '')
verifyEntry(3, 'New Dict', '')
verifyEntry(4, 'Show Answer', 'select-one')
verifyEntry(5, 'Time', 'text')
verifyEntry(6, 'Unknown', 'text')
verifyEntry(7, 'Weight', 'number')
verifyEntry(0, 'Display Name', 'text');
verifyEntry(1, 'Inputs', 'number');
verifyEntry(2, 'List', '');
verifyEntry(3, 'New Dict', '');
verifyEntry(4, 'Show Answer', 'select-one');
verifyEntry(5, 'Time', 'text');
verifyEntry(6, 'Unknown', 'text');
return verifyEntry(7, 'Weight', 'number');
});
it "returns its display name", ->
view = new MetadataView.Editor({collection: @model})
expect(view.getDisplayName()).toBe("Word cloud")
it("returns its display name", function() {
const view = new MetadataView.Editor({collection: this.model});
return expect(view.getDisplayName()).toBe("Word cloud");
});
it "returns an empty string if there is no display name property with a valid value", ->
view = new MetadataView.Editor({collection: new MetadataCollection()})
expect(view.getDisplayName()).toBe("")
it("returns an empty string if there is no display name property with a valid value", function() {
let view = new MetadataView.Editor({collection: new MetadataCollection()});
expect(view.getDisplayName()).toBe("");
view = new MetadataView.Editor({collection: new MetadataCollection([
{
@@ -182,391 +195,457 @@ define ["js/models/metadata", "js/collections/metadata", "js/views/metadata", "c
value: null
}])
})
expect(view.getDisplayName()).toBe("")
});
return expect(view.getDisplayName()).toBe("");
});
it "has no modified values by default", ->
view = new MetadataView.Editor({collection: @model})
expect(view.getModifiedMetadataValues()).toEqual({})
it("has no modified values by default", function() {
const view = new MetadataView.Editor({collection: this.model});
return expect(view.getModifiedMetadataValues()).toEqual({});
});
it "returns modified values only", ->
view = new MetadataView.Editor({collection: @model})
childModels = view.collection.models
childModels[0].setValue('updated display name')
childModels[1].setValue(20)
expect(view.getModifiedMetadataValues()).toEqual({
return it("returns modified values only", function() {
const view = new MetadataView.Editor({collection: this.model});
const childModels = view.collection.models;
childModels[0].setValue('updated display name');
childModels[1].setValue(20);
return expect(view.getModifiedMetadataValues()).toEqual({
display_name : 'updated display name',
num_inputs: 20
})
});
});
});
# Tests for individual views.
assertInputType = (view, expectedType) ->
input = view.$el.find('.setting-input')
expect(input.length).toEqual(1)
verifyInputType(input[0], expectedType)
// Tests for individual views.
const assertInputType = function(view, expectedType) {
const input = view.$el.find('.setting-input');
expect(input.length).toEqual(1);
return verifyInputType(input[0], expectedType);
};
assertValueInView = (view, expectedValue) ->
expect(view.getValueFromEditor()).toEqual(expectedValue)
const assertValueInView = (view, expectedValue) => expect(view.getValueFromEditor()).toEqual(expectedValue);
assertCanUpdateView = (view, newValue) ->
view.setValueInEditor(newValue)
expect(view.getValueFromEditor()).toEqual(newValue)
const assertCanUpdateView = function(view, newValue) {
view.setValueInEditor(newValue);
return expect(view.getValueFromEditor()).toEqual(newValue);
};
assertClear = (view, modelValue, editorValue=modelValue) ->
view.clear()
expect(view.model.getValue()).toBe(null)
expect(view.model.getDisplayValue()).toEqual(modelValue)
expect(view.getValueFromEditor()).toEqual(editorValue)
const assertClear = function(view, modelValue, editorValue) {
if (editorValue == null) { editorValue = modelValue; }
view.clear();
expect(view.model.getValue()).toBe(null);
expect(view.model.getDisplayValue()).toEqual(modelValue);
return expect(view.getValueFromEditor()).toEqual(editorValue);
};
assertUpdateModel = (view, originalValue, newValue) ->
view.setValueInEditor(newValue)
expect(view.model.getValue()).toEqual(originalValue)
view.updateModel()
expect(view.model.getValue()).toEqual(newValue)
const assertUpdateModel = function(view, originalValue, newValue) {
view.setValueInEditor(newValue);
expect(view.model.getValue()).toEqual(originalValue);
view.updateModel();
return expect(view.model.getValue()).toEqual(newValue);
};
describe "MetadataView.String is a basic string input with clear functionality", ->
beforeEach ->
model = new MetadataModel(genericEntry)
@view = new MetadataView.String({model: model})
describe("MetadataView.String is a basic string input with clear functionality", function() {
beforeEach(function() {
const model = new MetadataModel(genericEntry);
return this.view = new MetadataView.String({model});
});
it "uses a text input type", ->
assertInputType(@view, 'text')
it("uses a text input type", function() {
return assertInputType(this.view, 'text');
});
it "returns the intial value upon initialization", ->
assertValueInView(@view, 'Word cloud')
it("returns the intial value upon initialization", function() {
return assertValueInView(this.view, 'Word cloud');
});
it "can update its value in the view", ->
assertCanUpdateView(@view, "updated ' \" &")
it("can update its value in the view", function() {
return assertCanUpdateView(this.view, "updated ' \" &");
});
it "has a clear method to revert to the model default", ->
assertClear(@view, 'default value')
it("has a clear method to revert to the model default", function() {
return assertClear(this.view, 'default value');
});
it "has an update model method", ->
assertUpdateModel(@view, 'Word cloud', 'updated')
return it("has an update model method", function() {
return assertUpdateModel(this.view, 'Word cloud', 'updated');
});
});
describe "MetadataView.Option is an option input type with clear functionality", ->
beforeEach ->
model = new MetadataModel(selectEntry)
@view = new MetadataView.Option({model: model})
describe("MetadataView.Option is an option input type with clear functionality", function() {
beforeEach(function() {
const model = new MetadataModel(selectEntry);
return this.view = new MetadataView.Option({model});
});
it "uses a select input type", ->
assertInputType(@view, 'select-one')
it("uses a select input type", function() {
return assertInputType(this.view, 'select-one');
});
it "returns the intial value upon initialization", ->
assertValueInView(@view, 'always')
it("returns the intial value upon initialization", function() {
return assertValueInView(this.view, 'always');
});
it "can update its value in the view", ->
assertCanUpdateView(@view, "never")
it("can update its value in the view", function() {
return assertCanUpdateView(this.view, "never");
});
it "has a clear method to revert to the model default", ->
assertClear(@view, 'answered')
it("has a clear method to revert to the model default", function() {
return assertClear(this.view, 'answered');
});
it "has an update model method", ->
assertUpdateModel(@view, null, 'never')
it("has an update model method", function() {
return assertUpdateModel(this.view, null, 'never');
});
it "does not update to a value that is not an option", ->
@view.setValueInEditor("not an option")
expect(@view.getValueFromEditor()).toBe('always')
return it("does not update to a value that is not an option", function() {
this.view.setValueInEditor("not an option");
return expect(this.view.getValueFromEditor()).toBe('always');
});
});
describe "MetadataView.Number supports integer or float type and has clear functionality", ->
verifyValueAfterChanged = (view, value, expectedResult) ->
view.setValueInEditor(value)
view.changed()
expect(view.getValueFromEditor()).toBe(expectedResult)
describe("MetadataView.Number supports integer or float type and has clear functionality", function() {
const verifyValueAfterChanged = function(view, value, expectedResult) {
view.setValueInEditor(value);
view.changed();
return expect(view.getValueFromEditor()).toBe(expectedResult);
};
beforeEach ->
integerModel = new MetadataModel(integerEntry)
@integerView = new MetadataView.Number({model: integerModel})
beforeEach(function() {
const integerModel = new MetadataModel(integerEntry);
this.integerView = new MetadataView.Number({model: integerModel});
floatModel = new MetadataModel(floatEntry)
@floatView = new MetadataView.Number({model: floatModel})
const floatModel = new MetadataModel(floatEntry);
return this.floatView = new MetadataView.Number({model: floatModel});
});
it "uses a number input type", ->
assertInputType(@integerView, 'number')
assertInputType(@floatView, 'number')
it("uses a number input type", function() {
assertInputType(this.integerView, 'number');
return assertInputType(this.floatView, 'number');
});
it "returns the intial value upon initialization", ->
assertValueInView(@integerView, '5')
assertValueInView(@floatView, '10.2')
it("returns the intial value upon initialization", function() {
assertValueInView(this.integerView, '5');
return assertValueInView(this.floatView, '10.2');
});
it "can update its value in the view", ->
assertCanUpdateView(@integerView, "12")
assertCanUpdateView(@floatView, "-2.4")
it("can update its value in the view", function() {
assertCanUpdateView(this.integerView, "12");
return assertCanUpdateView(this.floatView, "-2.4");
});
it "has a clear method to revert to the model default", ->
assertClear(@integerView, 6, '6')
assertClear(@floatView, 2.7, '2.7')
it("has a clear method to revert to the model default", function() {
assertClear(this.integerView, 6, '6');
return assertClear(this.floatView, 2.7, '2.7');
});
it "has an update model method", ->
assertUpdateModel(@integerView, null, '90')
assertUpdateModel(@floatView, 10.2, '-9.5')
it("has an update model method", function() {
assertUpdateModel(this.integerView, null, '90');
return assertUpdateModel(this.floatView, 10.2, '-9.5');
});
it "knows the difference between integer and float", ->
expect(@integerView.isIntegerField()).toBeTruthy()
expect(@floatView.isIntegerField()).toBeFalsy()
it("knows the difference between integer and float", function() {
expect(this.integerView.isIntegerField()).toBeTruthy();
return expect(this.floatView.isIntegerField()).toBeFalsy();
});
it "sets attribtues related to min, max, and step", ->
verifyAttributes = (view, min, step, max=null) ->
inputEntry = view.$el.find('input')
expect(Number(inputEntry.attr('min'))).toEqual(min)
expect(Number(inputEntry.attr('step'))).toEqual(step)
if max is not null
expect(Number(inputEntry.attr('max'))).toEqual(max)
it("sets attribtues related to min, max, and step", function() {
const verifyAttributes = function(view, min, step, max=null) {
const inputEntry = view.$el.find('input');
expect(Number(inputEntry.attr('min'))).toEqual(min);
expect(Number(inputEntry.attr('step'))).toEqual(step);
if (max === !null) {
return expect(Number(inputEntry.attr('max'))).toEqual(max);
}
};
verifyAttributes(@integerView, 1, 1)
verifyAttributes(@floatView, 1.3, .1, 100.2)
verifyAttributes(this.integerView, 1, 1);
return verifyAttributes(this.floatView, 1.3, .1, 100.2);
});
it "corrects values that are out of range", ->
verifyValueAfterChanged(@integerView, '-4', '1')
verifyValueAfterChanged(@integerView, '1', '1')
verifyValueAfterChanged(@integerView, '0', '1')
verifyValueAfterChanged(@integerView, '3001', '3001')
it("corrects values that are out of range", function() {
verifyValueAfterChanged(this.integerView, '-4', '1');
verifyValueAfterChanged(this.integerView, '1', '1');
verifyValueAfterChanged(this.integerView, '0', '1');
verifyValueAfterChanged(this.integerView, '3001', '3001');
verifyValueAfterChanged(@floatView, '-4', '1.3')
verifyValueAfterChanged(@floatView, '1.3', '1.3')
verifyValueAfterChanged(@floatView, '1.2', '1.3')
verifyValueAfterChanged(@floatView, '100.2', '100.2')
verifyValueAfterChanged(@floatView, '100.3', '100.2')
verifyValueAfterChanged(this.floatView, '-4', '1.3');
verifyValueAfterChanged(this.floatView, '1.3', '1.3');
verifyValueAfterChanged(this.floatView, '1.2', '1.3');
verifyValueAfterChanged(this.floatView, '100.2', '100.2');
return verifyValueAfterChanged(this.floatView, '100.3', '100.2');
});
it "sets default values for integer and float fields that are empty", ->
verifyValueAfterChanged(@integerView, '', '6')
verifyValueAfterChanged(@floatView, '', '2.7')
it("sets default values for integer and float fields that are empty", function() {
verifyValueAfterChanged(this.integerView, '', '6');
return verifyValueAfterChanged(this.floatView, '', '2.7');
});
it "disallows invalid characters", ->
verifyValueAfterKeyPressed = (view, character, reject) ->
event = {
return it("disallows invalid characters", function() {
const verifyValueAfterKeyPressed = function(view, character, reject) {
const event = {
type : 'keypress',
which : character.charCodeAt(0),
keyCode: character.charCodeAt(0),
preventDefault : () -> 'no op'
preventDefault() { return 'no op'; }
};
spyOn(event, 'preventDefault');
view.$el.find('input').trigger(event);
if (reject) {
return expect(event.preventDefault).toHaveBeenCalled();
} else {
return expect(event.preventDefault).not.toHaveBeenCalled();
}
spyOn(event, 'preventDefault')
view.$el.find('input').trigger(event)
if (reject)
expect(event.preventDefault).toHaveBeenCalled()
else
expect(event.preventDefault).not.toHaveBeenCalled()
};
verifyDisallowedChars = (view) ->
verifyValueAfterKeyPressed(view, 'a', true)
verifyValueAfterKeyPressed(view, '.', view.isIntegerField())
verifyValueAfterKeyPressed(view, '[', true)
verifyValueAfterKeyPressed(view, '@', true)
const verifyDisallowedChars = function(view) {
verifyValueAfterKeyPressed(view, 'a', true);
verifyValueAfterKeyPressed(view, '.', view.isIntegerField());
verifyValueAfterKeyPressed(view, '[', true);
verifyValueAfterKeyPressed(view, '@', true);
for i in [0...9]
verifyValueAfterKeyPressed(view, String(i), false)
return [0, 1, 2, 3, 4, 5, 6, 7, 8].map((i) =>
verifyValueAfterKeyPressed(view, String(i), false));
};
verifyDisallowedChars(@integerView)
verifyDisallowedChars(@floatView)
verifyDisallowedChars(this.integerView);
return verifyDisallowedChars(this.floatView);
});
});
describe "MetadataView.List allows the user to enter an ordered list of strings", ->
beforeEach ->
listModel = new MetadataModel(listEntry)
@listView = new MetadataView.List({model: listModel})
@el = @listView.$el
main()
describe("MetadataView.List allows the user to enter an ordered list of strings", function() {
beforeEach(function() {
const listModel = new MetadataModel(listEntry);
this.listView = new MetadataView.List({model: listModel});
this.el = this.listView.$el;
return main();
});
it "returns the initial value upon initialization", ->
assertValueInView(@listView, ['the first display value', 'the second'])
it("returns the initial value upon initialization", function() {
return assertValueInView(this.listView, ['the first display value', 'the second']);
});
it "updates its value correctly", ->
assertCanUpdateView(@listView, ['a new item', 'another new item', 'a third'])
it("updates its value correctly", function() {
return assertCanUpdateView(this.listView, ['a new item', 'another new item', 'a third']);
});
it "has a clear method to revert to the model default", ->
@el.find('.create-setting').click()
assertClear(@listView, ['a thing', 'another thing'])
expect(@el.find('.create-setting')).not.toHaveClass('is-disabled')
it("has a clear method to revert to the model default", function() {
this.el.find('.create-setting').click();
assertClear(this.listView, ['a thing', 'another thing']);
return expect(this.el.find('.create-setting')).not.toHaveClass('is-disabled');
});
it "has an update model method", ->
assertUpdateModel(@listView, null, ['a new value'])
it("has an update model method", function() {
return assertUpdateModel(this.listView, null, ['a new value']);
});
it "can add an entry", ->
expect(@listView.model.get('value').length).toEqual(2)
@el.find('.create-setting').click()
expect(@el.find('input.input').length).toEqual(3)
it("can add an entry", function() {
expect(this.listView.model.get('value').length).toEqual(2);
this.el.find('.create-setting').click();
return expect(this.el.find('input.input').length).toEqual(3);
});
it "can remove an entry", ->
expect(@listView.model.get('value').length).toEqual(2)
@el.find('.remove-setting').first().click()
expect(@listView.model.get('value').length).toEqual(1)
it("can remove an entry", function() {
expect(this.listView.model.get('value').length).toEqual(2);
this.el.find('.remove-setting').first().click();
return expect(this.listView.model.get('value').length).toEqual(1);
});
it "only allows one blank entry at a time", ->
expect(@el.find('input').length).toEqual(2)
@el.find('.create-setting').click()
@el.find('.create-setting').click()
expect(@el.find('input').length).toEqual(3)
it("only allows one blank entry at a time", function() {
expect(this.el.find('input').length).toEqual(2);
this.el.find('.create-setting').click();
this.el.find('.create-setting').click();
return expect(this.el.find('input').length).toEqual(3);
});
it "re-enables the add setting button after entering a new value", ->
expect(@el.find('input').length).toEqual(2)
@el.find('.create-setting').click()
expect(@el.find('.create-setting')).toHaveClass('is-disabled')
@el.find('input').last().val('third setting')
@el.find('input').last().trigger('input')
expect(@el.find('.create-setting')).not.toHaveClass('is-disabled')
return it("re-enables the add setting button after entering a new value", function() {
expect(this.el.find('input').length).toEqual(2);
this.el.find('.create-setting').click();
expect(this.el.find('.create-setting')).toHaveClass('is-disabled');
this.el.find('input').last().val('third setting');
this.el.find('input').last().trigger('input');
return expect(this.el.find('.create-setting')).not.toHaveClass('is-disabled');
});
});
describe "MetadataView.RelativeTime allows the user to enter time string in HH:mm:ss format", ->
beforeEach ->
model = new MetadataModel(timeEntry)
@view = new MetadataView.RelativeTime({model: model})
describe("MetadataView.RelativeTime allows the user to enter time string in HH:mm:ss format", function() {
beforeEach(function() {
const model = new MetadataModel(timeEntry);
return this.view = new MetadataView.RelativeTime({model});
});
it "uses a text input type", ->
assertInputType(@view, 'text')
it("uses a text input type", function() {
return assertInputType(this.view, 'text');
});
it "returns the intial value upon initialization", ->
assertValueInView(@view, '12:12:12')
it("returns the intial value upon initialization", function() {
return assertValueInView(this.view, '12:12:12');
});
it "value is converted correctly", ->
view = @view
it("value is converted correctly", function() {
const { view } = this;
cases = [
const cases = [
{
input: '23:100:0'
input: '23:100:0',
output: '23:59:59'
},
{
input: '100000000000000000'
input: '100000000000000000',
output: '23:59:59'
},
{
input: '80000'
input: '80000',
output: '22:13:20'
},
{
input: '-100'
input: '-100',
output: '00:00:00'
},
{
input: '-100:-10'
input: '-100:-10',
output: '00:00:00'
},
{
input: '99:99'
input: '99:99',
output: '01:40:39'
},
{
input: '2'
input: '2',
output: '00:00:02'
},
{
input: '1:2'
input: '1:2',
output: '00:01:02'
},
{
input: '1:25'
input: '1:25',
output: '00:01:25'
},
{
input: '3:1:25'
input: '3:1:25',
output: '03:01:25'
},
{
input: ' 2 3 : 5 9 : 5 9 '
input: ' 2 3 : 5 9 : 5 9 ',
output: '23:59:59'
},
{
input: '9:1:25'
input: '9:1:25',
output: '09:01:25'
},
{
input: '77:72:77'
input: '77:72:77',
output: '23:59:59'
},
{
input: '22:100:100'
input: '22:100:100',
output: '23:41:40'
},
# negative value
// negative value
{
input: '-22:22:22'
input: '-22:22:22',
output: '00:22:22'
},
# simple string
// simple string
{
input: 'simple text'
input: 'simple text',
output: '00:00:00'
},
{
input: 'a10a:a10a:a10a'
input: 'a10a:a10a:a10a',
output: '00:00:00'
},
# empty string
// empty string
{
input: ''
input: '',
output: '00:00:00'
}
]
];
$.each cases, (index, data) ->
expect(view.parseRelativeTime(data.input)).toBe(data.output)
return $.each(cases, (index, data) => expect(view.parseRelativeTime(data.input)).toBe(data.output));
});
it "can update its value in the view", ->
assertCanUpdateView(@view, "23:59:59")
@view.setValueInEditor("33:59:59")
@view.updateModel()
assertValueInView(@view, "23:59:59")
it("can update its value in the view", function() {
assertCanUpdateView(this.view, "23:59:59");
this.view.setValueInEditor("33:59:59");
this.view.updateModel();
return assertValueInView(this.view, "23:59:59");
});
it "has a clear method to revert to the model default", ->
assertClear(@view, '00:00:00')
it("has a clear method to revert to the model default", function() {
return assertClear(this.view, '00:00:00');
});
it "has an update model method", ->
assertUpdateModel(@view, '12:12:12', '23:59:59')
return it("has an update model method", function() {
return assertUpdateModel(this.view, '12:12:12', '23:59:59');
});
});
describe "MetadataView.Dict allows the user to enter key-value pairs of strings", ->
beforeEach ->
dictModel = new MetadataModel($.extend(true, {}, dictEntry))
@dictView = new MetadataView.Dict({model: dictModel})
@el = @dictView.$el
main()
return describe("MetadataView.Dict allows the user to enter key-value pairs of strings", function() {
beforeEach(function() {
const dictModel = new MetadataModel($.extend(true, {}, dictEntry));
this.dictView = new MetadataView.Dict({model: dictModel});
this.el = this.dictView.$el;
return main();
});
it "returns the initial value upon initialization", ->
assertValueInView(@dictView, {
it("returns the initial value upon initialization", function() {
return assertValueInView(this.dictView, {
'en': 'English',
'ru': 'Русский',
'ua': 'Українська',
'fr': 'Français'
})
});
});
it "updates its value correctly", ->
assertCanUpdateView(@dictView, {
it("updates its value correctly", function() {
return assertCanUpdateView(this.dictView, {
'ru': 'Русский',
'ua': 'Українська',
'fr': 'Français'
})
});
});
it "has a clear method to revert to the model default", ->
@el.find('.create-setting').click()
assertClear(@dictView, {
it("has a clear method to revert to the model default", function() {
this.el.find('.create-setting').click();
assertClear(this.dictView, {
'en': 'English',
'ru': 'Русский'
})
expect(@el.find('.create-setting')).not.toHaveClass('is-disabled')
});
return expect(this.el.find('.create-setting')).not.toHaveClass('is-disabled');
});
it "has an update model method", ->
assertUpdateModel(@dictView, null, {'fr': 'Français'})
it("has an update model method", function() {
return assertUpdateModel(this.dictView, null, {'fr': 'Français'});
});
it "can add an entry", ->
expect(_.keys(@dictView.model.get('value')).length).toEqual(4)
@el.find('.create-setting').click()
expect(@el.find('input.input-key').length).toEqual(5)
it("can add an entry", function() {
expect(_.keys(this.dictView.model.get('value')).length).toEqual(4);
this.el.find('.create-setting').click();
return expect(this.el.find('input.input-key').length).toEqual(5);
});
it "can remove an entry", ->
expect(_.keys(@dictView.model.get('value')).length).toEqual(4)
@el.find('.remove-setting').first().click()
expect(_.keys(@dictView.model.get('value')).length).toEqual(3)
it("can remove an entry", function() {
expect(_.keys(this.dictView.model.get('value')).length).toEqual(4);
this.el.find('.remove-setting').first().click();
return expect(_.keys(this.dictView.model.get('value')).length).toEqual(3);
});
it "only allows one blank entry at a time", ->
expect(@el.find('input.input-key').length).toEqual(4)
@el.find('.create-setting').click()
@el.find('.create-setting').click()
expect(@el.find('input.input-key').length).toEqual(5)
it("only allows one blank entry at a time", function() {
expect(this.el.find('input.input-key').length).toEqual(4);
this.el.find('.create-setting').click();
this.el.find('.create-setting').click();
return expect(this.el.find('input.input-key').length).toEqual(5);
});
it "only allows unique keys", ->
data = [
it("only allows unique keys", function() {
const data = [
{
expectedValue: {'ru': 'Русский'},
initialValue: {'ru': 'Русский'},
testValue: {
'key': 'ru'
'key': 'ru',
'value': ''
}
},
@@ -574,7 +653,7 @@ define ["js/models/metadata", "js/collections/metadata", "js/views/metadata", "c
expectedValue: {'ru': 'Русский'},
initialValue: {'ru': 'Some value'},
testValue: {
'key': 'ru'
'key': 'ru',
'value': 'Русский'
}
},
@@ -582,27 +661,33 @@ define ["js/models/metadata", "js/collections/metadata", "js/views/metadata", "c
expectedValue: {'ru': 'Русский'},
initialValue: {'ru': 'Русский'},
testValue: {
'key': ''
'key': '',
'value': ''
}
}
]
];
_.each data, ((d, index) ->
@dictView.setValueInEditor(d.initialValue)
@dictView.updateModel();
@el.find('.create-setting').click()
item = @el.find('.list-settings-item').last()
return _.each(data, ((d, index) => {
this.dictView.setValueInEditor(d.initialValue);
this.dictView.updateModel();
this.el.find('.create-setting').click();
const item = this.el.find('.list-settings-item').last();
item.find('.input-key').val(d.testValue.key);
item.find('.input-value').val(d.testValue.value);
expect(@dictView.getValueFromEditor()).toEqual(d.expectedValue)
).bind(@)
return expect(this.dictView.getValueFromEditor()).toEqual(d.expectedValue);
})
);
});
it "re-enables the add setting button after entering a new value", ->
expect(@el.find('input.input-key').length).toEqual(4)
@el.find('.create-setting').click()
expect(@el.find('.create-setting')).toHaveClass('is-disabled')
@el.find('input.input-key').last().val('third setting')
@el.find('input.input-key').last().trigger('input')
expect(@el.find('.create-setting')).not.toHaveClass('is-disabled')
return it("re-enables the add setting button after entering a new value", function() {
expect(this.el.find('input.input-key').length).toEqual(4);
this.el.find('.create-setting').click();
expect(this.el.find('.create-setting')).toHaveClass('is-disabled');
this.el.find('input.input-key').last().val('third setting');
this.el.find('input.input-key').last().trigger('input');
return expect(this.el.find('.create-setting')).not.toHaveClass('is-disabled');
});
});
});
});

View File

@@ -1,27 +1,32 @@
define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js/models/course",
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js/models/course",
"js/collections/textbook", "js/views/show_textbook", "js/views/edit_textbook", "js/views/list_textbooks",
"js/views/edit_chapter", "common/js/components/views/feedback_prompt",
"common/js/components/views/feedback_notification", "common/js/components/utils/view_utils",
"edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers",
"js/spec_helpers/modal_helpers"],
(Textbook, Chapter, ChapterSet, Course, TextbookSet, ShowTextbook, EditTextbook, ListTextbooks, EditChapter,
Prompt, Notification, ViewUtils, AjaxHelpers, modal_helpers) ->
function(Textbook, Chapter, ChapterSet, Course, TextbookSet, ShowTextbook, EditTextbook, ListTextbooks, EditChapter,
Prompt, Notification, ViewUtils, AjaxHelpers, modal_helpers) {
describe "ShowTextbook", ->
tpl = readFixtures('show-textbook.underscore')
describe("ShowTextbook", function() {
const tpl = readFixtures('show-textbook.underscore');
beforeEach ->
setFixtures($("<script>", {id: "show-textbook-tpl", type: "text/template"}).text(tpl))
appendSetFixtures(sandbox({id: "page-notification"}))
appendSetFixtures(sandbox({id: "page-prompt"}))
@model = new Textbook({name: "Life Sciences", id: "0life-sciences"})
spyOn(@model, "destroy").and.callThrough()
@collection = new TextbookSet([@model])
@view = new ShowTextbook({model: @model})
beforeEach(function() {
setFixtures($("<script>", {id: "show-textbook-tpl", type: "text/template"}).text(tpl));
appendSetFixtures(sandbox({id: "page-notification"}));
appendSetFixtures(sandbox({id: "page-prompt"}));
this.model = new Textbook({name: "Life Sciences", id: "0life-sciences"});
spyOn(this.model, "destroy").and.callThrough();
this.collection = new TextbookSet([this.model]);
this.view = new ShowTextbook({model: this.model});
@promptSpies = jasmine.stealth.spyOnConstructor(Prompt, "Warning", ["show", "hide"])
@promptSpies.show.and.returnValue(@promptSpies)
window.course = new Course({
this.promptSpies = jasmine.stealth.spyOnConstructor(Prompt, "Warning", ["show", "hide"]);
this.promptSpies.show.and.returnValue(this.promptSpies);
return window.course = new Course({
id: "5",
name: "Course Name",
url_name: "course_name",
@@ -29,313 +34,347 @@ define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js
num: "course_num",
revision: "course_rev"
});
});
afterEach ->
delete window.course
afterEach(() => delete window.course);
describe "Basic", ->
it "should render properly", ->
@view.render()
expect(@view.$el).toContainText("Life Sciences")
describe("Basic", function() {
it("should render properly", function() {
this.view.render();
return expect(this.view.$el).toContainText("Life Sciences");
});
it "should set the 'editing' property on the model when the edit button is clicked", ->
@view.render().$(".edit").click()
expect(@model.get("editing")).toBeTruthy()
it("should set the 'editing' property on the model when the edit button is clicked", function() {
this.view.render().$(".edit").click();
return expect(this.model.get("editing")).toBeTruthy();
});
it "should pop a delete confirmation when the delete button is clicked", ->
@view.render().$(".delete").click()
expect(@promptSpies.constructor).toHaveBeenCalled()
ctorOptions = @promptSpies.constructor.calls.mostRecent().args[0]
expect(ctorOptions.title).toMatch(/Life Sciences/)
# hasn't actually been removed
expect(@model.destroy).not.toHaveBeenCalled()
expect(@collection).toContain(@model)
it("should pop a delete confirmation when the delete button is clicked", function() {
this.view.render().$(".delete").click();
expect(this.promptSpies.constructor).toHaveBeenCalled();
const ctorOptions = this.promptSpies.constructor.calls.mostRecent().args[0];
expect(ctorOptions.title).toMatch(/Life Sciences/);
// hasn't actually been removed
expect(this.model.destroy).not.toHaveBeenCalled();
return expect(this.collection).toContain(this.model);
});
it "should show chapters appropriately", ->
@model.get("chapters").add([{}, {}, {}])
@model.set('showChapters', false)
@view.render().$(".show-chapters").click()
expect(@model.get('showChapters')).toBeTruthy()
it("should show chapters appropriately", function() {
this.model.get("chapters").add([{}, {}, {}]);
this.model.set('showChapters', false);
this.view.render().$(".show-chapters").click();
return expect(this.model.get('showChapters')).toBeTruthy();
});
it "should hide chapters appropriately", ->
@model.get("chapters").add([{}, {}, {}])
@model.set('showChapters', true)
@view.render().$(".hide-chapters").click()
expect(@model.get('showChapters')).toBeFalsy()
return it("should hide chapters appropriately", function() {
this.model.get("chapters").add([{}, {}, {}]);
this.model.set('showChapters', true);
this.view.render().$(".hide-chapters").click();
return expect(this.model.get('showChapters')).toBeFalsy();
});
});
describe "AJAX", ->
beforeEach ->
@savingSpies = jasmine.stealth.spyOnConstructor(Notification, "Mini",
["show", "hide"])
@savingSpies.show.and.returnValue(@savingSpies)
CMS.URL.TEXTBOOKS = "/textbooks"
return describe("AJAX", function() {
beforeEach(function() {
this.savingSpies = jasmine.stealth.spyOnConstructor(Notification, "Mini",
["show", "hide"]);
this.savingSpies.show.and.returnValue(this.savingSpies);
return CMS.URL.TEXTBOOKS = "/textbooks";
});
afterEach ->
delete CMS.URL.TEXTBOOKS
afterEach(() => delete CMS.URL.TEXTBOOKS);
it "should destroy itself on confirmation", ->
requests = AjaxHelpers["requests"](this)
return it("should destroy itself on confirmation", function() {
const requests = AjaxHelpers["requests"](this);
@view.render().$(".delete").click()
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
expect(@model.destroy).toHaveBeenCalled()
expect(requests.length).toEqual(1)
expect(@savingSpies.constructor).toHaveBeenCalled()
expect(@savingSpies.show).toHaveBeenCalled()
expect(@savingSpies.hide).not.toHaveBeenCalled()
savingOptions = @savingSpies.constructor.calls.mostRecent().args[0]
expect(savingOptions.title).toMatch(/Deleting/)
# return a success response
requests[0].respond(204)
expect(@savingSpies.hide).toHaveBeenCalled()
expect(@collection.contains(@model)).toBeFalsy()
this.view.render().$(".delete").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.savingSpies.constructor).toHaveBeenCalled();
expect(this.savingSpies.show).toHaveBeenCalled();
expect(this.savingSpies.hide).not.toHaveBeenCalled();
const savingOptions = this.savingSpies.constructor.calls.mostRecent().args[0];
expect(savingOptions.title).toMatch(/Deleting/);
// return a success response
requests[0].respond(204);
expect(this.savingSpies.hide).toHaveBeenCalled();
return expect(this.collection.contains(this.model)).toBeFalsy();
});
});
});
describe "EditTextbook", ->
describe "Basic", ->
tpl = readFixtures('edit-textbook.underscore')
describe("EditTextbook", () =>
describe("Basic", function() {
const tpl = readFixtures('edit-textbook.underscore');
beforeEach ->
setFixtures($("<script>", {id: "edit-textbook-tpl", type: "text/template"}).text(tpl))
appendSetFixtures(sandbox({id: "page-notification"}))
appendSetFixtures(sandbox({id: "page-prompt"}))
@model = new Textbook({name: "Life Sciences", editing: true})
spyOn(@model, 'save')
@collection = new TextbookSet()
@collection.add(@model)
@view = new EditTextbook({model: @model})
spyOn(@view, 'render').and.callThrough()
beforeEach(function() {
setFixtures($("<script>", {id: "edit-textbook-tpl", type: "text/template"}).text(tpl));
appendSetFixtures(sandbox({id: "page-notification"}));
appendSetFixtures(sandbox({id: "page-prompt"}));
this.model = new Textbook({name: "Life Sciences", editing: true});
spyOn(this.model, 'save');
this.collection = new TextbookSet();
this.collection.add(this.model);
this.view = new EditTextbook({model: this.model});
return spyOn(this.view, 'render').and.callThrough();
});
it "should render properly", ->
@view.render()
expect(@view.$("input[name=textbook-name]").val()).toEqual("Life Sciences")
it("should render properly", function() {
this.view.render();
return expect(this.view.$("input[name=textbook-name]").val()).toEqual("Life Sciences");
});
it "should allow you to create new empty chapters", ->
@view.render()
numChapters = @model.get("chapters").length
@view.$(".action-add-chapter").click()
expect(@model.get("chapters").length).toEqual(numChapters+1)
expect(@model.get("chapters").last().isEmpty()).toBeTruthy()
it("should allow you to create new empty chapters", function() {
this.view.render();
const numChapters = this.model.get("chapters").length;
this.view.$(".action-add-chapter").click();
expect(this.model.get("chapters").length).toEqual(numChapters+1);
return expect(this.model.get("chapters").last().isEmpty()).toBeTruthy();
});
it "should save properly", ->
@view.render()
@view.$("input[name=textbook-name]").val("starfish")
@view.$("input[name=chapter1-name]").val("wallflower")
@view.$("input[name=chapter1-asset-path]").val("foobar")
@view.$("form").submit()
expect(@model.get("name")).toEqual("starfish")
chapter = @model.get("chapters").first()
expect(chapter.get("name")).toEqual("wallflower")
expect(chapter.get("asset_path")).toEqual("foobar")
expect(@model.save).toHaveBeenCalled()
it("should save properly", function() {
this.view.render();
this.view.$("input[name=textbook-name]").val("starfish");
this.view.$("input[name=chapter1-name]").val("wallflower");
this.view.$("input[name=chapter1-asset-path]").val("foobar");
this.view.$("form").submit();
expect(this.model.get("name")).toEqual("starfish");
const chapter = this.model.get("chapters").first();
expect(chapter.get("name")).toEqual("wallflower");
expect(chapter.get("asset_path")).toEqual("foobar");
return expect(this.model.save).toHaveBeenCalled();
});
it "should not save on invalid", ->
@view.render()
@view.$("input[name=textbook-name]").val("")
@view.$("input[name=chapter1-asset-path]").val("foobar.pdf")
@view.$("form").submit()
expect(@model.validationError).toBeTruthy()
expect(@model.save).not.toHaveBeenCalled()
it("should not save on invalid", function() {
this.view.render();
this.view.$("input[name=textbook-name]").val("");
this.view.$("input[name=chapter1-asset-path]").val("foobar.pdf");
this.view.$("form").submit();
expect(this.model.validationError).toBeTruthy();
return expect(this.model.save).not.toHaveBeenCalled();
});
it "does not save on cancel", ->
@model.get("chapters").add([{name: "a", asset_path: "b"}])
@view.render()
@view.$("input[name=textbook-name]").val("starfish")
@view.$("input[name=chapter1-asset-path]").val("foobar.pdf")
@view.$(".action-cancel").click()
expect(@model.get("name")).not.toEqual("starfish")
chapter = @model.get("chapters").first()
expect(chapter.get("asset_path")).not.toEqual("foobar")
expect(@model.save).not.toHaveBeenCalled()
it("does not save on cancel", function() {
this.model.get("chapters").add([{name: "a", asset_path: "b"}]);
this.view.render();
this.view.$("input[name=textbook-name]").val("starfish");
this.view.$("input[name=chapter1-asset-path]").val("foobar.pdf");
this.view.$(".action-cancel").click();
expect(this.model.get("name")).not.toEqual("starfish");
const chapter = this.model.get("chapters").first();
expect(chapter.get("asset_path")).not.toEqual("foobar");
return expect(this.model.save).not.toHaveBeenCalled();
});
it "should be possible to correct validation errors", ->
@view.render()
@view.$("input[name=textbook-name]").val("")
@view.$("input[name=chapter1-asset-path]").val("foobar.pdf")
@view.$("form").submit()
expect(@model.validationError).toBeTruthy()
expect(@model.save).not.toHaveBeenCalled()
@view.$("input[name=textbook-name]").val("starfish")
@view.$("input[name=chapter1-name]").val("foobar")
@view.$("form").submit()
expect(@model.validationError).toBeFalsy()
expect(@model.save).toHaveBeenCalled()
it("should be possible to correct validation errors", function() {
this.view.render();
this.view.$("input[name=textbook-name]").val("");
this.view.$("input[name=chapter1-asset-path]").val("foobar.pdf");
this.view.$("form").submit();
expect(this.model.validationError).toBeTruthy();
expect(this.model.save).not.toHaveBeenCalled();
this.view.$("input[name=textbook-name]").val("starfish");
this.view.$("input[name=chapter1-name]").val("foobar");
this.view.$("form").submit();
expect(this.model.validationError).toBeFalsy();
return expect(this.model.save).toHaveBeenCalled();
});
it "removes all empty chapters on cancel if the model has a non-empty chapter", ->
chapters = @model.get("chapters")
chapters.at(0).set("name", "non-empty")
@model.setOriginalAttributes()
@view.render()
chapters.add([{}, {}, {}]) # add three empty chapters
expect(chapters.length).toEqual(4)
@view.$(".action-cancel").click()
expect(chapters.length).toEqual(1)
expect(chapters.first().get('name')).toEqual("non-empty")
it("removes all empty chapters on cancel if the model has a non-empty chapter", function() {
const chapters = this.model.get("chapters");
chapters.at(0).set("name", "non-empty");
this.model.setOriginalAttributes();
this.view.render();
chapters.add([{}, {}, {}]); // add three empty chapters
expect(chapters.length).toEqual(4);
this.view.$(".action-cancel").click();
expect(chapters.length).toEqual(1);
return expect(chapters.first().get('name')).toEqual("non-empty");
});
it "removes all empty chapters on cancel except one if the model has no non-empty chapters", ->
chapters = @model.get("chapters")
@view.render()
chapters.add([{}, {}, {}]) # add three empty chapters
expect(chapters.length).toEqual(4)
@view.$(".action-cancel").click()
expect(chapters.length).toEqual(1)
return it("removes all empty chapters on cancel except one if the model has no non-empty chapters", function() {
const chapters = this.model.get("chapters");
this.view.render();
chapters.add([{}, {}, {}]); // add three empty chapters
expect(chapters.length).toEqual(4);
this.view.$(".action-cancel").click();
return expect(chapters.length).toEqual(1);
});
})
);
describe "ListTextbooks", ->
noTextbooksTpl = readFixtures("no-textbooks.underscore")
editTextbooktpl = readFixtures('edit-textbook.underscore')
describe("ListTextbooks", function() {
const noTextbooksTpl = readFixtures("no-textbooks.underscore");
const editTextbooktpl = readFixtures('edit-textbook.underscore');
beforeEach ->
appendSetFixtures($("<script>", {id: "no-textbooks-tpl", type: "text/template"}).text(noTextbooksTpl))
appendSetFixtures($("<script>", {id: "edit-textbook-tpl", type: "text/template"}).text(editTextbooktpl))
@collection = new TextbookSet
@view = new ListTextbooks({collection: @collection})
@view.render()
beforeEach(function() {
appendSetFixtures($("<script>", {id: "no-textbooks-tpl", type: "text/template"}).text(noTextbooksTpl));
appendSetFixtures($("<script>", {id: "edit-textbook-tpl", type: "text/template"}).text(editTextbooktpl));
this.collection = new TextbookSet;
this.view = new ListTextbooks({collection: this.collection});
return this.view.render();
});
it "should scroll to newly added textbook", ->
spyOn(ViewUtils, 'setScrollOffset')
@view.$(".new-button").click()
$sectionEl = @view.$el.find('section:last')
expect($sectionEl.length).toEqual(1)
expect(ViewUtils.setScrollOffset).toHaveBeenCalledWith($sectionEl, 0)
it("should scroll to newly added textbook", function() {
spyOn(ViewUtils, 'setScrollOffset');
this.view.$(".new-button").click();
const $sectionEl = this.view.$el.find('section:last');
expect($sectionEl.length).toEqual(1);
return expect(ViewUtils.setScrollOffset).toHaveBeenCalledWith($sectionEl, 0);
});
it "should focus first input element of newly added textbook", ->
spyOn(jQuery.fn, 'focus').and.callThrough()
jasmine.addMatchers
toHaveBeenCalledOnJQueryObject: () ->
return it("should focus first input element of newly added textbook", function() {
spyOn(jQuery.fn, 'focus').and.callThrough();
jasmine.addMatchers({
toHaveBeenCalledOnJQueryObject() {
return {
compare: (actual, expected) ->
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)
# testing for element focused seems to be tricky
# (see http://stackoverflow.com/questions/967096)
# and the following doesn't seem to work
# expect($inputEl).toBeFocused()
# expect($inputEl.find(':focus').length).toEqual(1)
expect(jQuery.fn.focus).toHaveBeenCalledOnJQueryObject($inputEl)
(actual.calls.mostRecent().object[0] === expected[0])
};
}
};
}});
this.view.$(".new-button").click();
const $inputEl = this.view.$el.find('section:last input:first');
expect($inputEl.length).toEqual(1);
// testing for element focused seems to be tricky
// (see http://stackoverflow.com/questions/967096)
// and the following doesn't seem to work
// expect($inputEl).toBeFocused()
// expect($inputEl.find(':focus').length).toEqual(1)
return expect(jQuery.fn.focus).toHaveBeenCalledOnJQueryObject($inputEl);
});
});
# describe "ListTextbooks", ->
# noTextbooksTpl = readFixtures("no-textbooks.underscore")
#
# beforeEach ->
# setFixtures($("<script>", {id: "no-textbooks-tpl", type: "text/template"}).text(noTextbooksTpl))
# @showSpies = spyOnConstructor("ShowTextbook", ["render"])
# @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.and.returnValue(@editSpies)
# @editSpies.$el = editEl
# @editSpies.el= editEl.get(0)
#
# @collection = new TextbookSet
# @view = new ListTextbooks({collection: @collection})
# @view.render()
#
# it "should render the empty template if there are no textbooks", ->
# expect(@view.$el).toContainText("You haven't added any textbooks to this course yet")
# expect(@view.$el).toContain(".new-button")
# expect(@showSpies.constructor).not.toHaveBeenCalled()
# expect(@editSpies.constructor).not.toHaveBeenCalled()
#
# it "should render ShowTextbook views by default if no textbook is being edited", ->
# # add three empty textbooks to the collection
# @collection.add([{}, {}, {}])
# # reset spies due to re-rendering on collection modification
# @showSpies.constructor.reset()
# @editSpies.constructor.reset()
# # render once and test
# @view.render()
#
# expect(@view.$el).not.toContainText(
# "You haven't added any textbooks to this course yet")
# expect(@showSpies.constructor).toHaveBeenCalled()
# expect(@showSpies.constructor.calls.length).toEqual(3);
# expect(@editSpies.constructor).not.toHaveBeenCalled()
#
# it "should render an EditTextbook view for a textbook being edited", ->
# # add three empty textbooks to the collection: the first and third
# # should be shown, and the second should be edited
# @collection.add([{editing: false}, {editing: true}, {editing: false}])
# editing = @collection.at(1)
# expect(editing.get("editing")).toBeTruthy()
# # reset spies
# @showSpies.constructor.reset()
# @editSpies.constructor.reset()
# # render once and test
# @view.render()
#
# expect(@showSpies.constructor).toHaveBeenCalled()
# expect(@showSpies.constructor.calls.length).toEqual(2)
# expect(@showSpies.constructor).not.toHaveBeenCalledWith({model: editing})
# expect(@editSpies.constructor).toHaveBeenCalled()
# expect(@editSpies.constructor.calls.length).toEqual(1)
# expect(@editSpies.constructor).toHaveBeenCalledWith({model: editing})
#
# it "should add a new textbook when the new-button is clicked", ->
# # reset spies
# @showSpies.constructor.reset()
# @editSpies.constructor.reset()
# # test
# @view.$(".new-button").click()
#
# expect(@collection.length).toEqual(1)
# expect(@view.$el).toContain(@editSpies.$el)
# expect(@view.$el).not.toContain(@showSpies.$el)
// describe "ListTextbooks", ->
// noTextbooksTpl = readFixtures("no-textbooks.underscore")
//
// beforeEach ->
// setFixtures($("<script>", {id: "no-textbooks-tpl", type: "text/template"}).text(noTextbooksTpl))
// @showSpies = spyOnConstructor("ShowTextbook", ["render"])
// @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.and.returnValue(@editSpies)
// @editSpies.$el = editEl
// @editSpies.el= editEl.get(0)
//
// @collection = new TextbookSet
// @view = new ListTextbooks({collection: @collection})
// @view.render()
//
// it "should render the empty template if there are no textbooks", ->
// expect(@view.$el).toContainText("You haven't added any textbooks to this course yet")
// expect(@view.$el).toContain(".new-button")
// expect(@showSpies.constructor).not.toHaveBeenCalled()
// expect(@editSpies.constructor).not.toHaveBeenCalled()
//
// it "should render ShowTextbook views by default if no textbook is being edited", ->
// # add three empty textbooks to the collection
// @collection.add([{}, {}, {}])
// # reset spies due to re-rendering on collection modification
// @showSpies.constructor.reset()
// @editSpies.constructor.reset()
// # render once and test
// @view.render()
//
// expect(@view.$el).not.toContainText(
// "You haven't added any textbooks to this course yet")
// expect(@showSpies.constructor).toHaveBeenCalled()
// expect(@showSpies.constructor.calls.length).toEqual(3);
// expect(@editSpies.constructor).not.toHaveBeenCalled()
//
// it "should render an EditTextbook view for a textbook being edited", ->
// # add three empty textbooks to the collection: the first and third
// # should be shown, and the second should be edited
// @collection.add([{editing: false}, {editing: true}, {editing: false}])
// editing = @collection.at(1)
// expect(editing.get("editing")).toBeTruthy()
// # reset spies
// @showSpies.constructor.reset()
// @editSpies.constructor.reset()
// # render once and test
// @view.render()
//
// expect(@showSpies.constructor).toHaveBeenCalled()
// expect(@showSpies.constructor.calls.length).toEqual(2)
// expect(@showSpies.constructor).not.toHaveBeenCalledWith({model: editing})
// expect(@editSpies.constructor).toHaveBeenCalled()
// expect(@editSpies.constructor.calls.length).toEqual(1)
// expect(@editSpies.constructor).toHaveBeenCalledWith({model: editing})
//
// it "should add a new textbook when the new-button is clicked", ->
// # reset spies
// @showSpies.constructor.reset()
// @editSpies.constructor.reset()
// # test
// @view.$(".new-button").click()
//
// expect(@collection.length).toEqual(1)
// expect(@view.$el).toContain(@editSpies.$el)
// expect(@view.$el).not.toContain(@showSpies.$el)
describe "EditChapter", ->
beforeEach ->
modal_helpers.installModalTemplates()
@model = new Chapter
name: "Chapter 1"
return describe("EditChapter", function() {
beforeEach(function() {
modal_helpers.installModalTemplates();
this.model = new Chapter({
name: "Chapter 1",
asset_path: "/ch1.pdf"
@collection = new ChapterSet()
@collection.add(@model)
@view = new EditChapter({model: @model})
spyOn(@view, "remove").and.callThrough()
CMS.URL.UPLOAD_ASSET = "/upload"
window.course = new Course({name: "abcde"})
});
this.collection = new ChapterSet();
this.collection.add(this.model);
this.view = new EditChapter({model: this.model});
spyOn(this.view, "remove").and.callThrough();
CMS.URL.UPLOAD_ASSET = "/upload";
return window.course = new Course({name: "abcde"});
});
afterEach ->
delete CMS.URL.UPLOAD_ASSET
delete window.course
afterEach(function() {
delete CMS.URL.UPLOAD_ASSET;
return delete window.course;
});
it "can render", ->
@view.render()
expect(@view.$("input.chapter-name").val()).toEqual("Chapter 1")
expect(@view.$("input.chapter-asset-path").val()).toEqual("/ch1.pdf")
it("can render", function() {
this.view.render();
expect(this.view.$("input.chapter-name").val()).toEqual("Chapter 1");
return expect(this.view.$("input.chapter-asset-path").val()).toEqual("/ch1.pdf");
});
it "can delete itself", ->
@view.render().$(".action-close").click()
expect(@collection.length).toEqual(0)
expect(@view.remove).toHaveBeenCalled()
it("can delete itself", function() {
this.view.render().$(".action-close").click();
expect(this.collection.length).toEqual(0);
return expect(this.view.remove).toHaveBeenCalled();
});
# it "can open an upload dialog", ->
# uploadSpies = spyOnConstructor("UploadDialog", ["show", "el"])
# uploadSpies.show.and.returnValue(uploadSpies)
#
# @view.render().$(".action-upload").click()
# ctorOptions = uploadSpies.constructor.calls.mostRecent().args[0]
# expect(ctorOptions.model.get('title')).toMatch(/abcde/)
# expect(typeof ctorOptions.onSuccess).toBe('function')
# expect(uploadSpies.show).toHaveBeenCalled()
// it "can open an upload dialog", ->
// uploadSpies = spyOnConstructor("UploadDialog", ["show", "el"])
// uploadSpies.show.and.returnValue(uploadSpies)
//
// @view.render().$(".action-upload").click()
// ctorOptions = uploadSpies.constructor.calls.mostRecent().args[0]
// expect(ctorOptions.model.get('title')).toMatch(/abcde/)
// expect(typeof ctorOptions.onSuccess).toBe('function')
// expect(uploadSpies.show).toHaveBeenCalled()
# Disabling because this test does not close the modal dialog. This can cause
# tests that run after it to fail (see STUD-1963).
xit "saves content when opening upload dialog", ->
@view.render()
@view.$("input.chapter-name").val("rainbows")
@view.$("input.chapter-asset-path").val("unicorns")
@view.$(".action-upload").click()
expect(@model.get("name")).toEqual("rainbows")
expect(@model.get("asset_path")).toEqual("unicorns")
// Disabling because this test does not close the modal dialog. This can cause
// tests that run after it to fail (see STUD-1963).
return xit("saves content when opening upload dialog", function() {
this.view.render();
this.view.$("input.chapter-name").val("rainbows");
this.view.$("input.chapter-asset-path").val("unicorns");
this.view.$(".action-upload").click();
expect(this.model.get("name")).toEqual("rainbows");
return expect(this.model.get("asset_path")).toEqual("unicorns");
});
});
});

View File

@@ -1,135 +1,161 @@
define ["sinon", "js/models/uploads", "js/views/uploads", "js/models/chapter",
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(["sinon", "js/models/uploads", "js/views/uploads", "js/models/chapter",
"edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "js/spec_helpers/modal_helpers"],
(sinon, FileUpload, UploadDialog, Chapter, AjaxHelpers, modal_helpers) ->
(sinon, FileUpload, UploadDialog, Chapter, AjaxHelpers, modal_helpers) =>
describe "UploadDialog", ->
tpl = readFixtures("upload-dialog.underscore")
describe("UploadDialog", function() {
const tpl = readFixtures("upload-dialog.underscore");
beforeEach ->
modal_helpers.installModalTemplates()
appendSetFixtures($("<script>", {id: "upload-dialog-tpl", type: "text/template"}).text(tpl))
CMS.URL.UPLOAD_ASSET = "/upload"
@model = new FileUpload(
beforeEach(function() {
let dialogResponse;
modal_helpers.installModalTemplates();
appendSetFixtures($("<script>", {id: "upload-dialog-tpl", type: "text/template"}).text(tpl));
CMS.URL.UPLOAD_ASSET = "/upload";
this.model = new FileUpload({
mimeTypes: ['application/pdf']
)
@dialogResponse = dialogResponse = []
@mockFiles = []
});
this.dialogResponse = (dialogResponse = []);
return this.mockFiles = [];});
afterEach ->
delete CMS.URL.UPLOAD_ASSET
modal_helpers.cancelModalIfShowing()
afterEach(function() {
delete CMS.URL.UPLOAD_ASSET;
return modal_helpers.cancelModalIfShowing();
});
createTestView = (test) ->
view = new UploadDialog(
const createTestView = function(test) {
const view = new UploadDialog({
model: test.model,
url: CMS.URL.UPLOAD_ASSET,
onSuccess: (response) =>
test.dialogResponse.push(response.response)
)
spyOn(view, 'remove').and.callThrough()
onSuccess: response => {
return test.dialogResponse.push(response.response);
}
});
spyOn(view, 'remove').and.callThrough();
# create mock file input, so that we aren't subject to browser restrictions
mockFileInput = jasmine.createSpy('mockFileInput')
mockFileInput.files = test.mockFiles
jqMockFileInput = jasmine.createSpyObj('jqMockFileInput', ['get', 'replaceWith'])
jqMockFileInput.get.and.returnValue(mockFileInput)
originalView$ = view.$
spyOn(view, "$").and.callFake (selector) ->
if selector == "input[type=file]"
jqMockFileInput
else
originalView$.apply(this, arguments)
@lastView = view
// create mock file input, so that we aren't subject to browser restrictions
const mockFileInput = jasmine.createSpy('mockFileInput');
mockFileInput.files = test.mockFiles;
const jqMockFileInput = jasmine.createSpyObj('jqMockFileInput', ['get', 'replaceWith']);
jqMockFileInput.get.and.returnValue(mockFileInput);
const originalView$ = view.$;
spyOn(view, "$").and.callFake(function(selector) {
if (selector === "input[type=file]") {
return jqMockFileInput;
} else {
return originalView$.apply(this, arguments);
}
});
return this.lastView = view;
};
describe "Basic", ->
it "should render without a file selected", ->
view = createTestView(this)
view.render()
expect(view.$el).toContainElement("input[type=file]")
expect(view.$(".action-upload")).toHaveClass("disabled")
describe("Basic", function() {
it("should render without a file selected", function() {
const view = createTestView(this);
view.render();
expect(view.$el).toContainElement("input[type=file]");
return expect(view.$(".action-upload")).toHaveClass("disabled");
});
it "should render with a PDF selected", ->
view = createTestView(this)
file = {name: "fake.pdf", "type": "application/pdf"}
@mockFiles.push(file)
@model.set("selectedFile", file)
view.render()
expect(view.$el).toContainElement("input[type=file]")
expect(view.$el).not.toContainElement("#upload_error")
expect(view.$(".action-upload")).not.toHaveClass("disabled")
it("should render with a PDF selected", function() {
const view = createTestView(this);
const file = {name: "fake.pdf", "type": "application/pdf"};
this.mockFiles.push(file);
this.model.set("selectedFile", file);
view.render();
expect(view.$el).toContainElement("input[type=file]");
expect(view.$el).not.toContainElement("#upload_error");
return expect(view.$(".action-upload")).not.toHaveClass("disabled");
});
it "should render an error with an invalid file type selected", ->
view = createTestView(this)
file = {name: "fake.png", "type": "image/png"}
@mockFiles.push(file)
@model.set("selectedFile", file)
view.render()
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 selected", function() {
const view = createTestView(this);
const file = {name: "fake.png", "type": "image/png"};
this.mockFiles.push(file);
this.model.set("selectedFile", file);
view.render();
expect(view.$el).toContainElement("input[type=file]");
expect(view.$el).toContainElement("#upload_error");
return expect(view.$(".action-upload")).toHaveClass("disabled");
});
it "should render an error with an invalid file type after a correct file type selected", ->
view = createTestView(this)
correctFile = {name: "fake.pdf", "type": "application/pdf"}
inCorrectFile = {name: "fake.png", "type": "image/png"}
event = {}
view.render()
return it("should render an error with an invalid file type after a correct file type selected", function() {
const view = createTestView(this);
const correctFile = {name: "fake.pdf", "type": "application/pdf"};
const inCorrectFile = {name: "fake.png", "type": "image/png"};
const event = {};
view.render();
event.target = {"files": [correctFile]}
view.selectFile(event)
expect(view.$el).toContainElement("input[type=file]")
expect(view.$el).not.toContainElement("#upload_error")
expect(view.$(".action-upload")).not.toHaveClass("disabled")
event.target = {"files": [correctFile]};
view.selectFile(event);
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").and.callFake (data) ->
if data.selectedFile != undefined
this.attributes.selectedFile = data.selectedFile
this.changed = {}
else
realMethod.apply(this, arguments)
const realMethod = this.model.set;
spyOn(this.model, "set").and.callFake(function(data) {
if (data.selectedFile !== undefined) {
this.attributes.selectedFile = data.selectedFile;
return this.changed = {};
} else {
return realMethod.apply(this, arguments);
}
});
event.target = {"files": [inCorrectFile]}
view.selectFile(event)
expect(view.$el).toContainElement("input[type=file]")
expect(view.$el).toContainElement("#upload_error")
expect(view.$(".action-upload")).toHaveClass("disabled")
event.target = {"files": [inCorrectFile]};
view.selectFile(event);
expect(view.$el).toContainElement("input[type=file]");
expect(view.$el).toContainElement("#upload_error");
return expect(view.$(".action-upload")).toHaveClass("disabled");
});
});
describe "Uploads", ->
beforeEach ->
@clock = sinon.useFakeTimers()
return describe("Uploads", function() {
beforeEach(function() {
return this.clock = sinon.useFakeTimers();
});
afterEach ->
modal_helpers.cancelModalIfShowing()
@clock.restore()
afterEach(function() {
modal_helpers.cancelModalIfShowing();
return this.clock.restore();
});
it "can upload correctly", ->
requests = AjaxHelpers.requests(this);
view = createTestView(this)
view.render()
view.upload()
expect(@model.get("uploading")).toBeTruthy()
AjaxHelpers.expectRequest(requests, "POST", "/upload")
AjaxHelpers.respondWithJson(requests, { response: "dummy_response"})
expect(@model.get("uploading")).toBeFalsy()
expect(@model.get("finished")).toBeTruthy()
expect(@dialogResponse.pop()).toEqual("dummy_response")
it("can upload correctly", function() {
const requests = AjaxHelpers.requests(this);
const view = createTestView(this);
view.render();
view.upload();
expect(this.model.get("uploading")).toBeTruthy();
AjaxHelpers.expectRequest(requests, "POST", "/upload");
AjaxHelpers.respondWithJson(requests, { response: "dummy_response"});
expect(this.model.get("uploading")).toBeFalsy();
expect(this.model.get("finished")).toBeTruthy();
return expect(this.dialogResponse.pop()).toEqual("dummy_response");
});
it "can handle upload errors", ->
requests = AjaxHelpers.requests(this);
view = createTestView(this)
view.render()
view.upload()
AjaxHelpers.respondWithError(requests)
expect(@model.get("title")).toMatch(/error/)
expect(view.remove).not.toHaveBeenCalled()
it("can handle upload errors", function() {
const requests = AjaxHelpers.requests(this);
const view = createTestView(this);
view.render();
view.upload();
AjaxHelpers.respondWithError(requests);
expect(this.model.get("title")).toMatch(/error/);
return expect(view.remove).not.toHaveBeenCalled();
});
it "removes itself after two seconds on successful upload", ->
requests = AjaxHelpers.requests(this);
view = createTestView(this)
view.render()
view.upload()
AjaxHelpers.respondWithJson(requests, { response: "dummy_response"})
return it("removes itself after two seconds on successful upload", function() {
const requests = AjaxHelpers.requests(this);
const view = createTestView(this);
view.render();
view.upload();
AjaxHelpers.respondWithJson(requests, { response: "dummy_response"});
expect(modal_helpers.isShowingModal(view)).toBeTruthy();
@clock.tick(2001)
expect(modal_helpers.isShowingModal(view)).toBeFalsy();
this.clock.tick(2001);
return expect(modal_helpers.isShowingModal(view)).toBeFalsy();
});
});
})
);