test_certificates.py 11.5 KB
Newer Older
1 2 3
"""Tests for display of certificates on the student dashboard. """

import unittest
4

5
import datetime
6
import ddt
7
import mock
8 9
from django.conf import settings
from django.core.urlresolvers import reverse
10
from django.test.utils import override_settings
11
from mock import patch
12
from pytz import UTC
13

14
from certificates.api import get_certificate_url  # pylint: disable=import-error
15
from certificates.models import CertificateStatuses  # pylint: disable=import-error
16
from certificates.tests.factories import GeneratedCertificateFactory  # pylint: disable=import-error
17
from course_modes.models import CourseMode
18
from student.models import LinkedInAddToProfileConfiguration
19 20 21 22 23
from student.tests.factories import CourseEnrollmentFactory, UserFactory
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory

24

25
# pylint: disable=no-member
26

27 28 29
PAST_DATE = datetime.datetime.now(UTC) - datetime.timedelta(days=2)
FUTURE_DATE = datetime.datetime.now(UTC) + datetime.timedelta(days=2)

30

31
class CertificateDisplayTestBase(SharedModuleStoreTestCase):
32 33 34 35 36 37
    """Tests display of certificates on the student dashboard. """

    USERNAME = "test_user"
    PASSWORD = "password"
    DOWNLOAD_URL = "http://www.example.com/certificate.pdf"

38 39
    @classmethod
    def setUpClass(cls):
40
        super(CertificateDisplayTestBase, cls).setUpClass()
41 42 43 44 45 46
        cls.course = CourseFactory()
        cls.course.certificates_display_behavior = "early_with_info"

        with cls.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, cls.course.id):
            cls.store.update_item(cls.course, cls.USERNAME)

47
    def setUp(self):
48
        super(CertificateDisplayTestBase, self).setUp()
49 50 51 52
        self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD)
        result = self.client.login(username=self.USERNAME, password=self.PASSWORD)
        self.assertTrue(result, msg="Could not log in")

53 54 55 56 57 58 59 60 61 62 63 64
    def _check_linkedin_visibility(self, is_visible):
        """
        Performs assertions on the Dashboard
        """
        response = self.client.get(reverse('dashboard'))
        if is_visible:
            self.assertContains(response, u'Add Certificate to LinkedIn Profile')
        else:
            self.assertNotContains(response, u'Add Certificate to LinkedIn Profile')

    def _create_certificate(self, enrollment_mode):
        """Simulate that the user has a generated certificate. """
65 66 67 68
        CourseEnrollmentFactory.create(
            user=self.user,
            course_id=self.course.id,
            mode=enrollment_mode)
69 70 71 72 73
        return GeneratedCertificateFactory(
            user=self.user,
            course_id=self.course.id,
            mode=enrollment_mode,
            download_url=self.DOWNLOAD_URL,
74
            status=CertificateStatuses.downloadable,
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
            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, u'Download Your ID Verified')
        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, u'Download')
        self.assertContains(response, u'(PDF)')
        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, u'View Test_Certificate')
        self.assertNotContains(response, u'Download Your Test_Certificate (PDF)')
        self.assertNotContains(response, u'Download Test_Certificate (PDF)')
        self.assertNotContains(response, self.DOWNLOAD_URL)


@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
109 110 111 112 113
class CertificateDashboardMessageDisplayTest(CertificateDisplayTestBase):
    """
    Tests the certificates messages for a course in the dashboard.
    """

114 115
    ENABLED_SIGNALS = ['course_published']

116 117 118 119 120 121 122 123 124 125
    @classmethod
    def setUpClass(cls):
        super(CertificateDashboardMessageDisplayTest, cls).setUpClass()
        cls.course.certificates_display_behavior = "end"
        cls.course.save()
        cls.store.update_item(cls.course, cls.USERNAME)

    def _check_message(self, certificate_available_date):
        response = self.client.get(reverse('dashboard'))

126 127 128 129
        if certificate_available_date is None:
            self.assertNotContains(response, u"Your certificate will be available on")
            self.assertNotContains(response, u"View Test_Certificate")
        elif datetime.datetime.now(UTC) < certificate_available_date:
130 131 132 133 134
            self.assertContains(response, u"Your certificate will be available on")
            self.assertNotContains(response, u"View Test_Certificate")
        else:
            self._check_can_download_certificate()

