def test_welcome_page(self): with actions.OverriddenConfig(sites.GCB_COURSES_CONFIG.name, ''): response = self.get('/admin/welcome') self.assertEquals(200, response.status_int) self.assertIn('Start Using Course Builder', response.body) self.assertIn( 'I agree that Google may collect information about this', response.body) self.assertIn( 'name="%s"' % course_creation.USAGE_REPORTING_CONSENT_CHECKBOX_NAME, response.body)
def add_course_and_register_student(admin_email): google_app_context = actions.simple_add_course( course_name, admin_email, course_title) actions.update_course_config( course_name, {'course': {'now_available': True, 'browsable': True,},}) actions.register(self, 'John Smith', course_name) with actions.OverriddenConfig(config.REPORT_ALLOWED.name, True): usage_reporting.StartReportingJobs._for_testing_only_get() self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.execute_all_deferred_tasks()
def test_announcement_translation_caching(self): LOCALE = 'de' with actions.OverriddenConfig(models.CAN_USE_MEMCACHE.name, True): with actions.OverriddenEnvironment({ 'i18n': { 'course:locale': 'en_US', 'extra_locales': [{ 'locale': LOCALE, 'availability': 'true' }] } }): key = self._add_announcement() data = { 'key': key, 'date': utc.to_text(seconds=0), 'html': 'Unsafe for operation', 'title': 'Attention', 'is_draft': False, } self._put_announcement(data) self._put_translation(data, LOCALE, 'Achtung', 'Gefahrlich!') actions.login('*****@*****.**') actions.register(self, 'John Doe') self._set_prefs_locale(None) self._verify_announcements([data['title']], [data['html']]) self._set_prefs_locale(LOCALE) self._verify_announcements(['Achtung'], ['Gefahrlich!']) # Verify that we have data added to the cache. cached = models.MemcacheManager.get( announcements.AnnouncementEntity._cache_key(LOCALE)) self.assertIsNotNone(cached) # Modify the translated version. actions.login(self.ADMIN_EMAIL) self._put_translation(data, LOCALE, 'Foo', 'Bar') # Verify that the cache has been purged cached = models.MemcacheManager.get( announcements.AnnouncementEntity._cache_key(LOCALE)) self.assertIsNone(cached) # And that the changed translations show up on the page. actions.login('*****@*****.**') self._verify_announcements(['Foo'], ['Bar'])
def test_change_base_announcment_updates_i18n_progress(self): LOCALE = 'de' with actions.OverriddenConfig(models.CAN_USE_MEMCACHE.name, True): with actions.OverriddenEnvironment({ 'course': { 'locale': 'en_US', }, 'extra_locales': [{ 'locale': LOCALE, 'availability': 'true' }] }): key = self._add_announcement() data = { 'key': key, 'date': utc.to_text(seconds=0), 'html': 'Unsafe for operation', 'title': 'Attention', 'is_draft': False, } self._put_announcement(data) self._put_translation(data, LOCALE, 'Achtung', 'Gefahrlich!') # Verify that having saved the translation, we are in progress # state DONE. resource_key = str( resource.Key( announcements.ResourceHandlerAnnouncement.TYPE, db.Key(encoded=data['key']).id())) progress = i18n_dashboard.I18nProgressDAO.load(resource_key) self.assertEquals(progress.get_progress(LOCALE), i18n_dashboard.I18nProgressDTO.DONE) # Modify the announcement in the base language. data['title'] = 'Informational' data['html'] = 'Now safe for operation again' self._put_announcement(data) self.execute_all_deferred_tasks() # Verify that saving the base version of the announcement # moves the progress state back. progress = i18n_dashboard.I18nProgressDAO.load(resource_key) self.assertEquals(progress.get_progress(LOCALE), i18n_dashboard.I18nProgressDTO.IN_PROGRESS)
def test_search_index_translated_announcements(self): LOCALE = 'de' with actions.OverriddenConfig(models.CAN_USE_MEMCACHE.name, True): with actions.OverriddenEnvironment({ 'course': { 'locale': 'en_US', }, 'extra_locales': [{ 'locale': LOCALE, 'availability': 'true' }] }): key = self._add_announcement() data = { 'key': key, 'date': utc.to_text(seconds=0), 'html': 'Unsafe for operation', 'title': 'Attention', 'is_draft': False, } self._put_announcement(data) self._put_translation(data, LOCALE, 'Achtung', 'Gefahrlich!') response = self.post( self.base + '/dashboard?action=index_course', { 'xsrf_token': crypto.XsrfTokenManager.create_xsrf_token( 'index_course') }) self.assertEquals(302, response.status_int) self.execute_all_deferred_tasks() actions.login('*****@*****.**') actions.register(self, 'John Doe') self._set_prefs_locale(LOCALE) response = self.get('search?query=Achtung') soup = self.parse_html_string_to_soup(response.body) snippets = soup.select('.gcb-search-result-snippet') self.assertEquals(snippets[0].text.strip(), 'Gefahrlich!...')
def test_announcement_caching(self): with actions.OverriddenConfig(models.CAN_USE_MEMCACHE.name, True): # Get the fact that there are no announcements into the cache. self._verify_announcements( [], ['Currently, there are no announcements.']) # Add an announcement key = self._add_announcement() data = { 'key': key, 'date': utc.to_text(seconds=0), 'html': 'Twas brillig, and the slithy toves', 'title': 'Jabberwocky', 'is_draft': True, } self._put_announcement(data) # Admin sees announcement on course page. self._verify_announcements([data['title'] + ' (Private)'], [data['html']]) # Capture cache content for later. cache_content = models.MemcacheManager.get( announcements.AnnouncementEntity._MEMCACHE_KEY) # Delete announcement. self._delete_announcement(key) # Check that we see no announcements. self._verify_announcements( [], ['Currently, there are no announcements.']) # Put cache content back and verify we see cache content on page. models.MemcacheManager.set( announcements.AnnouncementEntity._MEMCACHE_KEY, cache_content) self._verify_announcements([data['title'] + ' (Private)'], [data['html']])
def test_end_to_end(self): """Actually enroll and unenroll students; verify reporting counts.""" COURSE_NAME_BASE = 'test' NUM_COURSES = 2 NUM_STUDENTS = 3 THE_TIMESTAMP = 1427245200 for course_num in range(NUM_COURSES): course_name = '%s_%d' % (COURSE_NAME_BASE, course_num) actions.simple_add_course(course_name, ADMIN_EMAIL, course_name) actions.update_course_config(course_name, { 'course': { 'now_available': True, 'browsable': True, }, }) for student_num in range(NUM_STUDENTS): name = '%s_%d_%d' % (COURSE_NAME_BASE, course_num, student_num) actions.login(name + '@foo.com') actions.register(self, name, course_name) if student_num == 0: actions.unregister(self, course_name) actions.logout() # Expect no messages yet; haven't run job. self.assertEquals([], MockSender.get_sent()) # Run all counting jobs. with actions.OverriddenConfig(config.REPORT_ALLOWED.name, True): usage_reporting.StartReportingJobs._for_testing_only_get() self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.execute_all_deferred_tasks() # Verify counts. (Ignore dates, these are fickle and subject to # weirdness on hour boundaries. Also ignore course/instance IDs; # they are non-random and thus all the same.) num_enrolled_msgs = 0 num_unenrolled_msgs = 0 num_student_count_msgs = 0 for message in MockSender.get_sent(): if (message[messaging.Message._METRIC] == messaging.Message.METRIC_STUDENT_COUNT): num_student_count_msgs += 1 self.assertEquals(NUM_STUDENTS, message[messaging.Message._VALUE]) elif (message[messaging.Message._METRIC] == messaging.Message.METRIC_ENROLLED): num_enrolled_msgs += 1 self.assertEquals(NUM_STUDENTS, message[messaging.Message._VALUE]) elif (message[messaging.Message._METRIC] == messaging.Message.METRIC_UNENROLLED): num_unenrolled_msgs += 1 self.assertEquals(1, message[messaging.Message._VALUE]) self.assertEquals(NUM_COURSES, num_enrolled_msgs) self.assertEquals(NUM_COURSES, num_unenrolled_msgs) self.assertEquals(NUM_COURSES, num_student_count_msgs) sites.reset_courses()
def test_multiple_courses(self): COURSE_TWO = 'course_two' COURSE_TWO_NS = 'ns_' + COURSE_TWO # Slight cheat: Register gitkit data remover manually, rather than # enabling the entire module, which disrupts normal functional test # user login handling gitkit.EmailMapping.register_for_data_removal() actions.simple_add_course( COURSE_TWO, self.ADMIN_EMAIL, 'Data Removal Test Two') user = actions.login(self.STUDENT_EMAIL) with actions.OverriddenConfig(models.CAN_SHARE_STUDENT_PROFILE.name, True): actions.register(self, user.email(), course=self.COURSE) actions.register(self, user.email(), course=COURSE_TWO) # Slight cheat: Rather than enabling gitkit module, just call # the method that will insert the EmailMapping row. gitkit.EmailUpdatePolicy.apply(user) # Global profile object(s) should now exist. profile = models.StudentProfileDAO.get_profile_by_user_id( user.user_id()) self.assertIsNotNone(profile) email_policy = gitkit.EmailMapping.get_by_user_id(user.user_id()) self.assertIsNotNone(email_policy) # Unregister from 'data_removal_test' course. self._unregister_and_request_data_removal(self.COURSE) self._complete_removal() # Student object should be gone from data_removal_test course, but # not from course_two. with common_utils.Namespace(self.NAMESPACE): self.assertIsNone(models.Student.get_by_user(user)) with common_utils.Namespace(COURSE_TWO_NS): self.assertIsNotNone(models.Student.get_by_user(user)) # Global profile object(s) should still exist. profile = models.StudentProfileDAO.get_profile_by_user_id( user.user_id()) self.assertIsNotNone(profile) email_policy = gitkit.EmailMapping.get_by_user_id(user.user_id()) self.assertIsNotNone(email_policy) # Unregister from other course. self._unregister_and_request_data_removal(COURSE_TWO) self._complete_removal() # Both Student objects should now be gone. with common_utils.Namespace(self.NAMESPACE): self.assertIsNone(models.Student.get_by_user(user)) with common_utils.Namespace(COURSE_TWO_NS): self.assertIsNone(models.Student.get_by_user(user)) # Global profile object(s) should also be gone. profile = models.StudentProfileDAO.get_profile_by_user_id( user.user_id()) self.assertIsNone(profile) email_policy = gitkit.EmailMapping.get_by_user_id(user.user_id()) self.assertIsNone(email_policy)