Merge pull request #9438 from edx/ormsbee/instructor_dash_tests_speedup
Speed up Instructor Dash tests + add manual reset for SharedModuleStoreTestCase
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
Modulestore configuration for test cases.
|
||||
"""
|
||||
import datetime
|
||||
import functools
|
||||
import pytz
|
||||
from uuid import uuid4
|
||||
|
||||
@@ -220,7 +221,8 @@ class SharedModuleStoreTestCase(TestCase):
|
||||
Subclass for any test case that uses a ModuleStore that can be shared
|
||||
between individual tests. This class ensures that the ModuleStore is cleaned
|
||||
before/after the entire test case has run. Use this class if your tests
|
||||
set up one or a small number of courses that individual tests do not modify.
|
||||
set up one or a small number of courses that individual tests do not modify
|
||||
(or modify extermely rarely -- see @modifies_courseware).
|
||||
If your tests modify contents in the ModuleStore, you should use
|
||||
ModuleStoreTestCase instead.
|
||||
|
||||
@@ -279,6 +281,52 @@ class SharedModuleStoreTestCase(TestCase):
|
||||
OverrideFieldData.provider_classes = None
|
||||
super(SharedModuleStoreTestCase, self).setUp()
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Manually run tearDownClass/setUpClass again.
|
||||
|
||||
This is so that if you have a mostly read-only course that you're just
|
||||
modifying in one test, you can write `self.reset()` at the
|
||||
end of that test and reset the state of the world for other tests in
|
||||
the class.
|
||||
"""
|
||||
self.tearDownClass()
|
||||
self.setUpClass()
|
||||
|
||||
@staticmethod
|
||||
def modifies_courseware(f):
|
||||
"""
|
||||
Decorator to place around tests that modify course content.
|
||||
|
||||
For performance reasons, SharedModuleStoreTestCase intentionally does
|
||||
not reset the modulestore between individual tests. However, sometimes
|
||||
you might have a test case where the vast majority of tests treat a
|
||||
course as read-only, but one or two want to modify it. In that case, you
|
||||
can do this:
|
||||
|
||||
class MyTestCase(SharedModuleStoreTestCase):
|
||||
# ...
|
||||
@SharedModuleStoreTestCase.modifies_courseware
|
||||
def test_that_edits_modulestore(self):
|
||||
do_something()
|
||||
|
||||
This is equivalent to calling `self.reset()` at the end of
|
||||
your test.
|
||||
|
||||
If you find yourself using this functionality a lot, it might indicate
|
||||
that you should be using ModuleStoreTestCase instead, or that you should
|
||||
break up your tests into different TestCases.
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
"""Call the object method, and reset the test case afterwards."""
|
||||
return_val = f(*args, **kwargs)
|
||||
obj = args[0]
|
||||
obj.reset()
|
||||
return return_val
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class ModuleStoreTestCase(TestCase):
|
||||
"""
|
||||
|
||||
@@ -6,7 +6,7 @@ from nose.tools import raises
|
||||
from nose.plugins.attrib import attr
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
|
||||
from student.roles import CourseBetaTesterRole, CourseStaffRole
|
||||
|
||||
@@ -19,13 +19,15 @@ from instructor.access import (allow_access,
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestInstructorAccessList(ModuleStoreTestCase):
|
||||
class TestInstructorAccessList(SharedModuleStoreTestCase):
|
||||
""" Test access listings. """
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAccessList, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAccessList, self).setUp()
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
|
||||
self.instructors = [UserFactory.create() for _ in xrange(4)]
|
||||
for user in self.instructors:
|
||||
allow_access(self.course, user, 'instructor')
|
||||
@@ -43,8 +45,13 @@ class TestInstructorAccessList(ModuleStoreTestCase):
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestInstructorAccessAllow(ModuleStoreTestCase):
|
||||
class TestInstructorAccessAllow(SharedModuleStoreTestCase):
|
||||
""" Test access allow. """
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAccessAllow, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAccessAllow, self).setUp()
|
||||
|
||||
@@ -79,13 +86,15 @@ class TestInstructorAccessAllow(ModuleStoreTestCase):
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestInstructorAccessRevoke(ModuleStoreTestCase):
|
||||
class TestInstructorAccessRevoke(SharedModuleStoreTestCase):
|
||||
""" Test access revoke. """
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAccessRevoke, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAccessRevoke, self).setUp()
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
|
||||
self.staff = [UserFactory.create() for _ in xrange(4)]
|
||||
for user in self.staff:
|
||||
allow_access(self.course, user, 'staff')
|
||||
@@ -115,15 +124,17 @@ class TestInstructorAccessRevoke(ModuleStoreTestCase):
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestInstructorAccessForum(ModuleStoreTestCase):
|
||||
class TestInstructorAccessForum(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Test forum access control.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAccessForum, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAccessForum, self).setUp()
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
|
||||
self.mod_role = Role.objects.create(
|
||||
course_id=self.course.id,
|
||||
name=FORUM_ROLE_MODERATOR
|
||||
|
||||
@@ -51,7 +51,7 @@ from student.tests.factories import UserFactory, CourseModeFactory, AdminFactory
|
||||
from student.roles import CourseBetaTesterRole, CourseSalesAdminRole, CourseFinanceAdminRole, CourseInstructorRole
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
from xmodule.fields import Date
|
||||
|
||||
@@ -184,22 +184,26 @@ class TestCommonExceptions400(TestCase):
|
||||
@attr('shard_1')
|
||||
@patch('bulk_email.models.html_to_text', Mock(return_value='Mocking CourseEmail.text_message'))
|
||||
@patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': False})
|
||||
class TestInstructorAPIDenyLevels(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Ensure that users cannot access endpoints they shouldn't be able to.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAPIDenyLevels, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
cls.problem_location = msk_from_problem_urlname(
|
||||
cls.course.id,
|
||||
'robot-some-problem-urlname'
|
||||
)
|
||||
cls.problem_urlname = cls.problem_location.to_deprecated_string()
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAPIDenyLevels, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
self.user = UserFactory.create()
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
|
||||
self.problem_location = msk_from_problem_urlname(
|
||||
self.course.id,
|
||||
'robot-some-problem-urlname'
|
||||
)
|
||||
self.problem_urlname = self.problem_location.to_deprecated_string()
|
||||
_module = StudentModule.objects.create(
|
||||
student=self.user,
|
||||
course_id=self.course.id,
|
||||
@@ -347,18 +351,22 @@ class TestInstructorAPIDenyLevels(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
|
||||
@attr('shard_1')
|
||||
@patch.dict(settings.FEATURES, {'ALLOW_AUTOMATED_SIGNUPS': True})
|
||||
class TestInstructorAPIBulkAccountCreationAndEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Test Bulk account creation and enrollment from csv file
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAPIBulkAccountCreationAndEnrollment, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
cls.url = reverse('register_and_enroll_students', kwargs={'course_id': cls.course.id.to_deprecated_string()})
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAPIBulkAccountCreationAndEnrollment, self).setUp()
|
||||
|
||||
self.request = RequestFactory().request()
|
||||
self.course = CourseFactory.create()
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
self.url = reverse('register_and_enroll_students', kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
|
||||
self.not_enrolled_student = UserFactory(
|
||||
username='NotEnrolledStudent',
|
||||
@@ -647,7 +655,7 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(ModuleStoreTestCase, Log
|
||||
|
||||
@attr('shard_1')
|
||||
@ddt.ddt
|
||||
class TestInstructorAPIEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Test enrollment modification endpoint.
|
||||
|
||||
@@ -655,11 +663,23 @@ class TestInstructorAPIEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
job of test_enrollment. This tests the response and action switch.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAPIEnrollment, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
# Email URL values
|
||||
cls.site_name = microsite.get_value(
|
||||
'SITE_NAME',
|
||||
settings.SITE_NAME
|
||||
)
|
||||
cls.about_path = '/courses/{}/about'.format(cls.course.id)
|
||||
cls.course_path = '/courses/{}/'.format(cls.course.id)
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAPIEnrollment, self).setUp()
|
||||
|
||||
self.request = RequestFactory().request()
|
||||
self.course = CourseFactory.create()
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
|
||||
@@ -679,14 +699,6 @@ class TestInstructorAPIEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
self.notregistered_email = 'robot-not-an-email-yet@robot.org'
|
||||
self.assertEqual(User.objects.filter(email=self.notregistered_email).count(), 0)
|
||||
|
||||
# Email URL values
|
||||
self.site_name = microsite.get_value(
|
||||
'SITE_NAME',
|
||||
settings.SITE_NAME
|
||||
)
|
||||
self.about_path = '/courses/{}/about'.format(self.course.id)
|
||||
self.course_path = '/courses/{}/'.format(self.course.id)
|
||||
|
||||
# uncomment to enable enable printing of large diffs
|
||||
# from failed assertions in the event of a test failure.
|
||||
# (comment because pylint C0103(invalid-name))
|
||||
@@ -1399,15 +1411,25 @@ class TestInstructorAPIEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
|
||||
@attr('shard_1')
|
||||
@ddt.ddt
|
||||
class TestInstructorAPIBulkBetaEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Test bulk beta modify access endpoint.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAPIBulkBetaEnrollment, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
# Email URL values
|
||||
cls.site_name = microsite.get_value(
|
||||
'SITE_NAME',
|
||||
settings.SITE_NAME
|
||||
)
|
||||
cls.about_path = '/courses/{}/about'.format(cls.course.id)
|
||||
cls.course_path = '/courses/{}/'.format(cls.course.id)
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAPIBulkBetaEnrollment, self).setUp()
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
|
||||
@@ -1425,14 +1447,6 @@ class TestInstructorAPIBulkBetaEnrollment(ModuleStoreTestCase, LoginEnrollmentTe
|
||||
|
||||
self.request = RequestFactory().request()
|
||||
|
||||
# Email URL values
|
||||
self.site_name = microsite.get_value(
|
||||
'SITE_NAME',
|
||||
settings.SITE_NAME
|
||||
)
|
||||
self.about_path = '/courses/{}/about'.format(self.course.id)
|
||||
self.course_path = '/courses/{}/'.format(self.course.id)
|
||||
|
||||
# uncomment to enable enable printing of large diffs
|
||||
# from failed assertions in the event of a test failure.
|
||||
# (comment because pylint C0103(invalid-name))
|
||||
@@ -1720,7 +1734,7 @@ class TestInstructorAPIBulkBetaEnrollment(ModuleStoreTestCase, LoginEnrollmentTe
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestInstructorAPILevelsAccess(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Test endpoints whereby instructors can change permissions
|
||||
of other users.
|
||||
@@ -1731,11 +1745,14 @@ class TestInstructorAPILevelsAccess(ModuleStoreTestCase, LoginEnrollmentTestCase
|
||||
Actually, modify_access does not have a very meaningful
|
||||
response yet, so only the status code is tested.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAPILevelsAccess, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAPILevelsAccess, self).setUp()
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
|
||||
@@ -1961,14 +1978,17 @@ class TestInstructorAPILevelsAccess(ModuleStoreTestCase, LoginEnrollmentTestCase
|
||||
@attr('shard_1')
|
||||
@ddt.ddt
|
||||
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True})
|
||||
class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Test endpoints that show data without side effects.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAPILevelsDataDump, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAPILevelsDataDump, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
self.course_mode = CourseMode(course_id=self.course.id,
|
||||
mode_slug="honor",
|
||||
mode_display_name="honor cert",
|
||||
@@ -2539,9 +2559,11 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
|
||||
body = response.content.replace('\r', '')
|
||||
self.assertTrue(body.startswith(
|
||||
'"User ID","Anonymized User ID","Course Specific Anonymized User ID"'
|
||||
'\n"3","41","42"\n'
|
||||
'\n"{user_id}","41","42"\n'.format(user_id=self.students[0].id)
|
||||
))
|
||||
self.assertTrue(body.endswith('"8","41","42"\n'))
|
||||
self.assertTrue(
|
||||
body.endswith('"{user_id}","41","42"\n'.format(user_id=self.students[-1].id))
|
||||
)
|
||||
|
||||
def test_list_report_downloads(self):
|
||||
url = reverse('list_report_downloads', kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
@@ -2664,7 +2686,7 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Test endpoints whereby instructors can change student grades.
|
||||
This includes resetting attempts and starting rescore tasks.
|
||||
@@ -2672,23 +2694,24 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
|
||||
This test does NOT test whether the actions had an effect on the
|
||||
database, that is the job of task tests and test_enrollment.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAPIRegradeTask, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
cls.problem_location = msk_from_problem_urlname(
|
||||
cls.course.id,
|
||||
'robot-some-problem-urlname'
|
||||
)
|
||||
cls.problem_urlname = cls.problem_location.to_deprecated_string()
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAPIRegradeTask, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
|
||||
self.student = UserFactory()
|
||||
CourseEnrollment.enroll(self.student, self.course.id)
|
||||
|
||||
self.problem_location = msk_from_problem_urlname(
|
||||
self.course.id,
|
||||
'robot-some-problem-urlname'
|
||||
)
|
||||
|
||||
self.problem_urlname = self.problem_location.to_deprecated_string()
|
||||
|
||||
self.module_to_reset = StudentModule.objects.create(
|
||||
student=self.student,
|
||||
course_id=self.course.id,
|
||||
@@ -2827,21 +2850,51 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
|
||||
|
||||
@attr('shard_1')
|
||||
@patch.dict(settings.FEATURES, {'ENTRANCE_EXAMS': True})
|
||||
class TestEntranceExamInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Test endpoints whereby instructors can rescore student grades,
|
||||
reset student attempts and delete state for entrance exam.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestEntranceExamInstructorAPIRegradeTask, self).setUp()
|
||||
self.course = CourseFactory.create(
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestEntranceExamInstructorAPIRegradeTask, cls).setUpClass()
|
||||
cls.course = CourseFactory.create(
|
||||
org='test_org',
|
||||
course='test_course',
|
||||
run='test_run',
|
||||
entrance_exam_id='i4x://{}/{}/chapter/Entrance_exam'.format('test_org', 'test_course')
|
||||
)
|
||||
self.course_with_invalid_ee = CourseFactory.create(entrance_exam_id='invalid_exam')
|
||||
cls.course_with_invalid_ee = CourseFactory.create(entrance_exam_id='invalid_exam')
|
||||
|
||||
with cls.store.bulk_operations(cls.course.id, emit_signals=False):
|
||||
cls.entrance_exam = ItemFactory.create(
|
||||
parent=cls.course,
|
||||
category='chapter',
|
||||
display_name='Entrance exam'
|
||||
)
|
||||
subsection = ItemFactory.create(
|
||||
parent=cls.entrance_exam,
|
||||
category='sequential',
|
||||
display_name='Subsection 1'
|
||||
)
|
||||
vertical = ItemFactory.create(
|
||||
parent=subsection,
|
||||
category='vertical',
|
||||
display_name='Vertical 1'
|
||||
)
|
||||
cls.ee_problem_1 = ItemFactory.create(
|
||||
parent=vertical,
|
||||
category="problem",
|
||||
display_name="Exam Problem - Problem 1"
|
||||
)
|
||||
cls.ee_problem_2 = ItemFactory.create(
|
||||
parent=vertical,
|
||||
category="problem",
|
||||
display_name="Exam Problem - Problem 2"
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super(TestEntranceExamInstructorAPIRegradeTask, self).setUp()
|
||||
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
# Add instructor to invalid ee course
|
||||
@@ -2851,32 +2904,6 @@ class TestEntranceExamInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollm
|
||||
self.student = UserFactory()
|
||||
CourseEnrollment.enroll(self.student, self.course.id)
|
||||
|
||||
self.entrance_exam = ItemFactory.create(
|
||||
parent=self.course,
|
||||
category='chapter',
|
||||
display_name='Entrance exam'
|
||||
)
|
||||
subsection = ItemFactory.create(
|
||||
parent=self.entrance_exam,
|
||||
category='sequential',
|
||||
display_name='Subsection 1'
|
||||
)
|
||||
vertical = ItemFactory.create(
|
||||
parent=subsection,
|
||||
category='vertical',
|
||||
display_name='Vertical 1'
|
||||
)
|
||||
self.ee_problem_1 = ItemFactory.create(
|
||||
parent=vertical,
|
||||
category="problem",
|
||||
display_name="Exam Problem - Problem 1"
|
||||
)
|
||||
self.ee_problem_2 = ItemFactory.create(
|
||||
parent=vertical,
|
||||
category="problem",
|
||||
display_name="Exam Problem - Problem 2"
|
||||
)
|
||||
|
||||
ee_module_to_reset1 = StudentModule.objects.create(
|
||||
student=self.student,
|
||||
course_id=self.course.id,
|
||||
@@ -3073,27 +3100,30 @@ class TestEntranceExamInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollm
|
||||
@attr('shard_1')
|
||||
@patch('bulk_email.models.html_to_text', Mock(return_value='Mocking CourseEmail.text_message'))
|
||||
@patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': False})
|
||||
class TestInstructorSendEmail(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestInstructorSendEmail(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Checks that only instructors have access to email endpoints, and that
|
||||
these endpoints are only accessible with courses that actually exist,
|
||||
only with valid email messages.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorSendEmail, self).setUp()
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorSendEmail, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
test_subject = u'\u1234 test subject'
|
||||
test_message = u'\u6824 test message'
|
||||
self.full_test_message = {
|
||||
cls.full_test_message = {
|
||||
'send_to': 'staff',
|
||||
'subject': test_subject,
|
||||
'message': test_message,
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorSendEmail, self).setUp()
|
||||
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
|
||||
def test_send_email_as_logged_in_instructor(self):
|
||||
url = reverse('send_email', kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
response = self.client.post(url, self.full_test_message)
|
||||
@@ -3156,7 +3186,7 @@ class MockCompletionInfo(object):
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestInstructorAPITaskLists(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Test instructor task list endpoint.
|
||||
"""
|
||||
@@ -3204,23 +3234,26 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
attr_dict['created'] = attr_dict['created'].isoformat()
|
||||
return attr_dict
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAPITaskLists, self).setUp()
|
||||
self.course = CourseFactory.create(
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAPITaskLists, cls).setUpClass()
|
||||
cls.course = CourseFactory.create(
|
||||
entrance_exam_id='i4x://{}/{}/chapter/Entrance_exam'.format('test_org', 'test_course')
|
||||
)
|
||||
cls.problem_location = msk_from_problem_urlname(
|
||||
cls.course.id,
|
||||
'robot-some-problem-urlname'
|
||||
)
|
||||
cls.problem_urlname = cls.problem_location.to_deprecated_string()
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAPITaskLists, self).setUp()
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
|
||||
self.student = UserFactory()
|
||||
CourseEnrollment.enroll(self.student, self.course.id)
|
||||
|
||||
self.problem_location = msk_from_problem_urlname(
|
||||
self.course.id,
|
||||
'robot-some-problem-urlname'
|
||||
)
|
||||
self.problem_urlname = self.problem_location.to_deprecated_string()
|
||||
|
||||
self.module = StudentModule.objects.create(
|
||||
student=self.student,
|
||||
course_id=self.course.id,
|
||||
@@ -3316,15 +3349,18 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
|
||||
@attr('shard_1')
|
||||
@patch.object(instructor_task.api, 'get_instructor_task_history')
|
||||
class TestInstructorEmailContentList(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestInstructorEmailContentList(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Test the instructor email content history endpoint.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorEmailContentList, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorEmailContentList, self).setUp()
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
self.tasks = {}
|
||||
@@ -3500,10 +3536,30 @@ def get_extended_due(course, unit, user):
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestDueDateExtensions(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Test data dumps for reporting.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestDueDateExtensions, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
cls.due = datetime.datetime(2010, 5, 12, 2, 42, tzinfo=utc)
|
||||
|
||||
with cls.store.bulk_operations(cls.course.id, emit_signals=False):
|
||||
cls.week1 = ItemFactory.create(due=cls.due)
|
||||
cls.week2 = ItemFactory.create(due=cls.due)
|
||||
cls.week3 = ItemFactory.create() # No due date
|
||||
cls.course.children = [
|
||||
cls.week1.location.to_deprecated_string(),
|
||||
cls.week2.location.to_deprecated_string(),
|
||||
cls.week3.location.to_deprecated_string()
|
||||
]
|
||||
cls.homework = ItemFactory.create(
|
||||
parent_location=cls.week1.location,
|
||||
due=cls.due
|
||||
)
|
||||
cls.week1.children = [cls.homework.location.to_deprecated_string()]
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
@@ -3511,75 +3567,55 @@ class TestDueDateExtensions(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
super(TestDueDateExtensions, self).setUp()
|
||||
|
||||
due = datetime.datetime(2010, 5, 12, 2, 42, tzinfo=utc)
|
||||
course = CourseFactory.create()
|
||||
week1 = ItemFactory.create(due=due)
|
||||
week2 = ItemFactory.create(due=due)
|
||||
week3 = ItemFactory.create() # No due date
|
||||
course.children = [week1.location.to_deprecated_string(), week2.location.to_deprecated_string(),
|
||||
week3.location.to_deprecated_string()]
|
||||
|
||||
homework = ItemFactory.create(
|
||||
parent_location=week1.location,
|
||||
due=due
|
||||
)
|
||||
week1.children = [homework.location.to_deprecated_string()]
|
||||
|
||||
user1 = UserFactory.create()
|
||||
StudentModule(
|
||||
state='{}',
|
||||
student_id=user1.id,
|
||||
course_id=course.id,
|
||||
module_state_key=week1.location).save()
|
||||
course_id=self.course.id,
|
||||
module_state_key=self.week1.location).save()
|
||||
StudentModule(
|
||||
state='{}',
|
||||
student_id=user1.id,
|
||||
course_id=course.id,
|
||||
module_state_key=week2.location).save()
|
||||
course_id=self.course.id,
|
||||
module_state_key=self.week2.location).save()
|
||||
StudentModule(
|
||||
state='{}',
|
||||
student_id=user1.id,
|
||||
course_id=course.id,
|
||||
module_state_key=week3.location).save()
|
||||
course_id=self.course.id,
|
||||
module_state_key=self.week3.location).save()
|
||||
StudentModule(
|
||||
state='{}',
|
||||
student_id=user1.id,
|
||||
course_id=course.id,
|
||||
module_state_key=homework.location).save()
|
||||
course_id=self.course.id,
|
||||
module_state_key=self.homework.location).save()
|
||||
|
||||
user2 = UserFactory.create()
|
||||
StudentModule(
|
||||
state='{}',
|
||||
student_id=user2.id,
|
||||
course_id=course.id,
|
||||
module_state_key=week1.location).save()
|
||||
course_id=self.course.id,
|
||||
module_state_key=self.week1.location).save()
|
||||
StudentModule(
|
||||
state='{}',
|
||||
student_id=user2.id,
|
||||
course_id=course.id,
|
||||
module_state_key=homework.location).save()
|
||||
course_id=self.course.id,
|
||||
module_state_key=self.homework.location).save()
|
||||
|
||||
user3 = UserFactory.create()
|
||||
StudentModule(
|
||||
state='{}',
|
||||
student_id=user3.id,
|
||||
course_id=course.id,
|
||||
module_state_key=week1.location).save()
|
||||
course_id=self.course.id,
|
||||
module_state_key=self.week1.location).save()
|
||||
StudentModule(
|
||||
state='{}',
|
||||
student_id=user3.id,
|
||||
course_id=course.id,
|
||||
module_state_key=homework.location).save()
|
||||
course_id=self.course.id,
|
||||
module_state_key=self.homework.location).save()
|
||||
|
||||
self.course = course
|
||||
self.week1 = week1
|
||||
self.homework = homework
|
||||
self.week2 = week2
|
||||
self.week3 = week3
|
||||
self.user1 = user1
|
||||
self.user2 = user2
|
||||
|
||||
self.instructor = InstructorFactory(course_key=course.id)
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
|
||||
def test_change_due_date(self):
|
||||
@@ -3640,6 +3676,7 @@ class TestDueDateExtensions(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
})
|
||||
self.assertEqual(response.status_code, 400, response.content)
|
||||
|
||||
@SharedModuleStoreTestCase.modifies_courseware
|
||||
def test_reset_extension_to_deleted_date(self):
|
||||
"""
|
||||
Test that we can delete a due date extension after deleting the normal
|
||||
@@ -3690,10 +3727,18 @@ class TestDueDateExtensions(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
|
||||
@attr('shard_1')
|
||||
@override_settings(REGISTRATION_CODE_LENGTH=8)
|
||||
class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
class TestCourseRegistrationCodes(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Test data dumps for E-commerce Course Registration Codes.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestCourseRegistrationCodes, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
cls.url = reverse(
|
||||
'generate_registration_codes',
|
||||
kwargs={'course_id': cls.course.id.to_deprecated_string()}
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
@@ -3701,15 +3746,11 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
"""
|
||||
super(TestCourseRegistrationCodes, self).setUp()
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
CourseModeFactory.create(course_id=self.course.id, min_price=50)
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
CourseSalesAdminRole(self.course.id).add_users(self.instructor)
|
||||
|
||||
url = reverse('generate_registration_codes',
|
||||
kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 12, 'company_name': 'Test Group', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 122.45, 'recipient_name': 'Test123',
|
||||
@@ -3718,7 +3759,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': ''
|
||||
}
|
||||
|
||||
response = self.client.post(url, data, **{'HTTP_HOST': 'localhost'})
|
||||
response = self.client.post(self.url, data, **{'HTTP_HOST': 'localhost'})
|
||||
self.assertEqual(response.status_code, 200, response.content)
|
||||
for i in range(5):
|
||||
order = Order(user=self.instructor, status='purchased')
|
||||
@@ -4148,13 +4189,17 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestBulkCohorting(ModuleStoreTestCase):
|
||||
class TestBulkCohorting(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Test adding users to cohorts in bulk via CSV upload.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestBulkCohorting, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestBulkCohorting, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
self.staff_user = StaffFactory(course_key=self.course.id)
|
||||
self.non_staff_user = UserFactory.create()
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
|
||||
@@ -13,15 +13,19 @@ from student.models import CourseEnrollment
|
||||
from student.tests.factories import UserFactory
|
||||
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestInstructorAPIEnrollmentEmailLocalization(ModuleStoreTestCase):
|
||||
class TestInstructorAPIEnrollmentEmailLocalization(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Test whether the enroll, unenroll and beta role emails are sent in the
|
||||
proper language, i.e: the student's language.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorAPIEnrollmentEmailLocalization, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorAPIEnrollmentEmailLocalization, self).setUp()
|
||||
@@ -29,7 +33,6 @@ class TestInstructorAPIEnrollmentEmailLocalization(ModuleStoreTestCase):
|
||||
# Platform language is English, instructor's language is Chinese,
|
||||
# student's language is French, so the emails should all be sent in
|
||||
# French.
|
||||
self.course = CourseFactory.create()
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
set_user_preference(self.instructor, LANGUAGE_KEY, 'zh-cn')
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
|
||||
@@ -8,7 +8,7 @@ from nose.plugins.attrib import attr
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
from django.conf import settings
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from config_models.models import cache
|
||||
from courseware.tests.factories import GlobalStaffFactory, InstructorFactory, UserFactory
|
||||
@@ -18,19 +18,23 @@ from certificates import api as certs_api
|
||||
|
||||
@attr('shard_1')
|
||||
@ddt.ddt
|
||||
class CertificatesInstructorDashTest(ModuleStoreTestCase):
|
||||
class CertificatesInstructorDashTest(SharedModuleStoreTestCase):
|
||||
"""Tests for the certificate panel of the instructor dash. """
|
||||
|
||||
ERROR_REASON = "An error occurred!"
|
||||
DOWNLOAD_URL = "http://www.example.com/abcd123/cert.pdf"
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(CertificatesInstructorDashTest, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
cls.url = reverse(
|
||||
'instructor_dashboard',
|
||||
kwargs={'course_id': unicode(cls.course.id)}
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super(CertificatesInstructorDashTest, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
self.url = reverse(
|
||||
'instructor_dashboard',
|
||||
kwargs={'course_id': unicode(self.course.id)}
|
||||
)
|
||||
self.global_staff = GlobalStaffFactory()
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
|
||||
@@ -189,12 +193,15 @@ class CertificatesInstructorDashTest(ModuleStoreTestCase):
|
||||
@attr('shard_1')
|
||||
@override_settings(CERT_QUEUE='certificates')
|
||||
@ddt.ddt
|
||||
class CertificatesInstructorApiTest(ModuleStoreTestCase):
|
||||
class CertificatesInstructorApiTest(SharedModuleStoreTestCase):
|
||||
"""Tests for the certificates end-points in the instructor dash API. """
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(CertificatesInstructorApiTest, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(CertificatesInstructorApiTest, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
self.global_staff = GlobalStaffFactory()
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
|
||||
|
||||
@@ -13,18 +13,26 @@ from course_modes.models import CourseMode
|
||||
from student.roles import CourseFinanceAdminRole
|
||||
from shoppingcart.models import Coupon, CourseRegistrationCode
|
||||
from student.tests.factories import AdminFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestECommerceDashboardViews(ModuleStoreTestCase):
|
||||
class TestECommerceDashboardViews(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Check for E-commerce view on the new instructor dashboard
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestECommerceDashboardViews, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
# URL for instructor dash
|
||||
cls.url = reverse('instructor_dashboard', kwargs={'course_id': cls.course.id.to_deprecated_string()})
|
||||
cls.e_commerce_link = '<a href="" data-section="e-commerce">E-Commerce</a>'
|
||||
|
||||
def setUp(self):
|
||||
super(TestECommerceDashboardViews, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
|
||||
# Create instructor account
|
||||
self.instructor = AdminFactory.create()
|
||||
@@ -34,9 +42,6 @@ class TestECommerceDashboardViews(ModuleStoreTestCase):
|
||||
mode_display_name='honor', min_price=10, currency='usd'
|
||||
)
|
||||
mode.save()
|
||||
# URL for instructor dash
|
||||
self.url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
self.e_commerce_link = '<a href="" data-section="e-commerce">E-Commerce</a>'
|
||||
CourseFinanceAdminRole(self.course.id).add_users(self.instructor)
|
||||
|
||||
def test_pass_e_commerce_tab_in_instructor_dashboard(self):
|
||||
|
||||
@@ -11,30 +11,36 @@ from nose.plugins.attrib import attr
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
|
||||
from bulk_email.models import CourseAuthorization
|
||||
from xmodule.modulestore.tests.django_utils import TEST_DATA_MIXED_TOY_MODULESTORE, ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import (
|
||||
TEST_DATA_MIXED_TOY_MODULESTORE, SharedModuleStoreTestCase
|
||||
)
|
||||
from student.tests.factories import AdminFactory
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestNewInstructorDashboardEmailViewMongoBacked(ModuleStoreTestCase):
|
||||
class TestNewInstructorDashboardEmailViewMongoBacked(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Check for email view on the new instructor dashboard
|
||||
for Mongo-backed courses
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestNewInstructorDashboardEmailViewMongoBacked, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
# URL for instructor dash
|
||||
cls.url = reverse('instructor_dashboard', kwargs={'course_id': cls.course.id.to_deprecated_string()})
|
||||
# URL for email view
|
||||
cls.email_link = '<a href="" data-section="send_email">Email</a>'
|
||||
|
||||
def setUp(self):
|
||||
super(TestNewInstructorDashboardEmailViewMongoBacked, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
|
||||
# Create instructor account
|
||||
instructor = AdminFactory.create()
|
||||
self.client.login(username=instructor.username, password="test")
|
||||
|
||||
# URL for instructor dash
|
||||
self.url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
# URL for email view
|
||||
self.email_link = '<a href="" data-section="send_email">Email</a>'
|
||||
|
||||
# In order for bulk email to work, we must have both the ENABLE_INSTRUCTOR_EMAIL_FLAG
|
||||
# set to True and for the course to be Mongo-backed.
|
||||
# The flag is enabled and the course is Mongo-backed (should work)
|
||||
@@ -101,16 +107,25 @@ class TestNewInstructorDashboardEmailViewMongoBacked(ModuleStoreTestCase):
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestNewInstructorDashboardEmailViewXMLBacked(ModuleStoreTestCase):
|
||||
class TestNewInstructorDashboardEmailViewXMLBacked(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Check for email view on the new instructor dashboard
|
||||
"""
|
||||
|
||||
MODULESTORE = TEST_DATA_MIXED_TOY_MODULESTORE
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestNewInstructorDashboardEmailViewXMLBacked, cls).setUpClass()
|
||||
cls.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
|
||||
|
||||
# URL for instructor dash
|
||||
cls.url = reverse('instructor_dashboard', kwargs={'course_id': cls.course_key.to_deprecated_string()})
|
||||
# URL for email view
|
||||
cls.email_link = '<a href="" data-section="send_email">Email</a>'
|
||||
|
||||
def setUp(self):
|
||||
super(TestNewInstructorDashboardEmailViewXMLBacked, self).setUp()
|
||||
self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
|
||||
|
||||
# Create instructor account
|
||||
instructor = AdminFactory.create()
|
||||
|
||||
@@ -30,7 +30,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
|
||||
from submissions import api as sub_api
|
||||
from student.models import anonymous_id_for_user
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
@@ -296,40 +296,40 @@ class TestInstructorUnenrollDB(TestEnrollmentChangeBase):
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestInstructorEnrollmentStudentModule(ModuleStoreTestCase):
|
||||
class TestInstructorEnrollmentStudentModule(SharedModuleStoreTestCase):
|
||||
""" Test student module manipulations. """
|
||||
def setUp(self):
|
||||
super(TestInstructorEnrollmentStudentModule, self).setUp()
|
||||
store = modulestore()
|
||||
self.user = UserFactory()
|
||||
self.course = CourseFactory(
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorEnrollmentStudentModule, cls).setUpClass()
|
||||
cls.course = CourseFactory(
|
||||
name='fake',
|
||||
org='course',
|
||||
run='id',
|
||||
)
|
||||
# pylint: disable=no-member
|
||||
self.course_key = self.course.location.course_key
|
||||
self.parent = ItemFactory(
|
||||
category="library_content",
|
||||
user_id=self.user.id,
|
||||
parent=self.course,
|
||||
publish_item=True,
|
||||
modulestore=store,
|
||||
)
|
||||
self.child = ItemFactory(
|
||||
category="html",
|
||||
user_id=self.user.id,
|
||||
parent=self.parent,
|
||||
publish_item=True,
|
||||
modulestore=store,
|
||||
)
|
||||
self.unrelated = ItemFactory(
|
||||
category="html",
|
||||
user_id=self.user.id,
|
||||
parent=self.course,
|
||||
publish_item=True,
|
||||
modulestore=store,
|
||||
)
|
||||
cls.course_key = cls.course.location.course_key
|
||||
with cls.store.bulk_operations(cls.course.id, emit_signals=False):
|
||||
cls.parent = ItemFactory(
|
||||
category="library_content",
|
||||
parent=cls.course,
|
||||
publish_item=True,
|
||||
)
|
||||
cls.child = ItemFactory(
|
||||
category="html",
|
||||
parent=cls.parent,
|
||||
publish_item=True,
|
||||
)
|
||||
cls.unrelated = ItemFactory(
|
||||
category="html",
|
||||
parent=cls.course,
|
||||
publish_item=True,
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorEnrollmentStudentModule, self).setUp()
|
||||
|
||||
self.user = UserFactory()
|
||||
|
||||
parent_state = json.dumps({'attempts': 32, 'otherstuff': 'alsorobots'})
|
||||
child_state = json.dumps({'attempts': 10, 'whatever': 'things'})
|
||||
unrelated_state = json.dumps({'attempts': 12, 'brains': 'zombie'})
|
||||
@@ -567,26 +567,27 @@ class TestSendBetaRoleEmail(TestCase):
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestGetEmailParams(ModuleStoreTestCase):
|
||||
class TestGetEmailParams(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Test what URLs the function get_email_params returns under different
|
||||
production-like conditions.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestGetEmailParams, self).setUp()
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestGetEmailParams, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
# Explicitly construct what we expect the course URLs to be
|
||||
site = settings.SITE_NAME
|
||||
self.course_url = u'https://{}/courses/{}/'.format(
|
||||
site,
|
||||
self.course.id.to_deprecated_string()
|
||||
)
|
||||
self.course_about_url = self.course_url + 'about'
|
||||
self.registration_url = u'https://{}/register'.format(
|
||||
cls.course_url = u'https://{}/courses/{}/'.format(
|
||||
site,
|
||||
cls.course.id.to_deprecated_string()
|
||||
)
|
||||
cls.course_about_url = cls.course_url + 'about'
|
||||
cls.registration_url = u'https://{}/register'.format(site)
|
||||
|
||||
def setUp(self):
|
||||
super(TestGetEmailParams, self).setUp()
|
||||
|
||||
def test_normal_params(self):
|
||||
# For a normal site, what do we expect to get for the URLs?
|
||||
@@ -612,16 +613,19 @@ class TestGetEmailParams(ModuleStoreTestCase):
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestRenderMessageToString(ModuleStoreTestCase):
|
||||
class TestRenderMessageToString(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Test that email templates can be rendered in a language chosen manually.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestRenderMessageToString, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
cls.subject_template = 'emails/enroll_email_allowedsubject.txt'
|
||||
cls.message_template = 'emails/enroll_email_allowedmessage.txt'
|
||||
|
||||
def setUp(self):
|
||||
super(TestRenderMessageToString, self).setUp()
|
||||
self.subject_template = 'emails/enroll_email_allowedsubject.txt'
|
||||
self.message_template = 'emails/enroll_email_allowedmessage.txt'
|
||||
self.course = CourseFactory.create()
|
||||
|
||||
def get_email_params(self):
|
||||
"""
|
||||
|
||||
@@ -8,14 +8,21 @@ from courseware.models import XModuleUserStateSummaryField
|
||||
from courseware.tests.factories import UserStateSummaryFactory
|
||||
import instructor.hint_manager as view
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class HintManagerTest(ModuleStoreTestCase):
|
||||
class HintManagerTest(SharedModuleStoreTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(HintManagerTest, cls).setUpClass()
|
||||
cls.course = CourseFactory.create(org='Me', number='19.002', display_name='test_course')
|
||||
cls.url = '/courses/Me/19.002/test_course/hint_manager'
|
||||
cls.course_id = cls.course.id
|
||||
cls.problem_id = cls.course_id.make_usage_key('crowdsource_hinter', 'crowdsource_hinter_001')
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
@@ -24,13 +31,9 @@ class HintManagerTest(ModuleStoreTestCase):
|
||||
"""
|
||||
super(HintManagerTest, self).setUp()
|
||||
|
||||
self.course = CourseFactory.create(org='Me', number='19.002', display_name='test_course')
|
||||
self.url = '/courses/Me/19.002/test_course/hint_manager'
|
||||
self.user = UserFactory.create(username='robot', email='robot@edx.org', password='test', is_staff=True)
|
||||
self.c = Client()
|
||||
self.c.login(username='robot', password='test')
|
||||
self.course_id = self.course.id
|
||||
self.problem_id = self.course_id.make_usage_key('crowdsource_hinter', 'crowdsource_hinter_001')
|
||||
UserStateSummaryFactory.create(
|
||||
field_name='hints',
|
||||
usage_id=self.problem_id,
|
||||
|
||||
@@ -12,7 +12,7 @@ from django.core.urlresolvers import reverse
|
||||
from courseware.tests.helpers import LoginEnrollmentTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from student.tests.factories import UserFactory, CourseEnrollmentFactory, AdminFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from student.models import CourseEnrollment, CourseEnrollmentAllowed
|
||||
from instructor.views.legacy import get_and_clean_student_list, send_mail_to_student
|
||||
from django.core import mail
|
||||
@@ -22,10 +22,14 @@ USER_COUNT = 4
|
||||
|
||||
@attr('shard_1')
|
||||
@ddt.ddt
|
||||
class TestInstructorEnrollsStudent(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
class TestInstructorEnrollsStudent(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Check Enrollment/Unenrollment with/without auto-enrollment on activation and with/without email notification
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestInstructorEnrollsStudent, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorEnrollsStudent, self).setUp()
|
||||
@@ -33,8 +37,6 @@ class TestInstructorEnrollsStudent(ModuleStoreTestCase, LoginEnrollmentTestCase)
|
||||
instructor = AdminFactory.create()
|
||||
self.client.login(username=instructor.username, password='test')
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
|
||||
self.users = [
|
||||
UserFactory.create(username="student%d" % i, email="student%d@test.com" % i)
|
||||
for i in xrange(USER_COUNT)
|
||||
|
||||
@@ -9,7 +9,7 @@ from nose.plugins.attrib import attr
|
||||
|
||||
from student.tests.factories import UserFactory, CourseEnrollmentFactory
|
||||
from edxmako.tests import mako_middleware_process_request
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
from instructor.views import legacy
|
||||
@@ -18,12 +18,16 @@ from instructor.views import legacy
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestXss(ModuleStoreTestCase):
|
||||
class TestXss(SharedModuleStoreTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestXss, cls).setUpClass()
|
||||
cls._course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestXss, self).setUp()
|
||||
|
||||
self._request_factory = RequestFactory()
|
||||
self._course = CourseFactory.create()
|
||||
self._evil_student = UserFactory.create(
|
||||
email="robot+evil@edx.org",
|
||||
username="evil-robot",
|
||||
|
||||
@@ -10,29 +10,32 @@ from nose.plugins.attrib import attr
|
||||
|
||||
from student.roles import CourseFinanceAdminRole
|
||||
from student.tests.factories import AdminFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
@patch.dict(settings.FEATURES, {'ENABLE_PROCTORED_EXAMS': True})
|
||||
class TestProctoringDashboardViews(ModuleStoreTestCase):
|
||||
class TestProctoringDashboardViews(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Check for Proctoring view on the new instructor dashboard
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestProctoringDashboardViews, cls).setUpClass()
|
||||
cls.course = CourseFactory.create(enable_proctored_exams=True)
|
||||
|
||||
# URL for instructor dash
|
||||
cls.url = reverse('instructor_dashboard', kwargs={'course_id': cls.course.id.to_deprecated_string()})
|
||||
cls.proctoring_link = '<a href="" data-section="proctoring">Proctoring</a>'
|
||||
|
||||
def setUp(self):
|
||||
super(TestProctoringDashboardViews, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
self.course.enable_proctored_exams = True
|
||||
|
||||
# Create instructor account
|
||||
self.instructor = AdminFactory.create()
|
||||
self.client.login(username=self.instructor.username, password="test")
|
||||
self.course = self.update_course(self.course, self.instructor.id)
|
||||
|
||||
# URL for instructor dash
|
||||
self.url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
self.proctoring_link = '<a href="" data-section="proctoring">Proctoring</a>'
|
||||
CourseFinanceAdminRole(self.course.id).add_users(self.instructor)
|
||||
|
||||
def test_pass_proctoring_tab_in_instructor_dashboard(self):
|
||||
|
||||
@@ -15,19 +15,22 @@ import json
|
||||
from student.tests.factories import UserFactory, CourseModeFactory
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
@override_settings(REGISTRATION_CODE_LENGTH=8)
|
||||
class TestCourseRegistrationCodeStatus(ModuleStoreTestCase):
|
||||
class TestCourseRegistrationCodeStatus(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Test registration code status.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestCourseRegistrationCodeStatus, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestCourseRegistrationCodeStatus, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
CourseModeFactory.create(course_id=self.course.id, min_price=50)
|
||||
self.instructor = InstructorFactory(course_key=self.course.id)
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
|
||||
@@ -3,7 +3,7 @@ Tests for the InstructorService
|
||||
"""
|
||||
|
||||
import json
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from courseware.models import StudentModule
|
||||
from instructor.services import InstructorService
|
||||
@@ -15,31 +15,31 @@ from student.tests.factories import UserFactory
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class InstructorServiceTests(ModuleStoreTestCase):
|
||||
class InstructorServiceTests(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Tests for the InstructorService
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(InstructorServiceTests, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
cls.problem_location = msk_from_problem_urlname(
|
||||
cls.course.id,
|
||||
'robot-some-problem-urlname'
|
||||
)
|
||||
cls.other_problem_location = msk_from_problem_urlname(
|
||||
cls.course.id,
|
||||
'robot-some-other_problem-urlname'
|
||||
)
|
||||
cls.problem_urlname = unicode(cls.problem_location)
|
||||
cls.other_problem_urlname = unicode(cls.other_problem_location)
|
||||
|
||||
def setUp(self):
|
||||
super(InstructorServiceTests, self).setUp()
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
self.student = UserFactory()
|
||||
CourseEnrollment.enroll(self.student, self.course.id)
|
||||
|
||||
self.problem_location = msk_from_problem_urlname(
|
||||
self.course.id,
|
||||
'robot-some-problem-urlname'
|
||||
)
|
||||
|
||||
self.other_problem_location = msk_from_problem_urlname(
|
||||
self.course.id,
|
||||
'robot-some-other_problem-urlname'
|
||||
)
|
||||
|
||||
self.problem_urlname = unicode(self.problem_location)
|
||||
self.other_problem_urlname = unicode(self.other_problem_location)
|
||||
|
||||
self.service = InstructorService()
|
||||
self.module_to_reset = StudentModule.objects.create(
|
||||
student=self.student,
|
||||
|
||||
@@ -6,7 +6,7 @@ from django.core.urlresolvers import reverse
|
||||
from nose.plugins.attrib import attr
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
from student.tests.factories import UserFactory, CourseEnrollmentFactory, AdminFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from capa.tests.response_xml_factory import StringResponseXMLFactory
|
||||
from courseware.tests.factories import StudentModuleFactory
|
||||
from xmodule.modulestore.django import modulestore
|
||||
@@ -16,7 +16,7 @@ USER_COUNT = 11
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestGradebook(ModuleStoreTestCase):
|
||||
class TestGradebook(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Test functionality of the spoc gradebook. Sets up a course with assignments and
|
||||
students who've scored various scores on these assignments. Base class for further
|
||||
@@ -24,45 +24,48 @@ class TestGradebook(ModuleStoreTestCase):
|
||||
"""
|
||||
grading_policy = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestGradebook, cls).setUpClass()
|
||||
|
||||
# Create a course with the desired grading policy (from our class attribute)
|
||||
kwargs = {}
|
||||
if cls.grading_policy is not None:
|
||||
kwargs['grading_policy'] = cls.grading_policy
|
||||
cls.course = CourseFactory.create(**kwargs)
|
||||
|
||||
# Now give it some content
|
||||
with cls.store.bulk_operations(cls.course.id, emit_signals=False):
|
||||
chapter = ItemFactory.create(
|
||||
parent_location=cls.course.location,
|
||||
category="sequential",
|
||||
)
|
||||
section = ItemFactory.create(
|
||||
parent_location=chapter.location,
|
||||
category="sequential",
|
||||
metadata={'graded': True, 'format': 'Homework'}
|
||||
)
|
||||
cls.items = [
|
||||
ItemFactory.create(
|
||||
parent_location=section.location,
|
||||
category="problem",
|
||||
data=StringResponseXMLFactory().build_xml(answer='foo'),
|
||||
metadata={'rerandomize': 'always'}
|
||||
)
|
||||
for __ in xrange(USER_COUNT - 1)
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TestGradebook, self).setUp()
|
||||
|
||||
instructor = AdminFactory.create()
|
||||
self.client.login(username=instructor.username, password='test')
|
||||
|
||||
# remove the caches
|
||||
modulestore().request_cache = None
|
||||
modulestore().metadata_inheritance_cache_subsystem = None
|
||||
|
||||
kwargs = {}
|
||||
if self.grading_policy is not None:
|
||||
kwargs['grading_policy'] = self.grading_policy
|
||||
|
||||
self.course = CourseFactory.create(**kwargs)
|
||||
chapter = ItemFactory.create(
|
||||
parent_location=self.course.location,
|
||||
category="sequential",
|
||||
)
|
||||
section = ItemFactory.create(
|
||||
parent_location=chapter.location,
|
||||
category="sequential",
|
||||
metadata={'graded': True, 'format': 'Homework'}
|
||||
)
|
||||
|
||||
self.users = [UserFactory.create() for _ in xrange(USER_COUNT)]
|
||||
|
||||
for user in self.users:
|
||||
CourseEnrollmentFactory.create(user=user, course_id=self.course.id)
|
||||
|
||||
for i in xrange(USER_COUNT - 1):
|
||||
category = "problem"
|
||||
item = ItemFactory.create(
|
||||
parent_location=section.location,
|
||||
category=category,
|
||||
data=StringResponseXMLFactory().build_xml(answer='foo'),
|
||||
metadata={'rerandomize': 'always'}
|
||||
)
|
||||
|
||||
for i, item in enumerate(self.items):
|
||||
for j, user in enumerate(self.users):
|
||||
StudentModuleFactory.create(
|
||||
grade=1 if i < j else 0,
|
||||
|
||||
@@ -14,7 +14,7 @@ from nose.plugins.attrib import attr
|
||||
from courseware.field_overrides import OverrideFieldData # pylint: disable=import-error
|
||||
from student.tests.factories import UserFactory # pylint: disable=import-error
|
||||
from xmodule.fields import Date
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
@@ -102,23 +102,17 @@ class TestParseDatetime(unittest.TestCase):
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class TestFindUnit(ModuleStoreTestCase):
|
||||
class TestFindUnit(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Test the find_unit function.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Fixtures.
|
||||
"""
|
||||
super(TestFindUnit, self).setUp()
|
||||
|
||||
course = CourseFactory.create()
|
||||
week1 = ItemFactory.create(parent=course)
|
||||
homework = ItemFactory.create(parent=week1)
|
||||
|
||||
self.course = course
|
||||
self.homework = homework
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestFindUnit, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
with cls.store.bulk_operations(cls.course.id, emit_signals=False):
|
||||
week1 = ItemFactory.create(parent=cls.course)
|
||||
cls.homework = ItemFactory.create(parent=week1)
|
||||
|
||||
def test_find_unit_success(self):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user