Skip to content

Commit

Permalink
Merge pull request #820 from eduNEXT/caba/revert_PR_to_remove_certifi…
Browse files Browse the repository at this point in the history
…cate_view

Revert "refactor: Replace PDF course certificate view code (#30397)"
  • Loading branch information
CABA1998 authored Apr 25, 2024
2 parents d48d2e9 + d5ce713 commit 558a271
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 31 deletions.
82 changes: 81 additions & 1 deletion common/djangoapps/student/tests/test_certificates.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.data import CertificatesDisplayBehaviors

from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from lms.djangoapps.certificates.api import get_certificate_url
from lms.djangoapps.certificates.data import CertificateStatuses
Expand All @@ -35,6 +36,7 @@ class CertificateDisplayTestBase(SharedModuleStoreTestCase):
MODULESTORE = TEST_DATA_MONGO_AMNESTY_MODULESTORE
USERNAME = "test_user"
PASSWORD = "password"
DOWNLOAD_URL = "http://www.example.com/certificate.pdf"

@classmethod
def setUpClass(cls):
Expand All @@ -61,7 +63,7 @@ def _check_linkedin_visibility(self, is_visible):
else:
self.assertNotContains(response, 'Add Certificate to LinkedIn Profile')

def _create_certificate(self, enrollment_mode):
def _create_certificate(self, enrollment_mode, download_url=DOWNLOAD_URL):
"""Simulate that the user has a generated certificate. """
CourseEnrollmentFactory.create(
user=self.user,
Expand All @@ -71,10 +73,38 @@ def _create_certificate(self, enrollment_mode):
user=self.user,
course_id=self.course.id,
mode=enrollment_mode,
download_url=download_url,
status=CertificateStatuses.downloadable,
grade=0.98,
)

def _check_can_download_certificate(self):
"""
Inspect the dashboard to see if a certificate can be downloaded.
"""
response = self.client.get(reverse('dashboard'))
self.assertContains(response, 'Download my')
self.assertContains(response, self.DOWNLOAD_URL)

def _check_can_download_certificate_no_id(self):
"""
Inspects the dashboard to see if a certificate for a non verified course enrollment
is present
"""
response = self.client.get(reverse('dashboard'))
self.assertContains(response, 'Download')
self.assertContains(response, self.DOWNLOAD_URL)

def _check_can_not_download_certificate(self):
"""
Make sure response does not have any of the download certificate buttons
"""
response = self.client.get(reverse('dashboard'))
self.assertNotContains(response, 'View Test_Certificate')
self.assertNotContains(response, 'Download my Test_Certificate')
self.assertNotContains(response, 'Download my Test_Certificate')
self.assertNotContains(response, self.DOWNLOAD_URL)


@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
Expand All @@ -101,6 +131,7 @@ def _check_message(self, visible_date): # lint-amnesty, pylint: disable=missing
if is_past:
self.assertNotContains(response, test_message)
self.assertNotContains(response, "View Test_Certificate")
self._check_can_download_certificate()

