Jump to content

Playwright: Difference between revisions

From Official NixOS Wiki
Add a new way to use playwright with NixOS that employees a container runtime
Increased clarity, added summary of Playwright
Tags: Mobile edit Mobile web edit
Line 1: Line 1:
== Installing browsers for playwright under NixOS ==
Playwright Test is an end-to-end test framework for modern web apps. It bundles test runner, assertions, isolation, parallelization and rich tooling.


Normally, at first run, [https://playwright.dev/ playwright] will tell you to run <code>playwright install</code>. The purpose of this is to install browsers for you that it can then use for testing. The installation itself will technically work. Unfortunately, the installed browsers will not be suitable to be used inside NixOS. This is due to the fact that dependencies will not be at places where the browsers expect them to be. To mitigate this problem, nixpkgs has a package called <code>playwright-driver.browsers</code>. Before you start your script, make sure to set
== Installing browsers for Playwright under NixOS ==
<syntaxHighlight lang=nix>
At first run, Playwright will prompt the user to run <code>playwright install</code> to install browsers. However, the browsers will not function due to missing dependencies.
export PLAYWRIGHT_BROWSERS_PATH=/path/to/drivers
 
</syntaxHighlight>
The nixpkgs package <code>playwright-driver.browsers</code> provides a packaged form of the browsers that can be assigned to the PLAYWRIGHT_BROWSERS_PATH environment variable.


You can for example put this <code>shell.nix</code> in the directory with your playwright-related code:
An example <code>shell.nix</code> that installs [[ Visual Studio Code ]] and Playwright.


<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
{ pkgs ? import <nixpkgs> {} }:
{ pkgs ? import <nixpkgs> {} }:
   pkgs.mkShell {
   pkgs.mkShell {
     nativeBuildInputs = [
     nativeBuildInputs = with pkgs; [
       pkgs.playwright-driver.browsers
       vscode
      playwright-driver.browsers
     ];
     ];


Line 18: Line 19:
       export PLAYWRIGHT_BROWSERS_PATH=${pkgs.playwright-driver.browsers}
       export PLAYWRIGHT_BROWSERS_PATH=${pkgs.playwright-driver.browsers}
       export PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS=true
       export PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS=true
      export PLAYWRIGHT_HOST_PLATFORM_OVERRIDE="ubuntu-24.04"
     '';
     '';
}
}
</syntaxHighlight>
</syntaxHighlight>


== Playwright with Visual Studio Code ==
{{Note|The version of Playwright in Nixpkgs and your developer environment must match.}}
 
If you are using playwright with [https://wiki.nixos.org/wiki/Visual_Studio_Code Visual Studio Code], you may want to add <code>vscode</code> to the package list shown earlier. With Visual Studio Code installed you can  run <code>nix-shell --run "code ."</code> to open your playwright-related directory.
 
Don't forget to install the <code>Playwright Test for VSCode</code> extension in Visual Studio Code.
 
Then you should be able to run your tests in Visual Studio Code.
 
{{Note|Keep in mind that you need to use the same version of playwright in your node playwright project as in your nixpkgs, or else playwright will try to use browsers versions that aren't installed!}}


== Playwright with Devenv ==
== Using Devenv ==


With [https://devenv.sh/ Devenv] you can also set certain environment variables and pin packages to make playwright work.
With [https://devenv.sh/ Devenv] you can also set certain environment variables and pin packages to make playwright work.
Line 39: Line 33:
=== devenv.nix ===
=== devenv.nix ===


<syntaxhighlight lang="nix">
<syntaxHighlight lang=nix>
{ pkgs, lib, config, inputs, ... }:
{ pkgs, lib, config, inputs, ... }:


Line 70: Line 64:
   # https://devenv.sh/scripts/
   # https://devenv.sh/scripts/
   scripts.intro.exec = ''
   scripts.intro.exec = ''
     playwrightNpmVersion="$(npm view ./. devDependencies'[@playwright/test]')"
     playwrightNpmVersion="$(npm show @playwright/test version)"
     echo "❄️ Playwright nix version: ${pkgs-playwright.playwright.version}"
     echo "❄️ Playwright nix version: ${pkgs-playwright.playwright.version}"
     echo "📦 Playwright npm version: $playwrightNpmVersion"
     echo "📦 Playwright npm version: $playwrightNpmVersion"
Line 90: Line 84:
   # See full reference at https://devenv.sh/reference/options/
   # See full reference at https://devenv.sh/reference/options/
}
}
</syntaxhighlight>
</syntaxHighlight>


=== devenv.yaml ===
=== devenv.yaml ===
Line 133: Line 127:
</syntaxHighlight>
</syntaxHighlight>


== Playwright via Docker ==
== Bun + Playwright ==
https://playwright.dev/docs/docker#remote-connection can be a good option to make use of a container (instead of wrestling with NixOS) to run browsers.<syntaxhighlight lang="nix"># devenv.nix example that simplifies the configuration
 
# but it's easy to setup the env var and run the command manually without devenv too
Here is a <code>flake.nix</code> that is working with bun instead of nodejs (working today 2026-01-23).
Put <code>flake.nix</code>, <code>playwright.config.ts</code>, <code>package.json</code> and <code>tests/example.spec.ts</code> in the root directory of your project and run
* <code>nix develop</code> to install packages and enter the devShell
* <code>bun install</code> to download playwright dependencies to node_modules
* <code>bun --bun playwright test --headed</code> to test the setup
* <code>bun playwright test --headed</code> to verify that without <code>--bun</code> flag the nodejs version is run and bun library is not available.
 
==== flake.nix ====
 
<syntaxHighlight lang=nix>
{
  description = "Bun + Playwright dev shell";
 
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs";
    flake-utils.url = "github:numtide/flake-utils";
  };
 
  outputs =
    {
      self,
      nixpkgs,
      flake-utils,
      ...
    }:
    flake-utils.lib.eachDefaultSystem (
      system:
      let
        pkgs = import nixpkgs { inherit system; };
        browsers =
          (builtins.fromJSON (builtins.readFile "${pkgs.playwright-driver}/browsers.json")).browsers;
        chromium-rev = (builtins.head (builtins.filter (x: x.name == "chromium") browsers)).revision;
      in
      {
        devShells.default = pkgs.mkShell {
          packages = with pkgs; [
            bun
            playwright-driver.browsers
          ];
 
          shellHook = ''
            export PLAYWRIGHT_LAUNCH_OPTIONS_EXECUTABLE_PATH="${pkgs.playwright-driver.browsers}/chromium-${chromium-rev}/chrome-linux64/chrome";
          '';
        };
      }
    );
}
</syntaxHighlight>
 
==== playwright.config.ts ====
 
<syntaxHighlight lang=typescript>
import { defineConfig, devices } from '@playwright/test';
 
export default defineConfig({
  projects: [
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        launchOptions: {
          executablePath: process.env.PLAYWRIGHT_LAUNCH_OPTIONS_EXECUTABLE_PATH,
        },
      },
    },
  ],
});
</syntaxHighlight>
 
==== package.json ====


<syntaxHighlight lang=json>
{
{
   env.PW_TEST_CONNECT_WS_ENDPOINT = "ws://127.0.0.1:3000/";
   "$schema": "https://json.schemastore.org/package.json",
  "name": "playwright_with_bun",
  "version": "1.0.0",
  "description": "Playwright tests with Bun",
  "scripts": {
    "test": "bun --bun playwright test"
  },
  "dependencies": {
    "@playwright/test": "^1.58.0",
    "playwright-core": "^1.58.0"
  },
  "devDependencies": {
    "@types/bun": "^1.3.6"
  }
}
</syntaxHighlight>


  # run it with `devenv processes up` and in another terminal run `npm exec playwright test`
==== tests/example.spec.ts ====
  processes.pw-remote.exec = "docker run -p 3000:3000 --rm --init --workdir /home/pwuser --user pwuser mcr.microsoft.com/playwright:v1.52.0-noble /bin/sh -c 'npx -y playwright@1.52.0 run-server --port 3000 --host 0.0.0.0'";
}</syntaxhighlight>Will need a slight tweak like below for the IP address due to a small limitation comes with the container use.<syntaxhighlight lang="typescript">
// example.spec.ts


<syntaxHighlight lang=typescript>
import { test, expect } from '@playwright/test';
import { test, expect } from '@playwright/test';


test('has title', async ({ page }) => {
test('basic test', async ({ page }) => {
   // await page.goto('http://localhost:3333/'); // can't do due to the browsers running within a container but your server is running on the host
   await page.goto('https://example.com');
   await page.goto('http://host.docker.internal:3333/'); // so do this instead
   await expect(page).toHaveTitle(/Example/);
  // also make sure your server code runs for playwright, too - https://playwright.dev/docs/test-webserver#configuring-a-web-server
});


   await expect(page).toHaveTitle(/your website/);
test('Bun.stripANSI available', async () => {
   expect(typeof Bun.stripANSI).toBe('function');
});
});
</syntaxHighlight>
==== test output ====
<syntaxHighlight lang="shell-session" style="color: inherit; background-color: #000;">
[michi@nixos:~/dev/playwright_with_bun]$ bun --bun playwright test --headed
Running 2 tests using 1 worker
  ✓  1 [chromium] › tests/example.spec.ts:3:0 › basic test (1.1s)
  ✓  2 [chromium] › tests/example.spec.ts:8:0 › Bun.stripANSI available (3ms)
  2 passed (2.9s)
[michi@nixos:~/dev/playwright_with_bun]$ bun playwright test --headed
ReferenceError: Bun is not defined
    at Object.<anonymous> (/home/michi/dev/playwright_with_bun/playwright.config.ts:10:27)
    at Module._compile (node:internal/modules/cjs/loader:1706:14)
    at Module.newCompile2 (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/third_party/pirates.js:46:29)
    at Object.<anonymous> (node:internal/modules/cjs/loader:1839:10)
    at Object.newLoader2 [as .ts] (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/third_party/pirates.js:52:22)
    at Module.load (node:internal/modules/cjs/loader:1441:32)
    at Function._load (node:internal/modules/cjs/loader:1263:12)
    at TracingChannel.traceSync (node:diagnostics_channel:328:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:237:24)
    at Module.require (node:internal/modules/cjs/loader:1463:12)
    at require (node:internal/modules/helpers:147:16)
    at requireOrImport (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/transform/transform.js:225:18)
    at loadUserConfig (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/common/configLoader.js:107:89)
    at loadConfig (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/common/configLoader.js:119:28)
    at loadConfigFromFile (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/common/configLoader.js:331:10)
    at runTests (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/program.js:197:18)
    at i.<anonymous> (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/program.js:70:7)
error: "playwright" exited with code 1
</syntaxHighlight>


</syntaxhighlight>
=== Hints ===
[[Category:Development]]
* VSCode plugin playwright currently does not support bun. Stick to nodejs for now if you are a GUI guy.
* playwright version 1.58 seems to be the first version where the command <code>bun --bun playwright test</code> works
* see also https://github.com/MBanucu/bun-playwright-nix

Revision as of 21:19, 26 February 2026

Playwright Test is an end-to-end test framework for modern web apps. It bundles test runner, assertions, isolation, parallelization and rich tooling.

Installing browsers for Playwright under NixOS

At first run, Playwright will prompt the user to run playwright install to install browsers. However, the browsers will not function due to missing dependencies.

The nixpkgs package playwright-driver.browsers provides a packaged form of the browsers that can be assigned to the PLAYWRIGHT_BROWSERS_PATH environment variable.

An example shell.nix that installs Visual Studio Code and Playwright.

{ pkgs ? import <nixpkgs> {} }:
  pkgs.mkShell {
    nativeBuildInputs = with pkgs; [
      vscode
      playwright-driver.browsers
    ];

    shellHook = ''
      export PLAYWRIGHT_BROWSERS_PATH=${pkgs.playwright-driver.browsers}
      export PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS=true
      export PLAYWRIGHT_HOST_PLATFORM_OVERRIDE="ubuntu-24.04"
    '';
}
Note: The version of Playwright in Nixpkgs and your developer environment must match.

Using Devenv

With Devenv you can also set certain environment variables and pin packages to make playwright work. Again, make sure to pin to the same version of playwright in devenv.yaml and package.json!

devenv.nix

{ pkgs, lib, config, inputs, ... }:

let
  pkgs-playwright = import inputs.nixpkgs-playwright { system = pkgs.stdenv.system; };
  browsers = (builtins.fromJSON (builtins.readFile "${pkgs-playwright.playwright-driver}/browsers.json")).browsers;
  chromium-rev = (builtins.head (builtins.filter (x: x.name == "chromium") browsers)).revision;
in
{
  # https://devenv.sh/basics/
  env = {
    PLAYWRIGHT_BROWSERS_PATH = "${pkgs-playwright.playwright.browsers}";
    PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS = true;
    PLAYWRIGHT_NODEJS_PATH = "${pkgs.nodejs}/bin/node";
    PLAYWRIGHT_LAUNCH_OPTIONS_EXECUTABLE_PATH = "${pkgs-playwright.playwright.browsers}/chromium-${chromium-rev}/chrome-linux/chrome";
  };

  # https://devenv.sh/packages/
  packages = with pkgs; [
    just
    nodejs
  ];

  # https://devenv.sh/languages/
  languages.javascript.enable = true;

  dotenv.disableHint = true;
  cachix.enable = false;

  # https://devenv.sh/scripts/
  scripts.intro.exec = ''
    playwrightNpmVersion="$(npm show @playwright/test version)"
    echo "❄️ Playwright nix version: ${pkgs-playwright.playwright.version}"
    echo "📦 Playwright npm version: $playwrightNpmVersion"

    if [ "${pkgs-playwright.playwright.version}" != "$playwrightNpmVersion" ]; then
        echo "❌ Playwright versions in nix (in devenv.yaml) and npm (in package.json) are not the same! Please adapt the configuration."
    else
        echo "✅ Playwright versions in nix and npm are the same"
    fi

    echo
    env | grep ^PLAYWRIGHT
  '';

  enterShell = ''
    intro
  '';

  # See full reference at https://devenv.sh/reference/options/
}

devenv.yaml

# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
inputs:
  nixpkgs:
    url: github:NixOS/nixpkgs/nixos-unstable
  # Currently pinned to: playwright@1.52.0
  # See https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=playwright
  nixpkgs-playwright:
      url: github:NixOS/nixpkgs/979daf34c8cacebcd917d540070b52a3c2b9b16e

package.json

{
  "name": "e2e-tests-ng",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "@playwright/test": "1.52.0",
    "eslint": "^8.41.0"
  },
  "engines": {
    "node": ">=18.0.0"
  },
  "scripts": {
    "playwright:test": "playwright test",
    "playwright:test:ui": "playwright test --ui",
    "playwright:install": "playwright install",
    "lint": "eslint ."
  },
  "dependencies": {
    "dotenv": "^16.0.3",
    "jsrsasign": "^11.0.0"
  }
}

