Skip to content

Commit 3a0fdb3

Browse files
committed
fix(@angular/build): scope CHROME_BIN executable path to individual playwright instances
Previously, if CHROME_BIN was set in the environment and a user ran tests targeting the Playwright provider, the path was applied to the global Playwright launch options. This caused tests to crash if a user requested non-Chromium browsers (like Firefox) alongside Chromium, because Playwright would incorrectly attempt to launch the Chrome binary for the Firefox instance. This commit updates the browser configuration to map instances before providers are initialized, and selectively injects `launchOptions: { executablePath: process.env.CHROME_BIN }` at the individual instance level for chrome and chromium only. This restores parity where users can maintain CHROME_BIN variables while safely invoking alternative browsers.
1 parent 685ebae commit 3a0fdb3

File tree

2 files changed

+77
-8
lines changed

2 files changed

+77
-8
lines changed

packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ export async function setupBrowserConfiguration(
7979
);
8080
}
8181

82+
const instances = browsers.map(
83+
(b) => normalizeBrowserName(b) as { browser: string; headless: boolean; provider?: import('vitest/node').BrowserProviderOption },
84+
);
85+
8286
let provider: import('vitest/node').BrowserProviderOption | undefined;
8387
if (providerName) {
8488
const providerPackage = `@vitest/browser-${providerName}`;
@@ -90,17 +94,25 @@ export async function setupBrowserConfiguration(
9094
if (typeof providerFactory === 'function') {
9195
if (providerName === 'playwright') {
9296
const executablePath = process.env['CHROME_BIN'];
93-
provider = providerFactory({
94-
launchOptions: executablePath
95-
? {
96-
executablePath,
97-
}
98-
: undefined,
97+
const baseOptions = {
9998
contextOptions: {
10099
// Enables `prefer-color-scheme` for Vitest browser instead of `light`
101100
colorScheme: null,
102101
},
103-
});
102+
};
103+
104+
provider = providerFactory(baseOptions);
105+
106+
if (executablePath) {
107+
for (const instance of instances) {
108+
if (instance.browser === 'chrome' || instance.browser === 'chromium') {
109+
instance.provider = providerFactory({
110+
...baseOptions,
111+
launchOptions: { executablePath },
112+
});
113+
}
114+
}
115+
}
104116
} else {
105117
provider = providerFactory();
106118
}
@@ -133,7 +145,6 @@ export async function setupBrowserConfiguration(
133145
}
134146

135147
const isCI = !!process.env['CI'];
136-
const instances = browsers.map(normalizeBrowserName);
137148
const messages: string[] = [];
138149

139150
if (providerName === 'preview') {

packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider_spec.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,4 +215,62 @@ describe('setupBrowserConfiguration', () => {
215215
'The "headless" option is unnecessary as all browsers are already configured to run in headless mode.',
216216
]);
217217
});
218+
219+
describe('CHROME_BIN usage', () => {
220+
let originalChromeBin: string | undefined;
221+
222+
beforeEach(() => {
223+
originalChromeBin = process.env['CHROME_BIN'];
224+
process.env['CHROME_BIN'] = '/custom/path/to/chrome';
225+
});
226+
227+
afterEach(() => {
228+
if (originalChromeBin === undefined) {
229+
delete process.env['CHROME_BIN'];
230+
} else {
231+
process.env['CHROME_BIN'] = originalChromeBin;
232+
}
233+
});
234+
235+
it('should set executablePath on the individual chrome instance', async () => {
236+
const { browser } = await setupBrowserConfiguration(
237+
['ChromeHeadless', 'Chromium'],
238+
undefined,
239+
false,
240+
workspaceRoot,
241+
undefined,
242+
);
243+
244+
// Verify the global provider does NOT have executablePath
245+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
246+
expect((browser?.provider as any)?.options?.launchOptions?.executablePath).toBeUndefined();
247+
248+
// Verify the individual instances have executablePath
249+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
250+
expect((browser?.instances?.[0]?.provider as any)?.options?.launchOptions?.executablePath).toBe('/custom/path/to/chrome');
251+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
252+
expect((browser?.instances?.[1]?.provider as any)?.options?.launchOptions?.executablePath).toBe('/custom/path/to/chrome');
253+
});
254+
255+
it('should set executablePath for chrome instances but not for others when mixed browsers are requested', async () => {
256+
const { browser } = await setupBrowserConfiguration(
257+
['ChromeHeadless', 'Firefox'],
258+
undefined,
259+
false,
260+
workspaceRoot,
261+
undefined,
262+
);
263+
264+
// Verify the global provider does NOT have executablePath
265+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
266+
expect((browser?.provider as any)?.options?.launchOptions?.executablePath).toBeUndefined();
267+
268+
// Verify chrome gets it
269+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
270+
expect((browser?.instances?.[0]?.provider as any)?.options?.launchOptions?.executablePath).toBe('/custom/path/to/chrome');
271+
272+
// Verify firefox does not
273+
expect(browser?.instances?.[1]?.provider).toBeUndefined();
274+
});
275+
});
218276
});

0 commit comments

Comments
 (0)