else:
self.assertContains(response, test_message)
Expand Down Expand Up @@ -144,6 +175,27 @@ class CertificateDisplayTest(CertificateDisplayTestBase):
Tests of certificate display.
"""

@ddt.data('verified', 'professional')
@patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': False})
def test_display_verified_certificate(self, enrollment_mode):
self._create_certificate(enrollment_mode)
self._check_can_download_certificate()

@patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': False})
def test_no_certificate_status_no_problem(self):
with patch('common.djangoapps.student.views.dashboard.cert_info', return_value={}):
self._create_certificate('honor')
self._check_can_not_download_certificate()

@patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': False})
def test_display_verified_certificate_no_id(self):
"""
Confirm that if we get a certificate with a no-id-professional mode
we still can download our certificate
"""
self._create_certificate(CourseMode.NO_ID_PROFESSIONAL_MODE)
self._check_can_download_certificate_no_id()

@ddt.data('verified', 'honor', 'professional')
def test_unverified_certificate_message(self, enrollment_mode):
cert = self._create_certificate(enrollment_mode)
Expand Down Expand Up @@ -173,6 +225,34 @@ def test_post_to_linkedin_visibility(self):
self._check_linkedin_visibility(True)


@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class CertificateDisplayTestHtmlView(CertificateDisplayTestBase):
"""
Tests of webview certificate display
"""

@classmethod
def setUpClass(cls):
super().setUpClass()
cls.course.cert_html_view_enabled = True
cls.course.save()
cls.store.update_item(cls.course, cls.USERNAME)

@ddt.data('verified', 'honor')
@override_settings(CERT_NAME_SHORT='Test_Certificate')
@patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': True})
def test_display_download_certificate_button(self, enrollment_mode):
"""
Tests if CERTIFICATES_HTML_VIEW is True
and course has enabled web certificates via cert_html_view_enabled setting
and no active certificate configuration available
then any of the web view certificate Download button should not be visible.
"""
self._create_certificate(enrollment_mode, download_url='')
self._check_can_not_download_certificate()


@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class CertificateDisplayTestLinkedHtmlView(CertificateDisplayTestBase):
Expand Down
88 changes: 77 additions & 11 deletions lms/djangoapps/courseware/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1442,6 +1442,7 @@ def test_view_certificate_link(self):
user=self.user,
course_id=self.course.id,
status=CertificateStatuses.downloadable,
download_url="http://www.example.com/certificate.pdf",
mode='honor'
)

Expand Down Expand Up @@ -1492,6 +1493,34 @@ def test_view_certificate_link(self):
self.assertContains(resp, "Your certificate is available")
self.assertContains(resp, "earned a certificate for this course.")

@patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': False})
def test_view_certificate_link_hidden(self):
"""
If certificate web view is disabled then certificate web view button should not appear for user who certificate
is available/generated
"""
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)

with patch('lms.djangoapps.grades.course_grade_factory.CourseGradeFactory.read') as mock_create:
course_grade = mock_create.return_value
course_grade.passed = True
course_grade.summary = {'grade': 'Pass', 'percent': 0.75, 'section_breakdown': [], 'grade_breakdown': {}}

resp = self._get_progress_page()
self.assertContains(resp, "Download Your Certificate")

@ddt.data(
(True, 52),
(False, 52),
Expand Down Expand Up @@ -1563,7 +1592,9 @@ def test_page_with_invalidated_certificate_with_html_view(self):
Verify that for html certs if certificate is marked as invalidated than
re-generate button should not appear on progress page.
"""
generated_certificate = self.generate_certificate("honor")
generated_certificate = self.generate_certificate(
"http://www.example.com/certificate.pdf", "honor"
)

