Skip to content

Commit b7e2b50

Browse files
feat(testing): support browser executable path detection via environm… (#6308)
* feat(testing): support browser executable path detection via environment variables Add fallback chain for browser executable path using PUPPETEER_EXECUTABLE_PATH and CHROME_PATH environment variables, improving automatic browser detection for Puppeteer-based testing. * fix executablePath * always apply --no-sandbox in CI * update unit tests * fix unit tests
1 parent b07dda6 commit b7e2b50

File tree

4 files changed

+112
-18
lines changed

4 files changed

+112
-18
lines changed

src/compiler/config/test/validate-testing.spec.ts

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ describe('validateTesting', () => {
6464
});
6565

6666
describe('browserHeadless', () => {
67+
const originalCI = process.env.CI;
68+
beforeEach(() => {
69+
delete process.env.CI;
70+
});
71+
72+
afterEach(() => {
73+
process.env.CI = originalCI;
74+
});
75+
6776
describe("using 'headless' value from cli", () => {
6877
it.each([false, 'shell'])('sets browserHeadless to %s', (headless) => {
6978
userConfig.flags = { ...flags, e2e: true, headless };
@@ -129,6 +138,15 @@ describe('validateTesting', () => {
129138
});
130139

131140
describe('devTools', () => {
141+
const originalCI = process.env.CI;
142+
beforeEach(() => {
143+
delete process.env.CI;
144+
});
145+
146+
afterEach(() => {
147+
process.env.CI = originalCI;
148+
});
149+
132150
it('ignores devTools settings if CI is enabled', () => {
133151
userConfig.flags = { ...flags, ci: true, devtools: true, e2e: true };
134152
userConfig.testing = {};
@@ -184,6 +202,15 @@ describe('validateTesting', () => {
184202
});
185203

186204
describe('browserArgs', () => {
205+
const originalCI = process.env.CI;
206+
beforeEach(() => {
207+
delete process.env.CI;
208+
});
209+
210+
afterEach(() => {
211+
process.env.CI = originalCI;
212+
});
213+
187214
it('does not add duplicate default fields', () => {
188215
userConfig.flags = { ...flags, e2e: true };
189216
userConfig.testing = {
@@ -195,12 +222,24 @@ describe('validateTesting', () => {
195222
expect(config.testing.browserArgs).toEqual(['--unique', '--font-render-hinting=medium', '--incognito']);
196223
});
197224

198-
it('adds default browser args', () => {
199-
userConfig.flags = { ...flags, e2e: true };
225+
describe('adds default browser args', () => {
226+
const originalCI = process.env.CI;
200227

201-
const { config } = validateConfig(userConfig, mockLoadConfigInit());
228+
beforeAll(() => {
229+
delete process.env.CI;
230+
});
231+
232+
afterAll(() => {
233+
process.env.CI = originalCI;
234+
});
235+
236+
it('adds default browser args when not in CI', () => {
237+
userConfig.flags = { ...flags, e2e: true };
238+
239+
const { config } = validateConfig(userConfig, mockLoadConfigInit());
202240

203-
expect(config.testing.browserArgs).toEqual(['--font-render-hinting=medium', '--incognito']);
241+
expect(config.testing.browserArgs).toEqual(['--font-render-hinting=medium', '--incognito']);
242+
});
204243
});
205244

206245
it("adds additional browser args when the 'ci' flag is set", () => {
@@ -214,6 +253,58 @@ describe('validateTesting', () => {
214253
'--disable-dev-shm-usage',
215254
]);
216255
});
256+
257+
describe('adds additional browser args when process.env.CI is set', () => {
258+
const originalCI = process.env.CI;
259+
beforeAll(() => {
260+
process.env.CI = 'true';
261+
});
262+
263+
afterAll(() => {
264+
process.env.CI = originalCI;
265+
});
266+
267+
it('adds default browser args when CI is set', () => {
268+
userConfig.flags = { ...flags, ci: true, e2e: true };
269+
const { config } = validateConfig(userConfig, mockLoadConfigInit());
270+
expect(config.testing.browserArgs).toEqual([
271+
'--font-render-hinting=medium',
272+
'--incognito',
273+
'--no-sandbox',
274+
'--disable-setuid-sandbox',
275+
'--disable-dev-shm-usage',
276+
]);
277+
});
278+
});
279+
});
280+
281+
describe('browserArgs in CI', () => {
282+
const originalCI = process.env.CI;
283+
beforeEach(() => {
284+
process.env.CI = 'true';
285+
});
286+
287+
afterEach(() => {
288+
process.env.CI = originalCI;
289+
});
290+
291+
it("adds additional browser args when 'CI' environment variable is set", () => {
292+
userConfig.flags = { ...flags, e2e: true };
293+
userConfig.testing = {
294+
browserArgs: ['--unique', '--font-render-hinting=medium'],
295+
};
296+
297+
const { config } = validateConfig(userConfig, mockLoadConfigInit());
298+
299+
expect(config.testing.browserArgs).toEqual([
300+
'--unique',
301+
'--font-render-hinting=medium',
302+
'--incognito',
303+
'--no-sandbox',
304+
'--disable-setuid-sandbox',
305+
'--disable-dev-shm-usage',
306+
]);
307+
});
217308
});
218309

219310
describe('screenshotConnector', () => {

src/compiler/config/validate-testing.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const validateTesting = (config: d.ValidatedConfig, diagnostics: d.Diagno
4848
testing.browserArgs = testing.browserArgs || [];
4949
addTestingConfigOption(testing.browserArgs, '--font-render-hinting=medium');
5050
addTestingConfigOption(testing.browserArgs, '--incognito');
51-
if (config.flags.ci) {
51+
if (config.flags.ci || process.env.CI) {
5252
addTestingConfigOption(testing.browserArgs, '--no-sandbox');
5353
addTestingConfigOption(testing.browserArgs, '--disable-setuid-sandbox');
5454
addTestingConfigOption(testing.browserArgs, '--disable-dev-shm-usage');

src/declarations/stencil-public-compiler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2055,6 +2055,7 @@ export interface TestingConfig extends JestConfig {
20552055

20562056
/**
20572057
* Path to a Chromium or Chrome executable to run instead of the bundled Chromium.
2058+
* @default env.PUPPETEER_EXECUTABLE_PATH || env.CHROME_PATH || puppeteer.computeExecutablePath()
20582059
*/
20592060
browserExecutablePath?: string;
20602061

src/testing/puppeteer/puppeteer-browser.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as d from '@stencil/core/declarations';
22
import type { E2EProcessEnv, ValidatedConfig } from '@stencil/core/internal';
3-
import type * as puppeteer from 'puppeteer';
3+
import type { Browser, connect, ConnectOptions, executablePath, launch, LaunchOptions } from 'puppeteer';
44
import semverMajor from 'semver/functions/major';
55

66
export async function startPuppeteerBrowser(config: ValidatedConfig) {
@@ -49,28 +49,30 @@ export async function startPuppeteerBrowser(config: ValidatedConfig) {
4949

5050
// connection options will be used regardless whether a new browser instance is created or we attach to a
5151
// pre-existing instance
52-
const connectOpts: puppeteer.ConnectOptions = {
52+
const connectOpts: ConnectOptions = {
5353
slowMo: config.testing.browserSlowMo,
5454
};
5555

56-
let browser: puppeteer.Browser;
56+
let browser: Browser;
5757
if (config.testing.browserWSEndpoint) {
58-
browser = await puppeteer.connect({
58+
browser = await (puppeteer.connect as typeof connect)({
5959
browserWSEndpoint: config.testing.browserWSEndpoint,
6060
...connectOpts,
6161
});
6262
} else {
63-
const launchOpts: puppeteer.LaunchOptions & puppeteer.ConnectOptions = {
63+
const launchOpts: LaunchOptions & ConnectOptions = {
6464
args: config.testing.browserArgs,
6565
channel: config.testing.browserChannel,
6666
headless: config.testing.browserHeadless,
6767
devtools: config.testing.browserDevtools,
6868
...connectOpts,
6969
};
70-
if (config.testing.browserExecutablePath) {
71-
launchOpts.executablePath = config.testing.browserExecutablePath;
72-
}
73-
browser = await puppeteer.launch({ ...launchOpts });
70+
launchOpts.executablePath =
71+
process.env.PUPPETEER_EXECUTABLE_PATH ||
72+
process.env.CHROME_PATH ||
73+
(puppeteer.executablePath as typeof executablePath)(launchOpts);
74+
75+
browser = await (puppeteer.launch as typeof launch)({ ...launchOpts });
7476
}
7577

7678
env.__STENCIL_BROWSER_WS_ENDPOINT__ = browser.wsEndpoint();
@@ -94,23 +96,23 @@ export async function connectBrowser() {
9496
return null;
9597
}
9698

97-
const connectOpts: puppeteer.ConnectOptions = {
99+
const connectOpts: ConnectOptions = {
98100
browserWSEndpoint: wsEndpoint,
99101
};
100102

101103
const puppeteer = require(env.__STENCIL_PUPPETEER_MODULE__);
102104

103-
return await puppeteer.connect(connectOpts);
105+
return await (puppeteer.connect as typeof connect)(connectOpts);
104106
}
105107

106-
export async function disconnectBrowser(browser: puppeteer.Browser) {
108+
export async function disconnectBrowser(browser: Browser) {
107109
if (browser) {
108110
try {
109111
browser.disconnect();
110112
} catch (e) {}
111113
}
112114
}
113115

114-
export function newBrowserPage(browser: puppeteer.Browser) {
116+
export function newBrowserPage(browser: Browser) {
115117
return browser.newPage();
116118
}

0 commit comments

Comments
 (0)