Setting up Meticulous for Next.js with App Router
If you're setting up Meticulous for a Next.js app that uses the App Router you'll need to first follow the main setup guide. Once you've done so there are a few extra steps listed below.
1. Configure Meticulous to test your React server components (required)
Visit your project settings page and ensure you've selected the 'Stub all requests, apart from requests for server components and static assets' option in the 'Network Stubbing' section. This will ensure that Meticulous will correctly test your React server components.
2. Handling Math.random() and Date.now() in Server Components (recommended)
Meticulous works by executing user flows against the code before and after your code change, and comparing the resultant screenshots to detect any differences. For this to work the rendering of a page must be deterministic: a page rendered twice at around the same time with the same data should yield the same HTML. For client components Meticulous ensures this automatically - for example, Math.random() uses a fixed seed, and Date.now() will return a deterministic time relative to when the session started replaying. However Meticulous cannot automatically ensure this for server components.
If you use Math.random() in your server components then make sure you're on Next.js 14 or later, add a dependency on 'seedrandom' and add the following code to your instrumentation.ts:
import { headers } from 'next/headers';
import { alea } from 'seedrandom';
export function register() {
const nativeRandom = Math.random.bind(Math);
Math.random = () => {
try {
if (headers().get('meticulous-is-test') === '1') {
return alea(headers().get('meticulous-simulated-date') ?? undefined)();
}
} catch (e) {
// Fallback to nativeRandom below:
}
return nativeRandom();
};
}
You'll need to be on NextJS 14 or later for this to work.
And if you render text based on the current time (for example "Posted 7 minutes ago") in your server components, then first check if the meticulous-simulated-date header is present on the request, and if so then use that date instead of the current time. This avoids false positive diffs due to the time changing (e.g. "Posted 7 minutes ago" vs "Posted 8 minutes ago"): Meticulous will send the same timestamp every time for the same request. The timestamp is a UTC date in RFC 7231 format - you can parse it using Date.parse:
import { headers } from 'next/headers'
const getCurrentDate = () => {
// Meticulous sends a simulated date header when running tests: use that instead of the current date if present
const simulatedDate = headers().get('meticulous-simulated-date');
return simulatedDate ? new Date(Date.parse(simulatedDate)) : new Date();
}
You can test these changes by using a browser extension to set custom headers, setting meticulous-is-test
to '1' and meticulous-simulated-date
to 'Fri, 17 May 2024 13:35:20 GMT' and then visiting and reloading your page.
Alternatively you can add a meticulous-ignore
class to elements that contain a frequently changing server side rendered timestamp, or add the existing CSS selectors to the list of CSS selectors to ignore under 'Screenshots & Flakes' in your project settings. Learn more here.
3. Handling data that changes every minute (only needed in some cases)
Since Meticulous takes the screenshots for the base commit of your pull request and the head commit around the same time (within a few minutes of each other) changing data generally isn't a problem, even for server side rendered components. However if you do have data rendered in server components that changes on a minute by minute or second by second basis then you may get false diffs in the default setup. In this case please reach out to eng@meticulous.ai: we can help you set up a custom configuration to handle this.
Data that is fetched via fetch or XHR and rendered in client components is never an issue, even if it changes on a second by second basis. This is because Meticulous automatically stubs out the network responses for these requests with the responses recorded in the original session. Read more here.