richie-education 3.4.1-dev13 → 3.4.1-dev14

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.
@@ -1,7 +1,14 @@
1
1
  import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
2
2
  import { handle } from 'utils/errors/handle';
3
+ import { location } from 'utils/indirection/window';
3
4
  import LMSHandler from '.';
4
5
 
6
+ jest.mock('utils/indirection/window', () => ({
7
+ location: {
8
+ pathname: '/courses/a-test-course/',
9
+ assign: jest.fn(),
10
+ },
11
+ }));
5
12
  jest.mock('utils/context', () => ({
6
13
  __esModule: true,
7
14
  default: mockRichieContextFactory({
@@ -16,6 +23,12 @@ jest.mock('utils/context', () => ({
16
23
  endpoint: 'https://edx.endpoint/api',
17
24
  course_regexp: '.*edx.org/.*',
18
25
  },
26
+ {
27
+ backend: 'openedx-hawthorn',
28
+ endpoint: 'https://nau.endpoint/api',
29
+ course_regexp: '.*nau.org/.*',
30
+ next_url: 'richie-nau',
31
+ },
19
32
  ],
20
33
  }).one(),
21
34
  }));
@@ -24,6 +37,10 @@ const mockHandle: jest.Mock<typeof handle> = handle as any;
24
37
  jest.mock('utils/errors/handle');
25
38
 
26
39
  describe('API LMS', () => {
40
+ beforeEach(() => {
41
+ jest.clearAllMocks();
42
+ });
43
+
27
44
  it('returns OpenEdX API if url that match edx selector is provided', () => {
28
45
  const api = LMSHandler('https://edx.org/courses/a-test-course');
29
46
  expect(api).toBeDefined();
@@ -42,4 +59,20 @@ describe('API LMS', () => {
42
59
  new Error('No LMS Backend found for https://unknown.org/course/a-test-course.'),
43
60
  );
44
61
  });
62
+
63
+ it('uses default "richie" next prefix for openedx-hawthorn without next_url configured', () => {
64
+ const api = LMSHandler('https://edx.org/courses/a-test-course');
65
+ api.user.login();
66
+ expect(location.assign).toHaveBeenCalledWith(
67
+ `https://edx.endpoint/api/login?next=richie${location.pathname}`,
68
+ );
69
+ });
70
+
71
+ it('uses configured next_url prefix for openedx-hawthorn with next_url set', () => {
72
+ const api = LMSHandler('https://nau.org/courses/a-test-course');
73
+ api.user.login();
74
+ expect(location.assign).toHaveBeenCalledWith(
75
+ `https://nau.endpoint/api/login?next=richie-nau${location.pathname}`,
76
+ );
77
+ });
45
78
  });
@@ -15,7 +15,7 @@ const LmsAPIHandler = (url: string): APILms => {
15
15
  case APIBackend.OPENEDX_DOGWOOD:
16
16
  return OpenEdxDogwoodApiInterface(api);
17
17
  case APIBackend.OPENEDX_HAWTHORN:
18
- return OpenEdxHawthornApiInterface(api);
18
+ return OpenEdxHawthornApiInterface(api, { routes: {}, nextURL: api.next_url });
19
19
  }
20
20
 
21
21
  const error = new Error(`No LMS Backend found for ${url}.`);
@@ -3,10 +3,17 @@ import { faker } from '@faker-js/faker';
3
3
  import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
4
4
  import { handle } from 'utils/errors/handle';
5
5
  import { HttpError, HttpStatusCode } from 'utils/errors/HttpError';
6
+ import { location } from 'utils/indirection/window';
6
7
  import context from 'utils/context';
7
8
  import API from './openedx-hawthorn';
8
9
 
9
10
  jest.mock('utils/errors/handle');
11
+ jest.mock('utils/indirection/window', () => ({
12
+ location: {
13
+ pathname: '/courses/a-test-course/',
14
+ assign: jest.fn(),
15
+ },
16
+ }));
10
17
  jest.mock('utils/context', () => ({
11
18
  __esModule: true,
12
19
  default: mockRichieContextFactory({
@@ -45,6 +52,48 @@ describe('OpenEdX Hawthorn API', () => {
45
52
  });
46
53
  });
47
54
 
55
+ describe('user', () => {
56
+ beforeEach(() => {
57
+ jest.clearAllMocks();
58
+ });
59
+
60
+ describe('login', () => {
61
+ it('redirects to login with default "richie" next prefix when nextURL is not set', () => {
62
+ const api = API(LMSConf);
63
+ api.user.login();
64
+ expect(location.assign).toHaveBeenCalledWith(
65
+ `${EDX_ENDPOINT}/login?next=richie${location.pathname}`,
66
+ );
67
+ });
68
+
69
+ it('redirects to login with custom next prefix when nextURL option is provided', () => {
70
+ const api = API(LMSConf, { routes: {}, nextURL: 'richie-nau' });
71
+ api.user.login();
72
+ expect(location.assign).toHaveBeenCalledWith(
73
+ `${EDX_ENDPOINT}/login?next=richie-nau${location.pathname}`,
74
+ );
75
+ });
76
+ });
77
+
78
+ describe('register', () => {
79
+ it('redirects to register with default "richie" next prefix when nextURL is not set', () => {
80
+ const api = API(LMSConf);
81
+ api.user.register();
82
+ expect(location.assign).toHaveBeenCalledWith(
83
+ `${EDX_ENDPOINT}/register?next=richie${location.pathname}`,
84
+ );
85
+ });
86
+
87
+ it('redirects to register with custom next prefix when nextURL option is provided', () => {
88
+ const api = API(LMSConf, { routes: {}, nextURL: 'richie-ap' });
89
+ api.user.register();
90
+ expect(location.assign).toHaveBeenCalledWith(
91
+ `${EDX_ENDPOINT}/register?next=richie-ap${location.pathname}`,
92
+ );
93
+ });
94
+ });
95
+ });
96
+
48
97
  describe('enrollment', () => {
49
98
  beforeEach(() => {
50
99
  courseId = faker.string.uuid();
@@ -20,6 +20,8 @@ import { HttpError, HttpStatusCode } from 'utils/errors/HttpError';
20
20
  */
21
21
 
22
22
  const API = (APIConf: AuthenticationBackend | LMSBackend, options?: APIOptions): APILms => {
23
+ const nextURL = options?.nextURL ?? 'richie';
24
+
23
25
  const extractCourseIdFromUrl = (url: string): Maybe<Nullable<string>> => {
24
26
  const matches = url.match((APIConf as LMSBackend).course_regexp);
25
27
  return matches && matches[1] ? matches[1] : null;
@@ -61,8 +63,9 @@ const API = (APIConf: AuthenticationBackend | LMSBackend, options?: APIOptions):
61
63
  / ! \ Prefix next param with richie.
62
64
  In this way, OpenEdX Nginx conf knows that we want to go back to richie app after login/redirect
63
65
  */
64
- login: () => location.assign(`${ROUTES.user.login}?next=richie${location.pathname}`),
65
- register: () => location.assign(`${ROUTES.user.register}?next=richie${location.pathname}`),
66
+ login: () => location.assign(`${ROUTES.user.login}?next=${nextURL}${location.pathname}`),
67
+ register: () =>
68
+ location.assign(`${ROUTES.user.register}?next=${nextURL}${location.pathname}`),
66
69
  logout: async () => {
67
70
  await fetch(ROUTES.user.logout, {
68
71
  mode: 'no-cors',
package/js/types/api.ts CHANGED
@@ -63,6 +63,7 @@ export interface APIRoute {
63
63
 
64
64
  export interface APIOptions {
65
65
  routes: APIRoute;
66
+ nextURL?: string;
66
67
  }
67
68
 
68
69
  export enum APIBackend {
@@ -9,6 +9,7 @@ export interface LMSBackend {
9
9
  backend: string;
10
10
  course_regexp: RegExp | string;
11
11
  endpoint: string;
12
+ next_url?: string;
12
13
  }
13
14
 
14
15
  export interface AuthenticationBackend {
@@ -26,6 +27,7 @@ export interface AuthenticationBackend {
26
27
  username: string;
27
28
  email: string;
28
29
  };
30
+ next_url?: string;
29
31
  }
30
32
 
31
33
  enum FEATURES {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "richie-education",
3
- "version": "3.4.1-dev13",
3
+ "version": "3.4.1-dev14",
4
4
  "description": "A CMS to build learning portals for Open Education",
5
5
  "main": "sandbox/manage.py",
6
6
  "scripts": {