PY-2016.1.4 <alisantang@C02RP0YSG8WM.tld Merge branch 'master'

This commit is contained in:
alisan617
2016-06-21 19:08:45 -04:00
135 changed files with 1289 additions and 1238 deletions

View File

@@ -1,4 +1,4 @@
require ["jquery", "backbone", "coffee/src/main", "common/js/spec_helpers/ajax_helpers", "jquery.cookie"],
require ["jquery", "backbone", "coffee/src/main", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "jquery.cookie"],
($, Backbone, main, AjaxHelpers) ->
describe "CMS", ->
it "should initialize URL", ->

View File

@@ -1,4 +1,4 @@
define ["js/models/section", "common/js/spec_helpers/ajax_helpers", "js/utils/module"], (Section, AjaxHelpers, ModuleUtils) ->
define ["js/models/section", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "js/utils/module"], (Section, AjaxHelpers, ModuleUtils) ->
describe "Section", ->
describe "basic", ->
beforeEach ->

View File

@@ -1,4 +1,4 @@
define ["jquery", "common/js/spec_helpers/ajax_helpers", "squire"],
define ["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "squire"],
($, AjaxHelpers, Squire) ->
assetLibraryTpl = readFixtures('asset-library.underscore')

View File

@@ -1,4 +1,5 @@
define ["js/views/course_info_handout", "js/views/course_info_update", "js/models/module_info", "js/collections/course_update", "common/js/spec_helpers/ajax_helpers"],
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) ->
describe "Course Updates and Handouts", ->

View File

@@ -1,339 +1,341 @@
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","common/js/spec_helpers/ajax_helpers",
"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) ->
(Textbook, Chapter, ChapterSet, Course, TextbookSet, ShowTextbook, EditTextbook, ListTextbooks, EditChapter,
Prompt, Notification, ViewUtils, AjaxHelpers, modal_helpers) ->
describe "ShowTextbook", ->
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})
@promptSpies = jasmine.stealth.spyOnConstructor(Prompt, "Warning", ["show", "hide"])
@promptSpies.show.and.returnValue(@promptSpies)
window.course = new Course({
id: "5",
name: "Course Name",
url_name: "course_name",
org: "course_org",
num: "course_num",
revision: "course_rev"
});
afterEach ->
delete window.course
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.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 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 ->
@savingSpies = jasmine.stealth.spyOnConstructor(Notification, "Mini",
["show", "hide"])
@savingSpies.show.and.returnValue(@savingSpies)
CMS.URL.TEXTBOOKS = "/textbooks"
afterEach ->
delete CMS.URL.TEXTBOOKS
it "should destroy itself on confirmation", ->
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()
describe "EditTextbook", ->
describe "Basic", ->
tpl = readFixtures('edit-textbook.underscore')
describe "ShowTextbook", ->
tpl = readFixtures('show-textbook.underscore')
beforeEach ->
setFixtures($("<script>", {id: "edit-textbook-tpl", type: "text/template"}).text(tpl))
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", editing: true})
spyOn(@model, 'save')
@collection = new TextbookSet()
@collection.add(@model)
@view = new EditTextbook({model: @model})
spyOn(@view, 'render').and.callThrough()
@model = new Textbook({name: "Life Sciences", id: "0life-sciences"})
spyOn(@model, "destroy").and.callThrough()
@collection = new TextbookSet([@model])
@view = new ShowTextbook({model: @model})
it "should render properly", ->
@promptSpies = jasmine.stealth.spyOnConstructor(Prompt, "Warning", ["show", "hide"])
@promptSpies.show.and.returnValue(@promptSpies)
window.course = new Course({
id: "5",
name: "Course Name",
url_name: "course_name",
org: "course_org",
num: "course_num",
revision: "course_rev"
});
afterEach ->
delete window.course
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.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 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 ->
@savingSpies = jasmine.stealth.spyOnConstructor(Notification, "Mini",
["show", "hide"])
@savingSpies.show.and.returnValue(@savingSpies)
CMS.URL.TEXTBOOKS = "/textbooks"
afterEach ->
delete CMS.URL.TEXTBOOKS
it "should destroy itself on confirmation", ->
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()
describe "EditTextbook", ->
describe "Basic", ->
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()
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")
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()
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 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 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")
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()
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 focus first input element of newly added textbook", ->
spyOn(jQuery.fn, 'focus').and.callThrough()
jasmine.addMatchers
toHaveBeenCalledOnJQueryObject: () ->
return {
compare: (actual, expected) ->
return {
pass: actual.calls && actual.calls.mostRecent() &&
actual.calls.mostRecent().object[0] == expected[0]
}
it "should focus first input element of newly added textbook", ->
spyOn(jQuery.fn, 'focus').and.callThrough()
jasmine.addMatchers
toHaveBeenCalledOnJQueryObject: () ->
return {
compare: (actual, expected) ->
return {
pass: actual.calls && actual.calls.mostRecent() &&
actual.calls.mostRecent().object[0] == expected[0]
}
}
@view.$(".new-button").click()
$inputEl = @view.$el.find('section:last input:first')
expect($inputEl.length).toEqual(1)
# 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)
@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)
# 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"
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"})
describe "EditChapter", ->
beforeEach ->
modal_helpers.installModalTemplates()
@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"})
afterEach ->
delete CMS.URL.UPLOAD_ASSET
delete window.course
afterEach ->
delete CMS.URL.UPLOAD_ASSET
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", ->
@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 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.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).
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")

View File

@@ -1,133 +1,135 @@
define ["js/models/uploads", "js/views/uploads", "js/models/chapter", "common/js/spec_helpers/ajax_helpers", "js/spec_helpers/modal_helpers"], (FileUpload, UploadDialog, Chapter, AjaxHelpers, modal_helpers) ->
define ["js/models/uploads", "js/views/uploads", "js/models/chapter",
"edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "js/spec_helpers/modal_helpers"],
(FileUpload, UploadDialog, Chapter, AjaxHelpers, modal_helpers) ->
describe "UploadDialog", ->
tpl = readFixtures("upload-dialog.underscore")
describe "UploadDialog", ->
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(
mimeTypes: ['application/pdf']
)
@dialogResponse = dialogResponse = []
@mockFiles = []
afterEach ->
delete CMS.URL.UPLOAD_ASSET
modal_helpers.cancelModalIfShowing()
createTestView = (test) ->
view = new UploadDialog(
model: test.model,
url: CMS.URL.UPLOAD_ASSET,
onSuccess: (response) =>
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
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")
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 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 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()
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)
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")
describe "Uploads", ->
beforeEach ->
@clock = sinon.useFakeTimers()
modal_helpers.installModalTemplates()
appendSetFixtures($("<script>", {id: "upload-dialog-tpl", type: "text/template"}).text(tpl))
CMS.URL.UPLOAD_ASSET = "/upload"
@model = new FileUpload(
mimeTypes: ['application/pdf']
)
@dialogResponse = dialogResponse = []
@mockFiles = []
afterEach ->
delete CMS.URL.UPLOAD_ASSET
modal_helpers.cancelModalIfShowing()
@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")
createTestView = (test) ->
view = new UploadDialog(
model: test.model,
url: CMS.URL.UPLOAD_ASSET,
onSuccess: (response) =>
test.dialogResponse.push(response.response)
)
spyOn(view, 'remove').and.callThrough()
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()
# 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
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"})
expect(modal_helpers.isShowingModal(view)).toBeTruthy();
@clock.tick(2001)
expect(modal_helpers.isShowingModal(view)).toBeFalsy();
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")
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 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 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()
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)
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")
describe "Uploads", ->
beforeEach ->
@clock = sinon.useFakeTimers()
afterEach ->
modal_helpers.cancelModalIfShowing()
@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 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 "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"})
expect(modal_helpers.isShowingModal(view)).toBeTruthy();
@clock.tick(2001)
expect(modal_helpers.isShowingModal(view)).toBeFalsy();

View File

