From a86a8b522bc29fdfd7c87f7d37b18dc814479ea1 Mon Sep 17 00:00:00 2001 From: Zainab Amir Date: Fri, 8 Sep 2023 12:08:41 +0500 Subject: [PATCH] Recommendations v2 (#1040) * feat: add personalized recommendations (#1024) * use Algolia for personalized recommendations * show personalized recommendations to use that have consented to functional cookies * update tests VAN-1599 * Revert "fix: special characters in redirect url getting decoded to space (#1029)" (#1030) This reverts commit fc622413320aecbb93587674c1a41f6c59330264. * feat: update recommendations page design (#1036) VAN-1598 * feat: add events for recommendations (#1039) * feat: remove static recommendations --------- Co-authored-by: Syed Sajjad Hussain Shah <52817156+syedsajjadkazmii@users.noreply.github.com> --- .env | 2 +- package-lock.json | 222 +++++++++++++++++- package.json | 3 + src/config/index.js | 6 +- src/data/algolia.js | 20 ++ src/data/oneTrust.js | 3 + src/data/tests/algolia.test.js | 16 ++ src/data/{utils => tests}/dataUtils.test.js | 2 +- src/data/{utils => tests}/reduxUtils.test.js | 2 +- .../ProgressiveProfiling.jsx | 38 +-- .../tests/ProgressiveProfiling.test.jsx | 39 +-- .../ProductCard/BaseCard/index.jsx | 4 +- src/recommendations/ProductCard/index.jsx | 5 +- src/recommendations/RecommendationsList.jsx | 20 +- src/recommendations/RecommendationsPage.jsx | 135 +++++------ .../LargeLayout.jsx | 61 +++++ .../SmallLayout.jsx | 61 +++++ .../SmallLayout.test.jsx | 51 ++++ .../data/algoliaResultsParser.js | 29 +++ src/recommendations/data/constants.js | 8 +- .../data/hooks/useAlgoliaRecommendations.jsx | 77 ++++++ .../data/hooks/useProducts.jsx | 22 -- .../data/loadingCoursesPlaceholders.js | 36 +++ src/recommendations/data/service.js | 22 -- src/recommendations/data/tests/hooks.test.jsx | 56 +++++ src/recommendations/data/tests/mockedData.js | 88 +++++++ .../data/tests/parser.test.jsx | 26 ++ .../data/tests/test_utils/test_utils.jsx | 63 +++++ src/recommendations/messages.js | 7 +- src/recommendations/optimizelyExperiment.js | 42 ---- .../tests/RecommendationsPage.test.jsx | 102 ++++++-- src/recommendations/tests/track.test.js | 131 +++++++++++ src/recommendations/track.js | 64 +++-- src/sass/_recommendations_page.scss | 85 ++----- 34 files changed, 1200 insertions(+), 348 deletions(-) create mode 100644 src/data/algolia.js create mode 100644 src/data/oneTrust.js create mode 100644 src/data/tests/algolia.test.js rename src/data/{utils => tests}/dataUtils.test.js (88%) rename src/data/{utils => tests}/reduxUtils.test.js (92%) create mode 100644 src/recommendations/RecommendationsPageLayouts/LargeLayout.jsx create mode 100644 src/recommendations/RecommendationsPageLayouts/SmallLayout.jsx create mode 100644 src/recommendations/RecommendationsPageLayouts/SmallLayout.test.jsx create mode 100644 src/recommendations/data/algoliaResultsParser.js create mode 100644 src/recommendations/data/hooks/useAlgoliaRecommendations.jsx delete mode 100644 src/recommendations/data/hooks/useProducts.jsx create mode 100644 src/recommendations/data/loadingCoursesPlaceholders.js delete mode 100644 src/recommendations/data/service.js create mode 100644 src/recommendations/data/tests/hooks.test.jsx create mode 100644 src/recommendations/data/tests/mockedData.js create mode 100644 src/recommendations/data/tests/parser.test.jsx create mode 100644 src/recommendations/data/tests/test_utils/test_utils.jsx delete mode 100644 src/recommendations/optimizelyExperiment.js create mode 100644 src/recommendations/tests/track.test.js diff --git a/.env b/.env index cc64009467..26cf7ac76d 100644 --- a/.env +++ b/.env @@ -25,7 +25,7 @@ SEARCH_CATALOG_URL='' DISABLE_ENTERPRISE_LOGIN='' ENABLE_DYNAMIC_REGISTRATION_FIELDS='' ENABLE_PROGRESSIVE_PROFILING_ON_AUTHN='' -ENABLE_POPULAR_AND_TRENDING_RECOMMENDATIONS='' +ENABLE_POST_REGISTRATION_RECOMMENDATIONS='' MARKETING_EMAILS_OPT_IN='' SHOW_CONFIGURABLE_EDX_FIELDS='' # ***** Zendesk related keys ***** diff --git a/package-lock.json b/package-lock.json index 72518a2e60..10e5ce3a69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,10 @@ "@fortawesome/react-fontawesome": "^0.2.0", "@optimizely/react-sdk": "^2.9.1", "@redux-devtools/extension": "3.2.5", + "@testing-library/react": "^12.1.5", + "@testing-library/react-hooks": "^8.0.1", "algoliasearch": "^4.14.3", + "algoliasearch-helper": "^3.14.0", "classnames": "2.3.2", "core-js": "3.32.0", "fastest-levenshtein": "1.0.16", @@ -146,6 +149,11 @@ "@algolia/transporter": "4.19.1" } }, + "node_modules/@algolia/events": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + }, "node_modules/@algolia/logger-common": { "version": "4.19.1", "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.19.1.tgz", @@ -5774,6 +5782,158 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@testing-library/dom": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", + "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@testing-library/dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/dom/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react": { + "version": "12.1.5", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.5.tgz", + "integrity": "sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.0.0", + "@types/react-dom": "<18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "<18.0.0", + "react-dom": "<18.0.0" + } + }, + "node_modules/@testing-library/react-hooks": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz", + "integrity": "sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "react-error-boundary": "^3.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0", + "react": "^16.9.0 || ^17.0.0", + "react-dom": "^16.9.0 || ^17.0.0", + "react-test-renderer": "^16.9.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-test-renderer": { + "optional": true + } + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -5790,6 +5950,11 @@ "node": ">=10.13.0" } }, + "node_modules/@types/aria-query": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", + "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==" + }, "node_modules/@types/babel__core": { "version": "7.20.1", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", @@ -6055,15 +6220,23 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "node_modules/@types/react": { - "version": "18.2.11", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.11.tgz", - "integrity": "sha512-+hsJr9hmwyDecSMQAmX7drgbDpyE+EgSF6t7+5QEBAn1tQK7kl1vWZ4iRf6SjQ8lk7dyEULxUmZOIpN0W5baZA==", + "version": "17.0.63", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.63.tgz", + "integrity": "sha512-T+aaG8RlIkgJ4VzWLJYbMW9QX7sIAV8CcuyV6FU6Hm7yu3Bee1YBZQRu2vYEm/dU8kre+/mzl2aGYh5MFgVLaQ==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, + "node_modules/@types/react-dom": { + "version": "17.0.20", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.20.tgz", + "integrity": "sha512-4pzIjSxDueZZ90F52mU3aPoogkHIoSIDG+oQ+wQK7Cy2B9S+MvOqY0uEA/qawKz381qrEDkvpwyt8Bm31I8sbA==", + "dependencies": { + "@types/react": "^17" + } + }, "node_modules/@types/react-redux": { "version": "7.1.25", "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.25.tgz", @@ -6635,6 +6808,17 @@ "@algolia/transporter": "4.19.1" } }, + "node_modules/algoliasearch-helper": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.14.0.tgz", + "integrity": "sha512-gXDXzsSS0YANn5dHr71CUXOo84cN4azhHKUbg71vAWnH+1JBiR4jf7to3t3JHXknXkbV0F7f055vUSBKrltHLQ==", + "dependencies": { + "@algolia/events": "^4.0.1" + }, + "peerDependencies": { + "algoliasearch": ">= 3.1 < 6" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -9276,6 +9460,11 @@ "node": ">=6.0.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==" + }, "node_modules/dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -15766,6 +15955,14 @@ "yallist": "^3.0.2" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/mailto-link": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mailto-link/-/mailto-link-2.0.0.tgz", @@ -18276,6 +18473,21 @@ "react": ">= 16.8 || 18.0.0" } }, + "node_modules/react-error-boundary": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", + "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "node_modules/react-error-overlay": { "version": "6.0.11", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", @@ -18577,7 +18789,7 @@ "version": "16.15.0", "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", - "dev": true, + "devOptional": true, "dependencies": { "object-assign": "^4.1.1", "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" @@ -18632,7 +18844,7 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz", "integrity": "sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==", - "dev": true, + "devOptional": true, "dependencies": { "object-assign": "^4.1.1", "react-is": "^17.0.2", diff --git a/package.json b/package.json index f2603e4fdd..2702928385 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,10 @@ "@fortawesome/react-fontawesome": "^0.2.0", "@optimizely/react-sdk": "^2.9.1", "@redux-devtools/extension": "3.2.5", + "@testing-library/react": "^12.1.5", + "@testing-library/react-hooks": "^8.0.1", "algoliasearch": "^4.14.3", + "algoliasearch-helper": "^3.14.0", "classnames": "2.3.2", "core-js": "3.32.0", "fastest-levenshtein": "1.0.16", diff --git a/src/config/index.js b/src/config/index.js index ff01c92fe7..59fbe82fba 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -6,7 +6,7 @@ const configuration = { DISABLE_ENTERPRISE_LOGIN: process.env.DISABLE_ENTERPRISE_LOGIN || '', ENABLE_DYNAMIC_REGISTRATION_FIELDS: process.env.ENABLE_DYNAMIC_REGISTRATION_FIELDS || false, ENABLE_PROGRESSIVE_PROFILING_ON_AUTHN: process.env.ENABLE_PROGRESSIVE_PROFILING_ON_AUTHN || false, - ENABLE_POPULAR_AND_TRENDING_RECOMMENDATIONS: process.env.ENABLE_POPULAR_AND_TRENDING_RECOMMENDATIONS || false, + ENABLE_POST_REGISTRATION_RECOMMENDATIONS: process.env.ENABLE_POST_REGISTRATION_RECOMMENDATIONS || false, MARKETING_EMAILS_OPT_IN: process.env.MARKETING_EMAILS_OPT_IN || '', SHOW_CONFIGURABLE_EDX_FIELDS: process.env.SHOW_CONFIGURABLE_EDX_FIELDS || false, // Links @@ -26,12 +26,12 @@ const configuration = { BANNER_IMAGE_EXTRA_SMALL: process.env.BANNER_IMAGE_EXTRA_SMALL || '', // Recommendation constants GENERAL_RECOMMENDATIONS: process.env.GENERAL_RECOMMENDATIONS || '[]', - POPULAR_PRODUCTS: process.env.POPULAR_PRODUCTS || '[]', - TRENDING_PRODUCTS: process.env.TRENDING_PRODUCTS || '[]', // Miscellaneous INFO_EMAIL: process.env.INFO_EMAIL || '', ZENDESK_KEY: process.env.ZENDESK_KEY, ZENDESK_LOGO_URL: process.env.ZENDESK_LOGO_URL, + ALGOLIA_APP_ID: process.env.ALGOLIA_APP_ID || '', + ALGOLIA_SEARCH_API_KEY: process.env.ALGOLIA_SEARCH_API_KEY || '', }; export default configuration; diff --git a/src/data/algolia.js b/src/data/algolia.js new file mode 100644 index 0000000000..789dd1f0fd --- /dev/null +++ b/src/data/algolia.js @@ -0,0 +1,20 @@ +import { getConfig } from '@edx/frontend-platform'; +import algoliasearch from 'algoliasearch'; + +// initialize Algolia workers +const initializeSearchClient = () => algoliasearch( + getConfig().ALGOLIA_APP_ID, + getConfig().ALGOLIA_SEARCH_API_KEY, +); + +const getLocationRestrictionFilter = (userCountry) => { + if (userCountry) { + return `NOT blocked_in:"${userCountry}" AND (allowed_in:"null" OR allowed_in:"${userCountry}")`; + } + return ''; +}; + +export { + initializeSearchClient, + getLocationRestrictionFilter, +}; diff --git a/src/data/oneTrust.js b/src/data/oneTrust.js new file mode 100644 index 0000000000..b3f08bd6e7 --- /dev/null +++ b/src/data/oneTrust.js @@ -0,0 +1,3 @@ +const isOneTrustFunctionalCookieEnabled = () => !!window?.OnetrustActiveGroups?.includes('C0003'); + +export default isOneTrustFunctionalCookieEnabled; diff --git a/src/data/tests/algolia.test.js b/src/data/tests/algolia.test.js new file mode 100644 index 0000000000..720308d520 --- /dev/null +++ b/src/data/tests/algolia.test.js @@ -0,0 +1,16 @@ +import { getLocationRestrictionFilter } from '../algolia'; + +describe('algoliaUtilsTests', () => { + it('test getLocationRestrictionFilter returns filter if country is passed', () => { + const countryCode = 'PK'; + const filter = getLocationRestrictionFilter(countryCode); + const expectedFilter = `NOT blocked_in:"${countryCode}" AND (allowed_in:"null" OR allowed_in:"${countryCode}")`; + expect(filter).toEqual(expectedFilter); + }); + it('test getLocationRestrictionFilter returns empty string if country is not passed', () => { + const countryCode = ''; + const filter = getLocationRestrictionFilter(countryCode); + const expectedFilter = ''; + expect(filter).toEqual(expectedFilter); + }); +}); diff --git a/src/data/utils/dataUtils.test.js b/src/data/tests/dataUtils.test.js similarity index 88% rename from src/data/utils/dataUtils.test.js rename to src/data/tests/dataUtils.test.js index 21a2dec8f2..9362e195d7 100644 --- a/src/data/utils/dataUtils.test.js +++ b/src/data/tests/dataUtils.test.js @@ -1,5 +1,5 @@ -import { updatePathWithQueryParams } from './dataUtils'; import { LOGIN_PAGE } from '../constants'; +import { updatePathWithQueryParams } from '../utils/dataUtils'; describe('updatePathWithQueryParams', () => { it('should append query params into the path', () => { diff --git a/src/data/utils/reduxUtils.test.js b/src/data/tests/reduxUtils.test.js similarity index 92% rename from src/data/utils/reduxUtils.test.js rename to src/data/tests/reduxUtils.test.js index 3e5a3d802c..5a78205336 100644 --- a/src/data/utils/reduxUtils.test.js +++ b/src/data/tests/reduxUtils.test.js @@ -1,4 +1,4 @@ -import AsyncActionType from './reduxUtils'; +import AsyncActionType from '../utils/reduxUtils'; describe('AsyncActionType', () => { it('should return well formatted action strings', () => { diff --git a/src/progressive-profiling/ProgressiveProfiling.jsx b/src/progressive-profiling/ProgressiveProfiling.jsx index b10abfe15b..8b5878fe18 100644 --- a/src/progressive-profiling/ProgressiveProfiling.jsx +++ b/src/progressive-profiling/ProgressiveProfiling.jsx @@ -35,12 +35,9 @@ import { FAILURE_STATE, PENDING_STATE, } from '../data/constants'; +import isOneTrustFunctionalCookieEnabled from '../data/oneTrust'; import { getAllPossibleQueryParams, isHostAvailableInQueryParams } from '../data/utils'; import { FormFieldRenderer } from '../field-renderer'; -import { - activateRecommendationsExperiment, RECOMMENDATIONS_EXP_VARIATION, -} from '../recommendations/optimizelyExperiment'; -import { trackRecommendationsGroup, trackRecommendationsViewed } from '../recommendations/track'; const ProgressiveProfiling = (props) => { const { formatMessage } = useIntl(); @@ -56,7 +53,10 @@ const ProgressiveProfiling = (props) => { const queryParams = getAllPossibleQueryParams(); const authenticatedUser = getAuthenticatedUser() || location.state?.authenticatedUser; - const enablePopularAndTrendingRecommendations = getConfig().ENABLE_POPULAR_AND_TRENDING_RECOMMENDATIONS; + const functionalCookiesConsent = isOneTrustFunctionalCookieEnabled(); + const enablePostRegistrationRecommendations = ( + getConfig().ENABLE_POST_REGISTRATION_RECOMMENDATIONS && functionalCookiesConsent + ); const [registrationResult, setRegistrationResult] = useState({ redirectUrl: '' }); const [formFieldData, setFormFieldData] = useState({ fields: {}, extendedProfile: [] }); @@ -102,21 +102,27 @@ const ProgressiveProfiling = (props) => { }, [authenticatedUser]); useEffect(() => { + if (!enablePostRegistrationRecommendations) { + sendTrackEvent( + 'edx.bi.user.recommendations.not.enabled', + { functionalCookiesConsent, page: 'authn_recommendations' }, + ); + return; + } + if (registrationResult.redirectUrl && authenticatedUser?.userId) { const redirectQueryParams = getAllPossibleQueryParams(registrationResult.redirectUrl); - if (enablePopularAndTrendingRecommendations && !('enrollment_action' in redirectQueryParams) && !queryParams?.next) { - const userIdStr = authenticatedUser.userId.toString(); - const variation = activateRecommendationsExperiment(userIdStr); - const showRecommendations = variation === RECOMMENDATIONS_EXP_VARIATION; - - trackRecommendationsGroup(variation, authenticatedUser.userId); - setShowRecommendationsPage(showRecommendations); - if (!showRecommendations) { - trackRecommendationsViewed([], '', true, authenticatedUser.userId); - } + if (!('enrollment_action' in redirectQueryParams || queryParams?.next)) { + setShowRecommendationsPage(true); } } - }, [authenticatedUser, enablePopularAndTrendingRecommendations, registrationResult.redirectUrl, queryParams?.next]); + }, [ + authenticatedUser, + enablePostRegistrationRecommendations, + functionalCookiesConsent, + registrationResult.redirectUrl, + queryParams?.next, + ]); if ( !authenticatedUser diff --git a/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx b/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx index 8fbe69e6a8..d49fcc294f 100644 --- a/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx +++ b/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx @@ -16,7 +16,6 @@ import { FAILURE_STATE, RECOMMENDATIONS, } from '../../data/constants'; -import { activateRecommendationsExperiment } from '../../recommendations/optimizelyExperiment'; import { saveUserProfile } from '../data/actions'; import ProgressiveProfiling from '../ProgressiveProfiling'; @@ -35,11 +34,6 @@ jest.mock('@edx/frontend-platform/auth', () => ({ jest.mock('@edx/frontend-platform/logging', () => ({ getLoggingService: jest.fn(), })); -jest.mock('../../recommendations/optimizelyExperiment.js', () => ({ - activateRecommendationsExperiment: jest.fn(), - trackRecommendationViewedOptimizely: jest.fn(), - RECOMMENDATIONS_EXP_VARIATION: 'welcome_page_recommendations_enabled', -})); jest.mock('react-router-dom', () => { const mockNavigation = jest.fn(); @@ -220,8 +214,9 @@ describe('ProgressiveProfilingTests', () => { }); describe('Recommendations test', () => { + window.OnetrustActiveGroups = 'C0003'; mergeConfig({ - ENABLE_POPULAR_AND_TRENDING_RECOMMENDATIONS: true, + ENABLE_POST_REGISTRATION_RECOMMENDATIONS: true, }); it('should redirect to recommendations page if recommendations are enabled', () => { @@ -232,41 +227,13 @@ describe('ProgressiveProfilingTests', () => { success: true, }, }); - activateRecommendationsExperiment.mockImplementation(() => 'welcome_page_recommendations_enabled'); const progressiveProfilingPage = mount(reduxWrapper()); expect(progressiveProfilingPage.find('button.btn-brand').text()).toEqual('Next'); expect(mockNavigate).toHaveBeenCalledWith(RECOMMENDATIONS); }); - it('should fire segments recommendations viewed and variation group events', () => { - const viewedEventProperties = { - page: 'authn_recommendations', - products: [], - recommendation_type: '', - is_control: true, - user_id: 3, - }; - const groupEventProperties = { - page: 'authn_recommendations', - variation: 'control', - user_id: 3, - }; - activateRecommendationsExperiment.mockImplementation(() => 'control'); - store = mockStore({ - ...initialState, - welcomePage: { - ...initialState.welcomePage, - success: true, - }, - }); - - mount(reduxWrapper()); - expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.user.recommendations.group', groupEventProperties); - expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.user.recommendations.viewed', viewedEventProperties); - }); - - it('should not redirect to recommendations page if user is on its way to enroll in a course', () => { + it('should not redirect to recommendations page if user is on its way to enroll in a course', async () => { const redirectUrl = `${getConfig().LMS_BASE_URL}${DEFAULT_REDIRECT_URL}?enrollment_action=1`; useLocation.mockReturnValue({ state: { diff --git a/src/recommendations/ProductCard/BaseCard/index.jsx b/src/recommendations/ProductCard/BaseCard/index.jsx index 6c541b081d..8a8f0b7608 100644 --- a/src/recommendations/ProductCard/BaseCard/index.jsx +++ b/src/recommendations/ProductCard/BaseCard/index.jsx @@ -15,9 +15,9 @@ const BaseCard = ({ productTypeCopy, footer, handleOnClick, - isLoading = false, + isLoading, }) => ( -
+
{ const { formatMessage } = useIntl(); @@ -66,7 +67,6 @@ const ProductCard = ({ trackRecommendationClick( product, position + 1, - false, userId, ); }; @@ -82,6 +82,7 @@ const ProductCard = ({ productTypeCopy={productTypeCopy} productType={productType} variant={variant} + isLoading={isLoading} footer={(