richie-education 2.29.3-dev50 → 2.29.3-dev52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/js/hooks/useCourseRunOrder/index.tsx +1 -1
- package/js/hooks/useCreditCards/index.spec.tsx +3 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.spec.tsx +5 -10
- package/js/widgets/SyllabusCourseRunsList/components/CourseRunItemWithEnrollment/index.product.spec.tsx +40 -9
- package/js/widgets/SyllabusCourseRunsList/components/CourseRunItemWithEnrollment/index.tsx +9 -7
- package/package.json +1 -1
|
@@ -21,7 +21,7 @@ const useCourseRunOrder = (courseRun: CourseRun) => {
|
|
|
21
21
|
product_id: resourceLinkResources?.product,
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
return { item: orders
|
|
24
|
+
return { item: orders?.[0], states: { fetching, isFetched } };
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
export default useCourseRunOrder;
|
|
@@ -145,7 +145,9 @@ describe('useCreditCards', () => {
|
|
|
145
145
|
responseDeferred.resolve({});
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
await waitFor(() => {
|
|
149
|
+
expect(result.current.states.tokenizing).toBe(false);
|
|
150
|
+
});
|
|
149
151
|
expect(result.current.states.isPending).toBe(false);
|
|
150
152
|
expect(result.current.states.error).toBe(undefined);
|
|
151
153
|
});
|
|
@@ -995,10 +995,7 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
995
995
|
order.payment_schedule![1].state = PaymentScheduleState.REFUSED;
|
|
996
996
|
|
|
997
997
|
const formatPrice = (price: number, currency: string) =>
|
|
998
|
-
new Intl.NumberFormat('en', {
|
|
999
|
-
currency,
|
|
1000
|
-
style: 'currency',
|
|
1001
|
-
}).format(price);
|
|
998
|
+
new Intl.NumberFormat('en', { currency, style: 'currency' }).format(price);
|
|
1002
999
|
|
|
1003
1000
|
const { product } = mockCourseProductWithOrder(order);
|
|
1004
1001
|
fetchMock.get(
|
|
@@ -1038,12 +1035,10 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
1038
1035
|
|
|
1039
1036
|
// Click on pay button.
|
|
1040
1037
|
const payButton = screen.getByTestId('order-payment-retry-modal-submit-button');
|
|
1041
|
-
expect(payButton
|
|
1038
|
+
expect(payButton).toHaveTextContent(
|
|
1042
1039
|
'Pay ' +
|
|
1043
|
-
formatPrice(failedInstallment.amount, failedInstallment.currency).
|
|
1044
|
-
|
|
1045
|
-
' ',
|
|
1046
|
-
),
|
|
1040
|
+
formatPrice(failedInstallment.amount, failedInstallment.currency).replaceAll(/\s/g, ' '),
|
|
1041
|
+
{ normalizeWhitespace: true },
|
|
1047
1042
|
);
|
|
1048
1043
|
await user.click(payButton);
|
|
1049
1044
|
// Pay via mocked payment interface
|
|
@@ -1054,7 +1049,7 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
1054
1049
|
expect(screen.queryByText('Retry payment')).not.toBeInTheDocument();
|
|
1055
1050
|
|
|
1056
1051
|
// Success modal is shown, close it.
|
|
1057
|
-
screen.
|
|
1052
|
+
await screen.findByText('Payment successful');
|
|
1058
1053
|
screen.getByText('The payment was successful');
|
|
1059
1054
|
const okButton = screen.getByRole('button', { name: 'Ok' });
|
|
1060
1055
|
await user.click(okButton);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { screen, waitFor } from '@testing-library/react';
|
|
2
2
|
import fetchMock from 'fetch-mock';
|
|
3
|
+
import { faker } from '@faker-js/faker';
|
|
3
4
|
import {
|
|
4
5
|
CourseRunFactory,
|
|
5
6
|
RichieContextFactory as mockRichieContextFactory,
|
|
@@ -11,12 +12,12 @@ import { render } from 'utils/test/render';
|
|
|
11
12
|
import {
|
|
12
13
|
CourseLightFactory,
|
|
13
14
|
CredentialOrderFactory,
|
|
14
|
-
EnrollmentFactory,
|
|
15
15
|
ProductFactory,
|
|
16
16
|
} from 'utils/test/factories/joanie';
|
|
17
17
|
import { setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
|
|
18
18
|
import { mockPaginatedResponse } from 'utils/test/mockPaginatedResponse';
|
|
19
19
|
import { expectNoSpinner } from 'utils/test/expectSpinner';
|
|
20
|
+
import { ACTIVE_ORDER_STATES, PURCHASABLE_ORDER_STATES } from 'types/Joanie';
|
|
20
21
|
import CourseRunItemWithEnrollment from '.';
|
|
21
22
|
|
|
22
23
|
jest.mock('utils/context', () => ({
|
|
@@ -61,10 +62,9 @@ describe("CourseRunItemWithEnrollment for joanie's product's course run", () =>
|
|
|
61
62
|
expect(screen.queryByRole('link')).not.toBeInTheDocument();
|
|
62
63
|
});
|
|
63
64
|
|
|
64
|
-
it('should not render enrollment information when user
|
|
65
|
+
it('should not render enrollment information when user has no order for the product', async () => {
|
|
65
66
|
const course = CourseLightFactory().one();
|
|
66
67
|
const product = ProductFactory().one();
|
|
67
|
-
const order = CredentialOrderFactory().one();
|
|
68
68
|
const user = UserFactory().one();
|
|
69
69
|
const courseRun = CourseRunFactory({
|
|
70
70
|
title: 'run',
|
|
@@ -73,6 +73,39 @@ describe("CourseRunItemWithEnrollment for joanie's product's course run", () =>
|
|
|
73
73
|
resource_link: `https://joanie.endpoint/api/v1.0/courses/${course.code}/products/${product.id}`,
|
|
74
74
|
}).one();
|
|
75
75
|
|
|
76
|
+
fetchMock.get(
|
|
77
|
+
`https://joanie.endpoint/api/v1.0/orders/?course_code=${course.code}&product_id=${product.id}`,
|
|
78
|
+
mockPaginatedResponse([], 0, false),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
render(<CourseRunItemWithEnrollment item={courseRun} />, {
|
|
82
|
+
wrapper: BaseJoanieAppWrapper,
|
|
83
|
+
queryOptions: { client: createTestQueryClient({ user }) },
|
|
84
|
+
});
|
|
85
|
+
await expectNoSpinner();
|
|
86
|
+
|
|
87
|
+
// Only dates should be displayed.
|
|
88
|
+
screen.getByText('Run, from Jan 01, 2023 to Dec 31, 2023');
|
|
89
|
+
expect(fetchMock.called()).toBe(true);
|
|
90
|
+
const link = screen.queryByTitle('Go to course') as HTMLAnchorElement;
|
|
91
|
+
expect(link).not.toBeInTheDocument();
|
|
92
|
+
expect(screen.queryByLabelText('You are enrolled in this course run')).not.toBeInTheDocument();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should not render enrollment information when user has no active order for the product', async () => {
|
|
96
|
+
const course = CourseLightFactory().one();
|
|
97
|
+
const product = ProductFactory().one();
|
|
98
|
+
const user = UserFactory().one();
|
|
99
|
+
const order = CredentialOrderFactory({
|
|
100
|
+
state: faker.helpers.arrayElement(PURCHASABLE_ORDER_STATES),
|
|
101
|
+
}).one();
|
|
102
|
+
const courseRun = CourseRunFactory({
|
|
103
|
+
title: 'run',
|
|
104
|
+
start: new Date('2023-01-01').toISOString(),
|
|
105
|
+
end: new Date('2023-12-31').toISOString(),
|
|
106
|
+
resource_link: `https://joanie.endpoint/api/v1.0/courses/${course.code}/products/${product.id}`,
|
|
107
|
+
}).one();
|
|
108
|
+
|
|
76
109
|
fetchMock.get(
|
|
77
110
|
`https://joanie.endpoint/api/v1.0/orders/?course_code=${course.code}&product_id=${product.id}`,
|
|
78
111
|
mockPaginatedResponse([order], 1, false),
|
|
@@ -84,20 +117,19 @@ describe("CourseRunItemWithEnrollment for joanie's product's course run", () =>
|
|
|
84
117
|
});
|
|
85
118
|
await expectNoSpinner();
|
|
86
119
|
|
|
87
|
-
// Only dates should
|
|
120
|
+
// Only dates should be displayed.
|
|
88
121
|
screen.getByText('Run, from Jan 01, 2023 to Dec 31, 2023');
|
|
89
122
|
expect(fetchMock.called()).toBe(true);
|
|
90
123
|
const link = screen.queryByTitle('Go to course') as HTMLAnchorElement;
|
|
91
|
-
expect(link).toBeInTheDocument();
|
|
92
|
-
expect(link.href).toBe(`https://localhost/en/dashboard/courses/orders/${order.id}`);
|
|
124
|
+
expect(link).not.toBeInTheDocument();
|
|
93
125
|
expect(screen.queryByLabelText('You are enrolled in this course run')).not.toBeInTheDocument();
|
|
94
126
|
});
|
|
95
127
|
|
|
96
|
-
it('should render enrollment information when user
|
|
128
|
+
it('should render enrollment information when user has an active order for the product', async () => {
|
|
97
129
|
const course = CourseLightFactory().one();
|
|
98
130
|
const product = ProductFactory().one();
|
|
99
131
|
const order = CredentialOrderFactory({
|
|
100
|
-
|
|
132
|
+
state: faker.helpers.arrayElement(ACTIVE_ORDER_STATES),
|
|
101
133
|
}).one();
|
|
102
134
|
const user = UserFactory().one();
|
|
103
135
|
const courseRun = CourseRunFactory({
|
|
@@ -121,7 +153,6 @@ describe("CourseRunItemWithEnrollment for joanie's product's course run", () =>
|
|
|
121
153
|
expect(screen.queryByText('loading...')).not.toBeInTheDocument();
|
|
122
154
|
});
|
|
123
155
|
|
|
124
|
-
// Only dates should have been displayed.
|
|
125
156
|
screen.getByText('Run, from Jan 01, 2023 to Dec 31, 2023');
|
|
126
157
|
expect(fetchMock.called()).toBe(true);
|
|
127
158
|
|
|
@@ -8,6 +8,8 @@ import { getDashboardBasename } from 'widgets/Dashboard/hooks/useDashboardRouter
|
|
|
8
8
|
import { LearnerDashboardPaths } from 'widgets/Dashboard/utils/learnerRoutesPaths';
|
|
9
9
|
import useCourseRunOrder from 'hooks/useCourseRunOrder';
|
|
10
10
|
import { Spinner } from 'components/Spinner';
|
|
11
|
+
import { OrderHelper } from 'utils/OrderHelper';
|
|
12
|
+
import { extractResourceMetadata } from 'api/lms/joanie';
|
|
11
13
|
|
|
12
14
|
const messages = defineMessages({
|
|
13
15
|
goToCourse: {
|
|
@@ -33,6 +35,8 @@ type Props = {
|
|
|
33
35
|
|
|
34
36
|
const CourseRunItemWithEnrollment = ({ item }: Props) => {
|
|
35
37
|
const intl = useIntl();
|
|
38
|
+
const resourceLinkResources = extractResourceMetadata(item.resource_link);
|
|
39
|
+
const isProduct = !!(resourceLinkResources?.course && resourceLinkResources?.product);
|
|
36
40
|
const {
|
|
37
41
|
item: order,
|
|
38
42
|
states: { isFetched },
|
|
@@ -43,14 +47,12 @@ const CourseRunItemWithEnrollment = ({ item }: Props) => {
|
|
|
43
47
|
|
|
44
48
|
const { enrollmentIsActive: courseRunEnrollmentIsActive } = useCourseEnrollment(
|
|
45
49
|
item.resource_link,
|
|
46
|
-
|
|
50
|
+
!isProduct,
|
|
47
51
|
);
|
|
48
52
|
|
|
49
|
-
// user is
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
});
|
|
53
|
-
const enrollmentIsActive = !!(courseRunEnrollmentIsActive || productEnrollmentIsActive);
|
|
53
|
+
// user is enrolled to a product if there is an active order for the product
|
|
54
|
+
const orderIsActive = OrderHelper.isActive(order);
|
|
55
|
+
const enrollmentIsActive = !!(courseRunEnrollmentIsActive || orderIsActive);
|
|
54
56
|
|
|
55
57
|
if (!isFetched) {
|
|
56
58
|
return <Spinner />;
|
|
@@ -58,7 +60,7 @@ const CourseRunItemWithEnrollment = ({ item }: Props) => {
|
|
|
58
60
|
|
|
59
61
|
return (
|
|
60
62
|
<>
|
|
61
|
-
{enrollmentIsActive
|
|
63
|
+
{enrollmentIsActive ? (
|
|
62
64
|
// eslint-disable-next-line jsx-a11y/control-has-associated-label
|
|
63
65
|
<a href={courseUrl} title={intl.formatMessage(messages.goToCourse)}>
|
|
64
66
|
<CourseRunItem item={item} />
|