* feat: get suggested courses * style: run black * docs: update suggested courses mock * docs: remove cardID from mock * docs: fix mock course.title to course.courseName * docs: fix mock course.bannerUrl to bannerImgSrc * docs: fix mock provider to courseProvider * docs: remove old mock courseProvider fields * docs: fix mock "grades" to "gradeData" * docs: fix mock remove courseRun.lastEnrolled * docs: fix mock add enrollment.lastEnrolled * docs: mock remove enrollment.isStarted * docs: mock fix bad nesting in courseRun * docs: mock certificates to certificate * docs: mock remove certificate.isAvailable * docs: mock remove entitlement.isEntitlement * docs: mock add entitlement.expirationDate * docs: mock fix some entitlement inconsistencies * docs: mock remove entitlement.canViewCourse * docs: mock nest relatedPrograms under programs * fix: return null for missing resumeUrl * test: return null for missing resumeUrl * refactor: update suggested course fields * refactor: update related programs fields * docs: remove entitlement.expirationDate from mock Co-authored-by: nsprenkle <nsprenkle@2u.com>
1278 lines
45 KiB
Python
1278 lines
45 KiB
Python
"""Tests for serializers for the Learner Dashboard"""
|
|
|
|
from datetime import date, datetime, timedelta
|
|
from itertools import product
|
|
from random import randint
|
|
from unittest import mock
|
|
from uuid import uuid4
|
|
|
|
from django.conf import settings
|
|
from django.urls import reverse
|
|
from django.test import TestCase
|
|
import ddt
|
|
from opaque_keys.edx.keys import CourseKey
|
|
|
|
|
|
from common.djangoapps.course_modes.models import CourseMode
|
|
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
|
|
from common.djangoapps.entitlements.tests.factories import CourseEntitlementFactory
|
|
from common.djangoapps.student.tests.factories import (
|
|
CourseEnrollmentFactory,
|
|
UserFactory,
|
|
)
|
|
from openedx.core.djangoapps.catalog.tests.factories import (
|
|
CourseRunFactory as CatalogCourseRunFactory,
|
|
ProgramFactory,
|
|
)
|
|
from openedx.core.djangoapps.content.course_overviews.tests.factories import (
|
|
CourseOverviewFactory,
|
|
)
|
|
from lms.djangoapps.learner_home.serializers import (
|
|
CertificateSerializer,
|
|
CourseProviderSerializer,
|
|
CourseRunSerializer,
|
|
CourseSerializer,
|
|
EmailConfirmationSerializer,
|
|
EnrollmentSerializer,
|
|
EnterpriseDashboardSerializer,
|
|
EntitlementSerializer,
|
|
GradeDataSerializer,
|
|
CoursewareAccessSerializer,
|
|
LearnerEnrollmentSerializer,
|
|
PlatformSettingsSerializer,
|
|
ProgramsSerializer,
|
|
LearnerDashboardSerializer,
|
|
RelatedProgramSerializer,
|
|
SuggestedCourseSerializer,
|
|
UnfulfilledEntitlementSerializer,
|
|
)
|
|
|
|
from lms.djangoapps.learner_home.test_utils import (
|
|
datetime_to_django_format,
|
|
random_bool,
|
|
random_date,
|
|
random_url,
|
|
)
|
|
from xmodule.data import CertificatesDisplayBehaviors
|
|
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
|
from xmodule.modulestore.tests.factories import CourseFactory
|
|
|
|
|
|
class LearnerDashboardBaseTest(SharedModuleStoreTestCase):
|
|
"""Base class for common setup"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.user = UserFactory()
|
|
|
|
def create_test_enrollment(self, course_mode=CourseMode.AUDIT):
|
|
"""Create a test user, course, and enrollment. Return the enrollment."""
|
|
course = CourseFactory(self_paced=True)
|
|
CourseModeFactory(
|
|
course_id=course.id,
|
|
mode_slug=course_mode,
|
|
)
|
|
|
|
test_enrollment = CourseEnrollmentFactory(course_id=course.id, mode=course_mode)
|
|
|
|
# Add extra info to exercise serialization
|
|
test_enrollment.course_overview.marketing_url = random_url()
|
|
test_enrollment.course_overview.end = random_date()
|
|
test_enrollment.course_overview.certificate_available_date = random_date()
|
|
|
|
return test_enrollment
|
|
|
|
def create_test_entitlement_and_sessions(self):
|
|
"""
|
|
Create a test entitlement
|
|
|
|
Returns: (unfulfilled_entitlement, pseudo_sessions, available_sessions)
|
|
"""
|
|
unfulfilled_entitlement = CourseEntitlementFactory.create()
|
|
|
|
# Create pseudo-sessions
|
|
pseudo_sessions = {
|
|
str(unfulfilled_entitlement.uuid): CatalogCourseRunFactory.create()
|
|
}
|
|
|
|
# Create available sessions
|
|
available_sessions = {
|
|
str(unfulfilled_entitlement.uuid): CatalogCourseRunFactory.create_batch(3)
|
|
}
|
|
|
|
# Create related course overviews
|
|
course_key_str = pseudo_sessions[str(unfulfilled_entitlement.uuid)]["key"]
|
|
course_key = CourseKey.from_string(course_key_str)
|
|
course_overview = CourseOverviewFactory.create(id=course_key)
|
|
|
|
return unfulfilled_entitlement, pseudo_sessions, available_sessions
|
|
|
|
def _assert_all_keys_equal(self, dicts):
|
|
element_0 = dicts[0]
|
|
for element in dicts[1:]:
|
|
assert element_0.keys() == element.keys()
|
|
|
|
|
|
class TestPlatformSettingsSerializer(TestCase):
|
|
"""Tests for the PlatformSettingsSerializer"""
|
|
|
|
@classmethod
|
|
def generate_test_platform_settings(cls):
|
|
"""Util to generate test platform settings data"""
|
|
return {
|
|
"feedbackEmail": f"{uuid4()}@example.com",
|
|
"supportEmail": f"{uuid4()}@example.com",
|
|
"billingEmail": f"{uuid4()}@example.com",
|
|
"courseSearchUrl": f"{uuid4()}.example.com/search",
|
|
}
|
|
|
|
def test_happy_path(self):
|
|
input_data = self.generate_test_platform_settings()
|
|
output_data = PlatformSettingsSerializer(input_data).data
|
|
|
|
assert output_data == {
|
|
"supportEmail": input_data["supportEmail"],
|
|
"billingEmail": input_data["billingEmail"],
|
|
"courseSearchUrl": input_data["courseSearchUrl"],
|
|
}
|
|
|
|
|
|
class TestCourseProviderSerializer(LearnerDashboardBaseTest):
|
|
"""Tests for the CourseProviderSerializer"""
|
|
|
|
@classmethod
|
|
def generate_test_provider_info(cls):
|
|
"""Util to generate test provider info"""
|
|
return {
|
|
"name": f"{uuid4()}",
|
|
}
|
|
|
|
def test_happy_path(self):
|
|
test_enrollment = self.create_test_enrollment()
|
|
|
|
input_data = test_enrollment.course_overview
|
|
output_data = CourseProviderSerializer(input_data).data
|
|
|
|
self.assertEqual(output_data["name"], test_enrollment.course_overview.org)
|
|
|
|
|
|
class TestCourseSerializer(LearnerDashboardBaseTest):
|
|
"""Tests for the CourseSerializer"""
|
|
|
|
def test_happy_path(self):
|
|
test_enrollment = self.create_test_enrollment()
|
|
|
|
input_data = test_enrollment.course_overview
|
|
output_data = CourseSerializer(input_data).data
|
|
|
|
assert output_data == {
|
|
"bannerImgSrc": test_enrollment.course_overview.banner_image_url,
|
|
"courseName": test_enrollment.course_overview.display_name_with_default,
|
|
"courseNumber": test_enrollment.course_overview.display_number_with_default,
|
|
}
|
|
|
|
|
|
class TestCourseRunSerializer(LearnerDashboardBaseTest):
|
|
"""Tests for the CourseRunSerializer"""
|
|
|
|
def create_test_context(self, course_id):
|
|
return {
|
|
"resume_course_urls": {course_id: random_url()},
|
|
"ecommerce_payment_page": random_url(),
|
|
"course_mode_info": {
|
|
course_id: {
|
|
"verified_sku": str(uuid4()),
|
|
"days_for_upsell": randint(0, 14),
|
|
}
|
|
},
|
|
}
|
|
|
|
def test_with_data(self):
|
|
input_data = self.create_test_enrollment()
|
|
input_context = self.create_test_context(input_data.course.id)
|
|
|
|
output_data = CourseRunSerializer(input_data, context=input_context).data
|
|
|
|
# Serialization set up so all fields will have values to make testing easy
|
|
for key in output_data:
|
|
assert output_data[key] is not None
|
|
|
|
def test_missing_resume_url(self):
|
|
# Given a course run
|
|
input_data = self.create_test_enrollment()
|
|
input_context = self.create_test_context(input_data.course.id)
|
|
|
|
# ... where a user hasn't started
|
|
input_context["resume_course_urls"][input_data.course.id] = ""
|
|
|
|
# When I serialize
|
|
output_data = CourseRunSerializer(input_data, context=input_context).data
|
|
|
|
# Then the resumeUrl is None
|
|
self.assertIsNone(output_data["resumeUrl"])
|
|
|
|
|
|
@ddt.ddt
|
|
class TestCoursewareAccessSerializer(LearnerDashboardBaseTest):
|
|
"""Tests for the CoursewareAccessSerializer"""
|
|
|
|
def create_test_context(self, course):
|
|
return {
|
|
"course_access_checks": {
|
|
course.id: {
|
|
"has_unmet_prerequisites": False,
|
|
"is_too_early_to_view": False,
|
|
"user_has_staff_access": False,
|
|
}
|
|
}
|
|
}
|
|
|
|
@ddt.data(True, False)
|
|
def test_unmet_prerequisites(self, has_unmet_prerequisites):
|
|
# Given an enrollment
|
|
input_data = self.create_test_enrollment()
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# ... without unmet prerequisites
|
|
if has_unmet_prerequisites:
|
|
# ... or with unmet prerequisites
|
|
prerequisite_course = CourseFactory()
|
|
input_context.update(
|
|
{
|
|
"course_access_checks": {
|
|
input_data.course.id: {
|
|
"has_unmet_prerequisites": has_unmet_prerequisites,
|
|
}
|
|
}
|
|
}
|
|
)
|
|
|
|
# When I serialize
|
|
output_data = CoursewareAccessSerializer(input_data, context=input_context).data
|
|
|
|
# Then "hasUnmetPrerequisites" is outputs correctly
|
|
self.assertEqual(output_data["hasUnmetPrerequisites"], has_unmet_prerequisites)
|
|
|
|
@ddt.data(True, False)
|
|
def test_is_staff(self, is_staff):
|
|
# Given an enrollment
|
|
input_data = self.create_test_enrollment()
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# Where user has/hasn't staff access
|
|
input_context.update(
|
|
{
|
|
"course_access_checks": {
|
|
input_data.course.id: {
|
|
"user_has_staff_access": is_staff,
|
|
}
|
|
}
|
|
}
|
|
)
|
|
|
|
# When I serialize
|
|
output_data = CoursewareAccessSerializer(input_data, context=input_context).data
|
|
|
|
# Then "isStaff" serializes properly
|
|
self.assertEqual(output_data["isStaff"], is_staff)
|
|
|
|
@ddt.data(True, False)
|
|
def test_is_too_early(self, is_too_early):
|
|
# Given an enrollment
|
|
input_data = self.create_test_enrollment()
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# Where the course is/n't yet open for a learner
|
|
input_context.update(
|
|
{
|
|
"course_access_checks": {
|
|
input_data.course.id: {
|
|
"is_too_early_to_view": is_too_early,
|
|
}
|
|
}
|
|
}
|
|
)
|
|
|
|
# When I serialize
|
|
output_data = CoursewareAccessSerializer(input_data, context=input_context).data
|
|
|
|
# Then "isTooEarly" serializes properly
|
|
self.assertEqual(output_data["isTooEarly"], is_too_early)
|
|
|
|
|
|
@ddt.ddt
|
|
class TestEnrollmentSerializer(LearnerDashboardBaseTest):
|
|
"""Tests for the EnrollmentSerializer"""
|
|
|
|
def create_test_context(self, course):
|
|
"""Get a test context object"""
|
|
return {
|
|
"course_mode_info": {
|
|
course.id: {
|
|
"expiration_datetime": random_date(),
|
|
"show_upsell": True,
|
|
}
|
|
},
|
|
"course_optouts": [],
|
|
"show_email_settings_for": [course.id],
|
|
"show_courseware_link": {course.id: {"has_access": True}},
|
|
"resume_course_urls": {course.id: "some_url"},
|
|
"use_ecommerce_payment_flow": True,
|
|
}
|
|
|
|
def serialize_test_enrollment(self):
|
|
"""
|
|
Create a test enrollment, pass it to EnrollmentSerializer, and return the serialized data
|
|
"""
|
|
input_data = self.create_test_enrollment()
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
serializer = EnrollmentSerializer(input_data, context=input_context)
|
|
return serializer.data
|
|
|
|
def test_with_data(self):
|
|
output = self.serialize_test_enrollment()
|
|
# Serializaiton set up so all fields will have values to make testing easy
|
|
for key in output:
|
|
assert output[key] is not None
|
|
|
|
def test_audit_access_expired(self):
|
|
input_data = self.create_test_enrollment()
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# Example audit expired context
|
|
input_context.update(
|
|
{
|
|
"show_courseware_link": {
|
|
input_data.course.id: {"error_code": "audit_expired"}
|
|
},
|
|
}
|
|
)
|
|
|
|
serializer = EnrollmentSerializer(input_data, context=input_context)
|
|
output = serializer.data
|
|
|
|
assert output["isAuditAccessExpired"] is True
|
|
|
|
def test_user_can_upgrade(self):
|
|
input_data = self.create_test_enrollment()
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# Example audit expired context
|
|
input_context.update(
|
|
{
|
|
"course_mode_info": {
|
|
input_data.course.id: {"show_upsell": True, "verified_sku": uuid4()}
|
|
}
|
|
}
|
|
)
|
|
|
|
output = EnrollmentSerializer(input_data, context=input_context).data
|
|
assert output["canUpgrade"] is True
|
|
|
|
@ddt.data(None, "some_url")
|
|
def test_has_started(self, resume_url):
|
|
# Given the presence or lack of a resume_course_url
|
|
input_data = self.create_test_enrollment()
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
input_context.update(
|
|
{
|
|
"resume_course_urls": {
|
|
input_data.course.id: resume_url,
|
|
}
|
|
}
|
|
)
|
|
|
|
# When I get "hasStarted"
|
|
output = EnrollmentSerializer(input_data, context=input_context).data
|
|
|
|
# If I have a resume URL, "hasStarted" should be True, otherwise False
|
|
if resume_url:
|
|
self.assertTrue(output["hasStarted"])
|
|
else:
|
|
self.assertFalse(output["hasStarted"])
|
|
|
|
|
|
@ddt.ddt
|
|
class TestGradeDataSerializer(LearnerDashboardBaseTest):
|
|
"""Tests for the GradeDataSerializer"""
|
|
|
|
@mock.patch(
|
|
"lms.djangoapps.learner_home.serializers.user_has_passing_grade_in_course"
|
|
)
|
|
@ddt.data(True, False, None)
|
|
def test_happy_path(self, is_passing, mock_get_grade_data):
|
|
# Given a course where I am/not passing
|
|
input_data = self.create_test_enrollment()
|
|
mock_get_grade_data.return_value = is_passing
|
|
|
|
# When I serialize grade data
|
|
output_data = GradeDataSerializer(input_data).data
|
|
|
|
# Then I get the correct data shape out
|
|
self.assertDictEqual(
|
|
output_data,
|
|
{
|
|
"isPassing": is_passing,
|
|
},
|
|
)
|
|
|
|
|
|
@ddt.ddt
|
|
class TestCertificateSerializer(LearnerDashboardBaseTest):
|
|
"""Tests for the CertificateSerializer"""
|
|
|
|
@classmethod
|
|
def generate_test_certificate_info(cls):
|
|
"""Util to generate test certificate info"""
|
|
return {
|
|
"availableDate": random_date(allow_null=True),
|
|
"isRestricted": random_bool(),
|
|
"isAvailable": random_bool(),
|
|
"isEarned": random_bool(),
|
|
"isDownloadable": random_bool(),
|
|
"certPreviewUrl": random_url(allow_null=True),
|
|
"certDownloadUrl": random_url(allow_null=True),
|
|
"honorCertDownloadUrl": random_url(allow_null=True),
|
|
}
|
|
|
|
def create_test_context(self, course):
|
|
"""Get a test context object with an available certificate"""
|
|
return {
|
|
"cert_statuses": {
|
|
course.id: {
|
|
"cert_web_view_url": random_url(),
|
|
"status": "downloadable",
|
|
"show_cert_web_view": True,
|
|
}
|
|
}
|
|
}
|
|
|
|
def test_with_data(self):
|
|
"""Simple mappings test for a course with an available certificate"""
|
|
# Given a verified enrollment
|
|
input_data = self.create_test_enrollment(course_mode=CourseMode.VERIFIED)
|
|
|
|
# ... with a certificate
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# ... and some data preemptively gathered
|
|
available_date = random_date()
|
|
input_data.course.certificate_available_date = available_date
|
|
cert_url = input_context["cert_statuses"][input_data.course.id][
|
|
"cert_web_view_url"
|
|
]
|
|
|
|
# When I get certificate info
|
|
output_data = CertificateSerializer(input_data, context=input_context).data
|
|
|
|
# Then all the info is provided correctly
|
|
self.assertDictEqual(
|
|
output_data,
|
|
{
|
|
"availableDate": datetime_to_django_format(available_date),
|
|
"isRestricted": False,
|
|
"isEarned": True,
|
|
"isDownloadable": True,
|
|
"certPreviewUrl": cert_url,
|
|
},
|
|
)
|
|
|
|
@mock.patch.dict(settings.FEATURES, ENABLE_V2_CERT_DISPLAY_SETTINGS=False)
|
|
def test_available_date_old_format(self):
|
|
# Given new cert display settings are not enabled
|
|
input_data = self.create_test_enrollment(course_mode=CourseMode.VERIFIED)
|
|
input_data.course.certificate_available_date = random_date()
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# When I get certificate info
|
|
output_data = CertificateSerializer(input_data, context=input_context).data
|
|
|
|
# Then the available date is defaulted to the certificate available date
|
|
expected_available_date = datetime_to_django_format(
|
|
input_data.course.certificate_available_date
|
|
)
|
|
self.assertEqual(output_data["availableDate"], expected_available_date)
|
|
|
|
@mock.patch.dict(settings.FEATURES, ENABLE_V2_CERT_DISPLAY_SETTINGS=True)
|
|
def test_available_date_course_end(self):
|
|
# Given new cert display settings are enabled
|
|
input_data = self.create_test_enrollment(course_mode=CourseMode.VERIFIED)
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# ... and certificate display behavior is set to the course end date
|
|
input_data.course.certificates_display_behavior = (
|
|
CertificatesDisplayBehaviors.END
|
|
)
|
|
|
|
# When I try to get cert available date
|
|
output_data = CertificateSerializer(input_data, context=input_context).data
|
|
|
|
# Then the available date is the course end date
|
|
expected_available_date = datetime_to_django_format(input_data.course.end)
|
|
self.assertEqual(output_data["availableDate"], expected_available_date)
|
|
|
|
@mock.patch.dict(settings.FEATURES, ENABLE_V2_CERT_DISPLAY_SETTINGS=True)
|
|
def test_available_date_specific_end(self):
|
|
# Given new cert display settings are enabled
|
|
input_data = self.create_test_enrollment(course_mode=CourseMode.VERIFIED)
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# ... and certificate display behavior is set to a specified date
|
|
input_data.course.certificate_available_date = random_date()
|
|
input_data.course.certificates_display_behavior = (
|
|
CertificatesDisplayBehaviors.END_WITH_DATE
|
|
)
|
|
|
|
# When I try to get cert available date
|
|
output_data = CertificateSerializer(input_data, context=input_context).data
|
|
|
|
# Then the available date is the course end date
|
|
expected_available_date = datetime_to_django_format(
|
|
input_data.course.certificate_available_date
|
|
)
|
|
self.assertEqual(output_data["availableDate"], expected_available_date)
|
|
|
|
@ddt.data(
|
|
("downloadable", False),
|
|
("notpassing", False),
|
|
("restricted", True),
|
|
("auditing", False),
|
|
)
|
|
@ddt.unpack
|
|
def test_is_restricted(self, cert_status, is_restricted_expected):
|
|
"""Test for isRestricted field"""
|
|
# Given a verified enrollment with a certificate
|
|
input_data = self.create_test_enrollment(course_mode=CourseMode.VERIFIED)
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# ... and a cert status {cert_status}
|
|
input_context["cert_statuses"][input_data.course.id]["status"] = cert_status
|
|
|
|
# When I get certificate info
|
|
output_data = CertificateSerializer(input_data, context=input_context).data
|
|
|
|
# Then isRestricted should be calculated correctly
|
|
self.assertEqual(output_data["isRestricted"], is_restricted_expected)
|
|
|
|
@ddt.data(
|
|
("downloadable", True),
|
|
("notpassing", False),
|
|
("restricted", False),
|
|
("auditing", False),
|
|
("certificate_earned_but_not_available", True),
|
|
)
|
|
@ddt.unpack
|
|
def test_is_earned(self, cert_status, is_earned_expected):
|
|
"""Test for isEarned field"""
|
|
# Given a verified enrollment with a certificate
|
|
input_data = self.create_test_enrollment(course_mode=CourseMode.VERIFIED)
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# ... and a cert status {cert_status}
|
|
input_context["cert_statuses"][input_data.course.id]["status"] = cert_status
|
|
|
|
# When I get certificate info
|
|
output_data = CertificateSerializer(input_data, context=input_context).data
|
|
|
|
# Then isEarned should be calculated correctly
|
|
self.assertEqual(output_data["isEarned"], is_earned_expected)
|
|
|
|
@ddt.data(
|
|
("downloadable", True),
|
|
("notpassing", False),
|
|
("restricted", False),
|
|
("auditing", False),
|
|
("certificate_earned_but_not_available", False),
|
|
)
|
|
@ddt.unpack
|
|
def test_is_downloadable(self, cert_status, is_downloadable_expected):
|
|
"""Test for isDownloadable field"""
|
|
# Given a verified enrollment with a certificate
|
|
input_data = self.create_test_enrollment(course_mode=CourseMode.VERIFIED)
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# ... and a cert status {cert_status}
|
|
input_context["cert_statuses"][input_data.course.id]["status"] = cert_status
|
|
|
|
# When I get certificate info
|
|
output_data = CertificateSerializer(input_data, context=input_context).data
|
|
|
|
# Then isDownloadable should be calculated correctly
|
|
self.assertEqual(output_data["isDownloadable"], is_downloadable_expected)
|
|
|
|
@ddt.data(
|
|
(True, random_url()),
|
|
(False, random_url()),
|
|
(True, None),
|
|
(False, None),
|
|
)
|
|
@ddt.unpack
|
|
def test_cert_preview_url(self, show_cert_web_view, cert_web_view_url):
|
|
"""Test for certPreviewUrl field"""
|
|
# Given a verified enrollment with a certificate
|
|
input_data = self.create_test_enrollment(course_mode=CourseMode.VERIFIED)
|
|
input_context = self.create_test_context(input_data.course)
|
|
|
|
# ... and settings show_cert_web_view and cert_web_view_url
|
|
input_context["cert_statuses"][input_data.course.id][
|
|
"show_cert_web_view"
|
|
] = show_cert_web_view
|
|
input_context["cert_statuses"][input_data.course.id][
|
|
"cert_web_view_url"
|
|
] = cert_web_view_url
|
|
|
|
# When I get certificate info
|
|
output_data = CertificateSerializer(input_data, context=input_context).data
|
|
|
|
# Then certPreviewUrl should be calculated correctly
|
|
self.assertEqual(
|
|
output_data["certPreviewUrl"],
|
|
cert_web_view_url if show_cert_web_view else None,
|
|
)
|
|
|
|
|
|
@ddt.ddt
|
|
class TestEntitlementSerializer(TestCase):
|
|
"""Tests for the EntitlementSerializer"""
|
|
|
|
def _assert_available_sessions(self, input_sessions, output_sessions):
|
|
assert len(output_sessions) == len(input_sessions)
|
|
for input_session, output_session in zip(input_sessions, output_sessions):
|
|
assert output_session == {
|
|
"startDate": input_session["start"],
|
|
"endDate": input_session["end"],
|
|
"courseId": input_session["key"],
|
|
}
|
|
|
|
@ddt.unpack
|
|
@ddt.idata(product([True, False], repeat=2))
|
|
def test_serialize_entitlement(self, isExpired, isEnrolled):
|
|
entitlement_kwargs = {}
|
|
if isExpired:
|
|
entitlement_kwargs["expired_at"] = datetime.now()
|
|
if isEnrolled:
|
|
entitlement_kwargs[
|
|
"enrollment_course_run"
|
|
] = CourseEnrollmentFactory.create()
|
|
entitlement = CourseEntitlementFactory.create(**entitlement_kwargs)
|
|
available_sessions = CatalogCourseRunFactory.create_batch(4)
|
|
course_entitlement_available_sessions = {
|
|
str(entitlement.uuid): available_sessions
|
|
}
|
|
|
|
output_data = EntitlementSerializer(
|
|
entitlement,
|
|
context={
|
|
"course_entitlement_available_sessions": course_entitlement_available_sessions
|
|
},
|
|
).data
|
|
|
|
output_sessions = output_data.pop("availableSessions")
|
|
self._assert_available_sessions(available_sessions, output_sessions)
|
|
|
|
if isExpired:
|
|
expected_expiration_date = entitlement.expired_at
|
|
else:
|
|
expected_expiration_date = date.today() + timedelta(
|
|
days=entitlement.get_days_until_expiration()
|
|
)
|
|
|
|
assert output_data == {
|
|
"isRefundable": entitlement.is_entitlement_refundable(),
|
|
"isFulfilled": bool(entitlement.enrollment_course_run),
|
|
"changeDeadline": expected_expiration_date,
|
|
"isExpired": bool(entitlement.expired_at),
|
|
"expirationDate": expected_expiration_date,
|
|
"uuid": str(entitlement.uuid),
|
|
"enrollmentUrl": f"/api/entitlements/v1/entitlements/{entitlement.uuid}/enrollments",
|
|
}
|
|
|
|
|
|
class TestProgramsSerializer(TestCase):
|
|
"""Tests for the ProgramsSerializer and RelatedProgramsSerializer"""
|
|
|
|
@classmethod
|
|
def generate_test_related_program(cls):
|
|
"""Generate a program with random test data"""
|
|
return ProgramFactory()
|
|
|
|
@classmethod
|
|
def generate_test_programs_info(cls):
|
|
"""Util to generate test programs info"""
|
|
return {
|
|
"relatedPrograms": [cls.generate_test_related_program() for _ in range(3)],
|
|
}
|
|
|
|
def test_related_program_serializer(self):
|
|
"""Test the RelatedProgramSerializer"""
|
|
# Given a program
|
|
input_data = self.generate_test_related_program()
|
|
# When I serialize it
|
|
output_data = RelatedProgramSerializer(input_data).data
|
|
# Then the output should map with the input
|
|
self.assertEqual(
|
|
output_data,
|
|
{
|
|
"bannerImgSrc": input_data["banner_image"]["small"]["url"],
|
|
"logoImgSrc": input_data["authoring_organizations"][0][
|
|
"logo_image_url"
|
|
],
|
|
"numberOfCourses": len(input_data["courses"]),
|
|
"programType": input_data["type"],
|
|
"programUrl": settings.LMS_ROOT_URL
|
|
+ reverse(
|
|
"program_details_view", kwargs={"program_uuid": input_data["uuid"]}
|
|
),
|
|
"provider": input_data["authoring_organizations"][0]["name"],
|
|
"title": input_data["title"],
|
|
},
|
|
)
|
|
|
|
def test_programs_serializer(self):
|
|
"""Test the ProgramsSerializer"""
|
|
# Given a program with random test data
|
|
input_data = self.generate_test_programs_info()
|
|
|
|
# When I serialize the program
|
|
output_data = ProgramsSerializer(input_data).data
|
|
|
|
# Test the output
|
|
assert output_data["relatedPrograms"]
|
|
assert len(output_data["relatedPrograms"]) == len(input_data["relatedPrograms"])
|
|
self.assertEqual(
|
|
output_data,
|
|
{
|
|
"relatedPrograms": RelatedProgramSerializer(
|
|
input_data["relatedPrograms"], many=True
|
|
).data
|
|
},
|
|
)
|
|
|
|
def test_empty_sessions(self):
|
|
input_data = {"relatedPrograms": []}
|
|
output_data = ProgramsSerializer(input_data).data
|
|
|
|
assert output_data == {"relatedPrograms": []}
|
|
|
|
|
|
class TestLearnerEnrollmentsSerializer(LearnerDashboardBaseTest):
|
|
"""High-level tests for LearnerEnrollmentsSerializer"""
|
|
|
|
def test_happy_path(self):
|
|
"""Test that nothing breaks and the output fields look correct"""
|
|
|
|
enrollment = self.create_test_enrollment()
|
|
|
|
input_data = enrollment
|
|
input_context = {
|
|
"resume_course_urls": {enrollment.course.id: random_url()},
|
|
"ecommerce_payment_page": random_url(),
|
|
"course_mode_info": {
|
|
enrollment.course.id: {
|
|
"verified_sku": str(uuid4()),
|
|
"days_for_upsell": randint(0, 14),
|
|
}
|
|
},
|
|
"fulfilled_entitlements": {},
|
|
"unfulfilled_entitlement_pseudo_sessions": {},
|
|
"programs": {},
|
|
}
|
|
|
|
output_data = LearnerEnrollmentSerializer(
|
|
input_data, context=input_context
|
|
).data
|
|
expected_keys = [
|
|
"courseProvider",
|
|
"course",
|
|
"courseRun",
|
|
"enrollment",
|
|
"gradeData",
|
|
"certificate",
|
|
"entitlement",
|
|
"programs",
|
|
]
|
|
assert output_data.keys() == set(expected_keys)
|
|
|
|
|
|
class TestUnfulfilledEntitlementSerializer(LearnerDashboardBaseTest):
|
|
"""High-level tests for UnfulfilledEntitlementSerializer"""
|
|
|
|
def make_unfulfilled_entitlement(self):
|
|
""" Create an unfulflled entitlement, along with a pseudo session and available sessions"""
|
|
unfulfilled_entitlement = CourseEntitlementFactory.create()
|
|
pseudo_sessions = {
|
|
str(unfulfilled_entitlement.uuid): CatalogCourseRunFactory.create()
|
|
}
|
|
available_sessions = {
|
|
str(unfulfilled_entitlement.uuid): CatalogCourseRunFactory.create_batch(3)
|
|
}
|
|
return unfulfilled_entitlement, pseudo_sessions, available_sessions
|
|
|
|
def make_pseudo_session_course_overviews(self, unfulfilled_entitlement, pseudo_sessions):
|
|
""" Create course overview for course provider info """
|
|
course_key_str = pseudo_sessions[str(unfulfilled_entitlement.uuid)]["key"]
|
|
course_key = CourseKey.from_string(course_key_str)
|
|
course_overview = CourseOverviewFactory.create(id=course_key)
|
|
return {course_key: course_overview}
|
|
|
|
def test_happy_path(self):
|
|
"""Test that nothing breaks and the output fields look correct"""
|
|
unfulfilled_entitlement, pseudo_sessions, available_sessions = self.make_unfulfilled_entitlement()
|
|
pseudo_session_course_overviews = self.make_pseudo_session_course_overviews(
|
|
unfulfilled_entitlement,
|
|
pseudo_sessions
|
|
)
|
|
context = {
|
|
"unfulfilled_entitlement_pseudo_sessions": pseudo_sessions,
|
|
"course_entitlement_available_sessions": available_sessions,
|
|
"pseudo_session_course_overviews": pseudo_session_course_overviews,
|
|
"programs": {}
|
|
}
|
|
|
|
output_data = UnfulfilledEntitlementSerializer(
|
|
unfulfilled_entitlement, context=context
|
|
).data
|
|
|
|
expected_keys = [
|
|
"courseProvider",
|
|
"course",
|
|
"entitlement",
|
|
"programs",
|
|
"courseRun",
|
|
"gradeData",
|
|
"certificate",
|
|
"enrollment",
|
|
]
|
|
|
|
assert output_data.keys() == set(expected_keys)
|
|
assert output_data["courseProvider"] is not None
|
|
assert output_data["courseRun"] is None
|
|
assert output_data["gradeData"] is None
|
|
assert output_data["certificate"] is None
|
|
assert (
|
|
output_data["enrollment"]
|
|
== UnfulfilledEntitlementSerializer.STATIC_ENTITLEMENT_ENROLLMENT_DATA
|
|
)
|
|
assert output_data["course"] is not None
|
|
assert output_data["courseProvider"] is not None
|
|
assert output_data["programs"] == {"relatedPrograms": []}
|
|
|
|
def test_programs(self):
|
|
unfulfilled_entitlement, pseudo_sessions, available_sessions = self.make_unfulfilled_entitlement()
|
|
pseudo_session_course_overviews = self.make_pseudo_session_course_overviews(
|
|
unfulfilled_entitlement,
|
|
pseudo_sessions
|
|
)
|
|
related_programs = ProgramFactory.create_batch(3)
|
|
programs = {
|
|
str(unfulfilled_entitlement.course_uuid): related_programs
|
|
}
|
|
|
|
context = {
|
|
"unfulfilled_entitlement_pseudo_sessions": pseudo_sessions,
|
|
"course_entitlement_available_sessions": available_sessions,
|
|
"pseudo_session_course_overviews": pseudo_session_course_overviews,
|
|
"programs": programs
|
|
}
|
|
|
|
output_data = UnfulfilledEntitlementSerializer(
|
|
unfulfilled_entitlement, context=context
|
|
).data
|
|
|
|
assert output_data["programs"] == ProgramsSerializer(
|
|
{"relatedPrograms": related_programs}
|
|
).data
|
|
|
|
def test_static_enrollment_data(self):
|
|
"""
|
|
For an unfulfilled entitlement's "enrollment" data, we're returning a static dict.
|
|
This test is to ensure that that dict has the same keys as returned by the LearnerEnrollmentSerializer
|
|
"""
|
|
output_data = TestEnrollmentSerializer().serialize_test_enrollment()
|
|
expected_keys = (
|
|
UnfulfilledEntitlementSerializer.STATIC_ENTITLEMENT_ENROLLMENT_DATA.keys()
|
|
)
|
|
actual_keys = output_data.keys()
|
|
assert expected_keys == actual_keys
|
|
|
|
|
|
class TestSuggestedCourseSerializer(TestCase):
|
|
"""High-level tests for SuggestedCourseSerializer"""
|
|
|
|
@classmethod
|
|
def mock_suggested_courses(cls, courses_count=5):
|
|
"""
|
|
Sample return data from general recommendations
|
|
"""
|
|
suggested_courses = {
|
|
"courses": [],
|
|
"is_personalized_recommendation": False,
|
|
}
|
|
|
|
for i in range(courses_count):
|
|
suggested_courses["courses"].append(
|
|
{
|
|
"course_key": uuid4(),
|
|
"logo_image_url": random_url(),
|
|
"marketing_url": random_url(),
|
|
"title": str(uuid4()),
|
|
},
|
|
)
|
|
|
|
return suggested_courses
|
|
|
|
def test_happy_path(self):
|
|
"""Test that data serializes correctly"""
|
|
|
|
input_data = self.mock_suggested_courses(courses_count=1)["courses"][0]
|
|
|
|
output_data = SuggestedCourseSerializer(input_data).data
|
|
|
|
self.assertDictEqual(
|
|
output_data,
|
|
{
|
|
"bannerImgSrc": input_data["logo_image_url"],
|
|
"logoImgSrc": None,
|
|
"courseName": input_data["title"],
|
|
"courseUrl": input_data["marketing_url"],
|
|
},
|
|
)
|
|
|
|
|
|
class TestEmailConfirmationSerializer(TestCase):
|
|
"""High-level tests for EmailConfirmationSerializer"""
|
|
|
|
@classmethod
|
|
def generate_test_data(cls):
|
|
return {
|
|
"isNeeded": random_bool(),
|
|
"sendEmailUrl": random_url(),
|
|
}
|
|
|
|
def test_structure(self):
|
|
"""Test that nothing breaks and the output fields look correct"""
|
|
input_data = self.generate_test_data()
|
|
|
|
output_data = EmailConfirmationSerializer(input_data).data
|
|
|
|
expected_keys = [
|
|
"isNeeded",
|
|
"sendEmailUrl",
|
|
]
|
|
assert output_data.keys() == set(expected_keys)
|
|
|
|
def test_happy_path(self):
|
|
"""Test that data serializes correctly"""
|
|
|
|
input_data = self.generate_test_data()
|
|
|
|
output_data = EmailConfirmationSerializer(input_data).data
|
|
|
|
self.assertDictEqual(
|
|
output_data,
|
|
{
|
|
"isNeeded": input_data["isNeeded"],
|
|
"sendEmailUrl": input_data["sendEmailUrl"],
|
|
},
|
|
)
|
|
|
|
|
|
class TestEnterpriseDashboardSerializer(TestCase):
|
|
"""High-level tests for EnterpriseDashboardSerializer"""
|
|
|
|
@classmethod
|
|
def generate_test_data(cls):
|
|
return {
|
|
"uuid": str(uuid4()),
|
|
"name": str(uuid4()),
|
|
}
|
|
|
|
def test_structure(self):
|
|
"""Test that nothing breaks and the output fields look correct"""
|
|
input_data = self.generate_test_data()
|
|
|
|
output_data = EnterpriseDashboardSerializer(input_data).data
|
|
|
|
expected_keys = [
|
|
"label",
|
|
"url",
|
|
]
|
|
assert output_data.keys() == set(expected_keys)
|
|
|
|
def test_happy_path(self):
|
|
"""Test that data serializes correctly"""
|
|
|
|
input_data = self.generate_test_data()
|
|
|
|
output_data = EnterpriseDashboardSerializer(input_data).data
|
|
|
|
self.assertDictEqual(
|
|
output_data,
|
|
{
|
|
"label": input_data["name"],
|
|
"url": settings.ENTERPRISE_LEARNER_PORTAL_BASE_URL
|
|
+ "/"
|
|
+ input_data["uuid"],
|
|
},
|
|
)
|
|
|
|
|
|
class TestLearnerDashboardSerializer(LearnerDashboardBaseTest):
|
|
"""High-level tests for Learner Dashboard serialization"""
|
|
|
|
# Show full diff for serialization issues
|
|
maxDiff = None
|
|
|
|
def make_test_context(
|
|
self,
|
|
enrollments=None,
|
|
enrollments_with_entitlements=None,
|
|
unfulfilled_entitlements=None,
|
|
has_programs=False,
|
|
):
|
|
"""
|
|
Given enrollments and entitlements, generate a matching serializer context
|
|
"""
|
|
enrollments = enrollments or []
|
|
enrollments_with_entitlements = enrollments_with_entitlements or []
|
|
unfulfilled_entitlements = unfulfilled_entitlements or []
|
|
|
|
resume_course_urls = {
|
|
enrollment.course.id: random_url() for enrollment in enrollments
|
|
}
|
|
course_mode_info = {
|
|
enrollment.course.id: {
|
|
"verified_sku": str(uuid4()),
|
|
"days_for_upsell": randint(0, 14),
|
|
}
|
|
for enrollment in enrollments
|
|
if enrollment.mode == "audit"
|
|
}
|
|
|
|
all_entitlements = list(unfulfilled_entitlements)
|
|
fulfilled_entitlements = {}
|
|
for enrollment in enrollments_with_entitlements:
|
|
entitlement = CourseEntitlementFactory.create(
|
|
enrollment_course_run=enrollment
|
|
)
|
|
all_entitlements.append(entitlement)
|
|
fulfilled_entitlements[str(enrollment.course_id)] = entitlement
|
|
|
|
unfulfilled_entitlement_pseudo_sessions = {
|
|
str(unfulfilled_entitlement.uuid): CatalogCourseRunFactory.create()
|
|
for unfulfilled_entitlement in unfulfilled_entitlements
|
|
}
|
|
|
|
course_entitlement_available_sessions = {
|
|
str(entitlement.uuid): CatalogCourseRunFactory.create_batch(3)
|
|
for entitlement in all_entitlements
|
|
}
|
|
|
|
# Create related course overviews for entitlement pseudo sessions
|
|
pseudo_session_course_overviews = {}
|
|
for unfulfilled_entitlement in unfulfilled_entitlement_pseudo_sessions:
|
|
course_key_str = unfulfilled_entitlement_pseudo_sessions[
|
|
unfulfilled_entitlement
|
|
]["key"]
|
|
course_key = CourseKey.from_string(course_key_str)
|
|
course_overview = CourseOverviewFactory.create(id=course_key)
|
|
|
|
pseudo_session_course_overviews[course_key] = course_overview
|
|
programs = (
|
|
{
|
|
str(enrollment.course.id): ProgramFactory.create_batch(3)
|
|
for enrollment in enrollments
|
|
}
|
|
if has_programs
|
|
else {}
|
|
)
|
|
|
|
input_context = {
|
|
"resume_course_urls": resume_course_urls,
|
|
"ecommerce_payment_page": random_url(),
|
|
"course_mode_info": course_mode_info,
|
|
"fulfilled_entitlements": fulfilled_entitlements,
|
|
"unfulfilled_entitlement_pseudo_sessions": unfulfilled_entitlement_pseudo_sessions,
|
|
"course_entitlement_available_sessions": course_entitlement_available_sessions,
|
|
"pseudo_session_course_overviews": pseudo_session_course_overviews,
|
|
"programs": programs,
|
|
}
|
|
return input_context
|
|
|
|
def test_empty(self):
|
|
"""Test that empty inputs return the right keys"""
|
|
|
|
input_data = {
|
|
"emailConfirmation": None,
|
|
"enterpriseDashboard": None,
|
|
"platformSettings": None,
|
|
"enrollments": [],
|
|
"unfulfilledEntitlements": [],
|
|
"suggestedCourses": [],
|
|
}
|
|
output_data = LearnerDashboardSerializer(input_data).data
|
|
|
|
self.assertDictEqual(
|
|
output_data,
|
|
{
|
|
"emailConfirmation": None,
|
|
"enterpriseDashboard": None,
|
|
"platformSettings": None,
|
|
"courses": [],
|
|
"suggestedCourses": [],
|
|
},
|
|
)
|
|
|
|
def test_enrollments(self):
|
|
"""Test that enrollments-related info is linked and serialized correctly"""
|
|
|
|
enrollments = [self.create_test_enrollment()]
|
|
|
|
input_context = self.make_test_context(
|
|
enrollments=enrollments,
|
|
)
|
|
|
|
input_data = {
|
|
"emailConfirmation": None,
|
|
"enterpriseDashboard": None,
|
|
"platformSettings": None,
|
|
"enrollments": enrollments,
|
|
"unfulfilledEntitlements": [],
|
|
"suggestedCourses": [],
|
|
}
|
|
|
|
output_data = LearnerDashboardSerializer(input_data, context=input_context).data
|
|
|
|
# Right now just make sure nothing broke
|
|
courses = output_data.pop("courses")
|
|
assert courses is not None
|
|
|
|
def test_entitlements(self):
|
|
# One standard enrollment, one fulfilled entitlement, one unfulfilled enrollment
|
|
enrollments = [self.create_test_enrollment(), self.create_test_enrollment()]
|
|
unfulfilled_entitlements = [CourseEntitlementFactory.create()]
|
|
|
|
input_context = self.make_test_context(
|
|
enrollments=enrollments,
|
|
enrollments_with_entitlements=[enrollments[1]],
|
|
unfulfilled_entitlements=unfulfilled_entitlements,
|
|
has_programs=True,
|
|
)
|
|
|
|
input_data = {
|
|
"emailConfirmation": None,
|
|
"enterpriseDashboards": None,
|
|
"platformSettings": None,
|
|
"enrollments": enrollments,
|
|
"unfulfilledEntitlements": unfulfilled_entitlements,
|
|
"suggestedCourses": [],
|
|
}
|
|
|
|
output_data = LearnerDashboardSerializer(input_data, context=input_context).data
|
|
|
|
courses = output_data.pop("courses")
|
|
# We should have three dicts with identical keys for the course card elements
|
|
assert len(courses) == 3
|
|
self._assert_all_keys_equal(courses)
|
|
# Non-entitlement enrollment should have no entitlement info
|
|
assert not courses[0]["entitlement"]
|
|
# Fulfilled and Unfulfilled entitlement should have identical keys
|
|
fulfilled_entitlement = courses[1]["entitlement"]
|
|
unfulfilled_entitlement = courses[2]["entitlement"]
|
|
assert fulfilled_entitlement
|
|
assert unfulfilled_entitlement
|
|
assert fulfilled_entitlement.keys() == unfulfilled_entitlement.keys()
|
|
|
|
# test programs
|
|
assert courses[0]["programs"]
|
|
assert len(courses[0]["programs"]["relatedPrograms"]) == 3
|
|
|
|
def test_suggested_courses(self):
|
|
|
|
suggested_courses = TestSuggestedCourseSerializer.mock_suggested_courses()[
|
|
"courses"
|
|
]
|
|
|
|
input_data = {
|
|
"emailConfirmation": None,
|
|
"enterpriseDashboard": None,
|
|
"platformSettings": None,
|
|
"enrollments": [],
|
|
"unfulfilledEntitlements": [],
|
|
"suggestedCourses": suggested_courses,
|
|
}
|
|
output_data = LearnerDashboardSerializer(input_data).data
|
|
|
|
output_suggested_courses = output_data.pop("suggestedCourses")
|
|
|
|
self.assertEqual(len(suggested_courses), len(output_suggested_courses))
|
|
|
|
@mock.patch(
|
|
"lms.djangoapps.learner_home.serializers.SuggestedCourseSerializer.to_representation"
|
|
)
|
|
@mock.patch(
|
|
"lms.djangoapps.learner_home.serializers.UnfulfilledEntitlementSerializer.data"
|
|
)
|
|
@mock.patch(
|
|
"lms.djangoapps.learner_home.serializers.LearnerEnrollmentSerializer.data"
|
|
)
|
|
@mock.patch(
|
|
"lms.djangoapps.learner_home.serializers.PlatformSettingsSerializer.to_representation"
|
|
)
|
|
@mock.patch(
|
|
"lms.djangoapps.learner_home.serializers.EnterpriseDashboardSerializer.to_representation"
|
|
)
|
|
@mock.patch(
|
|
"lms.djangoapps.learner_home.serializers.EmailConfirmationSerializer.to_representation"
|
|
)
|
|
def test_linkage(
|
|
self,
|
|
mock_email_confirmation_serializer,
|
|
mock_enterprise_dashboard_serializer,
|
|
mock_platform_settings_serializer,
|
|
mock_learner_enrollment_serializer,
|
|
mock_entitlement_serializer,
|
|
mock_suggestions_serializer,
|
|
):
|
|
mock_email_confirmation_serializer.return_value = (
|
|
mock_email_confirmation_serializer
|
|
)
|
|
mock_enterprise_dashboard_serializer.return_value = (
|
|
mock_enterprise_dashboard_serializer
|
|
)
|
|
mock_platform_settings_serializer.return_value = (
|
|
mock_platform_settings_serializer
|
|
)
|
|
mock_learner_enrollment_serializer.return_value = (
|
|
mock_learner_enrollment_serializer
|
|
)
|
|
mock_entitlement_serializer.return_value = mock_entitlement_serializer
|
|
mock_suggestions_serializer.return_value = mock_suggestions_serializer
|
|
|
|
input_data = {
|
|
"emailConfirmation": {},
|
|
"enterpriseDashboard": {},
|
|
"platformSettings": {},
|
|
"enrollments": [{}],
|
|
"unfulfilledEntitlements": [{}],
|
|
"suggestedCourses": [{}],
|
|
}
|
|
output_data = LearnerDashboardSerializer(input_data).data
|
|
|
|
self.assertDictEqual(
|
|
output_data,
|
|
{
|
|
"emailConfirmation": mock_email_confirmation_serializer,
|
|
"enterpriseDashboard": mock_enterprise_dashboard_serializer,
|
|
"platformSettings": mock_platform_settings_serializer,
|
|
"courses": [
|
|
mock_learner_enrollment_serializer,
|
|
mock_entitlement_serializer,
|
|
],
|
|
"suggestedCourses": [mock_suggestions_serializer],
|
|
},
|
|
)
|