chore: test and api updates

This commit is contained in:
Ben Warzeski
2022-07-22 01:38:21 -04:00
parent 8edd4570b4
commit 7a46b3c2a8
17 changed files with 538 additions and 110 deletions

View File

@@ -2,7 +2,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Hyperlink } from '@edx/paragon';
import { MailtoLink, Hyperlink } from '@edx/paragon';
import { CheckCircle } from '@edx/paragon/icons';
import { useIntl } from '@edx/frontend-platform/i18n';
@@ -19,61 +19,76 @@ export const CertificateBanner = ({ courseNumber }) => {
hasFinished,
} = appHooks.useCardEnrollmentData(courseNumber);
const { isPassing } = appHooks.useCardGradeData(courseNumber);
const { minPassingGrade } = appHooks.useCardCourseRunData(courseNumber);
const { minPassingGrade, progressUrl } = appHooks.useCardCourseRunData(courseNumber);
const { supportEmail, billingEmail } = appHooks.usePlatformSettingsData();
const { formatMessage } = useIntl();
const emailLink = address => address && <MailtoLink to={address}>{address}</MailtoLink>;
if (certificate.isRestricted) {
return (
<Banner variant="danger">
{formatMessage(messages.certRestricted)}
<Hyperlink destination="info@example.com">info@example.com</Hyperlink>
{isVerified && (
<>
If you would like a refund on your Certificate of Achievement, please contact our billing address <Hyperlink destination="billing@example.com">billing@example.com</Hyperlink>
</>
{formatMessage(messages.certRestricted, { supportEmail: emailLink(supportEmail) })}
{isVerified && ' '}
{isVerified && formatMessage(
messages.certRefundContactBilling,
{ billingEmail: emailLink(billingEmail) },
)}
</Banner>
);
}
if (!isPassing) {
if (isAudit) {
return (<Banner> Grade required to pass the course: {minPassingGrade}% </Banner>);
return (
<Banner>
{formatMessage(messages.passingGrade, { minPassingGrade })}
</Banner>
);
}
if (hasFinished) {
return (
<Banner variant="warning">
You are not eligible for a certificate. <Hyperlink destination="">View grades.</Hyperlink>
{formatMessage(messages.notEligibleForCert)}.
{' '}
<Hyperlink destination={progressUrl}>{formatMessage(messages.viewGrades)}</Hyperlink>
</Banner>
);
}
return (
<Banner variant="warning">
Grade required for a certificate: {minPassingGrade}%
{formatMessage(messages.certMinGrade, { minPassingGrade })}
</Banner>
);
}
if (certificate.isDownloadable) {
if (certificate.previewUrl) {
if (certificate.certPreviewUrl) {
return (
<Banner variant="success" icon={CheckCircle}>
Congratulations. Your certificate is ready.
{formatMessage(messages.certReady)}
{' '}
<Hyperlink destination={certificate.previewUrl}>View Certificate.</Hyperlink>
<Hyperlink destination={certificate.certPreviewUrl}>
{formatMessage(messages.viewCertificate)}
</Hyperlink>
</Banner>
);
}
return (
<Banner variant="success" icon={CheckCircle}>
Congratulations. Your certificate is ready.
{formatMessage(messages.certReady)}
{' '}
<Hyperlink destination={certificate.downloadUrl}>Download Certificate.</Hyperlink>
<Hyperlink destination={certificate.certDownloadUrl}>
{formatMessage(messages.downloadCertificate)}
</Hyperlink>
</Banner>
);
}
if (certificate.isEarnedButUnavailable) {
return (
<Banner>
Your grade and certificate will be ready after {certificate.availableDate}.
{formatMessage(
messages.gradeAndCertReadyAfter,
{ availableDate: certificate.availableDate },
)}
</Banner>
);
}

View File

@@ -0,0 +1,111 @@
import React from 'react';
import { shallow } from 'enzyme';
import { Hyperlink } from '@edx/paragon';
import { hooks as appHooks } from 'data/redux';
import { CourseBanner } from './CourseBanner';
import messages from './messages';
jest.mock('components/Banner', () => 'Banner');
jest.mock('data/redux', () => ({
hooks: {
useCardCourseData: jest.fn(),
useCardCourseRunData: jest.fn(),
useCardEnrollmentData: jest.fn(),
},
}));
const courseNumber = 'my-test-course-number';
let el;
const enrollmentData = {
isVerified: false,
canUpgrade: false,
isAuditAccessExpired: false,
};
const courseRunData = {
isActive: false,
};
const courseData = {
website: 'test-course-website',
};
const render = (overrides = {}) => {
const {
course = {},
courseRun = {},
enrollment = {},
} = overrides;
appHooks.useCardCourseData.mockReturnValueOnce({
...courseData,
...course,
});
appHooks.useCardCourseRunData.mockReturnValueOnce({
...courseRunData,
...courseRun,
});
appHooks.useCardEnrollmentData.mockReturnValueOnce({
...enrollmentData,
...enrollment,
});
el = shallow(<CourseBanner courseNumber={courseNumber} />);
};
describe('CourseBanner', () => {
it('initializes data with course number from enrollment, course and course run data', () => {
render();
expect(appHooks.useCardCourseData).toHaveBeenCalledWith(courseNumber);
expect(appHooks.useCardCourseRunData).toHaveBeenCalledWith(courseNumber);
expect(appHooks.useCardEnrollmentData).toHaveBeenCalledWith(courseNumber);
});
test('no display if learner is verified', () => {
render({ enrollment: { isVerified: true } });
expect(el.isEmptyRender()).toEqual(true);
});
describe('audit access expired, can upgrade', () => {
beforeEach(() => {
render({ enrollment: { isAuditAccessExpired: true, canUpgrade: true } });
});
test('snapshot: (auditAccessExpired, upgradeToAccess)', () => {
expect(el).toMatchSnapshot();
});
test('messages: (auditAccessExpired, upgradeToAccess)', () => {
expect(el.text()).toContain(messages.auditAccessExpired.defaultMessage);
expect(el.text()).toContain(messages.upgradeToAccess.defaultMessage);
});
});
describe('audit access expired, cannot upgrade', () => {
beforeEach(() => {
render({ enrollment: { isAuditAccessExpired: true } });
});
test('snapshot: (auditAccessExpired, findAnotherCourse hyperlink)', () => {
expect(el).toMatchSnapshot();
});
test('messages: (auditAccessExpired, upgradeToAccess)', () => {
expect(el.text()).toContain(messages.auditAccessExpired.defaultMessage);
expect(el.find(Hyperlink).text()).toEqual(messages.findAnotherCourse.defaultMessage);
});
});
describe('course run active and cannot upgrade', () => {
beforeEach(() => {
render({ courseRun: { isActive: true } });
});
test('snapshot: (upgradseDeadlinePassed, exploreCourseDetails hyperlink)', () => {
expect(el).toMatchSnapshot();
});
test('messages: (upgradseDeadlinePassed, exploreCourseDetails hyperlink)', () => {
expect(el.text()).toContain(messages.upgradeDeadlinePassed.defaultMessage);
const link = el.find(Hyperlink);
expect(link.text()).toEqual(messages.exploreCourseDetails.defaultMessage);
expect(link.props().destination).toEqual(courseData.website);
});
});
test('no display if audit access not expired and (course is not active or can upgrade)', () => {
render();
expect(el.isEmptyRender()).toEqual(true);
render({ enrollment: { canUpgrade: true }, courseRun: { isActive: true } });
expect(el.isEmptyRender()).toEqual(true);
});
});

View File

@@ -1,27 +1,52 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Button, MailtoLink } from '@edx/paragon';
import { hooks as appHooks } from 'data/redux';
import Banner from 'components/Banner';
import messages from './messages';
export const EntitlementBanner = ({ courseNumber }) => {
const {
canChange,
isEntitlement,
isExpired,
hasSessions,
isFulfilled,
changeDeadline,
showExpirationWarning,
} = appHooks.useCardEntitlementsData(courseNumber);
const { supportEmail } = appHooks.usePlatformSettingsData();
const { formatDate, formatMessage } = useIntl();
if (!isEntitlement) {
return null;
}
if (isExpired || isFulfilled) {
return null;
if (!hasSessions && !isFulfilled) {
return (
<Banner variant="warning">
{formatMessage(messages.entitlementsUnavailable, {
emailLink: supportEmail && <MailtoLink to={supportEmail}>{supportEmail}</MailtoLink>,
})}
</Banner>
);
}
return canChange
? (<Banner>You must select a session to access the course.</Banner>)
: (<Banner>The deadline to select a session has passed</Banner>);
if (showExpirationWarning) {
return (
<Banner>
{formatMessage(messages.entitlementsExpiringSoon, {
changeDeadline: formatDate(changeDeadline),
selectSessionButton: (
<Button variant="link" size="inline" className="m-0 p-0">
{formatMessage(messages.selectSession)}
</Button>
),
})}
</Banner>
);
}
return null;
};
EntitlementBanner.propTypes = {
courseNumber: PropTypes.string.isRequired,

View File

@@ -0,0 +1,69 @@
import React from 'react';
import { shallow } from 'enzyme';
import { useIntl } from '@edx/frontend-platform/i18n';
import { hooks as appHooks } from 'data/redux';
import EntitlementBanner from './EntitlementBanner';
jest.mock('@edx/frontend-platform/i18n', () => ({
useIntl: jest.fn(),
}));
jest.mock('components/Banner', () => 'Banner');
jest.mock('data/redux', () => ({
hooks: {
usePlatformSettingsData: jest.fn(),
useCardEntitlementsData: jest.fn(),
},
}));
const courseNumber = 'my-test-course-number';
let el;
const entitlementsData = {
isEntitlement: true,
hasSessions: true,
isFulfilled: false,
changeDeadline: 'test-deadline',
showExpirationWarning: false,
};
const platformData = { supportEmail: 'test-support-email' };
const render = (overrides = {}) => {
const { entitlements = {} } = overrides;
appHooks.useCardEntitlementsData.mockReturnValueOnce({ ...entitlementsData, ...entitlements });
appHooks.usePlatformSettingsData.mockReturnValueOnce(platformData);
el = shallow(<EntitlementBanner courseNumber={courseNumber} />);
};
describe('EntitlementBanner', () => {
beforeEach(() => {
useIntl.mockReturnValue({
formatDate: (date) => date,
formatMessage: (message, values) => <div {...{ message, values }} />,
});
});
afterEach(() => {
jest.clearAllMocks();
});
it('initializes data with course number from entitlements', () => {
render();
expect(appHooks.useCardEntitlementsData).toHaveBeenCalledWith(courseNumber);
});
test('no display if not an entitlement', () => {
render({ entitlements: { isEntitlement: false } });
expect(el.isEmptyRender()).toEqual(true);
});
test('snapshot: no sessions available', () => {
render({ entitlements: { isFulfilled: false, hasSessions: false } });
expect(el).toMatchSnapshot();
});
test('snapshot: expiration warning', () => {
render({ entitlements: { showExpirationWarning: true } });
expect(el).toMatchSnapshot();
});
test('no display if sessions available and not displaying warning', () => {
render();
expect(el.isEmptyRender()).toEqual(true);
});
});

View File

@@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CourseBanner audit access expired, can upgrade snapshot: (auditAccessExpired, upgradeToAccess) 1`] = `
<Banner>
Your audit access to this course has expired.
Upgrade now to access your course again.
</Banner>
`;
exports[`CourseBanner audit access expired, cannot upgrade snapshot: (auditAccessExpired, findAnotherCourse hyperlink) 1`] = `
<Banner>
Your audit access to this course has expired.
<Hyperlink
destination=""
>
Find another course
</Hyperlink>
</Banner>
`;
exports[`CourseBanner course run active and cannot upgrade snapshot: (upgradseDeadlinePassed, exploreCourseDetails hyperlink) 1`] = `
<Banner>
Your upgrade deadline for this course has passed. To upgrade, enroll in a session that is farther in the future.
<Hyperlink
destination="test-course-website"
>
Explore course details.
</Hyperlink>
</Banner>
`;

View File

@@ -0,0 +1,60 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EntitlementBanner snapshot: expiration warning 1`] = `
<Banner>
<div
message={
Object {
"defaultMessage": "You must {selectSessionButton} by {changeDeadline} to access the course.",
"description": "Entitlements course message when the entitlement is expiring soon.",
"id": "learner-dash.courseCard.banners.entitlementsExpiringSoon",
}
}
values={
Object {
"changeDeadline": "test-deadline",
"selectSessionButton": <Button
className="m-0 p-0"
size="inline"
variant="link"
>
<div
message={
Object {
"defaultMessage": "select a session",
"description": "Entitlements session selection link text",
"id": "learner-dash.courseCard.banners.selectSession",
}
}
/>
</Button>,
}
}
/>
</Banner>
`;
exports[`EntitlementBanner snapshot: no sessions available 1`] = `
<Banner
variant="warning"
>
<div
message={
Object {
"defaultMessage": "There are no sessions available at the moment. The course team will create new sessions soon. If no sessions appear, please contact {emailLink} for information.",
"description": "Entitlements course message when no sessions are available",
"id": "learner-dash.courseCard.banners.entitlementsUnavailable",
}
}
values={
Object {
"emailLink": <MailtoLink
to="test-support-email"
>
test-support-email
</MailtoLink>,
}
}
/>
</Banner>
`;

View File

@@ -29,7 +29,67 @@ export const messages = StrictDict({
certRestricted: {
id: 'learner-dash.courseCard.banners.certificateRestricted',
description: 'Restricted certificate warning message',
defaultMessage: 'Your Certificate of Achievement is being held pending confirmation that the issuance of your Certificate is in compliance with strict U.S. embargoes on Iran, Cuba, Syria, and Sudan. If you think our system has mistakenly identified you as being connected with one of those countries, please let us know by contacting ',
defaultMessage: 'Your Certificate of Achievement is being held pending confirmation that the issuance of your Certificate is in compliance with strict U.S. embargoes on Iran, Cuba, Syria, and Sudan. If you think our system has mistakenly identified you as being connected with one of those countries, please let us know by contacting {supportEmail}.',
},
certRefundContactBilling: {
id: 'learner-dash.courseCard.banners.certificateRefundContactBilling',
description: 'Message to learners to contact billing for certificate refunds',
defaultMessage: 'If you would like a refund on your Certificate of Achievement, please contact our billing address {billingEmail}',
},
passingGrade: {
id: 'learner-dash.courseCard.banners.passingGrade',
description: 'Message to learners with minimum passing grade for the course',
defaultMessage: 'Grade required to pass the course: {minPassingGrade}',
},
notEligibleForCert: {
id: 'learner-dash.courseCard.banners.notEligibleForCert',
description: 'Certificate inelligibility message',
defaultMessage: 'You are not eligible for a certificate.',
},
viewGrades: {
id: 'learner-dash.courseCard.banners.viewGrades',
description: 'Gradses link text',
defaultMessage: 'View grades.',
},
certReady: {
id: 'learner-dash.courseCard.banners.certReady',
description: 'Certificate ready message',
defaultMessage: 'Congratulations. Your certificate is ready.',
},
viewCertificate: {
id: 'learner-dash.courseCard.banners.viewCertificate',
description: 'Certificate link text',
defaultMessage: 'View Certificate.',
},
certMinGrade: {
id: 'learner-dash.courseCard.banners.certMinGrade',
description: 'Passing grade requirement message',
defaultMessage: 'Grade required for a certificate: {minPassingGrade}',
},
downloadCertificate: {
id: 'learner-dash.courseCard.banners.downloadCertificate',
description: 'Certificate download link text',
defaultMessage: 'Download Certificate.',
},
gradeAndCertReadyAfter: {
id: 'learner-dash.courseCard.banners.gradseAndCertReadyAfter',
description: 'Grade and certificate availability date message',
defaultMessage: 'Your grade and certificate will be ready after {availableDate}.',
},
entitlementsUnavailable: {
id: 'learner-dash.courseCard.banners.entitlementsUnavailable',
description: 'Entitlements course message when no sessions are available',
defaultMessage: 'There are no sessions available at the moment. The course team will create new sessions soon. If no sessions appear, please contact {emailLink} for information.',
},
entitlementsExpiringSoon: {
id: 'learner-dash.courseCard.banners.entitlementsExpiringSoon',
description: 'Entitlements course message when the entitlement is expiring soon.',
defaultMessage: 'You must {selectSessionButton} by {changeDeadline} to access the course.',
},
selectSession: {
id: 'learner-dash.courseCard.banners.selectSession',
description: 'Entitlements session selection link text',
defaultMessage: 'select a session',
},
});

View File

@@ -127,7 +127,7 @@ describe('CourseCard hooks', () => {
describe('if verified and ended', () => {
it('returns course ended message with course end date', () => {
runHook({ courseRun: { isFinished: true } });
runHook({ courseRun: { isArchived: true } });
expect(out).toEqual(formatMessage(
messages.courseEnded,
{ endDate: formatDate(courseRunData.endDate) },

View File

@@ -1,23 +0,0 @@
import { useSelector } from 'react-redux';
import selectors from './selectors';
const { courseCard } = selectors;
export const useEmailConfirmationData = () => useSelector(selectors.emailConfirmation);
export const useEnterpriseDashboardData = () => useSelector(selectors.enterpriseDashboards);
export const usePlatformSettingsData = () => useSelector(selectors.platformSettings);
// eslint-disable-next-line
export const useCourseCardData = (selector) => (courseNumber) => useSelector(
(state) => selector(selectors.courseData(state)[courseNumber]),
);
export const useCardCertificateData = useCourseCardData(courseCard.certificates);
export const useCardCourseData = useCourseCardData(courseCard.course);
export const useCardCourseRunData = useCourseCardData(courseCard.courseRun);
export const useCardEnrollmentData = useCourseCardData(courseCard.enrollment);
export const useCardEntitlementsData = useCourseCardData(courseCard.entitlements);
export const useCardGradesData = useCourseCardData(courseCard.grades);
export const useCardProviderData = useCourseCardData(courseCard.provider);
export const useCardRelatedProgramsData = useCourseCardData(courseCard.relatedPrograms);

View File

@@ -9,6 +9,7 @@ const initialState = {
enterpriseDashboards: {},
platformSettings: {},
suggestedCourses: {},
filterState: {},
};
// eslint-disable-next-line no-unused-vars
@@ -16,18 +17,23 @@ const app = createSlice({
name: 'app',
initialState,
reducers: {
loadEnrollments: (state, { payload }) => ({
loadCourses: (state, { payload: { enrollments, entitlements } }) => ({
...state,
enrollments: payload.map(curr => curr.courseRun.courseNumber),
courseData: payload.reduce(
(obj, curr) => ({
...obj,
[curr.courseRun.courseNumber]: curr,
}),
{},
),
enrollments: [
...enrollments.map(curr => curr.courseRun.courseNumber),
...entitlements.map(curr => curr.courseRun.courseNumber),
],
courseData: {
...entitlements.reduce(
(obj, curr) => ({ ...obj, [curr.courseRun.courseNumber]: curr }),
{},
),
...enrollments.reduce(
(obj, curr) => ({ ...obj, [curr.courseRun.courseNumber]: curr }),
{},
),
},
}),
loadEntitlements: (state, { payload }) => ({ ...state, entitlements: payload }),
loadGlobalData: (state, { payload }) => ({
...state,
emailConfirmation: payload.emailConfirmation,

View File

@@ -20,45 +20,66 @@ describe('app reducer', () => {
});
};
describe('action handlers', () => {
test('loadEntitlements loads entitlements from payload', () => {
testAction(
actions.loadEntitlements(testValue),
{ entitlements: testValue },
);
});
describe('loadEnrollments', () => {
const enrollments = [
describe('loadCourses', () => {
const courseIds = [
'course-1',
'course-2',
'course-3',
];
const courseData = {
[enrollments[0]]: {
courseRun: { courseNumber: enrollments[0] },
const entitlementIds = [
'entitlement-course-1',
'entitlement-course-2',
];
const enrollmentData = [
{
courseRun: { courseNumber: courseIds[0] },
course: 1,
some: 'data',
},
[enrollments[1]]: {
courseRun: { courseNumber: enrollments[1] },
{
courseRun: { courseNumber: courseIds[1] },
course: 2,
some: 'other data',
},
[enrollments[2]]: {
courseRun: { courseNumber: enrollments[2] },
{
courseRun: { courseNumber: courseIds[2] },
course: 3,
some: 'still different data',
},
};
const enrollmentData = enrollments.map(v => courseData[v]);
];
const entitlementData = [
{
courseRun: { courseNumber: entitlementIds[0] },
course: 4,
some: 'STILL different data',
},
{
courseRun: { courseNumber: entitlementIds[1] },
course: 5,
some: 'still DIFFERENT data',
},
];
let out;
beforeEach(() => {
out = reducer(testState, actions.loadEnrollments(enrollmentData));
out = reducer(testState, actions.loadCourses({
enrollments: enrollmentData,
entitlements: entitlementData,
}));
});
it('loads list of courseRun ids into enrollments field', () => {
expect(out.enrollments).toEqual(enrollments);
expect(out.enrollments).toEqual([
...courseIds,
...entitlementIds,
]);
});
it('loads object keyed by courseRun ids into courseData field', () => {
expect(out.courseData).toEqual(courseData);
expect(out.courseData).toEqual({
[courseIds[0]]: enrollmentData[0],
[courseIds[1]]: enrollmentData[1],
[courseIds[2]]: enrollmentData[2],
[entitlementIds[0]]: entitlementData[0],
[entitlementIds[1]]: entitlementData[1],
});
});
});
});

View File

@@ -26,11 +26,15 @@ const mkCardSelector = (sel) => (state, courseNumber) => (
sel(courseCardData(state, courseNumber))
);
const dateSixMonthsFromNow = new Date();
dateSixMonthsFromNow.setDate(dateSixMonthsFromNow.getDate() + 180);
export const courseCard = StrictDict({
certificates: mkCardSelector(({ certificates }) => ({
availableDate: certificates.availableDate,
downloadUrl: certificates.downloadUrls?.download,
previewUrl: certificates.downloadUrls?.preview,
certDownloadUrl: certificates.certDownloadUrl,
honorCertDownloadUrl: certificates.honorCertDownloadUrl,
certPreviewUrl: certificates.certPreviewUrl,
isDownloadable: certificates.isDownloadable,
isEarnedButUnavailable: certificates.isEarned && !certificates.isAvailable,
isRestricted: certificates.isRestricted,
@@ -58,13 +62,20 @@ export const courseCard = StrictDict({
lastEnrolled: enrollment.lastEnrollment,
isEnrolled: enrollment.isEnrolled,
})),
entitlements: mkCardSelector(({ entitlements }) => ({
canChange: entitlements.canChange,
entitlementSessions: entitlements.availableSessions,
isEntitlement: entitlements.isEntitlement,
isExpired: entitlements.isExpired,
isFulfilled: entitlements.isFulfilled,
})),
entitlements: mkCardSelector(({ entitlements }) => {
const deadline = new Date(entitlements.changeDeadline);
const showExpirationWarning = deadline > new Date() && deadline <= dateSixMonthsFromNow;
return {
canChange: entitlements.canChange,
entitlementSessions: entitlements.availableSessions,
isEntitlement: entitlements.isEntitlement,
isExpired: entitlements.isExpired,
isFulfilled: entitlements.isFulfilled,
hasSessions: entitlements.availableSessions?.length > 0,
changeDeadline: entitlements.changeDeadline,
showExpirationWarning,
};
}),
grades: mkCardSelector(({ grades }) => ({ isPassing: grades.isPassing })),
provider: mkCardSelector(({ provider }) => ({ name: provider?.name })),
relatedPrograms: mkCardSelector(({ relatedPrograms }) => ({

View File

@@ -15,8 +15,7 @@ import requests from './requests';
export const initialize = () => (dispatch) => (
dispatch(requests.initializeList({
onSuccess: (({ enrollments, entitlements, ...globalData }) => {
dispatch(actions.app.loadEnrollments(enrollments));
dispatch(actions.app.loadEntitlements(entitlements));
dispatch(actions.app.loadCourses({ enrollments, entitlements }));
dispatch(actions.app.loadGlobalData(globalData));
}),
}))
@@ -25,8 +24,7 @@ export const initialize = () => (dispatch) => (
export const refreshList = () => (dispatch) => (
dispatch(requests.initializeList({
onSuccess: (({ enrollments, entitlements, ...globalData }) => {
dispatch(actions.app.loadEnrollments(enrollments));
dispatch(actions.app.loadEntitlements(entitlements));
dispatch(actions.app.loadCourses({ enrollments, entitlements }));
dispatch(actions.app.loadGlobalData(globalData));
}),
}))

View File

@@ -17,7 +17,7 @@ import {
*********************************************************************************/
const initializeList = () => Promise.resolve({
enrollments: fakeData.courseRunData,
entitlements: fakeData.entitlementCourses,
entitlements: fakeData.entitlementData,
...fakeData.globalData,
});

View File

@@ -48,6 +48,9 @@ const logos = {
const pastDate = '11/11/2000';
const futureDate = '11/11/3030';
const soonDate = new Date();
soonDate.setDate(soonDate.getDate() + 60);
const soonDateStr = soonDate.toDateString();
const globalData = {
emailConfirmation: {
@@ -100,7 +103,9 @@ export const genCertificateData = (data = {}) => ({
isAvailable: false,
isEarned: false,
isDownloadable: false,
downloadUrls: null, // { preview, download }
certPreviewUrl: 'edx.com/courses/my-course-url/cert-preview',
certDownloadUrl: 'edx.com/courses/my-course-url/cert-download',
honorCertDownloadUrl: 'edx.com/courses/my-course-url/honor-cert-download',
...data,
});
@@ -224,10 +229,8 @@ export const courseRuns = [
isAvailable: true,
isDownloadable: true,
availableDate: pastDate,
downloadUrls: {
preview: logos.edx,
download: logos.social,
},
certDownloadUrl: logos.social,
certPreviewUrl: logos.edx,
}),
entitlements: { isEntitlement: false },
},
@@ -241,9 +244,7 @@ export const courseRuns = [
isAvailable: true,
isDownloadable: true,
availableDate: pastDate,
downloadUrls: {
download: logos.social,
},
certDownloadUrl: logos.social,
}),
entitlements: { isEntitlement: false },
},
@@ -319,7 +320,6 @@ export const courseRuns = [
export const entitlementCourses = [
{
course: { title: genCourseTitle(100) },
entitlements: {
isEntitlement: true,
availableSessions,
@@ -331,7 +331,17 @@ export const entitlementCourses = [
isExpired: false,
},
}, {
course: { title: genCourseTitle(101) },
entitlements: {
isEntitlement: true,
availableSessions,
isRefundable: true,
isFulfilled: false,
canViewCourse: false,
changeDeadline: soonDateStr,
canChange: true,
isExpired: false,
},
}, {
entitlements: {
isEntitlement: true,
availableSessions,
@@ -343,10 +353,9 @@ export const entitlementCourses = [
isExpired: false,
},
}, {
course: { title: genCourseTitle(102) },
entitlements: {
isEntitlement: true,
availableSessions,
availableSessions: [],
isRefundable: true,
isFulfilled: false,
canViewCourse: false,
@@ -387,13 +396,45 @@ export const courseRunData = courseRuns.map(
...data,
courseRun: genCourseRunData({ ...data.courseRun, courseNumber }),
...iteratedData[providerIndex],
credit: { isPurchased: false, requestStatus: null },
};
},
);
export const entitlementData = entitlementCourses.map(
(data, index) => {
const title = genCourseTitle(100 + index);
const courseNumber = genCourseID(100 + index);
const providerIndex = index % 3;
const iteratedData = [
{
provider: providers.edx,
course: { title, bannerUrl: logos.edx },
relatedPrograms,
},
{
provider: providers.mit,
course: { title, bannerUrl: logos.science },
relatedPrograms: [relatedPrograms[0]],
},
{
provider: null,
course: { title, bannerUrl: logos.social },
relatedPrograms: [],
},
];
return {
...data,
enrollment: genEnrollmentData(),
grades: { isPassing: true },
certificates: genCertificateData(),
courseRun: genCourseRunData({ ...data.courseRun, courseNumber }),
...iteratedData[providerIndex],
};
},
);
export default {
courseRunData,
entitlementCourses,
entitlementData,
globalData,
};

View File

@@ -19,7 +19,7 @@ jest.mock('@edx/frontend-platform/i18n', () => {
const i18n = jest.requireActual('@edx/frontend-platform/i18n');
const PropTypes = jest.requireActual('prop-types');
const { formatMessage } = jest.requireActual('./testUtils');
const formatDate = jest.fn().mockName('useIntl.formatDate');
const formatDate = jest.fn(date => date).mockName('useIntl.formatDate');
return {
...i18n,
intlShape: PropTypes.shape({
@@ -86,6 +86,7 @@ jest.mock('@edx/paragon', () => jest.requireActual('testUtils').mockNestedCompon
Hyperlink: 'Hyperlink',
Icon: 'Icon',
IconButton: 'IconButton',
MailtoLink: 'MailtoLink',
ModalDialog: {
Header: 'ModalDialog.Header',
Body: 'ModalDialog.Body',

View File

@@ -80,7 +80,8 @@ const mockApi = () => {
resolveFns.init = {
success: () => resolve({
enrollments: fakeData.courseRunData,
entitlements: fakeData.entitlementCourses,
entitlements: fakeData.entitlementData,
...fakeData.globalData,
}),
};
}));
@@ -148,7 +149,6 @@ describe('ESG app integration tests', () => {
);
cardDetails = inspector.get.card.details(card);
console.log({ enrollment: courseData.enrollment });
[
courseData.provider.name,
courseNumber,