richie-education 2.29.1-dev32 → 2.29.1-dev37
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/components/ContractStatus/index.spec.tsx +1 -1
- package/js/components/ContractStatus/index.tsx +1 -1
- package/js/components/PurchaseButton/index.tsx +9 -27
- package/js/components/SaleTunnel/index.tsx +2 -1
- package/js/pages/DashboardOrderLayout/index.spec.tsx +10 -1
- package/js/utils/ProductHelper/index.spec.ts +322 -166
- package/js/utils/ProductHelper/index.ts +32 -0
- package/js/widgets/Dashboard/components/DashboardItem/Contract/index.spec.tsx +2 -2
- package/js/widgets/Dashboard/components/DashboardItem/Order/CertificateItem/index.tsx +51 -0
- package/js/widgets/Dashboard/components/DashboardItem/Order/ContractItem/index.tsx +52 -0
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemNotResumable.spec.tsx +109 -0
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.spec.tsx +105 -0
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +103 -324
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.spec.tsx +59 -8
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.useUnionResource.cache.spec.tsx +12 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemSavePaymentMethod.spec.tsx +116 -0
- package/js/widgets/Dashboard/components/DashboardItem/Order/Installment/index.tsx +174 -0
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentDetailsModal/index.tsx +2 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrganizationBlock/index.tsx +150 -0
- package/js/widgets/Dashboard/components/DashboardOrderLoader/index.tsx +29 -3
- package/package.json +2 -2
|
@@ -2,6 +2,8 @@ import { IntlShape } from 'react-intl';
|
|
|
2
2
|
import { CourseProductRelation, Product, TargetCourse } from 'types/Joanie';
|
|
3
3
|
import { Maybe } from 'types/utils';
|
|
4
4
|
import { IntlHelper } from 'utils/IntlHelper';
|
|
5
|
+
import * as Joanie from 'types/Joanie';
|
|
6
|
+
import { isOpenedCourseRunCertificate, isOpenedCourseRunCredential } from 'utils/CourseRuns';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Helper class for products
|
|
@@ -45,4 +47,34 @@ export class ProductHelper {
|
|
|
45
47
|
static getActiveOrderGroups(courseProductRelation: CourseProductRelation) {
|
|
46
48
|
return courseProductRelation.order_groups?.filter((orderGroup) => orderGroup.is_active);
|
|
47
49
|
}
|
|
50
|
+
|
|
51
|
+
static hasRemainingSeats(product: Maybe<Product>) {
|
|
52
|
+
if (!product) return false;
|
|
53
|
+
return typeof product?.remaining_order_count !== 'number' || product.remaining_order_count > 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static hasOpenedTargetCourse(product: Maybe<Product>, enrollment?: Maybe<Joanie.Enrollment>) {
|
|
57
|
+
if (!product) return false;
|
|
58
|
+
|
|
59
|
+
if (product.type === Joanie.ProductType.CERTIFICATE) {
|
|
60
|
+
if (!enrollment?.course_run) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
'Unable to check if the certificate product relies on an opened course run without enrollment.',
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return isOpenedCourseRunCertificate(enrollment.course_run.state);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
product.target_courses.length > 0 &&
|
|
71
|
+
product.target_courses.every(({ course_runs }) =>
|
|
72
|
+
course_runs.some((targetCourseRun) => isOpenedCourseRunCredential(targetCourseRun.state)),
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static isPurchasable(product: Maybe<Product>, enrollment?: Maybe<Joanie.Enrollment>) {
|
|
78
|
+
return this.hasOpenedTargetCourse(product, enrollment) && this.hasRemainingSeats(product);
|
|
79
|
+
}
|
|
48
80
|
}
|
|
@@ -122,7 +122,7 @@ describe.each([
|
|
|
122
122
|
expect(await screen.findByText(contract.definition.title)).toBeInTheDocument();
|
|
123
123
|
expect(screen.getByText(contract.order.product_title)).toBeInTheDocument();
|
|
124
124
|
expect(
|
|
125
|
-
screen.getByText('You have to sign this training contract to
|
|
125
|
+
screen.getByText('You have to sign this training contract to finalize your subscription.'),
|
|
126
126
|
).toBeInTheDocument();
|
|
127
127
|
|
|
128
128
|
expect(screen.queryByRole('button', { name: 'Sign' })).toBeInTheDocument();
|
|
@@ -149,7 +149,7 @@ describe.each([
|
|
|
149
149
|
expect(await screen.findByText(contract.definition.title)).toBeInTheDocument();
|
|
150
150
|
expect(screen.getByText(contract.order.product_title)).toBeInTheDocument();
|
|
151
151
|
expect(
|
|
152
|
-
screen.getByText('You have to sign this training contract to
|
|
152
|
+
screen.getByText('You have to sign this training contract to finalize your subscription.'),
|
|
153
153
|
).toBeInTheDocument();
|
|
154
154
|
|
|
155
155
|
expect(screen.queryByRole('link', { name: 'Sign' })).toBeInTheDocument();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { defineMessages, FormattedMessage } from 'react-intl';
|
|
2
|
+
import { CredentialOrder, Product } from 'types/Joanie';
|
|
3
|
+
import { DashboardItemCertificate } from 'widgets/Dashboard/components/DashboardItem/Certificate';
|
|
4
|
+
import { useCertificate } from 'hooks/useCertificates';
|
|
5
|
+
import { Spinner } from 'components/Spinner';
|
|
6
|
+
|
|
7
|
+
const messages = defineMessages({
|
|
8
|
+
loadingCertificate: {
|
|
9
|
+
id: 'components.DashboardItemOrder.CertificateItem.loadingCertificate',
|
|
10
|
+
description: 'Accessible label displayed while certificate is being fetched on the dashboard.',
|
|
11
|
+
defaultMessage: 'Loading certificate...',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
interface DashboardItemOrderCertificateProps {
|
|
16
|
+
order: CredentialOrder;
|
|
17
|
+
product: Product;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const CertificateItem = ({ order, product }: DashboardItemOrderCertificateProps) => {
|
|
21
|
+
if (!order.certificate_id) {
|
|
22
|
+
return (
|
|
23
|
+
<DashboardItemCertificate
|
|
24
|
+
certificateDefinition={product.certificate_definition}
|
|
25
|
+
productType={product.type}
|
|
26
|
+
mode="compact"
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
const certificate = useCertificate(order.certificate_id);
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
{certificate.states.fetching && (
|
|
34
|
+
<Spinner aria-labelledby="loading-certificate">
|
|
35
|
+
<span id="loading-certificate">
|
|
36
|
+
<FormattedMessage {...messages.loadingCertificate} />
|
|
37
|
+
</span>
|
|
38
|
+
</Spinner>
|
|
39
|
+
)}
|
|
40
|
+
{certificate.item && (
|
|
41
|
+
<DashboardItemCertificate
|
|
42
|
+
certificate={certificate.item}
|
|
43
|
+
productType={product.type}
|
|
44
|
+
mode="compact"
|
|
45
|
+
/>
|
|
46
|
+
)}
|
|
47
|
+
</>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default CertificateItem;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { defineMessages, FormattedMessage } from 'react-intl';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { CredentialOrder, Product } from 'types/Joanie';
|
|
4
|
+
import { OrderHelper } from 'utils/OrderHelper';
|
|
5
|
+
import ContractStatus from 'components/ContractStatus';
|
|
6
|
+
import SignContractButton from 'components/SignContractButton';
|
|
7
|
+
|
|
8
|
+
const messages = defineMessages({
|
|
9
|
+
trainingContractTitle: {
|
|
10
|
+
id: 'components.DashboardItemOrder.ContractItem.trainingContractTitle',
|
|
11
|
+
description: 'Title of the training contract section',
|
|
12
|
+
defaultMessage: 'Training contract',
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const ContractItem = ({ order, product }: { order: CredentialOrder; product: Product }) => {
|
|
17
|
+
if (!product?.contract_definition) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const needsSignature = OrderHelper.orderNeedsSignature(order);
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
id="dashboard-item-contract"
|
|
25
|
+
className="dashboard-splitted-card__item"
|
|
26
|
+
data-testid={`dashboard-item-contract-${order.id}`}
|
|
27
|
+
>
|
|
28
|
+
<div
|
|
29
|
+
className={classNames('dashboard-splitted-card__item__title', {
|
|
30
|
+
'dashboard-splitted-card__item__title--dot': needsSignature,
|
|
31
|
+
})}
|
|
32
|
+
>
|
|
33
|
+
<span>
|
|
34
|
+
<FormattedMessage {...messages.trainingContractTitle} />
|
|
35
|
+
</span>
|
|
36
|
+
</div>
|
|
37
|
+
<div className="dashboard-splitted-card__item__description">
|
|
38
|
+
<ContractStatus contract={order.contract} />
|
|
39
|
+
</div>
|
|
40
|
+
<div className="dashboard-splitted-card__item__actions">
|
|
41
|
+
<SignContractButton
|
|
42
|
+
order={order}
|
|
43
|
+
contract={order.contract}
|
|
44
|
+
writable={true}
|
|
45
|
+
className="dashboard-item__button"
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default ContractItem;
|
package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemNotResumable.spec.tsx
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test suite for DashboardItem component with a non-resumable order (inactive and related product
|
|
3
|
+
* is no more purchasable (no remaining seats if order group, some target courses are not opened)).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fetchMock from 'fetch-mock';
|
|
7
|
+
import { render, screen, within } from '@testing-library/react';
|
|
8
|
+
import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
|
|
9
|
+
import { setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
|
|
10
|
+
import { CredentialOrderFactory } from 'utils/test/factories/joanie';
|
|
11
|
+
import { OrderState } from 'types/Joanie';
|
|
12
|
+
import { mockCourseProductWithOrder } from 'utils/test/mockCourseProductWithOrder';
|
|
13
|
+
import { DashboardTest } from 'widgets/Dashboard/components/DashboardTest';
|
|
14
|
+
import { LearnerDashboardPaths } from 'widgets/Dashboard/utils/learnerRoutesPaths';
|
|
15
|
+
import { BaseJoanieAppWrapper } from 'utils/test/wrappers/BaseJoanieAppWrapper';
|
|
16
|
+
|
|
17
|
+
jest.mock('utils/context', () => ({
|
|
18
|
+
__esModule: true,
|
|
19
|
+
default: mockRichieContextFactory({
|
|
20
|
+
authentication: { backend: 'fonzie', endpoint: 'https://demo.endpoint' },
|
|
21
|
+
joanie_backend: { endpoint: 'https://joanie.endpoint' },
|
|
22
|
+
}).one(),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
describe('DashboardItemOrder / Not resumable', () => {
|
|
26
|
+
setupJoanieSession();
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
fetchMock.get(
|
|
29
|
+
'begin:https://joanie.endpoint/api/v1.0/enrollments/',
|
|
30
|
+
{ results: [], next: null, previous: null, count: null },
|
|
31
|
+
{ overwriteRoutes: true },
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
fetchMock.restore();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('non-writable', () => {
|
|
39
|
+
it('renders elements to explain that the order process is not resumable', async () => {
|
|
40
|
+
const order = CredentialOrderFactory({
|
|
41
|
+
state: OrderState.TO_SIGN,
|
|
42
|
+
}).one();
|
|
43
|
+
|
|
44
|
+
fetchMock.get('begin:https://joanie.endpoint/api/v1.0/orders/', {
|
|
45
|
+
results: [order],
|
|
46
|
+
next: null,
|
|
47
|
+
previous: null,
|
|
48
|
+
count: null,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const { product } = mockCourseProductWithOrder(order);
|
|
52
|
+
// Make product no more purchasable
|
|
53
|
+
product.remaining_order_count = 0;
|
|
54
|
+
|
|
55
|
+
render(<DashboardTest initialRoute={LearnerDashboardPaths.COURSES} />, {
|
|
56
|
+
wrapper: BaseJoanieAppWrapper,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const dashboardItem = await screen.findByTestId(`dashboard-item-order-${order.id}`);
|
|
60
|
+
within(dashboardItem).getByRole('heading', { level: 5, name: product.title });
|
|
61
|
+
within(dashboardItem).getByText(
|
|
62
|
+
'The subscription process cannot be resumed. The related training is no more purchasable.',
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// No subitem should be displayed (target course details)
|
|
66
|
+
expect(within(dashboardItem).queryAllByTestId('dashboard-sub-item')).toHaveLength(0);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('writable', () => {
|
|
71
|
+
it('renders elements to explain that the order process is not resumable', async () => {
|
|
72
|
+
const order = CredentialOrderFactory({
|
|
73
|
+
state: OrderState.TO_SAVE_PAYMENT_METHOD,
|
|
74
|
+
}).one();
|
|
75
|
+
|
|
76
|
+
fetchMock.get(
|
|
77
|
+
'https://joanie.endpoint/api/v1.0/orders/',
|
|
78
|
+
{ results: [order], next: null, previous: null, count: null },
|
|
79
|
+
{ overwriteRoutes: true },
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const url = `begin:https://joanie.endpoint/api/v1.0/orders/?`;
|
|
83
|
+
fetchMock.get(url, [order]);
|
|
84
|
+
|
|
85
|
+
const { product } = mockCourseProductWithOrder(order);
|
|
86
|
+
// Make product no more purchasable
|
|
87
|
+
product.remaining_order_count = 0;
|
|
88
|
+
|
|
89
|
+
render(
|
|
90
|
+
<DashboardTest initialRoute={LearnerDashboardPaths.ORDER.replace(':orderId', order.id)} />,
|
|
91
|
+
{
|
|
92
|
+
wrapper: BaseJoanieAppWrapper,
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const dashboardItem = await screen.findByTestId(`dashboard-item-order-${order.id}`);
|
|
97
|
+
within(dashboardItem).getByRole('heading', { level: 5, name: product.title });
|
|
98
|
+
within(dashboardItem).getByText(
|
|
99
|
+
'The subscription process cannot be resumed. The related training is no more purchasable.',
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// No subitem should be displayed (target course details)
|
|
103
|
+
expect(within(dashboardItem).queryAllByTestId('dashboard-sub-item')).toHaveLength(0);
|
|
104
|
+
|
|
105
|
+
// Organization block should not be displayed
|
|
106
|
+
expect(within(dashboardItem).queryByTestId('organization-block')).toBeNull();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { faker } from '@faker-js/faker';
|
|
13
13
|
import fetchMock from 'fetch-mock';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
|
+
import queryString from 'query-string';
|
|
15
16
|
import { DEFAULT_DATE_FORMAT } from 'hooks/useDateFormat';
|
|
16
17
|
import {
|
|
17
18
|
CourseStateFactory,
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
CourseLight,
|
|
32
33
|
CourseRun,
|
|
33
34
|
CredentialOrder,
|
|
35
|
+
NOT_CANCELED_ORDER_STATES,
|
|
34
36
|
OrderState,
|
|
35
37
|
PaymentScheduleState,
|
|
36
38
|
} from 'types/Joanie';
|
|
@@ -257,6 +259,15 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
257
259
|
order.target_courses = [];
|
|
258
260
|
const { product } = mockCourseProductWithOrder(order);
|
|
259
261
|
|
|
262
|
+
const orderQueryParameters = {
|
|
263
|
+
course_code: order.course.code,
|
|
264
|
+
product_id: order.product_id,
|
|
265
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
266
|
+
};
|
|
267
|
+
const queryParams = queryString.stringify(orderQueryParameters);
|
|
268
|
+
const url = `https://joanie.endpoint/api/v1.0/orders/?${queryParams}`;
|
|
269
|
+
fetchMock.get(url, [order]);
|
|
270
|
+
|
|
260
271
|
render(<DashboardItemOrder order={order} writable={true} showDetailsButton={false} />);
|
|
261
272
|
|
|
262
273
|
await screen.findByRole('heading', { level: 5, name: product.title });
|
|
@@ -284,6 +295,15 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
284
295
|
|
|
285
296
|
const { product } = mockCourseProductWithOrder(order);
|
|
286
297
|
|
|
298
|
+
const orderQueryParameters = {
|
|
299
|
+
course_code: order.course.code,
|
|
300
|
+
product_id: order.product_id,
|
|
301
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
302
|
+
};
|
|
303
|
+
const queryParams = queryString.stringify(orderQueryParameters);
|
|
304
|
+
const url = `https://joanie.endpoint/api/v1.0/orders/?${queryParams}`;
|
|
305
|
+
fetchMock.get(url, [order]);
|
|
306
|
+
|
|
287
307
|
render(<DashboardItemOrder order={order} writable={true} showDetailsButton={false} />);
|
|
288
308
|
|
|
289
309
|
await screen.findByRole('heading', { level: 5, name: product.title });
|
|
@@ -349,6 +369,14 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
349
369
|
{ results: [order], next: null, previous: null, count: null },
|
|
350
370
|
{ overwriteRoutes: true },
|
|
351
371
|
);
|
|
372
|
+
const orderQueryParameters = {
|
|
373
|
+
course_code: order.course.code,
|
|
374
|
+
product_id: order.product_id,
|
|
375
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
376
|
+
};
|
|
377
|
+
const queryParams = queryString.stringify(orderQueryParameters);
|
|
378
|
+
const url = `https://joanie.endpoint/api/v1.0/orders/?${queryParams}`;
|
|
379
|
+
fetchMock.get(url, [order]);
|
|
352
380
|
|
|
353
381
|
// The order with an enrollment that will be returned from the API when the orders will be
|
|
354
382
|
// invalided after the click on the Enroll button.
|
|
@@ -432,6 +460,15 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
432
460
|
{ overwriteRoutes: true },
|
|
433
461
|
);
|
|
434
462
|
|
|
463
|
+
const orderQueryParameters = {
|
|
464
|
+
course_code: order.course.code,
|
|
465
|
+
product_id: order.product_id,
|
|
466
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
467
|
+
};
|
|
468
|
+
const queryParams = queryString.stringify(orderQueryParameters);
|
|
469
|
+
const url = `https://joanie.endpoint/api/v1.0/orders/?${queryParams}`;
|
|
470
|
+
fetchMock.get(url, [order]);
|
|
471
|
+
|
|
435
472
|
fetchMock.post('https://joanie.endpoint/api/v1.0/enrollments/', {
|
|
436
473
|
status: HttpStatusCode.INTERNAL_SERVER_ERROR,
|
|
437
474
|
body: 'Internal Server Error',
|
|
@@ -502,6 +539,14 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
502
539
|
{ results: [order], next: null, previous: null, count: null },
|
|
503
540
|
{ overwriteRoutes: true },
|
|
504
541
|
);
|
|
542
|
+
const orderQueryParameters = {
|
|
543
|
+
course_code: order.course.code,
|
|
544
|
+
product_id: order.product_id,
|
|
545
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
546
|
+
};
|
|
547
|
+
const queryParams = queryString.stringify(orderQueryParameters);
|
|
548
|
+
const url = `https://joanie.endpoint/api/v1.0/orders/?${queryParams}`;
|
|
549
|
+
fetchMock.get(url, [order]);
|
|
505
550
|
|
|
506
551
|
// The order with new enrollment that will be returned from the API when the orders will be
|
|
507
552
|
// invalided after the click on the Enroll button.
|
|
@@ -608,6 +653,14 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
608
653
|
{ results: [order], next: null, previous: null, count: null },
|
|
609
654
|
{ overwriteRoutes: true },
|
|
610
655
|
);
|
|
656
|
+
const orderQueryParameters = {
|
|
657
|
+
course_code: order.course.code,
|
|
658
|
+
product_id: order.product_id,
|
|
659
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
660
|
+
};
|
|
661
|
+
const queryParams = queryString.stringify(orderQueryParameters);
|
|
662
|
+
const url = `https://joanie.endpoint/api/v1.0/orders/?${queryParams}`;
|
|
663
|
+
fetchMock.get(url, [order]);
|
|
611
664
|
|
|
612
665
|
const courseRun = order.target_courses[0].course_runs[0];
|
|
613
666
|
const newEnrolledCourseRun = order.target_courses[0].course_runs[1];
|
|
@@ -693,6 +746,14 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
693
746
|
{ results: [order], next: null, previous: null, count: null },
|
|
694
747
|
{ overwriteRoutes: true },
|
|
695
748
|
);
|
|
749
|
+
const orderQueryParameters = {
|
|
750
|
+
course_code: order.course.code,
|
|
751
|
+
product_id: order.product_id,
|
|
752
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
753
|
+
};
|
|
754
|
+
const queryParams = queryString.stringify(orderQueryParameters);
|
|
755
|
+
const url = `https://joanie.endpoint/api/v1.0/orders/?${queryParams}`;
|
|
756
|
+
fetchMock.get(url, [order]);
|
|
696
757
|
|
|
697
758
|
// The order with new enrollment that will be returned from the API when the orders will be
|
|
698
759
|
// invalided after the click on the Enroll button.
|
|
@@ -772,6 +833,14 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
772
833
|
}).many(1),
|
|
773
834
|
}).one();
|
|
774
835
|
const { product } = mockCourseProductWithOrder(order);
|
|
836
|
+
const orderQueryParameters = {
|
|
837
|
+
course_code: order.course.code,
|
|
838
|
+
product_id: order.product_id,
|
|
839
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
840
|
+
};
|
|
841
|
+
const queryParams = queryString.stringify(orderQueryParameters);
|
|
842
|
+
const url = `https://joanie.endpoint/api/v1.0/orders/?${queryParams}`;
|
|
843
|
+
fetchMock.get(url, [order]);
|
|
775
844
|
|
|
776
845
|
render(<DashboardItemOrder order={order} writable={true} showDetailsButton={false} />);
|
|
777
846
|
|
|
@@ -808,6 +877,15 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
808
877
|
|
|
809
878
|
const { product } = mockCourseProductWithOrder(order);
|
|
810
879
|
|
|
880
|
+
const orderQueryParameters = {
|
|
881
|
+
course_code: order.course.code,
|
|
882
|
+
product_id: order.product_id,
|
|
883
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
884
|
+
};
|
|
885
|
+
const queryParams = queryString.stringify(orderQueryParameters);
|
|
886
|
+
const url = `https://joanie.endpoint/api/v1.0/orders/?${queryParams}`;
|
|
887
|
+
fetchMock.get(url, [order]);
|
|
888
|
+
|
|
811
889
|
render(<DashboardItemOrder order={order} writable={true} showDetailsButton={false} />);
|
|
812
890
|
|
|
813
891
|
await screen.findByRole('heading', { level: 5, name: product.title });
|
|
@@ -836,6 +914,15 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
836
914
|
|
|
837
915
|
const { product } = mockCourseProductWithOrder(order);
|
|
838
916
|
|
|
917
|
+
const orderQueryParameters = {
|
|
918
|
+
course_code: order.course.code,
|
|
919
|
+
product_id: order.product_id,
|
|
920
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
921
|
+
};
|
|
922
|
+
const queryParams = queryString.stringify(orderQueryParameters);
|
|
923
|
+
const url = `https://joanie.endpoint/api/v1.0/orders/?${queryParams}`;
|
|
924
|
+
fetchMock.get(url, [order]);
|
|
925
|
+
|
|
839
926
|
render(<DashboardItemOrder order={order} writable={true} showDetailsButton={false} />);
|
|
840
927
|
|
|
841
928
|
await screen.findByRole('heading', { level: 5, name: product.title });
|
|
@@ -852,6 +939,15 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
852
939
|
const order: CredentialOrder = CredentialOrderFactory().one();
|
|
853
940
|
const { product } = mockCourseProductWithOrder(order);
|
|
854
941
|
|
|
942
|
+
const orderQueryParameters = {
|
|
943
|
+
course_code: order.course.code,
|
|
944
|
+
product_id: order.product_id,
|
|
945
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
946
|
+
};
|
|
947
|
+
const queryParams = queryString.stringify(orderQueryParameters);
|
|
948
|
+
const url = `https://joanie.endpoint/api/v1.0/orders/?${queryParams}`;
|
|
949
|
+
fetchMock.get(url, [order]);
|
|
950
|
+
|
|
855
951
|
render(<DashboardItemOrder order={order} writable={true} showDetailsButton={false} />);
|
|
856
952
|
|
|
857
953
|
await screen.findByRole('heading', { level: 5, name: product.title });
|
|
@@ -886,6 +982,15 @@ describe('<DashboardItemOrder/>', () => {
|
|
|
886
982
|
)
|
|
887
983
|
.get(`https://joanie.endpoint/api/v1.0/orders/${order.id}/`, validOrder);
|
|
888
984
|
|
|
985
|
+
const orderQueryParameters = {
|
|
986
|
+
course_code: order.course.code,
|
|
987
|
+
product_id: order.product_id,
|
|
988
|
+
state: NOT_CANCELED_ORDER_STATES,
|
|
989
|
+
};
|
|
990
|
+
const queryParams = queryString.stringify(orderQueryParameters);
|
|
991
|
+
const url = `https://joanie.endpoint/api/v1.0/orders/?${queryParams}`;
|
|
992
|
+
fetchMock.get(url, [order]);
|
|
993
|
+
|
|
889
994
|
order.state = OrderState.FAILED_PAYMENT;
|
|
890
995
|
order.payment_schedule![1].state = PaymentScheduleState.REFUSED;
|
|
891
996
|
|