Skip to content

Commit

Permalink
Merge pull request #148 from GoogleChrome/update-cls
Browse files Browse the repository at this point in the history
Update CLS to max session window 5s cap 1s gap
  • Loading branch information
philipwalton committed May 11, 2021
2 parents d51aa10 + 1867689 commit 8614473
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 9 deletions.
27 changes: 24 additions & 3 deletions src/getCLS.ts
Expand Up @@ -32,12 +32,32 @@ export const getCLS = (onReport: ReportHandler, reportAllChanges?: boolean) => {
let metric = initMetric('CLS', 0);
let report: ReturnType<typeof bindReporter>;

let sessionValue = 0;
let sessionEntries: PerformanceEntry[] = [];

const entryHandler = (entry: LayoutShift) => {
// Only count layout shifts without recent user input.
if (!entry.hadRecentInput) {
(metric.value as number) += entry.value;
metric.entries.push(entry);
report();
const firstSessionEntry = sessionEntries[0];
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];

// If the entry is part of the current session, add it.
// Otherwise, start a new session.
if (sessionValue &&
entry.startTime - lastSessionEntry.startTime < 1000 &&
entry.startTime - firstSessionEntry.startTime < 5000) {
sessionValue += entry.value;
sessionEntries.push(entry);
} else {
sessionValue = entry.value;
sessionEntries = [entry];
}

if (sessionValue > metric.value) {
metric.value = sessionValue;
metric.entries = sessionEntries;
report();
}
}
};

Expand All @@ -51,6 +71,7 @@ export const getCLS = (onReport: ReportHandler, reportAllChanges?: boolean) => {
});

onBFCacheRestore(() => {
sessionValue = 0;
metric = initMetric('CLS', 0);
report = bindReporter(onReport, metric, reportAllChanges);
});
Expand Down
97 changes: 97 additions & 0 deletions test/e2e/getCLS-test.js
Expand Up @@ -72,6 +72,103 @@ describe('getCLS()', async function() {
assert.strictEqual(cls.entries.length, 2);
});

it('resets the session after timeout or gap elapses', async function() {
if (!browserSupportsCLS) this.skip();

await browser.url('/test/cls');

// Wait until all images are loaded and rendered.
await imagesPainted();
await browser.pause(1000);

await stubVisibilityChange('hidden');
await beaconCountIs(1);

const [cls1] = await getBeacons();

assert(cls1.value >= 0);
assert(cls1.id.match(/^v1-\d+-\d+$/));
assert.strictEqual(cls1.name, 'CLS');
assert.strictEqual(cls1.value, cls1.delta);
assert.strictEqual(cls1.entries.length, 2);

await browser.pause(1000);
await stubVisibilityChange('visible');
await clearBeacons();

// Force 2 layout shifts, totaling 0.5.
await browser.executeAsync((done) => {
document.body.style.overflow = 'hidden'; // Prevent scroll bars.
document.querySelector('main').style.left = '25vmax';
setTimeout(() => {
document.querySelector('main').style.left = '0px';
done();
}, 50);
});

await stubVisibilityChange('hidden');
await beaconCountIs(1);

const [cls2] = await getBeacons();

// The value should be exactly 0.5, but round just in case.
assert.strictEqual(Math.round(cls2.value * 100) / 100, 0.5);
assert.strictEqual(cls2.name, 'CLS');
assert.strictEqual(cls2.value, cls1.value + cls2.delta);
assert.strictEqual(cls2.entries.length, 2);
assert(cls2.id.match(/^v1-\d+-\d+$/));

await browser.pause(1000);
await stubVisibilityChange('visible');
await clearBeacons();

// Force 4 separate layout shifts, totaling 1.5.
await browser.executeAsync((done) => {
document.querySelector('main').style.left = '25vmax';
setTimeout(() => {
document.querySelector('main').style.left = '0px';
setTimeout(() => {
document.querySelector('main').style.left = '50vmax';
setTimeout(() => {
document.querySelector('main').style.left = '0px';
done();
}, 50);
}, 50);
}, 50);
});

await stubVisibilityChange('hidden');
await beaconCountIs(1);

const [cls3] = await getBeacons();

// The value should be exactly 1.5, but round just in case.
assert.strictEqual(Math.round(cls3.value * 100) / 100, 1.5);
assert.strictEqual(cls3.name, 'CLS');
assert.strictEqual(cls3.value, cls2.value + cls3.delta);
assert.strictEqual(cls3.entries.length, 4);
assert(cls3.id.match(/^v1-\d+-\d+$/));

await browser.pause(1000);
await stubVisibilityChange('visible');
await clearBeacons();

// Force 2 layout shifts, totalling 1.0 (less than the previous max).
await browser.executeAsync((done) => {
document.querySelector('main').style.left = '50vmax';
setTimeout(() => {
document.querySelector('main').style.left = '0px';
done();
}, 50);
});

// Wait a bit to ensure no beacons were sent.
await browser.pause(1000);

const beacons = await getBeacons();
assert.strictEqual(beacons.length, 0);
});

it('does not report if the browser does not support CLS', async function() {
if (browserSupportsCLS) this.skip();

Expand Down
15 changes: 9 additions & 6 deletions test/views/layout.njk
Expand Up @@ -44,19 +44,22 @@
{% endif %}
{% block head %}{% endblock %}
<style>
html {
height: 100%
* {
box-sizing: border-box;
}
*[hidden] {
visibility: hidden;
}
body {
font: 1em/1.5 sans-serif;
margin: 0;
min-height: 100%;
}
main {
border: 1px solid transparent; /* Prevent margin collapsing */
min-height: 100vh;
padding: 0 1em;
}
*[hidden] {
visibility: hidden;
position: relative;
width: 100%;
}
</style>
</head>
Expand Down

0 comments on commit 8614473

Please sign in to comment.