Bun + Playwright

Here is a flake.nix that is working with bun instead of nodejs (working today 2026-01-23). Put flake.nix, playwright.config.ts, package.json and tests/example.spec.ts in the root directory of your project and run

  • nix develop to install packages and enter the devShell
  • bun install to download playwright dependencies to node_modules
  • bun --bun playwright test --headed to test the setup
  • bun playwright test --headed to verify that without --bun flag the nodejs version is run and bun library is not available.

flake.nix

{
  description = "Bun + Playwright dev shell";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs =
    {
      self,
      nixpkgs,
      flake-utils,
      ...
    }:
    flake-utils.lib.eachDefaultSystem (
      system:
      let
        pkgs = import nixpkgs { inherit system; };
        browsers =
          (builtins.fromJSON (builtins.readFile "${pkgs.playwright-driver}/browsers.json")).browsers;
        chromium-rev = (builtins.head (builtins.filter (x: x.name == "chromium") browsers)).revision;
      in
      {
        devShells.default = pkgs.mkShell {
          packages = with pkgs; [
            bun
            playwright-driver.browsers
          ];

          shellHook = ''
            export PLAYWRIGHT_LAUNCH_OPTIONS_EXECUTABLE_PATH="${pkgs.playwright-driver.browsers}/chromium-${chromium-rev}/chrome-linux64/chrome";
          '';
        };
      }
    );
}

