Skip to content

Commit 0d603b5

Browse files
feat: added app name identifier in segment events (#1277)
* feat: added app name identifier in registration call * feat: added utils for tracking events * refactor: mapped login events * refactor: mapped forgot password events * refactor: mapped reset password events * refactor: mapped register events * fix: fixed unit tests * refactor: mapped progressive prifiling events * fix: fixed unit tests * refactor: added app name in logistration events * refactor: resolved PR reviews and fixed tests
1 parent efaa83a commit 0d603b5

24 files changed

+419
-57
lines changed

src/data/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,4 @@ export const VALID_EMAIL_REGEX = '(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+
3737
// things like auto-enrollment upon login and registration.
3838
export const AUTH_PARAMS = ['course_id', 'enrollment_action', 'course_mode', 'email_opt_in', 'purchase_workflow', 'next', 'register_for_free', 'track', 'is_account_recovery', 'variant', 'host', 'cta'];
3939
export const REDIRECT = 'redirect';
40+
export const APP_NAME = 'authn_mfe';

src/data/segment/utils.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* eslint-disable import/prefer-default-export */
2+
import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
3+
4+
import { APP_NAME } from '../constants';
5+
6+
export const LINK_TIMEOUT = 300;
7+
8+
/**
9+
* Creates an event tracker function that sends a tracking event with the given name and options.
10+
*
11+
* @param {string} name - The name of the event to be tracked.
12+
* @param {object} [options={}] - Additional options to be included with the event.
13+
* @returns {function} - A function that, when called, sends the tracking event.
14+
*/
15+
export const createEventTracker = (name, options = {}) => () => sendTrackEvent(
16+
name,
17+
{ ...options, app_name: APP_NAME },
18+
);
19+
20+
/**
21+
* Creates an event tracker function that sends a tracking event with the given name and options.
22+
*
23+
* @param {string} name - The name of the event to be tracked.
24+
* @param {object} [options={}] - Additional options to be included with the event.
25+
* @returns {function} - A function that, when called, sends the tracking event.
26+
*/
27+
export const createPageEventTracker = (name, options = null) => () => sendPageEvent(
28+
name,
29+
options,
30+
{ app_name: APP_NAME },
31+
);
32+
33+
export const createLinkTracker = (tracker, href) => (e) => {
34+
e.preventDefault();
35+
tracker();
36+
return setTimeout(() => { window.location.href = href; }, LINK_TIMEOUT);
37+
};

src/forgot-password/ForgotPasswordPage.jsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React, { useEffect, useState } from 'react';
22
import { connect } from 'react-redux';
33

44
import { getConfig } from '@edx/frontend-platform';
5-
import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
65
import { useIntl } from '@edx/frontend-platform/i18n';
76
import {
87
Form,
@@ -25,6 +24,10 @@ import BaseContainer from '../base-container';
2524
import { FormGroup } from '../common-components';
2625
import { DEFAULT_STATE, LOGIN_PAGE, VALID_EMAIL_REGEX } from '../data/constants';
2726
import { updatePathWithQueryParams, windowScrollTo } from '../data/utils';
27+
import {
28+
trackForgotPasswordPageEvent,
29+
trackForgotPasswordPageViewed,
30+
} from '../tracking/trackers/forgotpassword';
2831

2932
const ForgotPasswordPage = (props) => {
3033
const platformName = getConfig().SITE_NAME;
@@ -41,8 +44,8 @@ const ForgotPasswordPage = (props) => {
4144
const navigate = useNavigate();
4245

4346
useEffect(() => {
44-
sendPageEvent('login_and_registration', 'forgot-password');
45-
sendTrackEvent('edx.bi.password_reset_form.viewed', { category: 'user-engagement' });
47+
trackForgotPasswordPageEvent();
48+
trackForgotPasswordPageViewed();
4649
}, []);
4750

4851
useEffect(() => {

src/login/LoginPage.jsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React, { useEffect, useMemo, useState } from 'react';
22
import { connect } from 'react-redux';
33

44
import { getConfig } from '@edx/frontend-platform';
5-
import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
65
import { injectIntl, useIntl } from '@edx/frontend-platform/i18n';
76
import {
87
Form, StatefulButton,
@@ -43,6 +42,9 @@ import {
4342
updatePathWithQueryParams,
4443
} from '../data/utils';
4544
import ResetPasswordSuccess from '../reset-password/ResetPasswordSuccess';
45+
import {
46+
trackForgotPasswordLinkClick, trackLoginPageViewed, trackLoginSuccess,
47+
} from '../tracking/trackers/login';
4648

4749
const LoginPage = (props) => {
4850
const {
@@ -78,9 +80,15 @@ const LoginPage = (props) => {
7880
const tpaHint = getTpaHint();
7981

8082
useEffect(() => {
81-
sendPageEvent('login_and_registration', 'login');
83+
trackLoginPageViewed();
8284
}, []);
8385

86+
useEffect(() => {
87+
if (loginResult.success) {
88+
trackLoginSuccess();
89+
}
90+
}, [loginResult]);
91+
8492
useEffect(() => {
8593
const payload = { ...queryParams };
8694
if (tpaHint) {
@@ -170,9 +178,6 @@ const LoginPage = (props) => {
170178
const { name } = event.target;
171179
setErrors(prevErrors => ({ ...prevErrors, [name]: '' }));
172180
};
173-
const trackForgotPasswordLinkClick = () => {
174-
sendTrackEvent('edx.bi.password-reset_form.toggled', { category: 'user-engagement' });
175-
};
176181

177182
const { provider, skipHintedLogin } = getTpaProvider(tpaHint, providers, secondaryProviders);
178183

src/login/tests/LoginPage.test.jsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import { act } from 'react-dom/test-utils';
1111
import { MemoryRouter } from 'react-router-dom';
1212
import configureStore from 'redux-mock-store';
1313

14-
import { COMPLETE_STATE, LOGIN_PAGE, PENDING_STATE } from '../../data/constants';
14+
import {
15+
APP_NAME, COMPLETE_STATE, LOGIN_PAGE, PENDING_STATE,
16+
} from '../../data/constants';
1517
import { backupLoginFormBegin, dismissPasswordResetBanner, loginRequest } from '../data/actions';
1618
import { INTERNAL_SERVER_ERROR } from '../data/constants';
1719
import LoginPage from '../LoginPage';
@@ -751,7 +753,7 @@ describe('LoginPage', () => {
751753

752754
it('should send page event when login page is rendered', () => {
753755
render(reduxWrapper(<IntlLoginPage {...props} />));
754-
expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'login');
756+
expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'login', { app_name: APP_NAME });
755757
});
756758

757759
it('tests that form is in invalid state when it is submitted', () => {
@@ -784,7 +786,7 @@ describe('LoginPage', () => {
784786
{ selector: '#forgot-password' },
785787
));
786788

787-
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.password-reset_form.toggled', { category: 'user-engagement' });
789+
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.password-reset_form.toggled', { category: 'user-engagement', app_name: APP_NAME });
788790
});
789791

790792
it('should backup the login form state when shouldBackupState is true', () => {

src/logistration/Logistration.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
tpaProvidersSelector,
2121
} from '../common-components/data/selectors';
2222
import messages from '../common-components/messages';
23-
import { LOGIN_PAGE, REGISTER_PAGE } from '../data/constants';
23+
import { APP_NAME, LOGIN_PAGE, REGISTER_PAGE } from '../data/constants';
2424
import {
2525
getTpaHint, getTpaProvider, updatePathWithQueryParams,
2626
} from '../data/utils';
@@ -56,11 +56,11 @@ const Logistration = (props) => {
5656
}, [navigate, disablePublicAccountCreation]);
5757

5858
const handleInstitutionLogin = (e) => {
59-
sendTrackEvent('edx.bi.institution_login_form.toggled', { category: 'user-engagement' });
59+
sendTrackEvent('edx.bi.institution_login_form.toggled', { category: 'user-engagement', app_name: APP_NAME });
6060
if (typeof e === 'string') {
61-
sendPageEvent('login_and_registration', e === '/login' ? 'login' : 'register');
61+
sendPageEvent('login_and_registration', e === '/login' ? 'login' : 'register', { app_name: APP_NAME });
6262
} else {
63-
sendPageEvent('login_and_registration', e.target.dataset.eventName);
63+
sendPageEvent('login_and_registration', e.target.dataset.eventName, { app_name: APP_NAME });
6464
}
6565

6666
setInstitutionLogin(!institutionLogin);
@@ -70,7 +70,7 @@ const Logistration = (props) => {
7070
if (tabKey === currentTab) {
7171
return;
7272
}
73-
sendTrackEvent(`edx.bi.${tabKey.replace('/', '')}_form.toggled`, { category: 'user-engagement' });
73+
sendTrackEvent(`edx.bi.${tabKey.replace('/', '')}_form.toggled`, { category: 'user-engagement', app_name: APP_NAME });
7474
props.clearThirdPartyAuthContextErrorMessage();
7575
if (tabKey === LOGIN_PAGE) {
7676
props.backupRegistrationForm();

src/logistration/Logistration.test.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import configureStore from 'redux-mock-store';
1111
import Logistration from './Logistration';
1212
import { clearThirdPartyAuthContextErrorMessage } from '../common-components/data/actions';
1313
import {
14+
APP_NAME,
1415
COMPLETE_STATE, LOGIN_PAGE, REGISTER_PAGE,
1516
} from '../data/constants';
1617
import { backupLoginForm } from '../login/data/actions';
@@ -229,8 +230,8 @@ describe('Logistration', () => {
229230
render(reduxWrapper(<IntlLogistration {...props} />));
230231
fireEvent.click(screen.getByText('Institution/campus credentials'));
231232

232-
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.institution_login_form.toggled', { category: 'user-engagement' });
233-
expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'institution_login');
233+
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.institution_login_form.toggled', { category: 'user-engagement', app_name: APP_NAME });
234+
expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'institution_login', { app_name: APP_NAME });
234235

235236
mergeConfig({
236237
DISABLE_ENTERPRISE_LOGIN: '',

src/progressive-profiling/ProgressiveProfiling.jsx

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
22
import { connect } from 'react-redux';
33

44
import { getConfig, snakeCaseObject } from '@edx/frontend-platform';
5-
import { identifyAuthenticatedUser, sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
5+
import { identifyAuthenticatedUser } from '@edx/frontend-platform/analytics';
66
import {
77
AxiosJwtAuthService,
88
configure as configureAuth,
@@ -39,6 +39,13 @@ import {
3939
import isOneTrustFunctionalCookieEnabled from '../data/oneTrust';
4040
import { getAllPossibleQueryParams, isHostAvailableInQueryParams } from '../data/utils';
4141
import { FormFieldRenderer } from '../field-renderer';
42+
import {
43+
trackDisablePostRegistrationRecommendations,
44+
trackProgressiveProfilingPageViewed,
45+
trackProgressiveProfilingSkipLinkClick,
46+
trackProgressiveProfilingSubmitClick,
47+
trackProgressiveProfilingSupportLinkCLick,
48+
} from '../tracking/trackers/progressive-profiling';
4249

4350
const ProgressiveProfiling = (props) => {
4451
const { formatMessage } = useIntl();
@@ -98,14 +105,13 @@ const ProgressiveProfiling = (props) => {
98105
useEffect(() => {
99106
if (authenticatedUser?.userId) {
100107
identifyAuthenticatedUser(authenticatedUser.userId);
101-
sendPageEvent('login_and_registration', 'welcome');
108+
trackProgressiveProfilingPageViewed();
102109
}
103110
}, [authenticatedUser]);
104111

105112
useEffect(() => {
106113
if (!enablePostRegistrationRecommendations) {
107-
sendTrackEvent(
108-
'edx.bi.user.recommendations.not.enabled',
114+
trackDisablePostRegistrationRecommendations(
109115
{ functionalCookiesConsent, page: 'authn_recommendations' },
110116
);
111117
return;
@@ -149,29 +155,23 @@ const ProgressiveProfiling = (props) => {
149155
});
150156
}
151157
props.saveUserProfile(authenticatedUser.username, snakeCaseObject(payload));
152-
153-
sendTrackEvent(
154-
'edx.bi.welcome.page.submit.clicked',
155-
{
156-
isGenderSelected: !!values.gender,
157-
isYearOfBirthSelected: !!values.year_of_birth,
158-
isLevelOfEducationSelected: !!values.level_of_education,
159-
isWorkExperienceSelected: !!values.work_experience,
160-
host: queryParams?.host || '',
161-
},
162-
);
158+
const eventProperties = {
159+
isGenderSelected: !!values.gender,
160+
isYearOfBirthSelected: !!values.year_of_birth,
161+
isLevelOfEducationSelected: !!values.level_of_education,
162+
isWorkExperienceSelected: !!values.work_experience,
163+
host: queryParams?.host || '',
164+
};
165+
trackProgressiveProfilingSubmitClick(eventProperties);
163166
};
164167

165168
const handleSkip = (e) => {
166169
e.preventDefault();
167170
window.history.replaceState(location.state, null, '');
168171
setShowModal(true);
169-
sendTrackEvent(
170-
'edx.bi.welcome.page.skip.link.clicked',
171-
{
172-
host: queryParams?.host || '',
173-
},
174-
);
172+
trackProgressiveProfilingSkipLinkClick({
173+
host: queryParams?.host || '',
174+
});
175175
};
176176

177177
const onChangeHandler = (e) => {
@@ -242,7 +242,7 @@ const ProgressiveProfiling = (props) => {
242242
destination={getConfig().AUTHN_PROGRESSIVE_PROFILING_SUPPORT_LINK}
243243
target="_blank"
244244
showLaunchIcon={false}
245-
onClick={() => (sendTrackEvent('edx.bi.welcome.page.support.link.clicked'))}
245+
onClick={() => (trackProgressiveProfilingSupportLinkCLick())}
246246
>
247247
{formatMessage(messages['optional.fields.information.link'])}
248248
</Hyperlink>

src/progressive-profiling/tests/ProgressiveProfiling.test.jsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { MemoryRouter, mockNavigate, useLocation } from 'react-router-dom';
1212
import configureStore from 'redux-mock-store';
1313

1414
import {
15+
APP_NAME,
1516
AUTHN_PROGRESSIVE_PROFILING,
1617
COMPLETE_STATE, DEFAULT_REDIRECT_URL,
1718
EMBEDDED,
@@ -143,8 +144,9 @@ describe('ProgressiveProfilingTests', () => {
143144
const modalContentContainer = document.getElementsByClassName('.pgn__modal-content-container');
144145

145146
expect(modalContentContainer).toBeTruthy();
147+
const payload = { host: '', app_name: APP_NAME };
146148

147-
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.skip.link.clicked', { host: '' });
149+
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.skip.link.clicked', payload);
148150
});
149151

150152
// ******** test event functionality ********
@@ -165,7 +167,7 @@ describe('ProgressiveProfilingTests', () => {
165167
const supportLink = screen.getByRole('link', { name: /learn more about how we use this information/i });
166168
fireEvent.click(supportLink);
167169

168-
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.support.link.clicked');
170+
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.support.link.clicked', { app_name: APP_NAME });
169171
});
170172

171173
it('should set empty host property value for non-embedded experience', () => {
@@ -175,6 +177,7 @@ describe('ProgressiveProfilingTests', () => {
175177
isLevelOfEducationSelected: false,
176178
isWorkExperienceSelected: false,
177179
host: '',
180+
app_name: APP_NAME,
178181
};
179182
delete window.location;
180183
window.location = { href: getConfig().BASE_URL.concat(AUTHN_PROGRESSIVE_PROFILING) };
@@ -316,7 +319,7 @@ describe('ProgressiveProfilingTests', () => {
316319
const skipLinkButton = screen.getByText('Skip for now');
317320
fireEvent.click(skipLinkButton);
318321

319-
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.skip.link.clicked', { host });
322+
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.skip.link.clicked', { host, app_name: APP_NAME });
320323
});
321324

322325
it('should show spinner while fetching the optional fields', () => {
@@ -349,6 +352,7 @@ describe('ProgressiveProfilingTests', () => {
349352
isLevelOfEducationSelected: false,
350353
isWorkExperienceSelected: false,
351354
host: 'http://example.com',
355+
app_name: APP_NAME,
352356
};
353357
delete window.location;
354358
window.location = {

src/register/RegistrationPage.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import React, {
44
import { useDispatch, useSelector } from 'react-redux';
55

66
import { getConfig } from '@edx/frontend-platform';
7-
import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
87
import { useIntl } from '@edx/frontend-platform/i18n';
98
import { Form, Spinner, StatefulButton } from '@openedx/paragon';
109
import classNames from 'classnames';
@@ -44,11 +43,12 @@ import { getThirdPartyAuthContext as getRegistrationDataFromBackend } from '../c
4443
import EnterpriseSSO from '../common-components/EnterpriseSSO';
4544
import ThirdPartyAuth from '../common-components/ThirdPartyAuth';
4645
import {
47-
COMPLETE_STATE, PENDING_STATE, REGISTER_PAGE,
46+
APP_NAME, COMPLETE_STATE, PENDING_STATE, REGISTER_PAGE,
4847
} from '../data/constants';
4948
import {
5049
getAllPossibleQueryParams, getTpaHint, getTpaProvider, isHostAvailableInQueryParams, setCookie,
5150
} from '../data/utils';
51+
import { trackRegistrationPageViewed, trackRegistrationSuccess } from '../tracking/trackers/register';
5252

5353
/**
5454
* Main Registration Page component
@@ -138,7 +138,7 @@ const RegistrationPage = (props) => {
138138

139139
useEffect(() => {
140140
if (!formStartTime) {
141-
sendPageEvent('login_and_registration', 'register');
141+
trackRegistrationPageViewed();
142142
const payload = { ...queryParams, is_register_page: true };
143143
if (tpaHint) {
144144
payload.tpa_hint = tpaHint;
@@ -183,7 +183,7 @@ const RegistrationPage = (props) => {
183183
useEffect(() => {
184184
if (registrationResult.success) {
185185
// This event is used by GTM
186-
sendTrackEvent('edx.bi.user.account.registered.client', {});
186+
trackRegistrationSuccess();
187187

188188
// This is used by the "User Retention Rate Event" on GTM
189189
setCookie(getConfig().USER_RETENTION_COOKIE_NAME, true);
@@ -222,7 +222,7 @@ const RegistrationPage = (props) => {
222222

223223
const registerUser = () => {
224224
const totalRegistrationTime = (Date.now() - formStartTime) / 1000;
225-
let payload = { ...formFields };
225+
let payload = { ...formFields, app_name: APP_NAME };
226226

227227
if (currentProvider) {
228228
delete payload.password;

0 commit comments

Comments
 (0)