From a8f2de837549d59c76252e308d83ddec7063c15e Mon Sep 17 00:00:00 2001 From: Ahsan Ulhaq Date: Mon, 16 May 2016 14:26:55 +0500 Subject: [PATCH] Course Dashboard Visual Update ECOM-4398 --- common/djangoapps/student/views.py | 35 ----- .../learner_dashboard/tests/test_programs.py | 31 +++- lms/djangoapps/learner_dashboard/views.py | 5 +- lms/static/sass/multicourse/_dashboard.scss | 10 +- lms/templates/dashboard.html | 13 -- .../djangoapps/credentials/tests/factories.py | 41 +++++ .../djangoapps/credentials/tests/mixins.py | 143 +++++++----------- .../credentials/tests/test_utils.py | 100 +++++++++++- openedx/core/djangoapps/credentials/utils.py | 32 ++++ .../core/djangoapps/programs/tests/mixins.py | 25 --- .../djangoapps/programs/tests/test_utils.py | 36 ++++- themes/edx.org/lms/templates/dashboard.html | 15 +- 12 files changed, 283 insertions(+), 203 deletions(-) create mode 100644 openedx/core/djangoapps/credentials/tests/factories.py diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index 2b5cfbe90e..3c8ae53ed3 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -122,8 +122,6 @@ from eventtracking import tracker # Note that this lives in LMS, so this dependency should be refactored. from notification_prefs.views import enable_notifications -# Note that this lives in openedx, so this dependency should be refactored. -from openedx.core.djangoapps.credentials.utils import get_user_program_credentials from openedx.core.djangoapps.credit.email_utils import get_credit_provider_display_names, make_providers_strings from openedx.core.djangoapps.user_api.preferences import api as preferences_api from openedx.core.djangoapps.programs.utils import get_programs_for_dashboard, get_display_category @@ -615,7 +613,6 @@ def dashboard(request): # This is passed along in the template context to allow rendering of # program-related information on the dashboard. course_programs = _get_course_programs(user, [enrollment.course_id for enrollment in course_enrollments]) - xseries_credentials = _get_xseries_credentials(user) # Construct a dictionary of course mode information # used to render the course list. We re-use the course modes dict @@ -741,7 +738,6 @@ def dashboard(request): 'nav_hidden': True, 'course_programs': course_programs, 'disable_courseware_js': True, - 'xseries_credentials': xseries_credentials, 'show_program_listing': ProgramsApiConfig.current().show_program_listing, } @@ -2483,34 +2479,3 @@ def _get_course_programs(user, user_enrolled_courses): # pylint: disable=invali log.warning('Program structure is invalid, skipping display: %r', program) return programs_data - - -def _get_xseries_credentials(user): - """Return program credentials data required for display on - the learner dashboard. - - Given a user, find all programs for which certificates have been earned - and return list of dictionaries of required program data. - - Arguments: - user (User): user object for getting programs credentials. - - Returns: - list of dict, containing data corresponding to the programs for which - the user has been awarded a credential. - """ - programs_credentials = get_user_program_credentials(user) - credentials_data = [] - for program in programs_credentials: - if program.get('category') == 'xseries': - try: - program_data = { - 'display_name': program['name'], - 'subtitle': program['subtitle'], - 'credential_url': program['credential_url'], - } - credentials_data.append(program_data) - except KeyError: - log.warning('Program structure is invalid: %r', program) - - return credentials_data diff --git a/lms/djangoapps/learner_dashboard/tests/test_programs.py b/lms/djangoapps/learner_dashboard/tests/test_programs.py index 11cfb12edb..016e42d82c 100644 --- a/lms/djangoapps/learner_dashboard/tests/test_programs.py +++ b/lms/djangoapps/learner_dashboard/tests/test_programs.py @@ -14,6 +14,7 @@ from opaque_keys.edx import locator 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.tests.mixins import ( ProgramsApiConfigMixin, @@ -155,18 +156,36 @@ class TestProgramListing( '{}?next={}'.format(reverse('signin_user'), self.url) ) - # TODO: Use a factory to generate this data. + def _expected_progam_credentials_data(self): + """ + Dry method for getting expected program credentials response data. + """ + return [ + credentials_factories.UserCredential( + id=1, + username='test', + credential=credentials_factories.ProgramCredential() + ), + credentials_factories.UserCredential( + id=2, + username='test', + credential=credentials_factories.ProgramCredential() + ) + ] + def _expected_credentials_data(self): """ Dry method for getting expected credentials.""" - + program_credentials_data = self._expected_progam_credentials_data() return [ { - "display_name": "Test Program A", - "credential_url": "http://credentials.edx.org/credentials/dummy-uuid-1/" + 'display_name': self.PROGRAMS_API_RESPONSE['results'][0]['name'], + 'subtitle': self.PROGRAMS_API_RESPONSE['results'][0]['subtitle'], + 'credential_url':program_credentials_data[0]['certificate_url'] }, { - "display_name": "Test Program B", - "credential_url": "http://credentials.edx.org/credentials/dummy-uuid-2/" + 'display_name': self.PROGRAMS_API_RESPONSE['results'][1]['name'], + 'subtitle':self.PROGRAMS_API_RESPONSE['results'][1]['subtitle'], + 'credential_url':program_credentials_data[1]['certificate_url'] } ] diff --git a/lms/djangoapps/learner_dashboard/views.py b/lms/djangoapps/learner_dashboard/views.py index ac75b6715a..9562a5025c 100644 --- a/lms/djangoapps/learner_dashboard/views.py +++ b/lms/djangoapps/learner_dashboard/views.py @@ -7,9 +7,10 @@ from django.views.decorators.http import require_GET 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 student.views import get_course_enrollments, _get_xseries_credentials +from student.views import get_course_enrollments @login_required @@ -39,7 +40,7 @@ def view_programs(request): 'xseries_url': marketing_root if ProgramsApiConfig.current().show_xseries_ad else None, 'nav_hidden': True, 'show_program_listing': show_program_listing, - 'credentials': _get_xseries_credentials(request.user), + 'credentials': get_programs_credentials(request.user, category='xseries'), 'disable_courseware_js': True, 'uses_pattern_library': True } diff --git a/lms/static/sass/multicourse/_dashboard.scss b/lms/static/sass/multicourse/_dashboard.scss index 59c18472fb..16a9a009e7 100644 --- a/lms/static/sass/multicourse/_dashboard.scss +++ b/lms/static/sass/multicourse/_dashboard.scss @@ -19,9 +19,6 @@ @include float(right); @include margin-left(flex-gutter()); width: flex-grid(3); - margin-top: ($baseline*2); - border-top: 3px solid $blue; - padding: $baseline 0; .course-advertise { @include clearfix(); @@ -36,7 +33,7 @@ } .ad-link { @include text-align(center); - .btn-find-courses { + .btn-neutral { padding-bottom: 12px; padding-top: 12px; } @@ -57,6 +54,9 @@ span { @include margin-left($baseline*0.25); } + .icon { + @include margin-right($baseline*0.25); + } } } } @@ -94,8 +94,6 @@ @include margin-left(flex-gutter()); width: flex-grid(3); margin-top: ($baseline*2); - border-top: 3px solid $blue; - padding: $baseline 0; .user-info { @include clearfix(); diff --git a/lms/templates/dashboard.html b/lms/templates/dashboard.html index ecb8447cca..7dd464724a 100644 --- a/lms/templates/dashboard.html +++ b/lms/templates/dashboard.html @@ -175,19 +175,6 @@ from openedx.core.djangolib.markup import Text, HTML - % if xseries_credentials: -
-

