1{
2 lib,
3 buildNpmPackage,
4 stdenv,
5 chromium,
6 ffmpeg,
7 jq,
8 nodejs,
9 fetchFromGitHub,
10 linkFarm,
11 callPackage,
12 makeFontsConf,
13 makeWrapper,
14 runCommand,
15 cacert,
16}:
17let
18 inherit (stdenv.hostPlatform) system;
19
20 throwSystem = throw "Unsupported system: ${system}";
21 suffix =
22 {
23 x86_64-linux = "linux";
24 aarch64-linux = "linux-arm64";
25 x86_64-darwin = "mac";
26 aarch64-darwin = "mac-arm64";
27 }
28 .${system} or throwSystem;
29
30 version = "1.54.1";
31
32 src = fetchFromGitHub {
33 owner = "Microsoft";
34 repo = "playwright";
35 rev = "v${version}";
36 hash = "sha256-xwyREgelHLkpbUXOZTppKK7L6dE4jx0d/lbDWSKGzTY=";
37 };
38
39 babel-bundle = buildNpmPackage {
40 pname = "babel-bundle";
41 inherit version src;
42 sourceRoot = "${src.name}/packages/playwright/bundles/babel";
43 npmDepsHash = "sha256-sdl+rMCmuOmY1f7oSfGuAAFCiPCFzqkQtFCncL4o5LQ=";
44 dontNpmBuild = true;
45 installPhase = ''
46 cp -r . "$out"
47 '';
48 };
49 expect-bundle = buildNpmPackage {
50 pname = "expect-bundle";
51 inherit version src;
52 sourceRoot = "${src.name}/packages/playwright/bundles/expect";
53 npmDepsHash = "sha256-KwxNqPefvPPHG4vbco2O4G8WlA7l33toJdfNWHMTDOQ=";
54 dontNpmBuild = true;
55 installPhase = ''
56 cp -r . "$out"
57 '';
58 };
59 utils-bundle = buildNpmPackage {
60 pname = "utils-bundle";
61 inherit version src;
62 sourceRoot = "${src.name}/packages/playwright/bundles/utils";
63 npmDepsHash = "sha256-InwWYRk6eRF62qI6qpVaPceIetSr3kPIBK4LdfeoJdo=";
64 dontNpmBuild = true;
65 installPhase = ''
66 cp -r . "$out"
67 '';
68 };
69 utils-bundle-core = buildNpmPackage {
70 pname = "utils-bundle-core";
71 inherit version src;
72 sourceRoot = "${src.name}/packages/playwright-core/bundles/utils";
73 npmDepsHash = "sha256-gEm2oTxj4QIiGnIOPffOLh3BYSngpGToF89ObnDYBqs=";
74 dontNpmBuild = true;
75 installPhase = ''
76 cp -r . "$out"
77 '';
78 };
79 zip-bundle = buildNpmPackage {
80 pname = "zip-bundle";
81 inherit version src;
82 sourceRoot = "${src.name}/packages/playwright-core/bundles/zip";
83 npmDepsHash = "sha256-c0UZ0Jg86icwJp3xarpXpxWjRYeIjz9wpWtJZDHkd8U=";
84 dontNpmBuild = true;
85 installPhase = ''
86 cp -r . "$out"
87 '';
88 };
89
90 playwright = buildNpmPackage {
91 pname = "playwright";
92 inherit version src;
93
94 sourceRoot = "${src.name}"; # update.sh depends on sourceRoot presence
95 npmDepsHash = "sha256-4bsX8Q8V3CBpIsyqMYTzfERQQPY5zlPf7CoqR6UkUHU=";
96
97 nativeBuildInputs = [
98 cacert
99 jq
100 ];
101
102 ELECTRON_SKIP_BINARY_DOWNLOAD = true;
103
104 postPatch = ''
105 sed -i '/\/\/ Update test runner./,/^\s*$/{d}' utils/build/build.js
106 sed -i '/^\/\/ Update bundles\./,/^[[:space:]]*}$/d' utils/build/build.js
107 sed -i '/execSync/d' ./utils/generate_third_party_notice.js
108 chmod +w packages/playwright/bundles/babel
109 ln -s ${babel-bundle}/node_modules packages/playwright/bundles/babel/node_modules
110 chmod +w packages/playwright/bundles/expect
111 ln -s ${expect-bundle}/node_modules packages/playwright/bundles/expect/node_modules
112 chmod +w packages/playwright/bundles/utils
113 ln -s ${utils-bundle}/node_modules packages/playwright/bundles/utils/node_modules
114 chmod +w packages/playwright-core/bundles/utils
115 ln -s ${utils-bundle-core}/node_modules packages/playwright-core/bundles/utils/node_modules
116 chmod +w packages/playwright-core/bundles/zip
117 ln -s ${zip-bundle}/node_modules packages/playwright-core/bundles/zip/node_modules
118 '';
119
120 installPhase = ''
121 runHook preInstall
122
123 shopt -s extglob
124
125 mkdir -p "$out/lib"
126 cp -r packages/playwright/node_modules "$out/lib/node_modules"
127
128 mkdir -p "$out/lib/node_modules/playwright"
129 cp -r packages/playwright/!(bundles|src|node_modules|.*) "$out/lib/node_modules/playwright"
130
131 # for not supported platforms (such as NixOS) playwright assumes that it runs on ubuntu-20.04
132 # that forces it to use overridden webkit revision
133 # let's remove that override to make it use latest revision provided in Nixpkgs
134 # https://github.com/microsoft/playwright/blob/baeb065e9ea84502f347129a0b896a85d2a8dada/packages/playwright-core/src/server/utils/hostPlatform.ts#L111
135 jq '(.browsers[] | select(.name == "webkit") | .revisionOverrides) |= del(."ubuntu20.04-x64", ."ubuntu20.04-arm64")' \
136 packages/playwright-core/browsers.json > browser.json.tmp && mv browser.json.tmp packages/playwright-core/browsers.json
137 mkdir -p "$out/lib/node_modules/playwright-core"
138 cp -r packages/playwright-core/!(bundles|src|bin|.*) "$out/lib/node_modules/playwright-core"
139
140 mkdir -p "$out/lib/node_modules/@playwright/test"
141 cp -r packages/playwright-test/* "$out/lib/node_modules/@playwright/test"
142
143 runHook postInstall
144 '';
145
146 meta = {
147 description = "Framework for Web Testing and Automation";
148 homepage = "https://playwright.dev";
149 license = lib.licenses.asl20;
150 maintainers = with lib.maintainers; [
151 kalekseev
152 marie
153 ];
154 inherit (nodejs.meta) platforms;
155 };
156 };
157
158 playwright-core = stdenv.mkDerivation (finalAttrs: {
159 pname = "playwright-core";
160 inherit (playwright) version src meta;
161
162 installPhase = ''
163 runHook preInstall
164
165 cp -r ${playwright}/lib/node_modules/playwright-core "$out"
166
167 runHook postInstall
168 '';
169
170 passthru = {
171 browsersJSON = (lib.importJSON ./browsers.json).browsers;
172 browsers = browsers { };
173 browsers-chromium = browsers {
174 withFirefox = false;
175 withWebkit = false;
176 withChromiumHeadlessShell = false;
177 };
178 inherit components;
179 };
180 });
181
182 playwright-test = stdenv.mkDerivation (finalAttrs: {
183 pname = "playwright-test";
184 inherit (playwright) version src;
185
186 nativeBuildInputs = [ makeWrapper ];
187 installPhase = ''
188 runHook preInstall
189
190 shopt -s extglob
191 mkdir -p $out/bin
192 cp -r ${playwright}/* $out
193
194 makeWrapper "${nodejs}/bin/node" "$out/bin/playwright" \
195 --add-flags "$out/lib/node_modules/@playwright/test/cli.js" \
196 --prefix NODE_PATH : ${placeholder "out"}/lib/node_modules \
197 --set-default PLAYWRIGHT_BROWSERS_PATH "${playwright-core.passthru.browsers}"
198
199 runHook postInstall
200 '';
201
202 meta = playwright.meta // {
203 mainProgram = "playwright";
204 };
205 });
206
207 components = {
208 chromium = callPackage ./chromium.nix {
209 inherit suffix system throwSystem;
210 inherit (playwright-core.passthru.browsersJSON.chromium) revision;
211 fontconfig_file = makeFontsConf {
212 fontDirectories = [ ];
213 };
214 };
215 chromium-headless-shell = callPackage ./chromium-headless-shell.nix {
216 inherit suffix system throwSystem;
217 inherit (playwright-core.passthru.browsersJSON.chromium) revision;
218 };
219 firefox = callPackage ./firefox.nix {
220 inherit suffix system throwSystem;
221 inherit (playwright-core.passthru.browsersJSON.firefox) revision;
222 };
223 webkit = callPackage ./webkit.nix {
224 inherit suffix system throwSystem;
225 inherit (playwright-core.passthru.browsersJSON.webkit) revision;
226 };
227 ffmpeg = callPackage ./ffmpeg.nix {
228 inherit suffix system throwSystem;
229 inherit (playwright-core.passthru.browsersJSON.ffmpeg) revision;
230 };
231 };
232
233 browsers = lib.makeOverridable (
234 {
235 withChromium ? true,
236 withFirefox ? true,
237 withWebkit ? true, # may require `export PLAYWRIGHT_HOST_PLATFORM_OVERRIDE="ubuntu-24.04"`
238 withFfmpeg ? true,
239 withChromiumHeadlessShell ? true,
240 fontconfig_file ? makeFontsConf {
241 fontDirectories = [ ];
242 },
243 }:
244 let
245 browsers =
246 lib.optionals withChromium [ "chromium" ]
247 ++ lib.optionals withChromiumHeadlessShell [ "chromium-headless-shell" ]
248 ++ lib.optionals withFirefox [ "firefox" ]
249 ++ lib.optionals withWebkit [ "webkit" ]
250 ++ lib.optionals withFfmpeg [ "ffmpeg" ];
251 in
252 linkFarm "playwright-browsers" (
253 lib.listToAttrs (
254 map (
255 name:
256 let
257 revName = if name == "chromium-headless-shell" then "chromium" else name;
258 value = playwright-core.passthru.browsersJSON.${revName};
259 in
260 lib.nameValuePair
261 # TODO check platform for revisionOverrides
262 "${lib.replaceStrings [ "-" ] [ "_" ] name}-${value.revision}"
263 components.${name}
264 ) browsers
265 )
266 )
267 );
268in
269{
270 playwright-core = playwright-core;
271 playwright-test = playwright-test;
272}