# Course certificate configurations
certificates = [
Expand Down Expand Up @@ -1598,7 +1629,9 @@ def test_page_with_allowlisted_certificate_with_html_view(self):
"""
Verify that view certificate appears for an allowlisted user
"""
generated_certificate = self.generate_certificate("honor")
generated_certificate = self.generate_certificate(
"http://www.example.com/certificate.pdf", "honor"
)

# Course certificate configurations
certificates = [
Expand Down Expand Up @@ -1632,6 +1665,24 @@ def test_page_with_allowlisted_certificate_with_html_view(self):
self.assertContains(resp, "View Certificate")
self.assert_invalidate_certificate(generated_certificate)

def test_page_with_invalidated_certificate_with_pdf(self):
"""
Verify that for pdf certs if certificate is marked as invalidated than
re-generate button should not appear on progress page.
"""
generated_certificate = self.generate_certificate(
"http://www.example.com/certificate.pdf", "honor"
)

with patch('lms.djangoapps.grades.course_grade_factory.CourseGradeFactory.read') as mock_create:
course_grade = mock_create.return_value
course_grade.passed = True
course_grade.summary = {'grade': 'Pass', 'percent': 0.75, 'section_breakdown': [], 'grade_breakdown': {}}

resp = self._get_progress_page()
self.assertContains(resp, 'Download Your Certificate')
self.assert_invalidate_certificate(generated_certificate)

@ddt.data(
*itertools.product(
(
Expand Down Expand Up @@ -1722,7 +1773,9 @@ def test_invalidated_cert_data(self):
"""
Verify that invalidated cert data is returned if cert is invalidated.
"""
generated_certificate = self.generate_certificate("honor")
generated_certificate = self.generate_certificate(
"http://www.example.com/certificate.pdf", "honor"
)

CertificateInvalidationFactory.create(
generated_certificate=generated_certificate,
Expand All @@ -1740,7 +1793,9 @@ def test_downloadable_get_cert_data(self):
Verify that downloadable cert data is returned if cert is downloadable even
when DISABLE_HONOR_CERTIFICATES feature flag is turned ON.
"""
self.generate_certificate("honor")
self.generate_certificate(
"http://www.example.com/certificate.pdf", "honor"
)
response = views.get_cert_data(
self.user, self.course, CourseMode.HONOR, MagicMock(passed=True)
)
Expand All @@ -1752,7 +1807,9 @@ def test_generating_get_cert_data(self):
"""
Verify that generating cert data is returned if cert is generating.
"""
self.generate_certificate("honor")
self.generate_certificate(
"http://www.example.com/certificate.pdf", "honor"
)
with patch('lms.djangoapps.certificates.api.certificate_downloadable_status',
return_value=self.mock_certificate_downloadable_status(is_generating=True)):
response = views.get_cert_data(self.user, self.course, CourseMode.HONOR, MagicMock(passed=True))
Expand All @@ -1764,7 +1821,9 @@ def test_unverified_get_cert_data(self):
"""
Verify that unverified cert data is returned if cert is unverified.
"""
self.generate_certificate("honor")
self.generate_certificate(
"http://www.example.com/certificate.pdf", "honor"
)
with patch('lms.djangoapps.certificates.api.certificate_downloadable_status',
return_value=self.mock_certificate_downloadable_status(is_unverified=True)):
response = views.get_cert_data(self.user, self.course, CourseMode.HONOR, MagicMock(passed=True))
Expand All @@ -1776,7 +1835,9 @@ def test_request_get_cert_data(self):
"""
Verify that requested cert data is returned if cert is to be requested.
"""
self.generate_certificate("honor")
self.generate_certificate(
"http://www.example.com/certificate.pdf", "honor"
)
with patch('lms.djangoapps.certificates.api.certificate_downloadable_status',
return_value=self.mock_certificate_downloadable_status()):
response = views.get_cert_data(self.user, self.course, CourseMode.HONOR, MagicMock(passed=True))
Expand All @@ -1788,7 +1849,9 @@ def test_earned_but_not_available_get_cert_data(self):
"""
Verify that earned but not available cert data is returned if cert has been earned, but isn't available.
"""
self.generate_certificate("verified")
self.generate_certificate(
"http://www.example.com/certificate.pdf", "verified"
)
with patch('lms.djangoapps.certificates.api.certificate_downloadable_status',
return_value=self.mock_certificate_downloadable_status(earned_but_not_available=True)):
response = views.get_cert_data(self.user, self.course, CourseMode.VERIFIED, MagicMock(passed=True))
Expand Down Expand Up @@ -1834,30 +1897,33 @@ def assert_invalidate_certificate(self, certificate):
self.assertContains(resp, 'Your certificate has been invalidated')
self.assertContains(resp, 'Please contact your course team if you have any questions.')
self.assertNotContains(resp, 'View my Certificate')
self.assertNotContains(resp, 'Download my Certificate')

def generate_certificate(self, mode):
def generate_certificate(self, url, mode):
""" Dry method to generate certificate. """

generated_certificate = GeneratedCertificateFactory.create(
user=self.user,
course_id=self.course.id,
status=CertificateStatuses.downloadable,
download_url=url,
mode=mode
)
CertificateGenerationConfiguration(enabled=True).save()
certs_api.set_cert_generation_enabled(self.course.id, True)
return generated_certificate

def mock_certificate_downloadable_status(
self, is_downloadable=False, is_generating=False, is_unverified=False, uuid=None,
self, is_downloadable=False, is_generating=False, is_unverified=False, uuid=None, download_url=None,
earned_but_not_available=None,
):
"""Dry method to mock certificate downloadable status response."""
return {
'is_downloadable': is_downloadable,
'is_generating': is_generating,
'is_unverified': is_unverified,
'uuid': uuid,
'download_url': uuid,
'uuid': download_url,
'earned_but_not_available': earned_but_not_available,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
course_key: null,
type: null,
status: null,
download_url: null,
grade: null,
created: null,
modified: null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ define([
grade: '0.0',
type: 'honor',
course_key: 'course-v1:edX+DemoX+Demo_Course',
download_url: null,
modified: '2015-08-06T19:47:07+00:00',
regenerate: true
},
Expand All @@ -26,6 +27,7 @@ define([
grade: '1.0',
type: 'verified',
course_key: 'edx/test/2015',
download_url: 'http://www.example.com/certificate.pdf',
modified: '2015-08-06T19:47:05+00:00',
regenerate: true
}
Expand All @@ -39,6 +41,7 @@ define([
grade: '',
type: '',
course_key: 'edx/test1/2016',
download_url: null,
modified: '',
regenerate: false
}
Expand Down Expand Up @@ -114,15 +117,17 @@ define([
expect(results[0][0]).toEqual(REGENERATE_SEARCH_RESULTS[0].course_key);
expect(results[0][1]).toEqual(REGENERATE_SEARCH_RESULTS[0].type);
expect(results[0][2]).toEqual(REGENERATE_SEARCH_RESULTS[0].status);
expect(results[0][3]).toEqual(REGENERATE_SEARCH_RESULTS[0].grade);
expect(results[0][4]).toEqual(REGENERATE_SEARCH_RESULTS[0].modified);
expect(results[0][3]).toContain('Not available');
expect(results[0][4]).toEqual(REGENERATE_SEARCH_RESULTS[0].grade);
expect(results[0][5]).toEqual(REGENERATE_SEARCH_RESULTS[0].modified);

// Check the second row of results
expect(results[1][0]).toEqual(REGENERATE_SEARCH_RESULTS[1].course_key);
expect(results[1][1]).toEqual(REGENERATE_SEARCH_RESULTS[1].type);
expect(results[1][2]).toEqual(REGENERATE_SEARCH_RESULTS[1].status);
expect(results[1][3]).toEqual(REGENERATE_SEARCH_RESULTS[1].grade);
expect(results[1][4]).toEqual(REGENERATE_SEARCH_RESULTS[1].modified);
expect(results[1][3]).toContain(REGENERATE_SEARCH_RESULTS[1].download_url);
expect(results[1][4]).toEqual(REGENERATE_SEARCH_RESULTS[1].grade);
expect(results[1][5]).toEqual(REGENERATE_SEARCH_RESULTS[1].modified);


searchFor('[email protected]', 'edx/test1/2016', requests, GENERATE_SEARCH_RESULTS);
Expand All @@ -133,8 +138,9 @@ define([
expect(results[0][0]).toEqual(GENERATE_SEARCH_RESULTS[0].course_key);
expect(results[0][1]).toEqual(GENERATE_SEARCH_RESULTS[0].type);
expect(results[0][2]).toEqual(GENERATE_SEARCH_RESULTS[0].status);
expect(results[0][3]).toEqual(GENERATE_SEARCH_RESULTS[0].grade);
expect(results[0][4]).toEqual(GENERATE_SEARCH_RESULTS[0].modified);
expect(results[0][3]).toContain('Not available');
expect(results[0][4]).toEqual(GENERATE_SEARCH_RESULTS[0].grade);
expect(results[0][5]).toEqual(GENERATE_SEARCH_RESULTS[0].modified);
});

it('searches for certificates and displays a message when there are no results', function() {
Expand Down
Loading

0 comments on commit 558a271

Please sign in to comment.