@@ -19,4 +22,8 @@ export const WidgetSidebar = () => {
return null;
};
+WidgetSidebar.propTypes = {
+ setSidebarShowing: PropTypes.func.isRequired,
+};
+
export default WidgetSidebar;
diff --git a/src/containers/WidgetContainers/NoCoursesSidebar/index.test.jsx b/src/containers/WidgetContainers/NoCoursesSidebar/index.test.jsx
index be4af29..b9bffdb 100644
--- a/src/containers/WidgetContainers/NoCoursesSidebar/index.test.jsx
+++ b/src/containers/WidgetContainers/NoCoursesSidebar/index.test.jsx
@@ -1,6 +1,7 @@
import { shallow } from 'enzyme';
import hooks from 'widgets/ProductRecommendations/hooks';
+import { mockFooterRecommendationsHook } from 'widgets/ProductRecommendations/testData';
import WidgetSidebar from '.';
jest.mock('widgets/LookingForChallengeWidget', () => 'LookingForChallengeWidget');
@@ -9,17 +10,28 @@ jest.mock('widgets/ProductRecommendations/hooks', () => ({
}));
describe('WidgetSidebar', () => {
+ beforeEach(() => jest.resetAllMocks());
+ const props = {
+ setSidebarShowing: jest.fn(),
+ };
+
describe('snapshots', () => {
test('default', () => {
- hooks.useShowRecommendationsFooter.mockReturnValueOnce(false);
- const wrapper = shallow(
);
+ hooks.useShowRecommendationsFooter.mockReturnValueOnce(
+ mockFooterRecommendationsHook.dontShowOrLoad,
+ );
+ const wrapper = shallow(
);
+ expect(props.setSidebarShowing).toHaveBeenCalledWith(true);
expect(wrapper).toMatchSnapshot();
});
});
test('is hidden if footer is shown', () => {
- hooks.useShowRecommendationsFooter.mockReturnValueOnce(true);
- const wrapper = shallow(
);
+ hooks.useShowRecommendationsFooter.mockReturnValueOnce(
+ mockFooterRecommendationsHook.showDontLoad,
+ );
+ const wrapper = shallow(
);
+ expect(props.setSidebarShowing).not.toHaveBeenCalled();
expect(wrapper.type()).toBeNull();
});
});
diff --git a/src/containers/WidgetContainers/WidgetFooter/index.jsx b/src/containers/WidgetContainers/WidgetFooter/index.jsx
index 86dbd52..45b6ace 100644
--- a/src/containers/WidgetContainers/WidgetFooter/index.jsx
+++ b/src/containers/WidgetContainers/WidgetFooter/index.jsx
@@ -4,9 +4,9 @@ import ProductRecommendations from 'widgets/ProductRecommendations';
import hooks from 'widgets/ProductRecommendations/hooks';
export const WidgetFooter = () => {
- const showRecommendationsFooter = hooks.useShowRecommendationsFooter();
+ const { shouldShowFooter, shouldLoadFooter } = hooks.useShowRecommendationsFooter();
- if (showRecommendationsFooter) {
+ if (shouldShowFooter && shouldLoadFooter) {
return (
diff --git a/src/containers/WidgetContainers/WidgetFooter/index.test.jsx b/src/containers/WidgetContainers/WidgetFooter/index.test.jsx
index 9854462..7c73936 100644
--- a/src/containers/WidgetContainers/WidgetFooter/index.test.jsx
+++ b/src/containers/WidgetContainers/WidgetFooter/index.test.jsx
@@ -1,6 +1,7 @@
import { shallow } from 'enzyme';
import hooks from 'widgets/ProductRecommendations/hooks';
+import { mockFooterRecommendationsHook } from 'widgets/ProductRecommendations/testData';
import WidgetFooter from '.';
jest.mock('widgets/LookingForChallengeWidget', () => 'LookingForChallengeWidget');
@@ -11,14 +12,26 @@ jest.mock('widgets/ProductRecommendations/hooks', () => ({
describe('WidgetFooter', () => {
describe('snapshots', () => {
test('default', () => {
- hooks.useShowRecommendationsFooter.mockReturnValueOnce(true);
+ hooks.useShowRecommendationsFooter.mockReturnValueOnce(
+ mockFooterRecommendationsHook.showAndLoad,
+ );
const wrapper = shallow(
);
expect(wrapper).toMatchSnapshot();
});
});
- test('is hidden when hook returns false', () => {
- hooks.useShowRecommendationsFooter.mockReturnValueOnce(false);
+ test('is hidden when shouldShowFooter is false but shouldLoadFooter is true', () => {
+ hooks.useShowRecommendationsFooter.mockReturnValueOnce(
+ mockFooterRecommendationsHook.loadDontShow,
+ );
+ const wrapper = shallow(
);
+ expect(wrapper.type()).toBeNull();
+ });
+
+ test('is hidden when shouldLoadFooter is false but shouldShowFooter is true', () => {
+ hooks.useShowRecommendationsFooter.mockReturnValueOnce(
+ mockFooterRecommendationsHook.showDontLoad,
+ );
const wrapper = shallow(
);
expect(wrapper.type()).toBeNull();
});
diff --git a/src/setupTest.jsx b/src/setupTest.jsx
index 0cbd1fb..60b4af2 100755
--- a/src/setupTest.jsx
+++ b/src/setupTest.jsx
@@ -14,6 +14,7 @@ jest.mock('react', () => ({
useEffect: jest.fn((cb, prereqs) => ({ useEffect: { cb, prereqs } })),
useMemo: jest.fn((cb, prereqs) => cb(prereqs)),
useContext: jest.fn(context => context),
+ useState: jest.fn(),
}));
jest.mock('reselect', () => ({
diff --git a/src/test/app.test.jsx b/src/test/app.test.jsx
index 2b51b81..6a38aa6 100644
--- a/src/test/app.test.jsx
+++ b/src/test/app.test.jsx
@@ -42,8 +42,8 @@ jest.unmock('react-redux');
jest.unmock('reselect');
jest.unmock('hooks');
-jest.mock('containers/WidgetContainers/LoadedSidebar', () => 'loaded-widget-sidebar');
-jest.mock('containers/WidgetContainers/NoCoursesSidebar', () => 'no-courses-widget-sidebar');
+jest.mock('containers/WidgetContainers/LoadedSidebar', () => jest.fn(() => 'loaded-widget-sidebar'));
+jest.mock('containers/WidgetContainers/NoCoursesSidebar', () => jest.fn(() => 'no-courses-widget-sidebar'));
jest.mock('components/NoticesWrapper', () => 'notices-wrapper');
jest.mock('@edx/frontend-platform', () => ({
diff --git a/src/widgets/ProductRecommendations/api.js b/src/widgets/ProductRecommendations/api.js
index f216070..827e7f3 100644
--- a/src/widgets/ProductRecommendations/api.js
+++ b/src/widgets/ProductRecommendations/api.js
@@ -1,10 +1,15 @@
import { get, stringifyUrl } from 'data/services/lms/utils';
import urls from 'data/services/lms/urls';
-export const productRecommendationsUrl = (courseId) => `${urls.api}/learner_recommendations/product_recommendations/${courseId}/`;
+export const crossProductAndAmplitudeRecommendationsUrl = (courseId) => `${urls.getApiUrl()}/learner_recommendations/product_recommendations/${courseId}/`;
+export const amplitudeRecommendationsUrl = () => `${urls.getApiUrl()}/learner_recommendations/product_recommendations/`;
-const fetchProductRecommendations = (courseId) => get(stringifyUrl(productRecommendationsUrl(courseId)));
+const fetchCrossProductRecommendations = (courseId) => (
+ get(stringifyUrl(crossProductAndAmplitudeRecommendationsUrl(courseId)))
+);
+const fetchAmplitudeRecommendations = () => get(stringifyUrl(amplitudeRecommendationsUrl()));
export default {
- fetchProductRecommendations,
+ fetchCrossProductRecommendations,
+ fetchAmplitudeRecommendations,
};
diff --git a/src/widgets/ProductRecommendations/api.test.js b/src/widgets/ProductRecommendations/api.test.js
index 3be3f9e..5bce09f 100644
--- a/src/widgets/ProductRecommendations/api.test.js
+++ b/src/widgets/ProductRecommendations/api.test.js
@@ -1,5 +1,6 @@
import { get, stringifyUrl } from 'data/services/lms/utils';
-import api, { productRecommendationsUrl } from './api';
+
+import api, { crossProductAndAmplitudeRecommendationsUrl, amplitudeRecommendationsUrl } from './api';
jest.mock('data/services/lms/utils', () => ({
stringifyUrl: (...args) => ({ stringifyUrl: args }),
@@ -7,10 +8,18 @@ jest.mock('data/services/lms/utils', () => ({
}));
describe('productRecommendationCourses api', () => {
- describe('fetchProductRecommendations', () => {
+ describe('fetchCrossProductRecommendations', () => {
it('calls get with the correct recommendation courses URL', () => {
- expect(api.fetchProductRecommendations('CourseRunKey')).toEqual(
- get(stringifyUrl(productRecommendationsUrl('CourseRunKey'))),
+ expect(api.fetchCrossProductRecommendations('CourseRunKey')).toEqual(
+ get(stringifyUrl(crossProductAndAmplitudeRecommendationsUrl('CourseRunKey'))),
+ );
+ });
+ });
+
+ describe('fetchAmplitudeRecommendations', () => {
+ it('calls get with the correct recommendation courses URL', () => {
+ expect(api.fetchAmplitudeRecommendations()).toEqual(
+ get(stringifyUrl(amplitudeRecommendationsUrl())),
);
});
});
diff --git a/src/widgets/ProductRecommendations/hooks.js b/src/widgets/ProductRecommendations/hooks.js
index a97da65..61b3cff 100644
--- a/src/widgets/ProductRecommendations/hooks.js
+++ b/src/widgets/ProductRecommendations/hooks.js
@@ -13,42 +13,56 @@ export const state = StrictDict({
});
export const useShowRecommendationsFooter = () => {
- const hasCourses = reduxHooks.useHasCourses();
const hasAvailableDashboards = reduxHooks.useHasAvailableDashboards();
- const initIsPending = reduxHooks.useRequestIsPending(RequestKeys.initialize);
+ const hasRequestCompleted = reduxHooks.useRequestIsCompleted(RequestKeys.initialize);
// Hardcoded to not show until experiment related code is implemented
- return !initIsPending && hasCourses && !hasAvailableDashboards && false;
+ return {
+ shouldShowFooter: false,
+ shouldLoadFooter: hasRequestCompleted && !hasAvailableDashboards,
+ };
};
export const useMostRecentCourseRunKey = () => {
- const mostRecentCourse = reduxHooks.useCurrentCourseList({
+ const mostRecentCourseRunKey = reduxHooks.useCurrentCourseList({
sortBy: SortKeys.enrolled,
filters: [],
pageSize: 0,
- }).visible[0].courseRun.courseId;
+ }).visible[0]?.courseRun?.courseId;
- return mostRecentCourse;
+ return mostRecentCourseRunKey;
};
-export const useFetchProductRecommendations = (setRequestState, setData) => {
+export const useFetchRecommendations = (setRequestState, setData) => {
const courseRunKey = module.useMostRecentCourseRunKey();
useEffect(() => {
let isMounted = true;
- api
- .fetchProductRecommendations(courseRunKey)
- .then((response) => {
- if (isMounted) {
- setData(response.data);
- setRequestState(RequestStates.completed);
- }
- })
- .catch(() => {
- if (isMounted) {
- setRequestState(RequestStates.failed);
- }
- });
+
+ const handleSuccess = (response) => {
+ if (isMounted) {
+ setData(response.data);
+ setRequestState(RequestStates.completed);
+ }
+ };
+
+ const handleError = () => {
+ if (isMounted) {
+ setRequestState(RequestStates.failed);
+ }
+ };
+
+ if (courseRunKey) {
+ api
+ .fetchCrossProductRecommendations(courseRunKey)
+ .then(handleSuccess)
+ .catch(handleError);
+ } else {
+ api
+ .fetchAmplitudeRecommendations()
+ .then(handleSuccess)
+ .catch(handleError);
+ }
return () => {
isMounted = false;
};
@@ -59,7 +73,7 @@ export const useFetchProductRecommendations = (setRequestState, setData) => {
export const useProductRecommendationsData = () => {
const [requestState, setRequestState] = module.state.requestState(RequestStates.pending);
const [data, setData] = module.state.data({});
- module.useFetchProductRecommendations(setRequestState, setData);
+ module.useFetchRecommendations(setRequestState, setData);
return {
productRecommendations: data,
diff --git a/src/widgets/ProductRecommendations/hooks.test.js b/src/widgets/ProductRecommendations/hooks.test.js
index 1531997..8ed332d 100644
--- a/src/widgets/ProductRecommendations/hooks.test.js
+++ b/src/widgets/ProductRecommendations/hooks.test.js
@@ -10,15 +10,15 @@ import api from './api';
import * as hooks from './hooks';
jest.mock('./api', () => ({
- fetchProductRecommendations: jest.fn(),
+ fetchCrossProductRecommendations: jest.fn(),
+ fetchAmplitudeRecommendations: jest.fn(),
}));
jest.mock('hooks', () => ({
reduxHooks: {
useCurrentCourseList: jest.fn(),
- useHasCourses: jest.fn(),
useHasAvailableDashboards: jest.fn(),
- useRequestIsPending: jest.fn(),
+ useRequestIsCompleted: jest.fn(),
},
}));
@@ -38,11 +38,16 @@ const courses = [
},
];
-const courseListData = {
+const populatedCourseListData = {
visible: courses,
numPages: 0,
};
+const emptyCourseListData = {
+ visible: [],
+ numPages: 0,
+};
+
let output;
describe('ProductRecommendations hooks', () => {
beforeEach(() => {
@@ -56,7 +61,7 @@ describe('ProductRecommendations hooks', () => {
describe('useMostRecentCourseRunKey', () => {
it('returns the courseId of the first course in the sorted visible array', () => {
- reduxHooks.useCurrentCourseList.mockReturnValueOnce(courseListData);
+ reduxHooks.useCurrentCourseList.mockReturnValueOnce(populatedCourseListData);
expect(hooks.useMostRecentCourseRunKey()).toBe(mostRecentCourseRunKey);
});
@@ -64,16 +69,17 @@ describe('ProductRecommendations hooks', () => {
describe('useShowRecommendationsFooter', () => {
// TODO: Update when hardcoded value is removed
- it('returns whether the footer widget should show', () => {
- reduxHooks.useHasCourses.mockReturnValueOnce(true);
+ it('returns whether the footer widget should show and should load', () => {
reduxHooks.useHasAvailableDashboards.mockReturnValueOnce(false);
- reduxHooks.useRequestIsPending.mockReturnValueOnce(false);
+ reduxHooks.useRequestIsCompleted.mockReturnValueOnce(true);
+ const { shouldShowFooter, shouldLoadFooter } = hooks.useShowRecommendationsFooter();
- expect(hooks.useShowRecommendationsFooter()).toBeFalsy();
+ expect(shouldShowFooter).toBeFalsy();
+ expect(shouldLoadFooter).toBeTruthy();
});
});
- describe('useFetchProductRecommendations', () => {
+ describe('useFetchRecommendations', () => {
describe('behavior', () => {
describe('useEffect call', () => {
let calls;
@@ -81,86 +87,176 @@ describe('ProductRecommendations hooks', () => {
const response = { data: 'response data' };
const setRequestState = jest.fn();
const setData = jest.fn();
- beforeEach(() => {
- reduxHooks.useCurrentCourseList.mockReturnValue(courseListData);
- hooks.useFetchProductRecommendations(setRequestState, setData);
+
+ const setUp = (mockCourseListData) => {
+ reduxHooks.useCurrentCourseList.mockReturnValue(mockCourseListData);
+ hooks.useFetchRecommendations(setRequestState, setData);
({ calls } = React.useEffect.mock);
([[cb]] = calls);
- });
+ };
+
it('calls useEffect once', () => {
+ setUp(populatedCourseListData);
expect(calls.length).toEqual(1);
});
- it('calls fetchProductRecommendations with the most recently enrolled courseId', () => {
- api.fetchProductRecommendations.mockReturnValueOnce(Promise.resolve(response));
- cb();
- expect(api.fetchProductRecommendations).toHaveBeenCalledWith(mostRecentCourseRunKey);
- });
- describe('successful fetch on mounted component', () => {
- it('sets the request state to completed and loads response', async () => {
- let resolveFn;
- api.fetchProductRecommendations.mockReturnValueOnce(new Promise(resolve => {
- resolveFn = resolve;
- }));
+ describe('without no courseId due to no enrolled courses', () => {
+ it('calls fetchAmplitudeRecommendations', () => {
+ setUp(emptyCourseListData);
+ api.fetchAmplitudeRecommendations.mockReturnValueOnce(Promise.resolve(response));
cb();
- expect(api.fetchProductRecommendations).toHaveBeenCalledWith(mostRecentCourseRunKey);
- expect(setRequestState).not.toHaveBeenCalled();
- expect(setData).not.toHaveBeenCalled();
- resolveFn(response);
- await waitFor(() => {
- expect(setRequestState).toHaveBeenCalledWith(RequestStates.completed);
- expect(setData).toHaveBeenCalledWith(response.data);
+ expect(api.fetchAmplitudeRecommendations).toHaveBeenCalled();
+ });
+ });
+ describe('with most recently enrolled courseId', () => {
+ it('calls fetchCrossProductRecommendations with the most recently enrolled courseId', () => {
+ setUp(populatedCourseListData);
+ api.fetchCrossProductRecommendations.mockReturnValueOnce(Promise.resolve(response));
+ cb();
+ expect(api.fetchCrossProductRecommendations).toHaveBeenCalledWith(mostRecentCourseRunKey);
+ });
+ });
+ describe('fetching cross product recommendations', () => {
+ beforeEach(() => setUp(populatedCourseListData));
+
+ describe('successful fetch on mounted component', () => {
+ it('sets the request state to completed and loads response', async () => {
+ let resolveFn;
+ api.fetchCrossProductRecommendations.mockReturnValueOnce(new Promise(resolve => {
+ resolveFn = resolve;
+ }));
+ cb();
+ expect(api.fetchCrossProductRecommendations).toHaveBeenCalledWith(mostRecentCourseRunKey);
+ expect(setRequestState).not.toHaveBeenCalled();
+ expect(setData).not.toHaveBeenCalled();
+ resolveFn(response);
+ await waitFor(() => {
+ expect(setRequestState).toHaveBeenCalledWith(RequestStates.completed);
+ expect(setData).toHaveBeenCalledWith(response.data);
+ });
});
});
- });
- describe('successful fetch on unmounted component', () => {
- it('does not set the state', async () => {
- let resolveFn;
- api.fetchProductRecommendations.mockReturnValueOnce(new Promise(resolve => {
- resolveFn = resolve;
- }));
- const unMount = cb();
- expect(api.fetchProductRecommendations).toHaveBeenCalledWith(mostRecentCourseRunKey);
- expect(setRequestState).not.toHaveBeenCalled();
- expect(setData).not.toHaveBeenCalled();
- unMount();
- resolveFn(response);
- await wait(10);
- expect(setRequestState).not.toHaveBeenCalled();
- expect(setData).not.toHaveBeenCalled();
+ describe('successful fetch on unmounted component', () => {
+ it('does not set the state', async () => {
+ let resolveFn;
+ api.fetchCrossProductRecommendations.mockReturnValueOnce(new Promise(resolve => {
+ resolveFn = resolve;
+ }));
+ const unMount = cb();
+ expect(api.fetchCrossProductRecommendations).toHaveBeenCalledWith(mostRecentCourseRunKey);
+ expect(setRequestState).not.toHaveBeenCalled();
+ expect(setData).not.toHaveBeenCalled();
+ unMount();
+ resolveFn(response);
+ await wait(10);
+ expect(setRequestState).not.toHaveBeenCalled();
+ expect(setData).not.toHaveBeenCalled();
+ });
});
- });
- describe('unsuccessful fetch on mounted component', () => {
- it('sets the request state to failed and does not set the data state', async () => {
- let rejectFn;
- api.fetchProductRecommendations.mockReturnValueOnce(new Promise((resolve, reject) => {
- rejectFn = reject;
- }));
- cb();
- expect(api.fetchProductRecommendations).toHaveBeenCalledWith(mostRecentCourseRunKey);
- expect(setRequestState).not.toHaveBeenCalled();
- expect(setData).not.toHaveBeenCalled();
- rejectFn();
- await waitFor(() => {
- expect(setRequestState).toHaveBeenCalledWith(RequestStates.failed);
+ describe('unsuccessful fetch on mounted component', () => {
+ it('sets the request state to failed and does not set the data state', async () => {
+ let rejectFn;
+ api.fetchCrossProductRecommendations.mockReturnValueOnce(new Promise((resolve, reject) => {
+ rejectFn = reject;
+ }));
+ cb();
+ expect(api.fetchCrossProductRecommendations).toHaveBeenCalledWith(mostRecentCourseRunKey);
+ expect(setRequestState).not.toHaveBeenCalled();
+ expect(setData).not.toHaveBeenCalled();
+ rejectFn();
+ await waitFor(() => {
+ expect(setRequestState).toHaveBeenCalledWith(RequestStates.failed);
+ expect(setData).not.toHaveBeenCalled();
+ });
+ });
+ });
+ describe('unsuccessful fetch on unmounted component', () => {
+ it('does not set the state', async () => {
+ let rejectFn;
+ api.fetchCrossProductRecommendations.mockReturnValueOnce(new Promise((resolve, reject) => {
+ rejectFn = reject;
+ }));
+ const unMount = cb();
+ expect(api.fetchCrossProductRecommendations).toHaveBeenCalledWith(mostRecentCourseRunKey);
+ expect(setRequestState).not.toHaveBeenCalled();
+ expect(setData).not.toHaveBeenCalled();
+ unMount();
+ rejectFn();
+ await wait(10);
+ expect(setRequestState).not.toHaveBeenCalled();
expect(setData).not.toHaveBeenCalled();
});
});
});
- describe('unsuccessful fetch on unmounted component', () => {
- it('does not set the state', async () => {
- let rejectFn;
- api.fetchProductRecommendations.mockReturnValueOnce(new Promise((resolve, reject) => {
- rejectFn = reject;
- }));
- const unMount = cb();
- expect(api.fetchProductRecommendations).toHaveBeenCalledWith(mostRecentCourseRunKey);
- expect(setRequestState).not.toHaveBeenCalled();
- expect(setData).not.toHaveBeenCalled();
- unMount();
- rejectFn();
- await wait(10);
- expect(setRequestState).not.toHaveBeenCalled();
- expect(setData).not.toHaveBeenCalled();
+ describe('fetching Amplitude recommendations', () => {
+ beforeEach(() => setUp(emptyCourseListData));
+
+ describe('successful fetch on mounted component', () => {
+ it('sets the request state to completed and loads response', async () => {
+ let resolveFn;
+ api.fetchAmplitudeRecommendations.mockReturnValueOnce(new Promise(resolve => {
+ resolveFn = resolve;
+ }));
+ cb();
+ expect(api.fetchAmplitudeRecommendations).toHaveBeenCalled();
+ expect(setRequestState).not.toHaveBeenCalled();
+ expect(setData).not.toHaveBeenCalled();
+ resolveFn(response);
+ await waitFor(() => {
+ expect(setRequestState).toHaveBeenCalledWith(RequestStates.completed);
+ expect(setData).toHaveBeenCalledWith(response.data);
+ });
+ });
+ });
+ describe('successful fetch on unmounted component', () => {
+ it('does not set the state', async () => {
+ let resolveFn;
+ api.fetchAmplitudeRecommendations.mockReturnValueOnce(new Promise(resolve => {
+ resolveFn = resolve;
+ }));
+ const unMount = cb();
+ expect(api.fetchAmplitudeRecommendations).toHaveBeenCalled();
+ expect(setRequestState).not.toHaveBeenCalled();
+ expect(setData).not.toHaveBeenCalled();
+ unMount();
+ resolveFn(response);
+ await wait(10);
+ expect(setRequestState).not.toHaveBeenCalled();
+ expect(setData).not.toHaveBeenCalled();
+ });
+ });
+ describe('unsuccessful fetch on mounted component', () => {
+ it('sets the request state to failed and does not set the data state', async () => {
+ let rejectFn;
+ api.fetchAmplitudeRecommendations.mockReturnValueOnce(new Promise((resolve, reject) => {
+ rejectFn = reject;
+ }));
+ cb();
+ expect(api.fetchAmplitudeRecommendations).toHaveBeenCalled();
+ expect(setRequestState).not.toHaveBeenCalled();
+ expect(setData).not.toHaveBeenCalled();
+ rejectFn();
+ await waitFor(() => {
+ expect(setRequestState).toHaveBeenCalledWith(RequestStates.failed);
+ expect(setData).not.toHaveBeenCalled();
+ });
+ });
+ });
+ describe('unsuccessful fetch on unmounted component', () => {
+ it('does not set the state', async () => {
+ let rejectFn;
+ api.fetchAmplitudeRecommendations.mockReturnValueOnce(new Promise((resolve, reject) => {
+ rejectFn = reject;
+ }));
+ const unMount = cb();
+ expect(api.fetchAmplitudeRecommendations).toHaveBeenCalled();
+ expect(setRequestState).not.toHaveBeenCalled();
+ expect(setData).not.toHaveBeenCalled();
+ unMount();
+ rejectFn();
+ await wait(10);
+ expect(setRequestState).not.toHaveBeenCalled();
+ expect(setData).not.toHaveBeenCalled();
+ });
});
});
});
@@ -170,10 +266,10 @@ describe('ProductRecommendations hooks', () => {
let fetchSpy;
beforeEach(() => {
state.mock();
- fetchSpy = jest.spyOn(hooks, 'useFetchProductRecommendations').mockImplementationOnce(() => {});
+ fetchSpy = jest.spyOn(hooks, 'useFetchRecommendations').mockImplementationOnce(() => {});
output = hooks.useProductRecommendationsData();
});
- it('calls useFetchProductRecommendations with setRequestState and setData', () => {
+ it('calls useFetchRecommendations with setRequestState and setData', () => {
expect(fetchSpy).toHaveBeenCalledWith(state.setState.requestState, state.setState.data);
});
it('initializes requestState as RequestStates.pending', () => {
diff --git a/src/widgets/ProductRecommendations/index.jsx b/src/widgets/ProductRecommendations/index.jsx
index 467ca3b..f0dea2f 100644
--- a/src/widgets/ProductRecommendations/index.jsx
+++ b/src/widgets/ProductRecommendations/index.jsx
@@ -18,7 +18,7 @@ const ProductRecommendations = () => {
return (
);
}
diff --git a/src/widgets/ProductRecommendations/index.test.jsx b/src/widgets/ProductRecommendations/index.test.jsx
index c100cc4..f85a480 100644
--- a/src/widgets/ProductRecommendations/index.test.jsx
+++ b/src/widgets/ProductRecommendations/index.test.jsx
@@ -6,7 +6,7 @@ import hooks from './hooks';
import ProductRecommendations from './index';
import LoadingView from './components/LoadingView';
import LoadedView from './components/LoadedView';
-import { mockResponse } from './testData';
+import { mockCrossProductResponse, mockAmplitudeResponse } from './testData';
jest.mock('./hooks', () => ({
useProductRecommendationsData: jest.fn(),
@@ -25,7 +25,7 @@ describe('ProductRecommendations', () => {
const successfullLoadValues = {
...defaultValues,
isLoaded: true,
- productRecommendations: mockResponse,
+ productRecommendations: mockCrossProductResponse,
};
const desktopWindowSize = {
@@ -41,7 +41,7 @@ describe('ProductRecommendations', () => {
expect(shallow(
)).toMatchSnapshot();
});
- it('renders the LoadedView with course data if the request completed', () => {
+ it('renders the LoadedView with cross product data if the request completed', () => {
useWindowSize.mockReturnValueOnce(desktopWindowSize);
hooks.useProductRecommendationsData.mockReturnValueOnce({
...successfullLoadValues,
@@ -50,8 +50,24 @@ describe('ProductRecommendations', () => {
expect(shallow(
)).toMatchObject(
shallow(
,
+ ),
+ );
+ });
+ it('renders the LoadedView with Amplitude course data if the request completed', () => {
+ useWindowSize.mockReturnValueOnce(desktopWindowSize);
+ hooks.useProductRecommendationsData.mockReturnValueOnce({
+ ...successfullLoadValues,
+ productRecommendations: mockAmplitudeResponse,
+ });
+
+ expect(shallow(
)).toMatchObject(
+ shallow(
+
,
),
);
diff --git a/src/widgets/ProductRecommendations/testData.js b/src/widgets/ProductRecommendations/testData.js
index 71e233f..ebc9bbd 100644
--- a/src/widgets/ProductRecommendations/testData.js
+++ b/src/widgets/ProductRecommendations/testData.js
@@ -22,10 +22,21 @@ export const getCoursesWithType = (courseTypes) => {
return courses;
};
+export const mockFooterRecommendationsHook = {
+ showAndLoad: { shouldShowFooter: true, shouldLoadFooter: true },
+ showDontLoad: { shouldShowFooter: true, shouldLoadFooter: false },
+ loadDontShow: { shouldShowFooter: false, shouldLoadFooter: true },
+ dontShowOrLoad: { shouldShowFooter: false, shouldLoadFooter: false },
+};
+
export const mockCrossProductCourses = getCoursesWithType(['executive-education-2u', 'bootcamp-2u']);
export const mockOpenCourses = getCoursesWithType(['verified-audit', 'audit', 'verified', 'course']);
-export const mockResponse = {
+export const mockCrossProductResponse = {
crossProductCourses: mockCrossProductCourses,
amplitudeCourses: mockOpenCourses,
};
+
+export const mockAmplitudeResponse = {
+ amplitudeCourses: mockOpenCourses,
+};