Add analytics event not-found and publish it from TecDocsNotFound.tsx

Signed-off-by: Morgan Bentell <mbentell@spotify.com>
This commit is contained in:
Morgan Bentell
2023-10-27 12:24:54 +02:00
parent 8562255396
commit 75b2d4dc07
3 changed files with 75 additions and 21 deletions
+8 -7
View File
@@ -56,13 +56,14 @@ learn how to contribute the integration yourself!
The following table summarizes events that, depending on the plugins you have
installed, may be captured.
| Action | Subject | Other Notes |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `navigate` | The URL of the page that was navigated to. | Fired immediately when route location changes (unless associated plugin/route data is ambiguous, in which case the event is fired after plugin/route data becomes known, immediately before the next event or document unload). The parameters of the current route will be included as attributes. |
| `click` | The text of the link that was clicked on. | The `to` attribute represents the URL clicked to. |
| `create` | The `name` of the software being created; if no `name` property is requested by the given Software Template, then the string `new {templateName}` is used instead. | The context holds an `entityRef`, set to the template's ref (e.g. `template:default/template-name`). |
| `search` | The search term entered in any search bar component. | The context holds `searchTypes`, representing `types` constraining the search. The `value` represents the total number of search results for the query. This may not be visible if the permission framework is being used. |
| `discover` | The title of the search result that was clicked on | The `value` is the result rank. A `to` attribute is also provided. |
| Action | Subject | Other Notes |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `navigate` | The URL of the page that was navigated to. | Fired immediately when route location changes (unless associated plugin/route data is ambiguous, in which case the event is fired after plugin/route data becomes known, immediately before the next event or document unload). The parameters of the current route will be included as attributes. |
| `click` | The text of the link that was clicked on. | The `to` attribute represents the URL clicked to. |
| `create` | The `name` of the software being created; if no `name` property is requested by the given Software Template, then the string `new {templateName}` is used instead. | The context holds an `entityRef`, set to the template's ref (e.g. `template:default/template-name`). |
| `search` | The search term entered in any search bar component. | The context holds `searchTypes`, representing `types` constraining the search. The `value` represents the total number of search results for the query. This may not be visible if the permission framework is being used. |
| `discover` | The title of the search result that was clicked on | The `value` is the result rank. A `to` attribute is also provided. |
| `not-found` | The path of the resource that resulted in a not found page | Fired by at least TechDocs. |
If there is an event you'd like to see captured, please [open an
issue](https://github.com/backstage/backstage/issues/new?assignees=&labels=enhancement&template=feature_template.md&title=[Analytics%20Event]:%20THE+EVENT+TO+CAPTURE) describing the event you want to see and the questions it
@@ -16,8 +16,37 @@
import { TechDocsNotFound } from './TechDocsNotFound';
import React from 'react';
import { screen } from '@testing-library/react';
import { renderInTestApp } from '@backstage/test-utils';
import { render, screen, waitFor } from '@testing-library/react';
import {
MockAnalyticsApi,
TestApiProvider,
renderInTestApp,
wrapInTestApp,
} from '@backstage/test-utils';
import { analyticsApiRef } from '@backstage/core-plugin-api';
jest.mock('@backstage/plugin-techdocs-react', () => {
const actualModule = jest.requireActual('@backstage/plugin-techdocs-react');
return {
...actualModule,
useTechDocsReaderPage: () => ({
entityRef: { name: 'name', namespace: 'namespace', kind: 'kind' },
}),
};
});
jest.mock('react-router-dom', () => {
const actualModule = jest.requireActual('react-router-dom');
return {
...actualModule,
useLocation: () =>
({
pathname: '/the/pathname',
search: '?the=search',
hash: '#the-anchor',
} as Location),
};
});
describe('<TechDocsNotFound />', () => {
it('should render with status code, status message and go back link', async () => {
@@ -27,21 +56,33 @@ describe('<TechDocsNotFound />', () => {
screen.getByText(/Looks like someone dropped the mic!/i);
expect(screen.getByTestId('go-back-link')).toBeDefined();
});
});
describe('<TechDocsNotFound errorMessage="This is a custom error message" />', () => {
it('should render with status code, custom error message and go back link', async () => {
await renderInTestApp(
<TechDocsNotFound errorMessage="This is a custom error message" />,
it('should trigger analytics event not-found', async () => {
const mockAnalyticsApi = new MockAnalyticsApi();
render(
wrapInTestApp(
<TestApiProvider apis={[[analyticsApiRef, mockAnalyticsApi]]}>
<TechDocsNotFound />
</TestApiProvider>,
),
);
screen.getByText(/This is a custom error message/i);
screen.getByText(/404/i);
screen.getByText(/Looks like someone dropped the mic!/i);
expect(screen.getByTestId('go-back-link')).toBeDefined();
await waitFor(() => {
expect(mockAnalyticsApi.getEvents()[0]).toMatchObject({
action: 'not-found',
subject: '/the/pathname?the=search#the-anchor',
attributes: {
name: 'name',
namespace: 'namespace',
kind: 'kind',
},
});
});
});
});
describe('<TechDocsNotFound statusCode={500} errorMessage="This is a custom error message" />', () => {
describe('<TechDocsNotFound errorMessage="This is a custom error message" />', () => {
it('should render with a 404 code, custom error message and go back link', async () => {
await renderInTestApp(
<TechDocsNotFound errorMessage="This is a custom error message" />,
@@ -14,9 +14,11 @@
* limitations under the License.
*/
import React from 'react';
import { useApi, configApiRef } from '@backstage/core-plugin-api';
import React, { useEffect } from 'react';
import { useApi, configApiRef, useAnalytics } from '@backstage/core-plugin-api';
import { ErrorPage } from '@backstage/core-components';
import { useTechDocsReaderPage } from '@backstage/plugin-techdocs-react';
import { useLocation } from 'react-router-dom';
type Props = {
errorMessage?: string;
@@ -25,6 +27,16 @@ type Props = {
export const TechDocsNotFound = ({ errorMessage }: Props) => {
const techdocsBuilder =
useApi(configApiRef).getOptionalString('techdocs.builder');
const analyticsApi = useAnalytics();
const { entityRef } = useTechDocsReaderPage();
const location = useLocation();
useEffect(() => {
const { pathname, search, hash } = location;
analyticsApi.captureEvent('not-found', `${pathname}${search}${hash}`, {
attributes: entityRef,
});
}, [analyticsApi, entityRef, location]);
let additionalInfo = '';
if (techdocsBuilder !== 'local') {