225 lines
9.4 KiB
Python
225 lines
9.4 KiB
Python
"""Tests for the certificates panel of the instructor dash. """
|
|
import contextlib
|
|
import ddt
|
|
import mock
|
|
from nose.plugins.attrib import attr
|
|
from django.core.urlresolvers import reverse
|
|
from django.test.utils import override_settings
|
|
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
|
from xmodule.modulestore.tests.factories import CourseFactory
|
|
from config_models.models import cache
|
|
from courseware.tests.factories import GlobalStaffFactory, InstructorFactory
|
|
from certificates.models import CertificateGenerationConfiguration
|
|
from certificates import api as certs_api
|
|
|
|
|
|
@attr('shard_1')
|
|
@ddt.ddt
|
|
class CertificatesInstructorDashTest(ModuleStoreTestCase):
|
|
"""Tests for the certificate panel of the instructor dash. """
|
|
|
|
ERROR_REASON = "An error occurred!"
|
|
DOWNLOAD_URL = "http://www.example.com/abcd123/cert.pdf"
|
|
|
|
def setUp(self):
|
|
super(CertificatesInstructorDashTest, self).setUp()
|
|
self.course = CourseFactory.create()
|
|
self.url = reverse(
|
|
'instructor_dashboard',
|
|
kwargs={'course_id': unicode(self.course.id)}
|
|
)
|
|
self.global_staff = GlobalStaffFactory()
|
|
self.instructor = InstructorFactory(course_key=self.course.id)
|
|
|
|
# Need to clear the cache for model-based configuration
|
|
cache.clear()
|
|
|
|
# Enable the certificate generation feature
|
|
CertificateGenerationConfiguration.objects.create(enabled=True)
|
|
|
|
def test_visible_only_to_global_staff(self):
|
|
# Instructors don't see the certificates section
|
|
self.client.login(username=self.instructor.username, password="test")
|
|
self._assert_certificates_visible(False)
|
|
|
|
# Global staff can see the certificates section
|
|
self.client.login(username=self.global_staff.username, password="test")
|
|
self._assert_certificates_visible(True)
|
|
|
|
def test_visible_only_when_feature_flag_enabled(self):
|
|
# Disable the feature flag
|
|
CertificateGenerationConfiguration.objects.create(enabled=False)
|
|
cache.clear()
|
|
|
|
# Now even global staff can't see the certificates section
|
|
self.client.login(username=self.global_staff.username, password="test")
|
|
self._assert_certificates_visible(False)
|
|
|
|
@ddt.data("started", "error", "success")
|
|
def test_show_certificate_status(self, status):
|
|
self.client.login(username=self.global_staff.username, password="test")
|
|
with self._certificate_status("honor", status):
|
|
self._assert_certificate_status("honor", status)
|
|
|
|
def test_show_enabled_button(self):
|
|
self.client.login(username=self.global_staff.username, password="test")
|
|
|
|
# Initially, no example certs are generated, so
|
|
# the enable button should be disabled
|
|
self._assert_enable_certs_button_is_disabled()
|
|
|
|
with self._certificate_status("honor", "success"):
|
|
# Certs are disabled for the course, so the enable button should be shown
|
|
self._assert_enable_certs_button(True)
|
|
|
|
# Enable certificates for the course
|
|
certs_api.set_cert_generation_enabled(self.course.id, True)
|
|
|
|
# Now the "disable" button should be shown
|
|
self._assert_enable_certs_button(False)
|
|
|
|
def test_can_disable_even_after_failure(self):
|
|
self.client.login(username=self.global_staff.username, password="test")
|
|
|
|
with self._certificate_status("honor", "error"):
|
|
# When certs are disabled for a course, then don't allow them
|
|
# to be enabled if certificate generation doesn't complete successfully
|
|
certs_api.set_cert_generation_enabled(self.course.id, False)
|
|
self._assert_enable_certs_button_is_disabled()
|
|
|
|
# However, if certificates are already enabled, allow them
|
|
# to be disabled even if an error has occurred
|
|
certs_api.set_cert_generation_enabled(self.course.id, True)
|
|
self._assert_enable_certs_button(False)
|
|
|
|
def _assert_certificates_visible(self, is_visible):
|
|
"""Check that the certificates section is visible on the instructor dash. """
|
|
response = self.client.get(self.url)
|
|
if is_visible:
|
|
self.assertContains(response, "Certificates")
|
|
else:
|
|
self.assertNotContains(response, "Certificates")
|
|
|
|
@contextlib.contextmanager
|
|
def _certificate_status(self, description, status):
|
|
"""Configure the certificate status by mocking the certificates API. """
|
|
patched = 'instructor.views.instructor_dashboard.certs_api.example_certificates_status'
|
|
with mock.patch(patched) as certs_api_status:
|
|
cert_status = [{
|
|
'description': description,
|
|
'status': status
|
|
}]
|
|
|
|
if status == 'error':
|
|
cert_status[0]['error_reason'] = self.ERROR_REASON
|
|
if status == 'success':
|
|
cert_status[0]['download_url'] = self.DOWNLOAD_URL
|
|
|
|
certs_api_status.return_value = cert_status
|
|
yield
|
|
|
|
def _assert_certificate_status(self, cert_name, expected_status):
|
|
"""Check the certificate status display on the instructor dash. """
|
|
response = self.client.get(self.url)
|
|
|
|
if expected_status == 'started':
|
|
expected = 'Generating example {name} certificate'.format(name=cert_name)
|
|
self.assertContains(response, expected)
|
|
elif expected_status == 'error':
|
|
expected = self.ERROR_REASON
|
|
self.assertContains(response, expected)
|
|
elif expected_status == 'success':
|
|
expected = self.DOWNLOAD_URL
|
|
self.assertContains(response, expected)
|
|
else:
|
|
self.fail("Invalid certificate status: {status}".format(status=expected_status))
|
|
|
|
def _assert_enable_certs_button_is_disabled(self):
|
|
"""Check that the "enable student-generated certificates" button is disabled. """
|
|
response = self.client.get(self.url)
|
|
expected_html = '<button class="is-disabled" disabled>Enable Student-Generated Certificates</button>'
|
|
self.assertContains(response, expected_html)
|
|
|
|
def _assert_enable_certs_button(self, is_enabled):
|
|
"""Check whether the button says "enable" or "disable" cert generation. """
|
|
response = self.client.get(self.url)
|
|
expected_html = (
|
|
'Enable Student-Generated Certificates' if is_enabled
|
|
else 'Disable Student-Generated Certificates'
|
|
)
|
|
self.assertContains(response, expected_html)
|
|
|
|
|
|
@attr('shard_1')
|
|
@override_settings(CERT_QUEUE='certificates')
|
|
@ddt.ddt
|
|
class CertificatesInstructorApiTest(ModuleStoreTestCase):
|
|
"""Tests for the certificates end-points in the instructor dash API. """
|
|
|
|
def setUp(self):
|
|
super(CertificatesInstructorApiTest, self).setUp()
|
|
self.course = CourseFactory.create()
|
|
self.global_staff = GlobalStaffFactory()
|
|
self.instructor = InstructorFactory(course_key=self.course.id)
|
|
|
|
# Enable certificate generation
|
|
cache.clear()
|
|
CertificateGenerationConfiguration.objects.create(enabled=True)
|
|
|
|
@ddt.data('generate_example_certificates', 'enable_certificate_generation')
|
|
def test_allow_only_global_staff(self, url_name):
|
|
url = reverse(url_name, kwargs={'course_id': self.course.id})
|
|
|
|
# Instructors do not have access
|
|
self.client.login(username=self.instructor.username, password='test')
|
|
response = self.client.post(url)
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
# Global staff have access
|
|
self.client.login(username=self.global_staff.username, password='test')
|
|
response = self.client.post(url)
|
|
self.assertEqual(response.status_code, 302)
|
|
|
|
def test_generate_example_certificates(self):
|
|
self.client.login(username=self.global_staff.username, password='test')
|
|
url = reverse(
|
|
'generate_example_certificates',
|
|
kwargs={'course_id': unicode(self.course.id)}
|
|
)
|
|
response = self.client.post(url)
|
|
|
|
# Expect a redirect back to the instructor dashboard
|
|
self._assert_redirects_to_instructor_dash(response)
|
|
|
|
# Expect that certificate generation started
|
|
# Cert generation will fail here because XQueue isn't configured,
|
|
# but the status should at least not be None.
|
|
status = certs_api.example_certificates_status(self.course.id)
|
|
self.assertIsNot(status, None)
|
|
|
|
@ddt.data(True, False)
|
|
def test_enable_certificate_generation(self, is_enabled):
|
|
self.client.login(username=self.global_staff.username, password='test')
|
|
url = reverse(
|
|
'enable_certificate_generation',
|
|
kwargs={'course_id': unicode(self.course.id)}
|
|
)
|
|
params = {'certificates-enabled': 'true' if is_enabled else 'false'}
|
|
response = self.client.post(url, data=params)
|
|
|
|
# Expect a redirect back to the instructor dashboard
|
|
self._assert_redirects_to_instructor_dash(response)
|
|
|
|
# Expect that certificate generation is now enabled for the course
|
|
actual_enabled = certs_api.cert_generation_enabled(self.course.id)
|
|
self.assertEqual(is_enabled, actual_enabled)
|
|
|
|
def _assert_redirects_to_instructor_dash(self, response):
|
|
"""Check that the response redirects to the certificates section. """
|
|
expected_redirect = reverse(
|
|
'instructor_dashboard',
|
|
kwargs={'course_id': unicode(self.course.id)}
|
|
)
|
|
expected_redirect += '#view-certificates'
|
|
self.assertRedirects(response, expected_redirect)
|