Skip to content

Commit 4fec3b3

Browse files
authored
test(tracing): Add CLS tests (#4410)
1 parent 8248657 commit 4fec3b3

File tree

8 files changed

+330
-2
lines changed

8 files changed

+330
-2
lines changed

packages/integration-tests/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"test": "playwright test ./suites"
2020
},
2121
"dependencies": {
22+
"@babel/preset-typescript": "^7.16.7",
2223
"@playwright/test": "^1.17.0",
2324
"babel-loader": "^8.2.2",
2425
"handlebars-loader": "^1.7.1",

packages/integration-tests/suites/tracing/metrics/init.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ window.Sentry = Sentry;
55

66
Sentry.init({
77
dsn: 'https://[email protected]/1337',
8-
integrations: [new Integrations.BrowserTracing()],
8+
integrations: [
9+
new Integrations.BrowserTracing({
10+
idleTimeout: 9000,
11+
}),
12+
],
913
tracesSampleRate: 1,
1014
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { simulateCLS } from '../../../../utils/web-vitals/cls.ts';
2+
3+
// Getting expected CLS parameter from URL hash
4+
const expectedCLS = Number(location.hash.slice(1));
5+
6+
simulateCLS(expectedCLS).then(
7+
// Triggering reload to make sure getCLS has its closure before we send the transaction
8+
() => location.reload(),
9+
);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<title></title>
6+
<script src="{{htmlWebpackPlugin.options.initialization}}"></script>
7+
</head>
8+
<body>
9+
<div id="content"></div>
10+
<script src="{{htmlWebpackPlugin.options.subject}}"></script>
11+
</body>
12+
</html>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { expect } from '@playwright/test';
2+
3+
import { sentryTest } from '../../../../utils/fixtures';
4+
import { getSentryTransactionRequest } from '../../../../utils/helpers';
5+
6+
sentryTest.beforeEach(async ({ browserName, page }) => {
7+
if (browserName !== 'chromium') {
8+
sentryTest.skip();
9+
}
10+
11+
await page.setViewportSize({ width: 800, height: 1200 });
12+
});
13+
14+
sentryTest('should capture a "GOOD" CLS vital with its source(s).', async ({ getLocalTestPath, page }) => {
15+
const url = await getLocalTestPath({ testDir: __dirname });
16+
const eventData = await getSentryTransactionRequest(page, `${url}#0.05`);
17+
18+
expect(eventData.measurements).toBeDefined();
19+
expect(eventData.measurements?.cls?.value).toBeDefined();
20+
expect(eventData.measurements?.cls?.value).toBeCloseTo(0.05);
21+
expect(eventData.tags?.['cls.source.1']).toBe('body > div#content > p#partial');
22+
});
23+
24+
sentryTest('should capture a "MEH" CLS vital with its source(s).', async ({ getLocalTestPath, page }) => {
25+
const url = await getLocalTestPath({ testDir: __dirname });
26+
const eventData = await getSentryTransactionRequest(page, `${url}#0.21`);
27+
28+
expect(eventData.measurements).toBeDefined();
29+
expect(eventData.measurements?.cls?.value).toBeDefined();
30+
expect(eventData.measurements?.cls?.value).toBeCloseTo(0.21);
31+
expect(eventData.tags?.['cls.source.1']).toBe('body > div#content > p');
32+
});
33+
34+
sentryTest('should capture a "POOR" CLS vital with its source(s).', async ({ getLocalTestPath, page }) => {
35+
const url = await getLocalTestPath({ testDir: __dirname });
36+
const eventData = await getSentryTransactionRequest(page, `${url}#0.35`);
37+
38+
expect(eventData.measurements).toBeDefined();
39+
expect(eventData.measurements?.cls?.value).toBeDefined();
40+
expect(eventData.measurements?.cls?.value).toBeCloseTo(0.35);
41+
expect(eventData.tags?.['cls.source.1']).toBe('body > div#content > p');
42+
});
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
const MOVE_ITERATIONS = 20;
2+
const MOVE_DELAY = 50;
3+
4+
function createContentfulBlock(): void {
5+
const intViewportHeight = document.documentElement.clientHeight;
6+
const intViewportWidth = document.documentElement.clientWidth;
7+
8+
const p = document.createElement('p');
9+
p.textContent = 'a contentful block';
10+
p.style.height = `${intViewportHeight / 20}px`;
11+
p.style.width = `${intViewportWidth}px`;
12+
p.style.overflow = 'hidden';
13+
p.style.position = 'absolute';
14+
p.style.backgroundColor = '#aaa';
15+
document.getElementById('content')?.appendChild(p);
16+
}
17+
18+
function addPercent(percent: number, prop: string): string {
19+
const n = Number(prop.replace('px', ''));
20+
const pixels = (document.documentElement.clientHeight * percent) / 100;
21+
return `${n + pixels}px`;
22+
}
23+
24+
async function moveBy(elem: HTMLParagraphElement, percent: number): Promise<void> {
25+
if (elem.getAttribute('id') === 'partial') {
26+
const max = Number(elem.getAttribute('max-steps'));
27+
let current = Number(elem.getAttribute('steps'));
28+
current += 1;
29+
if (current > max) {
30+
return;
31+
}
32+
elem.setAttribute('steps', `${current}`);
33+
}
34+
35+
return new Promise(resolve => {
36+
setTimeout(() => {
37+
elem.style.top = addPercent(percent, elem.style.top);
38+
resolve();
39+
}, MOVE_DELAY);
40+
});
41+
}
42+
43+
async function moveAll(elems: HTMLParagraphElement[], times: number): Promise<void> {
44+
for (let i = 0; i < times; i++)
45+
for (const el of elems) {
46+
await moveBy(el, 5);
47+
}
48+
}
49+
50+
function howMany(desiredCls: number): { extraSteps: number; createParagraphs: number } {
51+
const fullRuns = Math.floor(desiredCls / 0.095);
52+
const extraSteps = Math.round((desiredCls - fullRuns * 0.095) / 0.005);
53+
let create = fullRuns;
54+
if (extraSteps > 0) {
55+
create += 1;
56+
}
57+
58+
return { extraSteps: extraSteps, createParagraphs: create };
59+
}
60+
61+
export async function simulateCLS(desiredCLS: number): Promise<void> {
62+
const { extraSteps, createParagraphs } = howMany(desiredCLS);
63+
64+
for (let i = 0; i < createParagraphs; i++) {
65+
createContentfulBlock();
66+
}
67+
68+
const elems = Array.from(document.getElementsByTagName('p'));
69+
elems[0]?.setAttribute('id', 'partial');
70+
elems[0]?.setAttribute('max-steps', `${extraSteps}`);
71+
elems[0]?.setAttribute('steps', '0');
72+
73+
return moveAll(elems, MOVE_ITERATIONS);
74+
}

packages/integration-tests/webpack.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ const config = function(userConfig: Record<string, unknown>): Configuration {
77
module: {
88
rules: [
99
{
10-
test: /\.js$/,
10+
test: /\.(js|ts)$/,
1111
exclude: /node_modules/,
1212
loader: 'babel-loader',
13+
options: { presets: [['@babel/preset-typescript', { allowNamespaces: true }]] },
1314
},
1415
{
1516
test: /\.hbs$/,

0 commit comments

Comments
 (0)