Skip to content

Commit

Permalink
use fetch to send commercial metrics (#1382)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jakeii authored May 17, 2024
1 parent d983f2d commit 6580399
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 77 deletions.
5 changes: 5 additions & 0 deletions .changeset/lovely-zoos-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@guardian/commercial': minor
---

Use fetch instead of sendBeacon for commercial metrics
190 changes: 117 additions & 73 deletions src/core/send-commercial-metrics.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ const defaultMetrics = {
],
};

const expectedOptions = {
method: 'POST',
body: JSON.stringify(defaultMetrics),
keepalive: true,
cache: 'no-store',
mode: 'no-cors',
};

const tcfv2AllConsent: ConsentState = {
tcfv2: {
consents: {
Expand Down Expand Up @@ -124,7 +132,7 @@ afterEach(() => {
});

describe('send commercial metrics', () => {
Object.defineProperty(navigator, 'sendBeacon', {
Object.defineProperty(window, 'fetch', {
configurable: true,
enumerable: true,
value: jest.fn(),
Expand All @@ -147,8 +155,8 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('visibilitychange'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([
[Endpoints.PROD, JSON.stringify(defaultMetrics)],
expect((window.fetch as jest.Mock).mock.calls).toEqual([
[Endpoints.PROD, expectedOptions],
]);
});

Expand All @@ -166,7 +174,7 @@ describe('send commercial metrics', () => {
setVisibility('visible');
global.dispatchEvent(new Event('visibilitychange'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([]);
expect((window.fetch as jest.Mock).mock.calls).toEqual([]);
});

it('does not send metrics when user is not in sampling group', async () => {
Expand All @@ -183,7 +191,7 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('visibilitychange'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([]);
expect((window.fetch as jest.Mock).mock.calls).toEqual([]);
});

it('does not send metrics when consent does not include purpose 8', async () => {
Expand All @@ -200,7 +208,7 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('visibilitychange'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([]);
expect((window.fetch as jest.Mock).mock.calls).toEqual([]);
});

it('sends metrics when non-TCFv2 user (i.e. USA or Australia) consents', async () => {
Expand All @@ -217,8 +225,8 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('visibilitychange'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([
[Endpoints.PROD, JSON.stringify(defaultMetrics)],
expect((window.fetch as jest.Mock).mock.calls).toEqual([
[Endpoints.PROD, expectedOptions],
]);
});

Expand All @@ -236,8 +244,8 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('visibilitychange'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([
[Endpoints.PROD, JSON.stringify(defaultMetrics)],
expect((window.fetch as jest.Mock).mock.calls).toEqual([
[Endpoints.PROD, expectedOptions],
]);
});

Expand Down Expand Up @@ -280,8 +288,8 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('visibilitychange'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([
[Endpoints.PROD, JSON.stringify(defaultMetrics)],
expect((window.fetch as jest.Mock).mock.calls).toEqual([
[Endpoints.PROD, expectedOptions],
]);
});

Expand All @@ -300,7 +308,7 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('visibilitychange'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([]);
expect((window.fetch as jest.Mock).mock.calls).toEqual([]);
});

it('expects to be initialised before calling bypassCoreWebVitalsSampling', async () => {
Expand Down Expand Up @@ -333,16 +341,22 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('visibilitychange'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([
expect((window.fetch as jest.Mock).mock.calls).toEqual([
[
Endpoints.CODE,
JSON.stringify({
...defaultMetrics,
properties: [
{ name: 'isDev', value: 'testurl.theguardian.com' },
{ name: 'adBlockerInUse', value: 'false' },
],
}),
{
...expectedOptions,
body: JSON.stringify({
...defaultMetrics,
properties: [
{
name: 'isDev',
value: 'testurl.theguardian.com',
},
{ name: 'adBlockerInUse', value: 'false' },
],
}),
},
],
]);
});
Expand All @@ -368,17 +382,20 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('visibilitychange'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([
expect((window.fetch as jest.Mock).mock.calls).toEqual([
[
Endpoints.PROD,
JSON.stringify({
...defaultMetrics,
properties: [
{ name: 'downlink', value: '1' },
{ name: 'effectiveType', value: '4g' },
{ name: 'adBlockerInUse', value: 'false' },
],
}),
{
...expectedOptions,
body: JSON.stringify({
...defaultMetrics,
properties: [
{ name: 'downlink', value: '1' },
{ name: 'effectiveType', value: '4g' },
{ name: 'adBlockerInUse', value: 'false' },
],
}),
},
],
]);
});
Expand All @@ -404,18 +421,24 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('pagehide'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([
expect((window.fetch as jest.Mock).mock.calls).toEqual([
[
Endpoints.CODE,
JSON.stringify({
...defaultMetrics,
properties: [
{ name: 'downlink', value: '1' },
{ name: 'effectiveType', value: '4g' },
{ name: 'isDev', value: 'testurl.theguardian.com' },
{ name: 'adBlockerInUse', value: 'false' },
],
}),
{
...expectedOptions,
body: JSON.stringify({
...defaultMetrics,
properties: [
{ name: 'downlink', value: '1' },
{ name: 'effectiveType', value: '4g' },
{
name: 'isDev',
value: 'testurl.theguardian.com',
},
{ name: 'adBlockerInUse', value: 'false' },
],
}),
},
],
]);
});
Expand Down Expand Up @@ -467,17 +490,23 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('pagehide'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([
expect((window.fetch as jest.Mock).mock.calls).toEqual([
[
Endpoints.CODE,
JSON.stringify({
...defaultMetrics,
properties: [
{ name: 'downlink', value: '1' },
{ name: 'effectiveType', value: '4g' },
{ name: 'isDev', value: 'testurl.theguardian.com' },
],
}),
{
...expectedOptions,
body: JSON.stringify({
...defaultMetrics,
properties: [
{ name: 'downlink', value: '1' },
{ name: 'effectiveType', value: '4g' },
{
name: 'isDev',
value: 'testurl.theguardian.com',
},
],
}),
},
],
]);
});
Expand All @@ -498,17 +527,23 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('pagehide'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([
expect((window.fetch as jest.Mock).mock.calls).toEqual([
[
Endpoints.CODE,
JSON.stringify({
...defaultMetrics,
properties: [
{ name: 'adSlotsInline', value: '5' },
{ name: 'adSlotsTotal', value: '10' },
{ name: 'isDev', value: 'testurl.theguardian.com' },
],
}),
{
...expectedOptions,
body: JSON.stringify({
...defaultMetrics,
properties: [
{ name: 'adSlotsInline', value: '5' },
{ name: 'adSlotsTotal', value: '10' },
{
name: 'isDev',
value: 'testurl.theguardian.com',
},
],
}),
},
],
]);
});
Expand All @@ -531,13 +566,16 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('pagehide'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([
expect((window.fetch as jest.Mock).mock.calls).toEqual([
[
Endpoints.PROD,
JSON.stringify({
...defaultMetrics,
metrics: [{ name: 'offlineCount', value: 3 }],
}),
{
...expectedOptions,
body: JSON.stringify({
...defaultMetrics,
metrics: [{ name: 'offlineCount', value: 3 }],
}),
},
],
]);
});
Expand All @@ -558,13 +596,16 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('pagehide'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([
expect((window.fetch as jest.Mock).mock.calls).toEqual([
[
Endpoints.PROD,
JSON.stringify({
...defaultMetrics,
metrics: [{ name: 'offlineCount', value: 0 }],
}),
{
...expectedOptions,
body: JSON.stringify({
...defaultMetrics,
metrics: [{ name: 'offlineCount', value: 0 }],
}),
},
],
]);
});
Expand All @@ -585,13 +626,16 @@ describe('send commercial metrics', () => {
setVisibility('hidden');
global.dispatchEvent(new Event('pagehide'));

expect((navigator.sendBeacon as jest.Mock).mock.calls).toEqual([
expect((window.fetch as jest.Mock).mock.calls).toEqual([
[
Endpoints.PROD,
JSON.stringify({
...defaultMetrics,
metrics: [],
}),
{
...expectedOptions,
body: JSON.stringify({
...defaultMetrics,
metrics: [],
}),
},
],
]);
});
Expand Down
11 changes: 7 additions & 4 deletions src/core/send-commercial-metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,13 @@ function sendMetrics() {
commercialMetricsPayload,
);

return navigator.sendBeacon(
endpoint,
JSON.stringify(commercialMetricsPayload),
);
void fetch(endpoint, {
method: 'POST',
body: JSON.stringify(commercialMetricsPayload),
keepalive: true,
cache: 'no-store',
mode: 'no-cors',
});
}

type ArrayMetric = [key: string, value: string | number];
Expand Down

0 comments on commit 6580399

Please sign in to comment.