Commit fa7ed070 by Ahsan Ulhaq

Merge pull request #12468 from edx/ahsan/ECOM-4398-Course-Dashboard-Visual-Update

Course Dashboard Visual Update
parents f7e98062 a8f2de83
...@@ -122,8 +122,6 @@ from eventtracking import tracker ...@@ -122,8 +122,6 @@ from eventtracking import tracker
# Note that this lives in LMS, so this dependency should be refactored. # Note that this lives in LMS, so this dependency should be refactored.
from notification_prefs.views import enable_notifications 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.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.user_api.preferences import api as preferences_api
from openedx.core.djangoapps.programs.utils import get_programs_for_dashboard, get_display_category from openedx.core.djangoapps.programs.utils import get_programs_for_dashboard, get_display_category
...@@ -615,7 +613,6 @@ def dashboard(request): ...@@ -615,7 +613,6 @@ def dashboard(request):
# This is passed along in the template context to allow rendering of # This is passed along in the template context to allow rendering of
# program-related information on the dashboard. # program-related information on the dashboard.
course_programs = _get_course_programs(user, [enrollment.course_id for enrollment in course_enrollments]) 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 # Construct a dictionary of course mode information
# used to render the course list. We re-use the course modes dict # used to render the course list. We re-use the course modes dict
...@@ -741,7 +738,6 @@ def dashboard(request): ...@@ -741,7 +738,6 @@ def dashboard(request):
'nav_hidden': True, 'nav_hidden': True,
'course_programs': course_programs, 'course_programs': course_programs,
'disable_courseware_js': True, 'disable_courseware_js': True,
'xseries_credentials': xseries_credentials,
'show_program_listing': ProgramsApiConfig.current().show_program_listing, 'show_program_listing': ProgramsApiConfig.current().show_program_listing,
} }
...@@ -2483,34 +2479,3 @@ def _get_course_programs(user, user_enrolled_courses): # pylint: disable=invali ...@@ -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) log.warning('Program structure is invalid, skipping display: %r', program)
return programs_data 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
...@@ -14,6 +14,7 @@ from opaque_keys.edx import locator ...@@ -14,6 +14,7 @@ from opaque_keys.edx import locator
from provider.constants import CONFIDENTIAL from provider.constants import CONFIDENTIAL
from openedx.core.djangoapps.credentials.models import CredentialsApiConfig 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.credentials.tests.mixins import CredentialsDataMixin, CredentialsApiConfigMixin
from openedx.core.djangoapps.programs.tests.mixins import ( from openedx.core.djangoapps.programs.tests.mixins import (
ProgramsApiConfigMixin, ProgramsApiConfigMixin,
...@@ -155,18 +156,36 @@ class TestProgramListing( ...@@ -155,18 +156,36 @@ class TestProgramListing(
'{}?next={}'.format(reverse('signin_user'), self.url) '{}?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): def _expected_credentials_data(self):
""" Dry method for getting expected credentials.""" """ Dry method for getting expected credentials."""
program_credentials_data = self._expected_progam_credentials_data()
return [ return [
{ {
"display_name": "Test Program A", 'display_name': self.PROGRAMS_API_RESPONSE['results'][0]['name'],
"credential_url": "http://credentials.edx.org/credentials/dummy-uuid-1/" 'subtitle': self.PROGRAMS_API_RESPONSE['results'][0]['subtitle'],
'credential_url':program_credentials_data[0]['certificate_url']
}, },
{ {
"display_name": "Test Program B", 'display_name': self.PROGRAMS_API_RESPONSE['results'][1]['name'],
"credential_url": "http://credentials.edx.org/credentials/dummy-uuid-2/" 'subtitle':self.PROGRAMS_API_RESPONSE['results'][1]['subtitle'],
'credential_url':program_credentials_data[1]['certificate_url']
} }
] ]
......
...@@ -7,9 +7,10 @@ from django.views.decorators.http import require_GET ...@@ -7,9 +7,10 @@ from django.views.decorators.http import require_GET
from django.http import Http404 from django.http import Http404
from edxmako.shortcuts import render_to_response 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.models import ProgramsApiConfig
from openedx.core.djangoapps.programs.utils import ProgramProgressMeter, get_display_category 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 @login_required
...@@ -39,7 +40,7 @@ def view_programs(request): ...@@ -39,7 +40,7 @@ def view_programs(request):
'xseries_url': marketing_root if ProgramsApiConfig.current().show_xseries_ad else None, 'xseries_url': marketing_root if ProgramsApiConfig.current().show_xseries_ad else None,
'nav_hidden': True, 'nav_hidden': True,
'show_program_listing': show_program_listing, 'show_program_listing': show_program_listing,
'credentials': _get_xseries_credentials(request.user), 'credentials': get_programs_credentials(request.user, category='xseries'),
'disable_courseware_js': True, 'disable_courseware_js': True,
'uses_pattern_library': True 'uses_pattern_library': True
} }
......
...@@ -19,9 +19,6 @@ ...@@ -19,9 +19,6 @@
@include float(right); @include float(right);
@include margin-left(flex-gutter()); @include margin-left(flex-gutter());
width: flex-grid(3); width: flex-grid(3);
margin-top: ($baseline*2);
border-top: 3px solid $blue;
padding: $baseline 0;
.course-advertise { .course-advertise {
@include clearfix(); @include clearfix();
...@@ -36,7 +33,7 @@ ...@@ -36,7 +33,7 @@
} }
.ad-link { .ad-link {
@include text-align(center); @include text-align(center);
.btn-find-courses { .btn-neutral {
padding-bottom: 12px; padding-bottom: 12px;
padding-top: 12px; padding-top: 12px;
} }
...@@ -57,6 +54,9 @@ ...@@ -57,6 +54,9 @@
span { span {
@include margin-left($baseline*0.25); @include margin-left($baseline*0.25);
} }
.icon {
@include margin-right($baseline*0.25);
}
} }
} }
} }
...@@ -94,8 +94,6 @@ ...@@ -94,8 +94,6 @@
@include margin-left(flex-gutter()); @include margin-left(flex-gutter());
width: flex-grid(3); width: flex-grid(3);
margin-top: ($baseline*2); margin-top: ($baseline*2);
border-top: 3px solid $blue;
padding: $baseline 0;
.user-info { .user-info {
@include clearfix(); @include clearfix();
......
...@@ -175,19 +175,6 @@ from openedx.core.djangolib.markup import Text, HTML ...@@ -175,19 +175,6 @@ from openedx.core.djangolib.markup import Text, HTML
</ul> </ul>
</section> </section>
</section> </section>
% if xseries_credentials:
<div class="wrapper-xseries-certificates">
<p class="title">${_("XSeries Program Certificates")}</p>
<p class="copy">${_("You have received a certificate for the following XSeries programs:")}</p>
<ul>
% for xseries_credential in xseries_credentials:
<li>
<a class="copy" href="${xseries_credential['credential_url']}">${xseries_credential['display_name']}</a>
</li>
% endfor
</ul>
</div>
% endif
</section> </section>
</main> </main>
......
"""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'
...@@ -4,6 +4,7 @@ import json ...@@ -4,6 +4,7 @@ import json
import httpretty import httpretty
from openedx.core.djangoapps.credentials.models import CredentialsApiConfig from openedx.core.djangoapps.credentials.models import CredentialsApiConfig
from openedx.core.djangoapps.credentials.tests import factories
class CredentialsApiConfigMixin(object): class CredentialsApiConfigMixin(object):
...@@ -33,103 +34,63 @@ class CredentialsDataMixin(object): ...@@ -33,103 +34,63 @@ class CredentialsDataMixin(object):
CREDENTIALS_API_RESPONSE = { CREDENTIALS_API_RESPONSE = {
"next": None, "next": None,
"results": [ "results": [
{ factories.UserCredential(
"id": 1, id=1,
"username": "test", username='test',
"credential": { credential=factories.ProgramCredential(
"credential_id": 1, program_id=1
"program_id": 1 )
}, ),
"status": "awarded", factories.UserCredential(
"uuid": "dummy-uuid-1", id=2,
"certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-1/" username='test',
}, credential=factories.ProgramCredential(
{ program_id=2
"id": 2, )
"username": "test", ),
"credential": { factories.UserCredential(
"credential_id": 2, id=3,
"program_id": 2 status='revoked',
}, username='test',
"status": "awarded", credential=factories.ProgramCredential()
"uuid": "dummy-uuid-2", ),
"certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-2/" factories.UserCredential(
}, id=4,
{ username='test',
"id": 3, credential=factories.CourseCredential(
"username": "test", certificate_type='honor'
"credential": { )
"credential_id": 3, ),
"program_id": 3 factories.UserCredential(
}, id=5,
"status": "revoked", username='test',
"uuid": "dummy-uuid-3", credential=factories.CourseCredential(
"certificate_url": "http://credentials.edx.org/credentials/dummy-uuid-3/" course_id='edx/test02/2015'
}, )
{ ),
"id": 4, factories.UserCredential(
"username": "test", id=6,
"credential": { username='test',
"course_id": "edx/test01/2015", credential=factories.CourseCredential(
"credential_id": 4, course_id='edx/test02/2015'
"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/"
}
] ]
} }
CREDENTIALS_NEXT_API_RESPONSE = { CREDENTIALS_NEXT_API_RESPONSE = {
"next": None, "next": None,
"results": [ "results": [
{ factories.UserCredential(
"id": 7, id=7,
"username": "test", username='test',
"credential": { credential=factories.ProgramCredential()
"credential_id": 7, ),
"program_id": 7 factories.UserCredential(
}, id=8,
"status": "awarded", username='test',
"uuid": "dummy-uuid-7", credential=factories.ProgramCredential()
"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/"
}
] ]
} }
......
...@@ -3,17 +3,19 @@ import unittest ...@@ -3,17 +3,19 @@ import unittest
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.test import TestCase
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
import httpretty import httpretty
from edx_oauth2_provider.tests.factories import ClientFactory from edx_oauth2_provider.tests.factories import ClientFactory
from provider.constants import CONFIDENTIAL 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.models import CredentialsApiConfig
from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin, CredentialsDataMixin
from openedx.core.djangoapps.credentials.utils import ( 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.tests.mixins import ProgramsApiConfigMixin, ProgramsDataMixin
from openedx.core.djangoapps.programs.models import ProgramsApiConfig from openedx.core.djangoapps.programs.models import ProgramsApiConfig
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
...@@ -39,6 +41,39 @@ class TestCredentialsRetrieval(ProgramsApiConfigMixin, CredentialsApiConfigMixin ...@@ -39,6 +41,39 @@ class TestCredentialsRetrieval(ProgramsApiConfigMixin, CredentialsApiConfigMixin
cache.clear() 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 @httpretty.activate
def test_get_user_credentials(self): def test_get_user_credentials(self):
"""Verify user credentials data can be retrieve.""" """Verify user credentials data can be retrieve."""
...@@ -98,9 +133,10 @@ class TestCredentialsRetrieval(ProgramsApiConfigMixin, CredentialsApiConfigMixin ...@@ -98,9 +133,10 @@ class TestCredentialsRetrieval(ProgramsApiConfigMixin, CredentialsApiConfigMixin
self.mock_credentials_api(self.user, reset_url=False) self.mock_credentials_api(self.user, reset_url=False)
actual = get_user_program_credentials(self.user) actual = get_user_program_credentials(self.user)
program_credentials_data = self._expected_progam_credentials_data()
expected = self.PROGRAMS_API_RESPONSE['results'][:2] expected = self.PROGRAMS_API_RESPONSE['results'][:2]
expected[0]['credential_url'] = self.PROGRAMS_CREDENTIALS_DATA[0]['certificate_url'] expected[0]['credential_url'] = program_credentials_data[0]['certificate_url']
expected[1]['credential_url'] = self.PROGRAMS_CREDENTIALS_DATA[1]['certificate_url'] expected[1]['credential_url'] = program_credentials_data[1]['certificate_url']
# checking response from API is as expected # checking response from API is as expected
self.assertEqual(len(actual), 2) self.assertEqual(len(actual), 2)
...@@ -125,3 +161,57 @@ class TestCredentialsRetrieval(ProgramsApiConfigMixin, CredentialsApiConfigMixin ...@@ -125,3 +161,57 @@ class TestCredentialsRetrieval(ProgramsApiConfigMixin, CredentialsApiConfigMixin
self.mock_credentials_api(self.user, data=credential_data) self.mock_credentials_api(self.user, data=credential_data)
actual = get_user_program_credentials(self.user) actual = get_user_program_credentials(self.user)
self.assertEqual(actual, []) 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)
...@@ -64,3 +64,35 @@ def get_user_program_credentials(user): ...@@ -64,3 +64,35 @@ def get_user_program_credentials(user):
programs_credentials_data = get_programs_for_credentials(user, programs_credentials) programs_credentials_data = get_programs_for_credentials(user, programs_credentials)
return programs_credentials_data 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
...@@ -100,31 +100,6 @@ class ProgramsDataMixin(object): ...@@ -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): def mock_programs_api(self, data=None, status_code=200):
"""Utility for mocking out Programs API URLs.""" """Utility for mocking out Programs API URLs."""
self.assertTrue(httpretty.is_enabled(), msg='httpretty must be enabled to mock Programs API calls.') self.assertTrue(httpretty.is_enabled(), msg='httpretty must be enabled to mock Programs API calls.')
......
...@@ -12,7 +12,8 @@ from edx_oauth2_provider.tests.factories import ClientFactory ...@@ -12,7 +12,8 @@ from edx_oauth2_provider.tests.factories import ClientFactory
from provider.constants import CONFIDENTIAL from provider.constants import CONFIDENTIAL
from lms.djangoapps.certificates.api import MODES 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 import utils
from openedx.core.djangoapps.programs.models import ProgramsApiConfig from openedx.core.djangoapps.programs.models import ProgramsApiConfig
from openedx.core.djangoapps.programs.tests import factories from openedx.core.djangoapps.programs.tests import factories
...@@ -26,7 +27,7 @@ UTILS_MODULE = 'openedx.core.djangoapps.programs.utils' ...@@ -26,7 +27,7 @@ UTILS_MODULE = 'openedx.core.djangoapps.programs.utils'
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@attr('shard_2') @attr('shard_2')
class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, CredentialsDataMixin,
CredentialsApiConfigMixin, CacheIsolationTestCase): CredentialsApiConfigMixin, CacheIsolationTestCase):
"""Tests covering the retrieval of programs from the Programs service.""" """Tests covering the retrieval of programs from the Programs service."""
...@@ -40,6 +41,27 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, ...@@ -40,6 +41,27 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin,
cache.clear() 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 @httpretty.activate
def test_get_programs(self): def test_get_programs(self):
"""Verify programs data can be retrieved.""" """Verify programs data can be retrieved."""
...@@ -152,11 +174,12 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, ...@@ -152,11 +174,12 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin,
"""Verify programs data can be retrieved and parsed correctly for certificates.""" """Verify programs data can be retrieved and parsed correctly for certificates."""
self.create_programs_config() self.create_programs_config()
self.mock_programs_api() 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 = self.PROGRAMS_API_RESPONSE['results'][:2]
expected[0]['credential_url'] = self.PROGRAMS_CREDENTIALS_DATA[0]['certificate_url'] expected[0]['credential_url'] = program_credentials_data[0]['certificate_url']
expected[1]['credential_url'] = self.PROGRAMS_CREDENTIALS_DATA[1]['certificate_url'] expected[1]['credential_url'] = program_credentials_data[1]['certificate_url']
self.assertEqual(len(actual), 2) self.assertEqual(len(actual), 2)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
...@@ -167,8 +190,9 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, ...@@ -167,8 +190,9 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin,
self.create_programs_config() self.create_programs_config()
self.create_credentials_config() self.create_credentials_config()
self.mock_programs_api(data={'results': []}) 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, []) self.assertEqual(actual, [])
@httpretty.activate @httpretty.activate
......
...@@ -163,7 +163,7 @@ from openedx.core.djangolib.markup import Text, 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.")} ${_("Browse recently launched courses and see what's new in your favorite subjects.")}
</div> </div>
<div class="ad-link"> <div class="ad-link">
<a class="btn-find-courses" href="${marketing_link('COURSES')}"> <a class="btn-neutral" href="${marketing_link('COURSES')}">
<span class="icon fa fa-search" aria-hidden="true"></span> <span class="icon fa fa-search" aria-hidden="true"></span>
${_("Explore New Courses")} ${_("Explore New Courses")}
</a> </a>
...@@ -193,19 +193,6 @@ from openedx.core.djangolib.markup import Text, HTML ...@@ -193,19 +193,6 @@ from openedx.core.djangolib.markup import Text, HTML
</ul> </ul>
</section> </section>
</section> </section>
% if xseries_credentials:
<div class="wrapper-xseries-certificates">
<p class="title">${_("XSeries Program Certificates")}</p>
<p class="copy">${_("You have received a certificate for the following XSeries programs:")}</p>
<ul>
% for xseries_credential in xseries_credentials:
<li>
<a class="copy" href="${xseries_credential['credential_url']}">${xseries_credential['display_name']}</a>
</li>
% endfor
</ul>
</div>
% endif
</section> </section>
<section id="email-settings-modal" class="modal" aria-hidden="true"> <section id="email-settings-modal" class="modal" aria-hidden="true">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment