-
-
Notifications
You must be signed in to change notification settings - Fork 10k
Addon-Vitest: Make Playwright --with-deps platform-aware to avoid sudo prompt on Linux
#34121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
05cf78f
388e33b
590dffb
8de539a
c9f38b0
bfb242e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -391,7 +391,7 @@ describe('AddonVitestService', () => { | |
| vi.mocked(logger.warn).mockImplementation(() => {}); | ||
| // Mock getPackageCommand to return a string | ||
| vi.mocked(mockPackageManager.getPackageCommand).mockReturnValue( | ||
| 'npx playwright install chromium --with-deps' | ||
| 'npx playwright install chromium' | ||
| ); | ||
| }); | ||
|
|
||
|
|
@@ -416,24 +416,62 @@ describe('AddonVitestService', () => { | |
| }); | ||
|
|
||
| it('should execute playwright install command', async () => { | ||
| type ChildProcessFactory = (signal?: AbortSignal) => ResultPromise; | ||
| let commandFactory: ChildProcessFactory | ChildProcessFactory[]; | ||
| vi.mocked(prompt.confirm).mockResolvedValue(true); | ||
| vi.mocked(prompt.executeTaskWithSpinner).mockImplementation( | ||
| async (factory: ChildProcessFactory | ChildProcessFactory[]) => { | ||
| commandFactory = Array.isArray(factory) ? factory[0] : factory; | ||
| // Simulate the child process completion | ||
| commandFactory(); | ||
| const originalCI = process.env.CI; | ||
| delete process.env.CI; | ||
| try { | ||
| type ChildProcessFactory = (signal?: AbortSignal) => ResultPromise; | ||
| let commandFactory: ChildProcessFactory | ChildProcessFactory[]; | ||
| vi.mocked(prompt.confirm).mockResolvedValue(true); | ||
| vi.mocked(prompt.executeTaskWithSpinner).mockImplementation( | ||
| async (factory: ChildProcessFactory | ChildProcessFactory[]) => { | ||
| commandFactory = Array.isArray(factory) ? factory[0] : factory; | ||
| // Simulate the child process completion | ||
| commandFactory(); | ||
| } | ||
| ); | ||
|
Comment on lines
+428
to
+434
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move spinner mock implementations out of test cases and into
♻️ Suggested refactor describe('installPlaywright', () => {
+ type ChildProcessFactory = (signal?: AbortSignal) => ResultPromise;
+
beforeEach(() => {
// Mock the logger methods used in installPlaywright
vi.mocked(logger.log).mockImplementation(() => {});
vi.mocked(logger.warn).mockImplementation(() => {});
// Mock getPackageCommand to return a string
vi.mocked(mockPackageManager.getPackageCommand).mockReturnValue(
'npx playwright install chromium'
);
+ vi.mocked(prompt.executeTaskWithSpinner).mockImplementation(
+ async (factory: ChildProcessFactory | ChildProcessFactory[]) => {
+ const commandFactory = Array.isArray(factory) ? factory[0] : factory;
+ commandFactory();
+ return undefined;
+ }
+ );
});
it('should execute playwright install command', async () => {
const originalCI = process.env.CI;
delete process.env.CI;
try {
- type ChildProcessFactory = (signal?: AbortSignal) => ResultPromise;
- let commandFactory: ChildProcessFactory | ChildProcessFactory[];
vi.mocked(prompt.confirm).mockResolvedValue(true);
- vi.mocked(prompt.executeTaskWithSpinner).mockImplementation(
- async (factory: ChildProcessFactory | ChildProcessFactory[]) => {
- commandFactory = Array.isArray(factory) ? factory[0] : factory;
- // Simulate the child process completion
- commandFactory();
- }
- );
await service.installPlaywright();
expect(mockPackageManager.runPackageCommand).toHaveBeenCalledWith({
args: ['playwright', 'install', 'chromium'],
@@
it('should execute playwright install command with --with-deps in CI', async () => {
const originalCI = process.env.CI;
process.env.CI = 'true';
try {
- type ChildProcessFactory = (signal?: AbortSignal) => ResultPromise;
- let commandFactory: ChildProcessFactory | ChildProcessFactory[];
vi.mocked(prompt.confirm).mockResolvedValue(true);
- vi.mocked(prompt.executeTaskWithSpinner).mockImplementation(
- async (factory: ChildProcessFactory | ChildProcessFactory[]) => {
- commandFactory = Array.isArray(factory) ? factory[0] : factory;
- commandFactory();
- }
- );
await service.installPlaywright();
expect(mockPackageManager.runPackageCommand).toHaveBeenCalledWith({As per coding guidelines “Avoid inline mock implementations within test cases in Vitest tests” and “Implement mock behaviors in Also applies to: 454-459 🤖 Prompt for AI Agents |
||
|
|
||
| await service.installPlaywright(); | ||
|
|
||
| expect(mockPackageManager.runPackageCommand).toHaveBeenCalledWith({ | ||
| args: ['playwright', 'install', 'chromium'], | ||
| signal: undefined, | ||
| stdio: ['inherit', 'pipe', 'pipe'], | ||
| }); | ||
| } finally { | ||
| if (originalCI !== undefined) { | ||
| process.env.CI = originalCI; | ||
| } | ||
| ); | ||
|
|
||
| await service.installPlaywright(); | ||
|
|
||
| expect(mockPackageManager.runPackageCommand).toHaveBeenCalledWith({ | ||
| args: ['playwright', 'install', 'chromium', '--with-deps'], | ||
| signal: undefined, | ||
| stdio: ['inherit', 'pipe', 'pipe'], | ||
| }); | ||
| } | ||
|
Comment on lines
+443
to
+447
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Environment variable restoration logic differs across tests. The
Lines 443-447 and 535-539 may leave 🛠️ Suggested fix for consistent cleanup } finally {
- if (originalCI !== undefined) {
- process.env.CI = originalCI;
+ if (originalCI === undefined) {
+ delete process.env.CI;
+ } else {
+ process.env.CI = originalCI;
}
}Apply this pattern to all three locations for consistency. Also applies to: 502-508, 535-539 🤖 Prompt for AI Agents |
||
| }); | ||
|
|
||
| it('should execute playwright install command with --with-deps in CI', async () => { | ||
| const originalCI = process.env.CI; | ||
| process.env.CI = 'true'; | ||
| try { | ||
| type ChildProcessFactory = (signal?: AbortSignal) => ResultPromise; | ||
| let commandFactory: ChildProcessFactory | ChildProcessFactory[]; | ||
| vi.mocked(prompt.confirm).mockResolvedValue(true); | ||
| vi.mocked(prompt.executeTaskWithSpinner).mockImplementation( | ||
| async (factory: ChildProcessFactory | ChildProcessFactory[]) => { | ||
| commandFactory = Array.isArray(factory) ? factory[0] : factory; | ||
| commandFactory(); | ||
| } | ||
| ); | ||
|
|
||
| await service.installPlaywright(); | ||
|
|
||
| expect(mockPackageManager.runPackageCommand).toHaveBeenCalledWith({ | ||
| args: ['playwright', 'install', 'chromium', '--with-deps'], | ||
| signal: undefined, | ||
| stdio: ['inherit', 'pipe', 'pipe'], | ||
| }); | ||
| } finally { | ||
| if (originalCI === undefined) { | ||
| delete process.env.CI; | ||
| } else { | ||
| process.env.CI = originalCI; | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| it('should capture error stack when installation fails', async () => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -106,8 +106,9 @@ | |
| /** | ||
| * Install Playwright browser binaries for @storybook/addon-vitest | ||
| * | ||
| * Installs Chromium with dependencies via `npx playwright install chromium --with-deps` | ||
| * Installs Chromium via `npx playwright install chromium`. In CI environments, also installs | ||
| * system-level browser dependencies via `--with-deps`. | ||
| * | ||
|
Check failure on line 111 in code/core/src/cli/AddonVitestService.ts
|
||
| * @param packageManager - The package manager to use for installation | ||
| * @param prompt - The prompt instance for displaying progress | ||
| * @param logger - The logger instance for displaying messages | ||
|
|
@@ -123,7 +124,9 @@ | |
| ): Promise<{ errors: string[]; result: 'installed' | 'skipped' | 'aborted' | 'failed' }> { | ||
| const errors: string[] = []; | ||
|
|
||
| const playwrightCommand = ['playwright', 'install', 'chromium', '--with-deps']; | ||
| const playwrightCommand = process.env.CI | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot Here I would suggest also enabling --with-deps for os.platform darwin and win32 as they are officially supported.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 8de539a. |
||
| ? ['playwright', 'install', 'chromium', '--with-deps'] | ||
| : ['playwright', 'install', 'chromium']; | ||
| const playwrightCommandString = this.packageManager.getPackageCommand(playwrightCommand); | ||
|
|
||
| let result: 'installed' | 'skipped' | 'aborted' | 'failed'; | ||
|
|
@@ -319,7 +322,7 @@ | |
| isValid = | ||
| this.isWorkspaceConfigArray(declaration) || this.isDefineWorkspaceExpression(declaration); | ||
| }, | ||
| }); | ||
|
|
||
| return isValid; | ||
| } | ||
|
|
@@ -335,7 +338,7 @@ | |
| isValidVitestConfig = this.isSafeToExtendWorkspace(path.node.declaration); | ||
| } else if (this.isMergeConfigExpression(path.node.declaration)) { | ||
| // the config could be anywhere in the mergeConfig call, so we need to check each argument | ||
| const mergeCall = path.node.declaration as CallExpression; | ||
| isValidVitestConfig = mergeCall.arguments.some((arg) => | ||
| this.isSafeToExtendWorkspace(arg as CallExpression) | ||
| ); | ||
|
|
@@ -351,34 +354,34 @@ | |
| babel.types.isArrayExpression(node) && | ||
| node?.elements.every( | ||
| (el: any) => babel.types.isStringLiteral(el) || babel.types.isObjectExpression(el) | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| private isDefineWorkspaceExpression(node: any): boolean { | ||
| return ( | ||
| babel.types.isCallExpression(node) && | ||
| node.callee && | ||
| (node.callee as any)?.name === 'defineWorkspace' && | ||
| this.isWorkspaceConfigArray(node.arguments?.[0]) | ||
| ); | ||
| } | ||
|
|
||
| private isDefineConfigExpression(node: any): boolean { | ||
| return ( | ||
| babel.types.isCallExpression(node) && | ||
| (node.callee as any)?.name === 'defineConfig' && | ||
| babel.types.isObjectExpression(node.arguments?.[0]) | ||
| ); | ||
| } | ||
|
|
||
| private isMergeConfigExpression(path: babel.types.Node): boolean { | ||
| return babel.types.isCallExpression(path) && (path.callee as any)?.name === 'mergeConfig'; | ||
| } | ||
|
|
||
| private isSafeToExtendWorkspace(node: babel.types.Node): boolean { | ||
| // Extract the object expression to validate | ||
| let objectToValidate: babel.types.ObjectExpression | null = null; | ||
|
|
||
| if (babel.types.isCallExpression(node)) { | ||
| // Handle function calls like defineConfig({...}) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot here I would use os.platform to print the --with-deps command for darwin and win32 users, and for other users:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 8de539a. On macOS/Windows the warning shows
npx playwright install chromium --with-deps; on other platforms it showsnpx playwright install chromiumwith an appended note:Note: add --with-deps to the command above if you are on Debian or Ubuntu.