Adds frontend events for Google Analytics.

VAN-282)
This commit is contained in:
Adeel Khan
2021-01-22 02:59:23 +05:00
parent ab8fedd0c7
commit fc9ca1d258
7 changed files with 135 additions and 3 deletions

View File

@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
@@ -22,8 +23,13 @@ const LoginHelpLinks = (props) => {
setShowLoginHelpValue(!showLoginHelp);
};
const handleForgotPasswordLinkClickEvent = () => {
sendTrackEvent('edx.bi.password-reset_form.toggled', { category: 'user-engagement' });
};
const forgotPasswordLink = () => (
<a className="field-link" href={RESET_PAGE}>
<a className="field-link" href={RESET_PAGE} onClick={handleForgotPasswordLinkClickEvent}>
{intl.formatMessage(messages['forgot.password.link'])}
</a>
);

View File

@@ -3,6 +3,7 @@ import Skeleton from 'react-loading-skeleton';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
import {
Form, Hyperlink, Input, StatefulButton, ValidationFormGroup,
} from '@edx/paragon';
@@ -58,6 +59,8 @@ class LoginPage extends React.Component {
}
handleInstitutionLogin = () => {
sendTrackEvent('edx.bi.institution_login_form.toggled', { category: 'user-engagement' });
sendPageEvent('login_and_registration', 'institution_login');
this.setState(prevState => ({ institutionLogin: !prevState.institutionLogin }));
}
@@ -123,6 +126,10 @@ class LoginPage extends React.Component {
});
}
handleCreateAccountLinkClickEvent() {
sendTrackEvent('edx.bi.register_form.toggled', { category: 'user-engagement' });
}
renderThirdPartyAuth(providers, secondaryProviders, currentProvider, thirdPartyAuthApiStatus, intl) {
let thirdPartyComponent = null;
if ((providers.length || secondaryProviders.length) && !currentProvider) {
@@ -162,6 +169,8 @@ class LoginPage extends React.Component {
/>
);
}
sendPageEvent('login_and_registration', 'login');
return (
<>
<RedirectLogistration
@@ -185,7 +194,7 @@ class LoginPage extends React.Component {
<div className="d-flex flex-row">
<p>
{intl.formatMessage(messages['first.time.here'])}
<Hyperlink className="ml-1" destination={REGISTER_PAGE}>
<Hyperlink className="ml-1" destination={REGISTER_PAGE} onClick={this.handleCreateAccountLinkClickEvent}>
{intl.formatMessage(messages['create.an.account'])}.
</Hyperlink>
</p>

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import * as analytics from '@edx/frontend-platform/analytics';
import { mount } from 'enzyme';
import LoginHelpLinks from '../LoginHelpLinks';
@@ -11,6 +12,9 @@ jest.mock('@edx/frontend-platform', () => ({
getConfig: jest.fn().mockReturnValue({ LOGIN_ISSUE_SUPPORT_LINK: otherSignInIssues }),
}));
jest.mock('@edx/frontend-platform/analytics');
analytics.sendTrackEvent = jest.fn();
describe('LoginHelpLinks', () => {
let props = {};

View File

@@ -6,10 +6,16 @@ import configureStore from 'redux-mock-store';
import { getConfig } from '@edx/frontend-platform';
import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
import * as analytics from '@edx/frontend-platform/analytics';
import LoginPage from '../LoginPage';
import { RenderInstitutionButton } from '../../common-components';
import { PENDING_STATE } from '../../data/constants';
jest.mock('@edx/frontend-platform/analytics');
analytics.sendTrackEvent = jest.fn();
analytics.sendPageEvent = jest.fn();
const IntlLoginPage = injectIntl(LoginPage);
const mockStore = configureStore();
@@ -278,4 +284,34 @@ describe('LoginPage', () => {
loginPage.find(RenderInstitutionButton).simulate('click', { institutionLogin: true });
expect(loginPage.text().includes('Test University')).toBe(true);
});
it('send tracking event when create account link is clicked', () => {
const loginPage = mount(reduxWrapper(<IntlLoginPage {...props} />));
loginPage.find('a[href*="/register"]').simulate('click');
loginPage.update();
expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.bi.register_form.toggled', { category: 'user-engagement' });
});
it('send page event when login page is rendered', () => {
mount(reduxWrapper(<IntlLoginPage {...props} />));
expect(analytics.sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'login');
});
it('send tracking and page events when institutional button is clicked', () => {
store = mockStore({
...initialState,
commonComponents: {
...initialState.commonComponents,
thirdPartyAuthContext: {
...initialState.commonComponents.thirdPartyAuthContext,
secondaryProviders: [secondaryProviders],
},
},
});
const loginPage = mount(reduxWrapper(<IntlLoginPage {...props} />));
loginPage.find(RenderInstitutionButton).simulate('click', { institutionLogin: true });
expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.bi.institution_login_form.toggled', { category: 'user-engagement' });
expect(analytics.sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'institution_login');
});
});

View File

@@ -2,6 +2,7 @@ import React from 'react';
import { connect } from 'react-redux';
import Skeleton from 'react-loading-skeleton';
import PropTypes from 'prop-types';
import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
import {
Input,
StatefulButton,
@@ -229,6 +230,7 @@ class RegistrationPage extends React.Component {
this.setState({
enableOptionalField: targetValue,
});
sendTrackEvent('edx.bi.user.register.optional_fields_selected', {});
}
handleOnClick(e) {
@@ -244,6 +246,10 @@ class RegistrationPage extends React.Component {
}
}
handleLoginLinkClickEvent() {
sendTrackEvent('edx.bi.login_form.toggled', { category: 'user-engagement' });
}
validateInput(inputName, value) {
const {
errors,
@@ -543,6 +549,7 @@ class RegistrationPage extends React.Component {
);
}
sendPageEvent('login_and_registration', 'register');
return (
<>
<RedirectLogistration
@@ -563,7 +570,7 @@ class RegistrationPage extends React.Component {
)}
<div className="text-left">
<span>{intl.formatMessage(messages['already.have.an.edx.account'])}</span>
<a href={LOGIN_PAGE}>{intl.formatMessage(messages['sign.in.hyperlink'])}</a>
<a href={LOGIN_PAGE} onClick={this.handleLoginLinkClickEvent}>{intl.formatMessage(messages['sign.in.hyperlink'])}</a>
</div>
{(providers.length || secondaryProviders.length || thirdPartyAuthApiStatus === PENDING_STATE)
&& !currentProvider ? (

View File

@@ -5,12 +5,18 @@ import { mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { getConfig } from '@edx/frontend-platform';
import { IntlProvider, injectIntl, configure } from '@edx/frontend-platform/i18n';
import * as analytics from '@edx/frontend-platform/analytics';
import RegistrationPage from '../RegistrationPage';
import { RenderInstitutionButton } from '../../common-components';
import { PENDING_STATE } from '../../data/constants';
import { fetchRegistrationForm, fetchRealtimeValidations, registerNewUser } from '../data/actions';
jest.mock('@edx/frontend-platform/analytics');
analytics.sendTrackEvent = jest.fn();
analytics.sendPageEvent = jest.fn();
const IntlRegistrationPage = injectIntl(RegistrationPage);
const mockStore = configureStore();
@@ -141,6 +147,65 @@ describe('./RegistrationPage.js', () => {
expect(registrationPage.find('RegistrationPage').state('enableOptionalField')).toEqual(true);
});
it('send tracking event on optional checkbox enabled', () => {
store = mockStore({
...initialState,
logistration: {
...initialState.logistration,
formData: {
fields: [
{
label: 'Tell us why you\'re interested in edX',
name: 'goals',
type: 'textarea',
required: false,
},
{
label: 'Highest level of Education completed.',
name: 'level_of_education',
type: 'select',
options: [{ value: '', name: '--' }, { value: 'p', name: 'Doctorate' }],
required: false,
},
{
label: 'Year of birth.',
name: 'year_of_birth',
type: 'select',
options: [{ value: '', name: '--' }, { value: '2021', name: '2021' }],
required: false,
},
{
label: 'Gender.',
name: 'gender',
type: 'select',
options: [{ value: '', name: '--' }, { value: 'f', name: 'Female' }],
required: false,
},
],
},
},
});
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
registrationPage.find('input#optional').simulate('change', { target: { checked: true } });
registrationPage.update();
expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.bi.user.register.optional_fields_selected', {});
});
it('send tracking event when login link is clicked', () => {
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
registrationPage.find('a[href*="/login"]').simulate('click');
registrationPage.update();
expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.bi.login_form.toggled', { category: 'user-engagement' });
});
it('send page event when register page is rendered', () => {
mount(reduxWrapper(<IntlRegistrationPage {...props} />));
expect(analytics.sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'register');
});
it('should show optional fields section on optional check enabled', () => {
store = mockStore({
...initialState,

View File

@@ -27,6 +27,7 @@ exports[`./RegistrationPage.js should display no password field when current pro
</span>
<a
href="/login"
onClick={[Function]}
>
Sign in.
</a>
@@ -218,6 +219,7 @@ exports[`./RegistrationPage.js should match TPA provider snapshot 1`] = `
</span>
<a
href="/login"
onClick={[Function]}
>
Sign in.
</a>
@@ -473,6 +475,7 @@ exports[`./RegistrationPage.js should match default section snapshot 1`] = `
</span>
<a
href="/login"
onClick={[Function]}
>
Sign in.
</a>
@@ -693,6 +696,7 @@ exports[`./RegistrationPage.js should match pending button state snapshot 1`] =
</span>
<a
href="/login"
onClick={[Function]}
>
Sign in.
</a>
@@ -948,6 +952,7 @@ exports[`./RegistrationPage.js should show error message on 409 1`] = `
</span>
<a
href="/login"
onClick={[Function]}
>
Sign in.
</a>