Files
edx-platform/cms/static/coffee/spec/views/textbook_spec.coffee
2013-10-04 13:43:50 -04:00

319 lines
15 KiB
CoffeeScript

define ["js/models/textbook", "js/models/chapter", "js/collections/chapter", "js/models/section",
"js/collections/textbook", "js/views/show_textbook", "js/views/edit_textbook", "js/views/list_textbooks",
"js/views/edit_chapter", "js/views/feedback_prompt", "js/views/feedback_notification",
"sinon", "jasmine-stealth"],
(Textbook, Chapter, ChapterSet, Section, TextbookSet, ShowTextbook, EditTextbook, ListTexbook, EditChapter, Prompt, Notification, sinon) ->
feedbackTpl = readFixtures('system-feedback.underscore')
beforeEach ->
# remove this when we upgrade jasmine-jquery
@addMatchers
toContainText: (text) ->
trimmedText = $.trim(@actual.text())
if text and $.isFunction(text.test)
return text.test(trimmedText)
else
return trimmedText.indexOf(text) != -1;
describe "ShowTextbook", ->
tpl = readFixtures('show-textbook.underscore')
beforeEach ->
setFixtures($("<script>", {id: "show-textbook-tpl", type: "text/template"}).text(tpl))
appendSetFixtures($("<script>", {id: "system-feedback-tpl", type: "text/template"}).text(feedbackTpl))
appendSetFixtures(sandbox({id: "page-notification"}))
appendSetFixtures(sandbox({id: "page-prompt"}))
@model = new Textbook({name: "Life Sciences", id: "0life-sciences"})
spyOn(@model, "destroy").andCallThrough()
@collection = new TextbookSet([@model])
@view = new ShowTextbook({model: @model})
@promptSpies = spyOnConstructor(Prompt, "Warning", ["show", "hide"])
@promptSpies.show.andReturn(@promptSpies)
window.section = new Section({
id: "5",
name: "Course Name",
url_name: "course_name",
org: "course_org",
num: "course_num",
revision: "course_rev"
});
afterEach ->
delete window.section
describe "Basic", ->
it "should render properly", ->
@view.render()
expect(@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 pop a delete confirmation when the delete button is clicked", ->
@view.render().$(".delete").click()
expect(@promptSpies.constructor).toHaveBeenCalled()
ctorOptions = @promptSpies.constructor.mostRecentCall.args[0]
expect(ctorOptions.title).toMatch(/Life Sciences/)
# hasn't actually been removed
expect(@model.destroy).not.toHaveBeenCalled()
expect(@collection).toContain(@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 hide chapters appropriately", ->
@model.get("chapters").add([{}, {}, {}])
@model.set('showChapters', true)
@view.render().$(".hide-chapters").click()
expect(@model.get('showChapters')).toBeFalsy()
describe "AJAX", ->
beforeEach ->
@requests = requests = []
@xhr = sinon.useFakeXMLHttpRequest()
@xhr.onCreate = (xhr) -> requests.push(xhr)
@savingSpies = spyOnConstructor(Notification, "Mini",
["show", "hide"])
@savingSpies.show.andReturn(@savingSpies)
afterEach ->
@xhr.restore()
it "should destroy itself on confirmation", ->
@view.render().$(".delete").click()
ctorOptions = @promptSpies.constructor.mostRecentCall.args[0]
# run the primary function to indicate confirmation
ctorOptions.actions.primary.click(@promptSpies)
# AJAX request has been sent, but not yet returned
expect(@model.destroy).toHaveBeenCalled()
expect(@requests.length).toEqual(1)
expect(@savingSpies.constructor).toHaveBeenCalled()
expect(@savingSpies.show).toHaveBeenCalled()
expect(@savingSpies.hide).not.toHaveBeenCalled()
savingOptions = @savingSpies.constructor.mostRecentCall.args[0]
expect(savingOptions.title).toMatch(/Deleting/)
# return a success response
@requests[0].respond(200)
expect(@savingSpies.hide).toHaveBeenCalled()
expect(@collection.contains(@model)).toBeFalsy()
describe "EditTextbook", ->
describe "Basic", ->
tpl = readFixtures('edit-textbook.underscore')
chapterTpl = readFixtures('edit-chapter.underscore')
beforeEach ->
setFixtures($("<script>", {id: "edit-textbook-tpl", type: "text/template"}).text(tpl))
appendSetFixtures($("<script>", {id: "edit-chapter-tpl", type: "text/template"}).text(chapterTpl))
appendSetFixtures($("<script>", {id: "system-feedback-tpl", type: "text/template"}).text(feedbackTpl))
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').andCallThrough()
it "should render properly", ->
@view.render()
expect(@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 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 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 "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 "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 "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 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)
# describe "ListTextbooks", ->
# noTextbooksTpl = readFixtures("no-textbooks.underscore")
#
# beforeEach ->
# setFixtures($("<script>", {id: "no-textbooks-tpl", type: "text/template"}).text(noTextbooksTpl))
# appendSetFixtures($("<script>", {id: "system-feedback-tpl", type: "text/template"}).text(feedbackTpl))
# @showSpies = spyOnConstructor("ShowTextbook", ["render"])
# @showSpies.render.andReturn(@showSpies) # equivalent of `return this`
# showEl = $("<li>")
# @showSpies.$el = showEl
# @showSpies.el = showEl.get(0)
# @editSpies = spyOnConstructor("EditTextbook", ["render"])
# editEl = $("<li>")
# @editSpies.render.andReturn(@editSpies)
# @editSpies.$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", ->
tpl = readFixtures("edit-chapter.underscore")
beforeEach ->
setFixtures($("<script>", {id: "edit-chapter-tpl", type: "text/template"}).text(tpl))
appendSetFixtures($("<script>", {id: "system-feedback-tpl", type: "text/template"}).text(feedbackTpl))
@model = new Chapter
name: "Chapter 1"
asset_path: "/ch1.pdf"
@collection = new ChapterSet()
@collection.add(@model)
@view = new EditChapter({model: @model})
spyOn(@view, "remove").andCallThrough()
CMS.URL.UPLOAD_ASSET = "/upload"
window.section = new Section({name: "abcde"})
afterEach ->
delete CMS.URL.UPLOAD_ASSET
delete window.section
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 delete itself", ->
@view.render().$(".action-close").click()
expect(@collection.length).toEqual(0)
expect(@view.remove).toHaveBeenCalled()
# it "can open an upload dialog", ->
# uploadSpies = spyOnConstructor("UploadDialog", ["show", "el"])
# uploadSpies.show.andReturn(uploadSpies)
#
# @view.render().$(".action-upload").click()
# ctorOptions = uploadSpies.constructor.mostRecentCall.args[0]
# expect(ctorOptions.model.get('title')).toMatch(/abcde/)
# expect(typeof ctorOptions.onSuccess).toBe('function')
# expect(uploadSpies.show).toHaveBeenCalled()
it "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")