richie-education 2.28.2-dev39 → 2.28.2-dev58
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/.eslintrc.json +11 -2
- package/i18n/locales/ar-SA.json +209 -125
- package/i18n/locales/es-ES.json +210 -126
- package/i18n/locales/fa-IR.json +209 -125
- package/i18n/locales/fr-CA.json +209 -125
- package/i18n/locales/fr-FR.json +209 -125
- package/i18n/locales/ko-KR.json +209 -125
- package/i18n/locales/pt-PT.json +212 -128
- package/i18n/locales/ru-RU.json +209 -125
- package/i18n/locales/vi-VN.json +209 -125
- package/js/api/joanie.ts +14 -17
- package/js/api/lms/dummy.ts +1 -12
- package/js/components/ContractFrame/AbstractContractFrame.spec.tsx +16 -9
- package/js/components/ContractFrame/AbstractContractFrame.tsx +32 -25
- package/js/components/ContractFrame/LearnerContractFrame.tsx +2 -2
- package/js/components/ContractFrame/_styles.scss +6 -14
- package/js/components/CreditCardSelector/index.spec.tsx +7 -7
- package/js/components/CreditCardSelector/index.tsx +2 -2
- package/js/components/DownloadContractButton/index.spec.tsx +1 -1
- package/js/components/OpenEdxFullNameForm/index.spec.tsx +229 -0
- package/js/components/OpenEdxFullNameForm/index.tsx +7 -7
- package/js/components/PaymentInterfaces/LyraPopIn.tsx +2 -2
- package/js/components/PaymentInterfaces/PayplugLightbox.tsx +1 -1
- package/js/components/PaymentInterfaces/__mocks__/index.tsx +1 -4
- package/js/components/PaymentInterfaces/types.ts +5 -2
- package/js/components/PurchaseButton/index.spec.tsx +69 -37
- package/js/components/SaleTunnel/AddressSelector/index.spec.tsx +2 -1
- package/js/components/SaleTunnel/CertificateSaleTunnel/index.tsx +2 -2
- package/js/components/SaleTunnel/CredentialSaleTunnel/index.tsx +6 -10
- package/js/components/SaleTunnel/GenericSaleTunnel.tsx +75 -41
- package/js/components/SaleTunnel/SaleTunnelInformation/index.tsx +0 -30
- package/js/components/SaleTunnel/SaleTunnelSavePaymentMethod/_styles.scss +12 -0
- package/js/components/SaleTunnel/SaleTunnelSavePaymentMethod/index.tsx +160 -0
- package/js/components/SaleTunnel/SaleTunnelSuccess/index.tsx +15 -29
- package/js/components/SaleTunnel/Sponsors/SaleTunnelSponsors.tsx +5 -0
- package/js/components/SaleTunnel/SubscriptionButton/_styles.scss +7 -0
- package/js/components/SaleTunnel/SubscriptionButton/index.tsx +202 -0
- package/js/components/SaleTunnel/_styles.scss +10 -1
- package/js/components/SaleTunnel/hooks/useTerms.tsx +0 -77
- package/js/components/SaleTunnel/index.credential.spec.tsx +12 -21
- package/js/components/SaleTunnel/index.full-process.spec.tsx +110 -48
- package/js/components/SaleTunnel/index.spec.tsx +330 -779
- package/js/components/SignContractButton/index.omniscientOrders.spec.tsx +16 -11
- package/js/components/SignContractButton/index.spec.tsx +16 -20
- package/js/components/SignContractButton/index.tsx +3 -1
- package/js/hooks/useCreditCards/index.spec.tsx +70 -6
- package/js/hooks/useCreditCards/index.ts +49 -11
- package/js/hooks/useOrders/index.spec.tsx +322 -0
- package/js/hooks/{useOrders.ts → useOrders/index.ts} +40 -14
- package/js/hooks/useProductOrder/index.spec.tsx +77 -60
- package/js/hooks/useProductOrder/index.tsx +2 -2
- package/js/hooks/useResources/useResourcesRoot.ts +1 -0
- package/js/pages/DashboardCreditCardsManagement/CreditCardBrandLogo.spec.tsx +1 -1
- package/js/pages/DashboardCreditCardsManagement/CreditCardBrandLogo.tsx +4 -2
- package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.spec.tsx +8 -5
- package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.spec.tsx +8 -9
- package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.spec.tsx +1 -1
- package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.tsx +1 -6
- package/js/settings/settings.test.ts +11 -2
- package/js/translations/ar-SA.json +1 -1
- package/js/translations/es-ES.json +1 -1
- package/js/translations/fa-IR.json +1 -1
- package/js/translations/fr-CA.json +1 -1
- package/js/translations/fr-FR.json +1 -1
- package/js/translations/ko-KR.json +1 -1
- package/js/translations/pt-PT.json +1 -1
- package/js/translations/ru-RU.json +1 -1
- package/js/translations/vi-VN.json +1 -1
- package/js/types/Joanie.ts +49 -34
- package/js/utils/OrderHelper/index.ts +38 -42
- package/js/utils/search/getSuggestionsSection/index.spec.ts +3 -2
- package/js/utils/test/factories/joanie.ts +36 -51
- package/js/widgets/Dashboard/components/DashboardItem/CourseEnrolling/index.tsx +8 -18
- package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.spec.tsx +26 -32
- package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.tsx +11 -6
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.spec.tsx +7 -6
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +9 -10
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.spec.tsx +3 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.useUnionResource.cache.spec.tsx +6 -7
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentDetailsModal/index.tsx +28 -8
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentRetryModal/index.tsx +4 -6
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateLearnerMessage/index.spec.tsx +18 -71
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateLearnerMessage/index.tsx +34 -35
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateMessage/index.tsx +27 -24
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateTeacherMessage/index.spec.tsx +18 -73
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateTeacherMessage/index.tsx +32 -16
- package/js/widgets/Dashboard/components/DashboardOrderLoader/index.tsx +3 -11
- package/js/widgets/Dashboard/components/Signature/SignatureDummy.tsx +25 -3
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/EnrollableCourseRunList.tsx +2 -6
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/index.spec.tsx +7 -14
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseRunItem/index.spec.tsx +7 -5
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseRunItem/index.tsx +5 -7
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +242 -332
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +12 -13
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +10 -21
- package/js/widgets/SyllabusCourseRunsList/components/CourseRunEnrollment/index.joanie.spec.tsx +2 -2
- package/package.json +27 -27
- package/scss/components/_index.scss +2 -1
- package/js/components/PaymentButton/_styles.scss +0 -27
- package/js/components/SaleTunnel/GenericPaymentButton/index.tsx +0 -333
- package/js/components/SaleTunnel/SaleTunnelNotValidated/index.tsx +0 -70
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/ProductSignatureHeader/index.tsx +0 -41
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { screen, within } from '@testing-library/react';
|
|
1
|
+
import { act, screen, within } from '@testing-library/react';
|
|
2
2
|
import fetchMock from 'fetch-mock';
|
|
3
3
|
import queryString from 'query-string';
|
|
4
4
|
import userEvent from '@testing-library/user-event';
|
|
5
5
|
import countries from 'i18n-iso-countries';
|
|
6
|
+
import { getAllByRole } from '@testing-library/dom';
|
|
6
7
|
import {
|
|
7
8
|
RichieContextFactory as mockRichieContextFactory,
|
|
8
9
|
PacedCourseFactory,
|
|
@@ -13,11 +14,14 @@ import { setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
|
|
|
13
14
|
import CourseProductItem from 'widgets/SyllabusCourseRunsList/components/CourseProductItem';
|
|
14
15
|
import {
|
|
15
16
|
AddressFactory,
|
|
16
|
-
|
|
17
|
-
PaymentInstallmentFactory,
|
|
17
|
+
ContractFactory,
|
|
18
18
|
CourseProductRelationFactory,
|
|
19
|
+
CredentialOrderFactory,
|
|
20
|
+
CreditCardFactory,
|
|
21
|
+
PaymentFactory,
|
|
22
|
+
PaymentInstallmentFactory,
|
|
19
23
|
} from 'utils/test/factories/joanie';
|
|
20
|
-
import {
|
|
24
|
+
import { CourseRun, NOT_CANCELED_ORDER_STATES, OrderState } from 'types/Joanie';
|
|
21
25
|
import { Priority } from 'types';
|
|
22
26
|
import { expectMenuToBeClosed, expectMenuToBeOpen } from 'utils/test/Cunningham';
|
|
23
27
|
import { changeSelect } from 'components/Form/test-utils';
|
|
@@ -26,6 +30,7 @@ import { OpenEdxApiProfileFactory } from 'utils/test/factories/openEdx';
|
|
|
26
30
|
import { User } from 'types/User';
|
|
27
31
|
import { OpenEdxApiProfile } from 'types/openEdx';
|
|
28
32
|
import { createTestQueryClient } from 'utils/test/createTestQueryClient';
|
|
33
|
+
import { Deferred } from 'utils/test/deferred';
|
|
29
34
|
|
|
30
35
|
jest.mock('utils/context', () => ({
|
|
31
36
|
__esModule: true,
|
|
@@ -54,6 +59,12 @@ describe('SaleTunnel', () => {
|
|
|
54
59
|
let openApiEdxProfile: OpenEdxApiProfile;
|
|
55
60
|
setupJoanieSession();
|
|
56
61
|
|
|
62
|
+
const dateFormatter = Intl.DateTimeFormat('en', {
|
|
63
|
+
day: '2-digit',
|
|
64
|
+
month: 'short',
|
|
65
|
+
year: 'numeric',
|
|
66
|
+
});
|
|
67
|
+
|
|
57
68
|
const priceFormatter = (currency: string, price: number) =>
|
|
58
69
|
new Intl.NumberFormat('en', {
|
|
59
70
|
currency,
|
|
@@ -81,13 +92,14 @@ describe('SaleTunnel', () => {
|
|
|
81
92
|
fetchMock.get(`https://auth.test/api/v1.0/user/me`, richieUser);
|
|
82
93
|
});
|
|
83
94
|
|
|
84
|
-
it('tests the entire process of
|
|
95
|
+
it('tests the entire process of subscribing to a credential product', async () => {
|
|
85
96
|
/**
|
|
86
97
|
* Initialization.
|
|
87
98
|
*/
|
|
88
|
-
const
|
|
99
|
+
const course = PacedCourseFactory().one();
|
|
100
|
+
const relation = CourseProductRelationFactory({ course }).one();
|
|
89
101
|
const paymentSchedule = PaymentInstallmentFactory().many(2);
|
|
90
|
-
const { product
|
|
102
|
+
const { product } = relation;
|
|
91
103
|
|
|
92
104
|
fetchMock.get(
|
|
93
105
|
`https://joanie.endpoint/api/v1.0/courses/${course.code}/products/${product.id}/`,
|
|
@@ -101,22 +113,16 @@ describe('SaleTunnel', () => {
|
|
|
101
113
|
const orderQueryParameters = {
|
|
102
114
|
product_id: product.id,
|
|
103
115
|
course_code: course.code,
|
|
104
|
-
state:
|
|
116
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
105
117
|
};
|
|
106
118
|
fetchMock.get(
|
|
107
119
|
`https://joanie.endpoint/api/v1.0/orders/?${queryString.stringify(orderQueryParameters)}`,
|
|
108
120
|
[],
|
|
109
121
|
);
|
|
110
122
|
|
|
111
|
-
render(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
course={PacedCourseFactory({ id: course.id, code: course.code }).one()}
|
|
115
|
-
/>,
|
|
116
|
-
{
|
|
117
|
-
queryOptions: { client: createTestQueryClient({ user: richieUser }) },
|
|
118
|
-
},
|
|
119
|
-
);
|
|
123
|
+
render(<CourseProductItem productId={product.id} course={course} />, {
|
|
124
|
+
queryOptions: { client: createTestQueryClient({ user: richieUser }) },
|
|
125
|
+
});
|
|
120
126
|
|
|
121
127
|
// Wait for product information to be fetched
|
|
122
128
|
await screen.findByRole('heading', { level: 3, name: product.title });
|
|
@@ -216,10 +222,11 @@ describe('SaleTunnel', () => {
|
|
|
216
222
|
|
|
217
223
|
// - User fulfills address fields
|
|
218
224
|
const address = AddressFactory({ is_main: true }).one();
|
|
219
|
-
fetchMock
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
225
|
+
fetchMock
|
|
226
|
+
.post('https://joanie.endpoint/api/v1.0/addresses/', address)
|
|
227
|
+
.get('https://joanie.endpoint/api/v1.0/addresses/', [address], {
|
|
228
|
+
overwriteRoutes: true,
|
|
229
|
+
});
|
|
223
230
|
|
|
224
231
|
await user.type($titleField, address.title);
|
|
225
232
|
await user.type($firstnameField, address.first_name);
|
|
@@ -235,16 +242,23 @@ describe('SaleTunnel', () => {
|
|
|
235
242
|
).toBeInTheDocument();
|
|
236
243
|
|
|
237
244
|
/**
|
|
238
|
-
* Make sure
|
|
245
|
+
* Make sure the payment schedule is displayed.
|
|
239
246
|
*/
|
|
240
|
-
screen.getByRole('heading', {
|
|
241
|
-
|
|
247
|
+
screen.getByRole('heading', { name: 'Payment schedule' });
|
|
248
|
+
paymentSchedule.forEach((installment, index) => {
|
|
249
|
+
const row = screen.getByTestId(installment.id);
|
|
250
|
+
const cells = getAllByRole(row, 'cell');
|
|
251
|
+
expect(cells).toHaveLength(4);
|
|
252
|
+
expect(cells[0]).toHaveTextContent((index + 1).toString());
|
|
253
|
+
expect(cells[1]).toHaveTextContent(
|
|
254
|
+
priceFormatter(installment.currency, installment.amount).replace(/(\u202F|\u00a0)/g, ' '),
|
|
255
|
+
);
|
|
256
|
+
expect(cells[2]).toHaveTextContent(
|
|
257
|
+
`Withdrawn on ${dateFormatter.format(new Date(installment.due_date))}`,
|
|
258
|
+
);
|
|
259
|
+
expect(cells[3]).toHaveTextContent(new RegExp(installment.state, 'i'));
|
|
242
260
|
});
|
|
243
|
-
screen.getByText('Use another credit card during payment');
|
|
244
261
|
|
|
245
|
-
/**
|
|
246
|
-
* Make sure the total is the correct one.
|
|
247
|
-
*/
|
|
248
262
|
const $totalAmount = screen.getByTestId('sale-tunnel__total__amount');
|
|
249
263
|
expect($totalAmount).toHaveTextContent(
|
|
250
264
|
'Total' +
|
|
@@ -252,34 +266,84 @@ describe('SaleTunnel', () => {
|
|
|
252
266
|
);
|
|
253
267
|
|
|
254
268
|
/**
|
|
255
|
-
*
|
|
269
|
+
* Subscribe
|
|
256
270
|
*/
|
|
257
|
-
const
|
|
258
|
-
'By checking this box, you accept the General Terms of Sale',
|
|
259
|
-
);
|
|
260
|
-
await user.click($terms);
|
|
261
|
-
|
|
262
|
-
const { payment_info: paymentInfo, ...order } = CredentialOrderWithPaymentFactory().one();
|
|
271
|
+
const order = CredentialOrderFactory({ state: OrderState.TO_SIGN }).one();
|
|
263
272
|
fetchMock
|
|
273
|
+
.post('https://joanie.endpoint/api/v1.0/orders/', order)
|
|
264
274
|
.get(
|
|
265
275
|
`https://joanie.endpoint/api/v1.0/orders/?${queryString.stringify(orderQueryParameters)}`,
|
|
266
276
|
[order],
|
|
267
277
|
{ overwriteRoutes: true },
|
|
268
|
-
)
|
|
269
|
-
.post('https://joanie.endpoint/api/v1.0/orders/', order)
|
|
270
|
-
.patch(`https://joanie.endpoint/api/v1.0/orders/${order.id}/submit/`, {
|
|
271
|
-
paymentInfo,
|
|
272
|
-
})
|
|
273
|
-
.get(`https://joanie.endpoint/api/v1.0/orders/${order.id}/`, {
|
|
274
|
-
...order,
|
|
275
|
-
});
|
|
278
|
+
);
|
|
276
279
|
|
|
277
280
|
const $button = screen.getByRole('button', {
|
|
278
281
|
name: `Subscribe`,
|
|
279
282
|
}) as HTMLButtonElement;
|
|
280
283
|
await user.click($button);
|
|
281
284
|
|
|
282
|
-
|
|
285
|
+
order.state = OrderState.TO_SAVE_PAYMENT_METHOD;
|
|
286
|
+
order.contract = ContractFactory({ student_signed_on: new Date().toISOString() }).one();
|
|
287
|
+
|
|
288
|
+
const checkSignatureDeferred = new Deferred();
|
|
289
|
+
|
|
290
|
+
fetchMock
|
|
291
|
+
.post(`https://joanie.endpoint/api/v1.0/orders/${order.id}/submit_for_signature/`, {
|
|
292
|
+
invitation_link: 'https://dummysignaturebackend.fr/contract/1/sign',
|
|
293
|
+
})
|
|
294
|
+
.post(`https://joanie.endpoint/api/v1.0/signature/notifications/`, 200)
|
|
295
|
+
.get(`https://joanie.endpoint/api/v1.0/orders/${order.id}/`, checkSignatureDeferred.promise, {
|
|
296
|
+
overwriteRoutes: true,
|
|
297
|
+
})
|
|
298
|
+
.get(
|
|
299
|
+
`https://joanie.endpoint/api/v1.0/orders/?${queryString.stringify(orderQueryParameters)}`,
|
|
300
|
+
[order],
|
|
301
|
+
{ overwriteRoutes: true },
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
const $signButton = await screen.findByRole('button', { name: 'Sign' });
|
|
305
|
+
await user.click($signButton);
|
|
306
|
+
|
|
307
|
+
screen.getByRole('heading', { name: 'Signing the contract ...' });
|
|
308
|
+
|
|
309
|
+
// Then the signature check polling should be started
|
|
310
|
+
await screen.findByRole('heading', { name: 'Verifying signature ...' });
|
|
311
|
+
expect(
|
|
312
|
+
screen.getByText(
|
|
313
|
+
'We are waiting for the signature to be validated from our signature platform. It can take up to few minutes. Do not close this page.',
|
|
314
|
+
),
|
|
315
|
+
).toBeInTheDocument();
|
|
316
|
+
expect(screen.getByRole('status')).toBeInTheDocument();
|
|
317
|
+
|
|
318
|
+
await act(async () => {
|
|
319
|
+
checkSignatureDeferred.resolve(order);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Save payment method step
|
|
324
|
+
*/
|
|
325
|
+
const paymentMethod = CreditCardFactory().one();
|
|
326
|
+
order.state = OrderState.PENDING;
|
|
327
|
+
order.credit_card_id = paymentMethod.id;
|
|
328
|
+
fetchMock
|
|
329
|
+
.post('https://joanie.endpoint/api/v1.0/credit-cards/tokenize-card/', PaymentFactory().one())
|
|
330
|
+
.post(`https://joanie.endpoint/api/v1.0/orders/${order.id}/payment-method/`, 200)
|
|
331
|
+
.get(
|
|
332
|
+
'https://joanie.endpoint/api/v1.0/credit-cards/',
|
|
333
|
+
{ results: [paymentMethod] },
|
|
334
|
+
{ overwriteRoutes: true },
|
|
335
|
+
)
|
|
336
|
+
.get(
|
|
337
|
+
`https://joanie.endpoint/api/v1.0/orders/?${queryString.stringify(orderQueryParameters)}`,
|
|
338
|
+
[order],
|
|
339
|
+
{ overwriteRoutes: true },
|
|
340
|
+
);
|
|
341
|
+
await screen.findByRole('heading', { name: 'Define a payment method' });
|
|
342
|
+
screen.getByText('Use another credit card');
|
|
343
|
+
|
|
344
|
+
const $defineButton = screen.getByRole('button', { name: 'Define' });
|
|
345
|
+
await user.click($defineButton);
|
|
346
|
+
|
|
283
347
|
screen.getByText('Payment interface component');
|
|
284
348
|
await user.click(screen.getByTestId('payment-success'));
|
|
285
349
|
|
|
@@ -288,11 +352,9 @@ describe('SaleTunnel', () => {
|
|
|
288
352
|
*/
|
|
289
353
|
|
|
290
354
|
// Make sure the success step is shown.
|
|
291
|
-
expect(screen.queryByTestId('generic-sale-tunnel-payment-step')).not.toBeInTheDocument();
|
|
292
355
|
await screen.findByTestId('generic-sale-tunnel-success-step');
|
|
293
|
-
screen.getByText('
|
|
294
|
-
screen.
|
|
295
|
-
screen.getByRole('link', { name: 'Sign the training contract' });
|
|
356
|
+
screen.getByText('Subscription confirmed!');
|
|
357
|
+
screen.getByRole('link', { name: 'Close' });
|
|
296
358
|
|
|
297
359
|
/**
|
|
298
360
|
* Make sure the product is displayed as bought ( it verifies cache is well updated ).
|