Merge pull request #14350 from edx/release-candidate
Merge release-candidate to release
This commit is contained in:
@@ -981,6 +981,7 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
|
||||
"release_date": release_date,
|
||||
"visibility_state": visibility_state,
|
||||
"has_explicit_staff_lock": xblock.fields['visible_to_staff_only'].is_set_on(xblock),
|
||||
"self_paced": is_self_paced(course),
|
||||
"start": xblock.fields['start'].to_json(xblock.start),
|
||||
"graded": xblock.graded,
|
||||
"due_date": get_default_time_display(xblock.due),
|
||||
|
||||
@@ -712,7 +712,10 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
return $.extend(
|
||||
{},
|
||||
AbstractVisibilityEditor.prototype.getContext.call(this),
|
||||
{hide_after_due: this.modelVisibility() === 'hide_after_due'}
|
||||
{
|
||||
hide_after_due: this.modelVisibility() === 'hide_after_due',
|
||||
self_paced: this.model.get('self_paced') === true
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -12,9 +12,19 @@
|
||||
<li class="field-radio">
|
||||
<label class="label">
|
||||
<input class="input input-radio" name="content-visibility" type="radio" value="hide_after_due" aria-described-by="hide_after_due_description">
|
||||
<%- gettext('Hide content after due date') %>
|
||||
<% if (self_paced) { %>
|
||||
<%- gettext('Hide content after course end date') %>
|
||||
<% } else { %>
|
||||
<%- gettext('Hide content after due date') %>
|
||||
<% } %>
|
||||
</label>
|
||||
<p class='field-message' id='hide_after_due_description'> <%- gettext('After the subsection\'s due date has passed, learners can no longer access its content. The subsection remains included in grade calculations.') %> </p>
|
||||
<p class='field-message' id='hide_after_due_description'>
|
||||
<% if (self_paced) { %>
|
||||
<%- gettext('After the course\'s end date has passed, learners can no longer access subsection content. The subsection remains included in grade calculations.') %>
|
||||
<% } else { %>
|
||||
<%- gettext('After the subsection\'s due date has passed, learners can no longer access its content. The subsection remains included in grade calculations.') %>
|
||||
<% } %>
|
||||
</p>
|
||||
</li>
|
||||
<li class="field-radio">
|
||||
<label class="label">
|
||||
@@ -29,7 +39,7 @@
|
||||
<% if (hasExplicitStaffLock && !ancestorLocked) { %>
|
||||
<p class="tip tip-warning">
|
||||
<%- interpolate(
|
||||
gettext('If you select an option other than "%(hide_label)s", after the subsection release date has passed, published units in this subsection will become available to learners unless units are explicitly hidden.'),
|
||||
gettext('If you select an option other than "%(hide_label)s", published units in this subsection become available to learners unless they are explicitly hidden.'),
|
||||
{ hide_label: hide_label },
|
||||
true
|
||||
) %>
|
||||
|
||||
@@ -201,7 +201,12 @@ if (is_proctored_exam) {
|
||||
<p>
|
||||
<% if (xblockInfo.get('hide_after_due')) { %>
|
||||
<span class="icon fa fa-eye-slash" aria-hidden="true"></span>
|
||||
<span class="status-hide-after-due-value"> <%- gettext("Subsection is hidden after due date") %> </span>
|
||||
<span class="status-hide-after-due-value">
|
||||
<% if (course.get('self_paced')) { %>
|
||||
<%- gettext("Subsection is hidden after course end date") %> </span>
|
||||
<% } else { %>
|
||||
<%- gettext("Subsection is hidden after due date") %> </span>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -236,56 +236,26 @@ describe 'Problem', ->
|
||||
expect(@problem.el).toHaveHtml contents
|
||||
expect(window.SR.readTexts).toHaveBeenCalledWith ['no, try again']
|
||||
|
||||
it 'tests if all the capa buttons are disabled while submitting', (done)->
|
||||
deferred = $.Deferred()
|
||||
it 'tests if the submit button is disabled while submitting and the text changes on the button', ->
|
||||
self = this
|
||||
|
||||
runs = ->
|
||||
spyOn($, 'postWithPrefix').and.callFake (url, answers, callback) ->
|
||||
promise = undefined
|
||||
callback
|
||||
success: 'incorrect'
|
||||
contents: 'Incorrect'
|
||||
promise =
|
||||
always: (callable) ->
|
||||
callable()
|
||||
done: (callable) ->
|
||||
callable()
|
||||
spyOn @problem, 'enableAllButtons'
|
||||
@problem.submit()
|
||||
expect(@problem.enableAllButtons).toHaveBeenCalledWith false, true
|
||||
if jQuery.active == 0
|
||||
deferred.resolve()
|
||||
deferred.promise()
|
||||
|
||||
runs.call(self).then(->
|
||||
expect(self.problem.enableAllButtons).toHaveBeenCalledWith true, true
|
||||
return
|
||||
).always done
|
||||
|
||||
it 'tests the expected change in text of submit button', (done) ->
|
||||
deferred = $.Deferred()
|
||||
self = this
|
||||
|
||||
runs = ->
|
||||
spyOn($, 'postWithPrefix').and.callFake (url, answers, callback) ->
|
||||
promise = undefined
|
||||
promise =
|
||||
always: (callable) ->
|
||||
callable()
|
||||
done: (callable) ->
|
||||
callable()
|
||||
spyOn @problem.submitButtonLabel, 'text'
|
||||
@problem.submit()
|
||||
expect(@problem.submitButtonLabel.text).toHaveBeenCalledWith 'Submitting'
|
||||
if jQuery.active == 0
|
||||
deferred.resolve()
|
||||
deferred.promise()
|
||||
|
||||
runs.call(self).then(->
|
||||
expect(self.problem.submitButtonLabel.text).toHaveBeenCalledWith 'Submit'
|
||||
return
|
||||
).always done
|
||||
curr_html = @problem.el.html()
|
||||
spyOn($, 'postWithPrefix').and.callFake (url, answers, callback) ->
|
||||
# At this point enableButtons should have been called, making the submit button disabled with text 'submitting'
|
||||
expect(self.problem.submitButton).toHaveAttr('disabled');
|
||||
expect(self.problem.submitButtonLabel.text()).toBe('Submitting');
|
||||
callback
|
||||
success: 'incorrect' # does not matter if correct or incorrect here
|
||||
contents: curr_html
|
||||
promise =
|
||||
always: (callable) -> callable()
|
||||
done: (callable) -> callable()
|
||||
# Make sure the submit button is enabled before submitting
|
||||
$('#input_example_1').val('test').trigger('input')
|
||||
expect(@problem.submitButton).not.toHaveAttr('disabled')
|
||||
@problem.submit()
|
||||
# After submit, the button should not be disabled and should have text as 'Submit'
|
||||
expect(@problem.submitButtonLabel.text()).toBe('Submit')
|
||||
expect(@problem.submitButton).not.toHaveAttr('disabled')
|
||||
|
||||
describe 'submit button on problems', ->
|
||||
beforeEach ->
|
||||
@@ -424,27 +394,22 @@ describe 'Problem', ->
|
||||
@problem.reset()
|
||||
expect($('.notification-gentle-alert .notification-message').text()).toEqual("Error on reset.")
|
||||
|
||||
it 'tests if all the buttons are disabled and the text of submit button remains same while resetting', (done) ->
|
||||
deferred = $.Deferred()
|
||||
it 'tests that reset does not enable submit or modify the text while resetting', ->
|
||||
self = this
|
||||
|
||||
runs = ->
|
||||
spyOn($, 'postWithPrefix').and.callFake (url, answers, callback) ->
|
||||
promise = undefined
|
||||
promise = always: (callable) ->
|
||||
callable()
|
||||
spyOn @problem, 'enableAllButtons'
|
||||
@problem.reset()
|
||||
expect(@problem.enableAllButtons).toHaveBeenCalledWith false, false
|
||||
expect(@problem.submitButtonLabel).toHaveText 'Submit'
|
||||
if jQuery.active == 0
|
||||
deferred.resolve()
|
||||
deferred.promise()
|
||||
|
||||
runs.call(self).then(->
|
||||
expect(self.problem.enableAllButtons).toHaveBeenCalledWith true, false
|
||||
expect(self.problem.submitButtonLabel).toHaveText 'Submit'
|
||||
).always done
|
||||
curr_html = @problem.el.html()
|
||||
spyOn($, 'postWithPrefix').and.callFake (url, answers, callback) ->
|
||||
# enableButtons should have been called at this point to set them to all disabled
|
||||
expect(self.problem.submitButton).toHaveAttr('disabled')
|
||||
expect(self.problem.submitButtonLabel.text()).toBe('Submit')
|
||||
callback(success: 'correct', html: curr_html)
|
||||
promise =
|
||||
always: (callable) -> callable()
|
||||
# Submit should be disabled
|
||||
expect(@problem.submitButton).toHaveAttr('disabled')
|
||||
@problem.reset()
|
||||
# Submit should remain disabled
|
||||
expect(self.problem.submitButton).toHaveAttr('disabled')
|
||||
expect(self.problem.submitButtonLabel.text()).toBe('Submit')
|
||||
|
||||
describe 'show', ->
|
||||
beforeEach ->
|
||||
@@ -483,22 +448,14 @@ describe 'Problem', ->
|
||||
@problem.show()
|
||||
expect(@problem.el.find('.show').attr('disabled')).toEqual('disabled')
|
||||
|
||||
it 'sends a SR message when answer is present', (done) ->
|
||||
deferred = $.Deferred()
|
||||
it 'sends a SR message when answer is present', ->
|
||||
|
||||
runs = ->
|
||||
spyOn($, 'postWithPrefix').and.callFake (url, callback) ->
|
||||
callback answers:
|
||||
'1_1': 'answers'
|
||||
@problem.show()
|
||||
if jQuery.active == 0
|
||||
deferred.resolve()
|
||||
deferred.promise()
|
||||
spyOn($, 'postWithPrefix').and.callFake (url, callback) ->
|
||||
callback answers:
|
||||
'1_1': 'answers'
|
||||
@problem.show()
|
||||
|
||||
runs.call(this).then(->
|
||||
expect(window.SR.readText).toHaveBeenCalledWith 'Answers to this problem are now shown. Navigate through the problem to review it with answers inline.'
|
||||
return
|
||||
).always done
|
||||
expect(window.SR.readText).toHaveBeenCalledWith 'Answers to this problem are now shown. Navigate through the problem to review it with answers inline.'
|
||||
|
||||
describe 'multiple choice question', ->
|
||||
beforeEach ->
|
||||
@@ -723,28 +680,42 @@ describe 'Problem', ->
|
||||
expect($.postWithPrefix).toHaveBeenCalledWith '/problem/Problem1/problem_save',
|
||||
'foo=1&bar=2', jasmine.any(Function)
|
||||
|
||||
it 'tests if all the buttons are disabled and the text of submit button does not change while saving.', (done) ->
|
||||
deferred = $.Deferred()
|
||||
it 'tests that save does not enable the submit button or change the text when submit is originally disabled', ->
|
||||
self = this
|
||||
curr_html = @problem.el.html()
|
||||
runs = ->
|
||||
spyOn($, 'postWithPrefix').and.callFake (url, answers, callback) ->
|
||||
promise = undefined
|
||||
callback(success: 'correct', html: curr_html)
|
||||
promise = always: (callable) ->
|
||||
callable()
|
||||
spyOn @problem, 'enableAllButtons'
|
||||
@problem.save()
|
||||
expect(@problem.enableAllButtons).toHaveBeenCalledWith false, false
|
||||
expect(@problem.submitButtonLabel).toHaveText 'Submit'
|
||||
if jQuery.active == 0
|
||||
deferred.resolve()
|
||||
deferred.promise()
|
||||
spyOn($, 'postWithPrefix').and.callFake (url, answers, callback) ->
|
||||
# enableButtons should have been called at this point and the submit button should be unaffected
|
||||
expect(self.problem.submitButton).toHaveAttr('disabled')
|
||||
expect(self.problem.submitButtonLabel.text()).toBe('Submit')
|
||||
callback(success: 'correct', html: curr_html)
|
||||
promise =
|
||||
always: (callable) -> callable()
|
||||
# Expect submit to be disabled and labeled properly at the start
|
||||
expect(@problem.submitButton).toHaveAttr('disabled')
|
||||
expect(@problem.submitButtonLabel.text()).toBe('Submit')
|
||||
@problem.save()
|
||||
# Submit button should have the same state after save has completed
|
||||
expect(@problem.submitButton).toHaveAttr('disabled')
|
||||
expect(@problem.submitButtonLabel.text()).toBe('Submit')
|
||||
|
||||
runs.call(self).then(->
|
||||
expect(self.problem.enableAllButtons).toHaveBeenCalledWith true, false
|
||||
expect(self.problem.submitButtonLabel).toHaveText 'Submit'
|
||||
).always done
|
||||
it 'tests that save does not disable the submit button or change the text when submit is originally enabled', ->
|
||||
self = this
|
||||
curr_html = @problem.el.html()
|
||||
spyOn($, 'postWithPrefix').and.callFake (url, answers, callback) ->
|
||||
# enableButtons should have been called at this point, and the submit button should be disabled while submitting
|
||||
expect(self.problem.submitButton).toHaveAttr('disabled')
|
||||
expect(self.problem.submitButtonLabel.text()).toBe('Submit')
|
||||
callback(success: 'correct', html: curr_html)
|
||||
promise =
|
||||
always: (callable) -> callable()
|
||||
# Expect submit to be enabled and labeled properly at the start after adding an input
|
||||
$('#input_example_1').val('test').trigger('input')
|
||||
expect(@problem.submitButton).not.toHaveAttr('disabled')
|
||||
expect(@problem.submitButtonLabel.text()).toBe('Submit')
|
||||
@problem.save()
|
||||
# Submit button should have the same state after save has completed
|
||||
expect(@problem.submitButton).not.toHaveAttr('disabled')
|
||||
expect(@problem.submitButtonLabel.text()).toBe('Submit')
|
||||
|
||||
describe 'refreshMath', ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -36,9 +36,6 @@
|
||||
}
|
||||
return Problem.prototype.enableSubmitButton.apply(that, arguments);
|
||||
};
|
||||
this.enableAllButtons = function(enable, isFromCheckOperation) { // eslint-disable-line no-unused-vars
|
||||
return Problem.prototype.enableAllButtons.apply(that, arguments);
|
||||
};
|
||||
this.disableAllButtonsWhileRunning = function(
|
||||
operationCallback, isFromCheckOperation // eslint-disable-line no-unused-vars
|
||||
) {
|
||||
@@ -1157,32 +1154,38 @@
|
||||
*/
|
||||
Problem.prototype.disableAllButtonsWhileRunning = function(operationCallback, isFromCheckOperation) {
|
||||
var that = this;
|
||||
this.enableAllButtons(false, isFromCheckOperation);
|
||||
var allButtons = [this.resetButton, this.saveButton, this.showButton, this.hintButton, this.submitButton];
|
||||
var initiallyEnabledButtons = allButtons.filter(function(button) {
|
||||
return !button.attr('disabled');
|
||||
});
|
||||
this.enableButtons(initiallyEnabledButtons, false, isFromCheckOperation);
|
||||
return operationCallback().always(function() {
|
||||
return that.enableAllButtons(true, isFromCheckOperation);
|
||||
return that.enableButtons(initiallyEnabledButtons, true, isFromCheckOperation);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to enable/disable all buttons in problem.
|
||||
* Enables/disables buttons by removing/adding the disabled attribute. The submit button is checked
|
||||
* separately due to the changing text it contains.
|
||||
*
|
||||
* params:
|
||||
* 'enable' is a boolean to determine enabling/disabling of buttons.
|
||||
* 'isFromCheckOperation' is a boolean to keep track if operation was initiated
|
||||
* 'buttons' is an array of buttons that will have their 'disabled' attribute modified
|
||||
* 'enable' a boolean to either enable or disable the buttons passed in the first parameter
|
||||
* 'changeSubmitButtonText' is a boolean to keep track if operation was initiated
|
||||
* from submit so that text of submit button will also be changed while disabling/enabling
|
||||
* the submit button.
|
||||
*/
|
||||
Problem.prototype.enableAllButtons = function(enable, isFromCheckOperation) {
|
||||
// Called by disableAllButtonsWhileRunning to automatically disable all buttons while check,reset, or
|
||||
// save internal are running. Then enable all the buttons again after it is done.
|
||||
if (enable) {
|
||||
this.resetButton.add(this.saveButton).add(this.hintButton).add(this.showButton).
|
||||
removeAttr('disabled');
|
||||
} else {
|
||||
this.resetButton.add(this.saveButton).add(this.hintButton).add(this.showButton).
|
||||
attr({disabled: 'disabled'});
|
||||
}
|
||||
return this.enableSubmitButton(enable, isFromCheckOperation);
|
||||
Problem.prototype.enableButtons = function(buttons, enable, changeSubmitButtonText) {
|
||||
var that = this;
|
||||
buttons.forEach(function(button) {
|
||||
if (button.hasClass('submit')) {
|
||||
that.enableSubmitButton(enable, changeSubmitButtonText);
|
||||
} else if (enable) {
|
||||
button.removeAttr('disabled');
|
||||
} else {
|
||||
button.attr({disabled: 'disabled'});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -202,16 +202,16 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
raise NotFoundError('Unexpected dispatch type')
|
||||
|
||||
@classmethod
|
||||
def verify_current_content_visibility(cls, due, hide_after_due):
|
||||
def verify_current_content_visibility(cls, date, hide_after_date):
|
||||
"""
|
||||
Returns whether the content visibility policy passes
|
||||
for the given due date and hide_after_due values and
|
||||
for the given date and hide_after_date values and
|
||||
the current date-time.
|
||||
"""
|
||||
return (
|
||||
not due or
|
||||
not hide_after_due or
|
||||
datetime.now(UTC()) < due
|
||||
not date or
|
||||
not hide_after_date or
|
||||
datetime.now(UTC()) < date
|
||||
)
|
||||
|
||||
def student_view(self, context):
|
||||
@@ -246,20 +246,17 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
runtime user. If so, returns a banner_text or the fragment to
|
||||
display depending on whether staff is masquerading.
|
||||
"""
|
||||
if not self._can_user_view_content():
|
||||
subsection_format = (self.format or _("subsection")).lower() # pylint: disable=no-member
|
||||
|
||||
# Translators: subsection_format refers to the assignment
|
||||
# type of the subsection, such as Homework, Lab, Exam, etc.
|
||||
banner_text = _(
|
||||
"Because the due date has passed, "
|
||||
"this {subsection_format} is hidden from the learner."
|
||||
).format(subsection_format=subsection_format)
|
||||
course = self._get_course()
|
||||
if not self._can_user_view_content(course):
|
||||
if course.self_paced:
|
||||
banner_text = _("Because the course has ended, this assignment is hidden from the learner.")
|
||||
else:
|
||||
banner_text = _("Because the due date has passed, this assignment is hidden from the learner.")
|
||||
|
||||
hidden_content_html = self.system.render_template(
|
||||
'hidden_content.html',
|
||||
{
|
||||
'subsection_format': subsection_format,
|
||||
'self_paced': course.self_paced,
|
||||
'progress_url': context.get('progress_url'),
|
||||
}
|
||||
)
|
||||
@@ -280,14 +277,15 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
if content_milestones and self.runtime.user_is_staff:
|
||||
return banner_text
|
||||
|
||||
def _can_user_view_content(self):
|
||||
def _can_user_view_content(self, course):
|
||||
"""
|
||||
Returns whether the runtime user can view the content
|
||||
of this sequential.
|
||||
"""
|
||||
hidden_date = course.end if course.self_paced else self.due
|
||||
return (
|
||||
self.runtime.user_is_staff or
|
||||
self.verify_current_content_visibility(self.due, self.hide_after_due)
|
||||
self.verify_current_content_visibility(hidden_date, self.hide_after_due)
|
||||
)
|
||||
|
||||
def _student_view(self, context, banner_text=None):
|
||||
|
||||
@@ -3,49 +3,46 @@ Tests for sequence module.
|
||||
"""
|
||||
# pylint: disable=no-member
|
||||
from datetime import timedelta
|
||||
import ddt
|
||||
from django.utils.timezone import now
|
||||
from freezegun import freeze_time
|
||||
from mock import Mock
|
||||
from mock import Mock, patch
|
||||
from xmodule.seq_module import SequenceModule
|
||||
from xmodule.tests import get_test_system
|
||||
from xmodule.tests.helpers import StubUserService
|
||||
from xmodule.tests.xml import XModuleXmlImportTest
|
||||
from xmodule.tests.xml import factories as xml
|
||||
from xmodule.tests.xml import factories as xml, XModuleXmlImportTest
|
||||
from xmodule.x_module import STUDENT_VIEW
|
||||
from xmodule.seq_module import SequenceModule
|
||||
|
||||
TODAY = now()
|
||||
DUE_DATE = TODAY + timedelta(days=7)
|
||||
PAST_DUE_BEFORE_END_DATE = TODAY + timedelta(days=14)
|
||||
COURSE_END_DATE = TODAY + timedelta(days=21)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
"""
|
||||
Tests for the Sequence Module.
|
||||
Base class for tests of Sequence Module.
|
||||
"""
|
||||
TODAY = now()
|
||||
TOMORROW = TODAY + timedelta(days=1)
|
||||
DAY_AFTER_TOMORROW = TOMORROW + timedelta(days=1)
|
||||
def setUp(self):
|
||||
super(SequenceBlockTestCase, self).setUp()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(SequenceBlockTestCase, cls).setUpClass()
|
||||
course_xml = self._set_up_course_xml()
|
||||
self.course = self.process_xml(course_xml)
|
||||
self._set_up_module_system(self.course)
|
||||
|
||||
course_xml = cls._set_up_course_xml()
|
||||
cls.course = cls.process_xml(course_xml)
|
||||
cls._set_up_module_system(cls.course)
|
||||
|
||||
for chapter_index in range(len(cls.course.get_children())):
|
||||
chapter = cls._set_up_block(cls.course, chapter_index)
|
||||
setattr(cls, 'chapter_{}'.format(chapter_index + 1), chapter)
|
||||
for chapter_index in range(len(self.course.get_children())):
|
||||
chapter = self._set_up_block(self.course, chapter_index)
|
||||
setattr(self, 'chapter_{}'.format(chapter_index + 1), chapter)
|
||||
|
||||
for sequence_index in range(len(chapter.get_children())):
|
||||
sequence = cls._set_up_block(chapter, sequence_index)
|
||||
setattr(cls, 'sequence_{}_{}'.format(chapter_index + 1, sequence_index + 1), sequence)
|
||||
sequence = self._set_up_block(chapter, sequence_index)
|
||||
setattr(self, 'sequence_{}_{}'.format(chapter_index + 1, sequence_index + 1), sequence)
|
||||
|
||||
@classmethod
|
||||
def _set_up_course_xml(cls):
|
||||
@staticmethod
|
||||
def _set_up_course_xml():
|
||||
"""
|
||||
Sets up and returns XML course structure.
|
||||
"""
|
||||
course = xml.CourseFactory.build()
|
||||
course = xml.CourseFactory.build(end=str(COURSE_END_DATE))
|
||||
|
||||
chapter_1 = xml.ChapterFactory.build(parent=course) # has 2 child sequences
|
||||
xml.ChapterFactory.build(parent=course) # has 0 child sequences
|
||||
@@ -58,7 +55,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
xml.SequenceFactory.build( # sequence_4_1
|
||||
parent=chapter_4,
|
||||
hide_after_due=str(True),
|
||||
due=str(cls.TOMORROW),
|
||||
due=str(DUE_DATE),
|
||||
)
|
||||
|
||||
for _ in range(3):
|
||||
@@ -66,14 +63,13 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
|
||||
return course
|
||||
|
||||
@classmethod
|
||||
def _set_up_block(cls, parent, index_in_parent):
|
||||
def _set_up_block(self, parent, index_in_parent):
|
||||
"""
|
||||
Sets up the stub sequence module for testing.
|
||||
"""
|
||||
block = parent.get_children()[index_in_parent]
|
||||
|
||||
cls._set_up_module_system(block)
|
||||
self._set_up_module_system(block)
|
||||
|
||||
block.xmodule_runtime._services['bookmarks'] = Mock() # pylint: disable=protected-access
|
||||
block.xmodule_runtime._services['user'] = StubUserService() # pylint: disable=protected-access
|
||||
@@ -81,8 +77,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
block.parent = parent.location
|
||||
return block
|
||||
|
||||
@classmethod
|
||||
def _set_up_module_system(cls, block):
|
||||
def _set_up_module_system(self, block):
|
||||
"""
|
||||
Sets up the test module system for the given block.
|
||||
"""
|
||||
@@ -90,6 +85,28 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
module_system.descriptor_runtime = block._runtime # pylint: disable=protected-access
|
||||
block.xmodule_runtime = module_system
|
||||
|
||||
def _get_rendered_student_view(self, sequence, requested_child=None, extra_context=None, self_paced=False):
|
||||
"""
|
||||
Returns the rendered student view for the given sequence and the
|
||||
requested_child parameter.
|
||||
"""
|
||||
context = {'requested_child': requested_child}
|
||||
if extra_context:
|
||||
context.update(extra_context)
|
||||
|
||||
# The render operation will ask modulestore for the current course to get some data. As these tests were
|
||||
# originally not written to be compatible with a real modulestore, we've mocked out the relevant return values.
|
||||
with patch.object(SequenceModule, '_get_course') as mock_course:
|
||||
self.course.self_paced = self_paced
|
||||
mock_course.return_value = self.course
|
||||
return sequence.xmodule_runtime.render(sequence, STUDENT_VIEW, context).content
|
||||
|
||||
def _assert_view_at_position(self, rendered_html, expected_position):
|
||||
"""
|
||||
Verifies that the rendered view contains the expected position.
|
||||
"""
|
||||
self.assertIn("'position': {}".format(expected_position), rendered_html)
|
||||
|
||||
def test_student_view_init(self):
|
||||
seq_module = SequenceModule(runtime=Mock(position=2), descriptor=Mock(), scope_ids=Mock())
|
||||
self.assertEquals(seq_module.position, 2) # matches position set in the runtime
|
||||
@@ -112,22 +129,6 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
html = self._get_rendered_student_view(self.sequence_3_1, requested_child='last')
|
||||
self._assert_view_at_position(html, expected_position=3)
|
||||
|
||||
def _get_rendered_student_view(self, sequence, requested_child=None, extra_context=None):
|
||||
"""
|
||||
Returns the rendered student view for the given sequence and the
|
||||
requested_child parameter.
|
||||
"""
|
||||
context = {'requested_child': requested_child}
|
||||
if extra_context:
|
||||
context.update(extra_context)
|
||||
return sequence.xmodule_runtime.render(sequence, STUDENT_VIEW, context).content
|
||||
|
||||
def _assert_view_at_position(self, rendered_html, expected_position):
|
||||
"""
|
||||
Verifies that the rendered view contains the expected position.
|
||||
"""
|
||||
self.assertIn("'position': {}".format(expected_position), rendered_html)
|
||||
|
||||
def test_tooltip(self):
|
||||
html = self._get_rendered_student_view(self.sequence_3_1, requested_child=None)
|
||||
for child in self.sequence_3_1.children:
|
||||
@@ -138,26 +139,18 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
self.assertIn("seq_module.html", html)
|
||||
self.assertIn("'banner_text': None", html)
|
||||
|
||||
@freeze_time(DAY_AFTER_TOMORROW)
|
||||
@ddt.data(
|
||||
(None, 'subsection'),
|
||||
('Homework', 'homework'),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_hidden_content_past_due(self, format_type, expected_text):
|
||||
@freeze_time(COURSE_END_DATE)
|
||||
def test_hidden_content_past_due(self):
|
||||
progress_url = 'http://test_progress_link'
|
||||
self._set_sequence_format(self.sequence_4_1, format_type)
|
||||
html = self._get_rendered_student_view(
|
||||
self.sequence_4_1,
|
||||
extra_context=dict(progress_url=progress_url),
|
||||
)
|
||||
self.assertIn("hidden_content.html", html)
|
||||
self.assertIn(progress_url, html)
|
||||
self.assertIn("'subsection_format': '{}'".format(expected_text), html)
|
||||
|
||||
@freeze_time(DAY_AFTER_TOMORROW)
|
||||
@freeze_time(COURSE_END_DATE)
|
||||
def test_masquerade_hidden_content_past_due(self):
|
||||
self._set_sequence_format(self.sequence_4_1, "Homework")
|
||||
html = self._get_rendered_student_view(
|
||||
self.sequence_4_1,
|
||||
extra_context=dict(specific_masquerade=True),
|
||||
@@ -165,13 +158,23 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
self.assertIn("seq_module.html", html)
|
||||
self.assertIn(
|
||||
"'banner_text': 'Because the due date has passed, "
|
||||
"this homework is hidden from the learner.'",
|
||||
"this assignment is hidden from the learner.'",
|
||||
html
|
||||
)
|
||||
|
||||
def _set_sequence_format(self, sequence, format_type):
|
||||
"""
|
||||
Sets the format field on the given sequence to the
|
||||
given value.
|
||||
"""
|
||||
sequence._xmodule.format = format_type # pylint: disable=protected-access
|
||||
@freeze_time(PAST_DUE_BEFORE_END_DATE)
|
||||
def test_hidden_content_self_paced_past_due_before_end(self):
|
||||
html = self._get_rendered_student_view(self.sequence_4_1, self_paced=True)
|
||||
self.assertIn("seq_module.html", html)
|
||||
self.assertIn("'banner_text': None", html)
|
||||
|
||||
@freeze_time(COURSE_END_DATE + timedelta(days=7))
|
||||
def test_hidden_content_self_paced_past_end(self):
|
||||
progress_url = 'http://test_progress_link'
|
||||
html = self._get_rendered_student_view(
|
||||
self.sequence_4_1,
|
||||
extra_context=dict(progress_url=progress_url),
|
||||
self_paced=True,
|
||||
)
|
||||
self.assertIn("hidden_content.html", html)
|
||||
self.assertIn(progress_url, html)
|
||||
|
||||
@@ -209,13 +209,13 @@ class CoursewarePage(CoursePage):
|
||||
"""
|
||||
return self.q(css="div.proctored-exam.completed").visible
|
||||
|
||||
def content_hidden_past_due_date(self, content_type="subsection"):
|
||||
def content_hidden_past_due_date(self):
|
||||
"""
|
||||
Returns whether the "the due date for this ___ has passed" message is present.
|
||||
___ is the type of the hidden content, and defaults to subsection.
|
||||
This being true implies "the ___ contents are hidden because their due date has passed".
|
||||
"""
|
||||
message = "The due date for this {0} has passed.".format(content_type)
|
||||
message = "this assignment is no longer available"
|
||||
if self.q(css="div.seq_content").is_present():
|
||||
return False
|
||||
for html in self.q(css="div.hidden-content").html:
|
||||
|
||||
@@ -25,7 +25,7 @@ class HiddenContentTransformer(FilteringTransformerMixin, BlockStructureTransfor
|
||||
|
||||
Staff users are exempted from hidden content rules.
|
||||
"""
|
||||
VERSION = 1
|
||||
VERSION = 2
|
||||
MERGED_DUE_DATE = 'merged_due_date'
|
||||
MERGED_HIDE_AFTER_DUE = 'merged_hide_after_due'
|
||||
|
||||
@@ -41,7 +41,7 @@ class HiddenContentTransformer(FilteringTransformerMixin, BlockStructureTransfor
|
||||
def _get_merged_hide_after_due(cls, block_structure, block_key):
|
||||
"""
|
||||
Returns whether the block with the given block_key in the
|
||||
given block_structure should be visible to staff only per
|
||||
given block_structure should be hidden after due date per
|
||||
computed value from ancestry chain.
|
||||
"""
|
||||
return block_structure.get_transformer_block_field(
|
||||
@@ -81,6 +81,8 @@ class HiddenContentTransformer(FilteringTransformerMixin, BlockStructureTransfor
|
||||
func_merge_ancestors=min,
|
||||
)
|
||||
|
||||
block_structure.request_xblock_fields(u'self_paced', u'end')
|
||||
|
||||
def transform_block_filters(self, usage_info, block_structure):
|
||||
# Users with staff access bypass the Visibility check.
|
||||
if usage_info.has_staff_access:
|
||||
@@ -97,6 +99,10 @@ class HiddenContentTransformer(FilteringTransformerMixin, BlockStructureTransfor
|
||||
Returns whether the block with the given block_key should
|
||||
be hidden, given the current time.
|
||||
"""
|
||||
due = self._get_merged_due_date(block_structure, block_key)
|
||||
hide_after_due = self._get_merged_hide_after_due(block_structure, block_key)
|
||||
return not SequenceModule.verify_current_content_visibility(due, hide_after_due)
|
||||
self_paced = block_structure[block_structure.root_block_usage_key].self_paced
|
||||
if self_paced:
|
||||
hidden_date = block_structure[block_structure.root_block_usage_key].end
|
||||
else:
|
||||
hidden_date = self._get_merged_due_date(block_structure, block_key)
|
||||
return not SequenceModule.verify_current_content_visibility(hidden_date, hide_after_due)
|
||||
|
||||
@@ -199,7 +199,7 @@ class IndexQueryTestCase(ModuleStoreTestCase):
|
||||
NUM_PROBLEMS = 20
|
||||
|
||||
@ddt.data(
|
||||
(ModuleStoreEnum.Type.mongo, 8),
|
||||
(ModuleStoreEnum.Type.mongo, 9),
|
||||
(ModuleStoreEnum.Type.split, 4),
|
||||
)
|
||||
@ddt.unpack
|
||||
|
||||
@@ -4,15 +4,14 @@ Grades related signals.
|
||||
|
||||
from logging import getLogger
|
||||
|
||||
from courseware.model_data import get_score, set_score
|
||||
from django.dispatch import receiver
|
||||
from openedx.core.lib.grade_utils import is_score_higher
|
||||
from submissions.models import score_set, score_reset
|
||||
from util.date_utils import to_timestamp
|
||||
|
||||
from courseware.model_data import get_score, set_score
|
||||
from eventtracking import tracker
|
||||
from openedx.core.lib.grade_utils import is_score_higher
|
||||
from student.models import user_by_anonymous_id
|
||||
from util.date_utils import to_timestamp
|
||||
from track.event_transaction_utils import (
|
||||
get_event_transaction_type,
|
||||
get_event_transaction_id,
|
||||
|
||||
@@ -11,6 +11,7 @@ from logging import getLogger
|
||||
|
||||
from courseware.model_data import get_score
|
||||
from lms.djangoapps.course_blocks.api import get_course_blocks
|
||||
from openedx.core.djangoapps.celery_utils.task import PersistOnFailureTask
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
from submissions import api as sub_api
|
||||
@@ -54,7 +55,7 @@ def recalculate_subsection_grade(
|
||||
)
|
||||
|
||||
|
||||
@task(default_retry_delay=30, routing_key=settings.RECALCULATE_GRADES_ROUTING_KEY)
|
||||
@task(base=PersistOnFailureTask, default_retry_delay=30, routing_key=settings.RECALCULATE_GRADES_ROUTING_KEY)
|
||||
def recalculate_subsection_grade_v2(**kwargs):
|
||||
"""
|
||||
Updates a saved subsection grade.
|
||||
|
||||
@@ -2153,6 +2153,10 @@ INSTALLED_APPS = (
|
||||
|
||||
# additional release utilities to ease automation
|
||||
'release_util',
|
||||
|
||||
# Customized celery tasks, including persisting failed tasks so they can
|
||||
# be retried
|
||||
'openedx.core.djangoapps.celery_utils',
|
||||
)
|
||||
|
||||
# Migrations which are not in the standard module "migrations"
|
||||
|
||||
@@ -5,22 +5,35 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
%>
|
||||
|
||||
<div class="sequence hidden-content proctored-exam completed">
|
||||
<h3>
|
||||
${_("The due date for this {subsection_format} has passed.").format(
|
||||
subsection_format=subsection_format,
|
||||
)}
|
||||
</h3>
|
||||
<hr>
|
||||
<p>
|
||||
${Text(_(
|
||||
"Because the due date has passed, this {subsection_format} "
|
||||
"is no longer available.{line_break}If you have completed this {subsection_format}, "
|
||||
"your grade is available on the {link_start}progress page{link_end}."
|
||||
)).format(
|
||||
subsection_format=subsection_format,
|
||||
line_break=HTML("<br>"),
|
||||
link_start=HTML("<a href='{}'>").format(progress_url),
|
||||
link_end=HTML("</a>"),
|
||||
)}
|
||||
</p>
|
||||
<h3>
|
||||
% if self_paced:
|
||||
${_("The course has ended.")}
|
||||
% else:
|
||||
${_("The due date for this assignment has passed.")}
|
||||
% endif
|
||||
</h3>
|
||||
<hr>
|
||||
<p>
|
||||
% if self_paced:
|
||||
${Text(_(
|
||||
"Because the course has ended, this assignment is no longer "
|
||||
"available.{line_break}If you have completed this assignment, your "
|
||||
"grade is available on the {link_start}progress page{link_end}."
|
||||
)).format(
|
||||
line_break=HTML("<br>"),
|
||||
link_start=HTML("<a href='{}'>").format(progress_url),
|
||||
link_end=HTML("</a>"),
|
||||
)}
|
||||
% else:
|
||||
${Text(_(
|
||||
"Because the due date has passed, this assignment is no longer "
|
||||
"available.{line_break}If you have completed this assignment, your "
|
||||
"grade is available on the {link_start}progress page{link_end}."
|
||||
)).format(
|
||||
line_break=HTML("<br>"),
|
||||
link_start=HTML("<a href='{}'>").format(progress_url),
|
||||
link_end=HTML("</a>"),
|
||||
)}
|
||||
% endif
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
#pylint: disable=missing-docstring
|
||||
import unittest
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
|
||||
from openedx.core.djangoapps.api_admin.forms import ApiAccessRequestForm
|
||||
from openedx.core.djangoapps.api_admin.tests.utils import VALID_DATA
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@ddt.ddt
|
||||
class ApiAccessFormTest(TestCase):
|
||||
|
||||
|
||||
@@ -2,21 +2,20 @@
|
||||
from smtplib import SMTPException
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.db import IntegrityError
|
||||
from django.test import TestCase
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from microsite_configuration.tests.factories import SiteFactory
|
||||
from openedx.core.djangoapps.api_admin.models import ApiAccessRequest, ApiAccessConfig
|
||||
from openedx.core.djangoapps.api_admin.models import log as model_log
|
||||
from openedx.core.djangoapps.api_admin.tests.factories import ApiAccessRequestFactory
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class ApiAccessRequestTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@@ -79,7 +78,7 @@ class ApiAccessConfigTests(TestCase):
|
||||
)
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class ApiAccessRequestSignalTests(TestCase):
|
||||
def setUp(self):
|
||||
super(ApiAccessRequestSignalTests, self).setUp()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
""" Tests for the api_admin app's views. """
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
import ddt
|
||||
import httpretty
|
||||
@@ -16,19 +15,26 @@ from openedx.core.djangoapps.api_admin.tests.factories import (
|
||||
ApiAccessRequestFactory, ApplicationFactory, CatalogFactory
|
||||
)
|
||||
from openedx.core.djangoapps.api_admin.tests.utils import VALID_DATA
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
Application = get_application_model() # pylint: disable=invalid-name
|
||||
|
||||
|
||||
class ApiAdminTest(TestCase):
|
||||
"""
|
||||
Base class to allow API admin access to tests.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(ApiAdminTest, self).setUp()
|
||||
ApiAccessConfig(enabled=True).save()
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class ApiRequestViewTest(ApiAdminTest):
|
||||
"""
|
||||
Test the API Request View.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(ApiRequestViewTest, self).setUp()
|
||||
self.url = reverse('api_admin:api-request')
|
||||
@@ -93,10 +99,13 @@ class ApiRequestViewTest(ApiAdminTest):
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@override_settings(PLATFORM_NAME='edX')
|
||||
@ddt.ddt
|
||||
class ApiRequestStatusViewTest(ApiAdminTest):
|
||||
"""
|
||||
Tests of the API Status endpoint.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(ApiRequestStatusViewTest, self).setUp()
|
||||
password = 'abc123'
|
||||
@@ -198,10 +207,15 @@ class ApiRequestStatusViewTest(ApiAdminTest):
|
||||
self.assertIn('Enter a valid URL.', response.content)
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class ApiTosViewTest(ApiAdminTest):
|
||||
"""
|
||||
Tests of the API terms of service endpoint.
|
||||
"""
|
||||
def test_get_api_tos(self):
|
||||
"""Verify that the terms of service can be read."""
|
||||
"""
|
||||
Verify that the terms of service can be read.
|
||||
"""
|
||||
url = reverse('api_admin:api-tos')
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@@ -209,6 +223,9 @@ class ApiTosViewTest(ApiAdminTest):
|
||||
|
||||
|
||||
class CatalogTest(ApiAdminTest):
|
||||
"""
|
||||
Test the catalog API.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(CatalogTest, self).setUp()
|
||||
password = 'abc123'
|
||||
@@ -232,8 +249,11 @@ class CatalogTest(ApiAdminTest):
|
||||
)
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class CatalogSearchViewTest(CatalogTest):
|
||||
"""
|
||||
Test the catalog search endpoint.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(CatalogSearchViewTest, self).setUp()
|
||||
self.url = reverse('api_admin:catalog-search')
|
||||
@@ -254,8 +274,11 @@ class CatalogSearchViewTest(CatalogTest):
|
||||
self.assertRedirects(response, reverse('api_admin:catalog-search'))
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class CatalogListViewTest(CatalogTest):
|
||||
"""
|
||||
Test the catalog list endpoint.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(CatalogListViewTest, self).setUp()
|
||||
self.catalog_user = UserFactory()
|
||||
@@ -304,8 +327,11 @@ class CatalogListViewTest(CatalogTest):
|
||||
self.assertEqual(len([r for r in httpretty.httpretty.latest_requests if r.method == 'POST']), 0)
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class CatalogEditViewTest(CatalogTest):
|
||||
"""
|
||||
Test edits to the catalog endpoint.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(CatalogEditViewTest, self).setUp()
|
||||
self.catalog_user = UserFactory()
|
||||
@@ -353,8 +379,11 @@ class CatalogEditViewTest(CatalogTest):
|
||||
self.assertEqual(len([r for r in httpretty.httpretty.latest_requests if r.method == 'PATCH']), 0)
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class CatalogPreviewViewTest(CatalogTest):
|
||||
"""
|
||||
Test the catalog preview endpoint.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(CatalogPreviewViewTest, self).setUp()
|
||||
self.url = reverse('api_admin:catalog-preview')
|
||||
|
||||
@@ -4,13 +4,13 @@ Tests for bookmarks api.
|
||||
import ddt
|
||||
from mock import patch
|
||||
from nose.plugins.attrib import attr
|
||||
from unittest import skipUnless
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
|
||||
from .. import api
|
||||
@@ -38,7 +38,7 @@ class BookmarkApiEventTestMixin(object):
|
||||
|
||||
@attr(shard=2)
|
||||
@ddt.ddt
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
|
||||
@skip_unless_lms
|
||||
class BookmarksAPITests(BookmarkApiEventTestMixin, BookmarksTestsBase):
|
||||
"""
|
||||
These tests cover the parts of the API methods.
|
||||
|
||||
@@ -8,9 +8,6 @@ from freezegun import freeze_time
|
||||
import mock
|
||||
from nose.plugins.attrib import attr
|
||||
import pytz
|
||||
from unittest import skipUnless
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
from opaque_keys.edx.locator import CourseLocator, BlockUsageLocator
|
||||
@@ -19,6 +16,7 @@ from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.tests.factories import check_mongo_calls, CourseFactory, ItemFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from student.tests.factories import AdminFactory, UserFactory
|
||||
|
||||
from .. import DEFAULT_FIELDS, OPTIONAL_FIELDS, PathItem
|
||||
@@ -228,7 +226,7 @@ class BookmarksTestsBase(ModuleStoreTestCase):
|
||||
|
||||
@attr(shard=2)
|
||||
@ddt.ddt
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
|
||||
@skip_unless_lms
|
||||
class BookmarkModelTests(BookmarksTestsBase):
|
||||
"""
|
||||
Test the Bookmark model.
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
Tests for bookmark services.
|
||||
"""
|
||||
from nose.plugins.attrib import attr
|
||||
from unittest import skipUnless
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from ..services import BookmarksService
|
||||
from .test_models import BookmarksTestsBase
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
|
||||
@skip_unless_lms
|
||||
class BookmarksServiceTests(BookmarksTestsBase):
|
||||
"""
|
||||
Tests the Bookmarks service.
|
||||
|
||||
@@ -5,7 +5,6 @@ Tests for bookmark views.
|
||||
import ddt
|
||||
import json
|
||||
from nose.plugins.attrib import attr
|
||||
from unittest import skipUnless
|
||||
import urllib
|
||||
|
||||
from django.conf import settings
|
||||
@@ -13,6 +12,7 @@ from django.core.urlresolvers import reverse
|
||||
from mock import patch
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
|
||||
from .test_models import BookmarksTestsBase
|
||||
@@ -66,7 +66,7 @@ class BookmarksViewsTestsBase(BookmarksTestsBase, BookmarkApiEventTestMixin):
|
||||
|
||||
@attr(shard=2)
|
||||
@ddt.ddt
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
|
||||
@skip_unless_lms
|
||||
class BookmarksListViewTests(BookmarksViewsTestsBase):
|
||||
"""
|
||||
This contains the tests for GET & POST methods of bookmark.views.BookmarksListView class
|
||||
@@ -371,7 +371,7 @@ class BookmarksListViewTests(BookmarksViewsTestsBase):
|
||||
|
||||
@attr(shard=2)
|
||||
@ddt.ddt
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
|
||||
@skip_unless_lms
|
||||
class BookmarksDetailViewTests(BookmarksViewsTestsBase):
|
||||
"""
|
||||
This contains the tests for GET & DELETE methods of bookmark.views.BookmarksDetailView class
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
"""Tests for cached authentication middleware."""
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
from mock import patch
|
||||
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_cms, skip_unless_lms
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
|
||||
@@ -33,13 +31,13 @@ class CachedAuthMiddlewareTestCase(TestCase):
|
||||
response = self.client.get(test_url)
|
||||
self.assertRedirects(response, redirect_url)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
def test_session_change_lms(self):
|
||||
"""Test session verification with LMS-specific URLs."""
|
||||
dashboard_url = reverse('dashboard')
|
||||
self._test_change_session_hash(dashboard_url, reverse('signin_user') + '?next=' + dashboard_url)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in cms')
|
||||
@skip_unless_cms
|
||||
def test_session_change_cms(self):
|
||||
"""Test session verification with CMS-specific URLs."""
|
||||
home_url = reverse('home')
|
||||
|
||||
0
openedx/core/djangoapps/celery_utils/__init__.py
Normal file
0
openedx/core/djangoapps/celery_utils/__init__.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
import jsonfield.fields
|
||||
import model_utils.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='FailedTask',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
|
||||
('task_name', models.CharField(max_length=255)),
|
||||
('task_id', models.CharField(max_length=255, db_index=True)),
|
||||
('args', jsonfield.fields.JSONField(blank=True)),
|
||||
('kwargs', jsonfield.fields.JSONField(blank=True)),
|
||||
('exc', models.CharField(max_length=255)),
|
||||
('datetime_resolved', models.DateTimeField(default=None, null=True, db_index=True, blank=True)),
|
||||
],
|
||||
),
|
||||
migrations.AlterIndexTogether(
|
||||
name='failedtask',
|
||||
index_together=set([('task_name', 'exc')]),
|
||||
),
|
||||
]
|
||||
32
openedx/core/djangoapps/celery_utils/models.py
Normal file
32
openedx/core/djangoapps/celery_utils/models.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""
|
||||
Models to support persistent tasks.
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
from jsonfield import JSONField
|
||||
from model_utils.models import TimeStampedModel
|
||||
|
||||
|
||||
class FailedTask(TimeStampedModel):
|
||||
"""
|
||||
Representation of tasks that have failed
|
||||
"""
|
||||
task_name = models.CharField(max_length=255)
|
||||
task_id = models.CharField(max_length=255, db_index=True)
|
||||
args = JSONField(blank=True)
|
||||
kwargs = JSONField(blank=True)
|
||||
exc = models.CharField(max_length=255)
|
||||
datetime_resolved = models.DateTimeField(blank=True, null=True, default=None, db_index=True)
|
||||
|
||||
class Meta(object):
|
||||
index_together = [
|
||||
(u'task_name', u'exc'),
|
||||
]
|
||||
|
||||
def __unicode__(self):
|
||||
return u'FailedTask: {task_name}, args={args}, kwargs={kwargs} ({resolution})'.format(
|
||||
task_name=self.task_name,
|
||||
args=self.args,
|
||||
kwargs=self.kwargs,
|
||||
resolution=u"not resolved" if self.datetime_resolved is None else "resolved"
|
||||
)
|
||||
44
openedx/core/djangoapps/celery_utils/task.py
Normal file
44
openedx/core/djangoapps/celery_utils/task.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
Celery utility code for persistent tasks.
|
||||
"""
|
||||
|
||||
from celery import Task
|
||||
|
||||
from .models import FailedTask
|
||||
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
class PersistOnFailureTask(Task):
|
||||
"""
|
||||
Custom Celery Task base class that persists task data on failure.
|
||||
"""
|
||||
|
||||
def on_failure(self, exc, task_id, args, kwargs, einfo):
|
||||
"""
|
||||
If the task fails, persist a record of the task.
|
||||
"""
|
||||
FailedTask.objects.create(
|
||||
task_name=_truncate_to_field(FailedTask, 'task_name', self.name),
|
||||
task_id=task_id, # Fixed length UUID: No need to truncate
|
||||
args=args,
|
||||
kwargs=kwargs,
|
||||
exc=_truncate_to_field(FailedTask, 'exc', repr(exc)),
|
||||
)
|
||||
super(PersistOnFailureTask, self).on_failure(exc, task_id, args, kwargs, einfo)
|
||||
|
||||
|
||||
def _truncate_to_field(model, field_name, value):
|
||||
"""
|
||||
If data is too big for the field, it would cause a failure to
|
||||
insert, so we shorten it, truncating in the middle (because
|
||||
valuable information often shows up at the end.
|
||||
"""
|
||||
field = model._meta.get_field(field_name) # pylint: disable=protected-access
|
||||
if len(value) > field.max_length:
|
||||
midpoint = field.max_length // 2
|
||||
len_after_midpoint = field.max_length - midpoint
|
||||
first = value[:midpoint]
|
||||
sep = u'...'
|
||||
last = value[len(value) - len_after_midpoint + len(sep):]
|
||||
value = sep.join([first, last])
|
||||
return value
|
||||
79
openedx/core/djangoapps/celery_utils/tests/test_task.py
Normal file
79
openedx/core/djangoapps/celery_utils/tests/test_task.py
Normal file
@@ -0,0 +1,79 @@
|
||||
u"""
|
||||
Testing persistent tasks
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from celery import task
|
||||
from django.test import TestCase
|
||||
import six
|
||||
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from ..models import FailedTask
|
||||
from ..task import PersistOnFailureTask
|
||||
|
||||
|
||||
@skip_unless_lms
|
||||
class PersistOnFailureTaskTestCase(TestCase):
|
||||
"""
|
||||
Test that persistent tasks save the appropriate values when needed.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@task(base=PersistOnFailureTask)
|
||||
def exampletask(message=None):
|
||||
u"""
|
||||
A simple task for testing persistence
|
||||
"""
|
||||
if message:
|
||||
raise ValueError(message)
|
||||
return
|
||||
cls.exampletask = exampletask
|
||||
super(PersistOnFailureTaskTestCase, cls).setUpClass()
|
||||
|
||||
def test_exampletask_without_failure(self):
|
||||
result = self.exampletask.delay()
|
||||
result.wait()
|
||||
self.assertEqual(result.status, u'SUCCESS')
|
||||
self.assertFalse(FailedTask.objects.exists())
|
||||
|
||||
def test_exampletask_with_failure(self):
|
||||
result = self.exampletask.delay(message=u'The example task failed')
|
||||
with self.assertRaises(ValueError):
|
||||
result.wait()
|
||||
self.assertEqual(result.status, u'FAILURE')
|
||||
failed_task_object = FailedTask.objects.get()
|
||||
# Assert that we get the kind of data we expect
|
||||
self.assertEqual(
|
||||
failed_task_object.task_name,
|
||||
u'openedx.core.djangoapps.celery_utils.tests.test_task.exampletask'
|
||||
)
|
||||
self.assertEqual(failed_task_object.args, [])
|
||||
self.assertEqual(failed_task_object.kwargs, {u'message': u'The example task failed'})
|
||||
self.assertEqual(failed_task_object.exc, u"ValueError(u'The example task failed',)")
|
||||
self.assertIsNone(failed_task_object.datetime_resolved)
|
||||
|
||||
def test_persists_when_called_with_wrong_args(self):
|
||||
result = self.exampletask.delay(15, u'2001-03-04', err=True)
|
||||
with self.assertRaises(TypeError):
|
||||
result.wait()
|
||||
self.assertEqual(result.status, u'FAILURE')
|
||||
failed_task_object = FailedTask.objects.get()
|
||||
self.assertEqual(failed_task_object.args, [15, u'2001-03-04'])
|
||||
self.assertEqual(failed_task_object.kwargs, {u'err': True})
|
||||
|
||||
def test_persists_with_overlength_field(self):
|
||||
overlong_message = u''.join(u'%03d' % x for x in six.moves.range(100))
|
||||
result = self.exampletask.delay(message=overlong_message)
|
||||
with self.assertRaises(ValueError):
|
||||
result.wait()
|
||||
failed_task_object = FailedTask.objects.get()
|
||||
# Length is max field length
|
||||
self.assertEqual(len(failed_task_object.exc), 255)
|
||||
# Ellipses are put in the middle
|
||||
self.assertEqual(u'037...590', failed_task_object.exc[124:133])
|
||||
# The beginning of the input is captured
|
||||
self.assertEqual(failed_task_object.exc[:11], u"ValueError(")
|
||||
# The end of the input is captured
|
||||
self.assertEqual(failed_task_object.exc[-9:], u"098099',)")
|
||||
@@ -2,13 +2,9 @@
|
||||
Test the partitions and partitions service
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
from django.conf import settings
|
||||
import django.test
|
||||
from mock import patch
|
||||
from nose.plugins.attrib import attr
|
||||
from unittest import skipUnless
|
||||
|
||||
from courseware.masquerade import handle_ajax, setup_masquerade
|
||||
from courseware.tests.test_masquerade import StaffMasqueradeTestCase
|
||||
@@ -19,6 +15,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, TEST_DAT
|
||||
from xmodule.modulestore.tests.factories import ToyCourseFactory
|
||||
|
||||
from openedx.core.djangoapps.user_api.partition_schemes import RandomUserPartitionScheme
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from ..partition_scheme import CohortPartitionScheme, get_cohorted_user_partition
|
||||
from ..models import CourseUserGroupPartitionGroup
|
||||
from ..views import link_cohort_to_partition_group, unlink_cohort_partition_group
|
||||
@@ -383,7 +380,7 @@ class TestMasqueradedGroup(StaffMasqueradeTestCase):
|
||||
self._verify_masquerade_for_group(self.user_partition.groups[1])
|
||||
self._verify_masquerade_for_group(None)
|
||||
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS')
|
||||
@skip_unless_lms
|
||||
@patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
|
||||
def test_group_masquerade(self):
|
||||
"""
|
||||
@@ -391,7 +388,7 @@ class TestMasqueradedGroup(StaffMasqueradeTestCase):
|
||||
"""
|
||||
self._verify_masquerade_for_all_groups()
|
||||
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS')
|
||||
@skip_unless_lms
|
||||
@patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
|
||||
def test_group_masquerade_with_cohort(self):
|
||||
"""
|
||||
|
||||
@@ -8,9 +8,7 @@ import json
|
||||
from collections import namedtuple
|
||||
from datetime import datetime
|
||||
from nose.plugins.attrib import attr
|
||||
from unittest import skipUnless
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import Http404
|
||||
from django.test.client import RequestFactory
|
||||
@@ -22,6 +20,8 @@ from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from xmodule.modulestore.tests.factories import ItemFactory
|
||||
from lms.djangoapps.django_comment_client.constants import TYPE_ENTRY, TYPE_SUBCATEGORY
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
|
||||
|
||||
from ..models import CourseUserGroup, CourseCohort
|
||||
from ..views import (
|
||||
@@ -1207,7 +1207,7 @@ class RemoveUserFromCohortTestCase(CohortViewsTestCase):
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
|
||||
@skip_unless_lms
|
||||
class CourseCohortDiscussionTopicsTestCase(CohortViewsTestCase):
|
||||
"""
|
||||
Tests the `cohort_discussion_topics` view.
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
""" CourseTalk widget helpers tests """
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from unittest import skipUnless
|
||||
|
||||
from django import test
|
||||
from django.conf import settings
|
||||
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from openedx.core.djangoapps.coursetalk import helpers
|
||||
from openedx.core.djangoapps.coursetalk import models
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
|
||||
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
|
||||
@skip_unless_lms
|
||||
class CourseTalkKeyTests(test.TestCase):
|
||||
"""
|
||||
CourseTalkKeyTests:
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
"""Tests for models supporting Credentials-related functionality."""
|
||||
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from nose.plugins.attrib import attr
|
||||
|
||||
from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@attr(shard=2)
|
||||
class TestCredentialsApiConfig(CredentialsApiConfigMixin, TestCase):
|
||||
"""Tests covering the CredentialsApiConfig model."""
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
"""Tests covering Credentials utilities."""
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from nose.plugins.attrib import attr
|
||||
import httpretty
|
||||
@@ -18,11 +15,11 @@ from openedx.core.djangoapps.credentials.utils import (
|
||||
from openedx.core.djangoapps.credentials.tests import factories
|
||||
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin, ProgramsDataMixin
|
||||
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@attr(shard=2)
|
||||
class TestCredentialsRetrieval(ProgramsApiConfigMixin, CredentialsApiConfigMixin, CredentialsDataMixin,
|
||||
ProgramsDataMixin, CacheIsolationTestCase):
|
||||
|
||||
@@ -3,10 +3,8 @@ Tests for the API functions in the credit app.
|
||||
"""
|
||||
import datetime
|
||||
import json
|
||||
import unittest
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core import mail
|
||||
from django.test.utils import override_settings
|
||||
@@ -36,6 +34,7 @@ from openedx.core.djangoapps.credit.models import (
|
||||
CreditEligibility,
|
||||
CreditRequest
|
||||
)
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from course_modes.models import CourseMode
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import UserFactory
|
||||
@@ -203,7 +202,7 @@ class CreditApiTestBase(ModuleStoreTestCase):
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS')
|
||||
@skip_unless_lms
|
||||
@ddt.ddt
|
||||
class CreditRequirementApiTests(CreditApiTestBase):
|
||||
"""
|
||||
@@ -1165,7 +1164,7 @@ class CreditProviderIntegrationApiTests(CreditApiTestBase):
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS')
|
||||
@skip_unless_lms
|
||||
@override_settings(
|
||||
ECOMMERCE_API_URL=TEST_API_URL,
|
||||
ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY,
|
||||
|
||||
@@ -5,9 +5,6 @@ Tests for In-Course Reverification Access Control Partition scheme
|
||||
|
||||
import ddt
|
||||
from nose.plugins.attrib import attr
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from lms.djangoapps.verify_student.models import (
|
||||
VerificationCheckpoint,
|
||||
@@ -15,6 +12,7 @@ from lms.djangoapps.verify_student.models import (
|
||||
SkippedReverification,
|
||||
)
|
||||
from openedx.core.djangoapps.credit.partition_schemes import VerificationPartitionScheme
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.partitions.partitions import UserPartition, Group
|
||||
@@ -24,7 +22,7 @@ from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
@attr(shard=2)
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class ReverificationPartitionTest(ModuleStoreTestCase):
|
||||
"""Tests for the Reverification Partition Scheme. """
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@ import ddt
|
||||
import pytz
|
||||
from datetime import timedelta, datetime
|
||||
from mock import MagicMock
|
||||
from unittest import skipUnless
|
||||
|
||||
from django.conf import settings
|
||||
from django.test.client import RequestFactory
|
||||
from nose.plugins.attrib import attr
|
||||
from course_modes.models import CourseMode
|
||||
@@ -22,10 +20,11 @@ from openedx.core.djangoapps.credit.api import (
|
||||
)
|
||||
from openedx.core.djangoapps.credit.models import CreditCourse, CreditProvider
|
||||
from openedx.core.djangoapps.credit.signals import listen_for_grade_calculation
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS')
|
||||
@skip_unless_lms
|
||||
@ddt.ddt
|
||||
class TestMinGradedRequirementStatus(ModuleStoreTestCase):
|
||||
"""Test cases to check the minimum grade requirement status updated.
|
||||
|
||||
@@ -8,7 +8,6 @@ from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import unittest
|
||||
|
||||
import ddt
|
||||
import pytz
|
||||
@@ -28,6 +27,7 @@ from openedx.core.djangoapps.credit.signature import signature
|
||||
from openedx.core.djangoapps.credit.tests.factories import (
|
||||
CreditProviderFactory, CreditEligibilityFactory, CreditCourseFactory, CreditRequestFactory,
|
||||
)
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from openedx.core.lib.token_utils import JwtBuilder
|
||||
from student.tests.factories import UserFactory, AdminFactory
|
||||
from util.date_utils import to_timestamp
|
||||
@@ -113,7 +113,7 @@ class ReadOnlyMixin(object):
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class CreditCourseViewSetTests(AuthMixin, UserMixin, TestCase):
|
||||
""" Tests for the CreditCourse endpoints.
|
||||
|
||||
@@ -276,7 +276,7 @@ class CreditCourseViewSetTests(AuthMixin, UserMixin, TestCase):
|
||||
|
||||
@attr(shard=2)
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class CreditProviderViewSetTests(ApiTestCaseMixin, ReadOnlyMixin, AuthMixin, UserMixin, TestCase):
|
||||
""" Tests for CreditProviderViewSet. """
|
||||
list_path = 'credit:creditprovider-list'
|
||||
@@ -320,7 +320,7 @@ class CreditProviderViewSetTests(ApiTestCaseMixin, ReadOnlyMixin, AuthMixin, Use
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class CreditProviderRequestCreateViewTests(ApiTestCaseMixin, UserMixin, TestCase):
|
||||
""" Tests for CreditProviderRequestCreateView. """
|
||||
|
||||
@@ -470,7 +470,7 @@ class CreditProviderRequestCreateViewTests(ApiTestCaseMixin, UserMixin, TestCase
|
||||
|
||||
@attr(shard=2)
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class CreditProviderCallbackViewTests(UserMixin, TestCase):
|
||||
""" Tests for CreditProviderCallbackView. """
|
||||
|
||||
@@ -624,7 +624,7 @@ class CreditProviderCallbackViewTests(UserMixin, TestCase):
|
||||
|
||||
@attr(shard=2)
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class CreditEligibilityViewTests(AuthMixin, UserMixin, ReadOnlyMixin, TestCase):
|
||||
""" Tests for CreditEligibilityView. """
|
||||
view_name = 'credit:eligibility_details'
|
||||
|
||||
@@ -5,7 +5,6 @@ Tests for EmbargoMiddleware
|
||||
from contextlib import contextmanager
|
||||
import mock
|
||||
from nose.plugins.attrib import attr
|
||||
import unittest
|
||||
import pygeoip
|
||||
import ddt
|
||||
|
||||
@@ -14,6 +13,7 @@ from django.test.utils import override_settings
|
||||
from django.core.cache import cache
|
||||
from django.db import connection
|
||||
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from xmodule.modulestore.tests.django_utils import (
|
||||
@@ -41,7 +41,7 @@ MODULESTORE_CONFIG = mixed_store_config(settings.COMMON_TEST_DATA_ROOT, {})
|
||||
@attr(shard=3)
|
||||
@ddt.ddt
|
||||
@override_settings(MODULESTORE=MODULESTORE_CONFIG)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@mock.patch.dict(settings.FEATURES, {'EMBARGO': True})
|
||||
class EmbargoCheckAccessApiTests(ModuleStoreTestCase):
|
||||
"""Test the embargo API calls to determine whether a user has access. """
|
||||
@@ -239,7 +239,7 @@ class EmbargoCheckAccessApiTests(ModuleStoreTestCase):
|
||||
|
||||
@ddt.ddt
|
||||
@override_settings(MODULESTORE=MODULESTORE_CONFIG)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class EmbargoMessageUrlApiTests(UrlResetMixin, ModuleStoreTestCase):
|
||||
"""Test the embargo API calls for retrieving the blocking message URLs. """
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
Tests for EmbargoMiddleware with CountryAccessRules
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mock import patch
|
||||
from nose.plugins.attrib import attr
|
||||
import ddt
|
||||
@@ -11,11 +10,12 @@ from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache as django_cache
|
||||
|
||||
from config_models.models import cache as config_cache
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from util.testing import UrlResetMixin
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from config_models.models import cache as config_cache
|
||||
|
||||
from ..models import RestrictedCourse, IPFilter
|
||||
from ..test_utils import restrict_course
|
||||
@@ -23,7 +23,7 @@ from ..test_utils import restrict_course
|
||||
|
||||
@attr(shard=3)
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class EmbargoMiddlewareAccessTests(UrlResetMixin, ModuleStoreTestCase):
|
||||
"""Tests of embargo middleware country access rules.
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Tests for embargo app views. """
|
||||
|
||||
import unittest
|
||||
from mock import patch
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
@@ -8,11 +7,11 @@ import ddt
|
||||
|
||||
from util.testing import UrlResetMixin
|
||||
from .. import messages
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
|
||||
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@ddt.ddt
|
||||
class CourseAccessMessageViewTest(CacheIsolationTestCase, UrlResetMixin):
|
||||
"""Tests for the courseware access message view.
|
||||
|
||||
@@ -3,10 +3,10 @@ Provides unit tests for SSL based authentication portions
|
||||
of the external_auth app.
|
||||
"""
|
||||
# pylint: disable=no-member
|
||||
import copy
|
||||
import unittest
|
||||
|
||||
from contextlib import contextmanager
|
||||
import copy
|
||||
from mock import Mock, patch
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import SESSION_KEY
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
@@ -15,10 +15,10 @@ from django.core.urlresolvers import reverse
|
||||
from django.test.client import Client
|
||||
from django.test.client import RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
from mock import Mock, patch
|
||||
|
||||
from openedx.core.djangoapps.external_auth.models import ExternalAuthMap
|
||||
import openedx.core.djangoapps.external_auth.views as external_auth_views
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_cms, skip_unless_lms
|
||||
from student.models import CourseEnrollment
|
||||
from student.roles import CourseStaffRole
|
||||
from student.tests.factories import UserFactory
|
||||
@@ -81,7 +81,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
self.factory = RequestFactory()
|
||||
self.mock = Mock()
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
def test_ssl_login_with_signup_lms(self):
|
||||
"""
|
||||
Validate that an SSL login creates an eamap user and
|
||||
@@ -101,7 +101,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
with self.assertRaises(User.DoesNotExist):
|
||||
User.objects.get(email=self.USER_EMAIL)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in cms')
|
||||
@skip_unless_cms
|
||||
def test_ssl_login_with_signup_cms(self):
|
||||
"""
|
||||
Validate that an SSL login creates an eamap user and
|
||||
@@ -120,7 +120,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
with self.assertRaises(User.DoesNotExist):
|
||||
User.objects.get(email=self.USER_EMAIL)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP)
|
||||
def test_ssl_login_without_signup_lms(self):
|
||||
"""
|
||||
@@ -140,7 +140,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
except ExternalAuthMap.DoesNotExist, ex:
|
||||
self.fail('User did not get properly added to internal users, exception was {0}'.format(str(ex)))
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in cms')
|
||||
@skip_unless_cms
|
||||
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP)
|
||||
def test_ssl_login_without_signup_cms(self):
|
||||
"""
|
||||
@@ -166,7 +166,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
except ExternalAuthMap.DoesNotExist, ex:
|
||||
self.fail('User did not get properly added to internal users, exception was {0}'.format(str(ex)))
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP)
|
||||
def test_default_login_decorator_ssl(self):
|
||||
"""
|
||||
@@ -184,7 +184,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
response.redirect_chain[-1])
|
||||
self.assertIn(SESSION_KEY, self.client.session)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP)
|
||||
def test_registration_page_bypass(self):
|
||||
"""
|
||||
@@ -198,7 +198,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
response.redirect_chain[-1])
|
||||
self.assertIn(SESSION_KEY, self.client.session)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in cms')
|
||||
@skip_unless_cms
|
||||
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP)
|
||||
def test_cms_registration_page_bypass(self):
|
||||
"""
|
||||
@@ -217,7 +217,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
response = self.client.get(reverse('signup'), follow=True)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP)
|
||||
def test_signin_page_bypass(self):
|
||||
"""
|
||||
@@ -238,7 +238,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
response.redirect_chain[-1])
|
||||
self.assertIn(SESSION_KEY, self.client.session)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP)
|
||||
def test_ssl_bad_eamap(self):
|
||||
"""
|
||||
@@ -263,7 +263,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
SSL_CLIENT_S_DN=self.AUTH_DN.format(self.USER_NAME, self.USER_EMAIL))
|
||||
self.assertIn(SESSION_KEY, self.client.session)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@override_settings(FEATURES=FEATURES_WITHOUT_SSL_AUTH)
|
||||
def test_ssl_decorator_no_certs(self):
|
||||
"""Make sure no external auth happens without SSL enabled"""
|
||||
@@ -279,7 +279,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
self.assertTrue(self.mock.called)
|
||||
self.assertEqual(0, len(ExternalAuthMap.objects.all()))
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
def test_ssl_login_decorator(self):
|
||||
"""Create mock function to test ssl login decorator"""
|
||||
|
||||
@@ -305,7 +305,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
dec_mock(request)
|
||||
self.assertTrue(self.mock.called)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP)
|
||||
def test_ssl_decorator_auto_signup(self):
|
||||
"""
|
||||
@@ -330,7 +330,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
|
||||
self.assertTrue(self.mock.called)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_AUTO_ACTIVATE)
|
||||
def test_ssl_lms_redirection(self):
|
||||
"""
|
||||
@@ -361,7 +361,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
response.redirect_chain[-1])
|
||||
self.assertIn(SESSION_KEY, self.client.session)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in cms')
|
||||
@skip_unless_cms
|
||||
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_AUTO_ACTIVATE)
|
||||
def test_ssl_cms_redirection(self):
|
||||
"""
|
||||
@@ -393,7 +393,7 @@ class SSLClientTest(ModuleStoreTestCase):
|
||||
response.redirect_chain[-1])
|
||||
self.assertIn(SESSION_KEY, self.client.session)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_AUTO_ACTIVATE)
|
||||
def test_ssl_logout(self):
|
||||
"""
|
||||
|
||||
@@ -5,9 +5,7 @@ from contextlib import closing
|
||||
from itertools import product
|
||||
import os
|
||||
from tempfile import NamedTemporaryFile
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files.uploadedfile import UploadedFile
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
@@ -17,6 +15,7 @@ from nose.plugins.attrib import attr
|
||||
import piexif
|
||||
from PIL import Image
|
||||
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from ..exceptions import ImageValidationError
|
||||
from ..images import (
|
||||
create_profile_images,
|
||||
@@ -31,7 +30,7 @@ from .helpers import make_image_file, make_uploaded_file
|
||||
|
||||
@attr(shard=2)
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
|
||||
@skip_unless_lms
|
||||
class TestValidateUploadedImage(TestCase):
|
||||
"""
|
||||
Test validate_uploaded_image
|
||||
@@ -127,7 +126,7 @@ class TestValidateUploadedImage(TestCase):
|
||||
|
||||
@attr(shard=2)
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
|
||||
@skip_unless_lms
|
||||
class TestGenerateProfileImages(TestCase):
|
||||
"""
|
||||
Test create_profile_images
|
||||
@@ -224,7 +223,7 @@ class TestGenerateProfileImages(TestCase):
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
|
||||
@skip_unless_lms
|
||||
class TestRemoveProfileImages(TestCase):
|
||||
"""
|
||||
Test remove_profile_images
|
||||
|
||||
@@ -5,9 +5,7 @@ from contextlib import closing
|
||||
import datetime
|
||||
from nose.plugins.attrib import attr
|
||||
from pytz import UTC
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponse
|
||||
|
||||
@@ -24,6 +22,7 @@ from openedx.core.djangoapps.user_api.accounts.image_helpers import (
|
||||
get_profile_image_names,
|
||||
get_profile_image_storage,
|
||||
)
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
|
||||
from ..images import create_profile_images, ImageValidationError
|
||||
from ..views import LOG_MESSAGE_CREATE, LOG_MESSAGE_DELETE
|
||||
@@ -153,7 +152,7 @@ class ProfileImageEndpointMixin(UserSettingsEventTestMixin):
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
|
||||
@skip_unless_lms
|
||||
@mock.patch('openedx.core.djangoapps.profile_images.views.log')
|
||||
class ProfileImageViewGeneralTestCase(ProfileImageEndpointMixin, APITestCase):
|
||||
"""
|
||||
@@ -174,7 +173,7 @@ class ProfileImageViewGeneralTestCase(ProfileImageEndpointMixin, APITestCase):
|
||||
|
||||
@attr(shard=2)
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
|
||||
@skip_unless_lms
|
||||
@mock.patch('openedx.core.djangoapps.profile_images.views.log')
|
||||
class ProfileImageViewPostTestCase(ProfileImageEndpointMixin, APITestCase):
|
||||
"""
|
||||
@@ -384,7 +383,7 @@ class ProfileImageViewPostTestCase(ProfileImageEndpointMixin, APITestCase):
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
|
||||
@skip_unless_lms
|
||||
@mock.patch('openedx.core.djangoapps.profile_images.views.log')
|
||||
class ProfileImageViewDeleteTestCase(ProfileImageEndpointMixin, APITestCase):
|
||||
"""
|
||||
@@ -515,7 +514,7 @@ class DeprecatedProfileImageTestMixin(ProfileImageEndpointMixin):
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
|
||||
@skip_unless_lms
|
||||
@mock.patch('openedx.core.djangoapps.profile_images.views.log')
|
||||
class DeprecatedProfileImageUploadTestCase(DeprecatedProfileImageTestMixin, APITestCase):
|
||||
"""
|
||||
@@ -528,7 +527,7 @@ class DeprecatedProfileImageUploadTestCase(DeprecatedProfileImageTestMixin, APIT
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
|
||||
@skip_unless_lms
|
||||
@mock.patch('openedx.core.djangoapps.profile_images.views.log')
|
||||
class DeprecatedProfileImageRemoveTestCase(DeprecatedProfileImageTestMixin, APITestCase):
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
Tests for programs celery tasks.
|
||||
"""
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from celery.exceptions import MaxRetriesExceededError
|
||||
import ddt
|
||||
@@ -20,7 +19,7 @@ from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfi
|
||||
from openedx.core.djangoapps.programs.tests import factories
|
||||
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
|
||||
from openedx.core.djangoapps.programs.tasks.v1 import tasks
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
|
||||
@@ -28,7 +27,7 @@ TASKS_MODULE = 'openedx.core.djangoapps.programs.tasks.v1.tasks'
|
||||
UTILS_MODULE = 'openedx.core.djangoapps.programs.utils'
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class GetApiClientTestCase(TestCase, ProgramsApiConfigMixin):
|
||||
"""
|
||||
Test the get_api_client function
|
||||
@@ -53,7 +52,7 @@ class GetApiClientTestCase(TestCase, ProgramsApiConfigMixin):
|
||||
|
||||
|
||||
@httpretty.activate
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class GetCompletedProgramsTestCase(ProgramsApiConfigMixin, CacheIsolationTestCase):
|
||||
"""
|
||||
Test the get_completed_programs function
|
||||
@@ -113,7 +112,7 @@ class GetCompletedProgramsTestCase(ProgramsApiConfigMixin, CacheIsolationTestCas
|
||||
self._assert_num_requests(1)
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class GetAwardedCertificateProgramsTestCase(TestCase):
|
||||
"""
|
||||
Test the get_awarded_certificate_programs function
|
||||
@@ -154,7 +153,7 @@ class GetAwardedCertificateProgramsTestCase(TestCase):
|
||||
self.assertEqual(result, [1])
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class AwardProgramCertificateTestCase(TestCase):
|
||||
"""
|
||||
Test the award_program_certificate function
|
||||
@@ -183,7 +182,7 @@ class AwardProgramCertificateTestCase(TestCase):
|
||||
self.assertEqual(json.loads(httpretty.last_request().body), expected_body)
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@ddt.ddt
|
||||
@mock.patch(TASKS_MODULE + '.award_program_certificate')
|
||||
@mock.patch(TASKS_MODULE + '.get_awarded_certificate_programs')
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
"""Tests for the backpopulate_program_credentials management command."""
|
||||
import json
|
||||
from unittest import skipUnless
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.core.management import call_command, CommandError
|
||||
from django.test import TestCase
|
||||
from edx_oauth2_provider.tests.factories import ClientFactory
|
||||
@@ -17,6 +15,7 @@ from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFact
|
||||
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
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
|
||||
@@ -26,7 +25,7 @@ COMMAND_MODULE = 'openedx.core.djangoapps.programs.management.commands.backpopul
|
||||
@ddt.ddt
|
||||
@httpretty.activate
|
||||
@mock.patch(COMMAND_MODULE + '.award_program_certificates.delay')
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class BackpopulateProgramCredentialsTests(ProgramsApiConfigMixin, TestCase):
|
||||
"""Tests for the backpopulate_program_credentials management command."""
|
||||
course_id, alternate_course_id = 'org/course/run', 'org/alternate/run'
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
import copy
|
||||
import datetime
|
||||
import json
|
||||
from unittest import skipUnless
|
||||
import uuid
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
@@ -29,7 +27,7 @@ from openedx.core.djangoapps.programs import utils
|
||||
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 openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
|
||||
from student.tests.factories import UserFactory, CourseEnrollmentFactory
|
||||
from util.date_utils import strftime_localized
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
@@ -45,7 +43,7 @@ MARKETING_URL = 'https://www.example.com/marketing/path'
|
||||
@ddt.ddt
|
||||
@attr(shard=2)
|
||||
@httpretty.activate
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, CredentialsDataMixin,
|
||||
CredentialsApiConfigMixin, CacheIsolationTestCase):
|
||||
"""Tests covering the retrieval of programs from the Programs service."""
|
||||
@@ -190,7 +188,7 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, Credential
|
||||
self.assertEqual(actual, [])
|
||||
|
||||
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class GetProgramsByRunTests(TestCase):
|
||||
"""Tests verifying that programs are inverted correctly."""
|
||||
maxDiff = None
|
||||
@@ -262,7 +260,7 @@ class GetProgramsByRunTests(TestCase):
|
||||
self.assertEqual(course_ids, [])
|
||||
|
||||
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class GetCompletedCoursesTestCase(TestCase):
|
||||
"""
|
||||
Test the get_completed_courses function
|
||||
@@ -308,7 +306,7 @@ class GetCompletedCoursesTestCase(TestCase):
|
||||
|
||||
@attr(shard=2)
|
||||
@httpretty.activate
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class TestProgramProgressMeter(ProgramsApiConfigMixin, TestCase):
|
||||
"""Tests of the program progress utility class."""
|
||||
def setUp(self):
|
||||
@@ -700,7 +698,7 @@ class TestProgramProgressMeter(ProgramsApiConfigMixin, TestCase):
|
||||
|
||||
@ddt.ddt
|
||||
@override_settings(ECOMMERCE_PUBLIC_URL_ROOT=ECOMMERCE_URL_ROOT)
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@mock.patch(UTILS_MODULE + '.get_run_marketing_url', mock.Mock(return_value=MARKETING_URL))
|
||||
class TestProgramDataExtender(ProgramsApiConfigMixin, ModuleStoreTestCase):
|
||||
"""Tests of the program data extender utility class."""
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
Test site_configuration middleware.
|
||||
"""
|
||||
import ddt
|
||||
import unittest
|
||||
from mock import patch
|
||||
|
||||
from django.conf import settings
|
||||
@@ -22,6 +21,7 @@ from microsite_configuration.tests.tests import (
|
||||
MICROSITE_BACKENDS,
|
||||
)
|
||||
from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
|
||||
|
||||
# NOTE: We set SESSION_SAVE_EVERY_REQUEST to True in order to make sure
|
||||
@@ -29,7 +29,7 @@ from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfi
|
||||
# pylint: disable=no-member, protected-access
|
||||
@ddt.ddt
|
||||
@override_settings(SESSION_SAVE_EVERY_REQUEST=True)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class SessionCookieDomainMicrositeOverrideTests(DatabaseMicrositeTestCase):
|
||||
"""
|
||||
Tests regarding the session cookie management in the middlware for Microsites
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
"""
|
||||
Tests for comprehensive theme static files finders.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
|
||||
from openedx.core.djangoapps.theming.finders import ThemeFilesFinder
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class TestThemeFinders(TestCase):
|
||||
"""
|
||||
Test comprehensive theming static files finders.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""
|
||||
Test helpers for Comprehensive Theming.
|
||||
"""
|
||||
import unittest
|
||||
from mock import patch, Mock
|
||||
|
||||
from django.test import TestCase, override_settings
|
||||
@@ -12,6 +11,7 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_
|
||||
from openedx.core.djangoapps.theming import helpers as theming_helpers
|
||||
from openedx.core.djangoapps.theming.helpers import get_template_path_with_theme, strip_site_theme_templates_path, \
|
||||
get_themes, Theme, get_theme_base_dir
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_cms, skip_unless_lms
|
||||
|
||||
|
||||
class TestHelpers(TestCase):
|
||||
@@ -201,7 +201,7 @@ class TestHelpers(TestCase):
|
||||
self.assertEqual(theming_helpers.get_template_path("about.html"), "/microsite/about.html")
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class TestHelpersLMS(TestCase):
|
||||
"""Test comprehensive theming helper functions."""
|
||||
|
||||
@@ -244,7 +244,7 @@ class TestHelpersLMS(TestCase):
|
||||
self.assertEqual(template_path, '/red-theme/lms/templates/header.html')
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in cms')
|
||||
@skip_unless_cms
|
||||
class TestHelpersCMS(TestCase):
|
||||
"""Test comprehensive theming helper functions."""
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
"""
|
||||
Tests for microsites and comprehensive themes.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
from openedx.core.djangoapps.theming.models import SiteTheme
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class TestComprehensiveThemeLMS(TestCase):
|
||||
"""
|
||||
Test html, sass and static file overrides for comprehensive themes.
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
Tests for comprehensive theme static files storage classes.
|
||||
"""
|
||||
import ddt
|
||||
import unittest
|
||||
import re
|
||||
|
||||
from mock import patch
|
||||
@@ -12,9 +11,10 @@ from django.conf import settings
|
||||
|
||||
from openedx.core.djangoapps.theming.helpers import get_theme_base_dirs, Theme, get_theme_base_dir
|
||||
from openedx.core.djangoapps.theming.storage import ThemeStorage
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
@ddt.ddt
|
||||
class TestStorageLMS(TestCase):
|
||||
"""
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
"""
|
||||
Tests for Themeing locales
|
||||
Tests for Theming locales
|
||||
"""
|
||||
import os
|
||||
|
||||
import unittest
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
import os
|
||||
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
|
||||
|
||||
class TestComprehensiveThemeLocale(TestCase):
|
||||
"""
|
||||
Test Comprehensive Theme Locales
|
||||
"""
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
def test_theme_locale_path_in_settings(self):
|
||||
"""
|
||||
test comprehensive theming paths in settings.
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
"""
|
||||
Tests for comprehensive themes.
|
||||
Tests for comprehensive themes.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
from django.contrib import staticfiles
|
||||
|
||||
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_cms, skip_unless_lms
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class TestComprehensiveThemeLMS(TestCase):
|
||||
"""
|
||||
Test html, sass and static file overrides for comprehensive themes.
|
||||
@@ -66,7 +65,7 @@ class TestComprehensiveThemeLMS(TestCase):
|
||||
self.assertEqual(result, settings.TEST_THEME / 'lms/static/images/logo.png')
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in cms')
|
||||
@skip_unless_cms
|
||||
class TestComprehensiveThemeCMS(TestCase):
|
||||
"""
|
||||
Test html, sass and static file overrides for comprehensive themes.
|
||||
@@ -92,7 +91,7 @@ class TestComprehensiveThemeCMS(TestCase):
|
||||
self.assertContains(resp, "Login Page override for test-theme.")
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class TestComprehensiveThemeDisabledLMS(TestCase):
|
||||
"""
|
||||
Test Sass compilation order and sass overrides for comprehensive themes.
|
||||
@@ -115,7 +114,7 @@ class TestComprehensiveThemeDisabledLMS(TestCase):
|
||||
self.assertEqual(result, settings.REPO_ROOT / 'lms/static/images/logo.png')
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in cms')
|
||||
@skip_unless_cms
|
||||
class TestComprehensiveThemeDisabledCMS(TestCase):
|
||||
"""
|
||||
Test default html, sass and static file when no theme is applied.
|
||||
@@ -139,7 +138,7 @@ class TestComprehensiveThemeDisabledCMS(TestCase):
|
||||
self.assertNotContains(resp, "Login Page override for test-theme.")
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class TestStanfordTheme(TestCase):
|
||||
"""
|
||||
Test html, sass and static file overrides for stanford theme.
|
||||
|
||||
@@ -17,6 +17,7 @@ from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core import mail
|
||||
from django.test.client import RequestFactory
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from student.models import PendingEmailChange
|
||||
from student.tests.tests import UserSettingsEventTestMixin
|
||||
from ...errors import (
|
||||
@@ -35,7 +36,7 @@ def mock_render_to_string(template_name, context):
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Account APIs are only supported in LMS')
|
||||
@skip_unless_lms
|
||||
class TestAccountApi(UserSettingsEventTestMixin, TestCase):
|
||||
"""
|
||||
These tests specifically cover the parts of the API methods that are not covered by test_views.py.
|
||||
@@ -239,7 +240,7 @@ class TestAccountApi(UserSettingsEventTestMixin, TestCase):
|
||||
{'full': 50, 'small': 10},
|
||||
clear=True
|
||||
)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Account APIs are only supported in LMS')
|
||||
@skip_unless_lms
|
||||
class AccountSettingsOnCreationTest(TestCase):
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
@@ -333,7 +334,7 @@ class AccountCreationActivationAndPasswordChangeTest(TestCase):
|
||||
u'a' * (PASSWORD_MAX_LENGTH + 1)
|
||||
]
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
def test_activate_account(self):
|
||||
# Create the account, which is initially inactive
|
||||
activation_key = create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
||||
@@ -393,7 +394,7 @@ class AccountCreationActivationAndPasswordChangeTest(TestCase):
|
||||
def test_activate_account_invalid_key(self):
|
||||
activate_account(u'invalid')
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS')
|
||||
@skip_unless_lms
|
||||
def test_request_password_change(self):
|
||||
# Create and activate an account
|
||||
activation_key = create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
||||
@@ -411,7 +412,7 @@ class AccountCreationActivationAndPasswordChangeTest(TestCase):
|
||||
result = re.search(r'(?P<url>https?://[^\s]+)', email_body)
|
||||
self.assertIsNot(result, None)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS')
|
||||
@skip_unless_lms
|
||||
def test_request_password_change_invalid_user(self):
|
||||
with self.assertRaises(UserNotFound):
|
||||
request_password_change(self.EMAIL, self.ORIG_HOST, self.IS_SECURE)
|
||||
@@ -419,7 +420,7 @@ class AccountCreationActivationAndPasswordChangeTest(TestCase):
|
||||
# Verify that no email messages have been sent
|
||||
self.assertEqual(len(mail.outbox), 0)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS')
|
||||
@skip_unless_lms
|
||||
def test_request_password_change_inactive_user(self):
|
||||
# Create an account, but do not activate it
|
||||
create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
||||
|
||||
@@ -6,13 +6,12 @@ import hashlib
|
||||
from mock import patch
|
||||
from nose.plugins.attrib import attr
|
||||
from pytz import UTC
|
||||
from unittest import skipUnless
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
|
||||
from ..image_helpers import get_profile_image_urls_for_user
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from student.tests.factories import UserFactory
|
||||
from ..image_helpers import get_profile_image_urls_for_user
|
||||
|
||||
TEST_SIZES = {'full': 50, 'small': 10}
|
||||
TEST_PROFILE_IMAGE_UPLOAD_DT = datetime.datetime(2002, 1, 9, 15, 43, 01, tzinfo=UTC)
|
||||
@@ -20,7 +19,7 @@ TEST_PROFILE_IMAGE_UPLOAD_DT = datetime.datetime(2002, 1, 9, 15, 43, 01, tzinfo=
|
||||
|
||||
@attr(shard=2)
|
||||
@patch.dict('openedx.core.djangoapps.user_api.accounts.image_helpers.PROFILE_IMAGE_SIZES_MAP', TEST_SIZES, clear=True)
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class ProfileImageUrlTestCase(TestCase):
|
||||
"""
|
||||
Tests for profile image URL generation helpers.
|
||||
|
||||
@@ -12,7 +12,6 @@ import json
|
||||
from mock import patch
|
||||
from nose.plugins.attrib import attr
|
||||
from pytz import UTC
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
@@ -25,7 +24,7 @@ from student.tests.factories import UserFactory
|
||||
from student.models import UserProfile, LanguageProficiency, PendingEmailChange
|
||||
from openedx.core.djangoapps.user_api.accounts import ACCOUNT_VISIBILITY_PREF_KEY
|
||||
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
|
||||
from .. import PRIVATE_VISIBILITY, ALL_USERS_VISIBILITY
|
||||
|
||||
TEST_PROFILE_IMAGE_UPLOADED_AT = datetime.datetime(2002, 1, 9, 15, 43, 01, tzinfo=UTC)
|
||||
@@ -140,7 +139,7 @@ class UserAPITestCase(APITestCase):
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Account APIs are only supported in LMS')
|
||||
@skip_unless_lms
|
||||
@attr(shard=2)
|
||||
class TestOwnUsernameAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
"""
|
||||
@@ -193,7 +192,7 @@ class TestOwnUsernameAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Account APIs are only supported in LMS')
|
||||
@skip_unless_lms
|
||||
@patch('openedx.core.djangoapps.user_api.accounts.image_helpers._PROFILE_IMAGE_SIZES', [50, 10])
|
||||
@patch.dict(
|
||||
'openedx.core.djangoapps.user_api.accounts.image_helpers.PROFILE_IMAGE_SIZES_MAP',
|
||||
@@ -783,7 +782,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class TestAccountAPITransactions(TransactionTestCase):
|
||||
"""
|
||||
Tests the transactional behavior of the account API
|
||||
|
||||
@@ -49,6 +49,8 @@ class Command(BaseCommand):
|
||||
|
||||
# Fields output in the CSV
|
||||
OUTPUT_FIELD_NAMES = [
|
||||
"user_id",
|
||||
"username",
|
||||
"email",
|
||||
"full_name",
|
||||
"course_id",
|
||||
@@ -199,6 +201,8 @@ class Command(BaseCommand):
|
||||
query = (
|
||||
u"""
|
||||
SELECT
|
||||
user.`id` AS `user_id`,
|
||||
user.`username` AS username,
|
||||
user.`email` AS `email`,
|
||||
profile.`name` AS `full_name`,
|
||||
enrollment.`course_id` AS `course_id`,
|
||||
@@ -234,8 +238,10 @@ class Command(BaseCommand):
|
||||
cursor.execute(query)
|
||||
row_count = 0
|
||||
for row in self._iterate_results(cursor):
|
||||
email, full_name, course_id, is_opted_in, pref_set_datetime = row
|
||||
user_id, username, email, full_name, course_id, is_opted_in, pref_set_datetime = row
|
||||
writer.writerow({
|
||||
"user_id": user_id,
|
||||
"username": username.encode('utf-8'),
|
||||
"email": email.encode('utf-8'),
|
||||
# There should not be a case where users are without full_names. We only need this safe check because
|
||||
# of ECOM-1995.
|
||||
|
||||
@@ -6,10 +6,8 @@ import shutil
|
||||
import csv
|
||||
from collections import defaultdict
|
||||
from nose.plugins.attrib import attr
|
||||
from unittest import skipUnless
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.management.base import CommandError
|
||||
|
||||
@@ -21,11 +19,12 @@ from student.models import CourseEnrollment
|
||||
from openedx.core.djangoapps.user_api.preferences.api import update_email_opt_in
|
||||
from openedx.core.djangoapps.user_api.models import UserOrgTag
|
||||
from openedx.core.djangoapps.user_api.management.commands import email_opt_in_list
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@ddt.ddt
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class EmailOptInListTest(ModuleStoreTestCase):
|
||||
"""Tests for the email opt-in list management command. """
|
||||
|
||||
@@ -37,6 +36,8 @@ class EmailOptInListTest(ModuleStoreTestCase):
|
||||
|
||||
OUTPUT_FILE_NAME = "test_org_email_opt_in.csv"
|
||||
OUTPUT_FIELD_NAMES = [
|
||||
"user_id",
|
||||
"username",
|
||||
"email",
|
||||
"full_name",
|
||||
"course_id",
|
||||
@@ -401,6 +402,8 @@ class EmailOptInListTest(ModuleStoreTestCase):
|
||||
|
||||
# Check the header row
|
||||
self.assertEqual({
|
||||
"user_id": "user_id",
|
||||
"username": "username",
|
||||
"email": "email",
|
||||
"full_name": "full_name",
|
||||
"course_id": "course_id",
|
||||
@@ -411,6 +414,8 @@ class EmailOptInListTest(ModuleStoreTestCase):
|
||||
# Check data rows
|
||||
for user, course_id, opt_in_pref in args:
|
||||
self.assertIn({
|
||||
"user_id": str(user.id),
|
||||
"username": user.username.encode('utf-8'),
|
||||
"email": user.email.encode('utf-8'),
|
||||
"full_name": (
|
||||
user.profile.name.encode('utf-8')
|
||||
|
||||
@@ -4,17 +4,15 @@ Unit tests for preference APIs.
|
||||
"""
|
||||
import datetime
|
||||
import ddt
|
||||
import unittest
|
||||
from mock import patch
|
||||
from nose.plugins.attrib import attr
|
||||
from pytz import common_timezones, utc
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.test.utils import override_settings
|
||||
from dateutil.parser import parse as parse_datetime
|
||||
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
|
||||
from openedx.core.lib.time_zone_utils import get_display_time_zone
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
@@ -42,7 +40,7 @@ from ...preferences.api import (
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Account APIs are only supported in LMS')
|
||||
@skip_unless_lms
|
||||
class TestPreferenceAPI(CacheIsolationTestCase):
|
||||
"""
|
||||
These tests specifically cover the parts of the API methods that are not covered by test_views.py.
|
||||
|
||||
@@ -3,17 +3,16 @@
|
||||
Unit tests for preference APIs.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import ddt
|
||||
import json
|
||||
from mock import patch
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.test.testcases import TransactionTestCase
|
||||
from rest_framework.test import APIClient
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from ...accounts.tests.test_views import UserAPITestCase
|
||||
from ..api import set_user_preference
|
||||
from .test_api import get_expected_validation_developer_message, get_expected_key_error_user_message
|
||||
@@ -22,7 +21,7 @@ TOO_LONG_PREFERENCE_KEY = u"x" * 256
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class TestPreferencesAPI(UserAPITestCase):
|
||||
"""
|
||||
Unit tests /api/user/v1/accounts/{username}/
|
||||
@@ -316,7 +315,7 @@ class TestPreferencesAPI(UserAPITestCase):
|
||||
)
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class TestPreferencesAPITransactions(TransactionTestCase):
|
||||
"""
|
||||
Tests the transactional behavior of the preferences API
|
||||
@@ -363,7 +362,7 @@ class TestPreferencesAPITransactions(TransactionTestCase):
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class TestPreferencesDetailAPI(UserAPITestCase):
|
||||
"""
|
||||
Unit tests /api/user/v1/accounts/{username}/{preference_key}
|
||||
|
||||
@@ -21,7 +21,7 @@ from social.apps.django_app.default.models import UserSocialAuth
|
||||
from django_comment_common import models
|
||||
from openedx.core.lib.api.test_utils import ApiTestCase, TEST_API_KEY
|
||||
from openedx.core.lib.time_zone_utils import get_display_time_zone
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
|
||||
from student.tests.factories import UserFactory
|
||||
from third_party_auth.tests.testutil import simulate_running_pipeline, ThirdPartyAuthTestMixin
|
||||
from third_party_auth.tests.utils import (
|
||||
@@ -541,7 +541,7 @@ class PreferenceUsersListViewTest(UserApiTestCase):
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class LoginSessionViewTest(UserAPITestCase):
|
||||
"""Tests for the login end-points of the user API. """
|
||||
|
||||
@@ -713,7 +713,7 @@ class LoginSessionViewTest(UserAPITestCase):
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class PasswordResetViewTest(UserAPITestCase):
|
||||
"""Tests of the user API's password reset endpoint. """
|
||||
|
||||
@@ -771,7 +771,7 @@ class PasswordResetViewTest(UserAPITestCase):
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class RegistrationViewTest(ThirdPartyAuthTestMixin, UserAPITestCase):
|
||||
"""Tests for the registration end-points of the User API. """
|
||||
|
||||
@@ -1925,7 +1925,7 @@ class TestGoogleRegistrationView(
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@skip_unless_lms
|
||||
class UpdateEmailOptInTestCase(UserAPITestCase, SharedModuleStoreTestCase):
|
||||
"""Tests the UpdateEmailOptInPreference view. """
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ Utility classes for testing django applications.
|
||||
"""
|
||||
|
||||
import copy
|
||||
from unittest import skipUnless
|
||||
|
||||
import crum
|
||||
from django import db
|
||||
@@ -175,3 +176,17 @@ def get_mock_request(user=None):
|
||||
request.get_host = lambda: "edx.org"
|
||||
crum.set_current_request(request)
|
||||
return request
|
||||
|
||||
|
||||
def skip_unless_cms(func):
|
||||
"""
|
||||
Only run the decorated test in the CMS test suite
|
||||
"""
|
||||
return skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in CMS')(func)
|
||||
|
||||
|
||||
def skip_unless_lms(func):
|
||||
"""
|
||||
Only run the decorated test in the LMS test suite
|
||||
"""
|
||||
return skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS')(func)
|
||||
|
||||
@@ -49,7 +49,7 @@ edx-django-sites-extensions==2.1.1
|
||||
edx-enterprise==0.9.0
|
||||
edx-oauth2-provider==1.2.0
|
||||
edx-opaque-keys==0.4.0
|
||||
edx-organizations==0.4.1
|
||||
edx-organizations==0.4.2
|
||||
edx-rest-api-client==1.6.0
|
||||
edx-search==0.1.2
|
||||
facebook-sdk==0.4.0
|
||||
|
||||
Reference in New Issue
Block a user