richie-education 2.25.0-b2.dev69 → 2.25.0-b2.dev71

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.
@@ -0,0 +1,76 @@
1
+ import { defineMessages, useIntl } from 'react-intl';
2
+ import useDateRelative from 'hooks/useDateRelative';
3
+ import { Priority } from 'types';
4
+ import { CourseRun } from 'types/Joanie';
5
+ import useDateFormat, { DEFAULT_DATE_FORMAT } from 'hooks/useDateFormat';
6
+
7
+ const messages = defineMessages({
8
+ onGoingRunPeriod: {
9
+ id: 'components.useCourseRunPeriodMessage.onGoingRunPeriod',
10
+ description: 'Text to display when a course run is on going.',
11
+ defaultMessage: 'This session started on {startDate} and will end on {endDate}',
12
+ },
13
+ futureRunPeriod: {
14
+ id: 'components.useCourseRunPeriodMessage.futureRunPeriod',
15
+ description: 'Text to display the period of a future course run.',
16
+ defaultMessage: 'This session starts {relativeStartDate}, the {startDate}',
17
+ },
18
+ onGoingEnrolledRunPeriod: {
19
+ id: 'components.useCourseRunPeriodMessage.onGoingEnrolledRunPeriod',
20
+ description: 'Text to display when a course run is ongoing and the user is enrolled to.',
21
+ defaultMessage: "You are enrolled for this session. It's open from {startDate} to {endDate}",
22
+ },
23
+ futureEnrolledRunPeriod: {
24
+ id: 'components.useCourseRunPeriodMessage.futureEnrolledRunPeriod',
25
+ description: 'Text to display when a course run is not yet opened and the user is enrolled to.',
26
+ defaultMessage:
27
+ 'You are enrolled for this session. It starts {relativeStartDate}, the {startDate}.',
28
+ },
29
+ archivedEnrolledRunPeriod: {
30
+ id: 'components.useCourseRunPeriodMessage.archivedEnrolledRunPeriod',
31
+ description: 'Text to display when a course run is archived and the user is enrolled to.',
32
+ defaultMessage: 'You are enrolled for this session.',
33
+ },
34
+ });
35
+
36
+ const useCourseRunPeriodMessage = (courseRun: CourseRun, enrolled: boolean = false) => {
37
+ const intl = useIntl();
38
+ const formatDate = useDateFormat();
39
+
40
+ const relativeStartDate = useDateRelative(new Date(courseRun.start));
41
+ const startDate = formatDate(courseRun.start, DEFAULT_DATE_FORMAT);
42
+ const endDate = formatDate(courseRun.end, DEFAULT_DATE_FORMAT);
43
+ const isArchived = [Priority.ARCHIVED_CLOSED, Priority.ARCHIVED_OPEN].includes(
44
+ courseRun.state.priority,
45
+ );
46
+ const isOnGoing = [Priority.ONGOING_OPEN, Priority.ONGOING_CLOSED].includes(
47
+ courseRun.state.priority,
48
+ );
49
+ if (enrolled) {
50
+ if (isArchived) {
51
+ return intl.formatMessage(messages.archivedEnrolledRunPeriod);
52
+ }
53
+ if (isOnGoing) {
54
+ return intl.formatMessage(messages.onGoingEnrolledRunPeriod, {
55
+ startDate,
56
+ endDate,
57
+ });
58
+ }
59
+ return intl.formatMessage(messages.futureEnrolledRunPeriod, {
60
+ relativeStartDate,
61
+ startDate,
62
+ });
63
+ }
64
+ if (isOnGoing) {
65
+ return intl.formatMessage(messages.onGoingRunPeriod, {
66
+ startDate,
67
+ endDate,
68
+ });
69
+ }
70
+ return intl.formatMessage(messages.futureRunPeriod, {
71
+ relativeStartDate,
72
+ startDate,
73
+ });
74
+ };
75
+
76
+ export default useCourseRunPeriodMessage;
@@ -1,5 +1,5 @@
1
1
  import { render, screen } from '@testing-library/react';