playwright.config.ts

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  projects: [
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        launchOptions: {
          executablePath: process.env.PLAYWRIGHT_LAUNCH_OPTIONS_EXECUTABLE_PATH,
        },
      },
    },
  ],
});

package.json

{
  "$schema": "https://json.schemastore.org/package.json",
  "name": "playwright_with_bun",
  "version": "1.0.0",
  "description": "Playwright tests with Bun",
  "scripts": {
    "test": "bun --bun playwright test"
  },
  "dependencies": {
    "@playwright/test": "^1.58.0",
    "playwright-core": "^1.58.0"
  },
  "devDependencies": {
    "@types/bun": "^1.3.6"
  }
}

tests/example.spec.ts

import { test, expect } from '@playwright/test';

test('basic test', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveTitle(/Example/);
});

test('Bun.stripANSI available', async () => {
  expect(typeof Bun.stripANSI).toBe('function');
});

test output

[michi@nixos:~/dev/playwright_with_bun]$ bun --bun playwright test --headed

Running 2 tests using 1 worker

  ✓  1 [chromium] › tests/example.spec.ts:3:0 › basic test (1.1s)
  ✓  2 [chromium] › tests/example.spec.ts:8:0 › Bun.stripANSI available (3ms)

  2 passed (2.9s)

[michi@nixos:~/dev/playwright_with_bun]$ bun playwright test --headed
ReferenceError: Bun is not defined
    at Object.<anonymous> (/home/michi/dev/playwright_with_bun/playwright.config.ts:10:27)
    at Module._compile (node:internal/modules/cjs/loader:1706:14)
    at Module.newCompile2 (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/third_party/pirates.js:46:29)
    at Object.<anonymous> (node:internal/modules/cjs/loader:1839:10)
    at Object.newLoader2 [as .ts] (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/third_party/pirates.js:52:22)
    at Module.load (node:internal/modules/cjs/loader:1441:32)
    at Function._load (node:internal/modules/cjs/loader:1263:12)
    at TracingChannel.traceSync (node:diagnostics_channel:328:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:237:24)
    at Module.require (node:internal/modules/cjs/loader:1463:12)
    at require (node:internal/modules/helpers:147:16)
    at requireOrImport (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/transform/transform.js:225:18)
    at loadUserConfig (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/common/configLoader.js:107:89)
    at loadConfig (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/common/configLoader.js:119:28)
    at loadConfigFromFile (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/common/configLoader.js:331:10)
    at runTests (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/program.js:197:18)
    at i.<anonymous> (/home/michi/dev/playwright_with_bun/node_modules/playwright/lib/program.js:70:7)
error: "playwright" exited with code 1

Hints

  • VSCode plugin playwright currently does not support bun. Stick to nodejs for now if you are a GUI guy.
  • playwright version 1.58 seems to be the first version where the command bun --bun playwright test works
  • see also https://github.com/MBanucu/bun-playwright-nix