@wdio/xvfb 9.19.0 → 9.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/build/XvfbManager.d.ts +5 -0
- package/build/XvfbManager.d.ts.map +1 -1
- package/build/index.js +22 -5
- package/package.json +3 -3
- package/src/XvfbManager.ts +28 -6
- package/tests/XvfbManager.test.ts +153 -125
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ For testing on non-Linux systems:
|
|
|
65
65
|
```js
|
|
66
66
|
import { XvfbManager } from "@wdio/xvfb";
|
|
67
67
|
|
|
68
|
-
const manager = new XvfbManager({
|
|
68
|
+
const manager = new XvfbManager({
|
|
69
69
|
force: true,
|
|
70
70
|
xvfbMaxRetries: 3,
|
|
71
71
|
xvfbRetryDelay: 1000
|
|
@@ -238,9 +238,10 @@ You can customize xvfb behavior and retry settings:
|
|
|
238
238
|
export const config = {
|
|
239
239
|
// Xvfb configuration options (all optional)
|
|
240
240
|
autoXvfb: true, // Enable automatic xvfb (default: true)
|
|
241
|
+
xvfbAutoInstall: false, // Enable auto-install of xvfb if missing (default: false)
|
|
241
242
|
xvfbMaxRetries: 5, // Max retry attempts for xvfb failures (default: 3)
|
|
242
243
|
xvfbRetryDelay: 2000, // Base delay between retries in ms (default: 1000)
|
|
243
|
-
|
|
244
|
+
|
|
244
245
|
capabilities: [{
|
|
245
246
|
browserName: 'chrome',
|
|
246
247
|
'goog:chromeOptions': {
|
|
@@ -253,6 +254,7 @@ export const config = {
|
|
|
253
254
|
**Configuration Options:**
|
|
254
255
|
|
|
255
256
|
- **`autoXvfb`** *(boolean, default: true)*: Enable/disable automatic xvfb initialization
|
|
257
|
+
- **`xvfbAutoInstall`** *(boolean, default: false)*: Install `xvfb` packages automatically if `xvfb-run` is missing
|
|
256
258
|
- **`xvfbMaxRetries`** *(number, default: 3)*: Number of retry attempts when xvfb process fails
|
|
257
259
|
- **`xvfbRetryDelay`** *(number, default: 1000)*: Base delay between retries in milliseconds. Uses progressive delay (delay × attempt number)
|
|
258
260
|
|
package/build/XvfbManager.d.ts
CHANGED
|
@@ -12,6 +12,10 @@ export interface XvfbOptions {
|
|
|
12
12
|
* Skip xvfb-run availability check and force installation (for testing)
|
|
13
13
|
*/
|
|
14
14
|
forceInstall?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Enable automatic installation of Xvfb packages if `xvfb-run` is missing (default: false)
|
|
17
|
+
*/
|
|
18
|
+
autoInstall?: boolean;
|
|
15
19
|
/**
|
|
16
20
|
* Number of retry attempts for xvfb process failures (default: 3)
|
|
17
21
|
*/
|
|
@@ -26,6 +30,7 @@ export declare class XvfbManager {
|
|
|
26
30
|
private force;
|
|
27
31
|
private packageManagerOverride?;
|
|
28
32
|
private forceInstall;
|
|
33
|
+
private autoInstall;
|
|
29
34
|
private maxRetries;
|
|
30
35
|
private retryDelay;
|
|
31
36
|
private log;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"XvfbManager.d.ts","sourceRoot":"","sources":["../src/XvfbManager.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,MAAM,WAAW,WAAW;IACxB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAID,qBAAa,WAAW;IACpB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,sBAAsB,CAAC,CAAQ;IACvC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,GAAG,CAA2B;gBAE1B,OAAO,GAAE,WAAgB;
|
|
1
|
+
{"version":3,"file":"XvfbManager.d.ts","sourceRoot":"","sources":["../src/XvfbManager.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,MAAM,WAAW,WAAW;IACxB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAID,qBAAa,WAAW;IACpB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,sBAAsB,CAAC,CAAQ;IACvC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,GAAG,CAA2B;gBAE1B,OAAO,GAAE,WAAgB;IAUrC;;OAEG;IACH,SAAS,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,8BAA8B,GAAG,OAAO;IAoB9E;;;OAGG;IACU,IAAI,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,8BAA8B,GAAG,OAAO,CAAC,OAAO,CAAC;IAwB/F;;OAEG;YACW,sBAAsB;IAiDpC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA0B1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAI/B;;OAEG;IACH,OAAO,CAAC,oCAAoC;IAO5C;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA0BlC;;OAEG;IACH,OAAO,CAAC,eAAe;cAeP,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;YA4BzC,mBAAmB;IAoCjC;;OAEG;IACU,gBAAgB,CAAC,CAAC,EAC3B,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,OAAO,GAAE,MAAyB,GACnC,OAAO,CAAC,CAAC,CAAC;IA2Cb;;OAEG;IACH,OAAO,CAAC,WAAW;CAYtB"}
|
package/build/index.js
CHANGED
|
@@ -9,6 +9,7 @@ var XvfbManager = class {
|
|
|
9
9
|
force;
|
|
10
10
|
packageManagerOverride;
|
|
11
11
|
forceInstall;
|
|
12
|
+
autoInstall;
|
|
12
13
|
maxRetries;
|
|
13
14
|
retryDelay;
|
|
14
15
|
log;
|
|
@@ -16,6 +17,7 @@ var XvfbManager = class {
|
|
|
16
17
|
this.force = options.force ?? false;
|
|
17
18
|
this.packageManagerOverride = options.packageManager;
|
|
18
19
|
this.forceInstall = options.forceInstall ?? false;
|
|
20
|
+
this.autoInstall = options.autoInstall ?? false;
|
|
19
21
|
this.maxRetries = options.xvfbMaxRetries ?? 3;
|
|
20
22
|
this.retryDelay = options.xvfbRetryDelay ?? 1e3;
|
|
21
23
|
this.log = logger("@wdio/xvfb");
|
|
@@ -47,9 +49,13 @@ var XvfbManager = class {
|
|
|
47
49
|
}
|
|
48
50
|
this.log.info("Xvfb should run, checking if setup is needed");
|
|
49
51
|
try {
|
|
50
|
-
await this.ensureXvfbRunAvailable();
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
const isReady = await this.ensureXvfbRunAvailable();
|
|
53
|
+
if (isReady) {
|
|
54
|
+
this.log.info("xvfb-run is ready for use");
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
this.log.warn("xvfb-run not available; continuing without virtual display");
|
|
58
|
+
return false;
|
|
53
59
|
} catch (error) {
|
|
54
60
|
this.log.error("Failed to setup xvfb-run:", error);
|
|
55
61
|
throw error;
|
|
@@ -64,9 +70,18 @@ var XvfbManager = class {
|
|
|
64
70
|
try {
|
|
65
71
|
await execAsync("which xvfb-run");
|
|
66
72
|
this.log.info("xvfb-run found in PATH");
|
|
67
|
-
return;
|
|
73
|
+
return true;
|
|
68
74
|
} catch {
|
|
69
|
-
this.
|
|
75
|
+
if (!this.autoInstall) {
|
|
76
|
+
this.log.warn(
|
|
77
|
+
"xvfb-run not found. Skipping automatic installation. To enable auto-install, set 'xvfbAutoInstall: true' in your WDIO config."
|
|
78
|
+
);
|
|
79
|
+
this.log.warn(
|
|
80
|
+
"Hint: you can also install it manually via your distro's package manager (e.g., 'sudo apt-get install xvfb', 'sudo dnf install xorg-x11-server-Xvfb')."
|
|
81
|
+
);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
this.log.info("xvfb-run not found, installing xvfb packages (xvfbAutoInstall enabled)...");
|
|
70
85
|
}
|
|
71
86
|
} else {
|
|
72
87
|
this.log.info("Force install enabled, skipping availability check");
|
|
@@ -81,6 +96,7 @@ var XvfbManager = class {
|
|
|
81
96
|
this.log.info(
|
|
82
97
|
`Successfully installed xvfb-run at: ${stdout.trim()}`
|
|
83
98
|
);
|
|
99
|
+
return true;
|
|
84
100
|
} catch (error) {
|
|
85
101
|
this.log.error("Failed to install xvfb-run:", error);
|
|
86
102
|
throw new Error(
|
|
@@ -88,6 +104,7 @@ var XvfbManager = class {
|
|
|
88
104
|
);
|
|
89
105
|
}
|
|
90
106
|
}
|
|
107
|
+
return true;
|
|
91
108
|
}
|
|
92
109
|
/**
|
|
93
110
|
* Detect if headless mode is enabled in Chrome/Chromium capabilities
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wdio/xvfb",
|
|
3
|
-
"version": "9.19.
|
|
3
|
+
"version": "9.19.1",
|
|
4
4
|
"description": "A standalone utility to manage Xvfb (X Virtual Framebuffer) for headless testing",
|
|
5
5
|
"author": "WebdriverIO Team",
|
|
6
6
|
"homepage": "https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-xvfb",
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/is-ci": "^3.0.4",
|
|
46
46
|
"@types/node": "^20.0.0",
|
|
47
|
-
"@wdio/types": "9.19.
|
|
47
|
+
"@wdio/types": "9.19.1"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "df3ec33741d11d196adad148f4d066a3fcbcd51b"
|
|
53
53
|
}
|
package/src/XvfbManager.ts
CHANGED
|
@@ -18,6 +18,10 @@ export interface XvfbOptions {
|
|
|
18
18
|
* Skip xvfb-run availability check and force installation (for testing)
|
|
19
19
|
*/
|
|
20
20
|
forceInstall?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Enable automatic installation of Xvfb packages if `xvfb-run` is missing (default: false)
|
|
23
|
+
*/
|
|
24
|
+
autoInstall?: boolean;
|
|
21
25
|
/**
|
|
22
26
|
* Number of retry attempts for xvfb process failures (default: 3)
|
|
23
27
|
*/
|
|
@@ -35,6 +39,7 @@ export class XvfbManager {
|
|
|
35
39
|
private force: boolean
|
|
36
40
|
private packageManagerOverride?: string
|
|
37
41
|
private forceInstall: boolean
|
|
42
|
+
private autoInstall: boolean
|
|
38
43
|
private maxRetries: number
|
|
39
44
|
private retryDelay: number
|
|
40
45
|
private log: ReturnType<typeof logger>
|
|
@@ -43,6 +48,7 @@ export class XvfbManager {
|
|
|
43
48
|
this.force = options.force ?? false
|
|
44
49
|
this.packageManagerOverride = options.packageManager
|
|
45
50
|
this.forceInstall = options.forceInstall ?? false
|
|
51
|
+
this.autoInstall = options.autoInstall ?? false
|
|
46
52
|
this.maxRetries = options.xvfbMaxRetries ?? 3
|
|
47
53
|
this.retryDelay = options.xvfbRetryDelay ?? 1000
|
|
48
54
|
this.log = logger('@wdio/xvfb')
|
|
@@ -85,9 +91,14 @@ export class XvfbManager {
|
|
|
85
91
|
this.log.info('Xvfb should run, checking if setup is needed')
|
|
86
92
|
|
|
87
93
|
try {
|
|
88
|
-
await this.ensureXvfbRunAvailable()
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
const isReady = await this.ensureXvfbRunAvailable()
|
|
95
|
+
|
|
96
|
+
if (isReady) {
|
|
97
|
+
this.log.info('xvfb-run is ready for use')
|
|
98
|
+
return true
|
|
99
|
+
}
|
|
100
|
+
this.log.warn('xvfb-run not available; continuing without virtual display')
|
|
101
|
+
return false
|
|
91
102
|
} catch (error) {
|
|
92
103
|
this.log.error('Failed to setup xvfb-run:', error)
|
|
93
104
|
throw error
|
|
@@ -97,7 +108,7 @@ export class XvfbManager {
|
|
|
97
108
|
/**
|
|
98
109
|
* Ensure xvfb-run is available, installing if necessary
|
|
99
110
|
*/
|
|
100
|
-
private async ensureXvfbRunAvailable(): Promise<
|
|
111
|
+
private async ensureXvfbRunAvailable(): Promise<boolean> {
|
|
101
112
|
this.log.info('Checking if xvfb-run is available...')
|
|
102
113
|
|
|
103
114
|
if (!this.forceInstall) {
|
|
@@ -105,9 +116,18 @@ export class XvfbManager {
|
|
|
105
116
|
// Check if xvfb-run is already available
|
|
106
117
|
await execAsync('which xvfb-run')
|
|
107
118
|
this.log.info('xvfb-run found in PATH')
|
|
108
|
-
return
|
|
119
|
+
return true
|
|
109
120
|
} catch {
|
|
110
|
-
this.
|
|
121
|
+
if (!this.autoInstall) {
|
|
122
|
+
this.log.warn(
|
|
123
|
+
"xvfb-run not found. Skipping automatic installation. To enable auto-install, set 'xvfbAutoInstall: true' in your WDIO config."
|
|
124
|
+
)
|
|
125
|
+
this.log.warn(
|
|
126
|
+
"Hint: you can also install it manually via your distro's package manager (e.g., 'sudo apt-get install xvfb', 'sudo dnf install xorg-x11-server-Xvfb')."
|
|
127
|
+
)
|
|
128
|
+
return false
|
|
129
|
+
}
|
|
130
|
+
this.log.info('xvfb-run not found, installing xvfb packages (xvfbAutoInstall enabled)...')
|
|
111
131
|
}
|
|
112
132
|
} else {
|
|
113
133
|
this.log.info('Force install enabled, skipping availability check')
|
|
@@ -126,6 +146,7 @@ export class XvfbManager {
|
|
|
126
146
|
this.log.info(
|
|
127
147
|
`Successfully installed xvfb-run at: ${stdout.trim()}`
|
|
128
148
|
)
|
|
149
|
+
return true
|
|
129
150
|
} catch (error) {
|
|
130
151
|
this.log.error('Failed to install xvfb-run:', error)
|
|
131
152
|
throw new Error(
|
|
@@ -133,6 +154,7 @@ export class XvfbManager {
|
|
|
133
154
|
)
|
|
134
155
|
}
|
|
135
156
|
}
|
|
157
|
+
return true
|
|
136
158
|
}
|
|
137
159
|
|
|
138
160
|
/**
|
|
@@ -298,133 +298,161 @@ describe('XvfbManager', () => {
|
|
|
298
298
|
expect(result).toBe(true)
|
|
299
299
|
})
|
|
300
300
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
describe('cross-distribution support', () => {
|
|
329
|
-
beforeEach(() => {
|
|
330
|
-
mockPlatform.mockReturnValue('linux')
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
it('should detect Ubuntu distribution', async () => {
|
|
334
|
-
// Mock xvfb-run not found, then package manager detection
|
|
335
|
-
mockExecAsync
|
|
336
|
-
.mockRejectedValueOnce(new Error('Command not found'))
|
|
337
|
-
.mockResolvedValueOnce({ stdout: '/usr/bin/apt-get', stderr: '' })
|
|
338
|
-
.mockResolvedValueOnce({ stdout: 'installation success', stderr: '' })
|
|
339
|
-
.mockResolvedValueOnce({ stdout: '/usr/bin/xvfb-run\n', stderr: '' })
|
|
340
|
-
|
|
341
|
-
const manager = new XvfbManager()
|
|
342
|
-
delete process.env.DISPLAY
|
|
343
|
-
|
|
344
|
-
await manager.init()
|
|
345
|
-
|
|
346
|
-
expect(mockExecAsync).toHaveBeenCalledWith('which apt-get')
|
|
347
|
-
expect(mockExecAsync).toHaveBeenCalledWith(
|
|
348
|
-
'sudo apt-get update -qq && sudo apt-get install -y xvfb',
|
|
349
|
-
{ timeout: 240000 }
|
|
350
|
-
)
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
it('should detect dnf package manager', async () => {
|
|
354
|
-
// Mock xvfb-run not found, then package manager detection
|
|
355
|
-
mockExecAsync
|
|
356
|
-
.mockRejectedValueOnce(new Error('Command not found'))
|
|
357
|
-
.mockRejectedValueOnce(new Error('apt-get not found'))
|
|
358
|
-
.mockResolvedValueOnce({ stdout: '/usr/bin/dnf', stderr: '' })
|
|
359
|
-
.mockResolvedValueOnce({ stdout: 'installation success', stderr: '' })
|
|
360
|
-
.mockResolvedValueOnce({ stdout: '/usr/bin/xvfb-run\n', stderr: '' })
|
|
361
|
-
|
|
362
|
-
const manager = new XvfbManager()
|
|
363
|
-
delete process.env.DISPLAY
|
|
364
|
-
|
|
365
|
-
await manager.init()
|
|
366
|
-
|
|
367
|
-
expect(mockExecAsync).toHaveBeenCalledWith('which dnf')
|
|
368
|
-
expect(mockExecAsync).toHaveBeenCalledWith(
|
|
369
|
-
'sudo dnf makecache && sudo dnf install -y xorg-x11-server-Xvfb',
|
|
370
|
-
{ timeout: 240000 }
|
|
371
|
-
)
|
|
372
|
-
})
|
|
373
|
-
|
|
374
|
-
it('should detect pacman package manager', async () => {
|
|
375
|
-
// Mock xvfb-run not found, then package manager detection
|
|
376
|
-
mockExecAsync
|
|
377
|
-
.mockRejectedValueOnce(new Error('Command not found'))
|
|
378
|
-
.mockRejectedValueOnce(new Error('apt-get not found'))
|
|
379
|
-
.mockRejectedValueOnce(new Error('dnf not found'))
|
|
380
|
-
.mockRejectedValueOnce(new Error('yum not found'))
|
|
381
|
-
.mockRejectedValueOnce(new Error('zypper not found'))
|
|
382
|
-
.mockResolvedValueOnce({ stdout: '/usr/bin/pacman', stderr: '' })
|
|
383
|
-
.mockResolvedValueOnce({ stdout: 'installation success', stderr: '' })
|
|
384
|
-
.mockResolvedValueOnce({ stdout: '/usr/bin/xvfb-run\n', stderr: '' })
|
|
385
|
-
|
|
386
|
-
const manager = new XvfbManager()
|
|
387
|
-
delete process.env.DISPLAY
|
|
388
|
-
|
|
389
|
-
await manager.init()
|
|
390
|
-
|
|
391
|
-
expect(mockExecAsync).toHaveBeenCalledWith('which pacman')
|
|
392
|
-
expect(mockExecAsync).toHaveBeenCalledWith(
|
|
393
|
-
'sudo pacman -Sy --noconfirm xorg-server-xvfb',
|
|
394
|
-
{ timeout: 240000 }
|
|
395
|
-
)
|
|
396
|
-
})
|
|
397
|
-
|
|
398
|
-
it('should detect dnf when apt-get is not available', async () => {
|
|
399
|
-
// Mock xvfb-run not found, then package manager detection
|
|
400
|
-
mockExecAsync
|
|
401
|
-
.mockRejectedValueOnce(new Error('Command not found'))
|
|
402
|
-
.mockRejectedValueOnce(new Error('apt-get not found'))
|
|
403
|
-
.mockResolvedValueOnce({ stdout: '/usr/bin/dnf', stderr: '' })
|
|
404
|
-
.mockResolvedValueOnce({ stdout: 'installation success', stderr: '' })
|
|
405
|
-
.mockResolvedValueOnce({ stdout: '/usr/bin/xvfb-run\n', stderr: '' })
|
|
406
|
-
|
|
407
|
-
const manager = new XvfbManager()
|
|
408
|
-
delete process.env.DISPLAY
|
|
409
|
-
|
|
410
|
-
await manager.init()
|
|
411
|
-
|
|
412
|
-
expect(mockExecAsync).toHaveBeenCalledWith('which apt-get')
|
|
413
|
-
expect(mockExecAsync).toHaveBeenCalledWith('which dnf')
|
|
414
|
-
})
|
|
415
|
-
|
|
416
|
-
it('should handle unsupported package managers gracefully', async () => {
|
|
417
|
-
// Mock all package managers as not found
|
|
418
|
-
mockExecAsync
|
|
419
|
-
.mockRejectedValueOnce(new Error('Command not found'))
|
|
420
|
-
.mockRejectedValue(new Error('Package manager not found'))
|
|
301
|
+
describe('autoInstall', () => {
|
|
302
|
+
it('should install xvfb when xvfb-run is not available and autoInstall is enabled', async () => {
|
|
303
|
+
// Mock xvfb-run not found first, then found after installation
|
|
304
|
+
mockExecAsync
|
|
305
|
+
.mockRejectedValueOnce(new Error('Command not found'))
|
|
306
|
+
.mockResolvedValueOnce({ stdout: '', stderr: '' })
|
|
307
|
+
.mockResolvedValueOnce({ stdout: '/usr/bin/which', stderr: '' })
|
|
308
|
+
.mockResolvedValueOnce({ stdout: 'apt-get install success', stderr: '' })
|
|
309
|
+
.mockResolvedValueOnce({ stdout: '/usr/bin/xvfb-run\n', stderr: '' })
|
|
310
|
+
|
|
311
|
+
const manager = new XvfbManager({ autoInstall: true })
|
|
312
|
+
|
|
313
|
+
// Mock platform and environment
|
|
314
|
+
mockPlatform.mockReturnValue('linux')
|
|
315
|
+
delete process.env.DISPLAY
|
|
316
|
+
|
|
317
|
+
const result = await manager.init()
|
|
318
|
+
|
|
319
|
+
expect(result).toBe(true)
|
|
320
|
+
expect(mockExecAsync).toHaveBeenCalledWith('which xvfb-run')
|
|
321
|
+
expect(mockExecAsync).toHaveBeenCalledWith('which apt-get')
|
|
322
|
+
expect(mockExecAsync).toHaveBeenCalledWith(
|
|
323
|
+
'sudo apt-get update -qq && sudo apt-get install -y xvfb',
|
|
324
|
+
{ timeout: 240000 }
|
|
325
|
+
)
|
|
326
|
+
})
|
|
421
327
|
|
|
422
|
-
|
|
423
|
-
|
|
328
|
+
it('should not install and return false when xvfb-run is not available and autoInstall is disabled', async () => {
|
|
329
|
+
// Mock xvfb-run not found
|
|
330
|
+
mockExecAsync
|
|
331
|
+
.mockRejectedValueOnce(new Error('Command not found'))
|
|
332
|
+
|
|
333
|
+
const manager = new XvfbManager()
|
|
334
|
+
|
|
335
|
+
// Mock platform and environment
|
|
336
|
+
mockPlatform.mockReturnValue('linux')
|
|
337
|
+
delete process.env.DISPLAY
|
|
338
|
+
|
|
339
|
+
const result = await manager.init()
|
|
340
|
+
|
|
341
|
+
expect(result).toBe(false)
|
|
342
|
+
// Should only check for xvfb-run
|
|
343
|
+
expect(mockExecAsync).toHaveBeenCalledWith('which xvfb-run')
|
|
344
|
+
// And should not attempt any package manager detection or install
|
|
345
|
+
expect(mockExecAsync).not.toHaveBeenCalledWith('which apt-get')
|
|
346
|
+
expect(mockExecAsync).not.toHaveBeenCalledWith('which dnf')
|
|
347
|
+
expect(mockExecAsync).not.toHaveBeenCalledWith('which yum')
|
|
348
|
+
expect(mockExecAsync).not.toHaveBeenCalledWith('which zypper')
|
|
349
|
+
expect(mockExecAsync).not.toHaveBeenCalledWith('which pacman')
|
|
350
|
+
expect(mockExecAsync).not.toHaveBeenCalledWith('which apk')
|
|
351
|
+
expect(mockExecAsync).not.toHaveBeenCalledWith('which xbps-install')
|
|
352
|
+
})
|
|
424
353
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
354
|
+
describe('cross-distribution support', () => {
|
|
355
|
+
beforeEach(() => {
|
|
356
|
+
mockPlatform.mockReturnValue('linux')
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
it('should detect Ubuntu distribution', async () => {
|
|
360
|
+
// Mock xvfb-run not found, then package manager detection
|
|
361
|
+
mockExecAsync
|
|
362
|
+
.mockRejectedValueOnce(new Error('Command not found'))
|
|
363
|
+
.mockResolvedValueOnce({ stdout: '/usr/bin/apt-get', stderr: '' })
|
|
364
|
+
.mockResolvedValueOnce({ stdout: 'installation success', stderr: '' })
|
|
365
|
+
.mockResolvedValueOnce({ stdout: '/usr/bin/xvfb-run\n', stderr: '' })
|
|
366
|
+
|
|
367
|
+
const manager = new XvfbManager({ autoInstall: true })
|
|
368
|
+
delete process.env.DISPLAY
|
|
369
|
+
|
|
370
|
+
await manager.init()
|
|
371
|
+
|
|
372
|
+
expect(mockExecAsync).toHaveBeenCalledWith('which apt-get')
|
|
373
|
+
expect(mockExecAsync).toHaveBeenCalledWith(
|
|
374
|
+
'sudo apt-get update -qq && sudo apt-get install -y xvfb',
|
|
375
|
+
{ timeout: 240000 }
|
|
376
|
+
)
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
it('should detect dnf package manager', async () => {
|
|
380
|
+
// Mock xvfb-run not found, then package manager detection
|
|
381
|
+
mockExecAsync
|
|
382
|
+
.mockRejectedValueOnce(new Error('Command not found'))
|
|
383
|
+
.mockRejectedValueOnce(new Error('apt-get not found'))
|
|
384
|
+
.mockResolvedValueOnce({ stdout: '/usr/bin/dnf', stderr: '' })
|
|
385
|
+
.mockResolvedValueOnce({ stdout: 'installation success', stderr: '' })
|
|
386
|
+
.mockResolvedValueOnce({ stdout: '/usr/bin/xvfb-run\n', stderr: '' })
|
|
387
|
+
|
|
388
|
+
const manager = new XvfbManager({ autoInstall: true })
|
|
389
|
+
delete process.env.DISPLAY
|
|
390
|
+
|
|
391
|
+
await manager.init()
|
|
392
|
+
|
|
393
|
+
expect(mockExecAsync).toHaveBeenCalledWith('which dnf')
|
|
394
|
+
expect(mockExecAsync).toHaveBeenCalledWith(
|
|
395
|
+
'sudo dnf makecache && sudo dnf install -y xorg-x11-server-Xvfb',
|
|
396
|
+
{ timeout: 240000 }
|
|
397
|
+
)
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
it('should detect pacman package manager', async () => {
|
|
401
|
+
// Mock xvfb-run not found, then package manager detection
|
|
402
|
+
mockExecAsync
|
|
403
|
+
.mockRejectedValueOnce(new Error('Command not found'))
|
|
404
|
+
.mockRejectedValueOnce(new Error('apt-get not found'))
|
|
405
|
+
.mockRejectedValueOnce(new Error('dnf not found'))
|
|
406
|
+
.mockRejectedValueOnce(new Error('yum not found'))
|
|
407
|
+
.mockRejectedValueOnce(new Error('zypper not found'))
|
|
408
|
+
.mockResolvedValueOnce({ stdout: '/usr/bin/pacman', stderr: '' })
|
|
409
|
+
.mockResolvedValueOnce({ stdout: 'installation success', stderr: '' })
|
|
410
|
+
.mockResolvedValueOnce({ stdout: '/usr/bin/xvfb-run\n', stderr: '' })
|
|
411
|
+
|
|
412
|
+
const manager = new XvfbManager({ autoInstall: true })
|
|
413
|
+
delete process.env.DISPLAY
|
|
414
|
+
|
|
415
|
+
await manager.init()
|
|
416
|
+
|
|
417
|
+
expect(mockExecAsync).toHaveBeenCalledWith('which pacman')
|
|
418
|
+
expect(mockExecAsync).toHaveBeenCalledWith(
|
|
419
|
+
'sudo pacman -Sy --noconfirm xorg-server-xvfb',
|
|
420
|
+
{ timeout: 240000 }
|
|
421
|
+
)
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
it('should detect dnf when apt-get is not available', async () => {
|
|
425
|
+
// Mock xvfb-run not found, then package manager detection
|
|
426
|
+
mockExecAsync
|
|
427
|
+
.mockRejectedValueOnce(new Error('Command not found'))
|
|
428
|
+
.mockRejectedValueOnce(new Error('apt-get not found'))
|
|
429
|
+
.mockResolvedValueOnce({ stdout: '/usr/bin/dnf', stderr: '' })
|
|
430
|
+
.mockResolvedValueOnce({ stdout: 'installation success', stderr: '' })
|
|
431
|
+
.mockResolvedValueOnce({ stdout: '/usr/bin/xvfb-run\n', stderr: '' })
|
|
432
|
+
|
|
433
|
+
const manager = new XvfbManager({ autoInstall: true })
|
|
434
|
+
delete process.env.DISPLAY
|
|
435
|
+
|
|
436
|
+
await manager.init()
|
|
437
|
+
|
|
438
|
+
expect(mockExecAsync).toHaveBeenCalledWith('which apt-get')
|
|
439
|
+
expect(mockExecAsync).toHaveBeenCalledWith('which dnf')
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
it('should handle unsupported package managers gracefully', async () => {
|
|
443
|
+
// Mock all package managers as not found
|
|
444
|
+
mockExecAsync
|
|
445
|
+
.mockRejectedValueOnce(new Error('Command not found'))
|
|
446
|
+
.mockRejectedValue(new Error('Package manager not found'))
|
|
447
|
+
|
|
448
|
+
const manager = new XvfbManager({ autoInstall: true })
|
|
449
|
+
delete process.env.DISPLAY
|
|
450
|
+
|
|
451
|
+
await expect(manager.init()).rejects.toThrow(
|
|
452
|
+
'Unsupported package manager: unknown. Please install Xvfb manually.'
|
|
453
|
+
)
|
|
454
|
+
})
|
|
455
|
+
})
|
|
428
456
|
})
|
|
429
457
|
})
|
|
430
458
|
|