${_("XSeries Program Certificates")}

-

${_("You have received a certificate for the following XSeries programs:")}

- -
- % endif diff --git a/openedx/core/djangoapps/credentials/tests/factories.py b/openedx/core/djangoapps/credentials/tests/factories.py new file mode 100644 index 0000000000..b36ad3d6ae --- /dev/null +++ b/openedx/core/djangoapps/credentials/tests/factories.py @@ -0,0 +1,41 @@ +"""Factories for generating fake credentials-related data.""" +import factory +from factory.fuzzy import FuzzyText + + +class UserCredential(factory.Factory): + """Factory for stubbing user credentials resources from the User Credentials + API (v1). + """ + class Meta(object): + model = dict + + id = factory.Sequence(lambda n: n) # pylint: disable=invalid-name + username = FuzzyText(prefix='user_') + status = 'awarded' + uuid = FuzzyText(prefix='uuid_') + certificate_url = 'http=//credentials.edx.org/credentials/dummy-uuid' + credential = {} + + +class ProgramCredential(factory.Factory): + """Factory for stubbing program credentials resources from the Program + Credentials API (v1). + """ + class Meta(object): + model = dict + + credential_id = factory.Sequence(lambda n: n) + program_id = factory.Sequence(lambda n: n) + + +class CourseCredential(factory.Factory): + """Factory for stubbing course credentials resources from the Course + Credentials API (v1). + """ + class Meta(object): + model = dict + + course_id = 'edx/test01/2015' + credential_id = factory.Sequence(lambda n: n) + certificate_type = 'verified' diff --git a/openedx/core/djangoapps/credentials/tests/mixins.py b/openedx/core/djangoapps/credentials/tests/mixins.py index ae7b73254a..7561ec7f15 100644 --- a/openedx/core/djangoapps/credentials/tests/mixins.py +++ b/openedx/core/djangoapps/credentials/tests/mixins.py @@ -4,6 +4,7 @@ import json import httpretty from openedx.core.djangoapps.credentials.models import CredentialsApiConfig +from openedx.core.djangoapps.credentials.tests import factories class CredentialsApiConfigMixin(object): @@ -33,103 +34,63 @@ class CredentialsDataMixin(object): CREDENTIALS_API_RESPONSE = { "next": None, "results": [ - { - "id": 1, - "username": "test", - "credential": { - "credential_id": 1, - "program_id": 1 - }, - "status": "awarded", - "uuid": "dummy-uuid-1", - "certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-1/" - }, - { - "id": 2, - "username": "test", - "credential": { - "credential_id": 2, - "program_id": 2 - }, - "status": "awarded", - "uuid": "dummy-uuid-2", - "certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-2/" - }, - { - "id": 3, - "username": "test", - "credential": { - "credential_id": 3, - "program_id": 3 - }, - "status": "revoked", - "uuid": "dummy-uuid-3", - "certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-3/" - }, - { - "id": 4, - "username": "test", - "credential": { - "course_id": "edx/test01/2015", - "credential_id": 4, - "certificate_type": "honor" - }, - "status": "awarded", - "uuid": "dummy-uuid-4", - "certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-4/" - }, - { - "id": 5, - "username": "test", - "credential": { - "course_id": "edx/test02/2015", - "credential_id": 5, - "certificate_type": "verified" - }, - "status": "awarded", - "uuid": "dummy-uuid-5", - "certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-5/" - }, - { - "id": 6, - "username": "test", - "credential": { - "course_id": "edx/test03/2015", - "credential_id": 6, - "certificate_type": "honor" - }, - "status": "revoked", - "uuid": "dummy-uuid-6", - "certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-6/" - } + factories.UserCredential( + id=1, + username='test', + credential=factories.ProgramCredential( + program_id=1 + ) + ), + factories.UserCredential( + id=2, + username='test', + credential=factories.ProgramCredential( + program_id=2 + ) + ), + factories.UserCredential( + id=3, + status='revoked', + username='test', + credential=factories.ProgramCredential() + ), + factories.UserCredential( + id=4, + username='test', + credential=factories.CourseCredential( + certificate_type='honor' + ) + ), + factories.UserCredential( + id=5, + username='test', + credential=factories.CourseCredential( + course_id='edx/test02/2015' + ) + ), + factories.UserCredential( + id=6, + username='test', + credential=factories.CourseCredential( + course_id='edx/test02/2015' + ) + ), ] } CREDENTIALS_NEXT_API_RESPONSE = { "next": None, "results": [ - { - "id": 7, - "username": "test", - "credential": { - "credential_id": 7, - "program_id": 7 - }, - "status": "awarded", - "uuid": "dummy-uuid-7", - "certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-7" - }, - { - "id": 8, - "username": "test", - "credential": { - "credential_id": 8, - "program_id": 8 - }, - "status": "awarded", - "uuid": "dummy-uuid-8", - "certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-8/" - } + factories.UserCredential( + id=7, + username='test', + credential=factories.ProgramCredential() + ), + factories.UserCredential( + id=8, + username='test', + credential=factories.ProgramCredential() + ) ] } diff --git a/openedx/core/djangoapps/credentials/tests/test_utils.py b/openedx/core/djangoapps/credentials/tests/test_utils.py index 0d68827e9c..96a1e319cc 100644 --- a/openedx/core/djangoapps/credentials/tests/test_utils.py +++ b/openedx/core/djangoapps/credentials/tests/test_utils.py @@ -3,17 +3,19 @@ import unittest from django.conf import settings from django.core.cache import cache -from django.test import TestCase from nose.plugins.attrib import attr import httpretty from edx_oauth2_provider.tests.factories import ClientFactory from provider.constants import CONFIDENTIAL -from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin, CredentialsDataMixin from openedx.core.djangoapps.credentials.models import CredentialsApiConfig +from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin, CredentialsDataMixin from openedx.core.djangoapps.credentials.utils import ( - get_user_credentials, get_user_program_credentials + get_user_credentials, + get_user_program_credentials, + get_programs_credentials ) +from openedx.core.djangoapps.credentials.tests import factories from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin, ProgramsDataMixin from openedx.core.djangoapps.programs.models import ProgramsApiConfig from openedx.core.djangolib.testing.utils import CacheIsolationTestCase @@ -39,6 +41,39 @@ class TestCredentialsRetrieval(ProgramsApiConfigMixin, CredentialsApiConfigMixin cache.clear() + def _expected_progam_credentials_data(self): + """ + Dry method for getting expected program credentials response data. + """ + return [ + factories.UserCredential( + id=1, + username='test', + credential=factories.ProgramCredential() + ), + factories.UserCredential( + id=2, + username='test', + credential=factories.ProgramCredential() + ) + ] + + def expected_credentials_display_data(self): + """ Returns expected credentials data to be represented. """ + program_credentials_data = self._expected_progam_credentials_data() + return [ + { + 'display_name': self.PROGRAMS_API_RESPONSE['results'][0]['name'], + 'subtitle': self.PROGRAMS_API_RESPONSE['results'][0]['subtitle'], + 'credential_url':program_credentials_data[0]['certificate_url'] + }, + { + 'display_name': self.PROGRAMS_API_RESPONSE['results'][1]['name'], + 'subtitle':self.PROGRAMS_API_RESPONSE['results'][1]['subtitle'], + 'credential_url':program_credentials_data[1]['certificate_url'] + } + ] + @httpretty.activate def test_get_user_credentials(self): """Verify user credentials data can be retrieve.""" @@ -98,9 +133,10 @@ class TestCredentialsRetrieval(ProgramsApiConfigMixin, CredentialsApiConfigMixin self.mock_credentials_api(self.user, reset_url=False) actual = get_user_program_credentials(self.user) + program_credentials_data = self._expected_progam_credentials_data() expected = self.PROGRAMS_API_RESPONSE['results'][:2] - expected[0]['credential_url'] = self.PROGRAMS_CREDENTIALS_DATA[0]['certificate_url'] - expected[1]['credential_url'] = self.PROGRAMS_CREDENTIALS_DATA[1]['certificate_url'] + expected[0]['credential_url'] = program_credentials_data[0]['certificate_url'] + expected[1]['credential_url'] = program_credentials_data[1]['certificate_url'] # checking response from API is as expected self.assertEqual(len(actual), 2) @@ -125,3 +161,57 @@ class TestCredentialsRetrieval(ProgramsApiConfigMixin, CredentialsApiConfigMixin self.mock_credentials_api(self.user, data=credential_data) actual = get_user_program_credentials(self.user) self.assertEqual(actual, []) + + @httpretty.activate + def test_get_programs_credentials(self): + """ Verify that the program credentials data required for display can + be retrieved. + """ + # create credentials and program configuration + self.create_credentials_config() + self.create_programs_config() + + # Mocking the API responses from programs and credentials + self.mock_programs_api() + self.mock_credentials_api(self.user, reset_url=False) + actual = get_programs_credentials(self.user, category='xseries') + expected = self.expected_credentials_display_data() + + # Checking result is as expected + self.assertEqual(len(actual), 2) + self.assertEqual(actual, expected) + + @httpretty.activate + def test_get_programs_credentials_category(self): + """ Verify behaviour when program category is provided.""" + # create credentials and program configuration + self.create_credentials_config() + self.create_programs_config() + + # Mocking the API responses from programs and credentials + self.mock_programs_api() + self.mock_credentials_api(self.user, reset_url=False) + actual = get_programs_credentials(self.user, category='dummy_category') + expected = self.expected_credentials_display_data() + + self.assertEqual(len(actual), 0) + + actual = get_programs_credentials(self.user, category='xseries') + + self.assertEqual(len(actual), 2) + self.assertEqual(actual, expected) + + @httpretty.activate + def test_get_programs_credentials_no_category(self): + """ Verify behaviour when no program category is provided. """ + self.create_credentials_config() + self.create_programs_config() + + # Mocking the API responses from programs and credentials + self.mock_programs_api() + self.mock_credentials_api(self.user, reset_url=False) + actual = get_programs_credentials(self.user) + expected = self.expected_credentials_display_data() + + self.assertEqual(len(actual), 2) + self.assertEqual(actual, expected) diff --git a/openedx/core/djangoapps/credentials/utils.py b/openedx/core/djangoapps/credentials/utils.py index a61c17ae96..7f9cdaf361 100644 --- a/openedx/core/djangoapps/credentials/utils.py +++ b/openedx/core/djangoapps/credentials/utils.py @@ -64,3 +64,35 @@ def get_user_program_credentials(user): programs_credentials_data = get_programs_for_credentials(user, programs_credentials) return programs_credentials_data + + +def get_programs_credentials(user, category=None): + """Return program credentials data required for display. + + Given a user, find all programs for which certificates have been earned + and return list of dictionaries of required program data. + + Arguments: + user (User): user object for getting programs credentials. + category(str) : program category for getting credentials. + + Returns: + list of dict, containing data corresponding to the programs for which + the user has been awarded a credential. + """ + programs_credentials = get_user_program_credentials(user) + credentials_data = [] + for program in programs_credentials: + is_included = (category is None) or (program.get('category') == category) + if is_included: + try: + program_data = { + 'display_name': program['name'], + 'subtitle': program['subtitle'], + 'credential_url': program['credential_url'], + } + credentials_data.append(program_data) + except KeyError: + log.warning('Program structure is invalid: %r', program) + + return credentials_data diff --git a/openedx/core/djangoapps/programs/tests/mixins.py b/openedx/core/djangoapps/programs/tests/mixins.py index 4377198153..cb56b5917b 100644 --- a/openedx/core/djangoapps/programs/tests/mixins.py +++ b/openedx/core/djangoapps/programs/tests/mixins.py @@ -100,31 +100,6 @@ class ProgramsDataMixin(object): ] } - PROGRAMS_CREDENTIALS_DATA = [ - { - "id": 1, - "username": "test", - "credential": { - "credential_id": 1, - "program_id": 1 - }, - "status": "awarded", - "uuid": "dummy-uuid-1", - "certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-1/" - }, - { - "id": 2, - "username": "test", - "credential": { - "credential_id": 2, - "program_id": 2 - }, - "status": "awarded", - "uuid": "dummy-uuid-2", - "certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-2/" - } - ] - def mock_programs_api(self, data=None, 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.') diff --git a/openedx/core/djangoapps/programs/tests/test_utils.py b/openedx/core/djangoapps/programs/tests/test_utils.py index 245a9703b2..6edd3769a5 100644 --- a/openedx/core/djangoapps/programs/tests/test_utils.py +++ b/openedx/core/djangoapps/programs/tests/test_utils.py @@ -12,7 +12,8 @@ from edx_oauth2_provider.tests.factories import ClientFactory from provider.constants import CONFIDENTIAL from lms.djangoapps.certificates.api import MODES -from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin +from openedx.core.djangoapps.credentials.tests import factories as credentials_factories +from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin, CredentialsDataMixin from openedx.core.djangoapps.programs import utils from openedx.core.djangoapps.programs.models import ProgramsApiConfig from openedx.core.djangoapps.programs.tests import factories @@ -26,7 +27,7 @@ UTILS_MODULE = 'openedx.core.djangoapps.programs.utils' @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') @attr('shard_2') -class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, +class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, CredentialsDataMixin, CredentialsApiConfigMixin, CacheIsolationTestCase): """Tests covering the retrieval of programs from the Programs service.""" @@ -40,6 +41,27 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, cache.clear() + def _expected_progam_credentials_data(self): + """ + Dry method for getting expected program credentials response data. + """ + return [ + credentials_factories.UserCredential( + id=1, + username='test', + credential=credentials_factories.ProgramCredential( + program_id=1 + ) + ), + credentials_factories.UserCredential( + id=2, + username='test', + credential=credentials_factories.ProgramCredential( + program_id=2 + ) + ) + ] + @httpretty.activate def test_get_programs(self): """Verify programs data can be retrieved.""" @@ -152,11 +174,12 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, """Verify programs data can be retrieved and parsed correctly for certificates.""" self.create_programs_config() self.mock_programs_api() + program_credentials_data = self._expected_progam_credentials_data() - actual = utils.get_programs_for_credentials(self.user, self.PROGRAMS_CREDENTIALS_DATA) + actual = utils.get_programs_for_credentials(self.user, program_credentials_data) expected = self.PROGRAMS_API_RESPONSE['results'][:2] - expected[0]['credential_url'] = self.PROGRAMS_CREDENTIALS_DATA[0]['certificate_url'] - expected[1]['credential_url'] = self.PROGRAMS_CREDENTIALS_DATA[1]['certificate_url'] + expected[0]['credential_url'] = program_credentials_data[0]['certificate_url'] + expected[1]['credential_url'] = program_credentials_data[1]['certificate_url'] self.assertEqual(len(actual), 2) self.assertEqual(actual, expected) @@ -167,8 +190,9 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, self.create_programs_config() self.create_credentials_config() self.mock_programs_api(data={'results': []}) + program_credentials_data = self._expected_progam_credentials_data() - actual = utils.get_programs_for_credentials(self.user, self.PROGRAMS_CREDENTIALS_DATA) + actual = utils.get_programs_for_credentials(self.user, program_credentials_data) self.assertEqual(actual, []) @httpretty.activate diff --git a/themes/edx.org/lms/templates/dashboard.html b/themes/edx.org/lms/templates/dashboard.html index 96272a0849..bb83e7529c 100644 --- a/themes/edx.org/lms/templates/dashboard.html +++ b/themes/edx.org/lms/templates/dashboard.html @@ -163,7 +163,7 @@ from openedx.core.djangolib.markup import Text, HTML ${_("Browse recently launched courses and see what's new in your favorite subjects.")}