135
    @ddt.data(True, False, None)
136 137 138 139 140
    def test_certificate_available_date(self, past_certificate_available_date):
        cert = self._create_certificate('verified')
        cert.status = CertificateStatuses.downloadable
        cert.save()

141 142 143 144 145 146
        if past_certificate_available_date is None:
            certificate_available_date = None
        elif past_certificate_available_date:
            certificate_available_date = PAST_DATE
        elif not past_certificate_available_date:
            certificate_available_date = FUTURE_DATE
147 148 149 150 151 152 153 154 155 156

        self.course.certificate_available_date = certificate_available_date
        self.course.save()
        self.store.update_item(self.course, self.USERNAME)

        self._check_message(certificate_available_date)


@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
157 158 159 160 161
class CertificateDisplayTest(CertificateDisplayTestBase):
    """
    Tests of certificate display.
    """

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

168
    @patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': False})
169 170 171 172 173 174
    def test_no_certificate_status_no_problem(self):
        with patch('student.views.cert_info', return_value={}):
            self._create_certificate('honor')
            self._check_can_not_download_certificate()

    @patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': False})
175 176 177 178 179 180 181 182
    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()

183 184 185 186 187 188 189 190 191 192 193
    @ddt.data('verified', 'honor', 'professional')
    def test_unverified_certificate_message(self, enrollment_mode):
        cert = self._create_certificate(enrollment_mode)
        cert.status = CertificateStatuses.unverified
        cert.save()
        response = self.client.get(reverse('dashboard'))
        self.assertContains(
            response,
            u'do not have a current verified identity with {platform_name}'
            .format(platform_name=settings.PLATFORM_NAME))

194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
    def test_post_to_linkedin_invisibility(self):
        """
        Verifies that the post certificate to linked button
        does not appear by default (when config is not set)
        """
        self._create_certificate('honor')

        # until we set up the configuration, the LinkedIn action
        # button should not be visible
        self._check_linkedin_visibility(False)

    def test_post_to_linkedin_visibility(self):
        """
        Verifies that the post certificate to linked button appears
        as expected
        """
        self._create_certificate('honor')

        config = LinkedInAddToProfileConfiguration(
            company_identifier='0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9',
            enabled=True
        )
        config.save()

        # now we should see it
        self._check_linkedin_visibility(True)

221 222
    @mock.patch("openedx.core.djangoapps.theming.helpers.is_request_in_themed_site", mock.Mock(return_value=True))
    def test_post_to_linkedin_site_specific(self):
223
        """
224
        Verifies behavior for themed sites which disables the post to LinkedIn
225 226 227 228 229 230 231 232 233 234
        feature (for now)
        """
        self._create_certificate('honor')

        config = LinkedInAddToProfileConfiguration(
            company_identifier='0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9',
            enabled=True
        )
        config.save()

235
        # now we should not see it because we are in a themed site
236 237 238
        self._check_linkedin_visibility(False)


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

246 247 248 249 250 251
    @classmethod
    def setUpClass(cls):
        super(CertificateDisplayTestHtmlView, cls).setUpClass()
        cls.course.cert_html_view_enabled = True
        cls.course.save()
        cls.store.update_item(cls.course, cls.USERNAME)
252

253 254 255 256
    @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):
257
        """
258 259 260 261
        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 Download certificate button should not be visible.
262
        """
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
        self._create_certificate(enrollment_mode)
        self._check_can_not_download_certificate()


@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class CertificateDisplayTestLinkedHtmlView(CertificateDisplayTestBase):
    """
    Tests of linked student certificates.
    """

    @classmethod
    def setUpClass(cls):
        super(CertificateDisplayTestLinkedHtmlView, cls).setUpClass()
        cls.course.cert_html_view_enabled = True

        certificates = [
            {
                'id': 0,
                'name': 'Test Name',
                'description': 'Test Description',
                'is_active': True,
                'signatories': [],
                'version': 1
            }
        ]
        cls.course.certificates = {'certificates': certificates}

        cls.course.save()
        cls.store.update_item(cls.course, cls.USERNAME)

    @ddt.data('verified')
    @override_settings(CERT_NAME_SHORT='Test_Certificate')
    @patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': True})
    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)
301

302
        response = self.client.get(reverse('dashboard'))
303 304 305

        self.assertContains(response, u'View Test_Certificate')
        self.assertContains(response, test_url)