From b54096845784c74e23b61fadd4c76a2b43da4b53 Mon Sep 17 00:00:00 2001 From: Renzo Lucioni Date: Tue, 26 Jul 2016 10:59:45 -0400 Subject: [PATCH] Updates to facilitate program category casing changes The LMS continues to expect lowercased category slugs, while the Studio program creator now uses correctly cased slugs. Programs should be updated before this is released. Part of ECOM-5018. --- .../js/programs/models/program_model.js | 3 +- .../views/programs/program_creator_spec.js | 4 +- .../views/programs/program_details_spec.js | 2 +- .../programs/program_creator_form.underscore | 4 +- .../core/djangoapps/programs/tests/mixins.py | 4 +- .../djangoapps/programs/tests/test_utils.py | 41 ++++++++++--------- openedx/core/djangoapps/programs/utils.py | 12 +++++- 7 files changed, 42 insertions(+), 28 deletions(-) diff --git a/cms/static/js/programs/models/program_model.js b/cms/static/js/programs/models/program_model.js index 16776338a9..2a38c4e7db 100644 --- a/cms/static/js/programs/models/program_model.js +++ b/cms/static/js/programs/models/program_model.js @@ -24,7 +24,8 @@ define([ }, category: { required: true, - oneOf: ['xseries', 'micromasters'] + // TODO: Populate with the results of an API call for valid categories. + oneOf: ['XSeries', 'MicroMasters'] }, organizations: 'validateOrganizations', marketing_slug: { diff --git a/cms/static/js/spec/views/programs/program_creator_spec.js b/cms/static/js/spec/views/programs/program_creator_spec.js index 56de3e1439..4484a30132 100644 --- a/cms/static/js/spec/views/programs/program_creator_spec.js +++ b/cms/static/js/spec/views/programs/program_creator_spec.js @@ -77,7 +77,7 @@ define([ spyOn( Router.prototype, 'goHome' ); sampleInput = { - category: 'xseries', + category: 'XSeries', organizations: 'test-org-key', name: 'Test Course Name', subtitle: 'Test Course Subtitle', @@ -131,7 +131,7 @@ define([ it( 'should submit the form correctly when creating micromasters program ', function(){ var programId = 221; - sampleInput.category = 'micromasters'; + sampleInput.category = 'MicroMasters'; completeForm( sampleInput ); diff --git a/cms/static/js/spec/views/programs/program_details_spec.js b/cms/static/js/spec/views/programs/program_details_spec.js index b84b123dd8..1aacbfb5d4 100644 --- a/cms/static/js/spec/views/programs/program_details_spec.js +++ b/cms/static/js/spec/views/programs/program_details_spec.js @@ -86,7 +86,7 @@ define([ } ], programData = { - category: 'xseries', + category: 'XSeries', course_codes: [{ display_name: 'test-course-display_name', key: 'test-course-key', diff --git a/cms/templates/js/programs/program_creator_form.underscore b/cms/templates/js/programs/program_creator_form.underscore index 4fe47ce691..464d08046e 100644 --- a/cms/templates/js/programs/program_creator_form.underscore +++ b/cms/templates/js/programs/program_creator_form.underscore @@ -5,8 +5,8 @@
diff --git a/openedx/core/djangoapps/programs/tests/mixins.py b/openedx/core/djangoapps/programs/tests/mixins.py index 3b6bf79d7f..b3df487342 100644 --- a/openedx/core/djangoapps/programs/tests/mixins.py +++ b/openedx/core/djangoapps/programs/tests/mixins.py @@ -99,11 +99,13 @@ class ProgramsDataMixin(object): ] } - def mock_programs_api(self, data=None, status_code=200): + def mock_programs_api(self, data=None, program_id='', status_code=200): """Utility for mocking out Programs API URLs.""" self.assertTrue(httpretty.is_enabled(), msg='httpretty must be enabled to mock Programs API calls.') url = ProgramsApiConfig.current().internal_api_url.strip('/') + '/programs/' + if program_id: + url += '{}/'.format(str(program_id)) if data is None: data = self.PROGRAMS_API_RESPONSE diff --git a/openedx/core/djangoapps/programs/tests/test_utils.py b/openedx/core/djangoapps/programs/tests/test_utils.py index 8f16be3931..d20a4a3626 100644 --- a/openedx/core/djangoapps/programs/tests/test_utils.py +++ b/openedx/core/djangoapps/programs/tests/test_utils.py @@ -40,8 +40,10 @@ ECOMMERCE_URL_ROOT = 'https://example-ecommerce.com' MARKETING_URL = 'https://www.example.com/marketing/path' -@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') +@ddt.ddt @attr('shard_2') +@httpretty.activate +@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, CredentialsDataMixin, CredentialsApiConfigMixin, CacheIsolationTestCase): """Tests covering the retrieval of programs from the Programs service.""" @@ -77,7 +79,6 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, Credential ) ] - @httpretty.activate def test_get_programs(self): """Verify programs data can be retrieved.""" self.create_programs_config() @@ -92,7 +93,24 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, Credential # Verify the API was actually hit (not the cache). self.assertEqual(len(httpretty.httpretty.latest_requests), 1) - @httpretty.activate + @ddt.data(True, False) + def test_get_programs_category_casing(self, is_detail): + """Temporary. Verify that program categories are lowercased.""" + self.create_programs_config() + + program = factories.Program(category='camelCase') + + if is_detail: + program_id = program['id'] + + self.mock_programs_api(data=program, program_id=program_id) + data = utils.get_programs(self.user, program_id=program_id) + self.assertEqual(data['category'], 'camelcase') + else: + self.mock_programs_api(data={'results': [program]}) + data = utils.get_programs(self.user) + self.assertEqual(data[0]['category'], 'camelcase') + def test_get_programs_caching(self): """Verify that when enabled, the cache is used for non-staff users.""" self.create_programs_config(cache_ttl=1) @@ -133,7 +151,6 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, Credential self.assertEqual(actual, []) self.assertTrue(mock_init.called) - @httpretty.activate def test_get_programs_data_retrieval_failure(self): """Verify behavior when data can't be retrieved from Programs.""" self.create_programs_config() @@ -142,7 +159,6 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, Credential actual = utils.get_programs(self.user) self.assertEqual(actual, []) - @httpretty.activate def test_get_programs_for_dashboard(self): """Verify programs data can be retrieved and parsed correctly.""" self.create_programs_config() @@ -165,7 +181,6 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, Credential actual = utils.get_programs_for_dashboard(self.user, self.COURSE_KEYS) self.assertEqual(actual, {}) - @httpretty.activate def test_get_programs_for_dashboard_no_data(self): """Verify behavior when no programs data is found for the user.""" self.create_programs_config() @@ -174,17 +189,6 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, Credential actual = utils.get_programs_for_dashboard(self.user, self.COURSE_KEYS) self.assertEqual(actual, {}) - @httpretty.activate - def test_get_programs_for_dashboard_invalid_data(self): - """Verify behavior when the Programs API returns invalid data and parsing fails.""" - self.create_programs_config() - invalid_program = {'invalid_key': 'invalid_data'} - self.mock_programs_api(data={'results': [invalid_program]}) - - actual = utils.get_programs_for_dashboard(self.user, self.COURSE_KEYS) - self.assertEqual(actual, {}) - - @httpretty.activate def test_get_program_for_certificates(self): """Verify programs data can be retrieved and parsed correctly for certificates.""" self.create_programs_config() @@ -199,7 +203,6 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, Credential self.assertEqual(len(actual), 2) self.assertEqual(actual, expected) - @httpretty.activate def test_get_program_for_certificates_no_data(self): """Verify behavior when no programs data is found for the user.""" self.create_programs_config() @@ -210,7 +213,6 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, Credential actual = utils.get_programs_for_credentials(self.user, program_credentials_data) self.assertEqual(actual, []) - @httpretty.activate def test_get_program_for_certificates_id_not_exist(self): """Verify behavior when no program with the given program_id in credentials exists. @@ -233,7 +235,6 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, Credential actual = utils.get_programs_for_credentials(self.user, credential_data) self.assertEqual(actual, []) - @httpretty.activate def test_get_display_category_success(self): self.create_programs_config() self.mock_programs_api() diff --git a/openedx/core/djangoapps/programs/utils.py b/openedx/core/djangoapps/programs/utils.py index 4a1c1f7b31..ebd6e5f802 100644 --- a/openedx/core/djangoapps/programs/utils.py +++ b/openedx/core/djangoapps/programs/utils.py @@ -48,7 +48,17 @@ def get_programs(user, program_id=None): # 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', resource_id=program_id, cache_key=cache_key) + + data = get_edx_api_data(programs_config, user, 'programs', resource_id=program_id, cache_key=cache_key) + + # TODO: Temporary, to be removed once category names are cased for display. ECOM-5018. + if data and program_id: + data['category'] = data['category'].lower() + else: + for program in data: + program['category'] = program['category'].lower() + + return data def flatten_programs(programs, course_ids):