@@ -7,7 +7,23 @@ import { chromium } from "playwright";
77import { resolvePnpmRunner } from "./pnpm-runner.mjs" ;
88
99const repoRoot = resolve ( dirname ( fileURLToPath ( import . meta. url ) ) , ".." ) ;
10- const playwrightInstallArgs = [ "--dir" , "ui" , "exec" , "playwright" , "install" , "chromium" ] ;
10+ const playwrightInstallArgs = [
11+ "--dir" ,
12+ "ui" ,
13+ "exec" ,
14+ "playwright" ,
15+ "install" ,
16+ "chromium" ,
17+ ] ;
18+ const playwrightInstallWithDepsArgs = [
19+ "--dir" ,
20+ "ui" ,
21+ "exec" ,
22+ "playwright" ,
23+ "install" ,
24+ "--with-deps" ,
25+ "chromium" ,
26+ ] ;
1127const executableOverrideEnvKey = "PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH" ;
1228export const systemChromiumExecutableCandidates = [
1329 "/snap/bin/chromium" ,
@@ -17,8 +33,22 @@ export const systemChromiumExecutableCandidates = [
1733 "/usr/bin/google-chrome-stable" ,
1834] ;
1935
20- export function resolveSystemChromiumExecutablePath ( existsSync = existsSyncImpl ) {
21- return systemChromiumExecutableCandidates . find ( ( candidate ) => existsSync ( candidate ) ) ?? "" ;
36+ export function canRunChromiumExecutable ( executablePath , spawnSync = spawnSyncImpl ) {
37+ const result = spawnSync ( executablePath , [ "--version" ] , {
38+ stdio : "ignore" ,
39+ } ) ;
40+ return result . status === 0 ;
41+ }
42+
43+ export function resolveSystemChromiumExecutablePath (
44+ existsSync = existsSyncImpl ,
45+ spawnSync = spawnSyncImpl ,
46+ ) {
47+ return (
48+ systemChromiumExecutableCandidates . find (
49+ ( candidate ) => existsSync ( candidate ) && canRunChromiumExecutable ( candidate , spawnSync ) ,
50+ ) ?? ""
51+ ) ;
2252}
2353
2454export function resolvePlaywrightInstallRunner ( options = { } ) {
@@ -27,10 +57,23 @@ export function resolvePlaywrightInstallRunner(options = {}) {
2757 comSpec : options . comSpec ?? env . ComSpec ?? env . COMSPEC ,
2858 npmExecPath : env === process . env ? env . npm_execpath : ( env . npm_execpath ?? "" ) ,
2959 platform : options . platform ,
30- pnpmArgs : playwrightInstallArgs ,
60+ pnpmArgs : options . withDeps ? playwrightInstallWithDepsArgs : playwrightInstallArgs ,
3161 } ) ;
3262}
3363
64+ export function shouldInstallPlaywrightSystemDependencies ( options = { } ) {
65+ const env = options . env ?? process . env ;
66+ const platform = options . platform ?? process . platform ;
67+ const getuid = options . getuid ?? process . getuid ;
68+ if ( platform !== "linux" ) {
69+ return false ;
70+ }
71+ if ( typeof getuid === "function" && getuid ( ) === 0 ) {
72+ return true ;
73+ }
74+ return env . CI === "true" || env . GITHUB_ACTIONS === "true" ;
75+ }
76+
3477export function isDirectScriptExecution (
3578 argvEntry = process . argv [ 1 ] ,
3679 modulePath = fileURLToPath ( import . meta. url ) ,
@@ -56,22 +99,25 @@ export function ensurePlaywrightChromium(options = {}) {
5699 const spawnSync = options . spawnSync ?? spawnSyncImpl ;
57100
58101 if ( executableOverride ) {
59- if ( existsSync ( executableOverride ) ) {
102+ if ( existsSync ( executableOverride ) && canRunChromiumExecutable ( executableOverride , spawnSync ) ) {
60103 return 0 ;
61104 }
62105 log (
63- `[ui-e2e] ${ executableOverrideEnvKey } points to ${ executableOverride } , but that browser does not exist .` ,
106+ `[ui-e2e] ${ executableOverrideEnvKey } points to ${ executableOverride } , but that browser is not runnable .` ,
64107 ) ;
65108 return 1 ;
66109 }
67110
68- if ( existsSync ( executablePath ) ) {
111+ if ( existsSync ( executablePath ) && canRunChromiumExecutable ( executablePath , spawnSync ) ) {
69112 return 0 ;
70113 }
71114
72115 const systemExecutablePath =
73- options . systemExecutablePath ?? resolveSystemChromiumExecutablePath ( existsSync ) ;
74- if ( systemExecutablePath ) {
116+ options . systemExecutablePath ?? resolveSystemChromiumExecutablePath ( existsSync , spawnSync ) ;
117+ if (
118+ systemExecutablePath &&
119+ canRunChromiumExecutable ( systemExecutablePath , spawnSync )
120+ ) {
75121 log ( `[ui-e2e] Using system Chromium at ${ systemExecutablePath } .` ) ;
76122 return 0 ;
77123 }
@@ -83,7 +129,7 @@ export function ensurePlaywrightChromium(options = {}) {
83129 return 0 ;
84130 }
85131
86- log ( `[ui-e2e] Playwright Chromium is missing at ${ executablePath } ; installing chromium.` ) ;
132+ log ( `[ui-e2e] Playwright Chromium is not runnable at ${ executablePath } ; installing chromium.` ) ;
87133 const runner = resolvePlaywrightInstallRunner ( {
88134 comSpec : options . comSpec ,
89135 env,
@@ -101,9 +147,38 @@ export function ensurePlaywrightChromium(options = {}) {
101147 return status ;
102148 }
103149
104- if ( ! existsSync ( executablePath ) ) {
150+ if ( ! existsSync ( executablePath ) || ! canRunChromiumExecutable ( executablePath , spawnSync ) ) {
151+ if ( shouldInstallPlaywrightSystemDependencies ( {
152+ env,
153+ getuid : options . getuid ,
154+ platform : options . platform ,
155+ } ) ) {
156+ log (
157+ `[ui-e2e] Chromium is installed but still cannot start; installing Linux system dependencies.` ,
158+ ) ;
159+ const depsRunner = resolvePlaywrightInstallRunner ( {
160+ comSpec : options . comSpec ,
161+ env,
162+ platform : options . platform ,
163+ withDeps : true ,
164+ } ) ;
165+ const depsResult = spawnSync ( depsRunner . command , depsRunner . args , {
166+ cwd : options . cwd ?? repoRoot ,
167+ env,
168+ shell : depsRunner . shell ,
169+ stdio : options . stdio ?? "inherit" ,
170+ windowsVerbatimArguments : depsRunner . windowsVerbatimArguments ,
171+ } ) ;
172+ const depsStatus = depsResult . status ?? 1 ;
173+ if ( depsStatus !== 0 ) {
174+ return depsStatus ;
175+ }
176+ if ( existsSync ( executablePath ) && canRunChromiumExecutable ( executablePath , spawnSync ) ) {
177+ return 0 ;
178+ }
179+ }
105180 log (
106- `[ui-e2e] Playwright install completed but Chromium is still missing at ${ executablePath } .` ,
181+ `[ui-e2e] Playwright install completed but Chromium is still not runnable at ${ executablePath } .` ,
107182 ) ;
108183 return 1 ;
109184 }
0 commit comments