Testing Web Vitals With Cypress

craigmorten

Craig Morten

Posted on April 3, 2022

Testing Web Vitals With Cypress

It is well understood that performance is a critical consideration for any website which can have far reaching impacts on everything from customer satisfaction, SEO, and ultimately your bottom line. You cannot determine the success of performance work without the ability measure the results and compare to performance budgets - this calls for testing infrastructure to make sure you have the necessary visibility on metrics... introducing cypress-web-vitals.

cypress-web-vitals allows you to test against the Google Web Vital signals within your Cypress workflows through a new cy.vitals() custom command.

Web Vitals is an initiative by Google to provide unified guidance for quality signals that are essential to delivering a great user experience on the web.

Reference: https://web.dev/vitals/

 Getting started

In your project, install the dependencies:

npm install --save-dev cypress-web-vitals cypress-real-events
Enter fullscreen mode Exit fullscreen mode

Note: cypress-web-vitals currently makes use of cypress-real-events to click the page to calculate first input delay. Hence it is needed as a peer-dependency.

Within you support commands file (normally cypress/support/commands.js), add this one liner to get setup:

import "cypress-web-vitals";
Enter fullscreen mode Exit fullscreen mode

And now you're set to get going with Web Vital performance budget tests in your Cypress workflow! 🎉

Add you first test like so:

describe("Web Vitals", () => {
  it("should pass the meet Google's 'Good' thresholds", () => {
    cy.vitals({ url: "https://www.google.com/" });
  });
});
Enter fullscreen mode Exit fullscreen mode

You are now set up to test against all of the Web Vitals using Google's "Good" threshold values:

Customise your tests

You can further customise your tests through additional optional configuration which is passed to the cy.vitals(webVitalsConfig) call:

  • Optional url: string - The url to audit. If not provided you will need to have called cy.visit(url) prior to the command.
  • Optional firstInputSelector: string - The element to click for capturing FID. The first matching element is used. Default: "body".
  • Optional thresholds: object - The thresholds to audit the Web Vitals against. If not provided Google's "Good" thresholds will be used. If provided, any missing Web Vitals signals will not be audited.
  • Optional vitalsReportedTimeout: number - Time in ms to wait for Web Vitals to be reported before failing. Default: 10000.

For example:

// Use the `main` element for clicking to capture the FID.
cy.vitals({ firstInputSelector: "main" });

// Test the page against against a CLS threshold of 0.2.
cy.vitals({ thresholds: { cls: 0.2 } });
Enter fullscreen mode Exit fullscreen mode

For more details on usage refer to the API docs.

How does it work?

  1. The url is visited with the HTML response intercepted and modified by Cypress to include the web-vitals script and some code to record the Web Vitals values.
  2. Several elements (if exist) starting with the provided element (based on firstInputSelector) are then clicked in quick succession to simulate a user clicking on the page. Note: if choosing a custom element, don't pick something that will navigate away from the page otherwise the plugin will fail to capture the Web Vitals metrics.
  3. The audit then waits for the page load event to allow for the values of LCP and CLS to settle; which are subject to change as different parts of the page load into view.
  4. Next the audit simulates a page visibility state change which is required for the CLS Web Vital to be reported.
  5. The audit then attempts to wait for any outstanding Web Vitals to be reported for which thresholds have been provided.
  6. Finally the Web Vitals values are compared against the thresholds, logging successful results and throwing an error for any unsuccessful signals. Note: if the audit was unable to record a Web Vital then it is logged, but the test will not fail.

Testing sites in the wild

Here are some example test runs against FAANG homepages to see cypress-web-vitals in action:

Facebook

cy.vitals({ url: "https://www.facebook.com/" });
Enter fullscreen mode Exit fullscreen mode

Cypress Web Vitals test against the Facebook landing page. All metrics below Google's

Amazon

cy.vitals({ url: "https://www.amazon.com/" });
Enter fullscreen mode Exit fullscreen mode

Cypress Web Vitals test against the Amazon home page. All metrics below Google's

Netflix

cy.vitals({
  url: "https://www.netflix.com/gb/",
  firstInputSelector: ".our-story-card-title",
});
Enter fullscreen mode Exit fullscreen mode

Cypress Web Vitals test against the Netflix landing page. All metrics below Google's

For Netflix we have had to introduce a custom element choice for the simulated "first click". Even though first input delay can be measured when in cases where there is no event listener registered to the element, there are scenarios where clicking the body doesn't cut it. Some good examples of elements that will reliably trigger an FID metric are:

  • Text fields, checkboxes, and radio buttons (<input>, <textarea>)
  • Select dropdowns (<select>)
  • Links (<a>)

In order to ensure the Web Vitals can be tested against, it is best to try find an element that reliably triggers an FID, but won't navigate away from the page (perhaps avoid <a>!).

 Google

cy.vitals({ url: "https://www.google.com/" });
Enter fullscreen mode Exit fullscreen mode

Cypress Web Vitals test against the Google home page. All metrics below Google's

Wrap-up

Using any awesome performance testing tooling lately? Tried out cypress-web-vitals on your site and have results to share? Got any comments, queries, or questions? Leave a comment below!

That's all folks! 🚀

💖 💪 🙅 🚩
craigmorten
Craig Morten

Posted on April 3, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Testing Web Vitals With Cypress
javascript Testing Web Vitals With Cypress

April 3, 2022