richie-education 3.1.3-dev3 → 3.1.3-dev30
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/.storybook/__mocks__/utils/context.ts +4 -0
- package/js/api/joanie.ts +8 -8
- package/js/components/ContractFrame/OrganizationContractFrame.spec.tsx +11 -19
- package/js/components/ContractFrame/OrganizationContractFrame.tsx +4 -4
- package/js/components/CourseGlimpse/CourseGlimpseFooter.tsx +30 -5
- package/js/components/CourseGlimpse/index.spec.tsx +18 -0
- package/js/components/CourseGlimpse/index.stories.tsx +75 -4
- package/js/components/CourseGlimpse/index.tsx +4 -0
- package/js/components/CourseGlimpse/utils.ts +35 -30
- package/js/components/CourseGlimpseList/utils.ts +2 -2
- package/js/components/PurchaseButton/index.tsx +3 -3
- package/js/components/SaleTunnel/CredentialSaleTunnel/index.tsx +1 -3
- package/js/components/SaleTunnel/GenericSaleTunnel.tsx +13 -1
- package/js/components/SaleTunnel/SaleTunnelInformation/index.tsx +9 -7
- package/js/components/SaleTunnel/SubscriptionButton/index.tsx +1 -2
- package/js/components/SaleTunnel/index.credential.spec.tsx +5 -19
- package/js/components/SaleTunnel/index.full-process.spec.tsx +3 -3
- package/js/components/SaleTunnel/index.spec.tsx +171 -29
- package/js/components/SaleTunnel/index.stories.tsx +17 -3
- package/js/components/SaleTunnel/index.tsx +2 -2
- package/js/components/TeacherDashboardCourseList/index.spec.tsx +3 -3
- package/js/components/TeacherDashboardCourseList/index.tsx +2 -2
- package/js/hooks/useContractArchive/index.ts +3 -3
- package/js/hooks/useCourseProductUnion/index.spec.tsx +16 -18
- package/js/hooks/useCourseProductUnion/index.ts +7 -7
- package/js/hooks/useCourseProducts.ts +4 -8
- package/js/hooks/useDefaultOrganizationId/index.tsx +4 -7
- package/js/hooks/useOffering/index.ts +32 -0
- package/js/hooks/useTeacherCoursesSearch/index.tsx +2 -2
- package/js/hooks/useTeacherPendingContractsCount/index.ts +4 -4
- package/js/pages/DashboardCourses/index.spec.tsx +14 -14
- package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.spec.tsx +11 -14
- package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.tsx +4 -9
- package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.spec.tsx +11 -11
- package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.timer.spec.tsx +10 -13
- package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.tsx +4 -4
- package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.spec.tsx +20 -28
- package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.tsx +8 -11
- package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.spec.tsx +6 -6
- package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.tsx +4 -4
- package/js/pages/TeacherDashboardContractsLayout/hooks/useCheckContractArchiveExists/index.spec.tsx +7 -7
- package/js/pages/TeacherDashboardContractsLayout/hooks/useCheckContractArchiveExists/index.tsx +5 -5
- package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/contractArchiveLocalStorage.spec.ts +21 -28
- package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/contractArchiveLocalStorage.ts +13 -17
- package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.spec.tsx +11 -13
- package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.tsx +6 -6
- package/js/pages/TeacherDashboardContractsLayout/hooks/useHasContractToDownload/index.tsx +3 -3
- package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractFilters/index.spec.tsx +16 -16
- package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractFilters/index.tsx +4 -4
- package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractsToSign.tsx +4 -4
- package/js/pages/TeacherDashboardCourseLearnersLayout/hooks/useCourseLearnersFilters/index.spec.tsx +21 -21
- package/js/pages/TeacherDashboardCourseLearnersLayout/hooks/useCourseLearnersFilters/index.ts +5 -10
- package/js/pages/TeacherDashboardCourseLearnersLayout/index.spec.tsx +61 -79
- package/js/pages/TeacherDashboardCourseLearnersLayout/index.tsx +1 -1
- package/js/pages/TeacherDashboardCoursesLoader/index.spec.tsx +11 -11
- package/js/pages/TeacherDashboardOrganizationCourseLoader/index.spec.tsx +11 -11
- package/js/pages/TeacherDashboardTraining/TeacherDashboardTrainingLoader.tsx +7 -7
- package/js/pages/TeacherDashboardTraining/index.spec.tsx +21 -29
- package/js/pages/TeacherDashboardTraining/index.tsx +12 -16
- package/js/types/Course.ts +4 -0
- package/js/types/Joanie.ts +36 -29
- package/js/types/index.ts +6 -2
- package/js/utils/ProductHelper/index.ts +1 -5
- package/js/utils/test/factories/joanie.ts +19 -25
- package/js/utils/test/factories/richie.ts +10 -2
- package/js/utils/test/mockCourseProductWithOrder.ts +4 -4
- package/js/widgets/Dashboard/components/DashboardItem/Enrollment/DashboardItemEnrollment.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.spec.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +3 -3
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.useUnionResource.cache.spec.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/Installment/index.tsx +4 -4
- package/js/widgets/Dashboard/components/DashboardItem/stories.mock.ts +1 -1
- package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.spec.tsx +23 -28
- package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.tsx +4 -8
- package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/index.spec.tsx +17 -24
- package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/index.tsx +18 -21
- package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/utils.ts +4 -4
- package/js/widgets/Dashboard/components/TeacherDashboardOrganizationSidebar/index.tsx +3 -7
- package/js/widgets/Dashboard/utils/teacherDashboardPaths.tsx +4 -4
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/CourseProductItemFooter/index.tsx +19 -34
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/_styles.scss +35 -8
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/CourseRunList.tsx +3 -3
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/_styles.scss +9 -0
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +186 -140
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +11 -2
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +111 -24
- package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRun/index.stories.tsx +81 -0
- package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRun/index.tsx +14 -0
- package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRunCompacted/index.tsx +14 -0
- package/js/widgets/SyllabusCourseRunsList/index.spec.tsx +54 -8
- package/package.json +1 -1
- package/scss/objects/_course_glimpses.scss +16 -0
- package/js/hooks/useCourseProductRelation/index.ts +0 -44
package/js/pages/TeacherDashboardCourseLearnersLayout/hooks/useCourseLearnersFilters/index.spec.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fetchMock from 'fetch-mock';
|
|
2
2
|
import { renderHook, waitFor, act } from '@testing-library/react';
|
|
3
3
|
import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
|
|
4
|
-
import {
|
|
4
|
+
import { OfferingFactory, OrganizationFactory } from 'utils/test/factories/joanie';
|
|
5
5
|
import { JoanieAppWrapper, setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
|
|
6
6
|
import useCourseLearnersFilters from '.';
|
|
7
7
|
|
|
@@ -32,12 +32,12 @@ describe('useCourseLearnersFilters', () => {
|
|
|
32
32
|
expect(result.current.initialFilters).toStrictEqual({
|
|
33
33
|
organization_id: undefined,
|
|
34
34
|
course_id: undefined,
|
|
35
|
-
|
|
35
|
+
offering_id: undefined,
|
|
36
36
|
});
|
|
37
37
|
expect(result.current.filters).toStrictEqual({
|
|
38
38
|
organization_id: undefined,
|
|
39
39
|
course_id: undefined,
|
|
40
|
-
|
|
40
|
+
offering_id: undefined,
|
|
41
41
|
});
|
|
42
42
|
});
|
|
43
43
|
});
|
|
@@ -46,7 +46,7 @@ describe('useCourseLearnersFilters', () => {
|
|
|
46
46
|
const defaultOrganization = OrganizationFactory().one();
|
|
47
47
|
const filteredOrganization = OrganizationFactory({ id: 'filtered' }).one();
|
|
48
48
|
const routeOrganization = OrganizationFactory({ id: 'route' }).one();
|
|
49
|
-
const
|
|
49
|
+
const routeOffering = OfferingFactory().one();
|
|
50
50
|
// fetching user's organizations to initialize default organizationId.
|
|
51
51
|
fetchMock.get('https://joanie.endpoint/api/v1.0/organizations/', [
|
|
52
52
|
defaultOrganization,
|
|
@@ -56,9 +56,9 @@ describe('useCourseLearnersFilters', () => {
|
|
|
56
56
|
wrapper: ({ children }) => (
|
|
57
57
|
<JoanieAppWrapper
|
|
58
58
|
routerOptions={{
|
|
59
|
-
path: '/:organizationId/:courseId/:
|
|
59
|
+
path: '/:organizationId/:courseId/:offeringId',
|
|
60
60
|
initialEntries: [
|
|
61
|
-
`/${routeOrganization.id}/${
|
|
61
|
+
`/${routeOrganization.id}/${routeOffering.course.id}/${routeOffering.id}?organization_id=${filteredOrganization.id}`,
|
|
62
62
|
],
|
|
63
63
|
}}
|
|
64
64
|
>
|
|
@@ -70,13 +70,13 @@ describe('useCourseLearnersFilters', () => {
|
|
|
70
70
|
await waitFor(() => {
|
|
71
71
|
expect(result.current.initialFilters).toStrictEqual({
|
|
72
72
|
organization_id: routeOrganization.id,
|
|
73
|
-
course_id:
|
|
74
|
-
|
|
73
|
+
course_id: routeOffering.course.id,
|
|
74
|
+
offering_id: routeOffering.id,
|
|
75
75
|
});
|
|
76
76
|
expect(result.current.filters).toStrictEqual({
|
|
77
77
|
organization_id: routeOrganization.id,
|
|
78
|
-
course_id:
|
|
79
|
-
|
|
78
|
+
course_id: routeOffering.course.id,
|
|
79
|
+
offering_id: routeOffering.id,
|
|
80
80
|
});
|
|
81
81
|
});
|
|
82
82
|
});
|
|
@@ -84,7 +84,7 @@ describe('useCourseLearnersFilters', () => {
|
|
|
84
84
|
it("should use organizationId from query parameters when it's not in route params", async () => {
|
|
85
85
|
const defaultOrganization = OrganizationFactory().one();
|
|
86
86
|
const filteredOrganization = OrganizationFactory({ id: 'filtered' }).one();
|
|
87
|
-
const
|
|
87
|
+
const routeOffering = OfferingFactory({ id: 'route' }).one();
|
|
88
88
|
// fetching user's organizations to initialize default organizationId.
|
|
89
89
|
fetchMock.get('https://joanie.endpoint/api/v1.0/organizations/', [
|
|
90
90
|
defaultOrganization,
|
|
@@ -94,9 +94,9 @@ describe('useCourseLearnersFilters', () => {
|
|
|
94
94
|
wrapper: ({ children }) => (
|
|
95
95
|
<JoanieAppWrapper
|
|
96
96
|
routerOptions={{
|
|
97
|
-
path: '/:courseId/:
|
|
97
|
+
path: '/:courseId/:offeringId',
|
|
98
98
|
initialEntries: [
|
|
99
|
-
`/${
|
|
99
|
+
`/${routeOffering.course.id}/${routeOffering.id}/?organization_id=${filteredOrganization.id}`,
|
|
100
100
|
],
|
|
101
101
|
}}
|
|
102
102
|
>
|
|
@@ -108,13 +108,13 @@ describe('useCourseLearnersFilters', () => {
|
|
|
108
108
|
await waitFor(() => {
|
|
109
109
|
expect(result.current.initialFilters).toStrictEqual({
|
|
110
110
|
organization_id: filteredOrganization.id,
|
|
111
|
-
course_id:
|
|
112
|
-
|
|
111
|
+
course_id: routeOffering.course.id,
|
|
112
|
+
offering_id: routeOffering.id,
|
|
113
113
|
});
|
|
114
114
|
expect(result.current.filters).toStrictEqual({
|
|
115
115
|
organization_id: filteredOrganization.id,
|
|
116
|
-
course_id:
|
|
117
|
-
|
|
116
|
+
course_id: routeOffering.course.id,
|
|
117
|
+
offering_id: routeOffering.id,
|
|
118
118
|
});
|
|
119
119
|
});
|
|
120
120
|
});
|
|
@@ -122,7 +122,7 @@ describe('useCourseLearnersFilters', () => {
|
|
|
122
122
|
it('setFilters should update filter state', async () => {
|
|
123
123
|
const defaultOrganization = OrganizationFactory({ id: 'all' }).one();
|
|
124
124
|
const routeOrganization = OrganizationFactory({ id: 'route' }).one();
|
|
125
|
-
const
|
|
125
|
+
const routeOffering = OfferingFactory().one();
|
|
126
126
|
// fetching user's organizations to initialize default organizationId.
|
|
127
127
|
fetchMock.get('https://joanie.endpoint/api/v1.0/organizations/', [defaultOrganization]);
|
|
128
128
|
const { result } = renderHook(useCourseLearnersFilters, {
|
|
@@ -136,7 +136,7 @@ describe('useCourseLearnersFilters', () => {
|
|
|
136
136
|
const expectedInitialFilters = {
|
|
137
137
|
organization_id: defaultOrganization.id,
|
|
138
138
|
course_id: undefined,
|
|
139
|
-
|
|
139
|
+
offering_id: undefined,
|
|
140
140
|
};
|
|
141
141
|
await waitFor(() => {
|
|
142
142
|
expect(result.current.initialFilters).toStrictEqual(expectedInitialFilters);
|
|
@@ -144,8 +144,8 @@ describe('useCourseLearnersFilters', () => {
|
|
|
144
144
|
|
|
145
145
|
const newFilters = {
|
|
146
146
|
organization_id: routeOrganization.id,
|
|
147
|
-
course_id:
|
|
148
|
-
|
|
147
|
+
course_id: routeOffering.course.id,
|
|
148
|
+
offering_id: routeOffering.id,
|
|
149
149
|
};
|
|
150
150
|
act(() => {
|
|
151
151
|
result.current.setFilters(newFilters);
|
package/js/pages/TeacherDashboardCourseLearnersLayout/hooks/useCourseLearnersFilters/index.ts
CHANGED
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
import { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { useParams, useSearchParams } from 'react-router';
|
|
3
3
|
import useDefaultOrganizationId from 'hooks/useDefaultOrganizationId';
|
|
4
|
-
import {
|
|
5
|
-
CourseListItem,
|
|
6
|
-
CourseOrderResourceQuery,
|
|
7
|
-
CourseProductRelation,
|
|
8
|
-
Organization,
|
|
9
|
-
} from 'types/Joanie';
|
|
4
|
+
import { CourseListItem, CourseOrderResourceQuery, Offering, Organization } from 'types/Joanie';
|
|
10
5
|
|
|
11
6
|
export type CourseLearnersParams = {
|
|
12
7
|
courseId: CourseListItem['id'];
|
|
13
|
-
|
|
8
|
+
offeringId?: Offering['id'];
|
|
14
9
|
organizationId?: Organization['id'];
|
|
15
10
|
};
|
|
16
11
|
|
|
17
12
|
const useCourseLearnersFilters = () => {
|
|
18
|
-
const { courseId,
|
|
13
|
+
const { courseId, offeringId } = useParams<CourseLearnersParams>();
|
|
19
14
|
const [searchParams] = useSearchParams();
|
|
20
15
|
const searchFilters: CourseOrderResourceQuery = useMemo(() => {
|
|
21
16
|
return {
|
|
22
17
|
course_id: courseId,
|
|
23
18
|
organization_id: searchParams.get('organization_id') || undefined,
|
|
24
|
-
|
|
19
|
+
offering_id: searchParams.get('offering_id') || undefined,
|
|
25
20
|
};
|
|
26
21
|
}, Array.from(searchParams.entries()));
|
|
27
22
|
|
|
@@ -32,7 +27,7 @@ const useCourseLearnersFilters = () => {
|
|
|
32
27
|
return {
|
|
33
28
|
...searchFilters,
|
|
34
29
|
organization_id: defaultOrganizationId,
|
|
35
|
-
|
|
30
|
+
offering_id: offeringId,
|
|
36
31
|
};
|
|
37
32
|
}, [defaultOrganizationId]);
|
|
38
33
|
const [filters, setFilters] = useState<CourseOrderResourceQuery>(initialFilters);
|
|
@@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event';
|
|
|
4
4
|
import queryString from 'query-string';
|
|
5
5
|
import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
OfferingFactory,
|
|
8
8
|
NestedCourseOrderFactory,
|
|
9
9
|
OrganizationFactory,
|
|
10
10
|
} from 'utils/test/factories/joanie';
|
|
@@ -49,47 +49,44 @@ describe('pages/TeacherDashboardCourseLearnersLayout', () => {
|
|
|
49
49
|
organizationFilterShouldBeDisplayed: true,
|
|
50
50
|
},
|
|
51
51
|
])('$expectedLabel', async ({ nbOrganization, organizationFilterShouldBeDisplayed }) => {
|
|
52
|
-
const
|
|
52
|
+
const offering = OfferingFactory().one();
|
|
53
53
|
const organizationList = OrganizationFactory().many(nbOrganization);
|
|
54
54
|
fetchMock.get(
|
|
55
|
-
`https://joanie.endpoint/api/v1.0/organizations/?
|
|
55
|
+
`https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`,
|
|
56
56
|
organizationList,
|
|
57
57
|
);
|
|
58
58
|
|
|
59
59
|
// Course sidebar query
|
|
60
|
-
fetchMock.get(
|
|
61
|
-
`https://joanie.endpoint/api/v1.0/course-product-relations/${courseProductRelation.id}/`,
|
|
62
|
-
{},
|
|
63
|
-
);
|
|
60
|
+
fetchMock.get(`https://joanie.endpoint/api/v1.0/offerings/${offering.id}/`, {});
|
|
64
61
|
|
|
65
62
|
// First request before finding default organizationId
|
|
66
63
|
const courseOrderListQueryParams = {
|
|
67
|
-
|
|
64
|
+
offering_id: offering.id,
|
|
68
65
|
page: 1,
|
|
69
66
|
page_size: PER_PAGE.courseLearnerList,
|
|
70
67
|
};
|
|
71
68
|
fetchMock.get(
|
|
72
|
-
`https://joanie.endpoint/api/v1.0/courses/${
|
|
69
|
+
`https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
|
|
73
70
|
[],
|
|
74
71
|
);
|
|
75
72
|
|
|
76
73
|
if (organizationList.length > 0) {
|
|
77
74
|
// Course sidebar query
|
|
78
75
|
fetchMock.get(
|
|
79
|
-
`https://joanie.endpoint/api/v1.0/organizations/${organizationList[0].id}/contracts/?
|
|
76
|
+
`https://joanie.endpoint/api/v1.0/organizations/${organizationList[0].id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
|
|
80
77
|
[],
|
|
81
78
|
);
|
|
82
79
|
|
|
83
80
|
// Second request when default organizationId is fetched
|
|
84
81
|
fetchMock.get(
|
|
85
|
-
`https://joanie.endpoint/api/v1.0/courses/${
|
|
82
|
+
`https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify({ organization_id: organizationList[0].id, ...courseOrderListQueryParams }, { sort: false })}`,
|
|
86
83
|
[],
|
|
87
84
|
);
|
|
88
85
|
}
|
|
89
86
|
render(<TeacherDashboardCourseLearnersLayout />, {
|
|
90
87
|
routerOptions: {
|
|
91
|
-
path: '/:courseId/:
|
|
92
|
-
initialEntries: [`/${
|
|
88
|
+
path: '/:courseId/:offeringId',
|
|
89
|
+
initialEntries: [`/${offering.course.id}/${offering.id}`],
|
|
93
90
|
},
|
|
94
91
|
});
|
|
95
92
|
|
|
@@ -112,45 +109,42 @@ describe('pages/TeacherDashboardCourseLearnersLayout', () => {
|
|
|
112
109
|
});
|
|
113
110
|
|
|
114
111
|
it('should call onFiltersChange on organization filter change', async () => {
|
|
115
|
-
const
|
|
112
|
+
const offering = OfferingFactory().one();
|
|
116
113
|
const defaultOrganization = OrganizationFactory().one();
|
|
117
114
|
const otherOrganization = OrganizationFactory().one();
|
|
118
115
|
const organizationList = [defaultOrganization, otherOrganization];
|
|
119
116
|
|
|
120
117
|
// Course sidebar queries
|
|
121
118
|
fetchMock.get(
|
|
122
|
-
`https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?
|
|
119
|
+
`https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
|
|
123
120
|
[],
|
|
124
121
|
);
|
|
125
|
-
fetchMock.get(
|
|
126
|
-
`https://joanie.endpoint/api/v1.0/course-product-relations/${courseProductRelation.id}/`,
|
|
127
|
-
{},
|
|
128
|
-
);
|
|
122
|
+
fetchMock.get(`https://joanie.endpoint/api/v1.0/offerings/${offering.id}/`, {});
|
|
129
123
|
|
|
130
124
|
fetchMock.get(
|
|
131
|
-
`https://joanie.endpoint/api/v1.0/organizations/?
|
|
125
|
+
`https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`,
|
|
132
126
|
organizationList,
|
|
133
127
|
);
|
|
134
128
|
// First request before finding default organizationId
|
|
135
129
|
const courseOrderListQueryParams = {
|
|
136
|
-
|
|
130
|
+
offering_id: offering.id,
|
|
137
131
|
page: 1,
|
|
138
132
|
page_size: PER_PAGE.courseLearnerList,
|
|
139
133
|
};
|
|
140
134
|
fetchMock.get(
|
|
141
|
-
`https://joanie.endpoint/api/v1.0/courses/${
|
|
135
|
+
`https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
|
|
142
136
|
[],
|
|
143
137
|
);
|
|
144
138
|
// Second request when default organizationId is fetched
|
|
145
139
|
fetchMock.get(
|
|
146
|
-
`https://joanie.endpoint/api/v1.0/courses/${
|
|
140
|
+
`https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify({ organization_id: defaultOrganization.id, ...courseOrderListQueryParams }, { sort: false })}`,
|
|
147
141
|
[],
|
|
148
142
|
);
|
|
149
143
|
|
|
150
144
|
render(<TeacherDashboardCourseLearnersLayout />, {
|
|
151
145
|
routerOptions: {
|
|
152
|
-
path: '/:courseId/:
|
|
153
|
-
initialEntries: [`/${
|
|
146
|
+
path: '/:courseId/:offeringId',
|
|
147
|
+
initialEntries: [`/${offering.course.id}/${offering.id}`],
|
|
154
148
|
},
|
|
155
149
|
});
|
|
156
150
|
|
|
@@ -164,60 +158,57 @@ describe('pages/TeacherDashboardCourseLearnersLayout', () => {
|
|
|
164
158
|
const optionToSelect = screen.getByRole('option', { name: organizationList[1].title });
|
|
165
159
|
|
|
166
160
|
fetchMock.get(
|
|
167
|
-
`https://joanie.endpoint/api/v1.0/courses/${
|
|
161
|
+
`https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify({ organization_id: otherOrganization.id, ...courseOrderListQueryParams }, { sort: false })}`,
|
|
168
162
|
[],
|
|
169
163
|
);
|
|
170
164
|
await user.click(optionToSelect);
|
|
171
165
|
// onload default value is undefine and is onFiltersChange called once
|
|
172
166
|
expect(
|
|
173
167
|
fetchMock.called(
|
|
174
|
-
`https://joanie.endpoint/api/v1.0/courses/${
|
|
168
|
+
`https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify({ organization_id: otherOrganization.id, ...courseOrderListQueryParams }, { sort: false })}`,
|
|
175
169
|
),
|
|
176
170
|
).toBe(true);
|
|
177
171
|
});
|
|
178
172
|
|
|
179
|
-
it('should render a list of course learners for
|
|
173
|
+
it('should render a list of course learners for an offering', async () => {
|
|
180
174
|
const defaultOrganization = OrganizationFactory().one();
|
|
181
175
|
const otherOrganization = OrganizationFactory().one();
|
|
182
176
|
const organizationList = [defaultOrganization, otherOrganization];
|
|
183
|
-
const
|
|
177
|
+
const offering = OfferingFactory().one();
|
|
184
178
|
const courseOrderList = NestedCourseOrderFactory().many(3);
|
|
185
179
|
|
|
186
180
|
// Course sidebar queries
|
|
187
181
|
fetchMock.get(
|
|
188
|
-
`https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?
|
|
182
|
+
`https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
|
|
189
183
|
[],
|
|
190
184
|
);
|
|
191
|
-
fetchMock.get(
|
|
192
|
-
`https://joanie.endpoint/api/v1.0/course-product-relations/${courseProductRelation.id}/`,
|
|
193
|
-
{},
|
|
194
|
-
);
|
|
185
|
+
fetchMock.get(`https://joanie.endpoint/api/v1.0/offerings/${offering.id}/`, {});
|
|
195
186
|
|
|
196
187
|
fetchMock.get(
|
|
197
|
-
`https://joanie.endpoint/api/v1.0/organizations/?
|
|
188
|
+
`https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`,
|
|
198
189
|
organizationList,
|
|
199
190
|
);
|
|
200
191
|
|
|
201
192
|
// First request before finding default organizationId
|
|
202
193
|
const courseOrderListQueryParams = {
|
|
203
|
-
|
|
194
|
+
offering_id: offering.id,
|
|
204
195
|
page: 1,
|
|
205
196
|
page_size: PER_PAGE.courseLearnerList,
|
|
206
197
|
};
|
|
207
198
|
fetchMock.get(
|
|
208
|
-
`https://joanie.endpoint/api/v1.0/courses/${
|
|
199
|
+
`https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
|
|
209
200
|
courseOrderList,
|
|
210
201
|
);
|
|
211
202
|
// Second request when default organizationId is fetched
|
|
212
203
|
fetchMock.get(
|
|
213
|
-
`https://joanie.endpoint/api/v1.0/courses/${
|
|
204
|
+
`https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify({ organization_id: defaultOrganization.id, ...courseOrderListQueryParams }, { sort: false })}`,
|
|
214
205
|
courseOrderList,
|
|
215
206
|
);
|
|
216
207
|
|
|
217
208
|
render(<TeacherDashboardCourseLearnersLayout />, {
|
|
218
209
|
routerOptions: {
|
|
219
|
-
path: '/:courseId/:
|
|
220
|
-
initialEntries: [`/${
|
|
210
|
+
path: '/:courseId/:offeringId',
|
|
211
|
+
initialEntries: [`/${offering.course.id}/${offering.id}`],
|
|
221
212
|
},
|
|
222
213
|
});
|
|
223
214
|
|
|
@@ -239,42 +230,39 @@ describe('pages/TeacherDashboardCourseLearnersLayout', () => {
|
|
|
239
230
|
|
|
240
231
|
it('should render a list of course learners for an organization', async () => {
|
|
241
232
|
const organization = OrganizationFactory().one();
|
|
242
|
-
const
|
|
233
|
+
const offering = OfferingFactory().one();
|
|
243
234
|
const courseOrderList = NestedCourseOrderFactory().many(3);
|
|
244
235
|
|
|
245
236
|
// Course sidebar queries
|
|
246
237
|
fetchMock.get(
|
|
247
|
-
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?
|
|
238
|
+
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
|
|
248
239
|
[],
|
|
249
240
|
);
|
|
250
241
|
fetchMock.get(
|
|
251
|
-
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/
|
|
242
|
+
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offerings/${offering.id}/`,
|
|
252
243
|
{},
|
|
253
244
|
);
|
|
254
245
|
|
|
255
246
|
// before default organization's fetched, we query all organization to decide if we should display organization filter or not.
|
|
256
|
-
fetchMock.get(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
);
|
|
247
|
+
fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`, [
|
|
248
|
+
organization,
|
|
249
|
+
]);
|
|
260
250
|
|
|
261
251
|
const courseOrderListQueryParams = {
|
|
262
252
|
organization_id: organization.id,
|
|
263
|
-
|
|
253
|
+
offering_id: offering.id,
|
|
264
254
|
page: 1,
|
|
265
255
|
page_size: PER_PAGE.courseLearnerList,
|
|
266
256
|
};
|
|
267
257
|
fetchMock.get(
|
|
268
|
-
`https://joanie.endpoint/api/v1.0/courses/${
|
|
258
|
+
`https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
|
|
269
259
|
courseOrderList,
|
|
270
260
|
);
|
|
271
261
|
|
|
272
262
|
render(<TeacherDashboardCourseLearnersLayout />, {
|
|
273
263
|
routerOptions: {
|
|
274
|
-
path: '/:organizationId/:courseId/:
|
|
275
|
-
initialEntries: [
|
|
276
|
-
`/${organization.id}/${courseProductRelation.course.id}/${courseProductRelation.id}`,
|
|
277
|
-
],
|
|
264
|
+
path: '/:organizationId/:courseId/:offeringId',
|
|
265
|
+
initialEntries: [`/${organization.id}/${offering.course.id}/${offering.id}`],
|
|
278
266
|
},
|
|
279
267
|
});
|
|
280
268
|
|
|
@@ -294,41 +282,38 @@ describe('pages/TeacherDashboardCourseLearnersLayout', () => {
|
|
|
294
282
|
|
|
295
283
|
it('should render an empty table if there are no course learners', async () => {
|
|
296
284
|
const organization = OrganizationFactory().one();
|
|
297
|
-
const
|
|
285
|
+
const offering = OfferingFactory().one();
|
|
298
286
|
|
|
299
287
|
// Course sidebar queries
|
|
300
288
|
fetchMock.get(
|
|
301
|
-
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?
|
|
289
|
+
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
|
|
302
290
|
[],
|
|
303
291
|
);
|
|
304
292
|
fetchMock.get(
|
|
305
|
-
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/
|
|
293
|
+
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offerings/${offering.id}/`,
|
|
306
294
|
{},
|
|
307
295
|
);
|
|
308
296
|
|
|
309
297
|
// before default organization's fetched, we query all organization to decide if we should display organization filter or not.
|
|
310
|
-
fetchMock.get(
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
);
|
|
298
|
+
fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`, [
|
|
299
|
+
organization,
|
|
300
|
+
]);
|
|
314
301
|
|
|
315
302
|
const courseOrderListQueryParams = {
|
|
316
303
|
organization_id: organization.id,
|
|
317
|
-
|
|
304
|
+
offering_id: offering.id,
|
|
318
305
|
page: 1,
|
|
319
306
|
page_size: PER_PAGE.courseLearnerList,
|
|
320
307
|
};
|
|
321
308
|
fetchMock.get(
|
|
322
|
-
`https://joanie.endpoint/api/v1.0/courses/${
|
|
309
|
+
`https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
|
|
323
310
|
[],
|
|
324
311
|
);
|
|
325
312
|
|
|
326
313
|
render(<TeacherDashboardCourseLearnersLayout />, {
|
|
327
314
|
routerOptions: {
|
|
328
|
-
path: '/:organizationId/:courseId/:
|
|
329
|
-
initialEntries: [
|
|
330
|
-
`/${organization.id}/${courseProductRelation.course.id}/${courseProductRelation.id}`,
|
|
331
|
-
],
|
|
315
|
+
path: '/:organizationId/:courseId/:offeringId',
|
|
316
|
+
initialEntries: [`/${organization.id}/${offering.course.id}/${offering.id}`],
|
|
332
317
|
},
|
|
333
318
|
});
|
|
334
319
|
|
|
@@ -341,41 +326,38 @@ describe('pages/TeacherDashboardCourseLearnersLayout', () => {
|
|
|
341
326
|
|
|
342
327
|
it('should render an error banner if an error occured during course learners fetching', async () => {
|
|
343
328
|
const organization = OrganizationFactory().one();
|
|
344
|
-
const
|
|
329
|
+
const offering = OfferingFactory().one();
|
|
345
330
|
|
|
346
331
|
// Course sidebar queries
|
|
347
332
|
fetchMock.get(
|
|
348
|
-
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?
|
|
333
|
+
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
|
|
349
334
|
[],
|
|
350
335
|
);
|
|
351
336
|
fetchMock.get(
|
|
352
|
-
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/
|
|
337
|
+
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offerings/${offering.id}/`,
|
|
353
338
|
{},
|
|
354
339
|
);
|
|
355
340
|
|
|
356
341
|
// before default organization's fetched, we query all organization to decide if we should display organization filter or not.
|
|
357
|
-
fetchMock.get(
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
);
|
|
342
|
+
fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`, [
|
|
343
|
+
organization,
|
|
344
|
+
]);
|
|
361
345
|
|
|
362
346
|
const courseOrderListQueryParams = {
|
|
363
347
|
organization_id: organization.id,
|
|
364
|
-
|
|
348
|
+
offering_id: offering.id,
|
|
365
349
|
page: 1,
|
|
366
350
|
page_size: PER_PAGE.courseLearnerList,
|
|
367
351
|
};
|
|
368
352
|
fetchMock.get(
|
|
369
|
-
`https://joanie.endpoint/api/v1.0/courses/${
|
|
353
|
+
`https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
|
|
370
354
|
new Response('', { status: HttpStatusCode.NOT_FOUND }),
|
|
371
355
|
);
|
|
372
356
|
|
|
373
357
|
render(<TeacherDashboardCourseLearnersLayout />, {
|
|
374
358
|
routerOptions: {
|
|
375
|
-
path: '/:organizationId/:courseId/:
|
|
376
|
-
initialEntries: [
|
|
377
|
-
`/${organization.id}/${courseProductRelation.course.id}/${courseProductRelation.id}`,
|
|
378
|
-
],
|
|
359
|
+
path: '/:organizationId/:courseId/:offeringId',
|
|
360
|
+
initialEntries: [`/${organization.id}/${offering.course.id}/${offering.id}`],
|
|
379
361
|
},
|
|
380
362
|
});
|
|
381
363
|
|
|
@@ -65,7 +65,7 @@ export const TeacherDashboardCourseLearnersLayout = () => {
|
|
|
65
65
|
const {
|
|
66
66
|
items: organizations,
|
|
67
67
|
states: { isFetched: isOrganizationFetched },
|
|
68
|
-
} = useOrganizations({
|
|
68
|
+
} = useOrganizations({ offering_id: filters.offering_id });
|
|
69
69
|
const {
|
|
70
70
|
items: courseOrders,
|
|
71
71
|
meta,
|
|
@@ -2,7 +2,7 @@ import { screen, waitFor } from '@testing-library/react';
|
|
|
2
2
|
import fetchMock from 'fetch-mock';
|
|
3
3
|
import userEvent from '@testing-library/user-event';
|
|
4
4
|
import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
|
|
5
|
-
import { CourseListItemFactory,
|
|
5
|
+
import { CourseListItemFactory, OfferingFactory } from 'utils/test/factories/joanie';
|
|
6
6
|
import { expectNoSpinner } from 'utils/test/expectSpinner';
|
|
7
7
|
import { mockPaginatedResponse } from 'utils/test/mockPaginatedResponse';
|
|
8
8
|
import { PER_PAGE } from 'settings';
|
|
@@ -48,19 +48,19 @@ describe('components/TeacherDashboardCoursesLoader', () => {
|
|
|
48
48
|
mockPaginatedResponse(CourseListItemFactory().many(15), 15, false),
|
|
49
49
|
);
|
|
50
50
|
fetchMock.get(
|
|
51
|
-
`https://joanie.endpoint/api/v1.0/
|
|
52
|
-
mockPaginatedResponse(
|
|
51
|
+
`https://joanie.endpoint/api/v1.0/offerings/?product_type=credential&page=1&page_size=${perPage}`,
|
|
52
|
+
mockPaginatedResponse(OfferingFactory().many(15), 15, false),
|
|
53
53
|
);
|
|
54
54
|
|
|
55
55
|
render(<TeacherDashboardCoursesLoader />);
|
|
56
56
|
await expectNoSpinner('Loading courses...');
|
|
57
57
|
|
|
58
58
|
nbApiCalls += 1; // course api call
|
|
59
|
-
nbApiCalls += 1; //
|
|
59
|
+
nbApiCalls += 1; // offerings api call
|
|
60
60
|
const calledUrls = fetchMock.calls().map((call) => call[0]);
|
|
61
61
|
expect(calledUrls).toHaveLength(nbApiCalls);
|
|
62
62
|
expect(calledUrls).toContain(
|
|
63
|
-
`https://joanie.endpoint/api/v1.0/
|
|
63
|
+
`https://joanie.endpoint/api/v1.0/offerings/?product_type=credential&page=1&page_size=${perPage}`,
|
|
64
64
|
);
|
|
65
65
|
|
|
66
66
|
// section titles
|
|
@@ -80,8 +80,8 @@ describe('components/TeacherDashboardCoursesLoader', () => {
|
|
|
80
80
|
mockPaginatedResponse(CourseListItemFactory().many(15), 15, false),
|
|
81
81
|
);
|
|
82
82
|
fetchMock.get(
|
|
83
|
-
`https://joanie.endpoint/api/v1.0/
|
|
84
|
-
mockPaginatedResponse(
|
|
83
|
+
`https://joanie.endpoint/api/v1.0/offerings/?product_type=credential&page=1&page_size=${perPage}`,
|
|
84
|
+
mockPaginatedResponse(OfferingFactory().many(15), 15, false),
|
|
85
85
|
);
|
|
86
86
|
|
|
87
87
|
render(<TeacherDashboardCoursesLoader />);
|
|
@@ -93,22 +93,22 @@ describe('components/TeacherDashboardCoursesLoader', () => {
|
|
|
93
93
|
mockPaginatedResponse(CourseListItemFactory().many(5), 5, false),
|
|
94
94
|
);
|
|
95
95
|
fetchMock.get(
|
|
96
|
-
`https://joanie.endpoint/api/v1.0/
|
|
97
|
-
mockPaginatedResponse(
|
|
96
|
+
`https://joanie.endpoint/api/v1.0/offerings/?query=text+query&product_type=credential&page=1&page_size=${perPage}`,
|
|
97
|
+
mockPaginatedResponse(OfferingFactory().many(5), 5, false),
|
|
98
98
|
);
|
|
99
99
|
const user = userEvent.setup();
|
|
100
100
|
await user.type(screen.getByRole('textbox', { name: /Search/ }), 'text query');
|
|
101
101
|
await user.click(screen.getByRole('button', { name: /Search/ }));
|
|
102
102
|
|
|
103
103
|
nbApiCalls = 1; // course api call
|
|
104
|
-
nbApiCalls += 1; //
|
|
104
|
+
nbApiCalls += 1; // offerings api call
|
|
105
105
|
const calledUrls = fetchMock.calls().map((call) => call[0]);
|
|
106
106
|
expect(calledUrls).toHaveLength(nbApiCalls);
|
|
107
107
|
expect(calledUrls).toContain(
|
|
108
108
|
`https://joanie.endpoint/api/v1.0/courses/?query=text+query&has_listed_course_runs=true&page=1&page_size=${perPage}`,
|
|
109
109
|
);
|
|
110
110
|
expect(calledUrls).toContain(
|
|
111
|
-
`https://joanie.endpoint/api/v1.0/
|
|
111
|
+
`https://joanie.endpoint/api/v1.0/offerings/?query=text+query&product_type=credential&page=1&page_size=${perPage}`,
|
|
112
112
|
);
|
|
113
113
|
|
|
114
114
|
await waitFor(() => {
|