From dee24c8a8c984a1e63d73240fe8851f8bc1b46e1 Mon Sep 17 00:00:00 2001 From: Katrina Nguyen <71999631+katrinan029@users.noreply.github.com> Date: Tue, 5 Mar 2024 13:27:43 -0800 Subject: [PATCH 1/2] feat: update prequery experiment logic for variant group (#988) feat: update prequery experiment logic for variant group --- package-lock.json | 36 +++++++++---------- package.json | 8 ++--- src/components/search/Search.jsx | 26 ++++++++++---- .../search/tests/SearchSections.test.jsx | 27 +++++++++++--- src/utils/optimizely.js | 1 + 5 files changed, 66 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 116d96096d..58b5271315 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,10 +11,10 @@ "dependencies": { "@edx/brand": "npm:@openedx/brand-openedx@1.2.2", "@edx/frontend-component-footer": "13.0.2", - "@edx/frontend-enterprise-catalog-search": "7.0.0", - "@edx/frontend-enterprise-hotjar": "4.0.0", - "@edx/frontend-enterprise-logistration": "6.0.0", - "@edx/frontend-enterprise-utils": "6.0.0", + "@edx/frontend-enterprise-catalog-search": "8.0.0", + "@edx/frontend-enterprise-hotjar": "5.0.0", + "@edx/frontend-enterprise-logistration": "7.0.0", + "@edx/frontend-enterprise-utils": "7.0.0", "@edx/frontend-platform": "7.1.0", "@loadable/component": "5.16.3", "@openedx/paragon": "^21.5.7", @@ -2323,11 +2323,11 @@ } }, "node_modules/@edx/frontend-enterprise-catalog-search": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-catalog-search/-/frontend-enterprise-catalog-search-7.0.0.tgz", - "integrity": "sha512-w0DMJBz8cong8I/vyoX3T4cfAyeBaV4W5pWh0rkTQIBh2bathJdD9cP4MNZeWRp7gG1z2iw9/WhWMY5jcTpraw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-catalog-search/-/frontend-enterprise-catalog-search-8.0.0.tgz", + "integrity": "sha512-hl46gWVhH09AcepKP+X+Ug9ZXYO/8N19W5Dr/9tsX8+KB18rXVRy+0wZGhVtN9doq8hBbqKi5D0WkpC1NdMIxw==", "dependencies": { - "@edx/frontend-enterprise-utils": "^6.0.0", + "@edx/frontend-enterprise-utils": "^7.0.0", "classnames": "2.2.5", "lodash.debounce": "4.0.8", "prop-types": "15.7.2" @@ -2347,9 +2347,9 @@ "integrity": "sha512-DTt3GhOUDKhh4ONwIJW4lmhyotQmV2LjNlGK/J2hkwUcqcbKkCLAdJPtxQnxnlc7SR3f1CEXCyMmc7WLUsWbNA==" }, "node_modules/@edx/frontend-enterprise-hotjar": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-hotjar/-/frontend-enterprise-hotjar-4.0.0.tgz", - "integrity": "sha512-FCXjJJ8gdluxasywt07irIgxV6uRTzjDuOCdb5QF9mlj8I3sbURxXBWeYb5wg/Dj1usBq0vhSnWNNny5iwJK8w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-hotjar/-/frontend-enterprise-hotjar-5.0.0.tgz", + "integrity": "sha512-zCV09oi9AyJE2K/4lvmnOuHmOltOHnKr2/AqHky5NuXJVSHgFx+nA9qd8LrcEGFLyM0vi8sDozgY+STNNUYAdg==", "peerDependencies": { "react": "^16.12.0 || ^17.0.0", "react-dom": "^16.12.0 || ^17.0.0", @@ -2357,11 +2357,11 @@ } }, "node_modules/@edx/frontend-enterprise-logistration": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-logistration/-/frontend-enterprise-logistration-6.0.0.tgz", - "integrity": "sha512-HLJ7YF6SicgEj1tcjNJ6iHl6lxC+EvEGIuxzfZNg/WsDW411lq43N1eQquGFfcF3uDPyKeI8e6tyWsKqyvdFyA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-logistration/-/frontend-enterprise-logistration-7.0.0.tgz", + "integrity": "sha512-uqnsXtMqRPM9dXA457SwdMnfrO3yjXOnr25sCCdJvuyZCZOYbMFh69S/iWHMad8LwZlDuI4GJC1/lknAbB+0qg==", "dependencies": { - "@edx/frontend-enterprise-utils": "^6.0.0", + "@edx/frontend-enterprise-utils": "^7.0.0", "prop-types": "15.7.2" }, "peerDependencies": { @@ -2372,9 +2372,9 @@ } }, "node_modules/@edx/frontend-enterprise-utils": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-utils/-/frontend-enterprise-utils-6.0.0.tgz", - "integrity": "sha512-DgNvCg7Q9Z2102pZgkV4b1EYRfgJIELHcYHC/9kss87Y0aY9k9BThwqkFC46f/3NluYWzVxBRKnYW5MLGls5Lg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-utils/-/frontend-enterprise-utils-7.0.0.tgz", + "integrity": "sha512-6w5wnA7WSl9qVRexUdUXBCGr9XYelT8sJUO6DWY2pV1nvntcp5Ff1POX1dW75C/827m8zMxqcDyqZsTyz9Srsw==", "dependencies": { "@testing-library/react": "12.1.4", "history": "4.10.1" diff --git a/package.json b/package.json index c8604df23a..0a32f8a729 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,10 @@ "dependencies": { "@edx/brand": "npm:@openedx/brand-openedx@1.2.2", "@edx/frontend-component-footer": "13.0.2", - "@edx/frontend-enterprise-catalog-search": "7.0.0", - "@edx/frontend-enterprise-hotjar": "4.0.0", - "@edx/frontend-enterprise-logistration": "6.0.0", - "@edx/frontend-enterprise-utils": "6.0.0", + "@edx/frontend-enterprise-catalog-search": "8.0.0", + "@edx/frontend-enterprise-hotjar": "5.0.0", + "@edx/frontend-enterprise-logistration": "7.0.0", + "@edx/frontend-enterprise-utils": "7.0.0", "@edx/frontend-platform": "7.1.0", "@loadable/component": "5.16.3", "@openedx/paragon": "^21.5.7", diff --git a/src/components/search/Search.jsx b/src/components/search/Search.jsx index 856a85a808..ca4b533151 100644 --- a/src/components/search/Search.jsx +++ b/src/components/search/Search.jsx @@ -42,6 +42,14 @@ import AuthenticatedPageContext from '../app/AuthenticatedPageContext'; import { determineLearnerHasContentAssignmentsOnly } from '../enterprise-user-subsidy/data/utils'; import { EVENTS, isExperimentVariant, pushEvent } from '../../utils/optimizely'; +export const sendPushEvent = (isPreQueryEnabled, courseKeyMetadata) => { + if (isPreQueryEnabled) { + pushEvent(EVENTS.PREQUERY_SUGGESTION_CLICK, { courseKeyMetadata }); + } else { + pushEvent(EVENTS.SEARCH_SUGGESTION_CLICK, { courseKeyMetadata }); + } +}; + const Search = () => { const config = getConfig(); const { pathwayUUID } = useParams(); @@ -154,12 +162,17 @@ const Search = () => { const shouldDisplayBalanceAlert = hasNoEnterpriseOffersBalance || hasLowEnterpriseOffersBalance; const { content_type: contentType } = refinements; - const hasRefinements = Object.keys(refinements).filter(refinement => refinement !== 'showAll').length > 0 && (contentType !== undefined ? contentType.length > 0 : true); + const hasRefinements = Object.keys(refinements).filter(refinement => refinement !== 'showAll').length > 0 + && (contentType !== undefined ? contentType.length > 0 : true); - const optimizelyPrequerySuggestionClickHandler = (courseKey) => { - if (isExperimentVariation) { - pushEvent(EVENTS.PREQUERY_SUGGESTION_CLICK, { courseKey }); - } + const isPreQueryEnabled = enterpriseConfig.enterpriseFeatures?.featurePrequerySearchSuggestions + && isExperimentVariation; + + const optimizelySuggestionClickHandler = (courseKey) => { + // Programs pass in a list of keys. Optimizely does not accept array values + // so we are joining the items in the array. + const courseKeyMetadata = Array.isArray(courseKey) ? courseKey.join(', ') : courseKey; + sendPushEvent(isPreQueryEnabled, courseKeyMetadata); }; return ( @@ -185,7 +198,8 @@ const Search = () => { index={courseIndex} filters={filters} enterpriseConfig={enterpriseConfig} - optimizelyPrequerySuggestionClickHandler={optimizelyPrequerySuggestionClickHandler} + optimizelySuggestionClickHandler={optimizelySuggestionClickHandler} + isPreQueryEnabled={isPreQueryEnabled} /> )} diff --git a/src/components/search/tests/SearchSections.test.jsx b/src/components/search/tests/SearchSections.test.jsx index d37e1d0007..3ef5299910 100644 --- a/src/components/search/tests/SearchSections.test.jsx +++ b/src/components/search/tests/SearchSections.test.jsx @@ -9,10 +9,11 @@ import { renderWithRouter } from '../../../utils/tests'; import '@testing-library/jest-dom'; import SearchProgram from '../SearchProgram'; import SearchPathway from '../SearchPathway'; -import Search from '../Search'; +import Search, { sendPushEvent } from '../Search'; import AuthenticatedPageContext from '../../app/AuthenticatedPageContext'; import { UserSubsidyContext } from '../../enterprise-user-subsidy'; import { SUBSIDY_TYPE, SubsidyRequestsContext } from '../../enterprise-subsidy-requests'; +import { EVENTS, pushEvent } from '../../../utils/optimizely'; const APP_CONFIG = { ALGOLIA_INDEX_NAME: 'test-index-name', @@ -23,6 +24,11 @@ jest.mock('@edx/frontend-platform/config', () => ({ getConfig: jest.fn(() => APP_CONFIG), })); +jest.mock('../../../utils/optimizely', () => ({ + ...jest.requireActual('../../../utils/optimizely'), + pushEvent: jest.fn(), +})); + const searchContext1 = { refinements: { showAll: 1, content_type: ['course'] }, dispatch: () => null, @@ -41,6 +47,9 @@ const initialAppState = { name: 'BearsRUs', slug: 'test-enterprise-slug', showIntegrationWarning: false, + enterpriseFeatures: { + featurePrequerySearchSuggestions: true, + }, }, authenticatedUser: { userId: 'test-user-id' }, algolia: { @@ -85,7 +94,7 @@ describe('', () => { renderWithRouter( - + @@ -97,7 +106,7 @@ describe('', () => { renderWithRouter( - + @@ -110,7 +119,7 @@ describe('', () => { renderWithRouter( - + @@ -172,4 +181,14 @@ describe('', () => { ); expect(screen.getByText('Pathways (2 results)')).toBeInTheDocument(); }); + + describe('pushEvent', () => { + test.each([ + [true, 'test-course-101', EVENTS.PREQUERY_SUGGESTION_CLICK], + [false, 'test-course-102', EVENTS.SEARCH_SUGGESTION_CLICK], + ])('if isPrequeryEnabled is %p with course metadata %p, submit event %p', (isPrequeryEnabled, courseKeyMetadata, event) => { + sendPushEvent(isPrequeryEnabled, courseKeyMetadata); + expect(pushEvent).toHaveBeenCalledWith(event, { courseKeyMetadata }); + }); + }); }); diff --git a/src/utils/optimizely.js b/src/utils/optimizely.js index 0b7a0cb37d..60605a83d6 100644 --- a/src/utils/optimizely.js +++ b/src/utils/optimizely.js @@ -2,6 +2,7 @@ export const EVENTS = { ENROLLMENT_CLICK: 'enterprise_learner_portal_enrollment_click', FIRST_ENROLLMENT_CLICK: 'enterprise_learner_portal_first_enrollment_click', PREQUERY_SUGGESTION_CLICK: 'enterprise_learner_portal_prequery_suggestions_click', + SEARCH_SUGGESTION_CLICK: 'enterprise_learner_portal_search_suggestions_click', }; export const getActiveExperiments = () => { From 471e5cda8a87deecedd072333db319f012555245 Mon Sep 17 00:00:00 2001 From: Katrina Nguyen <71999631+katrinan029@users.noreply.github.com> Date: Tue, 5 Mar 2024 13:38:53 -0800 Subject: [PATCH 2/2] feat: add support for optimizely experiments on stage (#985) feat: add support for optimizely experiments on stage --- public/index.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/public/index.html b/public/index.html index f9bf23e122..e1cb9886ca 100644 --- a/public/index.html +++ b/public/index.html @@ -15,11 +15,12 @@ <% }); %> - <% if (htmlWebpackPlugin.options.NODE_ENV === 'production' && htmlWebpackPlugin.options.OPTIMIZELY_PROJECT_ID) { %> + <% if (htmlWebpackPlugin.options.NODE_ENV==='production' && htmlWebpackPlugin.options.OPTIMIZELY_PROJECT_ID && + htmlWebpackPlugin.options.DEPLOYMENT_ENV==='production') { %> <% } %> - <% if (htmlWebpackPlugin.options.NODE_ENV !== 'production' && htmlWebpackPlugin.options.OPTIMIZELY_PROJECT_ID) { %> + <% if (htmlWebpackPlugin.options.OPTIMIZELY_PROJECT_ID) { %> <% } %>