diff --git a/src/data/constants.js b/src/data/constants.js
index fc48e04e..eb1a1962 100644
--- a/src/data/constants.js
+++ b/src/data/constants.js
@@ -36,13 +36,3 @@ export const INVALID_NAME_REGEX = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{
// Query string parameters that can be passed to LMS to manage
// things like auto-enrollment upon login and registration.
export const AUTH_PARAMS = ['course_id', 'enrollment_action', 'course_mode', 'email_opt_in', 'purchase_workflow', 'next', 'save_for_later', 'register_for_free'];
-
-// Education difficulty level mapping
-export const EDUCATION_LEVEL_MAPPING = {
- p: 'Advanced',
- m: 'Advanced',
- b: 'Intermediate',
- a: 'Intermediate',
- hs: 'Introductory',
- jhs: 'Introductory',
-};
diff --git a/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx b/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx
index c1e749e7..b24c430e 100644
--- a/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx
+++ b/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx
@@ -7,11 +7,13 @@ import * as auth from '@edx/frontend-platform/auth';
import { configure, injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
import * as logging from '@edx/frontend-platform/logging';
import { mount } from 'enzyme';
+import { createMemoryHistory } from 'history';
import { act } from 'react-dom/test-utils';
+import { MemoryRouter, Router } from 'react-router-dom';
import configureStore from 'redux-mock-store';
import {
- COMPLETE_STATE, DEFAULT_REDIRECT_URL, FAILURE_STATE,
+ COMPLETE_STATE, DEFAULT_REDIRECT_URL, FAILURE_STATE, RECOMMENDATIONS,
} from '../../data/constants';
import { saveUserProfile } from '../data/actions';
import ProgressiveProfiling from '../ProgressiveProfiling';
@@ -31,6 +33,8 @@ auth.configure = jest.fn();
auth.ensureAuthenticatedUser = jest.fn().mockImplementation(() => Promise.resolve(true));
auth.hydrateAuthenticatedUser = jest.fn().mockImplementation(() => Promise.resolve(true));
+const history = createMemoryHistory();
+
describe('ProgressiveProfilingTests', () => {
mergeConfig({
AUTHN_PROGRESSIVE_PROFILING_SUPPORT_LINK: 'http://localhost:1999/welcome',
@@ -58,12 +62,18 @@ describe('ProgressiveProfilingTests', () => {
const reduxWrapper = children => (
- {children}
+
+ {children}
+
);
const getProgressiveProfilingPage = async () => {
- const progressiveProfilingPage = mount(reduxWrapper());
+ const progressiveProfilingPage = mount(reduxWrapper(
+
+
+ ,
+ ));
await act(async () => {
await Promise.resolve(progressiveProfilingPage);
await new Promise(resolve => setImmediate(resolve));
@@ -156,4 +166,60 @@ describe('ProgressiveProfilingTests', () => {
await getProgressiveProfilingPage();
expect(window.location.href).toBe(DASHBOARD_URL);
});
+
+ describe('Recommendations test', () => {
+ mergeConfig({
+ ENABLE_PERSONALIZED_RECOMMENDATIONS: true,
+ });
+
+ it('should redirect to recommendations page if recommendations are enabled', async () => {
+ store = mockStore({
+ welcomePage: {
+ ...initialState.welcomePage,
+ success: true,
+ },
+ });
+
+ auth.getAuthenticatedUser = jest.fn(() => ({ userId: 3, username: 'abc123' }));
+ const progressiveProfilingPage = await getProgressiveProfilingPage();
+
+ expect(progressiveProfilingPage.find('button.btn-brand').text()).toEqual('Next');
+ expect(history.location.pathname).toEqual(RECOMMENDATIONS);
+ });
+
+ it('should not redirect to recommendations page if user is on its way to enroll in a course', async () => {
+ delete window.location;
+ window.location = {
+ href: getConfig().BASE_URL,
+ assign: jest.fn().mockImplementation((value) => { window.location.href = value; }),
+ };
+
+ const redirectUrl = `${getConfig().LMS_BASE_URL}${DEFAULT_REDIRECT_URL}?enrollment_action=1`;
+ props = {
+ getFieldData: jest.fn(),
+ location: {
+ state: {
+ registrationResult: {
+ redirectUrl,
+ success: true,
+ },
+ optionalFields,
+ },
+ },
+ };
+
+ store = mockStore({
+ welcomePage: {
+ ...initialState.welcomePage,
+ success: true,
+ },
+ });
+
+ auth.getAuthenticatedUser = jest.fn(() => ({ userId: 3, username: 'abc123' }));
+ const progressiveProfilingPage = await getProgressiveProfilingPage();
+
+ expect(progressiveProfilingPage.find('button.btn-brand').text()).toEqual('Submit');
+ expect(window.location.href).toEqual(redirectUrl);
+ });
+ });
});
diff --git a/src/recommendations/RecommendationsList.jsx b/src/recommendations/RecommendationsList.jsx
index 53570554..3da2ed00 100644
--- a/src/recommendations/RecommendationsList.jsx
+++ b/src/recommendations/RecommendationsList.jsx
@@ -10,7 +10,7 @@ const RecommendationsList = (props) => {
const { title, recommendations } = props;
return (
-
+
{title}
diff --git a/src/recommendations/RecommendationsPage.jsx b/src/recommendations/RecommendationsPage.jsx
index 955710f2..02125b0d 100644
--- a/src/recommendations/RecommendationsPage.jsx
+++ b/src/recommendations/RecommendationsPage.jsx
@@ -7,31 +7,34 @@ import {
} from '@edx/paragon';
import PropTypes from 'prop-types';
-import { DEFAULT_REDIRECT_URL, EDUCATION_LEVEL_MAPPING } from '../data/constants';
+import { DEFAULT_REDIRECT_URL } from '../data/constants';
+import { EDUCATION_LEVEL_MAPPING, RECOMMENDATIONS_COUNT } from './data/constants';
import getPersonalizedRecommendations from './data/service';
import messages from './messages';
import RecommendationsList from './RecommendationsList';
const RecommendationsPage = (props) => {
const { intl, location } = props;
+ const registrationResponse = location.state?.registrationResult;
+ const DASHBOARD_URL = getConfig().LMS_BASE_URL.concat(DEFAULT_REDIRECT_URL);
const [isLoading, setIsLoading] = useState(true);
const [recommendations, setRecommendations] = useState([]);
const educationLevel = EDUCATION_LEVEL_MAPPING[location.state?.educationLevel];
- const DASHBOARD_URL = getConfig().LMS_BASE_URL.concat(DEFAULT_REDIRECT_URL);
- const registrationResponse = location.state?.registrationResult;
useEffect(() => {
- getPersonalizedRecommendations(educationLevel).then((response) => {
- if (response.length > 2) {
- setRecommendations(response);
- }
- setIsLoading(false);
- })
- .catch(() => {
+ if (registrationResponse) {
+ getPersonalizedRecommendations(educationLevel).then((response) => {
+ if (response.length) {
+ setRecommendations(response.slice(0, RECOMMENDATIONS_COUNT));
+ }
setIsLoading(false);
- });
- }, [DASHBOARD_URL, educationLevel]);
+ })
+ .catch(() => {
+ setIsLoading(false);
+ });
+ }
+ }, [registrationResponse, DASHBOARD_URL, educationLevel]);
if (!registrationResponse) {
global.location.assign(DASHBOARD_URL);
@@ -47,7 +50,7 @@ const RecommendationsPage = (props) => {
}
};
- if (!isLoading && recommendations.length < 3) {
+ if (!isLoading && recommendations.length < RECOMMENDATIONS_COUNT) {
handleRedirection();
}
@@ -64,7 +67,7 @@ const RecommendationsPage = (props) => {
- {(!isLoading && recommendations.length > 2) ? (
+ {(!isLoading && recommendations.length === RECOMMENDATIONS_COUNT) ? (
{
+ let defaultProps = {};
+ let store = {};
+
+ const registrationResult = {
+ redirectUrl: getConfig().LMS_BASE_URL.concat('/course-about-page-url'),
+ success: true,
+ };
+ const reduxWrapper = children => (
+
+ {children}
+
+ );
+
+ const getRecommendationsPage = async (props = defaultProps) => {
+ const recommendationsPage = mount(reduxWrapper());
+ await act(async () => {
+ await Promise.resolve(recommendationsPage);
+ recommendationsPage.update();
+ });
+
+ return recommendationsPage;
+ };
+
+ beforeEach(() => {
+ store = mockStore({});
+ defaultProps = {
+ location: {
+ state: {
+ registrationResult,
+ },
+ },
+ };
+ });
+
+ it('redirects to dashboard if user tries to access the page directly', async () => {
+ const DASHBOARD_URL = getConfig().LMS_BASE_URL.concat(DEFAULT_REDIRECT_URL);
+ delete window.location;
+ window.location = {
+ href: getConfig().BASE_URL,
+ assign: jest.fn().mockImplementation((value) => { window.location.href = value; }),
+ };
+ getPersonalizedRecommendations.default = jest.fn().mockImplementation(() => Promise.resolve([]));
+ await getRecommendationsPage({});
+
+ expect(getPersonalizedRecommendations.default).toHaveBeenCalledTimes(0);
+ expect(window.location.href).toEqual(DASHBOARD_URL);
+ });
+
+ it('should show loading state to user', async () => {
+ getPersonalizedRecommendations.default = jest.fn().mockImplementation(() => Promise.resolve(mockedResponse));
+ await act(async () => {
+ const recommendationsPage = mount(reduxWrapper());
+ expect(recommendationsPage.find('.centered-align-spinner').exists()).toBeTruthy();
+ });
+ });
+
+ it('should call getPersonalizedRecommendations', async () => {
+ delete window.location;
+ window.location = { assign: jest.fn() };
+ getPersonalizedRecommendations.default = jest.fn().mockImplementation(() => Promise.resolve([]));
+ await getRecommendationsPage();
+
+ expect(getPersonalizedRecommendations.default).toHaveBeenCalledTimes(1);
+ });
+
+ it('should display recommendations returned by Algolia', async () => {
+ getPersonalizedRecommendations.default = jest.fn().mockImplementation(() => Promise.resolve(mockedResponse));
+ const recommendationsPage = await getRecommendationsPage();
+
+ expect(recommendationsPage.find('#course-recommendations').exists()).toBeTruthy();
+ });
+
+ it('should redirect if recommended courses count is less than RECOMMENDATIONS_COUNT', async () => {
+ delete window.location;
+ window.location = { assign: jest.fn() };
+ getPersonalizedRecommendations.default = jest.fn().mockImplementation(() => Promise.resolve([mockedResponse[0]]));
+ const recommendationsPage = await getRecommendationsPage();
+
+ expect(recommendationsPage.find('#course-recommendations').exists()).toBeFalsy();
+ expect(window.location.href).toEqual(registrationResult.redirectUrl);
+ });
+
+ it('should display all owners for a course', async () => {
+ getPersonalizedRecommendations.default = jest.fn().mockImplementation(() => Promise.resolve(mockedResponse));
+ const recommendationsPage = await getRecommendationsPage();
+
+ expect(
+ recommendationsPage.find('.pgn__card-header-subtitle-md').getElements()[0].props.children,
+ ).toEqual('firstOwnerX, secondOwnerX');
+ });
+});
diff --git a/src/recommendations/tests/constants.js b/src/recommendations/tests/constants.js
new file mode 100644
index 00000000..0ab9c16d
--- /dev/null
+++ b/src/recommendations/tests/constants.js
@@ -0,0 +1,79 @@
+const mockedResponse = [
+ {
+ title: 'How to Learn Online 1',
+ marketingUrl: 'https://test-recommendations.com/course/how-to-learn-online-1',
+ cardImageUrl: 'https://test-recommendations.com/image/how-to-learn-online-1.png',
+ activeRunKey: 'course-v1:test+testX+2018',
+ owners: [
+ {
+ key: 'firstOwnerX',
+ logoImageUrl: 'https://test-recommendations.com/logos/how-to-learn-online-1.png',
+ name: 'first owner',
+ },
+ {
+ key: 'secondOwnerX',
+ logoImageUrl: 'https://test-recommendations.com/logos/how-to-learn-online-1.png',
+ name: 'second owner',
+ },
+ ],
+ objectId: 'course-how-to-learn-online-key-1',
+ },
+ {
+ title: 'How to Learn Online 2',
+ marketingUrl: 'https://test-recommendations.com/course/how-to-learn-online-2',
+ cardImageUrl: 'https://test-recommendations.com/image/how-to-learn-online-2.png',
+ activeRunKey: 'course-v1:test+testX+2019',
+ owners: [
+ {
+ key: 'testX',
+ logoImageUrl: 'https://test-recommendations.com/logos/how-to-learn-online-2.png',
+ name: 'test',
+ },
+ ],
+ objectId: 'course-how-to-learn-online-key-2',
+ },
+ {
+ title: 'How to Learn Online 3',
+ marketingUrl: 'https://test-recommendations.com/course/how-to-learn-online-3',
+ cardImageUrl: 'https://test-recommendations.com/image/how-to-learn-online-3.png',
+ activeRunKey: 'course-v1:test+testX+2020',
+ owners: [
+ {
+ key: 'testX',
+ logoImageUrl: 'https://test-recommendations.com/logos/how-to-learn-online-3.png',
+ name: 'test',
+ },
+ ],
+ objectId: 'course-how-to-learn-online-key-3',
+ },
+ {
+ title: 'How to Learn Online 4',
+ marketingUrl: 'https://test-recommendations.com/course/how-to-learn-online-4',
+ cardImageUrl: 'https://test-recommendations.com/image/how-to-learn-online-4.png',
+ activeRunKey: 'course-v1:test+testX+2021',
+ owners: [
+ {
+ key: 'testX',
+ logoImageUrl: 'https://test-recommendations.com/logos/how-to-learn-online-4.png',
+ name: 'test',
+ },
+ ],
+ objectId: 'course-how-to-learn-online-key-4',
+ },
+ {
+ title: 'How to Learn Online 5',
+ marketingUrl: 'https://test-recommendations.com/course/how-to-learn-online-5',
+ cardImageUrl: 'https://test-recommendations.com/image/how-to-learn-online-5.png',
+ activeRunKey: 'course-v1:test+testX+2022',
+ owners: [
+ {
+ key: 'testX',
+ logoImageUrl: 'https://test-recommendations.com/logos/how-to-learn-online-5.png',
+ name: 'test',
+ },
+ ],
+ objectId: 'course-how-to-learn-online-key-5',
+ },
+];
+
+export default mockedResponse;