Skip to content

Commit e68f26f

Browse files
committed
Fixed bots not being ignored in analytics
1 parent f20cfe8 commit e68f26f

File tree

3 files changed

+72
-17
lines changed

3 files changed

+72
-17
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"private": true,
33
"name": "website-frontend",
44
"description": "My website's frontend.",
5-
"version": "3.1.3",
5+
"version": "3.2.0",
66
"license": "UNLICENSED",
77
"author": "Steven Vachon <[email protected]> (https://svachon.com)",
88
"repository": "github:stevenvachon/website-frontend",

src/scripts/analytics.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Bots } from 'ua-parser-js/extensions';
12
import { UAParser } from 'ua-parser-js';
23

34
// Exported for tests
@@ -74,7 +75,9 @@ const updateActivity = () => {
7475

7576
export default () => {
7677
if (
77-
['crawler', 'fetcher'].includes(UAParser(navigator.userAgent).browser.type)
78+
['crawler', 'fetcher'].includes(
79+
UAParser(navigator.userAgent, Bots).browser.type
80+
)
7881
) {
7982
return;
8083
}

src/scripts/analytics.test.js

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,61 @@ import analytics, {
1818
SESSION_TIMEOUT_EVENT,
1919
} from './analytics.js';
2020

21+
beforeEach(() => {
22+
vi.stubGlobal('fetch', vi.fn());
23+
vi.stubGlobal('navigator', {
24+
...navigator,
25+
sendBeacon: vi.fn(),
26+
});
27+
});
28+
29+
[
30+
{
31+
title: 'Crawlers',
32+
// https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers
33+
userAgent:
34+
'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Chrome/W.X.Y.Z Safari/537.36',
35+
},
36+
{
37+
title: 'Fetchers',
38+
// https://developers.google.com/search/docs/crawling-indexing/google-user-triggered-fetchers
39+
userAgent:
40+
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36 (compatible; Google-Read-Aloud; +https://support.google.com/webmasters/answer/1061943)',
41+
},
42+
].forEach(({ title, userAgent }) =>
43+
describe(title, () => {
44+
beforeAll(() => {
45+
vi.stubGlobal('navigator', { ...navigator, userAgent });
46+
vi.useFakeTimers();
47+
});
48+
afterAll(() => vi.useRealTimers());
49+
50+
it('does not store an id in sessionStorage', () => {
51+
analytics();
52+
expect(sessionStorage.getItem(SESSION_ID_KEY)).toBe(null);
53+
});
54+
55+
[PAGE_LOAD_EVENT, SESSION_START_EVENT].forEach((event) =>
56+
it(`does not send a "${event}" event`, () => {
57+
analytics();
58+
expect(vi.mocked(fetch)).not.toHaveBeenCalled();
59+
})
60+
);
61+
62+
it(`does not send a "${PAGE_UNLOAD_EVENT}" event`, () => {
63+
analytics();
64+
dispatchEvent(new Event('beforeunload'));
65+
expect(vi.mocked(navigator.sendBeacon)).not.toHaveBeenCalled();
66+
});
67+
68+
it(`does not send a "${SESSION_TIMEOUT_EVENT}" event`, () => {
69+
analytics();
70+
vi.advanceTimersByTime(INACTIVITY_TIMEOUT);
71+
expect(vi.mocked(fetch)).not.toHaveBeenCalled();
72+
});
73+
})
74+
);
75+
2176
describe('Humans', () => {
2277
const MESSAGE_METADATA = {
2378
headers: { 'Content-Type': 'application/json' },
@@ -33,11 +88,9 @@ describe('Humans', () => {
3388
beforeEach(() => {
3489
vi.spyOn(crypto, 'randomUUID').mockReturnValue(STUBBED_UUID);
3590
vi.spyOn(Date, 'now').mockReturnValue(STUBBED_TIMESTAMP);
36-
vi.stubGlobal('fetch', vi.fn());
3791
vi.stubGlobal('location', new URL(STUBBED_LOCATION));
3892
vi.stubGlobal('navigator', {
3993
...navigator,
40-
sendBeacon: vi.fn(),
4194
userAgent: STUBBED_USERAGENT,
4295
});
4396
vi.stubGlobal('screen', {
@@ -48,7 +101,10 @@ describe('Humans', () => {
48101
});
49102

50103
afterEach(() => sessionStorage.removeItem(SESSION_ID_KEY));
51-
afterAll(() => vi.unstubAllGlobals());
104+
afterAll(() => {
105+
vi.restoreAllMocks();
106+
vi.unstubAllGlobals();
107+
});
52108

53109
describe('New session', () => {
54110
const ORIGINAL_REFERRER = document.referrer;
@@ -119,7 +175,7 @@ describe('Humans', () => {
119175
});
120176
});
121177

122-
it(`sends a "${PAGE_UNLOAD_EVENT}" when leaving the page`, async () => {
178+
it(`sends a "${PAGE_UNLOAD_EVENT}" event when leaving the page`, async () => {
123179
analytics();
124180
dispatchEvent(new Event('beforeunload'));
125181
expect(vi.mocked(navigator.sendBeacon)).toHaveBeenCalled();
@@ -140,11 +196,10 @@ describe('Humans', () => {
140196
describe('Existing session', () => {
141197
const STUBBED_UUID_UNUSED = '043f18e8-9cf6-4440-81b1-cd13e4902e28';
142198

143-
beforeAll(() =>
144-
vi.spyOn(crypto, 'randomUUID').mockReturnValue(STUBBED_UUID_UNUSED)
145-
);
146-
beforeEach(() => sessionStorage.setItem(SESSION_ID_KEY, STUBBED_UUID));
147-
afterAll(() => vi.mocked(crypto.randomUUID).mockRestore()); // Revert to previous spy
199+
beforeEach(() => {
200+
sessionStorage.setItem(SESSION_ID_KEY, STUBBED_UUID);
201+
vi.spyOn(crypto, 'randomUUID').mockReturnValue(STUBBED_UUID_UNUSED);
202+
});
148203

149204
it('reuses the stored id', () => {
150205
analytics();
@@ -195,12 +250,9 @@ describe('Humans', () => {
195250
},
196251
];
197252

198-
beforeEach(() => {
199-
vi.mocked(Date.now).mockRestore(); // Revert to native implementation
200-
vi.useFakeTimers();
201-
});
202-
203-
afterEach(() => vi.useRealTimers());
253+
beforeAll(() => vi.useFakeTimers());
254+
beforeEach(() => vi.mocked(Date.now).mockRestore());
255+
afterAll(() => vi.useRealTimers());
204256

205257
it(`sends a "${SESSION_TIMEOUT_EVENT}" event after prolonged user inactivity`, () => {
206258
analytics();

0 commit comments

Comments
 (0)