---
title: Write end-to-end tests with flag overrides
description: Test Next.js application behavior with the Flags SDK and Playwright.
url: "https://flags-sdk.dev/docs/frameworks/next/guides/e2e-tests"
docs_index: /llms.txt
lastUpdated: 2026-10-07
navTitle: "End-to-End Tests"
---

> For an index of all documentation, see [/llms.txt](/llms.txt).

Set flag values for a browser session, then check the running application's behavior. End-to-end (e2e) tests exercise the application through a browser.

An override sets a flag value for a request. The Flags SDK reads these values from the encrypted `vercel-flag-overrides` cookie.

Overrides work with any provider connected through the Flags SDK.

## Before you start

Use your application's existing `FLAGS_SECRET`, the secret that the Flags SDK uses for Flags Explorer overrides.

The Playwright configuration below loads the application's environment files. The application and tests use the same secret to read and create overrides.

If you have not configured the Flags SDK, complete the [Next.js quickstart](/docs/frameworks/next) first.

Install Playwright, its Chromium browser, and the Next.js environment loader if your project does not already have them:

```sh title="Terminal"
npm install --save-dev @playwright/test @next/env
npx playwright install chromium
```

These commands add Playwright and the environment loader to your project, then download Chromium for the tests.

## Add a page with two flag states

Use an existing page that uses a flag, or add this `/dashboard` example. The heading changes with the flag value.

Declare the flag:

```ts title="flags.ts#next"
import { flag } from 'flags/next';

export const showNewDashboard = flag<boolean>({
  key: 'show-new-dashboard',
  defaultValue: false,
  decide: () => false,
});
```

Evaluate it in a page that renders for each request:

```tsx title="app/dashboard/page.tsx#next"
import { showNewDashboard } from '../../flags';

export default async function DashboardPage() {
  const enabled = await showNewDashboard();

  return <h1>{enabled ? 'New dashboard' : 'Old dashboard'}</h1>;
}
```

## Configure Playwright

Load the application's development environment files, including `.env.local`, before Playwright starts the tests:

```ts title="playwright.config.ts"
import { loadEnvConfig } from '@next/env';
import { defineConfig } from '@playwright/test';

loadEnvConfig(process.cwd(), true);

const baseURL = process.env.BASE_URL ?? 'http://localhost:3000';

export default defineConfig({
  testDir: './tests',
  use: {
    baseURL,
    browserName: 'chromium',
  },
  webServer: process.env.BASE_URL
    ? undefined
    : {
        command: 'npm run dev',
        url: new URL('/dashboard', baseURL).toString(),
        reuseExistingServer: !process.env.CI,
      },
});
```

Playwright starts the application with your existing `dev` script and waits for `/dashboard` to respond.

For another page, update `webServer.url` and the test's `page.goto()` path. The override fixture uses `baseURL` to select the cookie's hostname.

To test an existing server or Preview Deployment, set `BASE_URL` and supply that application's `FLAGS_SECRET`.

> If you test on Vercel with Deployment Protection enabled, configure [Protection Bypass for Automation](https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/protection-bypass-automation).
>
> Pass the value of `VERCEL_AUTOMATION_BYPASS_SECRET` in the `x-vercel-protection-bypass` header on each request. This secret is separate from `FLAGS_SECRET`, which encrypts flag overrides.

See [Next.js environment loading](https://nextjs.org/docs/app/guides/environment-variables#loading-environment-variables-with-nextenv) and [Playwright configuration](https://playwright.dev/docs/test-configuration) for additional options.

## Create an override fixture

A Playwright fixture supplies tools and setup to a test. Add a `setFlagOverrides()` fixture that encrypts flag values and stores them in the override cookie:

```ts title="tests/fixtures.ts"
import { test as base } from '@playwright/test';
import { encryptOverrides, type FlagOverridesType } from 'flags';

type FlagFixtures = {
  setFlagOverrides: (values: FlagOverridesType) => Promise<void>;
};

export const test = base.extend<FlagFixtures>({
  setFlagOverrides: async ({ context, baseURL }, use) => {
    const secret = process.env.FLAGS_SECRET;

    if (!secret) {
      throw new Error('FLAGS_SECRET is missing from the application environment.');
    }
    if (!baseURL) {
      throw new Error('Set baseURL in the Playwright configuration.');
    }

    const target = new URL(baseURL);

    await use(async (values) => {
      const encrypted = await encryptOverrides(values, secret, '1h');

      await context.addCookies([
        {
          name: 'vercel-flag-overrides',
          value: encrypted,
          domain: target.hostname,
          path: '/',
          httpOnly: true,
          secure: target.protocol === 'https:',
          sameSite: 'Lax',
        },
      ]);
    });
  },
});

export { expect } from '@playwright/test';
```

Use the declared `key`, such as `show-new-dashboard`, as the map key. The exported function name can be different.

The cookie covers all paths on the target hostname. The encrypted values expire after one hour. See [`encryptOverrides()`](/docs/api-reference/core/core#encryptoverrides) and [Playwright's cookie API](https://playwright.dev/docs/api/class-browsercontext#browser-context-add-cookies) for details.

## Test both flag values

Import `test` and `expect` from your fixture file. Call `setFlagOverrides()` before the first page request that evaluates the flag:

```ts title="tests/dashboard.spec.ts"
import { expect, test } from './fixtures';

for (const enabled of [false, true]) {
  test(`shows the selected dashboard when enabled=${enabled}`, async ({
    page,
    setFlagOverrides,
  }) => {
    await setFlagOverrides({ 'show-new-dashboard': enabled });
    await page.goto('/dashboard');

    const selectedTitle = enabled ? 'New dashboard' : 'Old dashboard';
    const otherTitle = enabled ? 'Old dashboard' : 'New dashboard';

    await expect(
      page.getByRole('heading', { name: selectedTitle, exact: true }),
    ).toBeVisible();
    await expect(
      page.getByRole('heading', { name: otherTitle, exact: true }),
    ).toHaveCount(0);
  });
}
```

Run the tests:

```sh title="Terminal"
npx playwright test tests/dashboard.spec.ts
```

Both tests should pass. Each test checks that the page shows the heading for its flag value and hides the other heading.

Playwright gives each test a separate browser context, so cookies from one test do not affect another.

## Test additional values and pages

Overrides support boolean, string, and numeric values, as well as arrays and objects that your flag accepts. Set all required flag values in one call. Each `setFlagOverrides()` call replaces the cookie contents.

For pages that use precomputed flag values, set overrides before the request that computes those values. Check the page content to confirm that the application used the expected flag values.

The override cookie applies to requests on the configured hostname. Services that evaluate flags directly through a provider need their own test setup.

## Next steps

[Write unit tests with the Flags SDK](/docs/frameworks/next/guides/unit-tests)

---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)