def test_id_to_question_dict_constructed_correctly(self): """Tests id_to_question dicts are constructed correctly.""" course = self._get_sample_v15_course() tracker = UnitLessonCompletionTracker(course) assert_equals( tracker.get_id_to_questions_dict(), { 'u.1.l.2.c.QN': { 'answer_counts': [0], 'label': 'Unit 1 Lesson 1, Question mcq_new', 'location': 'unit?unit=1&lesson=2', 'num_attempts': 0, 'score': 0 }, 'u.1.l.2.c.QG.i.0': { 'answer_counts': [0], 'label': ('Unit 1 Lesson 1, Question Group question_group ' 'Question mcq_new'), 'location': 'unit?unit=1&lesson=2', 'num_attempts': 0, 'score': 0 }, 'u.1.l.2.c.QG.i.2': { 'answer_counts': [0], 'label': ('Unit 1 Lesson 1, Question Group question_group ' 'Question mcq_new'), 'location': 'unit?unit=1&lesson=2', 'num_attempts': 0, 'score': 0 } } ) assert_equals( tracker.get_id_to_assessments_dict(), { 's.4.c.QN2': { 'answer_counts': [0], 'label': 'New assessment, Question mcq_new', 'location': 'assessment?name=4', 'num_attempts': 0, 'score': 0 }, 's.4.c.QG2.i.0': { 'answer_counts': [0], 'label': ('New assessment, Question Group question_group ' 'Question mcq_new'), 'location': 'assessment?name=4', 'num_attempts': 0, 'score': 0 }, 's.4.c.QG2.i.2': { 'answer_counts': [0], 'label': ('New assessment, Question Group question_group ' 'Question mcq_new'), 'location': 'assessment?name=4', 'num_attempts': 0, 'score': 0 } } )
def test_student_cannot_see_reviews_prematurely(self): """Test that students cannot see others' reviews prematurely.""" email = '*****@*****.**' name = 'Student 1' submission = transforms.dumps([ {'index': 0, 'type': 'regex', 'value': 'S1-1', 'correct': True}, {'index': 1, 'type': 'choices', 'value': 3, 'correct': False}, {'index': 2, 'type': 'regex', 'value': 'is-S1', 'correct': True}, ]) payload = { 'answers': submission, 'assessment_type': LEGACY_REVIEW_UNIT_ID} actions.login(email) actions.register(self, name) response = actions.submit_assessment( self, LEGACY_REVIEW_UNIT_ID, payload) # Student 1 cannot see the reviews for his assignment yet, because he # has not submitted the two required reviews. response = self.get('assessment?name=%s' % LEGACY_REVIEW_UNIT_ID) assert_equals(response.status_int, 200) assert_contains('Due date for this assignment', response.body) assert_contains( 'After you have completed the required number of peer reviews', response.body) actions.logout()
def test_get_entity_id_wrapper_in_progress_works(self): """Tests get_entity_id wrappers in progress.ProgressStats.""" sites.setup_courses('course:/test::ns_test, course:/:/') course = courses.Course(None, app_context=sites.get_all_courses()[0]) progress_stats = ProgressStats(course) unit1 = course.add_unit() # pylint: disable-msg=protected-access assert_equals( progress_stats._get_unit_ids_of_type_unit(), [unit1.unit_id]) assessment1 = course.add_assessment() assert_equals( progress_stats._get_assessment_ids(), [assessment1.unit_id]) lesson11 = course.add_lesson(unit1) lesson12 = course.add_lesson(unit1) assert_equals( progress_stats._get_lesson_ids(unit1.unit_id), [lesson11.lesson_id, lesson12.lesson_id]) lesson11.has_activity = True course.set_activity_content(lesson11, u'var activity=[]', []) assert_equals( progress_stats._get_activity_ids(unit1.unit_id, lesson11.lesson_id), [0]) assert_equals( progress_stats._get_activity_ids(unit1.unit_id, lesson12.lesson_id), [])
def test_student_cannot_see_reviews_prematurely(self): """Test that students cannot see others' reviews prematurely.""" email = "*****@*****.**" name = "Student 1" submission = transforms.dumps( [ {"index": 0, "type": "regex", "value": "S1-1", "correct": True}, {"index": 1, "type": "choices", "value": 3, "correct": False}, {"index": 2, "type": "regex", "value": "is-S1", "correct": True}, ] ) payload = {"answers": submission, "assessment_type": LEGACY_REVIEW_UNIT_ID} actions.login(email) actions.register(self, name) response = actions.submit_assessment(self, LEGACY_REVIEW_UNIT_ID, payload) # Student 1 cannot see the reviews for his assignment yet, because he # has not submitted the two required reviews. response = self.get("assessment?name=%s" % LEGACY_REVIEW_UNIT_ID) assert_equals(response.status_int, 200) assert_contains("Due date for this assignment", response.body) assert_contains("After you have completed the required number of peer reviews", response.body) actions.logout()
def test_compute_entity_dict_constructs_dict_correctly(self): sites.setup_courses('course:/test::ns_test, course:/:/') course = courses.Course(None, app_context=sites.get_all_courses()[0]) progress_stats = ProgressStats(course) course_dict = progress_stats.compute_entity_dict('course', []) assert_equals(course_dict, { 'label': 'UNTITLED COURSE', 'u': {}, 's': {}})
def test_empty_student_progress_stats_analytics_displays_nothing(self): """Test analytics page on course dashboard when no progress stats.""" # The admin looks at the analytics page on the board to check right # message when no progress has been recorded. email = '*****@*****.**' actions.login(email, is_admin=True) response = self.get('dashboard?action=analytics') assert_contains( 'Google > Dashboard > Analytics', response.body) assert_contains('have not been calculated yet', response.body) compute_form = response.forms['gcb-compute-student-stats'] response = self.submit(compute_form) assert_equals(response.status_int, 302) assert len(self.taskq.GetTasks('default')) == 4 response = self.get('dashboard?action=analytics') assert_contains('is running', response.body) self.execute_all_deferred_tasks() response = self.get('dashboard?action=analytics') assert_contains('were last updated at', response.body) assert_contains('currently enrolled: 0', response.body) assert_contains('total: 0', response.body) assert_contains('Student Progress Statistics', response.body) assert_contains( 'No student progress has been recorded for this course.', response.body) actions.logout()
def test_manage_announcements(self): """Test course author can manage announcements.""" email = '*****@*****.**' name = 'Test Announcements' actions.login(email, True) actions.register(self, name) # add new response = actions.view_announcements(self) add_form = response.forms['gcb-add-announcement'] response = self.submit(add_form) assert_equals(response.status_int, 302) # check added response = actions.view_announcements(self) assert_contains('Sample Announcement (Draft)', response.body) # delete draft response = actions.view_announcements(self) delete_form = response.forms['gcb-delete-announcement-1'] response = self.submit(delete_form) assert_equals(response.status_int, 302) # check deleted assert_does_not_contain('Welcome to the final class!', response.body)
def test_attempt_activity_event(self): """Test activity attempt generates event.""" email = '*****@*****.**' name = 'Test Attempt Activity Event' actions.login(email) actions.register(self, name) # Enable event recording. config.Registry.db_overrides[ lessons.CAN_PERSIST_ACTIVITY_EVENTS.name] = True # Prepare event. request = {} request['source'] = 'test-source' request['payload'] = json.dumps({'Alice': 'Bob'}) # Check XSRF token is required. response = self.post('rest/events?%s' % urllib.urlencode( {'request': json.dumps(request)}), {}) assert_equals(response.status_int, 200) assert_contains('"status": 403', response.body) # Check PUT works. request['xsrf_token'] = XsrfTokenManager.create_xsrf_token( 'event-post') response = self.post('rest/events?%s' % urllib.urlencode( {'request': json.dumps(request)}), {}) assert_equals(response.status_int, 200) assert not response.body # Clean up. config.Registry.db_overrides = {}
def test_get_entity_id_wrapper_in_progress_works(self): """Tests get_entity_id wrappers in progress.ProgressStats.""" sites.setup_courses('course:/test::ns_test, course:/:/') course = courses.Course(None, app_context=sites.get_all_courses()[0]) progress_stats = ProgressStats(course) unit1 = course.add_unit() assert_equals( progress_stats._get_unit_ids_of_type_unit(), [unit1.unit_id]) assessment1 = course.add_assessment() assert_equals( progress_stats._get_assessment_ids(), [assessment1.unit_id]) lesson11 = course.add_lesson(unit1) lesson12 = course.add_lesson(unit1) assert_equals( progress_stats._get_lesson_ids(unit1.unit_id), [lesson11.lesson_id, lesson12.lesson_id]) lesson11.has_activity = True course.set_activity_content(lesson11, u'var activity=[]', []) assert_equals( progress_stats._get_activity_ids(unit1.unit_id, lesson11.lesson_id), [0]) assert_equals( progress_stats._get_activity_ids(unit1.unit_id, lesson12.lesson_id), [])
def test_student_progress_stats_analytics_displays_on_dashboard(self): """Test analytics page on course dashboard.""" self.enable_progress_tracking() student1 = '*****@*****.**' name1 = 'Test Student 1' student2 = '*****@*****.**' name2 = 'Test Student 2' # Student 1 completes a unit. actions.login(student1) actions.register(self, name1) actions.view_unit(self) actions.logout() # Student 2 completes a unit. actions.login(student2) actions.register(self, name2) actions.view_unit(self) actions.logout() # Admin logs back in and checks if progress exists. email = '*****@*****.**' actions.login(email, is_admin=True) response = self.get('dashboard?action=analytics') assert_contains( 'Google ><a href="%s"> Dashboard </a>> Analytics' % self.canonicalize('dashboard'), response.body) assert_contains('have not been calculated yet', response.body) compute_form = response.forms['gcb-compute-student-stats'] response = self.submit(compute_form) assert_equals(response.status_int, 302) assert len(self.taskq.GetTasks('default')) == 5 response = self.get('dashboard?action=analytics') assert_contains('is running', response.body) self.execute_all_deferred_tasks() response = self.get('dashboard?action=analytics') assert_contains('were last updated at', response.body) assert_contains('currently enrolled: 2', response.body) assert_contains('total: 2', response.body) assert_contains('Student Progress Statistics', response.body) assert_does_not_contain( 'No student progress has been recorded for this course.', response.body) # JSON code for the completion statistics. assert_contains( '\\"u.1.l.1\\": {\\"progress\\": 0, \\"completed\\": 2}', response.body) assert_contains( '\\"u.1\\": {\\"progress\\": 2, \\"completed\\": 0}', response.body)
def test_compute_question_stats_on_empty_course_returns_empty_dicts(self): sites.setup_courses('course:/test::ns_test, course:/:/') app_context = sites.get_all_courses()[0] question_stats_computer = analytics.ComputeQuestionStats(app_context) id_to_questions, id_to_assessments = question_stats_computer.run() assert_equals({}, id_to_questions) assert_equals({}, id_to_assessments)
def test_student_progress_stats_analytics_displays_on_dashboard(self): """Test analytics page on course dashboard.""" self.enable_progress_tracking() student1 = '*****@*****.**' name1 = 'Test Student 1' student2 = '*****@*****.**' name2 = 'Test Student 2' # Student 1 completes a unit. actions.login(student1) actions.register(self, name1) actions.view_unit(self) actions.logout() # Student 2 completes a unit. actions.login(student2) actions.register(self, name2) actions.view_unit(self) actions.logout() # Admin logs back in and checks if progress exists. email = '*****@*****.**' actions.login(email, is_admin=True) response = self.get('dashboard?action=analytics') assert_contains( 'Google > Dashboard > Analytics', response.body) assert_contains('have not been calculated yet', response.body) compute_form = response.forms['gcb-compute-student-stats'] response = self.submit(compute_form) assert_equals(response.status_int, 302) assert len(self.taskq.GetTasks('default')) == 4 response = self.get('dashboard?action=analytics') assert_contains('is running', response.body) self.execute_all_deferred_tasks() response = self.get('dashboard?action=analytics') assert_contains('were last updated at', response.body) assert_contains('currently enrolled: 2', response.body) assert_contains('total: 2', response.body) assert_contains('Student Progress Statistics', response.body) assert_does_not_contain( 'No student progress has been recorded for this course.', response.body) # JSON code for the completion statistics. assert_contains( '\\"u.1.l.1\\": {\\"progress\\": 0, \\"completed\\": 2}', response.body) assert_contains( '\\"u.1\\": {\\"progress\\": 2, \\"completed\\": 0}', response.body)
def test_compute_question_stats_on_empty_course_returns_empty_dicts(self): sites.setup_courses('course:/test::ns_test, course:/:/') app_context = sites.get_all_courses()[0] question_stats_computer = ( synchronous_providers.QuestionStatsGenerator(app_context)) id_to_questions, id_to_assessments = question_stats_computer.run() assert_equals({}, id_to_questions) assert_equals({}, id_to_assessments)
def test_announcements_rest(self): """Test REST access to announcements.""" email = '*****@*****.**' name = 'Test Announcements Rest' actions.login(email, True) actions.register(self, name) response = actions.view_announcements(self) assert_does_not_contain('My Test Title', response.body) # REST GET existing item items = AnnouncementEntity.all().fetch(1) for item in items: response = self.get('rest/announcements/item?key=%s' % item.key()) json_dict = json.loads(response.body) assert json_dict['status'] == 200 assert 'message' in json_dict assert 'payload' in json_dict payload_dict = json.loads(json_dict['payload']) assert 'title' in payload_dict assert 'date' in payload_dict # REST PUT item payload_dict['title'] = u'My Test Title Мой заголовок теста' payload_dict['date'] = '2012/12/31' payload_dict['is_draft'] = True request = {} request['key'] = str(item.key()) request['payload'] = json.dumps(payload_dict) # Check XSRF is required. response = self.put('rest/announcements/item?%s' % urllib.urlencode( {'request': json.dumps(request)}), {}) assert_equals(response.status_int, 200) assert_contains('"status": 403', response.body) # Check PUT works. request['xsrf_token'] = json_dict['xsrf_token'] response = self.put('rest/announcements/item?%s' % urllib.urlencode( {'request': json.dumps(request)}), {}) assert_equals(response.status_int, 200) assert_contains('"status": 200', response.body) # Confirm change is visible on the page. response = self.get('announcements') assert_contains( u'My Test Title Мой заголовок теста (Draft)', response.body) # REST GET not-existing item response = self.get('rest/announcements/item?key=not_existent_key') json_dict = json.loads(response.body) assert json_dict['status'] == 404
def validate_course_data(self, course): """Check course data is valid.""" old_namespace = namespace_manager.get_namespace() namespace_manager.set_namespace(course.namespace) try: students = models.Student.all().fetch(1000) assert len(students) == 1 for student in students: assert_equals(course.email, student.key().name()) assert_equals(course.name, student.name) finally: namespace_manager.set_namespace(old_namespace)
def test_xsrf_defence(self): """Test defense against XSRF attack.""" email = '*****@*****.**' name = 'Test Xsrf Defence' actions.login(email) actions.register(self, name) response = self.get('student/home') response.form.set('name', 'My New Name') response.form.set('xsrf_token', 'bad token') response = response.form.submit(expect_errors=True) assert_equals(response.status_int, 403)
def test_get_summarized_question_list_from_event(self): """Tests the transform functions per event type.""" sites.setup_courses('course:/test::ns_test, course:/:/') course = courses.Course(None, app_context=sites.get_all_courses()[0]) question_aggregator = (synchronous_providers.QuestionStatsGenerator. MultipleChoiceQuestionAggregator(course)) event_payloads = open( os.path.join(appengine_config.BUNDLE_ROOT, 'tests/unit/common/event_payloads.json')).read() event_payload_dict = transforms.loads(event_payloads) for event_info in event_payload_dict.values(): questions = question_aggregator._process_event( event_info['event_source'], event_info['event_data']) assert_equals(questions, event_info['transformed_dict_list'])
def test_anonymous_access(self): """Tests for disabled course explorer page.""" # disable the explorer config.Registry.test_overrides[ course_explorer.GCB_ENABLE_COURSE_EXPLORER_PAGE.name] = False self.assertFalse(course_explorer.GCB_ENABLE_COURSE_EXPLORER_PAGE.value) # check root URL's properly redirect to login response = self.get('/') assert_equals(response.status_int, 302) assert_contains( 'http://localhost/admin?action=welcome', response.location) response = self.get('/assets/img/your_logo_here.png') assert_equals(response.status_int, 302) assert_contains('accounts/Login', response.location) # check explorer pages are not accessible not_accessibles = [ '/explorer', '/explorer/courses', '/explorer/profile', '/explorer/assets/img/your_logo_here.png'] for not_accessible in not_accessibles: response = self.get(not_accessible, expect_errors=True) assert_equals(response.status_int, 404) # enable course explorer config.Registry.test_overrides[ course_explorer.GCB_ENABLE_COURSE_EXPLORER_PAGE.name] = True self.assertTrue(course_explorer.GCB_ENABLE_COURSE_EXPLORER_PAGE.value) # check explorer pages are accessible accessibles = [ '/explorer', '/explorer/courses', '/explorer/assets/img/your_logo_here.png'] for accessible in accessibles: response = self.get(accessible, expect_errors=True) assert_equals(response.status_int, 200) # check student pages are not accessible response = self.get('/explorer/profile') assert_equals(response.status_int, 302) self.assertEqual('http://localhost/explorer', response.location)
def test_static_files_cache_control(self): """Test static/zip handlers use proper Cache-Control headers.""" # Check static handler. response = self.get('/assets/css/main.css') assert_equals(response.status_int, 200) assert_contains('max-age=600', response.headers['Cache-Control']) assert_contains('public', response.headers['Cache-Control']) assert_does_not_contain('no-cache', response.headers['Cache-Control']) # Check zip file handler. response = self.get( '/static/inputex-3.1.0/src/inputex/assets/skins/sam/inputex.css') assert_equals(response.status_int, 200) assert_contains('max-age=600', response.headers['Cache-Control']) assert_contains('public', response.headers['Cache-Control']) assert_does_not_contain('no-cache', response.headers['Cache-Control'])
def submit_assessment(self, name, args): """Test student taking an assessment.""" response = self.get('assessment?name=%s' % name) assert_contains( '<script src="assets/js/assessment-%s.js"></script>' % name, response.body) # Extract XSRF token from the page. match = re.search(r'assessmentXsrfToken = [\']([^\']+)', response.body) assert match xsrf_token = match.group(1) args['xsrf_token'] = xsrf_token response = self.post('answer', args) assert_equals(response.status_int, 200) return response
def test_anonymous_access(self): """Tests for disabled course explorer page.""" # disable the explorer config.Registry.test_overrides[ course_explorer.GCB_ENABLE_COURSE_EXPLORER_PAGE.name] = False self.assertFalse(course_explorer.GCB_ENABLE_COURSE_EXPLORER_PAGE.value) # check root URL's properly redirect to login response = self.get('/') assert_equals(response.status_int, 302) assert_contains( 'http://localhost/admin/welcome', response.location) response = self.get('/assets/img/your_logo_here.png') assert_equals(response.status_int, 302) assert_contains('accounts/Login', response.location) # check explorer pages are not accessible not_accessibles = [ '/explorer', '/explorer/courses', '/explorer/profile', '/explorer/assets/img/your_logo_here.png'] for not_accessible in not_accessibles: response = self.get(not_accessible, expect_errors=True) assert_equals(response.status_int, 404) # enable course explorer config.Registry.test_overrides[ course_explorer.GCB_ENABLE_COURSE_EXPLORER_PAGE.name] = True self.assertTrue(course_explorer.GCB_ENABLE_COURSE_EXPLORER_PAGE.value) # check explorer pages are accessible accessibles = [ '/explorer', '/explorer/courses', '/explorer/assets/img/your_logo_here.png'] for accessible in accessibles: response = self.get(accessible, expect_errors=True) assert_equals(response.status_int, 200) # check student pages are not accessible response = self.get('/explorer/profile') assert_equals(response.status_int, 302) self.assertEqual('http://localhost/explorer', response.location)
def test_add_reviewer(self): """Test that admin can add a reviewer, and cannot re-add reviewers.""" email = '*****@*****.**' name = 'Test Add Reviewer' submission = transforms.dumps([ {'index': 0, 'type': 'regex', 'value': 'First answer to Q1', 'correct': True}, {'index': 1, 'type': 'choices', 'value': 3, 'correct': False}, {'index': 2, 'type': 'regex', 'value': 'First answer to Q3', 'correct': True}, ]) payload = { 'answers': submission, 'assessment_type': LEGACY_REVIEW_UNIT_ID} actions.login(email) actions.register(self, name) response = actions.submit_assessment( self, LEGACY_REVIEW_UNIT_ID, payload) # There is nothing to review on the review dashboard. response = actions.request_new_review( self, LEGACY_REVIEW_UNIT_ID, expected_status_code=200) assert_does_not_contain('Assignment to review', response.body) assert_contains('Sorry, there are no new submissions ', response.body) actions.logout() # The admin assigns the student to review his own work. actions.login(email, is_admin=True) response = actions.add_reviewer( self, LEGACY_REVIEW_UNIT_ID, email, email) assert_equals(response.status_int, 302) response = self.get(response.location) assert_does_not_contain( 'Error 412: The reviewer is already assigned', response.body) assert_contains('First answer to Q1', response.body) assert_contains( 'Review 1 from [email protected]', response.body) # The admin repeats the 'add reviewer' action. This should fail. response = actions.add_reviewer( self, LEGACY_REVIEW_UNIT_ID, email, email) assert_equals(response.status_int, 302) response = self.get(response.location) assert_contains( 'Error 412: The reviewer is already assigned', response.body)
def test_get_summarized_question_list_from_event(self): """Tests the transform functions per event type.""" sites.setup_courses('course:/test::ns_test, course:/:/') course = courses.Course(None, app_context=sites.get_all_courses()[0]) question_aggregator = (synchronous_providers.QuestionStatsGenerator .MultipleChoiceQuestionAggregator(course)) event_payloads = open(os.path.join( appengine_config.BUNDLE_ROOT, 'tests/unit/common/event_payloads.json')).read() event_payload_dict = transforms.loads(event_payloads) for event_info in event_payload_dict.values(): questions = question_aggregator._process_event( event_info['event_source'], event_info['event_data']) assert_equals(questions, event_info['transformed_dict_list'])
def test_attempt_activity_event(self): """Test activity attempt generates event.""" email = '*****@*****.**' name = 'Test Attempt Activity Event' actions.login(email) actions.register(self, name) # Enable event recording. config.Registry.db_overrides[ lessons.CAN_PERSIST_ACTIVITY_EVENTS.name] = True # Prepare event. request = {} request['source'] = 'test-source' request['payload'] = json.dumps({'Alice': u'Bob (тест данные)'}) # Check XSRF token is required. response = self.post('rest/events?%s' % urllib.urlencode( {'request': json.dumps(request)}), {}) assert_equals(response.status_int, 200) assert_contains('"status": 403', response.body) # Check PUT works. request['xsrf_token'] = XsrfTokenManager.create_xsrf_token( 'event-post') response = self.post('rest/events?%s' % urllib.urlencode( {'request': json.dumps(request)}), {}) assert_equals(response.status_int, 200) assert not response.body # Check event is properly recorded. old_namespace = namespace_manager.get_namespace() namespace_manager.set_namespace(self.namespace) try: events = models.EventEntity.all().fetch(1000) assert 1 == len(events) assert_contains( u'Bob (тест данные)', json.loads(events[0].data)['Alice']) finally: namespace_manager.set_namespace(old_namespace) # Clean up. config.Registry.db_overrides = {}
def test_course_pass(self): """Test student passing final exam.""" email = '*****@*****.**' name = 'Test Pass' post = {'assessment_type': 'postcourse', 'score': '100.00'} # Register. actions.login(email) actions.register(self, name) # Submit answer. response = self.submit_assessment('Post', post) assert_equals(response.status_int, 200) assert_contains('Your score is 70%', response.body) assert_contains('you have passed the course', response.body) # Check that the result shows up on the profile page. response = actions.check_profile(self, name) assert_contains('70', response.body) assert_contains('100', response.body)
def test_access_to_admin_pages(self): """Test access to admin pages.""" # assert anonymous user has no access response = self.testapp.get('/admin?action=settings') assert_equals(response.status_int, 302) # assert admin user has access email = '*****@*****.**' name = 'Test Access to Admin Pages' actions.login(email, True) actions.register(self, name) response = self.testapp.get('/admin') assert_contains('Power Searching with Google', response.body) assert_contains('All Courses', response.body) response = self.testapp.get('/admin?action=settings') assert_contains('gcb_admin_user_emails', response.body) assert_contains('gcb_config_update_interval_sec', response.body) assert_contains('All Settings', response.body) response = self.testapp.get('/admin?action=perf') assert_contains('gcb-admin-uptime-sec:', response.body) assert_contains('In-process Performance Counters', response.body) response = self.testapp.get('/admin?action=deployment') assert_contains('application_id: testbed-test', response.body) assert_contains('About the Application', response.body) actions.unregister(self) actions.logout() # assert not-admin user has no access actions.login(email) actions.register(self, name) response = self.testapp.get('/admin?action=settings') assert_equals(response.status_int, 302)
def test_get_entity_label_wrapper_in_progress_works(self): """Tests get_entity_label wrappers in progress.ProgressStats.""" sites.setup_courses('course:/test::ns_test, course:/:/') course = courses.Course(None, app_context=sites.get_all_courses()[0]) progress_stats = ProgressStats(course) unit1 = course.add_unit() # pylint: disable-msg=protected-access assert_equals( progress_stats._get_unit_label(unit1.unit_id), 'Unit %s' % unit1.index) assessment1 = course.add_assessment() assert_equals( progress_stats._get_assessment_label(assessment1.unit_id), assessment1.title) lesson11 = course.add_lesson(unit1) lesson12 = course.add_lesson(unit1) assert_equals( progress_stats._get_lesson_label(unit1.unit_id, lesson11.lesson_id), lesson11.index) lesson11.has_activity = True course.set_activity_content(lesson11, u'var activity=[]', []) assert_equals( progress_stats._get_activity_label( unit1.unit_id, lesson11.lesson_id, 0), 'L1.1') assert_equals( progress_stats._get_activity_label( unit1.unit_id, lesson12.lesson_id, 0), 'L1.2') lesson12.objectives = """ <question quid="123" weight="1" instanceid=1></question> random_text <gcb-youtube videoid="Kdg2drcUjYI" instanceid="VD"></gcb-youtube> more_random_text <question-group qgid="456" instanceid=2></question-group> yet_more_random_text """ cpt_ids = progress_stats._get_component_ids( unit1.unit_id, lesson12.lesson_id, 0) self.assertEqual(set([u'1', u'2']), set(cpt_ids))
def test_get_entity_label_wrapper_in_progress_works(self): """Tests get_entity_label wrappers in progress.ProgressStats.""" sites.setup_courses('course:/test::ns_test, course:/:/') course = courses.Course(None, app_context=sites.get_all_courses()[0]) progress_stats = ProgressStats(course) unit1 = course.add_unit() # pylint: disable-msg=protected-access assert_equals(progress_stats._get_unit_label(unit1.unit_id), 'Unit %s' % unit1.index) assessment1 = course.add_assessment() assert_equals( progress_stats._get_assessment_label(assessment1.unit_id), assessment1.title) lesson11 = course.add_lesson(unit1) lesson12 = course.add_lesson(unit1) assert_equals( progress_stats._get_lesson_label(unit1.unit_id, lesson11.lesson_id), lesson11.index) lesson11.has_activity = True course.set_activity_content(lesson11, u'var activity=[]', []) assert_equals( progress_stats._get_activity_label(unit1.unit_id, lesson11.lesson_id, 0), 'L1.1') assert_equals( progress_stats._get_activity_label(unit1.unit_id, lesson12.lesson_id, 0), 'L1.2') lesson12.objectives = """ <question quid="123" weight="1" instanceid=1></question> random_text <gcb-youtube videoid="Kdg2drcUjYI" instanceid="VD"></gcb-youtube> more_random_text <question-group qgid="456" instanceid=2></question-group> yet_more_random_text """ cpt_ids = progress_stats._get_component_ids(unit1.unit_id, lesson12.lesson_id, 0) self.assertEqual(set([u'1', u'2']), set(cpt_ids))
def test_add_reviewer(self): """Test that admin can add a reviewer, and cannot re-add reviewers.""" email = "*****@*****.**" name = "Test Add Reviewer" submission = transforms.dumps( [ {"index": 0, "type": "regex", "value": "First answer to Q1", "correct": True}, {"index": 1, "type": "choices", "value": 3, "correct": False}, {"index": 2, "type": "regex", "value": "First answer to Q3", "correct": True}, ] ) payload = {"answers": submission, "assessment_type": LEGACY_REVIEW_UNIT_ID} actions.login(email) actions.register(self, name) response = actions.submit_assessment(self, LEGACY_REVIEW_UNIT_ID, payload) # There is nothing to review on the review dashboard. response = actions.request_new_review(self, LEGACY_REVIEW_UNIT_ID, expected_status_code=200) assert_does_not_contain("Assignment to review", response.body) assert_contains("Sorry, there are no new submissions ", response.body) actions.logout() # The admin assigns the student to review his own work. actions.login(email, is_admin=True) response = actions.add_reviewer(self, LEGACY_REVIEW_UNIT_ID, email, email) assert_equals(response.status_int, 302) response = self.get(response.location) assert_does_not_contain("Error 412: The reviewer is already assigned", response.body) assert_contains("First answer to Q1", response.body) assert_contains("Review 1 from [email protected]", response.body) # The admin repeats the 'add reviewer' action. This should fail. response = actions.add_reviewer(self, LEGACY_REVIEW_UNIT_ID, email, email) assert_equals(response.status_int, 302) response = self.get(response.location) assert_contains("Error 412: The reviewer is already assigned", response.body)
def test_view_announcements(self): """Test student aspect of announcements.""" email = '*****@*****.**' name = 'Test Announcements' actions.login(email) actions.register(self, name) # Check no announcements yet. response = actions.view_announcements(self) assert_does_not_contain('Example Announcement', response.body) assert_does_not_contain('Welcome to the final class!', response.body) assert_contains('No announcements yet.', response.body) actions.logout() # Login as admin and add announcements. actions.login('*****@*****.**', True) actions.register(self, 'admin') response = actions.view_announcements(self) actions.logout() # Check we can see non-draft announcements. actions.login(email) response = actions.view_announcements(self) assert_contains('Example Announcement', response.body) assert_does_not_contain('Welcome to the final class!', response.body) assert_does_not_contain('No announcements yet.', response.body) # Check no access to access to draft announcements via REST handler. items = AnnouncementEntity.all().fetch(1000) for item in items: response = self.get('rest/announcements/item?key=%s' % item.key()) if item.is_draft: json_dict = json.loads(response.body) assert json_dict['status'] == 401 else: assert_equals(response.status_int, 200)
def test_python_console(self): """Test access rights to the Python console.""" email = '*****@*****.**' # Check normal user has no access. actions.login(email) response = self.testapp.get('/admin?action=console') assert_equals(response.status_int, 302) response = self.testapp.post('/admin?action=console') assert_equals(response.status_int, 302) # Check delegated admin has no access. os.environ['gcb_admin_user_emails'] = '[%s]' % email actions.login(email) response = self.testapp.get('/admin?action=console') assert_equals(response.status_int, 200) assert_contains( 'You must be an actual admin user to continue.', response.body) response = self.testapp.get('/admin?action=console') assert_equals(response.status_int, 200) assert_contains( 'You must be an actual admin user to continue.', response.body) del os.environ['gcb_admin_user_emails'] # Check actual admin has access. actions.login(email, True) response = self.testapp.get('/admin?action=console') assert_equals(response.status_int, 200) response.form.set('code', 'print "foo" + "bar"') response = self.submit(response.form) assert_contains('foobar', response.body)
def test_single_uncompleted_course(self): """Tests for a single available course.""" # This call should redirect to explorer page. response = self.get('/') assert_contains('/explorer', response.location) name = 'Test student courses page' email = 'Student' actions.login(email) # Test the explorer page. response = self.get('/explorer') assert_equals(response.status_int, 200) assert_contains('Register', response.body) # Navbar should not contain profile tab. assert_does_not_contain('<a href="/explorer/profile">Profile</a>', response.body) # Test 'my courses' page when a student is not enrolled in any course. response = self.get('/explorer/courses') assert_equals(response.status_int, 200) assert_contains('You are not currently enrolled in any course', response.body) # Test 'my courses' page when a student is enrolled in all courses. actions.register(self, name) response = self.get('/explorer/courses') assert_equals(response.status_int, 200) assert_contains('Go to course', response.body) assert_does_not_contain('You are not currently enrolled in any course', response.body) # After the student registers for a course, # profile tab should be visible in navbar. assert_contains('<a href="/explorer/profile">Profile</a>', response.body) # Test profile page. response = self.get('/explorer/profile') assert_contains('<td>%s</td>' % email, response.body) assert_contains('<td>%s</td>' % name, response.body) assert_contains('Progress', response.body) assert_does_not_contain('View score', response.body)
def test_single_uncompleted_course(self): """Tests for a single available course.""" # This call should redirect to explorer page. response = self.get('/') assert_contains('/explorer', response.location) name = 'Test student courses page' email = 'Student' actions.login(email) # Test the explorer page. response = self.get('/explorer') assert_equals(response.status_int, 200) assert_contains('Register', response.body) # Navbar should not contain profile tab. assert_does_not_contain( '<a href="/explorer/profile">Profile</a>', response.body) # Test 'my courses' page when a student is not enrolled in any course. response = self.get('/explorer/courses') assert_equals(response.status_int, 200) assert_contains('You are not currently enrolled in any course', response.body) # Test 'my courses' page when a student is enrolled in all courses. actions.register(self, name) response = self.get('/explorer/courses') assert_equals(response.status_int, 200) assert_contains('Go to course', response.body) assert_does_not_contain('You are not currently enrolled in any course', response.body) # After the student registers for a course, # profile tab should be visible in navbar. assert_contains( '<a href="/explorer/profile">Profile</a>', response.body) # Test profile page. response = self.get('/explorer/profile') assert_contains('<td>%s</td>' % email, response.body) assert_contains('<td>%s</td>' % name, response.body) assert_contains('Progress', response.body) assert_does_not_contain('View score', response.body)
def test_admin_list(self): """Test delegation of admin access to another user.""" email = '*****@*****.**' actions.login(email) # Add environment variable override. os.environ['gcb_admin_user_emails'] = '[%s]' % email # Add datastore override. prop = config.ConfigPropertyEntity( key_name='gcb_config_update_interval_sec') prop.value = '5' prop.is_draft = False prop.put() # Check user has access now. response = self.testapp.get('/admin?action=settings') assert_equals(response.status_int, 200) # Check overrides are active and have proper management actions. assert_contains('gcb_admin_user_emails', response.body) assert_contains('[[email protected]]', response.body) assert_contains( '/admin?action=config_override&name=gcb_admin_user_emails', response.body) assert_contains( '/admin?action=config_edit&name=gcb_config_update_interval_sec', response.body) # Check editor page has proper actions. response = self.testapp.get( '/admin?action=config_edit&name=gcb_config_update_interval_sec') assert_equals(response.status_int, 200) assert_contains('/admin?action=config_reset', response.body) assert_contains('name=gcb_config_update_interval_sec', response.body) # Remove override. del os.environ['gcb_admin_user_emails'] # Check user has no access. response = self.testapp.get('/admin?action=settings') assert_equals(response.status_int, 302)
def test_peer_review_analytics(self): """Test analytics page on course dashboard.""" student1 = '*****@*****.**' name1 = 'Test Student 1' student2 = '*****@*****.**' name2 = 'Test Student 2' peer = {'assessment_type': 'ReviewAssessmentExample'} # Student 1 submits a peer review assessment. actions.login(student1) actions.register(self, name1) actions.submit_assessment(self, 'ReviewAssessmentExample', peer) actions.logout() # Student 2 submits the same peer review assessment. actions.login(student2) actions.register(self, name2) actions.submit_assessment(self, 'ReviewAssessmentExample', peer) actions.logout() email = '*****@*****.**' # The admin looks at the analytics page on the dashboard. actions.login(email, is_admin=True) response = self.get('dashboard?action=analytics') assert_contains( 'Google ><a href="%s"> Dashboard </a>> Analytics' % self.canonicalize('dashboard'), response.body) assert_contains('have not been calculated yet', response.body) compute_form = response.forms['gcb-compute-student-stats'] response = self.submit(compute_form) assert_equals(response.status_int, 302) assert len(self.taskq.GetTasks('default')) == 5 response = self.get('dashboard?action=analytics') assert_contains('is running', response.body) self.execute_all_deferred_tasks() response = self.get('dashboard?action=analytics') assert_contains('were last updated at', response.body) assert_contains('currently enrolled: 2', response.body) assert_contains('total: 2', response.body) assert_contains('Peer Review Statistics', response.body) assert_contains('Sample peer review assignment', response.body) # JSON code for the completion statistics. assert_contains('"[{\\"stats\\": [2]', response.body) actions.logout() # Student2 requests a review. actions.login(student2) response = actions.request_new_review(self, LEGACY_REVIEW_UNIT_ID) review_step_key_2_for_1 = get_review_step_key(response) assert_contains('Assignment to review', response.body) # Student2 submits the review. response = actions.submit_review( self, LEGACY_REVIEW_UNIT_ID, review_step_key_2_for_1, get_review_payload('R2for1')) assert_contains( 'Your review has been submitted successfully', response.body) actions.logout() actions.login(email, is_admin=True) response = self.get('dashboard?action=analytics') assert_contains( 'Google ><a href="%s"> Dashboard </a>> Analytics' % self.canonicalize('dashboard'), response.body) compute_form = response.forms['gcb-compute-student-stats'] response = self.submit(compute_form) self.execute_all_deferred_tasks() response = self.get('dashboard?action=analytics') assert_contains('Peer Review Statistics', response.body) # JSON code for the completion statistics. assert_contains('"[{\\"stats\\": [1, 1]', response.body) actions.logout()
def test_draft_review_behaviour(self): """Test correctness of draft review visibility.""" email1 = '*****@*****.**' name1 = 'Student 1' submission1 = transforms.dumps([ { 'index': 0, 'type': 'regex', 'value': 'S1-1', 'correct': True }, { 'index': 1, 'type': 'choices', 'value': 3, 'correct': False }, { 'index': 2, 'type': 'regex', 'value': 'is-S1', 'correct': True }, ]) payload1 = { 'answers': submission1, 'assessment_type': LEGACY_REVIEW_UNIT_ID } email2 = '*****@*****.**' name2 = 'Student 2' submission2 = transforms.dumps([ { 'index': 0, 'type': 'regex', 'value': 'S2-1', 'correct': True }, { 'index': 1, 'type': 'choices', 'value': 3, 'correct': False }, { 'index': 2, 'type': 'regex', 'value': 'not-S1', 'correct': True }, ]) payload2 = { 'answers': submission2, 'assessment_type': LEGACY_REVIEW_UNIT_ID } email3 = '*****@*****.**' name3 = 'Student 3' submission3 = transforms.dumps([ { 'index': 0, 'type': 'regex', 'value': 'S3-1', 'correct': True }, { 'index': 1, 'type': 'choices', 'value': 3, 'correct': False }, { 'index': 2, 'type': 'regex', 'value': 'not-S1', 'correct': True }, ]) payload3 = { 'answers': submission3, 'assessment_type': LEGACY_REVIEW_UNIT_ID } # Student 1 submits the assignment. actions.login(email1) actions.register(self, name1) response = actions.submit_assessment(self, LEGACY_REVIEW_UNIT_ID, payload1) actions.logout() # Student 2 logs in and submits the assignment. actions.login(email2) actions.register(self, name2) response = actions.submit_assessment(self, LEGACY_REVIEW_UNIT_ID, payload2) # Student 2 requests a review, and is given Student 1's assignment. response = actions.request_new_review(self, LEGACY_REVIEW_UNIT_ID) review_step_key_2_for_1 = get_review_step_key(response) assert_contains('S1-1', response.body) # Student 2 saves her review as a draft. review_2_for_1_payload = get_review_payload('R2for1', is_draft=True) response = actions.submit_review(self, LEGACY_REVIEW_UNIT_ID, review_step_key_2_for_1, review_2_for_1_payload) assert_contains('Your review has been saved.', response.body) response = self.get('reviewdashboard?unit=%s' % LEGACY_REVIEW_UNIT_ID) assert_equals(response.status_int, 200) assert_contains('(Draft)', response.body) # Student 2's draft is still changeable. response = actions.view_review(self, LEGACY_REVIEW_UNIT_ID, review_step_key_2_for_1) assert_contains('Submit Review', response.body) response = actions.submit_review(self, LEGACY_REVIEW_UNIT_ID, review_step_key_2_for_1, review_2_for_1_payload) assert_contains('Your review has been saved.', response.body) # Student 2 logs out. actions.logout() # Student 3 submits the assignment. actions.login(email3) actions.register(self, name3) response = actions.submit_assessment(self, LEGACY_REVIEW_UNIT_ID, payload3) actions.logout() # Student 1 logs in and requests two assignments to review. actions.login(email1) response = self.get('/reviewdashboard?unit=%s' % LEGACY_REVIEW_UNIT_ID) response = actions.request_new_review(self, LEGACY_REVIEW_UNIT_ID) assert_contains('Assignment to review', response.body) assert_contains('not-S1', response.body) review_step_key_1_for_someone = get_review_step_key(response) response = actions.request_new_review(self, LEGACY_REVIEW_UNIT_ID) assert_contains('Assignment to review', response.body) assert_contains('not-S1', response.body) review_step_key_1_for_someone_else = get_review_step_key(response) response = self.get('reviewdashboard?unit=%s' % LEGACY_REVIEW_UNIT_ID) assert_equals(response.status_int, 200) assert_contains('disabled="true"', response.body) # Student 1 submits both reviews, fulfilling his quota. review_1_for_other_payload = get_review_payload('R1for') response = actions.submit_review(self, LEGACY_REVIEW_UNIT_ID, review_step_key_1_for_someone, review_1_for_other_payload) assert_contains('Your review has been submitted successfully', response.body) response = actions.submit_review(self, LEGACY_REVIEW_UNIT_ID, review_step_key_1_for_someone_else, review_1_for_other_payload) assert_contains('Your review has been submitted successfully', response.body) response = self.get('/reviewdashboard?unit=%s' % LEGACY_REVIEW_UNIT_ID) assert_contains('(Completed)', response.body) assert_does_not_contain('(Draft)', response.body) # Although Student 1 has submitted 2 reviews, he cannot view Student # 2's review because it is still in Draft status. response = self.get('assessment?name=%s' % LEGACY_REVIEW_UNIT_ID) assert_equals(response.status_int, 200) assert_contains('You have not received any peer reviews yet.', response.body) assert_does_not_contain('R2for1', response.body) # Student 1 logs out. actions.logout() # Student 2 submits her review for Student 1's assignment. actions.login(email2) response = self.get('review?unit=%s&key=%s' % (LEGACY_REVIEW_UNIT_ID, review_step_key_2_for_1)) assert_does_not_contain('Submitted review', response.body) response = actions.submit_review(self, LEGACY_REVIEW_UNIT_ID, review_step_key_2_for_1, get_review_payload('R2for1')) assert_contains('Your review has been submitted successfully', response.body) # Her review is now read-only. response = self.get('review?unit=%s&key=%s' % (LEGACY_REVIEW_UNIT_ID, review_step_key_2_for_1)) assert_contains('Submitted review', response.body) assert_contains('R2for1', response.body) # Student 2 logs out. actions.logout() # Now Student 1 can see the review he has received from Student 2. actions.login(email1) response = self.get('assessment?name=%s' % LEGACY_REVIEW_UNIT_ID) assert_equals(response.status_int, 200) assert_contains('R2for1', response.body)
def test_submit_assignment(self): """Test submission of peer-reviewed assignments.""" # Override course.yaml settings by patching app_context. get_environ_old = sites.ApplicationContext.get_environ def get_environ_new(self): environ = get_environ_old(self) environ['course']['browsable'] = False return environ sites.ApplicationContext.get_environ = get_environ_new email = '*****@*****.**' name = 'Test Peer Reviewed Assignment Submission' submission = transforms.dumps([ { 'index': 0, 'type': 'regex', 'value': 'First answer to Q1', 'correct': True }, { 'index': 1, 'type': 'choices', 'value': 3, 'correct': False }, { 'index': 2, 'type': 'regex', 'value': 'First answer to Q3', 'correct': True }, ]) second_submission = transforms.dumps([ { 'index': 0, 'type': 'regex', 'value': 'Second answer to Q1', 'correct': True }, { 'index': 1, 'type': 'choices', 'value': 3, 'correct': False }, { 'index': 2, 'type': 'regex', 'value': 'Second answer to Q3', 'correct': True }, ]) # Check that the sample peer-review assignment shows up in the preview # page. response = actions.view_preview(self) assert_contains('Sample peer review assignment', response.body) assert_does_not_contain('Review peer assignments', response.body) actions.login(email) actions.register(self, name) # Check that the sample peer-review assignment shows up in the course # page and that it can be visited. response = actions.view_course(self) assert_contains('Sample peer review assignment', response.body) assert_contains('Review peer assignments', response.body) assert_contains( '<a href="assessment?name=%s">' % LEGACY_REVIEW_UNIT_ID, response.body) assert_contains('<span> Review peer assignments </span>', response.body, collapse_whitespace=True) assert_does_not_contain('<a href="reviewdashboard', response.body, collapse_whitespace=True) # Check that the progress circle for this assignment is unfilled. assert_contains('progress-notstarted-%s' % LEGACY_REVIEW_UNIT_ID, response.body) assert_does_not_contain( 'progress-completed-%s' % LEGACY_REVIEW_UNIT_ID, response.body) # Try to access an invalid assignment. response = self.get('assessment?name=FakeAssessment', expect_errors=True) assert_equals(response.status_int, 404) # The student should not be able to see others' reviews because he/she # has not submitted an assignment yet. response = self.get('assessment?name=%s' % LEGACY_REVIEW_UNIT_ID) assert_does_not_contain('Submitted assignment', response.body) assert_contains('Due date for this assignment', response.body) assert_does_not_contain('Reviews received', response.body) # The student should not be able to access the review dashboard because # he/she has not submitted the assignment yet. response = self.get('reviewdashboard?unit=%s' % LEGACY_REVIEW_UNIT_ID, expect_errors=True) assert_contains('You must submit the assignment for', response.body) # The student submits the assignment. response = actions.submit_assessment( self, LEGACY_REVIEW_UNIT_ID, { 'answers': submission, 'assessment_type': LEGACY_REVIEW_UNIT_ID }) assert_contains('Thank you for completing this assignment', response.body) assert_contains('Review peer assignments', response.body) # The student views the submitted assignment, which has become readonly. response = self.get('assessment?name=%s' % LEGACY_REVIEW_UNIT_ID) assert_contains('First answer to Q1', response.body) assert_contains('Submitted assignment', response.body) # The student tries to re-submit the same assignment. This should fail. response = actions.submit_assessment( self, LEGACY_REVIEW_UNIT_ID, { 'answers': second_submission, 'assessment_type': LEGACY_REVIEW_UNIT_ID }, presubmit_checks=False) assert_contains('You have already submitted this assignment.', response.body) assert_contains('Review peer assignments', response.body) # The student views the submitted assignment. The new answers have not # been saved. response = self.get('assessment?name=%s' % LEGACY_REVIEW_UNIT_ID) assert_contains('First answer to Q1', response.body) assert_does_not_contain('Second answer to Q1', response.body) # The student checks the course page and sees that the progress # circle for this assignment has been filled, and that the 'Review # peer assignments' link is now available. response = actions.view_course(self) assert_contains('progress-completed-%s' % LEGACY_REVIEW_UNIT_ID, response.body) assert_does_not_contain('<span> Review peer assignments </span>', response.body, collapse_whitespace=True) assert_contains('<a href="reviewdashboard?unit=%s">' % LEGACY_REVIEW_UNIT_ID, response.body, collapse_whitespace=True) # The student should also be able to now view the review dashboard. response = self.get('reviewdashboard?unit=%s' % LEGACY_REVIEW_UNIT_ID) assert_contains('Assignments for your review', response.body) assert_contains('Review a new assignment', response.body) actions.logout() # Clean up app_context. sites.ApplicationContext.get_environ = get_environ_old
def test_compute_entity_dict_constructs_dict_for_empty_course_correctly( self): """Tests correct entity_structure is built.""" sites.setup_courses('course:/test::ns_test, course:/:/') course = courses.Course(None, app_context=sites.get_all_courses()[0]) unit1 = course.add_unit() assessment1 = course.add_assessment() progress_stats = ProgressStats(course) assert_equals( progress_stats.compute_entity_dict('course', []), {'label': 'UNTITLED COURSE', 'u': {unit1.unit_id: { 'label': 'Unit %s' % unit1.index, 'l': {}}}, 's': { assessment1.unit_id: {'label': assessment1.title}}}) lesson11 = course.add_lesson(unit1) assert_equals( progress_stats.compute_entity_dict('course', []), { "s": { assessment1.unit_id: { "label": assessment1.title } }, "u": { unit1.unit_id: { "l": { lesson11.lesson_id: { "a": {}, "h": { 0: { "c": {}, "label": "L1.1" } }, "label": lesson11.index } }, "label": "Unit %s" % unit1.index } }, 'label': 'UNTITLED COURSE' }) lesson11.objectives = """ <question quid="123" weight="1" instanceid="1"></question> random_text <gcb-youtube videoid="Kdg2drcUjYI" instanceid="VD"></gcb-youtube> more_random_text <question-group qgid="456" instanceid="2"></question-group> yet_more_random_text """ assert_equals( progress_stats.compute_entity_dict('course', []), { "s": { assessment1.unit_id: { "label": assessment1.title } }, "u": { unit1.unit_id: { "l": { lesson11.lesson_id: { "a": {}, "h": { 0: { "c": { u'1': { "label": "L1.1.1" }, u'2': { "label": "L1.1.2" } }, "label": "L1.1" } }, "label": lesson11.index } }, "label": "Unit %s" % unit1.index } }, "label": 'UNTITLED COURSE' })
def test_independence_of_draft_reviews(self): """Test that draft reviews do not interfere with each other.""" email1 = '*****@*****.**' name1 = 'Student 1' submission1 = transforms.dumps([ { 'index': 0, 'type': 'regex', 'value': 'S1-1', 'correct': True }, { 'index': 1, 'type': 'choices', 'value': 3, 'correct': False }, { 'index': 2, 'type': 'regex', 'value': 'is-S1', 'correct': True }, ]) payload1 = { 'answers': submission1, 'assessment_type': LEGACY_REVIEW_UNIT_ID } email2 = '*****@*****.**' name2 = 'Student 2' submission2 = transforms.dumps([ { 'index': 0, 'type': 'regex', 'value': 'S2-1', 'correct': True }, { 'index': 1, 'type': 'choices', 'value': 3, 'correct': False }, { 'index': 2, 'type': 'regex', 'value': 'not-S1', 'correct': True }, ]) payload2 = { 'answers': submission2, 'assessment_type': LEGACY_REVIEW_UNIT_ID } email3 = '*****@*****.**' name3 = 'Student 3' submission3 = transforms.dumps([ { 'index': 0, 'type': 'regex', 'value': 'S3-1', 'correct': True }, { 'index': 1, 'type': 'choices', 'value': 3, 'correct': False }, { 'index': 2, 'type': 'regex', 'value': 'not-S1', 'correct': True }, ]) payload3 = { 'answers': submission3, 'assessment_type': LEGACY_REVIEW_UNIT_ID } # Student 1 submits the assignment. actions.login(email1) actions.register(self, name1) response = actions.submit_assessment(self, LEGACY_REVIEW_UNIT_ID, payload1) actions.logout() # Student 2 logs in and submits the assignment. actions.login(email2) actions.register(self, name2) response = actions.submit_assessment(self, LEGACY_REVIEW_UNIT_ID, payload2) actions.logout() # Student 3 logs in and submits the assignment. actions.login(email3) actions.register(self, name3) response = actions.submit_assessment(self, LEGACY_REVIEW_UNIT_ID, payload3) actions.logout() # Student 1 logs in and requests two assignments to review. actions.login(email1) response = self.get('/reviewdashboard?unit=%s' % LEGACY_REVIEW_UNIT_ID) response = actions.request_new_review(self, LEGACY_REVIEW_UNIT_ID) assert_equals(response.status_int, 200) assert_contains('Assignment to review', response.body) assert_contains('not-S1', response.body) review_step_key_1_for_someone = get_review_step_key(response) response = actions.request_new_review(self, LEGACY_REVIEW_UNIT_ID) assert_equals(response.status_int, 200) assert_contains('Assignment to review', response.body) assert_contains('not-S1', response.body) review_step_key_1_for_someone_else = get_review_step_key(response) self.assertNotEqual(review_step_key_1_for_someone, review_step_key_1_for_someone_else) # Student 1 submits two draft reviews. response = actions.submit_review( self, LEGACY_REVIEW_UNIT_ID, review_step_key_1_for_someone, get_review_payload('R1forFirst', is_draft=True)) assert_contains('Your review has been saved.', response.body) response = actions.submit_review( self, LEGACY_REVIEW_UNIT_ID, review_step_key_1_for_someone_else, get_review_payload('R1forSecond', is_draft=True)) assert_contains('Your review has been saved.', response.body) # The two draft reviews should still be different when subsequently # accessed. response = self.get( 'review?unit=%s&key=%s' % (LEGACY_REVIEW_UNIT_ID, review_step_key_1_for_someone)) assert_contains('R1forFirst', response.body) response = self.get( 'review?unit=%s&key=%s' % (LEGACY_REVIEW_UNIT_ID, review_step_key_1_for_someone_else)) assert_contains('R1forSecond', response.body) # Student 1 logs out. actions.logout()
def test_submit_assignment(self): """Test submission of peer-reviewed assignments.""" # Override course.yaml settings by patching app_context. get_environ_old = sites.ApplicationContext.get_environ def get_environ_new(self): environ = get_environ_old(self) environ['course']['browsable'] = False return environ sites.ApplicationContext.get_environ = get_environ_new email = '*****@*****.**' name = 'Test Peer Reviewed Assignment Submission' submission = transforms.dumps([ {'index': 0, 'type': 'regex', 'value': 'First answer to Q1', 'correct': True}, {'index': 1, 'type': 'choices', 'value': 3, 'correct': False}, {'index': 2, 'type': 'regex', 'value': 'First answer to Q3', 'correct': True}, ]) second_submission = transforms.dumps([ {'index': 0, 'type': 'regex', 'value': 'Second answer to Q1', 'correct': True}, {'index': 1, 'type': 'choices', 'value': 3, 'correct': False}, {'index': 2, 'type': 'regex', 'value': 'Second answer to Q3', 'correct': True}, ]) # Check that the sample peer-review assignment shows up in the preview # page. response = actions.view_preview(self) assert_contains('Sample peer review assignment', response.body) assert_does_not_contain('Review peer assignments', response.body) actions.login(email) actions.register(self, name) # Check that the sample peer-review assignment shows up in the course # page and that it can be visited. response = actions.view_course(self) assert_contains('Sample peer review assignment', response.body) assert_contains('Review peer assignments', response.body) assert_contains( '<a href="assessment?name=%s">' % LEGACY_REVIEW_UNIT_ID, response.body) assert_contains('<span> Review peer assignments </span>', response.body, collapse_whitespace=True) assert_does_not_contain('<a href="reviewdashboard', response.body, collapse_whitespace=True) # Check that the progress circle for this assignment is unfilled. assert_contains( 'progress-notstarted-%s' % LEGACY_REVIEW_UNIT_ID, response.body) assert_does_not_contain( 'progress-completed-%s' % LEGACY_REVIEW_UNIT_ID, response.body) # Try to access an invalid assignment. response = self.get( 'assessment?name=FakeAssessment', expect_errors=True) assert_equals(response.status_int, 404) # The student should not be able to see others' reviews because he/she # has not submitted an assignment yet. response = self.get('assessment?name=%s' % LEGACY_REVIEW_UNIT_ID) assert_does_not_contain('Submitted assignment', response.body) assert_contains('Due date for this assignment', response.body) assert_does_not_contain('Reviews received', response.body) # The student should not be able to access the review dashboard because # he/she has not submitted the assignment yet. response = self.get( 'reviewdashboard?unit=%s' % LEGACY_REVIEW_UNIT_ID, expect_errors=True) assert_contains('You must submit the assignment for', response.body) # The student submits the assignment. response = actions.submit_assessment( self, LEGACY_REVIEW_UNIT_ID, {'answers': submission, 'assessment_type': LEGACY_REVIEW_UNIT_ID} ) assert_contains( 'Thank you for completing this assignment', response.body) assert_contains('Review peer assignments', response.body) # The student views the submitted assignment, which has become readonly. response = self.get('assessment?name=%s' % LEGACY_REVIEW_UNIT_ID) assert_contains('First answer to Q1', response.body) assert_contains('Submitted assignment', response.body) # The student tries to re-submit the same assignment. This should fail. response = actions.submit_assessment( self, LEGACY_REVIEW_UNIT_ID, {'answers': second_submission, 'assessment_type': LEGACY_REVIEW_UNIT_ID}, presubmit_checks=False ) assert_contains( 'You have already submitted this assignment.', response.body) assert_contains('Review peer assignments', response.body) # The student views the submitted assignment. The new answers have not # been saved. response = self.get('assessment?name=%s' % LEGACY_REVIEW_UNIT_ID) assert_contains('First answer to Q1', response.body) assert_does_not_contain('Second answer to Q1', response.body) # The student checks the course page and sees that the progress # circle for this assignment has been filled, and that the 'Review # peer assignments' link is now available. response = actions.view_course(self) assert_contains( 'progress-completed-%s' % LEGACY_REVIEW_UNIT_ID, response.body) assert_does_not_contain( '<span> Review peer assignments </span>', response.body, collapse_whitespace=True) assert_contains( '<a href="reviewdashboard?unit=%s">' % LEGACY_REVIEW_UNIT_ID, response.body, collapse_whitespace=True) # The student should also be able to now view the review dashboard. response = self.get('reviewdashboard?unit=%s' % LEGACY_REVIEW_UNIT_ID) assert_contains('Assignments for your review', response.body) assert_contains('Review a new assignment', response.body) actions.logout() # Clean up app_context. sites.ApplicationContext.get_environ = get_environ_old
def test_compute_entity_dict_constructs_dict_for_empty_course_correctly( self): """Tests correct entity_structure is built.""" sites.setup_courses('course:/test::ns_test, course:/:/') course = courses.Course(None, app_context=sites.get_all_courses()[0]) unit1 = course.add_unit() assessment1 = course.add_assessment() progress_stats = ProgressStats(course) # pylint: disable-msg=g-inconsistent-quotes assert_equals( progress_stats.compute_entity_dict('course', []), {'label': 'UNTITLED COURSE', 'u': {unit1.unit_id: { 'label': 'Unit %s' % unit1.index, 'l': {}}}, 's': { assessment1.unit_id: {'label': assessment1.title}}}) lesson11 = course.add_lesson(unit1) assert_equals( progress_stats.compute_entity_dict('course', []), { "s": { assessment1.unit_id: { "label": assessment1.title } }, "u": { unit1.unit_id: { "l": { lesson11.lesson_id: { "a": {}, "h": { 0: { "c": {}, "label": "L1.1" } }, "label": lesson11.index } }, "label": "Unit %s" % unit1.index } }, 'label': 'UNTITLED COURSE' }) lesson11.objectives = """ <question quid="123" weight="1" instanceid="1"></question> random_text <gcb-youtube videoid="Kdg2drcUjYI" instanceid="VD"></gcb-youtube> more_random_text <question-group qgid="456" instanceid="2"></question-group> yet_more_random_text """ assert_equals( progress_stats.compute_entity_dict('course', []), { "s": { assessment1.unit_id: { "label": assessment1.title } }, "u": { unit1.unit_id: { "l": { lesson11.lesson_id: { "a": {}, "h": { 0: { "c": { u'1': { "label": "L1.1.1" }, u'2': { "label": "L1.1.2" } }, "label": "L1.1" } }, "label": lesson11.index } }, "label": "Unit %s" % unit1.index } }, "label": 'UNTITLED COURSE' })