richie-education 2.25.0-b2.dev103 → 2.25.0-b2.dev110

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.
Files changed (33) hide show
  1. package/js/api/joanie.ts +13 -0
  2. package/js/hooks/useCourseOrders/index.ts +32 -0
  3. package/js/{pages/TeacherDashboardContractsLayout/hooks → hooks}/useDefaultOrganizationId/index.spec.tsx +6 -42
  4. package/js/hooks/useOrganizations/index.ts +4 -4
  5. package/js/hooks/useResources/index.tsx +2 -0
  6. package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.spec.tsx +68 -83
  7. package/js/pages/TeacherDashboardContractsLayout/components/ContractFiltersBar/index.spec.tsx +21 -62
  8. package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractFilters/index.tsx +1 -1
  9. package/js/pages/TeacherDashboardContractsLayout/styles.scss +0 -4
  10. package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.spec.tsx +62 -0
  11. package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.tsx +123 -0
  12. package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnersFiltersBar/index.spec.tsx +70 -0
  13. package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnersFiltersBar/index.tsx +31 -0
  14. package/js/pages/TeacherDashboardCourseLearnersLayout/hooks/useCourseLearnersFilters/index.spec.tsx +158 -0
  15. package/js/pages/TeacherDashboardCourseLearnersLayout/hooks/useCourseLearnersFilters/index.ts +44 -0
  16. package/js/pages/TeacherDashboardCourseLearnersLayout/index.spec.tsx +385 -0
  17. package/js/pages/TeacherDashboardCourseLearnersLayout/index.tsx +141 -0
  18. package/js/settings/index.ts +2 -2
  19. package/js/settings/settings.prod.ts +1 -0
  20. package/js/types/Joanie.ts +47 -0
  21. package/js/utils/OrderHelper/index.ts +5 -1
  22. package/js/utils/test/factories/cunningham.ts +13 -0
  23. package/js/utils/test/factories/joanie.ts +44 -0
  24. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateMessage/index.tsx +8 -2
  25. package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.tsx +1 -1
  26. package/js/widgets/Dashboard/components/FilterOrganization/index.tsx +27 -10
  27. package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/index.spec.tsx +36 -76
  28. package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/utils.ts +6 -1
  29. package/js/widgets/Dashboard/utils/teacherRouteMessages.tsx +23 -0
  30. package/js/widgets/Dashboard/utils/teacherRoutes.tsx +31 -0
  31. package/package.json +1 -1
  32. package/scss/objects/_dashboard.scss +7 -0
  33. /package/js/{pages/TeacherDashboardContractsLayout/hooks → hooks}/useDefaultOrganizationId/index.tsx +0 -0
package/js/api/joanie.ts CHANGED
@@ -190,6 +190,9 @@ export const getRoutes = () => {
190
190
  products: {
191
191
  get: `${baseUrl}/courses/:course_id/products/:id/`,
192
192
  },
193
+ orders: {
194
+ get: `${baseUrl}/courses/:course_id/orders/:id/`,
195
+ },
193
196
  },
194
197
  courseRuns: {
195
198
  get: `${baseUrl}/course-runs/:id/`,
@@ -464,6 +467,16 @@ const API = (): Joanie.API => {
464
467
  return fetchWithJWT(buildApiUrl(ROUTES.courses.products.get, filters)).then(checkStatus);
465
468
  },
466
469
  },
470
+ orders: {
471
+ get: async (filters?: Joanie.CourseOrderResourceQuery) => {
472
+ if (!filters || !filters.course_id) {
473
+ throw new Error('A course_id is required to fetch a course order');
474
+ }
475
+ return fetchWithJWT(buildApiUrl(ROUTES.courses.orders.get, filters), {
476
+ method: 'GET',
477
+ }).then(checkStatus);
478
+ },
479
+ },
467
480
  },
