From 1a61ba3cc74319f515b8da7581a25215ea0a90d8 Mon Sep 17 00:00:00 2001 From: Zainab Amir Date: Wed, 1 Mar 2023 17:49:38 +0500 Subject: [PATCH] feat: add support for fallback recs in case of error (#764) * feat: add support for fallback recs in case of error * feat: update segment event --- src/config/index.js | 2 +- src/recommendations/RecommendationsPage.jsx | 78 +++++++++-------- src/recommendations/messages.js | 2 +- .../tests/RecommendationsPage.test.jsx | 19 +++++ src/recommendations/tests/mockedData.js | 83 +------------------ 5 files changed, 66 insertions(+), 118 deletions(-) diff --git a/src/config/index.js b/src/config/index.js index 200dceea..a3c8b3f9 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -20,7 +20,7 @@ const configuration = { TOS_AND_HONOR_CODE: process.env.TOS_AND_HONOR_CODE || null, TOS_LINK: process.env.TOS_LINK || null, // Miscellaneous - GENERAL_RECOMMENDATIONS: process.env.GENERAL_RECOMMENDATIONS || [], + GENERAL_RECOMMENDATIONS: process.env.GENERAL_RECOMMENDATIONS || '[]', INFO_EMAIL: process.env.INFO_EMAIL || '', }; diff --git a/src/recommendations/RecommendationsPage.jsx b/src/recommendations/RecommendationsPage.jsx index a473dc86..8529854d 100644 --- a/src/recommendations/RecommendationsPage.jsx +++ b/src/recommendations/RecommendationsPage.jsx @@ -6,6 +6,7 @@ import { Hyperlink, Image, Spinner, StatefulButton, } from '@edx/paragon'; import PropTypes from 'prop-types'; +import { Helmet } from 'react-helmet'; import { DEFAULT_REDIRECT_URL } from '../data/constants'; import { EDUCATION_LEVEL_MAPPING, RECOMMENDATIONS_COUNT } from './data/constants'; @@ -27,6 +28,7 @@ const RecommendationsPage = (props) => { useEffect(() => { if (registrationResponse) { + const generalRecommendations = JSON.parse(getConfig().GENERAL_RECOMMENDATIONS); let coursesWithKeys = []; getPersonalizedRecommendations(educationLevel).then((response) => { coursesWithKeys = response.map(course => ({ @@ -37,7 +39,7 @@ const RecommendationsPage = (props) => { if (coursesWithKeys.length >= RECOMMENDATIONS_COUNT) { setRecommendations(coursesWithKeys.slice(0, RECOMMENDATIONS_COUNT)); } else { - const courseRecommendations = coursesWithKeys.concat(getConfig().GENERAL_RECOMMENDATIONS); + const courseRecommendations = coursesWithKeys.concat(generalRecommendations); // Remove duplicate recommendations const uniqueRecommendations = courseRecommendations.filter( (recommendation, index, self) => index === self.findIndex((existingRecommendation) => ( @@ -48,13 +50,14 @@ const RecommendationsPage = (props) => { } setIsLoading(false); - // We only want to track the recommendations returned by Algolia - const courseKeys = coursesWithKeys.map(course => course.courseKey); - trackRecommendationsViewed(courseKeys, false, userId); }) .catch(() => { + setRecommendations(generalRecommendations.slice(0, RECOMMENDATIONS_COUNT)); setIsLoading(false); }); + // We only want to track the recommendations returned by Algolia + const courseKeys = coursesWithKeys.map(course => course.courseKey); + trackRecommendationsViewed(courseKeys.slice(0, RECOMMENDATIONS_COUNT), false, userId); } }, [registrationResponse, DASHBOARD_URL, educationLevel, userId]); @@ -82,37 +85,44 @@ const RecommendationsPage = (props) => { }; return ( -
-
-
- - {getConfig().SITE_NAME} - -
- {(!isLoading && recommendations.length === RECOMMENDATIONS_COUNT) ? ( -
- -
- -
+ <> + + {intl.formatMessage(messages['recommendation.page.title'], + { siteName: getConfig().SITE_NAME })} + + +
+
+
+ + {getConfig().SITE_NAME} +
- ) - : ( - - )} -
+ {(!isLoading && recommendations.length === RECOMMENDATIONS_COUNT) ? ( +
+ +
+ +
+
+ ) + : ( + + )} +
+ ); }; diff --git a/src/recommendations/messages.js b/src/recommendations/messages.js index 3922a264..55052cf0 100644 --- a/src/recommendations/messages.js +++ b/src/recommendations/messages.js @@ -3,7 +3,7 @@ import { defineMessages } from '@edx/frontend-platform/i18n'; const messages = defineMessages({ 'recommendation.page.title': { id: 'recommendation.page.title', - defaultMessage: 'Recommendations| {siteName}', + defaultMessage: 'Recommendations | {siteName}', description: 'recommendation page title', }, 'recommendation.page.heading': { diff --git a/src/recommendations/tests/RecommendationsPage.test.jsx b/src/recommendations/tests/RecommendationsPage.test.jsx index 1842a951..b4dc8a66 100644 --- a/src/recommendations/tests/RecommendationsPage.test.jsx +++ b/src/recommendations/tests/RecommendationsPage.test.jsx @@ -2,6 +2,7 @@ import React from 'react'; import { Provider } from 'react-redux'; import { getConfig, mergeConfig } from '@edx/frontend-platform'; +import * as analytics from '@edx/frontend-platform/analytics'; import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n'; import { mount } from 'enzyme'; import { act } from 'react-dom/test-utils'; @@ -15,9 +16,16 @@ import { mockedGeneralRecommendations, mockedResponse } from './mockedData'; const IntlRecommendationsPage = injectIntl(RecommendationsPage); const mockStore = configureStore(); +jest.mock('@edx/frontend-platform/analytics'); jest.mock('../data/service'); +analytics.sendTrackEvent = jest.fn(); + describe('RecommendationsPageTests', () => { + mergeConfig({ + GENERAL_RECOMMENDATIONS: '[]', + }); + let defaultProps = {}; let store = {}; @@ -47,6 +55,7 @@ describe('RecommendationsPageTests', () => { location: { state: { registrationResult, + userId: 111, }, }, }; @@ -81,6 +90,16 @@ describe('RecommendationsPageTests', () => { await getRecommendationsPage(); expect(getPersonalizedRecommendations.default).toHaveBeenCalledTimes(1); + expect(analytics.sendTrackEvent).toHaveBeenCalledWith( + 'edx.bi.user.recommendations.viewed', + { + page: 'authn_recommendations', + course_key_array: [], + amplitude_recommendations: false, + is_control: false, + user_id: 111, + }, + ); }); it('should display recommendations returned by Algolia', async () => { diff --git a/src/recommendations/tests/mockedData.js b/src/recommendations/tests/mockedData.js index e3407da4..dd240d79 100644 --- a/src/recommendations/tests/mockedData.js +++ b/src/recommendations/tests/mockedData.js @@ -76,85 +76,4 @@ export const mockedResponse = [ }, ]; -export const mockedGeneralRecommendations = [ - { - courseKey: 'MITx+6.00.1x', - activeRunKey: 'course-v1:MITx+6.00.1x+1T2023', - cardImageUrl: 'https://prod-discovery.edx-cdn.org/media/course/image/956319ec-8665-4039-8bc6-32c9a9aea5e9-885268c71902.jpg', - marketingUrl: 'https://www.edx.org/course/introduction-to-computer-science-and-programming-7', - objectId: 'course-956319ec-8665-4039-8bc6-32c9a9aea5e9', - owners: [ - { - key: 'MITx', - logoImageUrl: 'https://prod-discovery.edx-cdn.org/organization/logos/2a73d2ce-c34a-4e08-8223-83bca9d2f01d-2cc8854c6fee.png', - name: 'Massachusetts Institute of Technology', - }, - ], - title: 'Introduction to Computer Science and Programming Using Python', - recommendationType: 'general', - }, - { - courseKey: 'IBM+PY0101EN', - activeRunKey: 'course-v1:IBM+PY0101EN+2T2021', - cardImageUrl: 'https://prod-discovery.edx-cdn.org/media/course/image/381a0046-5d78-4790-8776-74620d59f48e-e2e7f4677ce2.jpeg', - marketingUrl: 'https://www.edx.org/course/python-basics-for-data-science', - objectID: 'course-381a0046-5d78-4790-8776-74620d59f48e', - owners: [ - { - key: 'IBM', - logoImageUrl: 'https://prod-discovery.edx-cdn.org/organization/logos/87b07564-d569-4cfd-bee6-8b0a407acb73-dc33e4b5f353.png', - name: 'IBM', - }, - ], - title: 'Python Basics for Data Science', - recommendationType: 'general', - }, - { - courseKey: 'HarvardX+CS50P', - activeRunKey: 'course-v1:HarvardX+CS50P+Python', - cardImageUrl: 'https://prod-discovery.edx-cdn.org/media/course/image/2cc794d0-316d-42f7-bbfd-25c34e4cd5df-033e46d516c0.png', - marketingUrl: 'https://www.edx.org/course/cs50s-introduction-to-programming-with-python', - objectID: 'course-2cc794d0-316d-42f7-bbfd-25c34e4cd5df', - owners: [ - { - key: 'HarvardX', - logoImageUrl: 'https://prod-discovery.edx-cdn.org/organization/logos/44022f13-20df-4666-9111-cede3e5dc5b6-2cc39992c67a.png', - name: 'Harvard University', - }, - ], - title: 'CS50\'s Introduction to Programming with Python', - recommendationType: 'general', - }, - { - courseKey: 'UQx+IELTSx', - activeRunKey: 'course-v1:UQx+IELTSx+1T2022', - cardImageUrl: 'https://prod-discovery.edx-cdn.org/media/course/image/d61d7a1f-3333-4169-a786-92e2bf690c6f-fa8a6909baec.jpg', - marketingUrl: 'https://www.edx.org/course/ielts-academic-test-preparation', - objectID: 'course-d61d7a1f-3333-4169-a786-92e2bf690c6f', - owners: [ - { - key: 'UQx', - logoImageUrl: 'https://prod-discovery.edx-cdn.org/organization/logos/8554749f-b920-4d7f-8986-af6bb95290aa-f336c6a2ca11.png', - name: 'The University of Queensland', - }, - ], - title: 'IELTS Academic Test Preparation', - recommendationType: 'general', - }, - { - courseKey: 'HarvardX+CS50x', - activeRunKey: 'course-v1:HarvardX+CS50+X', - cardImageUrl: 'https://prod-discovery.edx-cdn.org/media/course/image/da1b2400-322b-459b-97b0-0c557f05d017-a3d1899c3344.png', - marketingUrl: 'https://www.edx.org/course/introduction-computer-science-harvardx-cs50x', - objectID: 'course-da1b2400-322b-459b-97b0-0c557f05d017', - owners: [ - { - key: 'HarvardX', - logoImageUrl: 'https://prod-discovery.edx-cdn.org/organization/logos/44022f13-20df-4666-9111-cede3e5dc5b6-2cc39992c67a.png', - name: 'Harvard University', - }, - ], - title: 'CS50\'s Introduction to Computer Science', - recommendationType: 'general', - }, -]; +export const mockedGeneralRecommendations = '[{"courseKey":"test+text1","activeRunKey":"course-v1:test+test1+2018","cardImageUrl":"https://test-recommendations.com/text-1.jpg","marketingUrl":"https://test-recommendations.com/test-1","objectId":"test-1","owners":[{"key":"Testx","logoImageUrl":"https://test-recommendations.com/organization/test-1.png","name":"General recommendation org 1"}],"title":"General recommendation 1","recommendationType":"general"},{"courseKey":"test+text2","activeRunKey":"course-v1:test+test2+2018","cardImageUrl":"https://test-recommendations.com/text-2.jpg","marketingUrl":"https://test-recommendations.com/test-2","objectId":"test-2","owners":[{"key":"Testx","logoImageUrl":"https://test-recommendations.com/organization/test-2.png","name":"General recommendation org 2"}],"title":"General recommendation 2","recommendationType":"general"},{"courseKey":"test+text3","activeRunKey":"course-v1:test+test3+2018","cardImageUrl":"https://test-recommendations.com/text-3.jpg","marketingUrl":"https://test-recommendations.com/test-3","objectId":"test-3","owners":[{"key":"Testx","logoImageUrl":"https://test-recommendations.com/organization/test-3.png","name":"General recommendation org 3"}],"title":"General recommendation 3","recommendationType":"general"},{"courseKey":"test+text4","activeRunKey":"course-v1:test+test4+2018","cardImageUrl":"https://test-recommendations.com/text-4.jpg","marketingUrl":"https://test-recommendations.com/test-4","objectId":"test-4","owners":[{"key":"Testx","logoImageUrl":"https://test-recommendations.com/organization/test-4.png","name":"General recommendation org 4"}],"title":"General recommendation 4","recommendationType":"general"},{"courseKey":"test+text5","activeRunKey":"course-v1:test+test5+2018","cardImageUrl":"https://test-recommendations.com/text-5.jpg","marketingUrl":"https://test-recommendations.com/test-5","objectId":"test-5","owners":[{"key":"Testx","logoImageUrl":"https://test-recommendations.com/organization/test-5.png","name":"General recommendation org 5"}],"title":"General recommendation 5","recommendationType":"general"}]';