def test_get_web_certificate_url(self): """ Test the get_certificate_url with a web cert course """ expected_url = reverse( 'certificates:render_cert_by_uuid', kwargs=dict(certificate_uuid=self.uuid) ) cert_url = certs_api.get_certificate_url( user_id=self.student.id, # pylint: disable=no-member course_id=self.web_cert_course.id, uuid=self.uuid ) self.assertEqual(expected_url, cert_url) expected_url = reverse( 'certificates:html_view', kwargs={ "user_id": str(self.student.id), # pylint: disable=no-member "course_id": unicode(self.web_cert_course.id), } ) cert_url = certs_api.get_certificate_url( user_id=self.student.id, # pylint: disable=no-member course_id=self.web_cert_course.id ) self.assertEqual(expected_url, cert_url)
def test_html_view_microsite_configuration_missing(self): test_configuration_string = """{ "default": { "accomplishment_class_append": "accomplishment-certificate", "platform_name": "edX", "company_about_url": "http://www.edx.org/about-us", "company_privacy_url": "http://www.edx.org/edx-privacy-policy", "company_tos_url": "http://www.edx.org/edx-terms-service", "company_verified_certificate_url": "http://www.edx.org/verified-certificate", "document_stylesheet_url_application": "/static/certificates/sass/main-ltr.css", "logo_src": "/static/certificates/images/logo-edx.svg", "logo_url": "http://www.edx.org", "company_about_description": "This should not survive being overwritten by static content" }, "honor": { "certificate_type": "Honor Code" } }""" config = self._certificate_html_view_configuration(configuration_string=test_configuration_string) self.assertEquals(config.configuration, test_configuration_string) test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) self._add_course_certificates(count=1, signatory_count=2) response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME) self.assertIn('edX', response.content) self.assertNotIn('platform_microsite', response.content) self.assertNotIn('http://www.microsite.org', response.content) self.assertNotIn('This should not survive being overwritten by static content', response.content)
def test_render_html_view_invalid_certificate_configuration(self): test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) response = self.client.get(test_url) self.assertIn("Invalid Certificate", response.content)
def test_rendering_course_organization_data(self): """ Test: organization data should render on certificate web view if course has organization. """ test_organization_data = { 'name': 'test organization', 'short_name': 'test_organization', 'description': 'Test Organization Description', 'active': True, 'logo': '/logo_test1.png/' } test_org = organizations_api.add_organization(organization_data=test_organization_data) organizations_api.add_organization_course(organization_data=test_org, course_id=unicode(self.course.id)) self._add_course_certificates(count=1, signatory_count=1, is_active=True) test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) response = self.client.get(test_url) self.assertIn( 'a course of study offered by test_organization, an online learning initiative of test organization', response.content ) self.assertNotIn( 'a course of study offered by testorg', response.content ) self.assertIn( '<title>test_organization {} Certificate |'.format(self.course.number, ), response.content ) self.assertIn('logo_test1.png', response.content)
def test_certificate_custom_template_with_org(self): """ Tests custom template search if we have a single template for organization and mode with course set to Null. This test should check template matching when org={org}, course=Null, mode={mode}. """ course = CourseFactory.create( org='cstX', number='cst_22', display_name='custom template course' ) self._add_course_certificates(count=1, signatory_count=2) self._create_custom_template(org_id=1, mode='honor') self._create_custom_template(org_id=1, mode='honor', course_key=course.id) test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) with patch('certificates.api.get_course_organizations') as mock_get_orgs: mock_get_orgs.side_effect = [ [{"id": 1, "name": "organization name"}], ] response = self.client.get(test_url) self.assertEqual(response.status_code, 200) self.assertContains(response, 'course name: course_title_0')
def test_evidence_event_sent(self): test_url = get_certificate_url(user_id=self.user.id, course_id=self.course_id) + '?evidence_visit=1' self.recreate_tracker() assertion = BadgeAssertion( user=self.user, course_id=self.course_id, mode='honor', data={ 'image': 'http://www.example.com/image.png', 'json': {'id': 'http://www.example.com/assertion.json'}, 'issuer': 'http://www.example.com/issuer.json', } ) assertion.save() response = self.client.get(test_url) self.assertEqual(response.status_code, 200) assert_event_matches( { 'name': 'edx.badge.assertion.evidence_visited', 'data': { 'course_id': 'testorg/run1/refundable_course', # pylint: disable=no-member 'assertion_id': assertion.id, 'assertion_json_url': 'http://www.example.com/assertion.json', 'assertion_image_url': 'http://www.example.com/image.png', 'user_id': self.user.id, 'issuer': 'http://www.example.com/issuer.json', 'enrollment_mode': 'honor', }, }, self.get_event() )
def test_certificate_asset_by_slug(self): """ Tests certificate template asset display by slug using static.certificate_asset_url method. """ self._add_course_certificates(count=1, signatory_count=2) self._create_custom_template(mode='honor') test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) # render certificate without template asset with patch('certificates.api.get_course_organizations') as mock_get_orgs: mock_get_orgs.return_value = [] response = self.client.get(test_url) self.assertContains(response, '<img class="custom-logo" src="" />') template_asset = CertificateTemplateAsset( description='custom logo', asset='certificate_template_assets/32/test_logo.png', asset_slug='custom-logo', ) template_asset.save() # render certificate with template asset with patch('certificates.api.get_course_organizations') as mock_get_orgs: mock_get_orgs.return_value = [] response = self.client.get(test_url) self.assertContains( response, '<img class="custom-logo" src="{}certificate_template_assets/32/test_logo.png" />'.format( settings.MEDIA_URL ) )
def test_render_html_view_invalid_user(self): test_url = get_certificate_url( user_id=111, course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member ) response = self.client.get(test_url) self.assertIn('invalid', response.content)
def test_render_html_view_invalid_course(self): test_url = get_certificate_url( user_id=self.user.id, course_id='missing/course/key' ) response = self.client.get(test_url) self.assertIn('invalid', response.content)
def test_render_html_view_invalid_user(self): test_url = get_certificate_url( user_id=111, course_id=unicode(self.course.id) ) response = self.client.get(test_url) self.assertIn('invalid', response.content)
def test_linked_student_to_web_view_credential(self, enrollment_mode): test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id), verify_uuid="abcdefg12345678" ) self._create_certificate(enrollment_mode) certificates = [ { "id": 0, "name": "Test Name", "description": "Test Description", "is_active": True, "signatories": [], "version": 1, } ] self.course.certificates = {"certificates": certificates} self.course.cert_html_view_enabled = True self.course.save() # pylint: disable=no-member self.store.update_item(self.course, self.user.id) response = self.client.get(reverse("dashboard")) self.assertContains(response, u"View Test_Certificate") self.assertContains(response, test_url)
def test_certificate_without_branding_urls(self): """ Test that links from CertificateHtmlViewConfiguration are used if corresponding microsite or marketing urls are not present. """ self._add_course_certificates(count=1, signatory_count=1, is_active=True) self.course.save() self.store.update_item(self.course, self.user.id) configuration = CertificateHtmlViewConfiguration.get_config() test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME) # ABOUT is not present in MICROSITE_CONFIGURATION['test_microsite']["urls"] or MKTG_URL_LINK_MAP, # so web certificate will use CertificateHtmlViewConfiguration url. self.assertContains( response, configuration['microsites']['testmicrosite']['company_about_url'], ) # PRIVACY is not present in MICROSITE_CONFIGURATION['test_microsite']["urls"] or MKTG_URL_LINK_MAP, # so web certificate will use CertificateHtmlViewConfiguration url. self.assertContains( response, configuration['microsites']['testmicrosite']['company_privacy_url'], ) # TOS_AND_HONOR is not present in MICROSITE_CONFIGURATION['test_microsite']["urls"] or MKTG_URL_LINK_MAP, # so web certificate will use CertificateHtmlViewConfiguration url. self.assertContains( response, configuration['microsites']['testmicrosite']['company_tos_url'], )
def test_render_html_view_disabled_feature_flag_returns_static_url(self): test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id), verify_uuid=self.cert.verify_uuid ) self.assertIn(str(self.cert.verify_uuid), test_url)
def test_certificate_custom_template_with_unicode_data(self, custom_certs_enabled): """ Tests custom template renders properly with unicode data. """ mode = 'honor' self._add_course_certificates(count=1, signatory_count=2) self._create_custom_template(mode=mode) with patch.dict("django.conf.settings.FEATURES", { "CERTIFICATES_HTML_VIEW": True, "CUSTOM_CERTIFICATE_TEMPLATES_ENABLED": custom_certs_enabled }): test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) with patch.dict("django.conf.settings.SOCIAL_SHARING_SETTINGS", { "CERTIFICATE_TWITTER": True, "CERTIFICATE_TWITTER_TEXT": u"nền tảng học tập" }): with patch('django.http.HttpRequest.build_absolute_uri') as mock_abs_uri: mock_abs_uri.return_value = '='.join(['http://localhost/?param', u'é']) with patch('certificates.api.get_course_organizations') as mock_get_orgs: mock_get_orgs.return_value = [] response = self.client.get(test_url) self.assertEqual(response.status_code, 200) if custom_certs_enabled: self.assertContains(response, 'mode: {}'.format(mode)) else: self.assertContains(response, "Tweet this Accomplishment") self.assertContains(response, 'https://twitter.com/intent/tweet')
def test_render_html_view_with_preview_mode(self): """ test certificate web view should render properly along with its signatories information when accessing it in preview mode. Either the certificate is marked active or not. """ self.cert.delete() self.assertEqual(len(GeneratedCertificate.objects.all()), 0) self._add_course_certificates(count=1, signatory_count=2) test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) response = self.client.get(test_url + '?preview=honor') # accessing certificate web view in preview mode without # staff or instructor access should show invalid certificate self.assertIn('Cannot Find Certificate', response.content) CourseStaffRole(self.course.id).add_users(self.user) response = self.client.get(test_url + '?preview=honor') self.assertNotIn(self.course.display_name, response.content) self.assertIn('course_title_0', response.content) self.assertIn('Signatory_Title 0', response.content) # mark certificate inactive but accessing in preview mode. self._add_course_certificates(count=1, signatory_count=2, is_active=False) response = self.client.get(test_url + '?preview=honor') self.assertNotIn(self.course.display_name, response.content) self.assertIn('course_title_0', response.content) self.assertIn('Signatory_Title 0', response.content)
def test_certificate_custom_template_with_org_mode_course(self): """ Tests custom template search and rendering. This test should check template matching when org={org}, course={course}, mode={mode}. """ self._add_course_certificates(count=1, signatory_count=2) self._create_custom_template(org_id=1, mode='honor', course_key=unicode(self.course.id)) self._create_custom_template(org_id=2, mode='honor') test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) with patch('certificates.api.get_course_organizations') as mock_get_orgs: mock_get_orgs.side_effect = [ [{"id": 1, "name": "organization name"}], [{"id": 2, "name": "organization name 2"}], ] response = self.client.get(test_url) self.assertEqual(response.status_code, 200) self.assertContains(response, 'lang: fr') self.assertContains(response, 'course name: course_title_0') # test with second organization template response = self.client.get(test_url) self.assertEqual(response.status_code, 200) self.assertContains(response, 'lang: fr') self.assertContains(response, 'course name: course_title_0')
def test_evidence_event_sent(self): self._add_course_certificates(count=1, signatory_count=2) cert_url = get_certificate_url( user_id=self.user.id, course_id=self.course_id ) test_url = '{}?evidence_visit=1'.format(cert_url) self.recreate_tracker() assertion = BadgeAssertionFactory.create( user=self.user, course_id=self.course_id, ) response = self.client.get(test_url) self.assertEqual(response.status_code, 200) assert_event_matches( { 'name': 'edx.badge.assertion.evidence_visited', 'data': { 'course_id': 'testorg/run1/refundable_course', 'assertion_id': assertion.id, 'assertion_json_url': 'http://www.example.com/assertion.json', 'assertion_image_url': 'http://www.example.com/image.png', 'user_id': self.user.id, 'issuer': 'http://www.example.com/issuer.json', 'enrollment_mode': 'honor', }, }, self.get_event() )
def test_linked_student_to_web_view_credential(self, enrollment_mode): test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) self._create_certificate(enrollment_mode) certificates = [ { 'id': 0, 'name': 'Test Name', 'description': 'Test Description', 'is_active': True, 'signatories': [], 'version': 1 } ] self.course.certificates = {'certificates': certificates} self.course.cert_html_view_enabled = True self.course.save() # pylint: disable=no-member self.store.update_item(self.course, self.user.id) response = self.client.get(reverse('dashboard')) self.assertContains(response, u'View Test_Certificate') self.assertContains(response, test_url)
def test_render_html_view_with_unicode_platform_name(self): test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course) ) self._add_course_certificates(count=1, signatory_count=0) response = self.client.get(test_url) self.assertEqual(response.status_code, 200)
def test_render_html_view_invalid_course_id(self): test_url = get_certificate_url( user_id=self.user.id, course_id='az/23423/4vs' ) response = self.client.get(test_url) self.assertIn('invalid', response.content)
def test_render_html_view_invalid_user_certificate(self): self.cert.delete() self.assertEqual(len(GeneratedCertificate.objects.all()), 0) test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) response = self.client.get(test_url) self.assertIn('invalid', response.content)
def test_render_html_view_invalid_user_certificate(self): self.cert.delete() self.assertEqual(len(GeneratedCertificate.objects.all()), 0) test_url = get_certificate_url( user_id=self.user.id, course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member ) response = self.client.get(test_url) self.assertIn('invalid', response.content)
def test_render_html_view_without_signatories(self): test_url = get_certificate_url( user_id=self.user.id, course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member ) self._add_course_certificates(count=1, signatory_count=0) response = self.client.get(test_url) self.assertNotIn('Signatory_Name 0', response.content) self.assertNotIn('Signatory_Title 0', response.content)
def test_render_500_view_invalid_certificate_configuration(self): CertificateHtmlViewConfiguration.objects.all().update(enabled=False) test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) response = self.client.get(test_url) self.assertIn("Invalid Certificate Configuration", response.content)
def test_linked_student_to_web_view_credential(self, enrollment_mode): cert = self._create_certificate(enrollment_mode) test_url = get_certificate_url(course_id=self.course.id, uuid=cert.verify_uuid) response = self.client.get(reverse('dashboard')) self.assertContains(response, u'View Test_Certificate') self.assertContains(response, test_url)
def test_view_certificate_link(self): """ If certificate web view is enabled then certificate web view button should appear for user who certificate is available/generated """ certificate = GeneratedCertificateFactory.create( user=self.user, course_id=self.course.id, status=CertificateStatuses.downloadable, download_url="http://www.example.com/certificate.pdf", mode='honor' ) # Enable the feature, but do not enable it for this course CertificateGenerationConfiguration(enabled=True).save() # Enable certificate generation for this course certs_api.set_cert_generation_enabled(self.course.id, True) #course certificate configurations certificates = [ { 'id': 1, 'name': 'Name 1', 'description': 'Description 1', 'course_title': 'course_title_1', 'org_logo_path': '/t4x/orgX/testX/asset/org-logo-1.png', 'signatories': [], 'version': 1, 'is_active': True } ] self.course.certificates = {'certificates': certificates} self.course.cert_html_view_enabled = True self.course.save() self.store.update_item(self.course, self.user.id) resp = views.progress(self.request, course_id=unicode(self.course.id)) self.assertContains(resp, u"View Certificate") self.assertContains(resp, u"You can now access your certificate") cert_url = certs_api.get_certificate_url( user_id=self.user.id, course_id=self.course.id, verify_uuid=certificate.verify_uuid ) self.assertContains(resp, cert_url) # when course certificate is not active certificates[0]['is_active'] = False self.store.update_item(self.course, self.user.id) resp = views.progress(self.request, course_id=unicode(self.course.id)) self.assertNotContains(resp, u"View Your Certificate") self.assertNotContains(resp, u"You can now view your certificate") self.assertContains(resp, u"We're creating your certificate.")
def test_linkedin_share_url(self): """ Test: LinkedIn share URL. """ self._add_course_certificates(count=1, signatory_count=1, is_active=True) test_url = get_certificate_url(course_id=self.course.id, uuid=self.cert.verify_uuid) response = self.client.get(test_url) self.assertEqual(response.status_code, 200) self.assertIn(urllib.quote_plus(self.request.build_absolute_uri(test_url)), response.content)
def test_render_html_view_without_signatories(self): self._add_course_certificates(count=1, signatory_count=0) test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course) ) response = self.client.get(test_url) self.assertNotIn('Signatory_Name 0', response.content) self.assertNotIn('Signatory_Title 0', response.content)
def test_get_pdf_certificate_url(self): """ Test the get_certificate_url with a pdf cert course """ cert_url = certs_api.get_certificate_url( user_id=self.student.id, # pylint: disable=no-member course_id=self.pdf_cert_course.id, uuid=self.uuid ) self.assertEqual('www.gmail.com', cert_url)
def test_linkedin_share_microsites(self): """ Test: LinkedIn share URL should not be visible when called from within a microsite (for now) """ self._add_course_certificates(count=1, signatory_count=1, is_active=True) test_url = get_certificate_url(course_id=self.cert.course_id, uuid=self.cert.verify_uuid) response = self.client.get(test_url) self.assertEqual(response.status_code, 200) # the URL should not be present self.assertNotIn(urllib.quote_plus(self.request.build_absolute_uri(test_url)), response.content)
def test_certificate_view_without_org_logo(self): test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) test_certificates = [ { 'id': 0, 'name': 'Certificate Name 0', 'signatories': [], 'version': 1, 'is_active': True } ] self.course.certificates = {'certificates': test_certificates} self.course.cert_html_view_enabled = True self.course.save() self.store.update_item(self.course, self.user.id) response = self.client.get(test_url) # make sure response html has only one organization logo container for edX self.assertContains(response, "<li class=\"wrapper-organization\">", 1)
def test_certificate_custom_template_with_org(self): """ Tests custom template search if if have a single template for all courses of organization. """ self._add_course_certificates(count=1, signatory_count=2) self._create_custom_template(1) self._create_custom_template(1, mode='honor') test_url = get_certificate_url(user_id=self.user.id, course_id=unicode(self.course.id)) with patch( 'certificates.api.get_course_organizations') as mock_get_orgs: mock_get_orgs.side_effect = [ [{ "id": 1, "name": "organization name" }], ] response = self.client.get(test_url) self.assertEqual(response.status_code, 200) self.assertContains( response, 'course name: {}'.format(self.course.display_name))
def test_render_certificate_only_for_downloadable_status(self): """ Tests taht Certificate HTML Web View returns Certificate only if certificate status is 'downloadable', for other statuses it should return "Invalid Certificate". """ self._add_course_certificates(count=1, signatory_count=2) test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) # Validate certificate response = self.client.get(test_url) self.assertIn(str(self.cert.verify_uuid), response.content) # Change status to 'generating' and validate that Certificate Web View returns "Invalid Certificate" self.cert.status = CertificateStatuses.generating self.cert.save() response = self.client.get(test_url) self.assertIn("Invalid Certificate", response.content) self.assertIn("Cannot Find Certificate", response.content) self.assertIn("We cannot find a certificate with this URL or ID number.", response.content)
def test_linked_student_to_web_view_credential(self, enrollment_mode): test_url = get_certificate_url(user_id=self.user.id, course_id=unicode(self.course.id)) self._create_certificate(enrollment_mode) certificates = [{ 'id': 0, 'name': 'Test Name', 'description': 'Test Description', 'is_active': True, 'signatories': [], 'version': 1 }] self.course.certificates = {'certificates': certificates} self.course.cert_html_view_enabled = True self.course.save() # pylint: disable=no-member self.store.update_item(self.course, self.user.id) response = self.client.get(reverse('dashboard')) self.assertContains(response, u'View Test_Certificate') self.assertContains(response, test_url)
def test_certificate_custom_template_with_unicode_data( self, custom_certs_enabled): """ Tests custom template renders properly with unicode data. """ mode = 'honor' self._add_course_certificates(count=1, signatory_count=2) self._create_custom_template(mode=mode) with patch.dict( "django.conf.settings.FEATURES", { "CERTIFICATES_HTML_VIEW": True, "CUSTOM_CERTIFICATE_TEMPLATES_ENABLED": custom_certs_enabled }): test_url = get_certificate_url(user_id=self.user.id, course_id=unicode(self.course.id)) with patch.dict( "django.conf.settings.SOCIAL_SHARING_SETTINGS", { "CERTIFICATE_TWITTER": True, "CERTIFICATE_TWITTER_TEXT": u"nền tảng học tập" }): with patch('django.http.HttpRequest.build_absolute_uri' ) as mock_abs_uri: mock_abs_uri.return_value = '='.join( ['http://localhost/?param', u'é']) with patch('certificates.api.get_course_organizations' ) as mock_get_orgs: mock_get_orgs.return_value = [] response = self.client.get(test_url) self.assertEqual(response.status_code, 200) if custom_certs_enabled: self.assertContains(response, 'mode: {}'.format(mode)) else: self.assertContains(response, "Tweet this Accomplishment") self.assertContains( response, 'https://twitter.com/intent/tweet')
def test_certificate_evidence_event_emitted(self): self.client.logout() self._add_course_certificates(count=1, signatory_count=2) self.recreate_tracker() test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) response = self.client.get(test_url) self.assertEqual(response.status_code, 200) actual_event = self.get_event() self.assertEqual(actual_event['name'], 'edx.certificate.evidence_visited') assert_event_matches( { 'user_id': self.user.id, 'certificate_id': unicode(self.cert.verify_uuid), 'enrollment_mode': self.cert.mode, 'certificate_url': test_url, 'course_id': unicode(self.course.id), 'social_network': CertificateSocialNetworks.linkedin }, actual_event['data'] )
def test_render_html_view_with_preview_mode(self): """ test certificate web view should render properly along with its signatories information when accessing it in preview mode. Either the certificate is marked active or not. """ self.cert.delete() self.assertEqual(len(GeneratedCertificate.objects.all()), 0) self._add_course_certificates(count=1, signatory_count=2) test_url = get_certificate_url(user_id=self.user.id, course_id=unicode(self.course.id)) response = self.client.get(test_url + '?preview=honor') self.assertNotIn(self.course.display_name, response.content) self.assertIn('course_title_0', response.content) self.assertIn('Signatory_Title 0', response.content) # mark certificate inactive but accessing in preview mode. self._add_course_certificates(count=1, signatory_count=2, is_active=False) response = self.client.get(test_url + '?preview=honor') self.assertNotIn(self.course.display_name, response.content) self.assertIn('course_title_0', response.content) self.assertIn('Signatory_Title 0', response.content)
def test_course_display_name_not_override_with_course_title(self): # if certificate in descriptor has not course_title then course name should not be overridden with this title. test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) test_certificates = [ { 'id': 0, 'name': 'Name 0', 'description': 'Description 0', 'signatories': [], 'version': 1, 'is_active':True } ] self.course.certificates = {'certificates': test_certificates} self.course.cert_html_view_enabled = True self.course.save() self.store.update_item(self.course, self.user.id) response = self.client.get(test_url) self.assertNotIn('test_course_title_0', response.content) self.assertIn('refundable course', response.content)
def test_audit_certificate_display(self, eligible_for_certificate): """ Ensure that audit-mode certs are not shown in the web view. """ # Convert the cert to audit, with the specified eligibility self.cert.mode = 'audit' self.cert.eligible_for_certificate = eligible_for_certificate self.cert.save() self._add_course_certificates(count=1, signatory_count=2) test_url = get_certificate_url(user_id=self.user.id, course_id=unicode(self.course.id)) response = self.client.get(test_url) if eligible_for_certificate: self.assertIn(str(self.cert.verify_uuid), response.content) else: self.assertIn("Invalid Certificate", response.content) self.assertIn("Cannot Find Certificate", response.content) self.assertIn( "We cannot find a certificate with this URL or ID number.", response.content) self.assertNotIn(str(self.cert.verify_uuid), response.content)
def test_certificate_without_branding_urls(self): """ Test that links from CertificateHtmlViewConfiguration are used if corresponding microsite or marketing urls are not present. """ self._add_course_certificates(count=1, signatory_count=1, is_active=True) self.course.save() self.store.update_item(self.course, self.user.id) configuration = CertificateHtmlViewConfiguration.get_config() test_url = get_certificate_url(user_id=self.user.id, course_id=unicode(self.course.id)) response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME) # ABOUT is not present in MICROSITE_CONFIGURATION['test_microsite']["urls"] or MKTG_URL_LINK_MAP, # so web certificate will use CertificateHtmlViewConfiguration url. self.assertContains( response, configuration['microsites']['testmicrosite']['company_about_url'], ) # PRIVACY is not present in MICROSITE_CONFIGURATION['test_microsite']["urls"] or MKTG_URL_LINK_MAP, # so web certificate will use CertificateHtmlViewConfiguration url. self.assertContains( response, configuration['microsites']['testmicrosite'] ['company_privacy_url'], ) # TOS_AND_HONOR is not present in MICROSITE_CONFIGURATION['test_microsite']["urls"] or MKTG_URL_LINK_MAP, # so web certificate will use CertificateHtmlViewConfiguration url. self.assertContains( response, configuration['microsites']['testmicrosite']['company_tos_url'], )
def test_certificate_branding(self): """ Test that link urls in certificate web view are customized according to site branding and microsite configuration. """ self._add_course_certificates(count=1, signatory_count=1, is_active=True) self.course.save() self.store.update_item(self.course, self.user.id) test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME) # logo_image_url Tis present in MICROSITE_CONFIGURATION['test_microsite']["urls"], # so web certificate will use that. self.assertContains( response, settings.MICROSITE_CONFIGURATION['test_microsite']['logo_image_url'], ) # ABOUT is present in MICROSITE_CONFIGURATION['test_microsite']["urls"] so web certificate will use that url. self.assertContains( response, settings.MICROSITE_CONFIGURATION['test_microsite']["urls"]['ABOUT'], ) # PRIVACY is present in MICROSITE_CONFIGURATION['test_microsite']["urls"] so web certificate will use that url. self.assertContains( response, settings.MICROSITE_CONFIGURATION['test_microsite']["urls"]['PRIVACY'], ) # TOS_AND_HONOR is present in MICROSITE_CONFIGURATION['test_microsite']["urls"], # so web certificate will use that url. self.assertContains( response, settings.MICROSITE_CONFIGURATION['test_microsite']["urls"]['TOS_AND_HONOR'], )
def test_evidence_event_sent(self): cert_url = get_certificate_url(user_id=self.user.id, course_id=self.course_id, verify_uuid=self.cert.verify_uuid) test_url = '{}?evidence_visit=1'.format(cert_url) self._add_course_certificates(count=1, signatory_count=2) self.recreate_tracker() assertion = BadgeAssertion( user=self.user, course_id=self.course_id, mode='honor', data={ 'image': 'http://www.example.com/image.png', 'json': { 'id': 'http://www.example.com/assertion.json' }, 'issuer': 'http://www.example.com/issuer.json', }) assertion.save() response = self.client.get(test_url) self.assertEqual(response.status_code, 200) assert_event_matches( { 'name': 'edx.badge.assertion.evidence_visited', 'data': { 'course_id': 'testorg/run1/refundable_course', # pylint: disable=no-member 'assertion_id': assertion.id, 'assertion_json_url': 'http://www.example.com/assertion.json', 'assertion_image_url': 'http://www.example.com/image.png', 'user_id': self.user.id, 'issuer': 'http://www.example.com/issuer.json', 'enrollment_mode': 'honor', }, }, self.get_event())
def test_certificate_custom_template_with_organization(self): """ Tests custom template search when we have a single template for a organization. This test should check template matching when org={org}, course=Null, mode=null. """ self._add_course_certificates(count=1, signatory_count=2) self._create_custom_template(org_id=1, mode='honor') self._create_custom_template(org_id=1, mode='honor', course_key=self.course.id) self._create_custom_template(org_id=2) test_url = get_certificate_url(user_id=self.user.id, course_id=unicode(self.course.id)) with patch( 'certificates.api.get_course_organizations') as mock_get_orgs: mock_get_orgs.side_effect = [ [{ "id": 2, "name": "organization name 2" }], ] response = self.client.get(test_url) self.assertEqual(response.status_code, 200)
def test_render_html_view_invalid_course(self): test_url = get_certificate_url(user_id=self.user.id, course_id='missing/course/key') response = self.client.get(test_url) self.assertIn('invalid', response.content)
def render_html_view(request, user_id, course_id): """ This public view generates an HTML representation of the specified student's certificate If a certificate is not available, we display a "Sorry!" screen instead """ # Create the initial view context, bootstrapping with Django settings and passed-in values context = {} context['platform_name'] = microsite.get_value("platform_name", settings.PLATFORM_NAME) context['course_id'] = course_id # Update the view context with the default ConfigurationModel settings configuration = CertificateHtmlViewConfiguration.get_config() # if we are in a microsite, then let's first see if there is an override # section in our config config_key = microsite.get_value('microsite_config_key', 'default') # if there is no special microsite override, then let's use default if config_key not in configuration: config_key = 'default' context.update(configuration.get(config_key, {})) # Translators: 'All rights reserved' is a legal term used in copyrighting to protect published content reserved = _("All rights reserved") context[ 'copyright_text'] = '© {year} {platform_name}. {reserved}.'.format( year=settings.COPYRIGHT_YEAR, platform_name=context.get('platform_name'), reserved=reserved) # Translators: This text is bound to the HTML 'title' element of the page and appears # in the browser title bar when a requested certificate is not found or recognized context['document_title'] = _("Invalid Certificate") # Translators: The & characters represent an ampersand character and can be ignored context['company_tos_urltext'] = _("Terms of Service & Honor Code") # Translators: A 'Privacy Policy' is a legal document/statement describing a website's use of personal information context['company_privacy_urltext'] = _("Privacy Policy") # Translators: This line appears as a byline to a header image and describes the purpose of the page context['logo_subtitle'] = _("Certificate Validation") invalid_template_path = 'certificates/invalid.html' # Kick the user back to the "Invalid" screen if the feature is disabled if not has_html_certificates_enabled(course_id): return render_to_response(invalid_template_path, context) # Load the core building blocks for the view context try: course_key = CourseKey.from_string(course_id) user = User.objects.get(id=user_id) course = modulestore().get_course(course_key) if not course: raise CourseDoesNotExist # Attempt to load the user's generated certificate data user_certificate = GeneratedCertificate.objects.get( user=user, course_id=course_key) # If there's no generated certificate data for this user, we need to see if we're in 'preview' mode... # If we are, we'll need to create a mock version of the user_certificate container for previewing except GeneratedCertificate.DoesNotExist: if request.GET.get('preview', None): user_certificate = GeneratedCertificate( mode=request.GET.get('preview'), verify_uuid=unicode(uuid4().hex), modified_date=datetime.now().date()) else: return render_to_response(invalid_template_path, context) # For any other expected exceptions, kick the user back to the "Invalid" screen except (InvalidKeyError, CourseDoesNotExist, User.DoesNotExist): return render_to_response(invalid_template_path, context) # Badge Request Event Tracking Logic if 'evidence_visit' in request.GET: try: badge = BadgeAssertion.objects.get(user=user, course_id=course_key) tracker.emit( 'edx.badge.assertion.evidence_visited', { 'user_id': user.id, 'course_id': unicode(course_key), 'enrollment_mode': badge.mode, 'assertion_id': badge.id, 'assertion_image_url': badge.data['image'], 'assertion_json_url': badge.data['json']['id'], 'issuer': badge.data['issuer'], }) except BadgeAssertion.DoesNotExist: log.warn( "Could not find badge for %s on course %s.", user.id, course_key, ) # Okay, now we have all of the pieces, time to put everything together # Get the active certificate configuration for this course # If we do not have an active certificate, we'll need to send the user to the "Invalid" screen # Passing in the 'preview' parameter, if specified, will return a configuration, if defined active_configuration = get_active_web_certificate( course, request.GET.get('preview')) if active_configuration is None: return render_to_response(invalid_template_path, context) else: context['certificate_data'] = active_configuration # Append/Override the existing view context values with any mode-specific ConfigurationModel values context.update(configuration.get(user_certificate.mode, {})) # Append/Override the existing view context values with request-time values _update_certificate_context(context, course, user, user_certificate) # If enabled, show the LinkedIn "add to profile" button # Clicking this button sends the user to LinkedIn where they # can add the certificate information to their profile. linkedin_config = LinkedInAddToProfileConfiguration.current() if linkedin_config.enabled: context['linked_in_url'] = linkedin_config.add_to_profile_url( course.id, course.display_name, user_certificate.mode, request.build_absolute_uri( get_certificate_url(user_id=user.id, course_id=unicode(course.id)))) # Microsites will need to be able to override any hard coded # content that was put into the context in the # _update_certificate_context() call above. For example the # 'company_about_description' talks about edX, which we most likely # do not want to keep in a microsite # # So we need to re-apply any configuration/content that # we are sourceing from the database. This is somewhat duplicative of # the code at the beginning of this method, but we # need the configuration at the top as some error code paths # require that to be set up early on in the pipeline # microsite_config_key = microsite.get_value('microsite_config_key') if microsite_config_key: context.update(configuration.get(microsite_config_key, {})) # track certificate evidence_visited event for analytics when certificate_user and accessing_user are different if request.user and request.user.id != user.id: emit_certificate_event( 'evidence_visited', user, course_id, course, { 'certificate_id': user_certificate.verify_uuid, 'enrollment_mode': user_certificate.mode, 'social_network': CertificateSocialNetworks.linkedin }) # Append/Override the existing view context values with any course-specific static values from Advanced Settings context.update(course.cert_html_view_overrides) # FINALLY, generate and send the output the client if settings.FEATURES.get('CUSTOM_CERTIFICATE_TEMPLATES_ENABLED', False): custom_template = get_certificate_template(course_key, user_certificate.mode) if custom_template: template = Template(custom_template) context = RequestContext(request, context) return HttpResponse(template.render(context)) return render_to_response("certificates/valid.html", context)
def test_render_html_view_invalid_certificate_configuration(self): test_url = get_certificate_url(user_id=self.user.id, course_id=unicode(self.course.id)) response = self.client.get(test_url) self.assertIn("Invalid Certificate", response.content)
def test_render_html_view_invalid_user(self): test_url = get_certificate_url(user_id=111, course_id=unicode(self.course.id)) response = self.client.get(test_url) self.assertIn('invalid', response.content)
def test_rendering_maximum_data(self): """ Tests at least one data item from different context update methods to make sure every context update method is invoked while rendering certificate template. """ long_org_name = 'Long org name' short_org_name = 'short_org_name' test_organization_data = { 'name': long_org_name, 'short_name': short_org_name, 'description': 'Test Organization Description', 'active': True, 'logo': '/logo_test1.png' } test_org = organizations_api.add_organization(organization_data=test_organization_data) organizations_api.add_organization_course(organization_data=test_org, course_id=unicode(self.course.id)) self._add_course_certificates(count=1, signatory_count=1, is_active=True) BadgeAssertionFactory.create( user=self.user, course_id=self.course_id, ) self.course.cert_html_view_overrides = { "logo_src": "/static/certificates/images/course_override_logo.png" } self.course.save() self.store.update_item(self.course, self.user.id) test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME) # Test an item from basic info self.assertIn( 'Terms of Service & Honor Code', response.content ) self.assertIn( 'Certificate ID Number', response.content ) # Test an item from html cert configuration self.assertIn( '<a class="logo" href="http://test_microsite.localhost">', response.content ) # Test an item from course info self.assertIn( 'course_title_0', response.content ) # Test an item from user info self.assertIn( "{fullname}, you earned a certificate!".format(fullname=self.user.profile.name), response.content ) # Test an item from social info self.assertIn( "Post on Facebook", response.content ) self.assertIn( "Share on Twitter", response.content ) # Test an item from certificate/org info self.assertIn( "a course of study offered by {partner_short_name}, " "an online learning initiative of " "{partner_long_name}.".format( partner_short_name=short_org_name, partner_long_name=long_org_name, platform_name='Test Microsite' ), response.content ) # Test item from badge info self.assertIn( "Add to Mozilla Backpack", response.content ) # Test item from microsite info self.assertIn( "http://www.testmicrosite.org/about-us", response.content ) # Test course overrides self.assertIn( "/static/certificates/images/course_override_logo.png", response.content )
def test_render_html_view_invalid_course_id(self): test_url = get_certificate_url(user_id=self.user.id, course_id='az/23423/4vs') response = self.client.get(test_url) self.assertIn('invalid', response.content)
def test_cert_url_empty_with_invalid_certificate(self): """ Test certificate url is empty if html view is not enabled and certificate is not yet generated """ url = certs_api.get_certificate_url(self.student.id, self.course.id) self.assertEqual(url, "")
def _update_certificate_context(context, course, user, user_certificate): """ Build up the certificate web view context using the provided values (Helper method to keep the view clean) """ # Populate dynamic output values using the course/certificate data loaded above user_fullname = user.profile.name platform_name = microsite.get_value("platform_name", settings.PLATFORM_NAME) certificate_type = context.get('certificate_type') context['username'] = user.username context['course_mode'] = user_certificate.mode context['accomplishment_user_id'] = user.id context['accomplishment_copy_name'] = user_fullname context['accomplishment_copy_username'] = user.username context['accomplishment_copy_course_org'] = course.org context['accomplishment_copy_course_name'] = course.display_name context['course_image_url'] = course_image_url(course) context['share_settings'] = settings.FEATURES.get( 'SOCIAL_SHARING_SETTINGS', {}) try: badge = BadgeAssertion.objects.get( user=user, course_id=course.location.course_key) except BadgeAssertion.DoesNotExist: badge = None context['badge'] = badge # Override the defaults with any mode-specific static values context['certificate_id_number'] = user_certificate.verify_uuid context['certificate_verify_url'] = "{prefix}{uuid}{suffix}".format( prefix=context.get('certificate_verify_url_prefix'), uuid=user_certificate.verify_uuid, suffix=context.get('certificate_verify_url_suffix')) # Translators: The format of the date includes the full name of the month context['certificate_date_issued'] = _('{month} {day}, {year}').format( month=user_certificate.modified_date.strftime("%B"), day=user_certificate.modified_date.day, year=user_certificate.modified_date.year) accd_course_org_html = '<span class="detail--xuniversity">{partner_name}</span>'.format( partner_name=course.org) accd_platform_name_html = '<span class="detail--company">{platform_name}</span>'.format( platform_name=platform_name) # Translators: This line appears on the certificate after the name of a course, and provides more # information about the organizations providing the course material to platform users context['accomplishment_copy_course_description'] = _( 'a course of study offered by {partner_name}, ' 'through {platform_name}.').format( partner_name=accd_course_org_html, platform_name=accd_platform_name_html) # Translators: Accomplishments describe the awards/certifications obtained by students on this platform context['accomplishment_copy_about'] = _( 'About {platform_name} Accomplishments').format( platform_name=platform_name) context['accomplishment_more_title'] = _( "More Information About {user_name}'s Certificate:").format( user_name=user_fullname) # Translators: This line appears on the page just before the generation date for the certificate context['certificate_date_issued_title'] = _("Issued On:") # Translators: The Certificate ID Number is an alphanumeric value unique to each individual certificate context['certificate_id_number_title'] = _('Certificate ID Number') context['certificate_info_title'] = _('About {platform_name} Certificates' ).format(platform_name=platform_name) # Translators: This text describes the purpose (and therefore, value) of a course certificate # 'verifying your identity' refers to the process for establishing the authenticity of the student context['certificate_info_description'] = _( "{platform_name} acknowledges achievements through certificates, which " "are awarded for various activities {platform_name} students complete " "under the <a href='{tos_url}'>{platform_name} Honor Code</a>. Some " "certificates require completing additional steps, such as " "<a href='{verified_cert_url}'> verifying your identity</a>.").format( platform_name=platform_name, tos_url=context.get('company_tos_url'), verified_cert_url=context.get('company_verified_certificate_url')) context['certificate_verify_title'] = _( "How {platform_name} Validates Student Certificates").format( platform_name=platform_name) # Translators: This text describes the validation mechanism for a certificate file (known as GPG security) context['certificate_verify_description'] = _( 'Certificates issued by {platform_name} are signed by a gpg key so ' 'that they can be validated independently by anyone with the ' '{platform_name} public key. For independent verification, ' '{platform_name} uses what is called a ' '"detached signature""".').format(platform_name=platform_name) context['certificate_verify_urltext'] = _( "Validate this certificate for yourself") # Translators: This text describes (at a high level) the mission and charter the edX platform and organization context['company_about_description'] = _( "{platform_name} offers interactive online classes and MOOCs from the " "world's best universities, including MIT, Harvard, Berkeley, University " "of Texas, and many others. {platform_name} is a non-profit online " "initiative created by founding partners Harvard and MIT.").format( platform_name=platform_name) context['company_about_title'] = _("About {platform_name}").format( platform_name=platform_name) context['company_about_urltext'] = _( "Learn more about {platform_name}").format(platform_name=platform_name) context['company_courselist_urltext'] = _( "Learn with {platform_name}").format(platform_name=platform_name) context['company_careers_urltext'] = _("Work at {platform_name}").format( platform_name=platform_name) context['company_contact_urltext'] = _("Contact {platform_name}").format( platform_name=platform_name) # Translators: This text appears near the top of the certficate and describes the guarantee provided by edX context['document_banner'] = _( "{platform_name} acknowledges the following student accomplishment" ).format(platform_name=platform_name) # Translators: This text represents the verification of the certificate context['document_meta_description'] = _( 'This is a valid {platform_name} certificate for {user_name}, ' 'who participated in {partner_name} {course_number}').format( platform_name=platform_name, user_name=user_fullname, partner_name=course.org, course_number=course.number) # Translators: This text is bound to the HTML 'title' element of the page and appears in the browser title bar context['document_title'] = _( "{partner_name} {course_number} Certificate | {platform_name}").format( partner_name=course.org, course_number=course.number, platform_name=platform_name) # Translators: This text fragment appears after the student's name (displayed in a large font) on the certificate # screen. The text describes the accomplishment represented by the certificate information displayed to the user context['accomplishment_copy_description_full'] = _( "successfully completed, received a passing grade, and was " "awarded a {platform_name} {certificate_type} " "Certificate of Completion in ").format( platform_name=platform_name, certificate_type=context.get("certificate_type")) certificate_type_description = get_certificate_description( user_certificate.mode, certificate_type, platform_name) if certificate_type_description: context['certificate_type_description'] = certificate_type_description # If enabled, show the LinkedIn "add to profile" button # Clicking this button sends the user to LinkedIn where they # can add the certificate information to their profile. linkedin_config = LinkedInAddToProfileConfiguration.current() if linkedin_config.enabled: context['linked_in_url'] = linkedin_config.add_to_profile_url( course.id, course.display_name, user_certificate.mode, get_certificate_url(user_id=user.id, course_id=unicode(course.id), verify_uuid=user_certificate.verify_uuid)) # Translators: This line is displayed to a user who has completed a course and achieved a certification context['accomplishment_banner_opening'] = _( "{fullname}, you've earned a certificate!").format( fullname=user_fullname) # Translators: This line congratulates the user and instructs them to share their accomplishment on social networks context['accomplishment_banner_congrats'] = _( "Congratulations! This page summarizes all of the details of what " "you've accomplished. Show it off to family, friends, and colleagues " "in your social and professional networks.") # Translators: This line leads the reader to understand more about the certificate that a student has been awarded context['accomplishment_copy_more_about'] = _( "More about {fullname}'s accomplishment").format( fullname=user_fullname)
def test_render_html_view_disabled_feature_flag_returns_static_url(self): test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) self.assertIn(str(self.cert.download_url), test_url)