From f74c9b6de5fb460530d3b2a41df9abdb762462ee Mon Sep 17 00:00:00 2001 From: Matt Drayer Date: Wed, 2 Sep 2015 10:09:19 -0400 Subject: [PATCH] mattdrayer/cert-webview-add-org-fields: Added new fields to context --- .../certificates/tests/test_views.py | 480 ---------------- .../certificates/tests/test_webview_views.py | 525 ++++++++++++++++++ lms/djangoapps/certificates/views/webview.py | 6 +- 3 files changed, 529 insertions(+), 482 deletions(-) create mode 100644 lms/djangoapps/certificates/tests/test_webview_views.py diff --git a/lms/djangoapps/certificates/tests/test_views.py b/lms/djangoapps/certificates/tests/test_views.py index 2fd8f3ece9..ea65a064f7 100644 --- a/lms/djangoapps/certificates/tests/test_views.py +++ b/lms/djangoapps/certificates/tests/test_views.py @@ -361,486 +361,6 @@ class MicrositeCertificatesViewsTests(ModuleStoreTestCase): self.assertNotIn('This should not survive being overwritten by static content', response.content) -@attr('shard_1') -class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): - """ - Tests for the certificates web/html views - """ - def setUp(self): - super(CertificatesViewsTests, self).setUp() - self.client = Client() - self.course = CourseFactory.create( - org='testorg', number='run1', display_name='refundable course' - ) - self.course_id = self.course.location.course_key - self.user = UserFactory.create( - email='joe_user@edx.org', - username='joeuser', - password='foo' - ) - self.user.profile.name = "Joe User" - self.user.profile.save() - self.client.login(username=self.user.username, password='foo') - self.request = RequestFactory().request() - - self.cert = GeneratedCertificate.objects.create( - user=self.user, - course_id=self.course_id, - verify_uuid=uuid4(), - download_uuid=uuid4(), - download_url="http://www.example.com/certificates/download", - grade="0.95", - key='the_key', - distinction=True, - status='generated', - mode='honor', - name=self.user.profile.name, - ) - CourseEnrollmentFactory.create( - user=self.user, - course_id=self.course_id - ) - CertificateHtmlViewConfigurationFactory.create() - LinkedInAddToProfileConfigurationFactory.create() - - def _add_course_certificates(self, count=1, signatory_count=0, is_active=True): - """ - Create certificate for the course. - """ - signatories = [ - { - 'name': 'Signatory_Name ' + str(i), - 'title': 'Signatory_Title ' + str(i), - 'organization': 'Signatory_Organization ' + str(i), - 'signature_image_path': '/static/certificates/images/demo-sig{}.png'.format(i), - 'id': i, - } for i in xrange(0, signatory_count) - - ] - - certificates = [ - { - 'id': i, - 'name': 'Name ' + str(i), - 'description': 'Description ' + str(i), - 'course_title': 'course_title_' + str(i), - 'org_logo_path': '/t4x/orgX/testX/asset/org-logo-{}.png'.format(i), - 'signatories': signatories, - 'version': 1, - 'is_active': is_active - } for i in xrange(0, count) - ] - - self.course.certificates = {'certificates': certificates} - self.course.cert_html_view_enabled = True - self.course.save() - self.store.update_item(self.course, self.user.id) - - def _create_custom_template(self, org_id=None, mode=None, course_key=None): - """ - Creates a custom certificate template entry in DB. - """ - template_html = """ - - - lang: ${LANGUAGE_CODE} - course name: ${accomplishment_copy_course_name} - mode: ${course_mode} - ${accomplishment_copy_course_description} - - - """ - template = CertificateTemplate( - name='custom template', - template=template_html, - organization_id=org_id, - course_key=course_key, - mode=mode, - is_active=True - ) - template.save() - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - 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( - user_id=self.user.id, - course_id=unicode(self.course.id) - ) - response = self.client.get(test_url) - self.assertTrue(urllib.quote_plus(self.request.build_absolute_uri(test_url)) in response.content) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - 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( - 'test_organization {} Certificate |'.format(self.course.number, ), - response.content - ) - self.assertIn('logo_test1.png', response.content) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - def test_render_html_view_valid_certificate(self): - 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) - self.assertIn(str(self.cert.verify_uuid), response.content) - - # Hit any "verified" mode-specific branches - self.cert.mode = 'verified' - self.cert.save() - response = self.client.get(test_url) - self.assertIn(str(self.cert.verify_uuid), response.content) - - # Hit any 'xseries' mode-specific branches - self.cert.mode = 'xseries' - self.cert.save() - response = self.client.get(test_url) - self.assertIn(str(self.cert.verify_uuid), response.content) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - def test_render_html_view_with_valid_signatories(self): - 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) - self.assertIn('course_title_0', response.content) - self.assertIn('Signatory_Name 0', response.content) - self.assertIn('Signatory_Title 0', response.content) - self.assertIn('Signatory_Organization 0', response.content) - self.assertIn('/static/certificates/images/demo-sig0.png', response.content) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - 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) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - 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) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - def test_render_html_view_without_signatories(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.assertNotIn('Signatory_Name 0', response.content) - self.assertNotIn('Signatory_Title 0', response.content) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_DISABLED) - 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) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - 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) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - 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) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - 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) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - 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) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - 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) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - 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) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - 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'] - ) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) - def test_evidence_event_sent(self): - cert_url = get_certificate_url( - user_id=self.user.id, - course_id=self.course_id - ) - 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() - ) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_DISABLED) - def test_request_certificate_without_passing(self): - self.cert.status = CertificateStatuses.unavailable - self.cert.save() - request_certificate_url = reverse('certificates.views.request_certificate') - response = self.client.post(request_certificate_url, {'course_id': unicode(self.course.id)}) - self.assertEqual(response.status_code, 200) - response_json = json.loads(response.content) - self.assertEqual(CertificateStatuses.notpassing, response_json['add_status']) - - @override_settings(FEATURES=FEATURES_WITH_CERTS_DISABLED) - @override_settings(CERT_QUEUE='test-queue') - def test_request_certificate_after_passing(self): - self.cert.status = CertificateStatuses.unavailable - self.cert.save() - request_certificate_url = reverse('certificates.views.request_certificate') - with patch('capa.xqueue_interface.XQueueInterface.send_to_queue') as mock_queue: - mock_queue.return_value = (0, "Successfully queued") - with patch('courseware.grades.grade') as mock_grade: - mock_grade.return_value = {'grade': 'Pass', 'percent': 0.75} - response = self.client.post(request_certificate_url, {'course_id': unicode(self.course.id)}) - self.assertEqual(response.status_code, 200) - response_json = json.loads(response.content) - self.assertEqual(CertificateStatuses.generating, response_json['add_status']) - - @override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED) - @override_settings(LANGUAGE_CODE='fr') - def test_certificate_custom_template_with_org_mode_course(self): - """ - Tests custom template search and rendering. - """ - self._add_course_certificates(count=1, signatory_count=2) - self._create_custom_template(1, mode='honor', course_key=unicode(self.course.id)) - self._create_custom_template(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: {}'.format(self.course.display_name)) - # 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: {}'.format(self.course.display_name)) - - @override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED) - 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)) - - @override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED) - def test_certificate_custom_template_with_course_mode(self): - """ - Tests custom template search if if have a single template for a course mode. - """ - mode = 'honor' - self._add_course_certificates(count=1, signatory_count=2) - self._create_custom_template(mode=mode) - 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.return_value = [] - response = self.client.get(test_url) - self.assertEqual(response.status_code, 200) - self.assertContains(response, 'mode: {}'.format(mode)) - - class TrackShareRedirectTest(UrlResetMixin, ModuleStoreTestCase, EventTrackingTestCase): """ Verifies the badge image share event is sent out. diff --git a/lms/djangoapps/certificates/tests/test_webview_views.py b/lms/djangoapps/certificates/tests/test_webview_views.py new file mode 100644 index 0000000000..417e5126bb --- /dev/null +++ b/lms/djangoapps/certificates/tests/test_webview_views.py @@ -0,0 +1,525 @@ +"""Tests for certificates views. """ + +import json +from uuid import uuid4 +from nose.plugins.attrib import attr +from mock import patch + +from django.conf import settings +from django.core.urlresolvers import reverse +from django.test.client import Client +from django.test.utils import override_settings + +from openedx.core.lib.tests.assertions.events import assert_event_matches +from student.tests.factories import UserFactory, CourseEnrollmentFactory +from track.tests import EventTrackingTestCase +from xmodule.modulestore.tests.factories import CourseFactory +from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase + +from certificates.api import get_certificate_url +from certificates.models import ( + GeneratedCertificate, + BadgeAssertion, + CertificateStatuses, + CertificateSocialNetworks, + CertificateTemplate, +) + +from certificates.tests.factories import ( + CertificateHtmlViewConfigurationFactory, + LinkedInAddToProfileConfigurationFactory, +) +from util import organizations_helpers as organizations_api +from django.test.client import RequestFactory +import urllib + +FEATURES_WITH_CERTS_ENABLED = settings.FEATURES.copy() +FEATURES_WITH_CERTS_ENABLED['CERTIFICATES_HTML_VIEW'] = True + +FEATURES_WITH_CERTS_DISABLED = settings.FEATURES.copy() +FEATURES_WITH_CERTS_DISABLED['CERTIFICATES_HTML_VIEW'] = False + +FEATURES_WITH_CUSTOM_CERTS_ENABLED = { + "CUSTOM_CERTIFICATE_TEMPLATES_ENABLED": True +} +FEATURES_WITH_CUSTOM_CERTS_ENABLED.update(FEATURES_WITH_CERTS_ENABLED) + + +@attr('shard_1') +class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): + """ + Tests for the certificates web/html views + """ + def setUp(self): + super(CertificatesViewsTests, self).setUp() + self.client = Client() + self.course = CourseFactory.create( + org='testorg', number='run1', display_name='refundable course' + ) + self.course_id = self.course.location.course_key + self.user = UserFactory.create( + email='joe_user@edx.org', + username='joeuser', + password='foo' + ) + self.user.profile.name = "Joe User" + self.user.profile.save() + self.client.login(username=self.user.username, password='foo') + self.request = RequestFactory().request() + + self.cert = GeneratedCertificate.objects.create( + user=self.user, + course_id=self.course_id, + verify_uuid=uuid4(), + download_uuid=uuid4(), + download_url="http://www.example.com/certificates/download", + grade="0.95", + key='the_key', + distinction=True, + status='generated', + mode='honor', + name=self.user.profile.name, + ) + CourseEnrollmentFactory.create( + user=self.user, + course_id=self.course_id + ) + CertificateHtmlViewConfigurationFactory.create() + LinkedInAddToProfileConfigurationFactory.create() + + def _add_course_certificates(self, count=1, signatory_count=0, is_active=True): + """ + Create certificate for the course. + """ + signatories = [ + { + 'name': 'Signatory_Name ' + str(i), + 'title': 'Signatory_Title ' + str(i), + 'organization': 'Signatory_Organization ' + str(i), + 'signature_image_path': '/static/certificates/images/demo-sig{}.png'.format(i), + 'id': i, + } for i in xrange(signatory_count) + + ] + + certificates = [ + { + 'id': i, + 'name': 'Name ' + str(i), + 'description': 'Description ' + str(i), + 'course_title': 'course_title_' + str(i), + 'org_logo_path': '/t4x/orgX/testX/asset/org-logo-{}.png'.format(i), + 'signatories': signatories, + 'version': 1, + 'is_active': is_active + } for i in xrange(count) + ] + + self.course.certificates = {'certificates': certificates} + self.course.cert_html_view_enabled = True + self.course.save() + self.store.update_item(self.course, self.user.id) + + def _create_custom_template(self, org_id=None, mode=None, course_key=None): + """ + Creates a custom certificate template entry in DB. + """ + template_html = """ + <html> + <body> + lang: ${LANGUAGE_CODE} + course name: ${accomplishment_copy_course_name} + mode: ${course_mode} + ${accomplishment_copy_course_description} + </body> + </html> + """ + template = CertificateTemplate( + name='custom template', + template=template_html, + organization_id=org_id, + course_key=course_key, + mode=mode, + is_active=True + ) + template.save() + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + 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( + user_id=self.user.id, + course_id=unicode(self.course.id) + ) + response = self.client.get(test_url) + self.assertTrue(urllib.quote_plus(self.request.build_absolute_uri(test_url)) in response.content) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + 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) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + def test_render_html_view_valid_certificate(self): + 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) + self.assertIn(str(self.cert.verify_uuid), response.content) + + # Hit any "verified" mode-specific branches + self.cert.mode = 'verified' + self.cert.save() + response = self.client.get(test_url) + self.assertIn(str(self.cert.verify_uuid), response.content) + + # Hit any 'xseries' mode-specific branches + self.cert.mode = 'xseries' + self.cert.save() + response = self.client.get(test_url) + self.assertIn(str(self.cert.verify_uuid), response.content) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + def test_render_html_view_with_valid_signatories(self): + 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) + self.assertIn('course_title_0', response.content) + self.assertIn('Signatory_Name 0', response.content) + self.assertIn('Signatory_Title 0', response.content) + self.assertIn('Signatory_Organization 0', response.content) + self.assertIn('/static/certificates/images/demo-sig0.png', response.content) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + 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) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + 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) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + def test_render_html_view_without_signatories(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.assertNotIn('Signatory_Name 0', response.content) + self.assertNotIn('Signatory_Title 0', response.content) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_DISABLED) + 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) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + 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) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + 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) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + 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) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + 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) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + 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) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + 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) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + 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'] + ) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + def test_evidence_event_sent(self): + cert_url = get_certificate_url( + user_id=self.user.id, + course_id=self.course_id + ) + 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() + ) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_DISABLED) + def test_request_certificate_without_passing(self): + self.cert.status = CertificateStatuses.unavailable + self.cert.save() + request_certificate_url = reverse('certificates.views.request_certificate') + response = self.client.post(request_certificate_url, {'course_id': unicode(self.course.id)}) + self.assertEqual(response.status_code, 200) + response_json = json.loads(response.content) + self.assertEqual(CertificateStatuses.notpassing, response_json['add_status']) + + @override_settings(FEATURES=FEATURES_WITH_CERTS_DISABLED) + @override_settings(CERT_QUEUE='test-queue') + def test_request_certificate_after_passing(self): + self.cert.status = CertificateStatuses.unavailable + self.cert.save() + request_certificate_url = reverse('certificates.views.request_certificate') + with patch('capa.xqueue_interface.XQueueInterface.send_to_queue') as mock_queue: + mock_queue.return_value = (0, "Successfully queued") + with patch('courseware.grades.grade') as mock_grade: + mock_grade.return_value = {'grade': 'Pass', 'percent': 0.75} + response = self.client.post(request_certificate_url, {'course_id': unicode(self.course.id)}) + self.assertEqual(response.status_code, 200) + response_json = json.loads(response.content) + self.assertEqual(CertificateStatuses.generating, response_json['add_status']) + + @override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED) + @override_settings(LANGUAGE_CODE='fr') + def test_certificate_custom_template_with_org_mode_course(self): + """ + Tests custom template search and rendering. + """ + self._add_course_certificates(count=1, signatory_count=2) + self._create_custom_template(1, mode='honor', course_key=unicode(self.course.id)) + self._create_custom_template(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: {}'.format(self.course.display_name)) + # 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: {}'.format(self.course.display_name)) + + @override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED) + 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)) + + @override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED) + def test_certificate_custom_template_with_course_mode(self): + """ + Tests custom template search if if have a single template for a course mode. + """ + mode = 'honor' + self._add_course_certificates(count=1, signatory_count=2) + self._create_custom_template(mode=mode) + 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.return_value = [] + response = self.client.get(test_url) + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'mode: {}'.format(mode)) diff --git a/lms/djangoapps/certificates/views/webview.py b/lms/djangoapps/certificates/views/webview.py index e9cd519432..f4e010df73 100644 --- a/lms/djangoapps/certificates/views/webview.py +++ b/lms/djangoapps/certificates/views/webview.py @@ -93,8 +93,10 @@ def _update_certificate_context(context, course, user, user_certificate): if organizations: #TODO Need to add support for multiple organizations, Currently we are interested in the first one. organization = organizations[0] - partner_long_name = organization.get('name', None) - partner_short_name = organization.get('short_name', course.org) + partner_long_name = organization.get('name', partner_long_name) + partner_short_name = organization.get('short_name', partner_short_name) + context['organization_long_name'] = partner_long_name + context['organization_short_name'] = partner_short_name context['organization_logo'] = organization.get('logo', None) context['username'] = user.username