2
- import { IntlProvider } from 'react-intl';
2
+ import { IntlProvider, createIntl } from 'react-intl';
3
3
  import { PropsWithChildren } from 'react';
4
4
  import { CredentialOrderFactory, EnrollmentFactory } from 'utils/test/factories/joanie';
5
5
  import { Priority } from 'types';
@@ -8,7 +8,8 @@ import { DEFAULT_DATE_FORMAT } from 'hooks/useDateFormat';
8
8
  import { CourseRunFactoryFromPriority } from 'utils/test/factories/richie';
9
9
  import { noop } from 'utils';
10
10
  import { computeState } from 'utils/CourseRuns';
11
- import { DashboardItemCourseEnrollingRun, Enrolled } from './DashboardItemCourseEnrolling';
11
+ import { formatRelativeDate } from 'utils/relativeDate';
12
+ import { DashboardItemCourseEnrollingRun, Enrolled } from '.';
12
13
 
13
14
  /**
14
15
  * Most of the component of this file are tested from DashboardItemEnrollment.spec.tsx and
@@ -23,59 +24,89 @@ describe('<Enrolled/>', () => {
23
24
  {
24
25
  buttonTestLabel: 'and access course button',
25
26
  priority: Priority.ONGOING_OPEN,
27
+ priorityLabel: 'ONGOING_OPEN',
26
28
  expectButton: true,
29
+ expectLabelTemplate:
30
+ "You are enrolled for this session. It's open from %fromDate% to %toDate%",
27
31
  },
28
32
  {
29
33
  buttonTestLabel: 'and no access course button',
30
34
  priority: Priority.FUTURE_OPEN,
35
+ priorityLabel: 'FUTURE_OPEN',
31
36
  expectButton: false,
37
+ expectLabelTemplate:
38
+ 'You are enrolled for this session. It starts %fromRelativeDate%, the %fromDate%.',
32
39
  },
33
40
  {
34
41
  buttonTestLabel: 'and access course button',
35
42
  priority: Priority.ARCHIVED_OPEN,
43
+ priorityLabel: 'ARCHIVED_OPEN',
36
44
  expectButton: true,
45
+ expectLabelTemplate: `You are enrolled for this session.`,
37
46
  },
38
47
  {
39
48
  buttonTestLabel: 'and no access course button',
40
49
  priority: Priority.FUTURE_NOT_YET_OPEN,
50
+ priorityLabel: 'FUTURE_NOT_YET_OPEN',
41
51
  expectButton: false,
52
+ expectLabelTemplate:
53
+ 'You are enrolled for this session. It starts %fromRelativeDate%, the %fromDate%.',
42
54
  },
43
55
  {
44
56
  buttonTestLabel: 'and no access course button',
45
57
  priority: Priority.FUTURE_CLOSED,
58
+ priorityLabel: 'FUTURE_CLOSED',
46
59
  expectButton: false,
60
+ expectLabelTemplate:
61
+ 'You are enrolled for this session. It starts %fromRelativeDate%, the %fromDate%.',
47
62
  },
48
63
  {
49
64
  buttonTestLabel: 'and access course button',
50
65
  priority: Priority.ONGOING_CLOSED,
66
+ priorityLabel: 'ONGOING_CLOSED',
51
67
  expectButton: true,
68
+ expectLabelTemplate: `You are enrolled for this session. It's open from %fromDate% to %toDate%`,
52
69
  },
53
70
  {
54
71
  buttonTestLabel: 'and access course button',
55
72
  priority: Priority.ARCHIVED_CLOSED,
73
+ priorityLabel: 'ARCHIVED_CLOSED',
56
74
  expectButton: true,
75
+ expectLabelTemplate: `You are enrolled for this session.`,
57
76
  },
58
77
  {
59
78
  buttonTestLabel: 'and no access course button',
60
79
  priority: Priority.TO_BE_SCHEDULED,
80
+ priorityLabel: 'TO_BE_SCHEDULED',
61
81
  expectButton: false,
82
+ expectLabelTemplate:
83
+ 'You are enrolled for this session. It starts %fromRelativeDate%, the %fromDate%.',
62
84
  },
63
85
  ])(
64
- 'handles enrollments with priority=$priority $buttonTestLabel',
65
- async ({ priority, expectButton }) => {
86
+ 'handles enrollments with priority=$priorityLabel $buttonTestLabel',
87
+ async ({ priority, expectButton, expectLabelTemplate }) => {
66
88
  const enrollment: Enrollment = EnrollmentFactory().one();
67
89
  enrollment.course_run.state.priority = priority;
68
90
  render(<Enrolled enrollment={enrollment} />, { wrapper });
69
- await screen.findByText(
70
- 'You are enrolled for the session from ' +
71
- new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
72
- new Date(enrollment.course_run.start),
73
- ) +
74
- ' to ' +
75
- new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
76
- new Date(enrollment.course_run.end),
77
- ),
91
+ const intl = createIntl({ locale: 'en' });
92
+
93
+ const fromDate = new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
94
+ new Date(enrollment.course_run.start),
95
+ );
96
+ const fromRelativeDate = formatRelativeDate(
97
+ new Date(enrollment.course_run.start),
98
+ new Date(),
99
+ intl.locale,
78
100
  );
101
+ const toDate = new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
102
+ new Date(enrollment.course_run.end),
103
+ );
104
+
105
+ const expectLabel = expectLabelTemplate
106
+ .replace('%fromRelativeDate%', fromRelativeDate)
107
+ .replace('%fromDate%', fromDate)
108
+ .replace('%toDate%', toDate);
109
+ expect(await screen.findByText(expectLabel)).toBeInTheDocument();
79
110
  if (expectButton) {
80
111
  const link = screen.getByRole('link', { name: 'Access to course' });
81
112
  expect(link).toBeEnabled();
@@ -4,8 +4,8 @@ import { faker } from '@faker-js/faker';
4
4
  import { TargetCourseFactory } from 'utils/test/factories/joanie';
5
5
  import { StorybookHelper } from 'utils/StorybookHelper';
6
6
  import { Priority } from 'types';
7
- import { enrollment } from './stories.mock';
8
- import { DashboardItemCourseEnrolling } from './DashboardItemCourseEnrolling';
7
+ import { enrollment } from '../stories.mock';
8
+ import { DashboardItemCourseEnrolling } from '.';
9
9
 
10
10
  export default {
11
11
  component: DashboardItemCourseEnrolling,
@@ -13,11 +13,12 @@ import {
13
13
  } from 'types/Joanie';
14
14
  import { Spinner } from 'components/Spinner';
15
15
  import Banner, { BannerType } from 'components/Banner';
16
- import useDateFormat, { DEFAULT_DATE_FORMAT } from 'hooks/useDateFormat';
17
16
  import { Icon, IconTypeEnum } from 'components/Icon';
17
+ import useDateFormat from 'hooks/useDateFormat';
18
18
  import { orderNeedsSignature } from 'widgets/Dashboard/components/DashboardItem/utils/order';
19
- import { RouterButton } from '../RouterButton';
20
- import { useEnroll } from '../../hooks/useEnroll';
19
+ import { RouterButton } from 'widgets/Dashboard/components/RouterButton';
20
+ import { useEnroll } from 'widgets/Dashboard/hooks/useEnroll';
21
+ import useCourseRunPeriodMessage from './hooks/useCourseRunPeriodMessage';
21
22
 
22
23
  const messages = defineMessages({
23
24
  notEnrolled: {
@@ -41,16 +42,6 @@ const messages = defineMessages({
41
42
  description: 'Button to access course when the user is enrolled',
42
43
  defaultMessage: 'Access to course',
43
44
  },
44
- runPeriod: {
45
- id: 'components.DashboardItemEnrollment.runPeriod',
46
- description: 'Text to display the period of a course run',
47
- defaultMessage: 'From {startDate} to {endDate}',
48
- },
49
- enrolledRunPeriod: {
50
- id: 'components.DashboardItemEnrollment.enrolledRunPeriod',
51
- description: 'Text to display the period of a course run',
52
- defaultMessage: 'You are enrolled for the session from {startDate} to {endDate}',
53
- },
54
45
  enrolled: {
55
46
  id: 'components.DashboardItemEnrollment.enrolled',
56
47
  description: 'Text shown when user is enrolled in a course run',
@@ -152,7 +143,7 @@ const DashboardItemCourseEnrollingRuns = ({
152
143
  const { enroll, isLoading, error } = useEnroll(enrollments, order);
153
144
 
154
145
  // Hide runs with finished enrollment.
155
- const datas = useMemo(() => {
146
+ const courseRunOpenForEnrollmentList = useMemo(() => {
156
147
  const activeEnrollment = CoursesHelper.findActiveEnrollment(course, enrollments);
157
148
  return course.course_runs
158
149
  .map((courseRun) => ({
@@ -160,6 +151,9 @@ const DashboardItemCourseEnrollingRuns = ({
160
151
  selected: activeEnrollment?.course_run.id === courseRun.id,
161
152
  }))
162
153
  .filter(
154
+ // FIXME(rlecellier): question!
155
+ // does that mean the we hide the enrollment when user cannot enroll?
156
+ // even if he's already enrolled ?
163
157
  (data) => data.selected || data.courseRun.state.priority <= Priority.FUTURE_NOT_YET_OPEN,
164
158
  );
165
159
  }, [course, enrollments]);
@@ -168,13 +162,13 @@ const DashboardItemCourseEnrollingRuns = ({
168
162
  return (
169
163
  <div className="dashboard-item__course-enrolling__runs">
170
164
  {error && <Banner message={error} type={BannerType.ERROR} />}
171
- {datas.length === 0 && (
165
+ {courseRunOpenForEnrollmentList.length === 0 && (
172
166
  <div className="dashboard-item__course-enrolling__no-runs">
173
167
  <Icon name={IconTypeEnum.WARNING} size="small" />
174
168
  <FormattedMessage {...messages.noCourseRunAvailable} />
175
169
  </div>
176
170
  )}
177
- {datas.map((data) => (
171
+ {courseRunOpenForEnrollmentList.map((data) => (
178
172
  <DashboardItemCourseEnrollingRun
179
173
  key={data.courseRun.id}
180
174
  courseRun={data.courseRun}
@@ -217,6 +211,7 @@ export const DashboardItemCourseEnrollingRun = ({
217
211
  }: DashboardItemCourseEnrollingRunProps) => {
218
212
  const intl = useIntl();
219
213
  const formatDate = useDateFormat();
214
+ const courseRunPeriodMessage = useCourseRunPeriodMessage(courseRun, selected);
220
215
  const haveToSignContract = order ? orderNeedsSignature(order, product) : false;
221
216
  const isOpenedForEnrollment = useMemo(
222
217
  () => courseRun.state.priority < Priority.FUTURE_NOT_YET_OPEN,
@@ -239,16 +234,11 @@ export const DashboardItemCourseEnrollingRun = ({
239
234
  )}
240
235
  <strong>{courseRun.title}</strong>
241
236
  </p>
242
- <FormattedMessage
243
- {...(selected ? messages.enrolledRunPeriod : messages.runPeriod)}
244
- values={{
245
- startDate: formatDate(courseRun.start, DEFAULT_DATE_FORMAT),
246
- endDate: formatDate(courseRun.end, DEFAULT_DATE_FORMAT),
247
- }}
248
- />
237
+ {courseRunPeriodMessage}
249
238
  </div>
250
239
  {courseRun.state.priority === Priority.FUTURE_NOT_YET_OPEN && (
251
240
  <div className="dashboard-item__course-enrolling__run__not-opened">
241
+ -{' '}
252
242
  <FormattedMessage
253
243
  {...messages.enrollmentNotYetOpened}
254
244
  values={{ enrollment_start: formatDate(courseRun.enrollment_start) }}
@@ -256,18 +246,22 @@ export const DashboardItemCourseEnrollingRun = ({
256
246
  </div>
257
247
  )}
258
248
  </div>
259
- <div>
260
- {selected ? (
261
- <Button
262
- color="secondary"
263
- size="small"
264
- href={courseRun.resource_link}
265
- data-testid="dashboard-item-enrollment__button"
266
- className="dashboard-item__button"
267
- >
268
- <FormattedMessage {...messages.accessCourse} />
269
- </Button>
270
- ) : (
249
+ {selected ? (
250
+ SHOW_ACCESS_COURSE_PRIORITIES.includes(courseRun.state.priority) && (
251
+ <div>
252
+ <Button
253
+ color="secondary"
254
+ size="small"
255
+ href={courseRun.resource_link}
256
+ data-testid="dashboard-item-enrollment__button"
257
+ className="dashboard-item__button"
258
+ >
259
+ <FormattedMessage {...messages.accessCourse} />
260
+ </Button>
261
+ </div>
262
+ )
263
+ ) : (
264
+ <div>
271
265
  <Button
272
266
  disabled={!isOpenedForEnrollment || haveToSignContract}
273
267
  color="tertiary"
@@ -277,8 +271,8 @@ export const DashboardItemCourseEnrollingRun = ({
277
271
  >
278
272
  <FormattedMessage {...messages.enrollRun} />
279
273
  </Button>
280
- )}
281
- </div>
274
+ </div>
275
+ )}
282
276
  </div>
283
277
  );
284
278
  };
@@ -313,7 +307,7 @@ const NotEnrolled = ({
313
307
  );
314
308
  };
315
309
 
316
- const SHOW_ACCESS_COURSE_PRIORITIES = [
310
+ export const SHOW_ACCESS_COURSE_PRIORITIES = [
317
311
  Priority.ONGOING_OPEN,
318
312
  Priority.ARCHIVED_OPEN,
319
313
  Priority.ONGOING_CLOSED,
@@ -327,11 +321,16 @@ export const Enrolled = ({
327
321
  icon?: boolean;
328
322
  enrollment: Enrollment;
329
323
  }) => {
324
+ const courseRunPeriodMessage = useCourseRunPeriodMessage(enrollment.course_run, true);
330
325
  return (
331
326
  <>
332
327
  <div className="dashboard-item__block__status">
333
328
  {icon && <Icon name={IconTypeEnum.SCHOOL} />}
334
- <EnrolledStatus enrollment={enrollment} />
329
+ {enrollment.is_active ? (
330
+ courseRunPeriodMessage
331
+ ) : (
332
+ <FormattedMessage {...messages.statusNotActive} />
333
+ )}
335
334
  </div>
336
335
  {SHOW_ACCESS_COURSE_PRIORITIES.includes(enrollment.course_run.state.priority) && (
337
336
  <Button
@@ -347,21 +346,3 @@ export const Enrolled = ({
347
346
  </>
348
347
  );
349
348
  };
350
-
351
- const EnrolledStatus = ({ enrollment }: { enrollment: Enrollment }) => {
352
- const formatDate = useDateFormat();
353
-
354
- if (!enrollment.is_active) {
355
- return <FormattedMessage {...messages.statusNotActive} />;
356
- }
357
-
358
- return (
359
- <FormattedMessage
360
- {...messages.enrolledRunPeriod}
361
- values={{
362
- startDate: formatDate(enrollment.course_run.start, DEFAULT_DATE_FORMAT),
363
- endDate: formatDate(enrollment.course_run.end, DEFAULT_DATE_FORMAT),
364
- }}
365
- />
366
- );
367
- };
@@ -51,16 +51,13 @@ describe('<DashboardItemEnrollment/>', () => {
51
51
  expect(link).toBeEnabled();
52
52
  expect(link).toHaveAttribute('href', enrollment.course_run.resource_link);
53
53
 
54
- screen.getByText(
55
- 'You are enrolled for the session from ' +
56
- new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
57
- new Date(enrollment.course_run.start),
58
- ) +
59
- ' to ' +
60
- new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
61
- new Date(enrollment.course_run.end),
62
- ),
54
+ const fromDate = new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
55
+ new Date(enrollment.course_run.start),
63
56
  );
57
+ const toDate = new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
58
+ new Date(enrollment.course_run.end),
59
+ );
60
+ screen.getByText(`You are enrolled for this session. It's open from ${fromDate} to ${toDate}`);
64
61
  });
65
62
 
66
63
  it('renders a closed enrollment', () => {
@@ -80,16 +77,7 @@ describe('<DashboardItemEnrollment/>', () => {
80
77
  const link = screen.getByRole('link', { name: 'Access to course' });
81
78
  expect(link).toBeEnabled();
82
79
  expect(link).toHaveAttribute('href', enrollment.course_run.resource_link);
83
- screen.getByText(
84
- 'You are enrolled for the session from ' +
85
- new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
86
- new Date(enrollment.course_run.start),
87
- ) +
88
- ' to ' +
89
- new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
90
- new Date(enrollment.course_run.end),
91
- ),
92
- );
80
+ expect(screen.getByText(/You are enrolled for this session./)).toBeInTheDocument();
93
81
  });
94
82
 
95
83
  it('renders an inactive enrollment', () => {
@@ -1,6 +1,6 @@
1
1
  import { useMemo } from 'react';
2
2
  import { Enrollment, isCertificateProduct } from 'types/Joanie';
3
- import { Enrolled } from '../DashboardItemCourseEnrolling';
3
+ import { Enrolled } from '../CourseEnrolling';
4
4
  import { DashboardItem } from '..';
5
5
  import ProductCertificateFooter from './ProductCertificateFooter';
6
6
 
@@ -16,7 +16,10 @@ import { PropsWithChildren } from 'react';
16
16
  import fetchMock from 'fetch-mock';
17
17
  import { createMemoryRouter, RouterProvider } from 'react-router-dom';
18
18
  import { DEFAULT_DATE_FORMAT } from 'hooks/useDateFormat';
19
- import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
19
+ import {
20
+ CourseStateFactory,
21
+ RichieContextFactory as mockRichieContextFactory,
22
+ } from 'utils/test/factories/richie';
20
23
  import {
21
24
  CertificateFactory,
22
25
  CourseLightFactory,
@@ -213,18 +216,19 @@ describe('<DashboardItemOrder/>', () => {
213
216
  await screen.findByRole('heading', { level: 5, name: product.title });
214
217
  await screen.findByText('Ref. ' + (order.course as CourseLight).code);
215
218
  await screen.findByText('On going');
219
+ const fromDate = new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
220
+ new Date(order.target_enrollments[0].course_run.start),
221
+ );
222
+ const toDate = new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
223
+ new Date(order.target_enrollments[0].course_run.end),
224
+ );
216
225
  await resolveAll(order.target_courses, async (course) => {
217
226
  await screen.findByRole('heading', { level: 6, name: course.title });
218
- screen.getByText(
219
- 'You are enrolled for the session from ' +
220
- new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
221
- new Date(order.target_enrollments[0].course_run.start),
222
- ) +
223
- ' to ' +
224
- new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
225
- new Date(order.target_enrollments[0].course_run.end),
226
- ),
227
- );
227
+ expect(
228
+ screen.getByText(
229
+ `You are enrolled for this session. It's open from ${fromDate} to ${toDate}`,
230
+ ),
231
+ ).toBeInTheDocument();
228
232
  screen.getByRole('link', { name: 'Access to course' });
229
233
  });
230
234
  });
@@ -269,7 +273,15 @@ describe('<DashboardItemOrder/>', () => {
269
273
 
270
274
  it('renders a writable order with enrolled target course', async () => {
271
275
  const order: CredentialOrder = CredentialOrderFactory({
272
- target_courses: TargetCourseFactory().many(1),
276
+ target_courses: [
277
+ TargetCourseFactory({
278
+ course_runs: [
279
+ CourseRunFactory({
280
+ state: CourseStateFactory({ priority: Priority.ONGOING_OPEN }).one(),
281
+ }).one(),
282
+ ],
283
+ }).one(),
284
+ ],
273
285
  }).one();
274
286
  // Make target course enrolled.
275
287
  order.target_enrollments = [
@@ -297,13 +309,17 @@ describe('<DashboardItemOrder/>', () => {
297
309
  // Expect the first courseRun to be enrolled but not the others.
298
310
  if (i === 0) {
299
311
  expect(queryByRole(runElement, 'button', { name: 'Enroll' })).toBeNull();
300
- getByText(
301
- runElement,
302
- 'You are enrolled for the session from ' +
303
- new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(new Date(courseRun.start)) +
304
- ' to ' +
305
- new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(new Date(courseRun.end)),
312
+ const fromDate = new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
313
+ new Date(courseRun.start),
306
314
  );
315
+ const toDate = new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
316
+ new Date(courseRun.end),
317
+ );
318
+ expect(
319
+ screen.getByText(
320
+ `You are enrolled for this session. It's open from ${fromDate} to ${toDate}`,
321
+ ),
322
+ ).toBeInTheDocument();
307
323
  const button = getByRole(runElement, 'link', { name: 'Access to course' });
308
324
  expect(button).toHaveAttribute('href', courseRun.resource_link);
309
325
  } else {
@@ -323,7 +339,15 @@ describe('<DashboardItemOrder/>', () => {
323
339
  it('renders a writable order with not enrolled target course and enrolls it', async () => {
324
340
  // Initial order without enrollment.
325
341
  const order: CredentialOrder = CredentialOrderFactory({
326
- target_courses: TargetCourseFactory().many(1),
342
+ target_courses: [
343
+ TargetCourseFactory({
344
+ course_runs: [
345
+ CourseRunFactory({
346
+ state: CourseStateFactory({ priority: Priority.ONGOING_OPEN }).one(),
347
+ }).one(),
348
+ ],
349
+ }).one(),
350
+ ],
327
351
  target_enrollments: [],
328
352
  }).one();
329
353
  const { product } = mockCourseProductWithOrder(order);
@@ -451,7 +475,16 @@ describe('<DashboardItemOrder/>', () => {
451
475
  it('renders a writable order with enrolled target course and changes the enrollment', async () => {
452
476
  // Initial order with first course run enrolled.
453
477
  const order: CredentialOrder = CredentialOrderFactory({
454
- target_courses: TargetCourseFactory().many(1),
478
+ target_courses: [
479
+ TargetCourseFactory({
480
+ course_runs: [
481
+ CourseRunFactory().one(),
482
+ CourseRunFactory({
483
+ state: CourseStateFactory({ priority: Priority.ONGOING_OPEN }).one(),
484
+ }).one(),
485
+ ],
486
+ }).one(),
487
+ ],
455
488
  }).one();
456
489
  const initialEnrolledCourseRun = order.target_courses[0].course_runs[0];
457
490
  order.target_enrollments = EnrollmentFactory({ course_run: initialEnrolledCourseRun }).many(1);
@@ -550,7 +583,16 @@ describe('<DashboardItemOrder/>', () => {
550
583
  it('renders a writable order with enrolled target course and refuse the confirm message when enrolling', async () => {
551
584
  // Initial order without enrollment.
552
585
  const order: CredentialOrder = CredentialOrderFactory({
553
- target_courses: TargetCourseFactory().many(1),
586
+ target_courses: [
587
+ TargetCourseFactory({
588
+ course_runs: [
589
+ CourseRunFactory({
590
+ state: CourseStateFactory({ priority: Priority.ONGOING_OPEN }).one(),
591
+ }).one(),
592
+ CourseRunFactory().one(),
593
+ ],
594
+ }).one(),
595
+ ],
554
596
  }).one();
555
597
 
556
598
  const initialEnrolledCourseRun = order.target_courses[0].course_runs[0];
@@ -620,11 +662,20 @@ describe('<DashboardItemOrder/>', () => {
620
662
  it('renders a writable order with non-enrolled (is_active=false) target course and changes the enrollment', async () => {
621
663
  // Initial order with first course run enrolled.
622
664
  const order: CredentialOrder = CredentialOrderFactory({
623
- target_courses: TargetCourseFactory().many(1),
665
+ target_courses: TargetCourseFactory({
666
+ course_runs: [
667
+ CourseRunFactory({
668
+ state: CourseStateFactory({ priority: Priority.ONGOING_OPEN }).one(),
669
+ }).one(),
670
+ ],
671
+ }).many(1),
624
672
  }).one();
625
673
 
626
674
  const courseRun = order.target_courses[0].course_runs[0];
627
- const enrollment = EnrollmentFactory({ course_run: courseRun, is_active: false }).one();
675
+ const enrollment = EnrollmentFactory({
676
+ course_run: courseRun,
677
+ is_active: false,
678
+ }).one();
628
679
  order.target_enrollments = [enrollment];
629
680
 
630
681
  // When the existing enrollment will be set as is_active: true.
@@ -725,6 +776,7 @@ describe('<DashboardItemOrder/>', () => {
725
776
  new Intl.DateTimeFormat('en', DEFAULT_DATE_FORMAT).format(
726
777
  new Date(order.target_courses[0].course_runs[0].enrollment_start),
727
778
  ),
779
+ { exact: false },
728
780
  );
729
781
 
730
782
  // Enroll button should be disabled.
@@ -759,7 +811,7 @@ describe('<DashboardItemOrder/>', () => {
759
811
 
760
812
  // The course run should be shown as enrolled even if is it past.
761
813
  const runElement = screen.getByTestId('dashboard-item__course-enrolling__run__' + courseRun.id);
762
- getByRole(runElement, 'link', { name: 'Access to course' });
814
+ expect(screen.queryByRole('link', { name: 'Access to course' })).not.toBeInTheDocument();
763
815
  expect(queryByRole(runElement, 'button', { name: 'Enroll' })).toBeNull();
764
816
  });
765
817
 
@@ -13,7 +13,7 @@ import { useCourseProduct } from 'hooks/useCourseProducts';
13
13
  import { orderNeedsSignature } from 'widgets/Dashboard/components/DashboardItem/utils/order';
14
14
 
15
15
  import { DashboardSubItemsList } from '../DashboardSubItemsList';
16
- import { DashboardItemCourseEnrolling } from '../DashboardItemCourseEnrolling';
16
+ import { DashboardItemCourseEnrolling } from '../CourseEnrolling';
17
17
  import { DashboardItem } from '../index';
18
18
  import { DashboardItemContract } from '../Contract';
19
19
  import OrderStateMessage from './OrderStateMessage';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "richie-education",
3
- "version": "2.25.0-b2.dev69",
3
+ "version": "2.25.0-b2.dev71",
4
4
  "description": "A CMS to build learning portals for Open Education",
5
5
  "main": "sandbox/manage.py",
6
6
  "scripts": {