diff --git a/lms/djangoapps/learner_dashboard/tests/test_programs.py b/lms/djangoapps/learner_dashboard/tests/test_programs.py index ad98aed18a..bfbf6737e9 100644 --- a/lms/djangoapps/learner_dashboard/tests/test_programs.py +++ b/lms/djangoapps/learner_dashboard/tests/test_programs.py @@ -2,6 +2,7 @@ Unit tests covering the program listing and detail pages. """ import datetime +import json import unittest from urlparse import urljoin @@ -16,10 +17,11 @@ from provider.constants import CONFIDENTIAL from openedx.core.djangoapps.credentials.models import CredentialsApiConfig from openedx.core.djangoapps.credentials.tests import factories as credentials_factories from openedx.core.djangoapps.credentials.tests.mixins import CredentialsDataMixin, CredentialsApiConfigMixin +from openedx.core.djangoapps.programs.models import ProgramsApiConfig +from openedx.core.djangoapps.programs.tests import factories from openedx.core.djangoapps.programs.tests.mixins import ( ProgramsApiConfigMixin, ProgramsDataMixin) -from openedx.core.djangoapps.programs.models import ProgramsApiConfig from student.models import CourseEnrollment from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase @@ -226,25 +228,56 @@ class TestProgramListing( self.assertNotContains(response, certificate['credential_url']) -@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') +@httpretty.activate @override_settings(MKTG_URLS={'ROOT': 'http://edx.org'}) +@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') class TestProgramDetails(ProgramsApiConfigMixin, TestCase): """ Unit tests for the program details page """ + program_id = 123 + password = 'test' + def setUp(self): super(TestProgramDetails, self).setUp() - self.user = UserFactory() - self.details_page = reverse('program_details_view', args=['123']) + self.details_page = reverse('program_details_view', args=[self.program_id]) - self.client.login(username=self.user.username, password='test') + self.user = UserFactory() + self.client.login(username=self.user.username, password=self.password) + + ClientFactory(name=ProgramsApiConfig.OAUTH2_CLIENT_NAME, client_type=CONFIDENTIAL) + + self.data = factories.Program( + organizations=[factories.Organization()], + course_codes=[ + factories.CourseCode(run_modes=[factories.RunMode()]), + ] + ) + + def _mock_programs_api(self): + """Helper for mocking out Programs API URLs.""" + self.assertTrue(httpretty.is_enabled(), msg='httpretty must be enabled to mock Programs API calls.') + + url = '{api_root}/programs/{id}/'.format( + api_root=ProgramsApiConfig.current().internal_api_url.strip('/'), + id=self.program_id + ) + body = json.dumps(self.data) + + httpretty.register_uri(httpretty.GET, url, body=body, content_type='application/json') + + def _assert_program_data_present(self, response): + """Verify that program data is present.""" + self.assertContains(response, 'programData') + self.assertContains(response, self.data['name']) def test_login_required(self): """ Verify that login is required to access the page. """ self.create_programs_config() + self._mock_programs_api() self.client.logout() @@ -254,10 +287,10 @@ class TestProgramDetails(ProgramsApiConfigMixin, TestCase): '{}?next={}'.format(reverse('signin_user'), self.details_page) ) - self.client.login(username=self.user.username, password='test') + self.client.login(username=self.user.username, password=self.password) response = self.client.get(self.details_page) - self.assertEquals(response.status_code, 200) + self._assert_program_data_present(response) def test_404_if_disabled(self): """ @@ -271,12 +304,13 @@ class TestProgramDetails(ProgramsApiConfigMixin, TestCase): def test_page_routing(self): """Verify that the page can be hit with or without a program name in the URL.""" self.create_programs_config() + self._mock_programs_api() response = self.client.get(self.details_page) - self.assertEquals(response.status_code, 200) + self._assert_program_data_present(response) response = self.client.get(self.details_page + 'program_name/') - self.assertEquals(response.status_code, 200) + self._assert_program_data_present(response) response = self.client.get(self.details_page + 'program_name/invalid/') self.assertEquals(response.status_code, 404) diff --git a/lms/djangoapps/learner_dashboard/views.py b/lms/djangoapps/learner_dashboard/views.py index 0735934687..0a9a6482fe 100644 --- a/lms/djangoapps/learner_dashboard/views.py +++ b/lms/djangoapps/learner_dashboard/views.py @@ -9,7 +9,7 @@ from django.http import Http404 from edxmako.shortcuts import render_to_response from openedx.core.djangoapps.credentials.utils import get_programs_credentials from openedx.core.djangoapps.programs.models import ProgramsApiConfig -from openedx.core.djangoapps.programs.utils import ProgramProgressMeter, get_display_category +from openedx.core.djangoapps.programs.utils import ProgramProgressMeter, get_programs, get_display_category from student.views import get_course_enrollments @@ -50,13 +50,16 @@ def view_programs(request): @login_required @require_GET -def program_details(request, program_id): # pylint: disable=unused-argument +def program_details(request, program_id): """View details about a specific program.""" show_program_details = ProgramsApiConfig.current().show_program_details if not show_program_details: raise Http404 + program_data = get_programs(request.user, program_id=program_id) + context = { + 'program_data': program_data, 'nav_hidden': True, 'disable_courseware_js': True, 'uses_pattern_library': True diff --git a/lms/templates/learner_dashboard/program_details.html b/lms/templates/learner_dashboard/program_details.html index 7999fb2a81..2b4b0e9a6d 100644 --- a/lms/templates/learner_dashboard/program_details.html +++ b/lms/templates/learner_dashboard/program_details.html @@ -13,7 +13,9 @@ from openedx.core.djangolib.js_utils import ( <%block name="js_extra"> <%static:require_module module_name="js/learner_dashboard/program_details_factory" class_name="ProgramDetailsFactory"> -ProgramDetailsFactory({}); +ProgramDetailsFactory({ + programData: ${program_data | n, dump_js_escaped_json} +}); diff --git a/openedx/core/djangoapps/programs/utils.py b/openedx/core/djangoapps/programs/utils.py index 080e0cfe5a..afb4a27ad5 100644 --- a/openedx/core/djangoapps/programs/utils.py +++ b/openedx/core/djangoapps/programs/utils.py @@ -10,7 +10,7 @@ from openedx.core.lib.edx_api_utils import get_edx_api_data log = logging.getLogger(__name__) -def get_programs(user): +def get_programs(user, program_id=None): """Given a user, get programs from the Programs service. Returned value is cached depending on user permissions. Staff users making requests against Programs will receive unpublished programs, while regular users will only receive @@ -19,6 +19,9 @@ def get_programs(user): Arguments: user (User): The user to authenticate as when requesting programs. + Keyword Arguments: + program_id (int): Identifies a specific program for which to retrieve data. + Returns: list of dict, representing programs returned by the Programs service. """ @@ -27,7 +30,7 @@ def get_programs(user): # Bypass caching for staff users, who may be creating Programs and want # to see them displayed immediately. cache_key = programs_config.CACHE_KEY if programs_config.is_cache_enabled and not user.is_staff else None - return get_edx_api_data(programs_config, user, 'programs', cache_key=cache_key) + return get_edx_api_data(programs_config, user, 'programs', resource_id=program_id, cache_key=cache_key) def flatten_programs(programs, course_ids):