468
481
  courseRuns: {
469
482
  get: (filters: Joanie.CourseRunFilters) => {
@@ -0,0 +1,32 @@
1
+ import { defineMessages } from 'react-intl';
2
+ import { useJoanieApi } from 'contexts/JoanieApiContext';
3
+ import { useResource, useResources, UseResourcesProps } from 'hooks/useResources';
4
+ import { API, CourseOrderResourceQuery, NestedCourseOrder } from 'types/Joanie';
5
+
6
+ const messages = defineMessages({
7
+ errorGet: {
8
+ id: 'hooks.useCourseOrders.errorSelect',
9
+ description: 'Error message shown to the user when orders fetch request fails.',
10
+ defaultMessage: 'An error occurred while fetching orders. Please retry later.',
11
+ },
12
+ errorNotFound: {
13
+ id: 'hooks.useCourseOrders.errorNotFound',
14
+ description: 'Error message shown to the user when no orders matches.',
15
+ defaultMessage: 'Cannot find orders',
16
+ },
17
+ });
18
+
19
+ const props: UseResourcesProps<
20
+ NestedCourseOrder,
21
+ CourseOrderResourceQuery,
22
+ API['courses']['orders']
23
+ > = {
24
+ queryKey: ['courses', 'orders'],
25
+ apiInterface: () => useJoanieApi().courses.orders,
26
+ session: true,
27
+ messages,
28
+ };
29
+
30
+ export const useCourseOrder = useResource(props);
31
+
32
+ export const useCourseOrders = useResources(props);
@@ -1,30 +1,21 @@
1
- import { QueryClientProvider } from '@tanstack/react-query';
2
- import { PropsWithChildren } from 'react';
3
- import { IntlProvider } from 'react-intl';
4
- import { MemoryRouter, Route, Routes } from 'react-router-dom';
5
1
  import { renderHook, waitFor } from '@testing-library/react';
6
2
  import fetchMock from 'fetch-mock';
7
3
  import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
8
- import JoanieSessionProvider from 'contexts/SessionContext/JoanieSessionProvider';
9
- import { createTestQueryClient } from 'utils/test/createTestQueryClient';
10
4
  import { OrganizationFactory } from 'utils/test/factories/joanie';
11
5
  import { Organization } from 'types/Joanie';
6
+ import { JoanieAppWrapper, setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
12
7
  import useDefaultOrganizationId from '.';
13
8
 
14
9
  jest.mock('utils/context', () => ({
15
10
  __esModule: true,
16
11
  default: mockRichieContextFactory({
17
12
  authentication: { backend: 'fonzie', endpoint: 'https://demo.test' },
18
- joanie_backend: { endpoint: 'https://joanie.test' },
13
+ joanie_backend: { endpoint: 'https://joanie.endpoint' },
19
14
  }).one(),
20
15
  }));
21
16
 
22
- interface WrapperProps {
23
- routePath: string;
24
- initialEntry: string;
25
- }
26
-
27
17
  describe('useDefaultOrganizationId', () => {
18
+ setupJoanieSession();
28
19
  const organizations: {
29
20
  routeOrganization: Organization;
30
21
  queryOrganization: Organization;
@@ -35,33 +26,6 @@ describe('useDefaultOrganizationId', () => {
35
26
  userOrganizationList: OrganizationFactory().many(2),
36
27
  };
37
28
 
38
- const Wrapper = ({ children, routePath, initialEntry }: PropsWithChildren<WrapperProps>) => {
39
- return (
40
- <IntlProvider locale="en">
41
- <QueryClientProvider client={createTestQueryClient({ user: true })}>
42
- <JoanieSessionProvider>
43
- <MemoryRouter initialEntries={[initialEntry]}>
44
- <Routes>
45
- <Route path={routePath} element={children} />
46
- </Routes>
47
- </MemoryRouter>
48
- </JoanieSessionProvider>
49
- </QueryClientProvider>
50
- </IntlProvider>
51
- );
52
- };
53
-
54
- beforeEach(() => {
55
- // Joanie provider's calls
56
- fetchMock.get('https://joanie.test/api/v1.0/orders/', []);
57
- fetchMock.get('https://joanie.test/api/v1.0/credit-cards/', []);
58
- fetchMock.get('https://joanie.test/api/v1.0/addresses/', []);
59
- });
60
-
61
- afterEach(() => {
62
- fetchMock.restore();
63
- });
64
-
65
29
  it.each([
66
30
  {
67
31
  testLabel: 'route organization before query',
@@ -112,16 +76,16 @@ describe('useDefaultOrganizationId', () => {
112
76
  }
113
77
 
114
78
  fetchMock.get(
115
- 'https://joanie.test/api/v1.0/organizations/',
79
+ 'https://joanie.endpoint/api/v1.0/organizations/',
116
80
  [...userOrganizationList, routeOrganization, queryOrganization].filter(
117
81
  (organization) => organization !== undefined,
118
82
  ),
119
83
  );
120
84
  const { result } = renderHook(useDefaultOrganizationId, {
121
85
  wrapper: ({ children }) => (
122
- <Wrapper routePath={routePath} initialEntry={initialEntry}>
86
+ <JoanieAppWrapper routerOptions={{ path: routePath, initialEntries: [initialEntry] }}>
123
87
  {children}
124
- </Wrapper>
88
+ </JoanieAppWrapper>
125
89
  ),
126
90
  });
127
91
 
@@ -1,6 +1,6 @@
1
1
  import { defineMessages } from 'react-intl';
2
2
 
3
- import { Organization } from 'types/Joanie';
3
+ import { API, Organization, OrganizationResourceQuery } from 'types/Joanie';
4
4
  import { useJoanieApi } from 'contexts/JoanieApiContext';
5
5
  import { useResource, useResources, UseResourcesProps } from '../useResources';
6
6
 
@@ -21,11 +21,11 @@ const messages = defineMessages({
21
21
  * Joanie Api hook to retrieve organizations
22
22
  * owned by the authenticated user.
23
23
  */
24
- const props: UseResourcesProps<Organization> = {
24
+ const props: UseResourcesProps<Organization, OrganizationResourceQuery, API['organizations']> = {
25
25
  queryKey: ['organizations'],
26
26
  apiInterface: () => useJoanieApi().organizations,
27
27
  session: true,
28
28
  messages,
29
29
  };
30
- export const useOrganizations = useResources<Organization>(props);
31
- export const useOrganization = useResource<Organization>(props);
30
+ export const useOrganizations = useResources<Organization, OrganizationResourceQuery>(props);
31
+ export const useOrganization = useResource<Organization, OrganizationResourceQuery>(props);
@@ -40,6 +40,8 @@ export interface UseResourcesCallbackProps<
40
40
  TResourceQuery extends ResourcesQuery = ResourcesQuery,
41
41
  TApiResource extends ApiResourceInterface<TData> = ApiResourceInterface<TData>,
42
42
  > extends UseResourcesProps<TData, TResourceQuery, TApiResource> {
43
+ // TODO(rlecellier): we've cases where fitlers should be required.
44
+ // like in useCourseOrder where the course_id filter must be set.
43
45
  filters?: TResourceQuery;
44
46
  queryOptions?: Omit<QueryOptions<TData>, 'queryFn' | 'queryKey'>;
45
47
  }
@@ -1,25 +1,21 @@
1
- import { IntlProvider } from 'react-intl';
2
- import { QueryClientProvider } from '@tanstack/react-query';
3
- import { CunninghamProvider } from '@openfun/cunningham-react';
4
1
  import fetchMock from 'fetch-mock';
5
- import { MemoryRouter, Route, Routes } from 'react-router-dom';
6
- import { render, screen } from '@testing-library/react';
2
+ import { screen } from '@testing-library/react';
7
3
  import { getAllByRole } from '@testing-library/dom';
8
4
  import userEvent from '@testing-library/user-event';
9
5
  import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
10
- import JoanieSessionProvider from 'contexts/SessionContext/JoanieSessionProvider';
11
- import { createTestQueryClient } from 'utils/test/createTestQueryClient';
12
6
  import { ContractFactory, OrganizationFactory } from 'utils/test/factories/joanie';
13
7
  import { expectNoSpinner } from 'utils/test/expectSpinner';
14
8
  import { expectBannerError } from 'utils/test/expectBanner';
15
9
  import { HttpStatusCode } from 'utils/errors/HttpError';
10
+ import { setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
11
+ import { render } from 'utils/test/render';
16
12
  import TeacherDashboardContracts from '.';
17
13
 
18
14
  jest.mock('utils/context', () => ({
19
15
  __esModule: true,
20
16
  default: mockRichieContextFactory({
21
17
  authentication: { backend: 'fonzie', endpoint: 'https://auth.endpoint.test' },
22
- joanie_backend: { endpoint: 'https://joanie.test' },
18
+ joanie_backend: { endpoint: 'https://joanie.endpoint' },
23
19
  }).one(),
24
20
  }));
25
21
 
@@ -34,36 +30,8 @@ jest.mock('components/ContractFrame', () => ({
34
30
  }) => isOpen && <p>ContractFrame opened for {organizationId}</p>,
35
31
  }));
36
32
 
37
- const Wrapper = ({ path, initialEntry }: { path: string; initialEntry: string }) => {
38
- return (
39
- <IntlProvider locale="en">
40
- <QueryClientProvider client={createTestQueryClient({ user: true })}>
41
- <JoanieSessionProvider>
42
- <CunninghamProvider>
43
- <MemoryRouter initialEntries={[initialEntry]}>
44
- <Routes>
45
- <Route path={path} element={<TeacherDashboardContracts />} />
46
- </Routes>
47
- </MemoryRouter>
48
- </CunninghamProvider>
49
- </JoanieSessionProvider>
50
- </QueryClientProvider>
51
- </IntlProvider>
52
- );
53
- };
54
-
55
33
  describe('pages/TeacherDashboardContracts', () => {
56
- beforeEach(() => {
57
- // Joanie providers calls
58
- fetchMock.get('https://joanie.test/api/v1.0/orders/', []);
59
- fetchMock.get('https://joanie.test/api/v1.0/credit-cards/', []);
60
- fetchMock.get('https://joanie.test/api/v1.0/addresses/', []);
61
- });
62
-
63
- afterEach(() => {
64
- fetchMock.restore();
65
- jest.resetAllMocks();
66
- });
34
+ setupJoanieSession();
67
35
 
68
36
  it('should render a list of contracts for a course product relation', async () => {
69
37
  const contracts = ContractFactory({
@@ -74,24 +42,24 @@ describe('pages/TeacherDashboardContracts', () => {
74
42
  const defaultOrganization = organizations[0];
75
43
 
76
44
  // OrganizationContractFilter request all organizations forwho the user have access
77
- fetchMock.get(`https://joanie.test/api/v1.0/organizations/`, organizations);
45
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/`, organizations);
78
46
  // TeacherDashboardContracts request a paginated list of contracts to display
79
47
  fetchMock.get(
80
- `https://joanie.test/api/v1.0/organizations/${defaultOrganization.id}/contracts/?course_product_relation_id=2&signature_state=signed&page=1&page_size=25`,
48
+ `https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?course_product_relation_id=2&signature_state=signed&page=1&page_size=25`,
81
49
  { results: contracts, count: 0, previous: null, next: null },
82
50
  );
83
51
  // useTeacherContractsToSign request all contract to sign, without pagination
84
52
  fetchMock.get(
85
- `https://joanie.test/api/v1.0/organizations/${defaultOrganization.id}/contracts/?signature_state=half_signed&course_product_relation_id=2`,
53
+ `https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?signature_state=half_signed&course_product_relation_id=2`,
86
54
  { results: [], count: 0, previous: null, next: null },
87
55
  );
88
56
 
89
- render(
90
- <Wrapper
91
- path="/courses/:courseId/products/:courseProductRelationId/contracts"
92
- initialEntry="/courses/1/products/2/contracts"
93
- />,
94
- );
57
+ render(<TeacherDashboardContracts />, {
58
+ routerOptions: {
59
+ path: '/courses/:courseId/products/:courseProductRelationId/contracts',
60
+ initialEntries: ['/courses/1/products/2/contracts'],
61
+ },
62
+ });
95
63
 
96
64
  await expectNoSpinner();
97
65
 
@@ -139,21 +107,24 @@ describe('pages/TeacherDashboardContracts', () => {
139
107
  organization_signed_on: Date.toString(),
140
108
  }).many(3);
141
109
 
110
+ // OrganizationContractFilter request all organizations forwho the user have access
111
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/`, []);
112
+
142
113
  fetchMock.get(
143
- `https://joanie.test/api/v1.0/organizations/1/contracts/?signature_state=signed&page=1&page_size=25`,
114
+ `https://joanie.endpoint/api/v1.0/organizations/1/contracts/?signature_state=signed&page=1&page_size=25`,
144
115
  { results: contracts, count: 0, previous: null, next: null },
145
116
  );
146
117
  fetchMock.get(
147
- `https://joanie.test/api/v1.0/organizations/1/contracts/?signature_state=half_signed`,
118
+ `https://joanie.endpoint/api/v1.0/organizations/1/contracts/?signature_state=half_signed`,
148
119
  { results: [], count: 0, previous: null, next: null },
149
120
  );
150
121
 
151
- render(
152
- <Wrapper
153
- path="/organizations/:organizationId/contracts"
154
- initialEntry="/organizations/1/contracts"
155
- />,
156
- );
122
+ render(<TeacherDashboardContracts />, {
123
+ routerOptions: {
124
+ path: '/organizations/:organizationId/contracts',
125
+ initialEntries: ['/organizations/1/contracts'],
126
+ },
127
+ });
157
128
 
158
129
  await expectNoSpinner();
159
130
 
@@ -193,21 +164,23 @@ describe('pages/TeacherDashboardContracts', () => {
193
164
  });
194
165
 
195
166
  it('should render an empty table if there are no contracts', async () => {
167
+ // OrganizationContractFilter request all organizations forwho the user have access
168
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/`, []);
196
169
  fetchMock.get(
197
- `https://joanie.test/api/v1.0/organizations/1/contracts/?signature_state=signed&page=1&page_size=25`,
170
+ `https://joanie.endpoint/api/v1.0/organizations/1/contracts/?signature_state=signed&page=1&page_size=25`,
198
171
  { results: [], count: 0, previous: null, next: null },
199
172
  );
200
173
  fetchMock.get(
201
- `https://joanie.test/api/v1.0/organizations/1/contracts/?signature_state=half_signed`,
174
+ `https://joanie.endpoint/api/v1.0/organizations/1/contracts/?signature_state=half_signed`,
202
175
  { results: [], count: 0, previous: null, next: null },
203
176
  );
204
177
 
205
- render(
206
- <Wrapper
207
- path="/organizations/:organizationId/contracts"
208
- initialEntry="/organizations/1/contracts"
209
- />,
210
- );
178
+ render(<TeacherDashboardContracts />, {
179
+ routerOptions: {
180
+ path: '/organizations/:organizationId/contracts',
181
+ initialEntries: ['/organizations/1/contracts'],
182
+ },
183
+ });
211
184
 
212
185
  await expectNoSpinner();
213
186
 
@@ -239,25 +212,27 @@ describe('pages/TeacherDashboardContracts', () => {
239
212
  abilities: { sign: true },
240
213
  }).many(3);
241
214
 
215
+ // OrganizationContractFilter request all organizations forwho the user have access
216
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/`, []);
242
217
  fetchMock.get(
243
- `https://joanie.test/api/v1.0/organizations/1/contracts/?signature_state=signed&page=1&page_size=25`,
218
+ `https://joanie.endpoint/api/v1.0/organizations/1/contracts/?signature_state=signed&page=1&page_size=25`,
244
219
  { results: [], count: 0, previous: null, next: null },
245
220
  );
246
221
  fetchMock.get(
247
- `https://joanie.test/api/v1.0/organizations/1/contracts/?signature_state=half_signed&page=1&page_size=25`,
222
+ `https://joanie.endpoint/api/v1.0/organizations/1/contracts/?signature_state=half_signed&page=1&page_size=25`,
248
223
  { results: contracts, count: 3, previous: null, next: null },
249
224
  );
250
225
  fetchMock.get(
251
- `https://joanie.test/api/v1.0/organizations/1/contracts/?signature_state=half_signed`,
226
+ `https://joanie.endpoint/api/v1.0/organizations/1/contracts/?signature_state=half_signed`,
252
227
  { results: contracts, count: 3, previous: null, next: null },
253
228
  );
254
229
 
255
- render(
256
- <Wrapper
257
- path="/organizations/:organizationId/contracts"
258
- initialEntry="/organizations/1/contracts?signature_state=half_signed"
259
- />,
260
- );
230
+ render(<TeacherDashboardContracts />, {
231
+ routerOptions: {
232
+ path: '/organizations/:organizationId/contracts',
233
+ initialEntries: ['/organizations/1/contracts?signature_state=half_signed'],
234
+ },
235
+ });
261
236
 
262
237
  await expectNoSpinner();
263
238
 
@@ -306,21 +281,24 @@ describe('pages/TeacherDashboardContracts', () => {
306
281
  });
307
282
 
308
283
  it('should render an error banner if an error occured during contracts fetching', async () => {
284
+ // OrganizationContractFilter request all organizations forwho the user have access
285
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/`, []);
286
+
309
287
  fetchMock.get(
310
- `https://joanie.test/api/v1.0/organizations/1/contracts/?signature_state=signed&page=1&page_size=25`,
288
+ `https://joanie.endpoint/api/v1.0/organizations/1/contracts/?signature_state=signed&page=1&page_size=25`,
311
289
  HttpStatusCode.NOT_FOUND,
312
290
  );
313
291
  fetchMock.get(
314
- `https://joanie.test/api/v1.0/organizations/1/contracts/?signature_state=half_signed`,
292
+ `https://joanie.endpoint/api/v1.0/organizations/1/contracts/?signature_state=half_signed`,
315
293
  HttpStatusCode.NOT_FOUND,
316
294
  );
317
295
 
318
- render(
319
- <Wrapper
320
- path="/organizations/:organizationId/contracts"
321
- initialEntry="/organizations/1/contracts"
322
- />,
323
- );
296
+ render(<TeacherDashboardContracts />, {
297
+ routerOptions: {
298
+ path: '/organizations/:organizationId/contracts',
299
+ initialEntries: ['/organizations/1/contracts'],
300
+ },
301
+ });
324
302
 
325
303
  await expectNoSpinner();
326
304
  await expectBannerError('An error occurred while fetching contracts. Please retry later.');
@@ -328,21 +306,28 @@ describe('pages/TeacherDashboardContracts', () => {
328
306
 
329
307
  it('should hide organization filter when user only have one organization', async () => {
330
308
  const defaultOrganization = OrganizationFactory().one();
331
- fetchMock.get('https://joanie.test/api/v1.0/organizations/', [defaultOrganization]);
309
+ fetchMock.get('https://joanie.endpoint/api/v1.0/organizations/', [defaultOrganization]);
332
310
 
333
311
  const contracts = ContractFactory({
334
312
  student_signed_on: Date.toString(),
335
313
  abilities: { sign: true },
336
314
  }).many(3);
337
315
  fetchMock.get(
338
- `https://joanie.test/api/v1.0/organizations/${defaultOrganization.id}/contracts/?signature_state=signed&page=1&page_size=25`,
316
+ `https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?signature_state=signed&page=1&page_size=25`,
339
317
  { results: [], count: 0, previous: null, next: null },
340
318
  );
341
319
  fetchMock.get(
342
- `https://joanie.test/api/v1.0/organizations/${defaultOrganization.id}/contracts/?signature_state=half_signed`,
320
+ `https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?signature_state=half_signed`,
343
321
  { results: contracts, count: 3, previous: null, next: null },
344
322
  );
345
- render(<Wrapper path="/" initialEntry="" />);
323
+
324
+ render(<TeacherDashboardContracts />, {
325
+ routerOptions: {
326
+ path: '/',
327
+ initialEntries: ['/'],
328
+ },
329
+ });
330
+
346
331
  await expectNoSpinner();
347
332
 
348
333
  // Signature state filter should have been rendered
@@ -1,50 +1,25 @@
1
- import { IntlProvider } from 'react-intl';
2
- import { QueryClientProvider } from '@tanstack/react-query';
3
- import { CunninghamProvider } from '@openfun/cunningham-react';
4
- import { render, screen, waitFor } from '@testing-library/react';
5
- import { PropsWithChildren } from 'react';
1
+ import { screen, waitFor } from '@testing-library/react';
6
2
  import fetchMock from 'fetch-mock';
7
3
  import { userEvent } from '@testing-library/user-event';
8
4
  import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
9
- import JoanieSessionProvider from 'contexts/SessionContext/JoanieSessionProvider';
10
- import { createTestQueryClient } from 'utils/test/createTestQueryClient';
11
5
  import { ContractState } from 'types/Joanie';
12
6
  import { OrganizationFactory } from 'utils/test/factories/joanie';
13
7
  import { expectNoSpinner } from 'utils/test/expectSpinner';
14
8
  import { noop } from 'utils';
9
+ import { setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
10
+ import { render } from 'utils/test/render';
15
11
  import ContractFiltersBar from '.';
16
12
 
17
13
  jest.mock('utils/context', () => ({
18
14
  __esModule: true,
19
15
  default: mockRichieContextFactory({
20
16
  authentication: { backend: 'fonzie', endpoint: 'https://auth.endpoint.test' },
21
- joanie_backend: { endpoint: 'https://joanie.test' },
17
+ joanie_backend: { endpoint: 'https://joanie.endpoint' },
22
18
  }).one(),
23
19
  }));
24
20
 
25
21
  describe('<ContractFiltersBar/>', () => {
26
- const Wrapper = ({ children }: PropsWithChildren) => {
27
- return (
28
- <IntlProvider locale="en">
29
- <QueryClientProvider client={createTestQueryClient({ user: true })}>
30
- <JoanieSessionProvider>
31
- <CunninghamProvider>{children}</CunninghamProvider>
32
- </JoanieSessionProvider>
33
- </QueryClientProvider>
34
- </IntlProvider>
35
- );
36
- };
37
-
38
- beforeEach(() => {
39
- fetchMock.get('https://joanie.test/api/v1.0/orders/', []);
40
- fetchMock.get('https://joanie.test/api/v1.0/credit-cards/', []);
41
- fetchMock.get('https://joanie.test/api/v1.0/addresses/', []);
42
- });
43
-
44
- afterEach(() => {
45
- fetchMock.restore();
46
- jest.resetAllMocks();
47
- });
22
+ setupJoanieSession();
48
23
 
49
24
  it('should render SignatureState and Organization filters with default value', async () => {
50
25
  const filterChange = jest.fn();
@@ -54,13 +29,9 @@ describe('<ContractFiltersBar/>', () => {
54
29
  organization_id: organizations[1].id,
55
30
  };
56
31
 
57
- fetchMock.get('https://joanie.test/api/v1.0/organizations/', organizations);
32
+ fetchMock.get('https://joanie.endpoint/api/v1.0/organizations/', organizations);
58
33
 
59
- render(
60
- <Wrapper>
61
- <ContractFiltersBar onFiltersChange={filterChange} defaultValues={defaultValues} />
62
- </Wrapper>,
63
- );
34
+ render(<ContractFiltersBar onFiltersChange={filterChange} defaultValues={defaultValues} />);
64
35
 
65
36
  await waitFor(() => expectNoSpinner());
66
37
 
@@ -101,16 +72,14 @@ describe('<ContractFiltersBar/>', () => {
101
72
  organization_id: organizations[0].id,
102
73
  };
103
74
 
104
- fetchMock.get('https://joanie.test/api/v1.0/organizations/', organizations);
75
+ fetchMock.get('https://joanie.endpoint/api/v1.0/organizations/', organizations);
105
76
 
106
77
  render(
107
- <Wrapper>
108
- <ContractFiltersBar
109
- onFiltersChange={noop}
110
- defaultValues={defaultValues}
111
- hideFilterSignatureState={true}
112
- />
113
- </Wrapper>,
78
+ <ContractFiltersBar
79
+ onFiltersChange={noop}
80
+ defaultValues={defaultValues}
81
+ hideFilterSignatureState={true}
82
+ />,
114
83
  );
115
84
 
116
85
  await waitFor(() => expectNoSpinner());
@@ -132,16 +101,14 @@ describe('<ContractFiltersBar/>', () => {
132
101
  organization_id: organizations[0].id,
133
102
  };
134
103
 
135
- fetchMock.get('https://joanie.test/api/v1.0/organizations/', organizations);
104
+ fetchMock.get('https://joanie.endpoint/api/v1.0/organizations/', organizations);
136
105
 
137
106
  render(
138
- <Wrapper>
139
- <ContractFiltersBar
140
- onFiltersChange={noop}
141
- defaultValues={defaultValues}
142
- hideFilterOrganization={true}
143
- />
144
- </Wrapper>,
107
+ <ContractFiltersBar
108
+ onFiltersChange={noop}
109
+ defaultValues={defaultValues}
110
+ hideFilterOrganization={true}
111
+ />,
145
112
  );
146
113
 
147
114
  await waitFor(() => expectNoSpinner());
@@ -161,19 +128,11 @@ describe('<ContractFiltersBar/>', () => {
161
128
  const organizations = OrganizationFactory().many(2);
162
129
  const handleChange = jest.fn();
163
130
 
164
- fetchMock.get('https://joanie.test/api/v1.0/organizations/', organizations);
131
+ fetchMock.get('https://joanie.endpoint/api/v1.0/organizations/', organizations);
165
132
 
166
- render(
167
- <Wrapper>
168
- <ContractFiltersBar onFiltersChange={handleChange} />
169
- </Wrapper>,
170
- );
133
+ render(<ContractFiltersBar onFiltersChange={handleChange} />);
171
134
 
172
135
  await waitFor(() => expectNoSpinner());
173
-
174
- // Organization filter should have been rendered and the first organization should be selected
175
- const organizationFilter = screen.getByRole('combobox', { name: 'Organization' });
176
- expect(organizationFilter).toHaveAttribute('value', organizations[0].title);
177
136
  expect(handleChange).toHaveBeenNthCalledWith(1, { organization_id: organizations[0].id });
178
137
  });
179
138
  });
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useMemo, useState } from 'react';
2
2
  import { useParams, useSearchParams } from 'react-router-dom';
3
+ import useDefaultOrganizationId from 'hooks/useDefaultOrganizationId';
3
4
  import { ContractResourceQuery, ContractState } from 'types/Joanie';
4
- import useDefaultOrganizationId from '../useDefaultOrganizationId';
5
5
 
6
6
  export type TeacherDashboardContractsParams = {
7
7
  organizationId?: string;
@@ -1,8 +1,4 @@
1
1
  .teacher-contract-page {
2
- background: #fff;
3
- border-radius: 6px;
4
- padding: 1rem;
5
-
6
2
  .product-title-column {
7
3
  max-width: rem-calc(200px);
8
4
  overflow: hidden;