@@ -8,7 +8,7 @@ define([ // jshint ignore:line
'js/certificates/views/certificate_details',
'js/certificates/views/certificate_preview',
'common/js/components/views/feedback_notification',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/view_helpers',
'js/spec_helpers/validation_helpers',

View File

@@ -8,7 +8,7 @@ define([ // jshint ignore:line
'js/certificates/collections/certificates',
'js/certificates/views/certificate_editor',
'common/js/components/views/feedback_notification',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/view_helpers',
'js/spec_helpers/validation_helpers',

View File

@@ -7,7 +7,7 @@ define([ // jshint ignore:line
'js/certificates/views/certificate_preview',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/view_helpers',
'common/js/spec_helpers/ajax_helpers'
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
],
function(_, $, Course, CertificatePreview, TemplateHelpers, ViewHelpers, AjaxHelpers) {
'use strict';

View File

@@ -11,7 +11,7 @@ define([ // jshint ignore:line
'js/certificates/views/certificates_list',
'js/certificates/views/certificate_preview',
'common/js/components/views/feedback_notification',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers',
'js/certificates/spec/custom_matchers'
],

View File

@@ -1,4 +1,5 @@
define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notification", "common/js/spec_helpers/ajax_helpers", "jquery", "underscore"],
define(["js/utils/drag_and_drop", "common/js/components/views/feedback_notification",
"edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "jquery", "underscore"],
function (ContentDragger, Notification, AjaxHelpers, $, _) {
describe("Overview drag and drop functionality", function () {
beforeEach(function () {

View File

@@ -1,13 +1,13 @@
define(
[
'jquery', 'underscore',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'js/views/video/transcripts/utils',
'js/views/video/transcripts/metadata_videolist', 'js/models/metadata',
'js/views/abstract_editor',
'common/js/spec_helpers/ajax_helpers',
'xmodule'
],
function ($, _, Utils, VideoList, MetadataModel, AbstractEditor, AjaxHelpers) {
function ($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
'use strict';
describe('CMS.Views.Metadata.VideoList', function () {
var videoListEntryTemplate = readFixtures(

View File

@@ -1,4 +1,4 @@
define([ "jquery", "common/js/spec_helpers/ajax_helpers", "URI", "js/views/assets",
define([ "jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "URI", "js/views/assets",
"js/collections/asset", "common/js/spec_helpers/view_helpers"],
function ($, AjaxHelpers, URI, AssetsView, AssetCollection, ViewHelpers) {

View File

@@ -1,4 +1,4 @@
define([ "jquery", "common/js/spec_helpers/ajax_helpers", "js/spec_helpers/edit_helpers",
define([ "jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "js/spec_helpers/edit_helpers",
"js/views/container", "js/models/xblock_info", "jquery.simulate",
"xmodule", "coffee/src/main", "xblock/cms.runtime.v1"],
function ($, AjaxHelpers, EditHelpers, ContainerView, XBlockInfo) {

View File

@@ -1,16 +1,15 @@
define([
'underscore', 'js/models/course', 'js/models/group_configuration', 'js/models/group',
'js/collections/group_configuration', 'js/collections/group',
'js/views/group_configuration_details', 'js/views/group_configurations_list', 'js/views/group_configuration_editor',
'js/views/group_configuration_item', 'js/views/experiment_group_edit', 'js/views/content_group_list',
'js/views/content_group_details', 'js/views/content_group_editor', 'js/views/content_group_item',
'common/js/components/views/feedback_notification', 'common/js/spec_helpers/ajax_helpers', 'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/view_helpers'
'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/view_helpers', 'js/models/course', 'js/models/group_configuration', 'js/models/group',
'js/collections/group_configuration', 'js/collections/group', 'js/views/group_configuration_details',
'js/views/group_configurations_list', 'js/views/group_configuration_editor', 'js/views/group_configuration_item',
'js/views/experiment_group_edit', 'js/views/content_group_list', 'js/views/content_group_details',
'js/views/content_group_editor', 'js/views/content_group_item'
], function(
_, Course, GroupConfigurationModel, GroupModel, GroupConfigurationCollection, GroupCollection,
GroupConfigurationDetailsView, GroupConfigurationsListView, GroupConfigurationEditorView,
GroupConfigurationItemView, ExperimentGroupEditView, GroupList, ContentGroupDetailsView,
ContentGroupEditorView, ContentGroupItemView, Notification, AjaxHelpers, TemplateHelpers, ViewHelpers
_, AjaxHelpers, TemplateHelpers, ViewHelpers, Course, GroupConfigurationModel, GroupModel,
GroupConfigurationCollection, GroupCollection, GroupConfigurationDetailsView, GroupConfigurationsListView,
GroupConfigurationEditorView, GroupConfigurationItemView, ExperimentGroupEditView, GroupList,
ContentGroupDetailsView, ContentGroupEditorView, ContentGroupItemView
) {
'use strict';
var SELECTORS = {
@@ -286,7 +285,7 @@ define([
it('should hide empty usage appropriately', function() {
this.model.set('showGroups', true);
this.view.$('.hide-groups').click();
assertHideEmptyUsages(this.view)
assertHideEmptyUsages(this.view);
});
it('should show non-empty usage appropriately', function() {
@@ -298,7 +297,7 @@ define([
this.view,
'This Group Configuration is used in:',
'Cannot delete when in use by an experiment'
)
);
});
it('should hide non-empty usage appropriately', function() {
@@ -922,7 +921,7 @@ define([
this.view,
'This content group is used in:',
'Cannot delete when in use by a unit'
)
);
});
it('should hide non-empty usage appropriately', function() {
@@ -988,7 +987,7 @@ define([
notificationSpy = ViewHelpers.createNotificationSpy();
this.view.$(SELECTORS.inputName).val('New Content Group');
ViewHelpers.submitAndVerifyFormError(this.view, requests, notificationSpy)
ViewHelpers.submitAndVerifyFormError(this.view, requests, notificationSpy);
});
it('does not save on cancel', function() {

View File

@@ -1,4 +1,5 @@
define(['jquery', 'js/factories/login', 'common/js/spec_helpers/ajax_helpers', 'common/js/components/utils/view_utils'],
define(['jquery', 'js/factories/login', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/components/utils/view_utils'],
function($, LoginFactory, AjaxHelpers, ViewUtils) {
'use strict';
describe("Studio Login Page", function() {

View File

@@ -1,4 +1,4 @@
define(["jquery", "underscore", "common/js/spec_helpers/ajax_helpers", "js/spec_helpers/edit_helpers",
define(["jquery", "underscore", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "js/spec_helpers/edit_helpers",
"js/views/modals/edit_xblock", "js/models/xblock_info"],
function ($, _, AjaxHelpers, EditHelpers, EditXBlockModal, XBlockInfo) {

View File

@@ -1,4 +1,4 @@
define(["jquery", "underscore", "common/js/spec_helpers/ajax_helpers", "URI", "js/models/xblock_info",
define(["jquery", "underscore", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "URI", "js/models/xblock_info",
"js/views/paged_container", "js/views/paging_header",
"common/js/components/views/paging_footer", "js/views/xblock"],
function ($, _, AjaxHelpers, URI, XBlockInfo, PagedContainer, PagingHeader, PagingFooter, XBlockView) {

View File

@@ -1,4 +1,4 @@
define(["jquery", "underscore", "underscore.string", "common/js/spec_helpers/ajax_helpers",
define(["jquery", "underscore", "underscore.string", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers",
"common/js/spec_helpers/template_helpers", "js/spec_helpers/edit_helpers",
"js/views/pages/container", "js/views/pages/paged_container", "js/models/xblock_info", "jquery.simulate"],
function ($, _, str, AjaxHelpers, TemplateHelpers, EditHelpers, ContainerPage, PagedContainerPage, XBlockInfo) {

View File

@@ -1,4 +1,4 @@
define(["jquery", "underscore", "underscore.string", "common/js/spec_helpers/ajax_helpers",
define(["jquery", "underscore", "underscore.string", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers",
"common/js/spec_helpers/template_helpers", "js/spec_helpers/edit_helpers",
"common/js/components/views/feedback_prompt", "js/views/pages/container",
"js/views/pages/container_subviews", "js/models/xblock_info", "js/views/utils/xblock_utils",

View File

@@ -1,6 +1,6 @@
define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/utils/view_utils", "js/views/pages/course_outline",
"js/models/xblock_outline_info", "js/utils/date_utils", "js/spec_helpers/edit_helpers",
"common/js/spec_helpers/template_helpers", 'js/models/course',],
define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/js/components/utils/view_utils",
"js/views/pages/course_outline", "js/models/xblock_outline_info", "js/utils/date_utils",
"js/spec_helpers/edit_helpers", "common/js/spec_helpers/template_helpers", 'js/models/course'],
function($, AjaxHelpers, ViewUtils, CourseOutlinePage, XBlockOutlineInfo, DateUtils,
EditHelpers, TemplateHelpers, Course) {

View File

@@ -1,5 +1,6 @@
define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/spec_helpers/view_helpers", "js/views/course_rerun",
"js/views/utils/create_course_utils", "common/js/components/utils/view_utils", "jquery.simulate"],
define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/js/spec_helpers/view_helpers",
"js/views/course_rerun", "js/views/utils/create_course_utils", "common/js/components/utils/view_utils",
"jquery.simulate"],
function ($, AjaxHelpers, ViewHelpers, CourseRerunUtils, CreateCourseUtilsFactory, ViewUtils) {
describe("Create course rerun page", function () {
var selectors = {

View File

@@ -1,4 +1,6 @@
define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/spec_helpers/view_helpers", "js/index",
define(["jquery",
"edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers",
"common/js/spec_helpers/view_helpers", "js/index",
"common/js/components/utils/view_utils"],
function ($, AjaxHelpers, ViewHelpers, IndexUtils, ViewUtils) {
describe("Course listing page", function () {

View File

@@ -1,5 +1,5 @@
define([
"jquery", "common/js/spec_helpers/ajax_helpers", "common/js/spec_helpers/view_helpers",
"jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/js/spec_helpers/view_helpers",
"js/factories/manage_users_lib", "common/js/components/utils/view_utils"
],
function ($, AjaxHelpers, ViewHelpers, ManageUsersFactory, ViewUtils) {

View File

@@ -1,11 +1,13 @@
define([
"jquery",
"URI",
"common/js/spec_helpers/ajax_helpers",
"edx-ui-toolkit/js/pagination/paging-collection",
"js/views/paging",
"js/views/paging_header"
], function ($, URI, AjaxHelpers, PagingCollection, PagingView, PagingHeader) {
"jquery",
"URI",
"edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers",
"edx-ui-toolkit/js/pagination/paging-collection",
"js/views/paging",
"js/views/paging_header"
],
function ($, URI, AjaxHelpers, PagingCollection, PagingView, PagingHeader) {
'use strict';
var createPageableItem = function(index) {
var id = 'item_' + index;

View File

@@ -1,6 +1,6 @@
define([
'jquery', 'js/models/settings/course_details', 'js/views/settings/main',
'common/js/spec_helpers/ajax_helpers', 'common/js/spec_helpers/template_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'common/js/spec_helpers/template_helpers',
], function($, CourseDetailsModel, MainView, AjaxHelpers, TemplateHelpers) {
'use strict';
@@ -125,10 +125,9 @@ define([
//input some invalid values.
expect(entrance_exam_min_score.val('101').trigger('input')).toHaveClass("error");
expect(entrance_exam_min_score.val('invalidVal').trigger('input')).toHaveClass("error");
});
it('should provide a default value for the minimum score percentage', function(){
it('should provide a default value for the minimum score percentage', function() {
var entrance_exam_min_score = this.view.$(SELECTORS.entrance_exam_min_score);
@@ -138,7 +137,7 @@ define([
.toEqual(this.model.defaults.entrance_exam_minimum_score_pct);
});
it('show and hide the grade requirement section when the check box is selected and deselected respectively', function(){
it('shows and hide the grade requirement section appropriately', function() {
var entrance_exam_enabled_field = this.view.$(SELECTORS.entrance_exam_enabled_field);

View File

@@ -1,8 +1,9 @@
define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/spec_helpers/template_helpers",
define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/js/spec_helpers/template_helpers",
"common/js/spec_helpers/view_helpers", "common/js/components/utils/view_utils", "js/models/course",
"js/views/unit_outline", "js/models/xblock_info"],
function ($, AjaxHelpers, TemplateHelpers, ViewHelpers, ViewUtils,
function($, AjaxHelpers, TemplateHelpers, ViewHelpers, ViewUtils,
Course, UnitOutlineView, XBlockInfo) {
'use strict';
describe("UnitOutlineView", function() {
var createUnitOutlineView, createMockXBlockInfo,

View File

@@ -1,4 +1,4 @@
define([ "jquery", "underscore", "common/js/spec_helpers/ajax_helpers", "js/spec_helpers/edit_helpers",
define([ "jquery", "underscore", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "js/spec_helpers/edit_helpers",
"js/views/xblock_editor", "js/models/xblock_info"],
function ($, _, AjaxHelpers, EditHelpers, XBlockEditorView, XBlockInfo) {

View File

@@ -1,4 +1,4 @@
define(["jquery", "URI", "common/js/spec_helpers/ajax_helpers", "common/js/components/utils/view_utils",
define(["jquery", "URI", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/js/components/utils/view_utils",
"js/views/xblock", "js/models/xblock_info", "xmodule", "coffee/src/main", "xblock/cms.runtime.v1"],
function ($, URI, AjaxHelpers, ViewUtils, XBlockView, XBlockInfo) {
"use strict";

View File

@@ -1,4 +1,4 @@
define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/spec_helpers/template_helpers",
define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/js/spec_helpers/template_helpers",
"js/spec_helpers/edit_helpers", "js/models/xblock_info", "js/views/xblock_string_field_editor"],
function ($, AjaxHelpers, TemplateHelpers, EditHelpers, XBlockInfo, XBlockStringFieldEditor) {
describe("XBlockStringFieldEditorView", function () {

View File

@@ -1,9 +1,9 @@
/**
* Provides helper methods for invoking Studio editors in Jasmine tests.
*/
define(["jquery", "underscore", "common/js/spec_helpers/ajax_helpers", "common/js/spec_helpers/template_helpers",
"js/spec_helpers/modal_helpers", "js/views/modals/edit_xblock", "js/collections/component_template",
"xmodule", "coffee/src/main", "xblock/cms.runtime.v1"],
define(["jquery", "underscore", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers",
"common/js/spec_helpers/template_helpers", "js/spec_helpers/modal_helpers", "js/views/modals/edit_xblock",
"js/collections/component_template", "xmodule", "coffee/src/main", "xblock/cms.runtime.v1"],
function($, _, AjaxHelpers, TemplateHelpers, modal_helpers, EditXBlockModal, ComponentTemplates) {
var installMockXBlock, uninstallMockXBlock, installMockXModule, uninstallMockXModule,

View File

@@ -166,7 +166,7 @@ else:
%></%def>
<%def name="show_language_selector()"><%
return settings.FEATURES.get('SHOW_LANGUAGE_SELECTOR', False)
return get_value('SHOW_LANGUAGE_SELECTOR', settings.FEATURES.get('SHOW_LANGUAGE_SELECTOR', False))
%></%def>
<%def name="get_released_languages()"><%

View File

@@ -891,7 +891,7 @@ class AnonymousLookupTable(ModuleStoreTestCase):
self.assertEqual(anonymous_id, anonymous_id_for_user(self.user, course2.id, save=False))
# TODO: Clean up these tests so that they use the ProgramsDataMixin.
# TODO: Clean up these tests so that they use program factories.
@attr('shard_3')
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@ddt.ddt

View File

@@ -9,7 +9,7 @@
}, options);
this.view = new DiscussionTopicMenuView(options);
this.view.render().appendTo('#fixture-element');
this.defaultTextWidth = this.view.getNameWidth(this.completeText);
this.defaultTextWidth = this.completeText.length;
};
this.openMenu = function () {
@@ -67,31 +67,10 @@
expect(this.completeText).toEqual(dropdownText);
});
it('completely show just sub-category', function() {
var dropdownText;
this.createTopicView();
this.view.maxNameWidth = this.defaultTextWidth - 10;
this.view.$el.find('a.topic-title').first().click();
dropdownText = this.view.$el.find('.js-selected-topic').text();
expect(dropdownText.indexOf('…')).toEqual(0);
expect(dropdownText).toContain(this.selectedOptionText);
});
it('partially show sub-category', function() {
this.createTopicView();
var parentWidth = this.view.getNameWidth(this.parentCategoryText),
dropdownText;
this.view.maxNameWidth = this.defaultTextWidth - parentWidth;
this.view.$el.find('a.topic-title').first().click();
dropdownText = this.view.$el.find('.js-selected-topic').text();
expect(dropdownText.indexOf('…')).toEqual(0);
expect(dropdownText.lastIndexOf('…')).toBeGreaterThan(0);
});
it('broken span doesn\'t occur', function() {
var dropdownText;
this.createTopicView();
this.view.maxNameWidth = this.view.getNameWidth(this.selectedOptionText) + 100;
this.view.maxNameWidth = this.selectedOptionText.length + 100;
this.view.$el.find('a.topic-title').first().click();
dropdownText = this.view.$el.find('.js-selected-topic').text();
expect(dropdownText.indexOf('/ span>')).toEqual(-1);

View File

@@ -16,7 +16,7 @@
initialize: function(options) {
this.course_settings = options.course_settings;
this.currentTopicId = options.topicId;
this.maxNameWidth = 100;
this.maxNameWidth = 26;
_.bindAll(this,
'toggleTopicDropdown', 'handleTopicEvent', 'hideTopicDropdown', 'ignoreClick'
);
@@ -88,9 +88,6 @@
this.dropdownButton.addClass('dropped');
this.topicMenu.show();
$(document.body).on('click.topicMenu', this.hideTopicDropdown);
// Set here because 1) the window might get resized and things could
// change and 2) can't set in initialize because the button is hidden
this.maxNameWidth = this.dropdownButton.width() - 40;
return this;
},
@@ -146,29 +143,12 @@
}
},
// @TODO move into utils.coffee
getNameWidth: function(name) {
var test = $('<div>'),
width;
test.css({
'font-size': this.dropdownButton.css('font-size'),
'opacity': 0,
'position': 'absolute',
'left': -1000,
'top': -1000
}).html(name).appendTo(document.body);
width = test.width();
test.remove();
return width;
},
// @TODO move into utils.coffee
fitName: function(name) {
var ellipsisText = gettext('…'),
partialName, path, rawName;
if (this.getNameWidth(name) < this.maxNameWidth) {
if (name.length < this.maxNameWidth) {
return name;
} else {
path = _.map(name.split('/'), function(item){
@@ -176,15 +156,15 @@
});
while (path.length > 1) {
path.shift();
partialName = ellipsisText + ' / ' + path.join(' / ');
if (this.getNameWidth(partialName) < this.maxNameWidth) {
return partialName;
partialName = ellipsisText + '/' + path.join('/');
if (partialName.length > this.maxNameWidth) {
return partialName;
}
}
rawName = path[0];
name = ellipsisText + ' / ' + rawName;
while (this.getNameWidth(name) > this.maxNameWidth) {
rawName = rawName.slice(0, -1);
while (name.length > this.maxNameWidth) {
rawName = rawName.slice(0, this.maxNameWidth);
name = ellipsisText + ' / ' + rawName + ' ' + ellipsisText;
}
}

View File

@@ -3,7 +3,7 @@ define([
'backbone',
'underscore',
'edx-ui-toolkit/js/pagination/paging-collection',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/components/views/paginated_view'
], function ($, Backbone, _, PagingCollection, AjaxHelpers, PaginatedView) {
'use strict';

View File

@@ -3,7 +3,7 @@ define([
'URI',
'underscore',
'edx-ui-toolkit/js/pagination/paging-collection',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/components/views/paging_footer'
], function ($, URI, _, PagingCollection, AjaxHelpers, PagingFooter) {
'use strict';

View File

@@ -1,17 +1,17 @@
define([
'underscore',
'URI',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'edx-ui-toolkit/js/pagination/paging-collection',
'common/js/components/views/search_field',
'common/js/spec_helpers/ajax_helpers'
], function (_, URI, PagingCollection, SearchFieldView, AjaxHelpers) {
'common/js/components/views/search_field'
], function(_, URI, AjaxHelpers, PagingCollection, SearchFieldView) {
'use strict';
describe('SearchFieldView', function () {
describe('SearchFieldView', function() {
var searchFieldView,
mockUrl = '/api/mock_collection';
var newCollection = function (size, perPage) {
var results = _.map(_.range(size), function (i) { return {foo: i}; });
var newCollection = function(size, perPage) {
var results = _.map(_.range(size), function(i) { return {foo: i}; });
var TestPagingCollection = PagingCollection.extend({
state: {
pageSize: 5
@@ -29,7 +29,7 @@ define([
return collection;
};
var createSearchFieldView = function (options) {
var createSearchFieldView = function(options) {
options = _.extend(
{
type: 'test',
@@ -41,14 +41,14 @@ define([
return new SearchFieldView(options);
};
var assertQueryParams = function (request, expectedParameters) {
var assertQueryParams = function(request, expectedParameters) {
var urlParams = new URI(request.url).query(true);
_.each(expectedParameters, function (value, key) {
_.each(expectedParameters, function(value, key) {
expect(urlParams[key]).toBe(value);
});
};
var assertNotInQueryParams = function (request, param) {
var assertNotInQueryParams = function(request, param) {
var urlParams = new URI(request.url).query(true);
return !urlParams.hasOwnProperty(param);
};
@@ -57,20 +57,20 @@ define([
setFixtures('<section class="test-search"></section>');
});
it('correctly displays itself', function () {
it('correctly displays itself', function() {
searchFieldView = createSearchFieldView().render();
expect(searchFieldView.$('.search-field').val(), '');
expect(searchFieldView.$('.action-clear')).toHaveClass('is-hidden');
});
it('can display with an initial search string', function () {
it('can display with an initial search string', function() {
searchFieldView = createSearchFieldView({
searchString: 'foo'
}).render();
expect(searchFieldView.$('.search-field').val(), 'foo');
});
it('refreshes the collection when performing a search', function () {
it('refreshes the collection when performing a search', function() {
var requests = AjaxHelpers.requests(this);
searchFieldView = createSearchFieldView().render();
searchFieldView.$('.search-field').val('foo');
@@ -80,7 +80,7 @@ define([
page_size: '5',
text_search: 'foo'
});
AjaxHelpers.respondWithJson(requests, {
count: 10,
page: 1,
@@ -90,7 +90,7 @@ define([
expect(searchFieldView.$('.search-field').val(), 'foo');
});
it('can clear the search', function () {
it('can clear the search', function() {
var requests = AjaxHelpers.requests(this);
searchFieldView = createSearchFieldView({
searchString: 'foo'

View File

@@ -1,207 +0,0 @@
define(['sinon', 'underscore', 'URI'], function(sinon, _, URI) {
'use strict';
var XML_HTTP_READY_STATES, fakeServer, fakeRequests, currentRequest, expectRequest, expectNoRequests,
expectJsonRequest, expectPostRequest, expectRequestURL, skipResetRequest,
respondWithJson, respondWithError, respondWithTextError, respondWithNoContent;
XML_HTTP_READY_STATES = {
UNSENT: 0,
OPENED: 1,
LOADING: 3,
DONE: 4
};
/* These utility methods are used by Jasmine tests to create a mock server or
* get reference to mock requests. In either case, the cleanup (restore) is done with
* an after function.
*
* This pattern is being used instead of the more common beforeEach/afterEach pattern
* because we were seeing sporadic failures in the afterEach restore call. The cause of the
* errors were that one test suite was incorrectly being linked as the parent of an unrelated
* test suite (causing both suites' afterEach methods to be called). No solution for the root
* cause has been found, but initializing sinon and cleaning it up on a method-by-method
* basis seems to work. For more details, see STUD-1264.
*/
/**
* Get a reference to the mocked server, and respond
* to all requests with the specified statusCode.
*/
fakeServer = function (response) {
var server = sinon.fakeServer.create();
afterEach(function() {
if (server) {
server.restore();
}
});
server.respondWith(response);
return server;
};
/**
* Keep track of all requests to a fake server, and
* return a reference to the Array. This allows tests
* to respond for individual requests.
*/
fakeRequests = function () {
var requests = [],
xhr = sinon.useFakeXMLHttpRequest();
requests.currentIndex = 0;
xhr.onCreate = function(request) {
requests.push(request);
};
afterEach(function() {
if (xhr && xhr.hasOwnProperty('restore')) {
xhr.restore();
}
});
return requests;
};
/**
* Returns the request that has not yet been responded to. If no such request
* is available then the current test will fail.
* @param requests The Sinon requests list.
* @returns {*} The current request.
*/
currentRequest = function(requests) {
expect(requests.length).toBeGreaterThan(requests.currentIndex);
return requests[requests.currentIndex];
};
expectRequest = function(requests, method, url, body) {
var request = currentRequest(requests);
expect(request.readyState).toEqual(XML_HTTP_READY_STATES.OPENED);
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
if (typeof body === 'undefined') {
// The body of the request may not be germane to the current test-- like some call by a library,
// so allow it to be ignored.
return;
}
expect(request.requestBody).toEqual(body);
};
/**
* Verifies the there are no unconsumed requests.
*/
expectNoRequests = function(requests) {
expect(requests.length).toEqual(requests.currentIndex);
};
expectJsonRequest = function(requests, method, url, jsonRequest) {
jsonRequest = jsonRequest || null;
var request = currentRequest(requests);
expect(request.readyState).toEqual(XML_HTTP_READY_STATES.OPENED);
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
expect(JSON.parse(request.requestBody)).toEqual(jsonRequest === undefined ? null : jsonRequest);
};
/**
* Expect that a JSON request be made with the given URL and parameters.
* @param requests The collected requests
* @param expectedUrl The expected URL excluding the parameters
* @param expectedParameters An object representing the URL parameters
*/
expectRequestURL = function(requests, expectedUrl, expectedParameters) {
var request = currentRequest(requests),
parameters;
expect(new URI(request.url).path()).toEqual(expectedUrl);
parameters = new URI(request.url).query(true);
delete parameters._; // Ignore the cache-busting argument
expect(parameters).toEqual(expectedParameters);
};
/**
* Intended for use with POST requests using application/x-www-form-urlencoded.
*/
expectPostRequest = function(requests, url, body) {
var request = currentRequest(requests);
expect(request.readyState).toEqual(XML_HTTP_READY_STATES.OPENED);
expect(request.url).toEqual(url);
expect(request.method).toEqual("POST");
expect(_.difference(request.requestBody.split('&'), body.split('&'))).toEqual([]);
};
/**
* Verify that the HTTP request was marked as reset, and then skip it.
*
* Note: this is typically used when code has explicitly canceled a request
* after it has been sent. A good example is when a user chooses to cancel
* a slow running search.
*/
skipResetRequest = function(requests) {
var request = currentRequest(requests);
expect(request.readyState).toEqual(XML_HTTP_READY_STATES.UNSENT);
requests.currentIndex++;
};
respondWithJson = function(requests, jsonResponse) {
var request = currentRequest(requests);
request.respond(200,
{ 'Content-Type': 'application/json' },
JSON.stringify(jsonResponse));
requests.currentIndex++;
};
respondWithError = function(requests, statusCode, jsonResponse) {
var request = currentRequest(requests);
if (_.isUndefined(statusCode)) {
statusCode = 500;
}
if (_.isUndefined(jsonResponse)) {
jsonResponse = {};
}
request.respond(
statusCode,
{ 'Content-Type': 'application/json' },
JSON.stringify(jsonResponse)
);
requests.currentIndex++;
};
respondWithTextError = function(requests, statusCode, textResponse) {
var request = currentRequest(requests);
if (_.isUndefined(statusCode)) {
statusCode = 500;
}
if (_.isUndefined(textResponse)) {
textResponse = "";
}
request.respond(
statusCode,
{ 'Content-Type': 'text/plain' },
textResponse
);
requests.currentIndex++;
};
respondWithNoContent = function(requests) {
var request = currentRequest(requests);
request.respond(
204,
{ 'Content-Type': 'application/json' }
);
requests.currentIndex++;
};
return {
server: fakeServer,
requests: fakeRequests,
currentRequest: currentRequest,
expectRequest: expectRequest,
expectNoRequests: expectNoRequests,
expectJsonRequest: expectJsonRequest,
expectPostRequest: expectPostRequest,
expectRequestURL: expectRequestURL,
skipResetRequest: skipResetRequest,
respondWithJson: respondWithJson,
respondWithError: respondWithError,
respondWithTextError: respondWithTextError,
respondWithNoContent: respondWithNoContent
};
});

View File

@@ -1,52 +0,0 @@
/**
* Generally useful helper functions for writing Jasmine unit tests.
*/
define([], function () {
'use strict';
/**
* Runs func as a test case multiple times, using entries from data as arguments. Like Python's DDT.
* @param data An object mapping test names to arrays of function parameters. The name is passed to it() as the name
* of the test case, and the list of arguments is applied as arguments to func.
* @param func The function that actually expresses the logic of the test.
*/
var withData = function (data, func) {
for (var name in data) {
if (data.hasOwnProperty(name)) {
(function (name) {
it(name, function () {
func.apply(this, data[name]);
});
})(name);
}
}
};
/**
* Runs test multiple times, wrapping each call in a describe with beforeEach specified by setup and arguments and
* name coming from entries in config.
* @param config An object mapping configuration names to arrays of setup function parameters. The name is passed
* to describe as the name of the group of tests, and the list of arguments is applied as arguments to setup.
* @param setup The function to setup the given configuration before each test case. Runs in beforeEach.
* @param test The function that actually express the logic of the test. May include it() or more describe().
*/
var withConfiguration = function (config, setup, test) {
for (var name in config) {
if (config.hasOwnProperty(name)) {
(function (name) {
describe(name, function () {
beforeEach(function () {
setup.apply(this, config[name]);
});
test();
});
})(name);
}
}
};
return {
withData: withData,
withConfiguration: withConfiguration
};
});

View File

@@ -4,7 +4,7 @@
;(function (define) {
'use strict';
define(["jquery", "common/js/components/views/feedback_notification", "common/js/components/views/feedback_prompt",
'common/js/spec_helpers/ajax_helpers'],
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'],
function($, NotificationView, Prompt, AjaxHelpers) {
var installViewTemplates, createFeedbackSpy, verifyFeedbackShowing,
verifyFeedbackHidden, createNotificationSpy, verifyNotificationShowing,

View File

@@ -9,13 +9,18 @@ from django.conf import settings
from django.core.urlresolvers import reverse
from django.test.utils import override_settings
from certificates.models import (
CertificateInvalidation,
CertificateStatuses,
GeneratedCertificate
)
from certificates.tests.factories import CertificateInvalidationFactory
from opaque_keys.edx.keys import CourseKey
from student.tests.factories import UserFactory
from student.models import CourseEnrollment
from student.roles import GlobalStaff, SupportStaffRole
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from certificates.models import GeneratedCertificate, CertificateStatuses
FEATURES_WITH_CERTS_ENABLED = settings.FEATURES.copy()
FEATURES_WITH_CERTS_ENABLED['CERTIFICATES_HTML_VIEW'] = True
@@ -326,6 +331,33 @@ class CertificateRegenerateTests(CertificateSupportTestCase):
num_certs = GeneratedCertificate.eligible_certificates.filter(user=self.student).count()
self.assertEqual(num_certs, 1)
def test_regenerate_cert_with_invalidated_record(self):
""" If the certificate is marked as invalid, regenerate the certificate
and verify the invalidate entry is deactivated. """
# mark certificate as invalid
self._invalidate_certificate(self.cert)
self.assertInvalidatedCertExists()
# after invalidation certificate status become un-available.
self.assertGeneratedCertExists(
user=self.student, status=CertificateStatuses.unavailable
)
# Should be able to regenerate
response = self._regenerate(
course_key=self.CERT_COURSE_KEY,
username=self.STUDENT_USERNAME
)
self.assertEqual(response.status_code, 200)
self.assertInvalidatedCertDoesNotExist()
# Check that the user's certificate was updated
# Since the student hasn't actually passed the course,
# we'd expect that the certificate status will be "notpassing"
self.assertGeneratedCertExists(
user=self.student, status=CertificateStatuses.notpassing
)
def _regenerate(self, course_key=None, username=None):
"""Call the regeneration end-point and return the response. """
url = reverse("certificates:regenerate_certificate_for_user")
@@ -339,6 +371,41 @@ class CertificateRegenerateTests(CertificateSupportTestCase):
return self.client.post(url, params)
def _invalidate_certificate(self, certificate):
""" Dry method to mark certificate as invalid. """
CertificateInvalidationFactory.create(
generated_certificate=certificate,
invalidated_by=self.support,
active=True
)
# Invalidate user certificate
certificate.invalidate()
self.assertFalse(certificate.is_valid())
def assertInvalidatedCertExists(self):
""" Dry method to check certificate invalidated entry exists. """
self.assertTrue(
CertificateInvalidation.objects.filter(
generated_certificate__user=self.student, active=True
).exists()
)
def assertInvalidatedCertDoesNotExist(self):
""" Dry method to check certificate invalidated entry does not exists. """
self.assertFalse(
CertificateInvalidation.objects.filter(
generated_certificate__user=self.student, active=True
).exists()
)
def assertGeneratedCertExists(self, user, status):
""" Dry method to check if certificate exists. """
self.assertTrue(
GeneratedCertificate.objects.filter( # pylint: disable=no-member
user=user, status=status
).exists()
)
@ddt.ddt
class CertificateGenerateTests(CertificateSupportTestCase):

View File

@@ -19,15 +19,16 @@ from django.db import transaction
from django.db.models import Q
from django.utils.translation import ugettext as _
from certificates import api
from certificates.models import CertificateInvalidation
from courseware.access import has_access
from instructor_task.api import generate_certificates_for_students
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from xmodule.modulestore.django import modulestore
from student.models import User, CourseEnrollment
from courseware.access import has_access
from util.json_request import JsonResponse
from certificates import api
from instructor_task.api import generate_certificates_for_students
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from student.models import User, CourseEnrollment
from util.json_request import JsonResponse
from xmodule.modulestore.django import modulestore
log = logging.getLogger(__name__)
@@ -196,7 +197,7 @@ def regenerate_certificate_for_user(request):
# Attempt to regenerate certificates
try:
api.regenerate_user_certificates(params["user"], params["course_key"], course=course)
certificate = api.regenerate_user_certificates(params["user"], params["course_key"], course=course)
except: # pylint: disable=bare-except
# We are pessimistic about the kinds of errors that might get thrown by the
# certificates API. This may be overkill, but we're logging everything so we can
@@ -208,6 +209,9 @@ def regenerate_certificate_for_user(request):
)
return HttpResponseServerError(_("An unexpected error occurred while regenerating certificates."))
# Deactivate certificate invalidation by setting active to False.
_deactivate_invalidation(certificate)
log.info(
"Started regenerating certificates for user %s in course %s from the support page.",
params["user"].id, params["course_key"]
@@ -267,3 +271,25 @@ def generate_certificate_for_user(request):
specific_student_id=params["user"].id
)
return HttpResponse(200)
def _deactivate_invalidation(certificate):
"""
Deactivate certificate invalidation by setting active to False.
Arguments:
certificate : The student certificate object
Return:
None
"""
try:
# Fetch CertificateInvalidation object
certificate_invalidation = CertificateInvalidation.objects.get(
generated_certificate=certificate,
active=True
)
# Deactivate certificate invalidation if it was fetched successfully.
certificate_invalidation.deactivate()
except CertificateInvalidation.DoesNotExist: # pylint: disable=bare-except
pass

View File

@@ -1,268 +1,324 @@
# -*- coding: utf-8 -*-
"""
Unit tests covering the program listing and detail pages.
"""
import datetime
import json
import re
import unittest
from urlparse import urljoin
from bs4 import BeautifulSoup
from django.conf import settings
from django.core.urlresolvers import reverse
from django.test import override_settings, TestCase
from django.test import override_settings
from django.utils.text import slugify
from edx_oauth2_provider.tests.factories import ClientFactory
import httpretty
from opaque_keys.edx import locator
from provider.constants import CONFIDENTIAL
from openedx.core.djangoapps.credentials.models import CredentialsApiConfig
from openedx.core.djangoapps.credentials.tests import factories as credentials_factories
from openedx.core.djangoapps.credentials.tests.mixins import CredentialsDataMixin, CredentialsApiConfigMixin
from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
from openedx.core.djangoapps.programs.tests import factories
from openedx.core.djangoapps.programs.tests.mixins import (
ProgramsApiConfigMixin,
ProgramsDataMixin)
from student.models import CourseEnrollment
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
from openedx.core.djangoapps.programs.tests import factories as programs_factories
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
from openedx.core.djangoapps.programs.utils import get_display_category
from student.tests.factories import UserFactory, CourseEnrollmentFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@httpretty.activate
@override_settings(MKTG_URLS={'ROOT': 'https://www.example.com'})
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@override_settings(MKTG_URLS={'ROOT': 'http://edx.org'})
class TestProgramListing(
ModuleStoreTestCase,
ProgramsApiConfigMixin,
ProgramsDataMixin,
CredentialsDataMixin,
CredentialsApiConfigMixin):
"""
Unit tests for getting the list of programs enrolled by a logged in user
"""
PASSWORD = 'test'
class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, SharedModuleStoreTestCase):
"""Unit tests for the program listing page."""
maxDiff = None
password = 'test'
url = reverse('program_listing_view')
def setUp(self):
"""
Add a student
"""
super(TestProgramListing, self).setUp()
ClientFactory(name=CredentialsApiConfig.OAUTH2_CLIENT_NAME, client_type=CONFIDENTIAL)
ClientFactory(name=ProgramsApiConfig.OAUTH2_CLIENT_NAME, client_type=CONFIDENTIAL)
self.student = UserFactory()
@classmethod
def setUpClass(cls):
super(TestProgramListing, cls).setUpClass()
def _create_course_and_enroll(self, student, org, course, run):
"""
Creates a course and associated enrollment.
"""
course_location = locator.CourseLocator(org, course, run)
course = CourseFactory.create(
org=course_location.org,
number=course_location.course,
run=course_location.run
for name in [ProgramsApiConfig.OAUTH2_CLIENT_NAME, CredentialsApiConfig.OAUTH2_CLIENT_NAME]:
ClientFactory(name=name, client_type=CONFIDENTIAL)
cls.course = CourseFactory()
organization = programs_factories.Organization()
run_mode = programs_factories.RunMode(course_key=unicode(cls.course.id)) # pylint: disable=no-member
course_code = programs_factories.CourseCode(run_modes=[run_mode])
cls.first_program = programs_factories.Program(
organizations=[organization],
course_codes=[course_code]
)
cls.second_program = programs_factories.Program(
organizations=[organization],
course_codes=[course_code]
)
enrollment = CourseEnrollment.enroll(student, course.id)
enrollment.created = datetime.datetime(2000, 12, 31, 0, 0, 0, 0)
enrollment.save()
def _get_program_url(self, marketing_slug):
"""
Helper function to get the program card url
"""
return urljoin(
settings.MKTG_URLS.get('ROOT'),
'xseries' + '/{}'
).format(marketing_slug)
cls.data = sorted([cls.first_program, cls.second_program], key=cls.program_sort_key)
def _setup_and_get_program(self):
"""
The core function to setup the mock program api,
then call the django test client to get the actual program listing page
make sure the request suceeds and make sure x_series_url is on the page
"""
self.mock_programs_api()
self.client.login(username=self.student.username, password=self.PASSWORD)
response = self.client.get(self.url)
x_series_url = urljoin(settings.MKTG_URLS.get('ROOT'), 'xseries')
self.assertContains(response, x_series_url)
return response
cls.marketing_root = urljoin(settings.MKTG_URLS.get('ROOT'), 'xseries').rstrip('/')
def _get_program_checklist(self, program_id):
"""
The convenience function to get all the program related page element we would like to check against
"""
return [
self.PROGRAM_NAMES[program_id],
self._get_program_url(self.PROGRAMS_API_RESPONSE['results'][program_id]['marketing_slug']),
self.PROGRAMS_API_RESPONSE['results'][program_id]['organizations'][0]['display_name'],
]
def setUp(self):
super(TestProgramListing, self).setUp()
def _assert_progress_data_present(self, response):
"""Verify that progress data is present."""
self.assertContains(response, 'userProgress')
self.user = UserFactory()
self.client.login(username=self.user.username, password=self.password)
@httpretty.activate
def test_get_program_with_no_enrollment(self):
@classmethod
def program_sort_key(cls, program):
"""
Helper function used to sort dictionaries representing programs.
"""
return program['id']
def credential_sort_key(self, credential):
"""
Helper function used to sort dictionaries representing credentials.
"""
try:
return credential['certificate_url']
except KeyError:
return credential['credential_url']
def mock_programs_api(self, data):
"""Helper for mocking out Programs API URLs."""
self.assertTrue(httpretty.is_enabled(), msg='httpretty must be enabled to mock Programs API calls.')
url = ProgramsApiConfig.current().internal_api_url.strip('/') + '/programs/'
body = json.dumps({'results': data})
httpretty.register_uri(httpretty.GET, url, body=body, content_type='application/json')
def mock_credentials_api(self, data):
"""Helper for mocking out Credentials API URLs."""
self.assertTrue(httpretty.is_enabled(), msg='httpretty must be enabled to mock Credentials API calls.')
url = '{base}/user_credentials/?username={username}'.format(
base=CredentialsApiConfig.current().internal_api_url.strip('/'),
username=self.user.username
)
body = json.dumps({'results': data})
httpretty.register_uri(httpretty.GET, url, body=body, content_type='application/json')
def load_serialized_data(self, response, key):
"""
Extract and deserialize serialized data from the response.
"""
pattern = re.compile(r'{key}: (?P<data>\[.*\])'.format(key=key))
match = pattern.search(response.content)
serialized = match.group('data')
return json.loads(serialized)
def assert_dict_contains_subset(self, superset, subset):
"""
Verify that the dict superset contains the dict subset.
Works like assertDictContainsSubset, deprecated since Python 3.2.
See: https://docs.python.org/2.7/library/unittest.html#unittest.TestCase.assertDictContainsSubset.
"""
superset_keys = set(superset.keys())
subset_keys = set(subset.keys())
intersection = {key: superset[key] for key in superset_keys & subset_keys}
self.assertEqual(subset, intersection)
def test_login_required(self):
"""
Verify that login is required to access the page.
"""
self.create_programs_config()
response = self._setup_and_get_program()
for program_element in self._get_program_checklist(0):
self.assertNotContains(response, program_element)
for program_element in self._get_program_checklist(1):
self.assertNotContains(response, program_element)
self.mock_programs_api(self.data)
@httpretty.activate
def test_get_one_program(self):
self.create_programs_config()
self._create_course_and_enroll(self.student, *self.COURSE_KEYS[0].split('/'))
response = self._setup_and_get_program()
for program_element in self._get_program_checklist(0):
self.assertContains(response, program_element)
for program_element in self._get_program_checklist(1):
self.assertNotContains(response, program_element)
self.client.logout()
self._assert_progress_data_present(response)
@httpretty.activate
def test_get_both_program(self):
self.create_programs_config()
self._create_course_and_enroll(self.student, *self.COURSE_KEYS[0].split('/'))
self._create_course_and_enroll(self.student, *self.COURSE_KEYS[5].split('/'))
response = self._setup_and_get_program()
for program_element in self._get_program_checklist(0):
self.assertContains(response, program_element)
for program_element in self._get_program_checklist(1):
self.assertContains(response, program_element)
self._assert_progress_data_present(response)
def test_get_programs_dashboard_not_enabled(self):
self.create_programs_config(program_listing_enabled=False)
self.client.login(username=self.student.username, password=self.PASSWORD)
response = self.client.get(self.url)
self.assertEqual(response.status_code, 404)
def test_xseries_advertise_disabled(self):
self.create_programs_config(xseries_ad_enabled=False)
self.client.login(username=self.student.username, password=self.PASSWORD)
response = self.client.get(self.url)
x_series_url = urljoin(settings.MKTG_URLS.get('ROOT'), 'xseries')
self.assertNotContains(response, x_series_url)
def test_get_programs_not_logged_in(self):
self.create_programs_config()
response = self.client.get(self.url)
self.assertRedirects(
response,
'{}?next={}'.format(reverse('signin_user'), self.url)
)
def _expected_progam_credentials_data(self):
self.client.login(username=self.user.username, password=self.password)
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
def test_404_if_disabled(self):
"""
Dry method for getting expected program credentials response data.
Verify that the page 404s if disabled.
"""
return [
credentials_factories.UserCredential(
id=1,
username='test',
credential=credentials_factories.ProgramCredential()
),
credentials_factories.UserCredential(
id=2,
username='test',
credential=credentials_factories.ProgramCredential()
self.create_programs_config(program_listing_enabled=False)
response = self.client.get(self.url)
self.assertEqual(response.status_code, 404)
def test_empty_state(self):
"""
Verify that the response contains no programs data when no programs are engaged.
"""
self.create_programs_config()
self.mock_programs_api(self.data)
response = self.client.get(self.url)
self.assertContains(response, 'programsData: []')
def test_programs_listed(self):
"""
Verify that the response contains accurate programs data when programs are engaged.
"""
self.create_programs_config()
self.mock_programs_api(self.data)
CourseEnrollmentFactory(user=self.user, course_id=self.course.id) # pylint: disable=no-member
response = self.client.get(self.url)
actual = self.load_serialized_data(response, 'programsData')
actual = sorted(actual, key=self.program_sort_key)
for index, actual_program in enumerate(actual):
expected_program = self.data[index]
self.assert_dict_contains_subset(actual_program, expected_program)
self.assertEqual(
actual_program['display_category'],
get_display_category(expected_program)
)
]
def _expected_credentials_data(self):
""" Dry method for getting expected credentials."""
program_credentials_data = self._expected_progam_credentials_data()
return [
{
'display_name': self.PROGRAMS_API_RESPONSE['results'][0]['name'],
'subtitle': self.PROGRAMS_API_RESPONSE['results'][0]['subtitle'],
'credential_url':program_credentials_data[0]['certificate_url']
},
{
'display_name': self.PROGRAMS_API_RESPONSE['results'][1]['name'],
'subtitle':self.PROGRAMS_API_RESPONSE['results'][1]['subtitle'],
'credential_url':program_credentials_data[1]['certificate_url']
}
]
def test_toggle_xseries_advertising(self):
"""
Verify that when XSeries advertising is disabled, no link to the marketing site
appears in the response (and vice versa).
"""
# Verify the URL is present when advertising is enabled.
self.create_programs_config()
self.mock_programs_api(self.data)
@httpretty.activate
def test_get_xseries_certificates_with_data(self):
response = self.client.get(self.url)
self.assertContains(response, self.marketing_root)
# Verify the URL is missing when advertising is disabled.
self.create_programs_config(xseries_ad_enabled=False)
response = self.client.get(self.url)
self.assertNotContains(response, self.marketing_root)
def test_links_to_detail_pages(self):
"""
Verify that links to detail pages are present when enabled, instead of
links to the marketing site.
"""
self.create_programs_config()
self.mock_programs_api(self.data)
CourseEnrollmentFactory(user=self.user, course_id=self.course.id) # pylint: disable=no-member
response = self.client.get(self.url)
actual = self.load_serialized_data(response, 'programsData')
actual = sorted(actual, key=self.program_sort_key)
for index, actual_program in enumerate(actual):
expected_program = self.data[index]
base = reverse('program_details_view', args=[expected_program['id']]).rstrip('/')
slug = slugify(expected_program['name'])
self.assertEqual(
actual_program['detail_url'],
'{}/{}'.format(base, slug)
)
# Verify that links to the marketing site are present when detail pages are disabled.
self.create_programs_config(program_details_enabled=False)
response = self.client.get(self.url)
actual = self.load_serialized_data(response, 'programsData')
actual = sorted(actual, key=self.program_sort_key)
for index, actual_program in enumerate(actual):
expected_program = self.data[index]
self.assertEqual(
actual_program['detail_url'],
'{}/{}'.format(self.marketing_root, expected_program['marketing_slug'])
)
def test_certificates_listed(self):
"""
Verify that the response contains accurate certificate data when certificates are available.
"""
self.create_programs_config()
self.create_credentials_config(is_learner_issuance_enabled=True)
self.client.login(username=self.student.username, password=self.PASSWORD)
self.mock_programs_api(self.data)
# mock programs and credentials apis
self.mock_programs_api()
self.mock_credentials_api(self.student, data=self.CREDENTIALS_API_RESPONSE, reset_url=False)
first_credential = credentials_factories.UserCredential(
username=self.user.username,
credential=credentials_factories.ProgramCredential(
program_id=self.first_program['id']
)
)
second_credential = credentials_factories.UserCredential(
username=self.user.username,
credential=credentials_factories.ProgramCredential(
program_id=self.second_program['id']
)
)
response = self.client.get(reverse("program_listing_view"))
for certificate in self._expected_credentials_data():
self.assertContains(response, certificate['display_name'])
self.assertContains(response, certificate['credential_url'])
credentials_data = sorted([first_credential, second_credential], key=self.credential_sort_key)
self.assertContains(response, 'images/xseries-certificate-visual.png')
self.mock_credentials_api(credentials_data)
@httpretty.activate
def test_get_xseries_certificates_without_data(self):
response = self.client.get(self.url)
actual = self.load_serialized_data(response, 'certificatesData')
actual = sorted(actual, key=self.credential_sort_key)
self.create_programs_config()
self.create_credentials_config(is_learner_issuance_enabled=True)
for index, actual_credential in enumerate(actual):
expected_credential = credentials_data[index]
self.client.login(username=self.student.username, password=self.PASSWORD)
# mock programs and credentials apis
self.mock_programs_api()
self.mock_credentials_api(self.student, data={"results": []}, reset_url=False)
response = self.client.get(reverse("program_listing_view"))
for certificate in self._expected_credentials_data():
self.assertNotContains(response, certificate['display_name'])
self.assertNotContains(response, certificate['credential_url'])
self.assertEqual(
# TODO: certificate_url is needlessly transformed to credential_url. (╯°□°)╯︵ ┻━┻
# Clean this up!
actual_credential['credential_url'],
expected_credential['certificate_url']
)
@httpretty.activate
@override_settings(MKTG_URLS={'ROOT': 'http://edx.org'})
@override_settings(MKTG_URLS={'ROOT': 'https://www.example.com'})
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class TestProgramDetails(ProgramsApiConfigMixin, SharedModuleStoreTestCase):
"""
Unit tests for the program details page
"""
"""Unit tests for the program details page."""
program_id = 123
password = 'test'
url = reverse('program_details_view', args=[program_id])
@classmethod
def setUpClass(cls):
super(TestProgramDetails, cls).setUpClass()
cls.course = CourseFactory()
ClientFactory(name=ProgramsApiConfig.OAUTH2_CLIENT_NAME, client_type=CONFIDENTIAL)
course = CourseFactory()
organization = programs_factories.Organization()
run_mode = programs_factories.RunMode(course_key=unicode(course.id)) # pylint: disable=no-member
course_code = programs_factories.CourseCode(run_modes=[run_mode])
cls.data = programs_factories.Program(
organizations=[organization],
course_codes=[course_code]
)
def setUp(self):
super(TestProgramDetails, self).setUp()
self.details_page = reverse('program_details_view', args=[self.program_id])
self.user = UserFactory()
self.client.login(username=self.user.username, password=self.password)
ClientFactory(name=ProgramsApiConfig.OAUTH2_CLIENT_NAME, client_type=CONFIDENTIAL)
self.organization = factories.Organization()
self.run_mode = factories.RunMode(course_key=unicode(self.course.id)) # pylint: disable=no-member
self.course_code = factories.CourseCode(run_modes=[self.run_mode])
self.data = factories.Program(
organizations=[self.organization],
course_codes=[self.course_code]
)
def _mock_programs_api(self, data, status=200):
def mock_programs_api(self, data, status=200):
"""Helper for mocking out Programs API URLs."""
self.assertTrue(httpretty.is_enabled(), msg='httpretty must be enabled to mock Programs API calls.')
@@ -281,15 +337,15 @@ class TestProgramDetails(ProgramsApiConfigMixin, SharedModuleStoreTestCase):
content_type='application/json',
)
def _assert_program_data_present(self, response):
def assert_program_data_present(self, response):
"""Verify that program data is present."""
self.assertContains(response, 'programData')
self.assertContains(response, 'urls')
self.assertContains(response, 'program_listing_url')
self.assertContains(response, self.data['name'])
self._assert_programs_tab_present(response)
self.assert_programs_tab_present(response)
def _assert_programs_tab_present(self, response):
def assert_programs_tab_present(self, response):
"""Verify that the programs tab is present in the nav."""
soup = BeautifulSoup(response.content, 'html.parser')
self.assertTrue(
@@ -301,20 +357,20 @@ class TestProgramDetails(ProgramsApiConfigMixin, SharedModuleStoreTestCase):
Verify that login is required to access the page.
"""
self.create_programs_config()
self._mock_programs_api(self.data)
self.mock_programs_api(self.data)
self.client.logout()
response = self.client.get(self.details_page)
response = self.client.get(self.url)
self.assertRedirects(
response,
'{}?next={}'.format(reverse('signin_user'), self.details_page)
'{}?next={}'.format(reverse('signin_user'), self.url)
)
self.client.login(username=self.user.username, password=self.password)
response = self.client.get(self.details_page)
self._assert_program_data_present(response)
response = self.client.get(self.url)
self.assert_program_data_present(response)
def test_404_if_disabled(self):
"""
@@ -322,33 +378,33 @@ class TestProgramDetails(ProgramsApiConfigMixin, SharedModuleStoreTestCase):
"""
self.create_programs_config(program_details_enabled=False)
response = self.client.get(self.details_page)
self.assertEquals(response.status_code, 404)
def test_page_routing(self):
"""Verify that the page can be hit with or without a program name in the URL."""
self.create_programs_config()
self._mock_programs_api(self.data)
response = self.client.get(self.details_page)
self._assert_program_data_present(response)
response = self.client.get(self.details_page + 'program_name/')
self._assert_program_data_present(response)
response = self.client.get(self.details_page + 'program_name/invalid/')
self.assertEquals(response.status_code, 404)
response = self.client.get(self.url)
self.assertEqual(response.status_code, 404)
def test_404_if_no_data(self):
"""Verify that the page 404s if no program data is found."""
self.create_programs_config()
self._mock_programs_api(self.data, status=404)
response = self.client.get(self.details_page)
self.assertEquals(response.status_code, 404)
self.mock_programs_api(self.data, status=404)
response = self.client.get(self.url)
self.assertEqual(response.status_code, 404)
httpretty.reset()
self._mock_programs_api({})
response = self.client.get(self.details_page)
self.assertEquals(response.status_code, 404)
self.mock_programs_api({})
response = self.client.get(self.url)
self.assertEqual(response.status_code, 404)
def test_page_routing(self):
"""Verify that the page can be hit with or without a program name in the URL."""
self.create_programs_config()
self.mock_programs_api(self.data)
response = self.client.get(self.url)
self.assert_program_data_present(response)
response = self.client.get(self.url + 'program_name/')
self.assert_program_data_present(response)
response = self.client.get(self.url + 'program_name/invalid/')
self.assertEqual(response.status_code, 404)

View File

@@ -21,28 +21,26 @@ from lms.djangoapps.learner_dashboard.utils import (
@require_GET
def view_programs(request):
"""View programs in which the user is engaged."""
show_program_listing = ProgramsApiConfig.current().show_program_listing
if not show_program_listing:
programs_config = ProgramsApiConfig.current()
if not programs_config.show_program_listing:
raise Http404
meter = utils.ProgramProgressMeter(request.user)
programs = meter.engaged_programs
# TODO: Pull 'xseries' string from configuration model.
marketing_root = urljoin(settings.MKTG_URLS.get('ROOT'), 'xseries').strip('/')
marketing_root = urljoin(settings.MKTG_URLS.get('ROOT'), 'xseries').rstrip('/')
for program in programs:
program['detail_url'] = utils.get_program_detail_url(program, marketing_root)
program['display_category'] = utils.get_display_category(program)
program['marketing_url'] = '{root}/{slug}'.format(
root=marketing_root,
slug=program['marketing_slug']
)
context = {
'programs': programs,
'progress': meter.progress,
'xseries_url': marketing_root if ProgramsApiConfig.current().show_xseries_ad else None,
'xseries_url': marketing_root if programs_config.show_xseries_ad else None,
'nav_hidden': True,
'show_program_listing': show_program_listing,
'show_program_listing': programs_config.show_program_listing,
'credentials': get_programs_credentials(request.user, category='xseries'),
'disable_courseware_js': True,
'uses_pattern_library': True
@@ -55,8 +53,8 @@ def view_programs(request):
@require_GET
def program_details(request, program_id):
"""View details about a specific program."""
show_program_details = ProgramsApiConfig.current().show_program_details
if not show_program_details:
programs_config = ProgramsApiConfig.current()
if not programs_config.show_program_details:
raise Http404
program_data = utils.get_programs(request.user, program_id=program_id)
@@ -65,7 +63,6 @@ def program_details(request, program_id):
raise Http404
program_data = utils.supplement_program_data(program_data, request.user)
show_program_listing = ProgramsApiConfig.current().show_program_listing
urls = {
'program_listing_url': reverse('program_listing_view'),
@@ -77,7 +74,7 @@ def program_details(request, program_id):
context = {
'program_data': program_data,
'urls': urls,
'show_program_listing': show_program_listing,
'show_program_listing': programs_config.show_program_listing,
'nav_hidden': True,
'disable_courseware_js': True,
'uses_pattern_library': True

View File

@@ -1,5 +1,5 @@
define([
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'support/js/spec_helpers/enrollment_helpers',
'support/js/collections/enrollment',
], function (AjaxHelpers, EnrollmentHelpers, EnrollmentCollection) {

View File

@@ -1,5 +1,5 @@
define([
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'support/js/spec_helpers/enrollment_helpers',
'support/js/models/enrollment'
], function (AjaxHelpers, EnrollmentHelpers, EnrollmentModel) {

View File

@@ -1,6 +1,6 @@
define([
'jquery',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'support/js/views/certificates'
], function($, AjaxHelpers, CertificatesView) {
'use strict';

View File

@@ -1,6 +1,6 @@
define([
'underscore',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'support/js/spec_helpers/enrollment_helpers',
'support/js/models/enrollment',
'support/js/views/enrollment_modal'

View File

@@ -1,6 +1,6 @@
define([
'underscore',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'support/js/spec_helpers/enrollment_helpers',
'support/js/views/enrollment'
], function (_, AjaxHelpers, EnrollmentHelpers, EnrollmentView) {

View File

@@ -1,4 +1,4 @@
define(['backbone', 'URI', 'underscore', 'common/js/spec_helpers/ajax_helpers',
define(['backbone', 'URI', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'teams/js/spec_helpers/team_spec_helpers'],
function (Backbone, URI, _, AjaxHelpers, TeamSpecHelpers) {
'use strict';

View File

@@ -2,7 +2,7 @@ define([
'jquery',
'underscore',
'backbone',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'teams/js/views/edit_team_members',
'teams/js/models/team',
'teams/js/views/team_utils',

View File

@@ -2,7 +2,7 @@ define([
'jquery',
'underscore',
'backbone',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/page_helpers',
'teams/js/views/edit_team',
'teams/js/models/team',

View File

@@ -6,7 +6,7 @@ define([
'teams/js/views/instructor_tools',
'teams/js/views/team_utils',
'teams/js/spec_helpers/team_spec_helpers',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/page_helpers'
], function ($, Backbone, _, Team, InstructorToolsView, TeamUtils, TeamSpecHelpers, AjaxHelpers, PageHelpers) {
'use strict';

View File

@@ -3,7 +3,7 @@ define([
'teams/js/collections/my_teams',
'teams/js/views/my_teams',
'teams/js/spec_helpers/team_spec_helpers',
'common/js/spec_helpers/ajax_helpers'
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
], function (Backbone, MyTeamsCollection, MyTeamsView, TeamSpecHelpers, AjaxHelpers) {
'use strict';
describe('My Teams View', function () {

View File

@@ -1,5 +1,5 @@
define([
'underscore', 'common/js/spec_helpers/ajax_helpers', 'teams/js/views/team_discussion',
'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'teams/js/views/team_discussion',
'teams/js/spec_helpers/team_spec_helpers',
'xmodule_js/common_static/coffee/spec/discussion/discussion_spec_helper'
], function (_, AjaxHelpers, TeamDiscussionView, TeamSpecHelpers, DiscussionSpecHelper) {

View File

@@ -1,5 +1,5 @@
define([
'backbone', 'underscore', 'common/js/spec_helpers/ajax_helpers', 'teams/js/models/team',
'backbone', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'teams/js/models/team',
'teams/js/views/team_profile_header_actions', 'teams/js/spec_helpers/team_spec_helpers'
], function (Backbone, _, AjaxHelpers, TeamModel, TeamProfileHeaderActionsView, TeamSpecHelpers) {
'use strict';

View File

@@ -1,5 +1,5 @@
define([
'underscore', 'common/js/spec_helpers/ajax_helpers', 'teams/js/models/team',
'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'teams/js/models/team',
'teams/js/views/team_profile', 'teams/js/spec_helpers/team_spec_helpers',
'xmodule_js/common_static/coffee/spec/discussion/discussion_spec_helper'
], function (_, AjaxHelpers, TeamModel, TeamProfileView, TeamSpecHelpers, DiscussionSpecHelper) {

View File

@@ -2,22 +2,22 @@ define([
'jquery',
'backbone',
'logger',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/spec-helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/page_helpers',
'common/js/spec_helpers/spec_helpers',
'teams/js/views/teams_tab',
'teams/js/spec_helpers/team_spec_helpers'
], function ($, Backbone, Logger, AjaxHelpers, PageHelpers, SpecHelpers, TeamsTabView, TeamSpecHelpers) {
], function($, Backbone, Logger, SpecHelpers, AjaxHelpers, PageHelpers, TeamsTabView, TeamSpecHelpers) {
'use strict';
describe('TeamsTab', function() {
var requests;
var expectError = function (teamsTabView, text) {
var expectError = function(teamsTabView, text) {
expect(teamsTabView.$('.warning').text()).toContain(text);
};
var expectFocus = function (element) {
var expectFocus = function(element) {
expect(element.focus).toHaveBeenCalled();
};
@@ -50,7 +50,7 @@ define([
return teamsTabView;
};
beforeEach(function () {
beforeEach(function() {
setFixtures('<div class="teams-content"></div>');
spyOn($.fn, 'focus');
spyOn(Logger, 'log');
@@ -62,7 +62,7 @@ define([
}
);
describe('Navigation', function () {
describe('Navigation', function() {
it('does not render breadcrumbs for the top level tabs', function() {
var teamsTabView = createTeamsTabView(this);
teamsTabView.router.navigate('#my-teams', {trigger: true});
@@ -71,20 +71,20 @@ define([
expect(teamsTabView.$('.breadcrumbs').length).toBe(0);
});
it('does not interfere with anchor links to #content', function () {
it('does not interfere with anchor links to #content', function() {
var teamsTabView = createTeamsTabView(this);
teamsTabView.router.navigate('#content', {trigger: true});
expect(teamsTabView.$('.wrapper-msg')).toHaveClass('is-hidden');
});
it('displays and focuses an error message when trying to navigate to a nonexistent page', function () {
it('displays and focuses an error message when trying to navigate to a nonexistent page', function() {
var teamsTabView = createTeamsTabView(this);
teamsTabView.router.navigate('no_such_page', {trigger: true});
expectError(teamsTabView, 'The page "no_such_page" could not be found.');
expectFocus(teamsTabView.$('.warning'));
});
it('displays and focuses an error message when trying to navigate to a nonexistent topic', function () {
it('displays and focuses an error message when trying to navigate to a nonexistent topic', function() {
var teamsTabView = createTeamsTabView(this);
teamsTabView.router.navigate('topics/no_such_topic', {trigger: true});
AjaxHelpers.expectRequest(requests, 'GET', '/api/team/v0/topics/no_such_topic,course/1', null);
@@ -93,7 +93,7 @@ define([
expectFocus(teamsTabView.$('.warning'));
});
it('displays and focuses an error message when trying to navigate to a nonexistent team', function () {
it('displays and focuses an error message when trying to navigate to a nonexistent team', function() {
var teamsTabView = createTeamsTabView(this);
teamsTabView.router.navigate('teams/' + TeamSpecHelpers.testTopicID + '/no_such_team', {trigger: true});
AjaxHelpers.expectRequest(requests, 'GET', '/api/team/v0/teams/no_such_team?expand=user', null);
@@ -102,23 +102,27 @@ define([
expectFocus(teamsTabView.$('.warning'));
});
it('displays and focuses an error message when it receives a 401 AJAX response', function () {
it('displays and focuses an error message when it receives a 401 AJAX response', function() {
var teamsTabView = createTeamsTabView(this).render();
teamsTabView.router.navigate('topics/' + TeamSpecHelpers.testTopicID, {trigger: true});
AjaxHelpers.respondWithError(requests, 401);
expectError(teamsTabView, "Your request could not be completed. Reload the page and try again.");
expectError(teamsTabView, 'Your request could not be completed. Reload the page and try again.');
expectFocus(teamsTabView.$('.warning'));
});
it('displays and focuses an error message when it receives a 500 AJAX response', function () {
it('displays and focuses an error message when it receives a 500 AJAX response', function() {
var teamsTabView = createTeamsTabView(this).render();
teamsTabView.router.navigate('topics/' + TeamSpecHelpers.testTopicID, {trigger: true});
AjaxHelpers.respondWithError(requests, 500);
expectError(teamsTabView, "Your request could not be completed due to a server problem. Reload the page and try again. If the issue persists, click the Help tab to report the problem.");
expectError(
teamsTabView,
'Your request could not be completed due to a server problem. Reload the page and try again. ' +
'If the issue persists, click the Help tab to report the problem.'
);
expectFocus(teamsTabView.$('.warning'));
});
it('does not navigate to the topics page when syncing its collection if not on the search page', function () {
it('does not navigate to the topics page when syncing its collection if not on search page', function() {
var teamsTabView = createTeamsTabView(this),
collection = TeamSpecHelpers.createMockTeams();
teamsTabView.createTeamsListView({
@@ -131,7 +135,7 @@ define([
});
});
describe('Analytics Events', function () {
describe('Analytics Events', function() {
SpecHelpers.withData({
'fires a page view event for the topic page': [
'topics/' + TeamSpecHelpers.testTopicID,
@@ -173,7 +177,7 @@ define([
team_id: 'test_team_id'
}
]
}, function (url, expectedEvent) {
}, function(url, expectedEvent) {
var teamsTabView = createTeamsTabView(this, {
userInfo: TeamSpecHelpers.createMockUserInfo({staff: true})
});
@@ -186,8 +190,8 @@ define([
});
});
describe('Discussion privileges', function () {
it('allows privileged access to any team', function () {
describe('Discussion privileges', function() {
it('allows privileged access to any team', function() {
var teamsTabView = createTeamsTabView(this, {
userInfo: TeamSpecHelpers.createMockUserInfo({privileged: true})
});
@@ -197,7 +201,7 @@ define([
expect(teamsTabView.readOnlyDiscussion(undefined)).toBe(false);
});
it('allows access to a team which an unprivileged user is a member of', function () {
it('allows access to a team which an unprivileged user is a member of', function() {
var teamsTabView = createTeamsTabView(this, {
userInfo: TeamSpecHelpers.createMockUserInfo({
username: TeamSpecHelpers.testUser,
@@ -215,7 +219,7 @@ define([
})).toBe(false);
});
it('does not allow access if the user is neither privileged nor a team member', function () {
it('does not allow access if the user is neither privileged nor a team member', function() {
var teamsTabView = createTeamsTabView(this, {
userInfo: TeamSpecHelpers.createMockUserInfo({privileged: false, staff: true})
});
@@ -225,7 +229,7 @@ define([
});
});
describe('Search', function () {
describe('Search', function() {
var performSearch = function(requests, teamsTabView) {
teamsTabView.$('.search-field').val('foo');
teamsTabView.$('.action-search').click();
@@ -239,7 +243,7 @@ define([
AjaxHelpers.expectNoRequests(requests);
};
it('can search teams', function () {
it('can search teams', function() {
var teamsTabView = createTeamsTabView(this);
teamsTabView.browseTopic(TeamSpecHelpers.testTopicID);
verifyTeamsRequest({
@@ -252,7 +256,7 @@ define([
expect(teamsTabView.$('.page-description').text()).toBe('Showing results for "foo"');
});
it('can clear a search', function () {
it('can clear a search', function() {
var teamsTabView = createTeamsTabView(this);
teamsTabView.browseTopic(TeamSpecHelpers.testTopicID);
AjaxHelpers.respondWithJson(requests, {});
@@ -272,7 +276,7 @@ define([
expect(teamsTabView.$('.page-description').text()).toBe('Test description 1');
});
it('can navigate back to all teams from a search', function () {
it('can navigate back to all teams from a search', function() {
var teamsTabView = createTeamsTabView(this);
teamsTabView.browseTopic(TeamSpecHelpers.testTopicID);
AjaxHelpers.respondWithJson(requests, {});
@@ -292,7 +296,7 @@ define([
expect(teamsTabView.$('.page-description').text()).toBe('Test description 1');
});
it('does not switch to showing results when the search returns an error', function () {
it('does not switch to showing results when the search returns an error', function() {
var teamsTabView = createTeamsTabView(this);
teamsTabView.browseTopic(TeamSpecHelpers.testTopicID);
AjaxHelpers.respondWithJson(requests, {});

View File

@@ -1,6 +1,6 @@
define([
'backbone', 'underscore', 'teams/js/collections/topic', 'teams/js/views/topics',
'teams/js/spec_helpers/team_spec_helpers', 'common/js/spec_helpers/ajax_helpers'
'teams/js/spec_helpers/team_spec_helpers', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
], function (Backbone, _, TopicCollection, TopicsView, TeamSpecHelpers, AjaxHelpers) {
'use strict';
describe('TopicsView', function () {

View File

@@ -33,18 +33,19 @@
setActiveRunMode: function(runMode){
if (runMode){
this.set({
certificate_url: runMode.certificate_url,
course_image_url: runMode.course_image_url || '',
course_key: runMode.course_key,
course_url: runMode.course_url || '',
display_name: this.context.display_name,
key: this.context.key,
marketing_url: runMode.marketing_url || '',
start_date: runMode.start_date,
end_date: runMode.end_date,
is_enrolled: runMode.is_enrolled,
is_enrollment_open: runMode.is_enrollment_open,
course_key: runMode.course_key,
course_url: runMode.course_url || '',
course_image_url: runMode.course_image_url || '',
key: this.context.key,
marketing_url: runMode.marketing_url || '',
mode_slug: runMode.mode_slug,
run_key: runMode.run_key
run_key: runMode.run_key,
start_date: runMode.start_date
});
}
},

View File

@@ -15,7 +15,7 @@
type: data.display_category + ' Program',
subtitle: data.subtitle,
organizations: data.organizations,
marketingUrl: data.marketing_url,
detailUrl: data.detail_url,
smallBannerUrl: data.banner_image_urls.w348h116,
mediumBannerUrl: data.banner_image_urls.w435h145,
largeBannerUrl: data.banner_image_urls.w726h242,

View File

@@ -0,0 +1,33 @@
;(function (define) {
'use strict';
define(['backbone',
'jquery',
'underscore',
'gettext',
'edx-ui-toolkit/js/utils/html-utils',
'text!../../../templates/learner_dashboard/certificate_status.underscore'
],
function(
Backbone,
$,
_,
gettext,
HtmlUtils,
certificateStatusTpl
) {
return Backbone.View.extend({
tpl: HtmlUtils.template(certificateStatusTpl),
initialize: function(options) {
this.$el = options.$el;
this.render();
},
render: function() {
var data = this.model.toJSON();
HtmlUtils.setHtml(this.$el, this.tpl(data));
}
});
}
);
}).call(this, define || RequireJS.define);

View File

@@ -7,6 +7,7 @@
'gettext',
'edx-ui-toolkit/js/utils/html-utils',
'js/learner_dashboard/models/course_enroll_model',
'js/learner_dashboard/views/certificate_status_view',
'js/learner_dashboard/views/course_enroll_view',
'text!../../../templates/learner_dashboard/course_card.underscore'
],
@@ -17,6 +18,7 @@
gettext,
HtmlUtils,
EnrollModel,
CertificateStatusView,
CourseEnrollView,
pageTpl
) {
@@ -42,12 +44,24 @@
},
postRender: function(){
var $certStatus = this.$('.certificate-status');
this.enrollView = new CourseEnrollView({
$parentEl: this.$('.course-actions'),
model: this.model,
urlModel: this.urlModel,
enrollModel: this.enrollModel
});
if ( this.model.get('certificate_url') ) {
this.certificateStatus = new CertificateStatusView({
$el: $certStatus,
model: this.model
});
} else {
// Styles are applied to the element that show if it's empty
$certStatus.remove();
}
}
});
}

View File

@@ -1,6 +1,6 @@
define([
'js/api_admin/views/catalog_preview',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
], function (
CatalogPreviewView, AjaxHelpers
) {

View File

@@ -1,4 +1,4 @@
define(['common/js/spec_helpers/ajax_helpers', 'js/ccx/schedule'],
define(['edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'js/ccx/schedule'],
function(AjaxHelpers) {
describe("edx.ccx.schedule.ScheduleView", function() {
var view = null;

View File

@@ -2,9 +2,9 @@ define([
'jquery',
'jquery.ajax-retry',
'js/commerce/views/receipt_view',
'common/js/spec_helpers/ajax_helpers'
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
],
function ($, AjaxRetry, ReceiptView, AjaxHelpers){
function ($, AjaxRetry, ReceiptView, AjaxHelpers) {
'use strict';
describe('edx.commerce.ReceiptView', function() {
var data, courseResponseData, providerResponseData, mockRequests, mockRender, createReceiptView,

View File

@@ -1,4 +1,4 @@
define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers',
define(['backbone', 'jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers', 'js/bookmarks/views/bookmark_button'
],
function (Backbone, $, _, AjaxHelpers, TemplateHelpers, BookmarkButtonView) {

View File

@@ -3,7 +3,7 @@ define(['backbone',
'underscore',
'logger',
'URI',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers',
'js/bookmarks/views/bookmarks_list_button',
'js/bookmarks/views/bookmarks_list',

View File

@@ -1,4 +1,6 @@
define(['common/js/spec_helpers/template_helpers', 'common/js/spec_helpers/ajax_helpers', 'js/dashboard/donation'],
define(['common/js/spec_helpers/template_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'js/dashboard/donation'],
function(TemplateHelpers, AjaxHelpers) {
'use strict';

View File

@@ -1,5 +1,5 @@
define([
'jquery', 'common/js/spec_helpers/ajax_helpers','common/js/spec_helpers/template_helpers',
'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers','common/js/spec_helpers/template_helpers',
'js/discovery/discovery_factory'
], function($, AjaxHelpers, TemplateHelpers, DiscoveryFactory) {
'use strict';

View File

@@ -1,5 +1,5 @@
define([
'common/js/spec_helpers/ajax_helpers', 'js/discovery/models/course_discovery'
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'js/discovery/models/course_discovery'
], function(AjaxHelpers, CourseDiscovery) {
'use strict';

View File

@@ -1,5 +1,5 @@
define([
'common/js/spec_helpers/ajax_helpers', 'js/discovery/models/search_state'
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'js/discovery/models/search_state'
], function(AjaxHelpers, SearchState) {
'use strict';

View File

@@ -1,4 +1,4 @@
define(['underscore', 'URI', 'common/js/spec_helpers/ajax_helpers'], function(_, URI, AjaxHelpers) {
define(['underscore', 'URI', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'], function(_, URI, AjaxHelpers) {
'use strict';
var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
LONG_TEXT, PRUNED_TEXT, TRUNCATED_TEXT, SHORT_TEXT,

View File

@@ -1,5 +1,5 @@
define([
'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers', 'js/spec/edxnotes/helpers',
'jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'js/spec/edxnotes/helpers',
'annotator_1.2.9', 'logger', 'js/edxnotes/views/notes_factory'
], function($, _, AjaxHelpers, Helpers, Annotator, Logger, NotesFactory) {
'use strict';

View File

@@ -1,6 +1,6 @@
define([
'jquery', 'underscore', 'annotator_1.2.9',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'js/spec/edxnotes/helpers',
'js/edxnotes/views/notes_factory'
], function ($, _, Annotator, AjaxHelpers, Helpers, NotesFactory) {

View File

@@ -1,5 +1,5 @@
define([
'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers',
'jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers', 'js/spec/edxnotes/helpers', 'logger',
'js/edxnotes/models/note', 'js/edxnotes/views/note_item',
], function(

View File

@@ -1,5 +1,5 @@
define([
'annotator_1.2.9', 'js/edxnotes/views/notes_factory', 'common/js/spec_helpers/ajax_helpers',
'annotator_1.2.9', 'js/edxnotes/views/notes_factory', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'js/spec/edxnotes/helpers'
], function(Annotator, NotesFactory, AjaxHelpers, Helpers) {
'use strict';

View File

@@ -1,6 +1,6 @@
define([
'jquery', 'underscore', 'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/ajax_helpers', 'js/spec/edxnotes/helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'js/spec/edxnotes/helpers',
'js/edxnotes/views/page_factory'
], function($, _, TemplateHelpers, AjaxHelpers, Helpers, NotesFactory) {
'use strict';
@@ -15,7 +15,6 @@ define([
this.view = new NotesFactory({notes: notes, pageSize: 10});
});
it('should be displayed properly', function() {
var requests = AjaxHelpers.requests(this),
tab;

View File

@@ -1,5 +1,5 @@
define([
'jquery', 'underscore', 'annotator_1.2.9', 'common/js/spec_helpers/ajax_helpers',
'jquery', 'underscore', 'annotator_1.2.9', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'js/edxnotes/views/notes_visibility_factory', 'js/spec/edxnotes/helpers'
], function(
$, _, Annotator, AjaxHelpers, NotesVisibilityFactory, Helpers
@@ -9,7 +9,7 @@ define([
var params = {
endpoint: '/test_endpoint',
user: 'a user',
usageId : 'an usage',
usageId: 'an usage',
courseId: 'a course',
token: Helpers.makeToken(),
tokenUrl: '/test_token_url'
@@ -88,16 +88,17 @@ define([
it('can handle errors', function() {
var requests = AjaxHelpers.requests(this),
errorContainer = $('.annotator-notice');
$errorContainer = $('.annotator-notice');
this.button.click();
AjaxHelpers.respondWithError(requests);
expect(errorContainer).toContainText(
"An error has occurred. Make sure that you are connected to the Internet, and then try refreshing the page."
expect($errorContainer).toContainText(
'An error has occurred. Make sure that you are connected to the Internet, ' +
'and then try refreshing the page.'
);
expect(errorContainer).toBeVisible();
expect(errorContainer).toHaveClass('annotator-notice-show');
expect(errorContainer).toHaveClass('annotator-notice-error');
expect($errorContainer).toBeVisible();
expect($errorContainer).toHaveClass('annotator-notice-show');
expect($errorContainer).toHaveClass('annotator-notice-error');
this.button.click();
@@ -112,10 +113,10 @@ define([
AjaxHelpers.respondWithJson(requests, {});
AjaxHelpers.respondWithJson(requests, {});
expect(errorContainer).not.toHaveClass('annotator-notice-show');
expect($errorContainer).not.toHaveClass('annotator-notice-show');
});
it('toggles notes when CTRL + SHIFT + [ keydown on document', function () {
it('toggles notes when CTRL + SHIFT + [ keydown on document', function() {
// Character '[' has keyCode 219
$(document).trigger($.Event('keydown', {keyCode: 219, ctrlKey: true, shiftKey: true}));
expect(this.toggleNotes.toggleHandler).toHaveBeenCalled();

View File

@@ -1,7 +1,7 @@
define([
'jquery',
'underscore',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'edx-ui-toolkit/js/utils/html-utils',
'js/edxnotes/views/search_box',
'js/edxnotes/collections/notes',

View File

@@ -1,5 +1,5 @@
define([
'jquery', 'common/js/spec_helpers/template_helpers', 'common/js/spec_helpers/ajax_helpers',
'jquery', 'common/js/spec_helpers/template_helpers', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'js/edxnotes/collections/notes', 'js/edxnotes/collections/tabs', 'js/edxnotes/views/tabs/recent_activity',
'js/spec/edxnotes/helpers'
], function(

View File

@@ -1,5 +1,6 @@
define([
'jquery', 'underscore', 'common/js/spec_helpers/template_helpers', 'common/js/spec_helpers/ajax_helpers',
'jquery', 'underscore', 'common/js/spec_helpers/template_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'logger', 'js/edxnotes/collections/tabs', 'js/edxnotes/views/tabs/search_results',
'js/spec/edxnotes/helpers'
], function(

View File

@@ -1,4 +1,5 @@
define(['backbone', 'jquery', 'common/js/spec_helpers/ajax_helpers', 'common/js/spec_helpers/template_helpers',
define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers',
'js/groups/views/cohorts', 'js/groups/collections/cohort', 'js/groups/models/content_group',
'js/groups/models/course_cohort_settings', 'js/utils/animation', 'js/vendor/jquery.qubit',
'js/groups/views/course_cohort_settings_notification', 'js/groups/models/cohort_discussions',

View File

@@ -1,7 +1,7 @@
/*global define, sinon */
define([
'jquery',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'js/certificates/models/certificate_exception',
'js/certificates/views/certificate_whitelist',
'js/certificates/views/certificate_whitelist_editor',

View File

@@ -1,7 +1,7 @@
/*global define */
define([
'jquery',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'js/certificates/models/certificate_invalidation',
'js/certificates/views/certificate_invalidation_view',
'js/certificates/collections/certificate_invalidation_collection'

View File

@@ -1,7 +1,7 @@
/*global define, onCertificatesReady */
define([
'jquery',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'js/instructor_dashboard/certificates'
],
function($, AjaxHelpers) {

View File

@@ -1,5 +1,8 @@
/*global define */
define(['jquery', 'coffee/src/instructor_dashboard/data_download', 'common/js/spec_helpers/ajax_helpers', 'slick.grid'],
define(['jquery',
'coffee/src/instructor_dashboard/data_download',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'slick.grid'],
function ($, DataDownload, AjaxHelpers) {
'use strict';
describe("edx.instructor_dashboard.data_download.DataDownload_Certificate", function() {

View File

@@ -1,4 +1,4 @@
define(['jquery', 'coffee/src/instructor_dashboard/student_admin', 'common/js/spec_helpers/ajax_helpers'],
define(['jquery', 'coffee/src/instructor_dashboard/student_admin', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'],
function ($, StudentAdmin, AjaxHelpers) {
//'coffee/src/instructor_dashboard/student_admin'
'use strict';

View File

@@ -29,14 +29,14 @@ define([
run_key: '2T2016',
course_started: true,
is_enrolled: true,
certificate_url: '',
certificate_url: ''
}]
},
setupView = function(isEnrolled){
setupView = function(data, isEnrolled){
context.run_modes[0].is_enrolled = isEnrolled;
setFixtures('<div class="course-card card"></div>');
courseCardModel = new CourseCardModel(context);
courseCardModel = new CourseCardModel(data);
view = new CourseCardView({
model: courseCardModel
});
@@ -54,7 +54,7 @@ define([
};
beforeEach(function() {
setupView(false);
setupView(context, false);
});
afterEach(function() {
@@ -67,7 +67,7 @@ define([
it('should render the course card based on the data enrolled', function() {
view.remove();
setupView(true);
setupView(context, true);
validateCourseInfoDisplay();
});
@@ -75,12 +75,25 @@ define([
validateCourseInfoDisplay();
});
it('should update render if the course card is_enrolled updated', function(){
it('should update render if the course card is_enrolled updated', function() {
courseCardModel.set({
is_enrolled: true
});
validateCourseInfoDisplay();
});
it('should only show certificate status section if a certificate has been earned', function() {
var data = context,
certUrl = 'sample-certificate';
setupView(context, false);
expect(view.$('certificate-status').length).toEqual(0);
view.remove();
data.run_modes[0].certificate_url = certUrl;
setupView(data, false);
expect(view.$('.certificate-status').length).toEqual(1);
expect(view.$('.certificate-status .cta-secondary').attr('href')).toEqual(certUrl);
});
});
}
);

View File

@@ -28,7 +28,7 @@ define([
modified: '2016-03-25T13:45:21.220732Z',
marketing_slug: 'p_2?param=haha&test=b',
id: 146,
marketing_url: 'http://www.edx.org/xseries/p_2?param=haha&test=b',
detail_url: 'http://courses.edx.org/dashboard/programs/1/foo',
banner_image_urls: {
w348h116: 'http://www.edx.org/images/test1',
w435h145: 'http://www.edx.org/images/test2',
@@ -55,7 +55,7 @@ define([
expect($card.find('.title').html().trim()).toEqual(program.name);
expect($card.find('.category span').html().trim()).toEqual('XSeries Program');
expect($card.find('.organization').html().trim()).toEqual(program.organizations[0].key);
expect($card.find('.card-link').attr('href')).toEqual(program.marketing_url);
expect($card.find('.card-link').attr('href')).toEqual(program.detail_url);
};
beforeEach(function() {

View File

@@ -2,7 +2,7 @@ define([
'jquery',
'backbone',
'logger',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/page_helpers',
'common/js/spec_helpers/template_helpers',
'js/search/base/models/search_result',

View File

@@ -1,4 +1,4 @@
define(['common/js/spec_helpers/ajax_helpers', 'js/shoppingcart/shoppingcart'],
define(['edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'js/shoppingcart/shoppingcart'],
function(AjaxHelpers) {
'use strict';

View File

@@ -2,7 +2,7 @@ define([
'backbone',
'jquery',
'js/staff_debug_actions',
'common/js/spec_helpers/ajax_helpers'
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
],
function (Backbone, $, tmp, AjaxHelpers) {
'use strict';

View File

@@ -5,7 +5,7 @@
'underscore',
'backbone',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/ajax_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'js/student_account/views/AccessView',
'js/student_account/views/FormView',
'js/student_account/enrollment',

View File

@@ -1,4 +1,8 @@
define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers', 'common/js/spec_helpers/template_helpers',
define(['backbone',
'jquery',
'underscore',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers',
'js/spec/views/fields_helpers',
'js/spec/student_account/helpers',
'js/spec/student_account/account_settings_fields_helpers',

View File

@@ -1,4 +1,8 @@
define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers', 'common/js/spec_helpers/template_helpers',
define(['backbone',
'jquery',
'underscore',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers',
'js/spec/views/fields_helpers',
'string_utils'],
function (Backbone, $, _, AjaxHelpers, TemplateHelpers, FieldViewsSpecHelpers) {

View File

@@ -1,4 +1,8 @@
define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers', 'common/js/spec_helpers/template_helpers',
define(['backbone',
'jquery',
'underscore',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers',
'js/views/fields',
'js/spec/views/fields_helpers',
'js/spec/student_account/account_settings_fields_helpers',

View File

@@ -1,4 +1,8 @@
define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers', 'common/js/spec_helpers/template_helpers',
define(['backbone',
'jquery',
'underscore',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers',
'js/spec/student_account/helpers',
'js/views/fields',
'js/student_account/models/user_account_model',

View File

@@ -1,4 +1,4 @@
define(['common/js/spec_helpers/ajax_helpers', 'js/student_account/emailoptin'],
define(['edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'js/student_account/emailoptin'],
function( AjaxHelpers, EmailOptInInterface ) {
'use strict';

Some files were not shown because too many files have changed in this diff Show More