Skip to content

Commit

Permalink
feat: ✨ Tracking of search action ENT-4126 (#95)
Browse files Browse the repository at this point in the history
* feat: ✨ Tracking of search action

When search button is clicked or search initiated, send tracking event with query string data

ENT-4126

* feat: ✨ Use trackingName from context if found

Only send tracking during search if context contains the trackingEvent string input value

ENT-4126

* test: use user-events instead of fireEvent

use user-events instead of fireEvent

ENT-4126

* feat: refine query_submitted event name

better naming of the query_submitted action as a event name under catalog_search

ENT-4126
  • Loading branch information
binodpant committed Apr 26, 2021
1 parent fd7344a commit 5942d04
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.vscode
.idea
coverage
dist
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
12.10.0
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@testing-library/jest-dom": "5.11.6",
"@testing-library/react": "11.2.6",
"@testing-library/react-hooks": "3.4.2",
"@testing-library/user-event": "^13.1.5",
"algoliasearch": "^4.8.5",
"axios-mock-adapter": "1.19.0",
"classnames": "^2.2.5",
Expand Down
14 changes: 13 additions & 1 deletion src/course-search/SearchBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,26 @@ import classNames from 'classnames';
import { SearchField } from '@edx/paragon';
import { connectSearchBox } from 'react-instantsearch-dom';

import { sendTrackEvent } from '@edx/frontend-platform/analytics';

import { deleteRefinementAction, setRefinementAction } from './data/actions';
import { STYLE_VARIANTS } from '../constants';
import { SearchContext } from './SearchContext';
import { QUERY_PARAM_FOR_PAGE, QUERY_PARAM_FOR_SEARCH_QUERY } from './data/constants';

export const searchText = 'Search courses';
// this prefix will be combined with one of the SearchBox props to create a full tracking event name
// only if event name prop is provided by user. In the absence of the tracking name prop,
// no tracking event will be sent.
export const SEARCH_EVENT_NAME_PREFIX = 'edx.enterprise';
export const QUERY_SUBMITTED_EVENT = 'catalog_search.query_submitted';

export const SearchBoxBase = ({
className,
defaultRefinement,
variant,
}) => {
const { dispatch } = useContext(SearchContext);
const { dispatch, trackingName } = useContext(SearchContext);

/**
* Handles when a search is submitted by adding the user's search
Expand All @@ -26,6 +33,11 @@ export const SearchBoxBase = ({
const handleSubmit = (searchQuery) => {
dispatch(setRefinementAction(QUERY_PARAM_FOR_SEARCH_QUERY, searchQuery));
dispatch(deleteRefinementAction(QUERY_PARAM_FOR_PAGE));
if (trackingName) {
sendTrackEvent(`${SEARCH_EVENT_NAME_PREFIX}.${trackingName}.${QUERY_SUBMITTED_EVENT}`, {
query: searchQuery,
});
}
};

/**
Expand Down
7 changes: 5 additions & 2 deletions src/course-search/SearchContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const getRefinementsToSet = (queryParams, activeFacetAttributes) => {
return refinementsToSet;
};

const SearchData = ({ children, searchFacetFilters }) => {
const SearchData = ({ children, searchFacetFilters, trackingName }) => {
const [refinementsFromQueryParams, dispatch] = useReducer(
refinementsReducer,
{},
Expand Down Expand Up @@ -61,8 +61,9 @@ const SearchData = ({ children, searchFacetFilters }) => {
refinementsFromQueryParams,
dispatch,
searchFacetFilters,
trackingName,
}),
[refinementsFromQueryParams, dispatch, searchFacetFilters],
[refinementsFromQueryParams, dispatch, searchFacetFilters, trackingName],
);

return (
Expand All @@ -72,6 +73,7 @@ const SearchData = ({ children, searchFacetFilters }) => {

SearchData.defaultProps = {
searchFacetFilters: SEARCH_FACET_FILTERS,
trackingName: null,
};

SearchData.propTypes = {
Expand All @@ -81,6 +83,7 @@ SearchData.propTypes = {
title: PropTypes.string.isRequired,
isSortedAlphabetical: PropTypes.bool,
})),
trackingName: PropTypes.string,
};

export default SearchData;
37 changes: 31 additions & 6 deletions src/course-search/tests/SearchBox.test.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import React from 'react';
import { screen, fireEvent } from '@testing-library/react';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/extend-expect';

import { SearchBoxBase, searchText } from '../SearchBox';
import { renderWithSearchContext } from '../../utils/tests';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';

import {
SearchBoxBase,
searchText,
SEARCH_EVENT_NAME_PREFIX,
QUERY_SUBMITTED_EVENT,
} from '../SearchBox';
import { renderWithSearchContext, renderWithSearchContextAndTracking } from '../../utils/tests';

jest.mock('@edx/frontend-platform/analytics');

const TEST_QUERY = 'test query';

Expand All @@ -30,18 +40,33 @@ describe('<SearchBox />', () => {
const { history } = renderWithSearchContext(<SearchBoxBase />);

// fill in search input and submit the search
fireEvent.change(screen.getByRole('searchbox'), { target: { value: TEST_QUERY } });
fireEvent.click(screen.getByText('submit search'));
userEvent.type(screen.getByRole('searchbox'), TEST_QUERY);
userEvent.click(screen.getByText('submit search'));

// assert url is updated with the query
expect(history).toHaveLength(2);
expect(history.location.search).toEqual('?q=test%20query');
// check tracking is not invoked due to absent trackingName in context
expect(sendTrackEvent).not.toHaveBeenCalled();

// clear the input
fireEvent.click(screen.getByText('clear search'));
userEvent.click(screen.getByText('clear search'));

// assert query no longer exists in url
expect(history).toHaveLength(3);
expect(history.location.search).toEqual('');
});
test('tracking event when search initiated with trackingName present in context', () => {
renderWithSearchContextAndTracking(<SearchBoxBase />, 'aProduct');

// fill in search input and submit the search
userEvent.type(screen.getByRole('searchbox'), TEST_QUERY);
userEvent.click(screen.getByText('submit search'));

// check tracking is invoked due to trackingName in context
expect(sendTrackEvent).toHaveBeenCalledWith(
`${SEARCH_EVENT_NAME_PREFIX}.aProduct.${QUERY_SUBMITTED_EVENT}`,
{ query: TEST_QUERY },
);
});
});
6 changes: 6 additions & 0 deletions src/utils/tests.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ export const renderWithSearchContext = (children) => renderWithRouter(
{children}
</SearchData>,
);

export const renderWithSearchContextAndTracking = (children, trackingName) => renderWithRouter(
<SearchData trackingName={trackingName}>
{children}
</SearchData>,
);

0 comments on commit 5942d04

Please sign in to comment.