def test_course_updates_invalid_url(self): """ Tests the error conditions for the invalid course updates URL. """ # Testing the response code by passing slash separated course id whose format is valid but no course # having this id exists. invalid_course_key = f'{self.course.id}_blah_blah_blah' course_updates_url = reverse_course_url('course_info_handler', invalid_course_key) response = self.client.get(course_updates_url) self.assertEqual(response.status_code, 404) # Testing the response code by passing split course id whose format is valid but no course # having this id exists. split_course_key = CourseLocator(org='orgASD', course='course_01213', run='Run_0_hhh_hhh_hhh') course_updates_url_split = reverse_course_url('course_info_handler', split_course_key) response = self.client.get(course_updates_url_split) self.assertEqual(response.status_code, 404) # Testing the response by passing split course id whose format is invalid. invalid_course_id = f'invalid.course.key/{split_course_key}' course_updates_url_split = reverse_course_url('course_info_handler', invalid_course_id) response = self.client.get(course_updates_url_split) self.assertEqual(response.status_code, 404)
def setUp(self): """ Sets up the test course. """ super(ExportTestCase, self).setUp() self.url = reverse_course_url('export_handler', self.course.id) self.status_url = reverse_course_url('export_status_handler', self.course.id)
def setUp(self): """ Sets up the test course. """ super(ExportTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments self.url = reverse_course_url('export_handler', self.course.id) self.status_url = reverse_course_url('export_status_handler', self.course.id)
def test_json_responses(self): outline_url = reverse_course_url('course_handler', self.course.id) chapter = ItemFactory.create(parent_location=self.course.location, category='chapter', display_name="Week 1") lesson = ItemFactory.create(parent_location=chapter.location, category='sequential', display_name="Lesson 1") subsection = ItemFactory.create( parent_location=lesson.location, category='vertical', display_name='Subsection 1' ) ItemFactory.create(parent_location=subsection.location, category="video", display_name="My Video") resp = self.client.get(outline_url, HTTP_ACCEPT='application/json') json_response = json.loads(resp.content.decode('utf-8')) # First spot check some values in the root response self.assertEqual(json_response['category'], 'course') self.assertEqual(json_response['id'], six.text_type(self.course.location)) self.assertEqual(json_response['display_name'], self.course.display_name) self.assertTrue(json_response['published']) self.assertIsNone(json_response['visibility_state']) # Now verify the first child children = json_response['child_info']['children'] self.assertGreater(len(children), 0) first_child_response = children[0] self.assertEqual(first_child_response['category'], 'chapter') self.assertEqual(first_child_response['id'], six.text_type(chapter.location)) self.assertEqual(first_child_response['display_name'], 'Week 1') self.assertTrue(json_response['published']) self.assertEqual(first_child_response['visibility_state'], VisibilityState.unscheduled) self.assertGreater(len(first_child_response['child_info']['children']), 0) # Finally, validate the entire response for consistency self.assert_correct_json_response(json_response)
def test_delete_image_type_asset(self): """ Tests deletion of image type asset """ image_asset = self.get_sample_asset(self.asset_name, asset_type="image") thumbnail_image_asset = self.get_sample_asset('delete_test_thumbnail', asset_type="image") # upload image response = self.client.post(self.url, {"name": "delete_image_test", "file": image_asset}) self.assertEqual(response.status_code, 200) uploaded_image_url = json.loads(response.content.decode('utf-8'))['asset']['url'] # upload image thumbnail response = self.client.post(self.url, {"name": "delete_image_thumb_test", "file": thumbnail_image_asset}) self.assertEqual(response.status_code, 200) thumbnail_url = json.loads(response.content.decode('utf-8'))['asset']['url'] thumbnail_location = StaticContent.get_location_from_path(thumbnail_url) image_asset_location = AssetKey.from_string(uploaded_image_url) content = contentstore().find(image_asset_location) content.thumbnail_location = thumbnail_location contentstore().save(content) with mock.patch('opaque_keys.edx.locator.CourseLocator.make_asset_key') as mock_asset_key: mock_asset_key.return_value = thumbnail_location test_url = reverse_course_url( 'assets_handler', self.course.id, kwargs={'asset_key_string': str(uploaded_image_url)}) resp = self.client.delete(test_url, HTTP_ACCEPT="application/json") self.assertEqual(resp.status_code, 204)
def test_json_responses(self, is_concise): """ Verify the JSON responses returned for the course. Arguments: is_concise (Boolean) : If True, fetch concise version of course outline. """ outline_url = reverse_course_url('course_handler', self.course.id) outline_url = outline_url + '?format=concise' if is_concise else outline_url resp = self.client.get(outline_url, HTTP_ACCEPT='application/json') json_response = json.loads(resp.content.decode('utf-8')) # First spot check some values in the root response self.assertEqual(json_response['category'], 'course') self.assertEqual(json_response['id'], six.text_type(self.course.location)) self.assertEqual(json_response['display_name'], self.course.display_name) self.assertNotEqual(json_response.get('published', False), is_concise) self.assertIsNone(json_response.get('visibility_state')) # Now verify the first child children = json_response['child_info']['children'] self.assertGreater(len(children), 0) first_child_response = children[0] self.assertEqual(first_child_response['category'], 'chapter') self.assertEqual(first_child_response['id'], six.text_type(self.chapter.location)) self.assertEqual(first_child_response['display_name'], 'Week 1') self.assertNotEqual(json_response.get('published', False), is_concise) if not is_concise: self.assertEqual(first_child_response['visibility_state'], VisibilityState.unscheduled) self.assertGreater(len(first_child_response['child_info']['children']), 0) # Finally, validate the entire response for consistency self.assert_correct_json_response(json_response, is_concise)
def create_update_url(self, provided_id=None, course_key=None): if course_key is None: course_key = self.course.id kwargs = {'provided_id': str(provided_id)} if provided_id else None return reverse_course_url('course_info_update_handler', course_key, kwargs=kwargs)
def setUp(self): super(CreditEligibilityTest, self).setUp() self.course = CourseFactory.create(org='edX', number='dummy', display_name='Credit Course') self.course_details_url = reverse_course_url( 'settings_handler', six.text_type(self.course.id))
def test_exam_settings_alert_with_exam_settings_disabled( self, page_handler): """ An alert should appear if current exam settings are invalid. The exam settings alert should direct users to the advanced settings page if the exam settings feature is not available. """ # create an error by setting proctortrack as the provider and not setting # the (required) escalation contact self.course.proctoring_provider = 'proctortrack' self.course.enable_proctored_exams = True self.save_course() url = reverse_course_url(page_handler, self.course.id) resp = self.client.get(url, HTTP_ACCEPT='text/html') alert_text = self._get_exam_settings_alert_text(resp.content) assert ( 'This course has proctored exam settings that are incomplete or invalid.' in alert_text) self.maxDiff = None if page_handler == 'advanced_settings_handler': assert ( 'You will be unable to make changes until the following settings are updated on the page below.' in alert_text) else: assert 'To update these settings go to the Advanced Settings page.' in alert_text
def test_notifications_handler_dismiss(self): state = CourseRerunUIStateManager.State.FAILED should_display = True rerun_course_key = CourseLocator(org='testx', course='test_course', run='test_run') # add an instructor to this course user2 = UserFactory() add_instructor(rerun_course_key, self.user, user2) # create a test notification rerun_state = CourseRerunState.objects.update_state( course_key=rerun_course_key, new_state=state, allow_not_found=True) CourseRerunState.objects.update_should_display( entry_id=rerun_state.id, user=user2, should_display=should_display) # try to get information on this notification notification_dismiss_url = reverse_course_url( 'course_notifications_handler', self.course.id, kwargs={ 'action_state_id': rerun_state.id, }) resp = self.client.delete(notification_dismiss_url) self.assertEqual(resp.status_code, 200) with self.assertRaises(CourseRerunState.DoesNotExist): # delete nofications that are dismissed CourseRerunState.objects.get(id=rerun_state.id) self.assertFalse(has_course_author_access(user2, rerun_course_key))
def setUp(self): """ Setup test course, user, and url. """ super().setUp() self.course_module = modulestore().get_course(self.course.id) self.test_url = reverse_course_url('export_git', self.course.id)
def test_certificate_activation_success(self, signatory_path): """ Activate and Deactivate the course certificate """ test_url = reverse_course_url('certificate_activation_handler', self.course.id) self._add_course_certificates(count=1, signatory_count=2, asset_path_format=signatory_path) is_active = True for i in range(2): if i == 1: is_active = not is_active response = self.client.post(test_url, data=json.dumps( {"is_active": is_active}), content_type="application/json", HTTP_ACCEPT="application/json", HTTP_X_REQUESTED_WITH="XMLHttpRequest") self.assertEqual(response.status_code, 200) course = self.store.get_course(self.course.id) certificates = course.certificates['certificates'] self.assertEqual(certificates[0].get('is_active'), is_active) cert_event_type = 'activated' if is_active else 'deactivated' self.assert_event_emitted( '.'.join(['edx.certificate.configuration', cert_event_type]), course_id=str(self.course.id), )
def test_output_non_course_author(self): """ Verify that users who aren't authors of the course are unable to see the output of export tasks """ client, _ = self.create_non_staff_authed_user_client() resp = client.get(reverse_course_url('export_output_handler', self.course.id)) self.assertEqual(resp.status_code, 403)
def test_manage_library_users(self): """ Simple test that the Library "User Access" view works. Also tests that we can use the REST API to assign a user to a library. """ library = LibraryFactory.create() extra_user, _ = self.create_non_staff_user() manage_users_url = reverse_library_url( 'manage_library_users', str(library.location.library_key)) response = self.client.get(manage_users_url) self.assertEqual(response.status_code, 200) # extra_user has not been assigned to the library so should not show up in the list: self.assertNotContains(response, extra_user.username) # Now add extra_user to the library: user_details_url = reverse_course_url( 'course_team_handler', library.location.library_key, kwargs={'email': extra_user.email}) edit_response = self.client.ajax_post(user_details_url, {"role": LibraryUserRole.ROLE}) self.assertIn(edit_response.status_code, (200, 204)) # Now extra_user should apear in the list: response = self.client.get(manage_users_url) self.assertEqual(response.status_code, 200) self.assertContains(response, extra_user.username)
def setUp(self): """ Setup test course, user, and url. """ super(TestExportGit, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments self.course_module = modulestore().get_course(self.course.id) self.test_url = reverse_course_url('export_git', self.course.id)
def setUp(self): super(ImportEntranceExamTestCase, self).setUp() self.url = reverse_course_url('import_handler', self.course.id) self.content_dir = path(tempfile.mkdtemp()) self.addCleanup(shutil.rmtree, self.content_dir) # Create tar test file ----------------------------------------------- # OK course with entrance exam section: entrance_exam_dir = tempfile.mkdtemp(dir=self.content_dir) # test course being deeper down than top of tar file embedded_exam_dir = os.path.join(entrance_exam_dir, "grandparent", "parent") os.makedirs(os.path.join(embedded_exam_dir, "course")) os.makedirs(os.path.join(embedded_exam_dir, "chapter")) with open(os.path.join(embedded_exam_dir, "course.xml"), "w+") as f: f.write('<course url_name="2013_Spring" org="EDx" course="0.00x"/>') with open(os.path.join(embedded_exam_dir, "course", "2013_Spring.xml"), "w+") as f: f.write( '<course ' 'entrance_exam_enabled="true" entrance_exam_id="xyz" entrance_exam_minimum_score_pct="0.7">' '<chapter url_name="2015_chapter_entrance_exam"/></course>' ) with open(os.path.join(embedded_exam_dir, "chapter", "2015_chapter_entrance_exam.xml"), "w+") as f: f.write('<chapter display_name="Entrance Exam" in_entrance_exam="true" is_entrance_exam="true"></chapter>') self.entrance_exam_tar = os.path.join(self.content_dir, "entrance_exam.tar.gz") with tarfile.open(self.entrance_exam_tar, "w:gz") as gtar: gtar.add(entrance_exam_dir)
def setUp(self): super(ImportTestCase, self).setUp() self.url = reverse_course_url('import_handler', self.course.id) self.content_dir = path(tempfile.mkdtemp()) self.addCleanup(shutil.rmtree, self.content_dir) def touch(name): """ Equivalent to shell's 'touch'""" with open(name, 'a'): # pylint: disable=W6005 os.utime(name, None) # Create tar test files ----------------------------------------------- # OK course: good_dir = tempfile.mkdtemp(dir=self.content_dir) # test course being deeper down than top of tar file embedded_dir = os.path.join(good_dir, "grandparent", "parent") os.makedirs(os.path.join(embedded_dir, "course")) with open(os.path.join(embedded_dir, "course.xml"), "w+") as f: f.write('<course url_name="2013_Spring" org="EDx" course="0.00x"/>') with open(os.path.join(embedded_dir, "course", "2013_Spring.xml"), "w+") as f: f.write('<course></course>') self.good_tar = os.path.join(self.content_dir, "good.tar.gz") with tarfile.open(self.good_tar, "w:gz") as gtar: gtar.add(good_dir) # Bad course (no 'course.xml' file): bad_dir = tempfile.mkdtemp(dir=self.content_dir) touch(os.path.join(bad_dir, "bad.xml")) self.bad_tar = os.path.join(self.content_dir, "bad.tar.gz") with tarfile.open(self.bad_tar, "w:gz") as btar: btar.add(bad_dir) self.unsafe_common_dir = path(tempfile.mkdtemp(dir=self.content_dir))
def get_details_url(self, textbook_id): """ Returns the URL for textbook detail handler. """ return reverse_course_url('textbooks_detail_handler', self.course.id, kwargs={'textbook_id': textbook_id})
def test_delete_asset(self): """ Tests the happy path :) """ test_url = reverse_course_url( 'assets_handler', self.course.id, kwargs={'asset_key_string': six.text_type(self.uploaded_url)}) resp = self.client.delete(test_url, HTTP_ACCEPT="application/json") self.assertEqual(resp.status_code, 204)
def test_delete_asset_with_invalid_thumbnail(self): """ Tests the sad path :( """ test_url = reverse_course_url( 'assets_handler', self.course.id, kwargs={'asset_key_string': str(self.uploaded_url)}) self.content.thumbnail_location = StaticContent.get_location_from_path('/c4x/edX/toy/asset/invalid') contentstore().save(self.content) resp = self.client.delete(test_url, HTTP_ACCEPT="application/json") self.assertEqual(resp.status_code, 204)
def setUp(self): super().setUp() self.url = reverse_course_url('assets_handler', self.course.id) # First, upload something. self.asset_name = 'download_test' resp = self.upload_asset(self.asset_name) self.assertEqual(resp.status_code, 200) self.uploaded_url = json.loads(resp.content.decode('utf-8'))['asset']['url']
def test_course_index_invalid_url(self): """ Tests the error conditions for the invalid course index URL. """ # Testing the response code by passing slash separated course key, no course # having this key exists. invalid_course_key = '{}_some_invalid_run'.format(self.course.id) course_outline_url = reverse_course_url('course_handler', invalid_course_key) response = self.client.get_html(course_outline_url) self.assertEqual(response.status_code, 404) # Testing the response code by passing split course key, no course # having this key exists. split_course_key = CourseLocator(org='invalid_org', course='course_01111', run='Run_0_invalid') course_outline_url_split = reverse_course_url('course_handler', split_course_key) response = self.client.get_html(course_outline_url_split) self.assertEqual(response.status_code, 404)
def test_delete_asset_with_invalid_asset(self): """ Tests the sad path :( """ test_url = reverse_course_url( 'assets_handler', self.course.id, kwargs={'asset_key_string': "/c4x/edX/toy/asset/invalid.pdf"} ) resp = self.client.delete(test_url, HTTP_ACCEPT="application/json") self.assertEqual(resp.status_code, 404)
def setUp(self): super(DownloadTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments self.url = reverse_course_url('assets_handler', self.course.id) # First, upload something. self.asset_name = 'download_test' resp = self.upload_asset(self.asset_name) self.assertEqual(resp.status_code, 200) self.uploaded_url = json.loads( resp.content.decode('utf-8'))['asset']['url']
def test_negative_conditions(self): """ Test the error conditions for the access """ outline_url = reverse_course_url('course_handler', self.course.id) # register a non-staff member and try to delete the course branch non_staff_client, _ = self.create_non_staff_authed_user_client() response = non_staff_client.delete(outline_url, {}, HTTP_ACCEPT='application/json') self.assertEqual(response.status_code, 403)
def test_view_with_exam_settings_enabled(self, handler): """ Tests pages should have `Exam Settings` item if course does have Exam Settings view enabled. """ outline_url = reverse_course_url(handler, self.course.id) resp = self.client.get(outline_url, HTTP_ACCEPT='text/html') self.assertEqual(resp.status_code, 200) self.assertContains(resp, 'Proctored Exam Settings')
def test_exam_settings_alert_not_shown(self, page_handler): """ Alert should not be visible if no proctored exam setting error exists """ url = reverse_course_url(page_handler, self.course.id) resp = self.client.get(url, HTTP_ACCEPT='text/html') parsed_html = lxml.html.fromstring(resp.content) alert_nodes = parsed_html.find_class('exam-settings-alert') assert len(alert_nodes) == 0
def test_header_menu_with_exam_settings_enabled(self): """ Tests course header menu should have `Exam Settings` menu item if course does have Exam Settings view enabled. """ outline_url = reverse_course_url('course_handler', self.course.id) resp = self.client.get(outline_url, HTTP_ACCEPT='text/html') self.assertEqual(resp.status_code, 200) self.assertContains(resp, '<li class="nav-item nav-course-settings-exams">')
def _url(self, cid=-1): """ Return url for the handler. """ cid = cid if cid > 0 else self._id return reverse_course_url( 'certificates_detail_handler', self.course.id, kwargs={'certificate_id': cid}, )
def _url(self, cid=-1): """ Return url for the handler. """ cid = cid if cid > 0 else self.ID return reverse_course_url( 'group_configurations_detail_handler', self.course.id, kwargs={'group_configuration_id': cid}, )