From 16ac741bf4293a8da0288f7140d7750d586b5e83 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Fri, 30 Jan 2026 15:59:43 -0500 Subject: [PATCH] fix(@schematics/angular): add actionable feedback to vitest-browser schematic This commit adds a message at the end of the vitest-browser schematic to inform users how to configure browsers in angular.json or via the CLI. It also adds unit tests for the schematic. --- .../angular/vitest-browser/index.ts | 7 + .../angular/vitest-browser/index_spec.ts | 127 ++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 packages/schematics/angular/vitest-browser/index_spec.ts diff --git a/packages/schematics/angular/vitest-browser/index.ts b/packages/schematics/angular/vitest-browser/index.ts index bdc366bf4423..78c3bdc6179d 100644 --- a/packages/schematics/angular/vitest-browser/index.ts +++ b/packages/schematics/angular/vitest-browser/index.ts @@ -98,6 +98,13 @@ export default function (options: VitestBrowserOptions): Rule { install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto, }), ), + (_, context) => { + context.logger.info( + 'Vitest browser testing support has been added. ' + + "To run tests in a browser, add a 'browsers' field to the 'test' target in 'angular.json', " + + "or use the '--browsers' command line option.", + ); + }, ]); }; } diff --git a/packages/schematics/angular/vitest-browser/index_spec.ts b/packages/schematics/angular/vitest-browser/index_spec.ts new file mode 100644 index 000000000000..9027742f73a9 --- /dev/null +++ b/packages/schematics/angular/vitest-browser/index_spec.ts @@ -0,0 +1,127 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; +import { parse } from 'jsonc-parser'; + +describe('Vitest Browser Provider Schematic', () => { + const schematicRunner = new SchematicTestRunner( + '@schematics/angular', + require.resolve('../collection.json'), + ); + + let tree: UnitTestTree; + + beforeEach(async () => { + tree = await schematicRunner.runSchematic('workspace', { + name: 'workspace', + newProjectRoot: 'projects', + version: '6.0.0', + }); + + tree = await schematicRunner.runSchematic( + 'application', + { + name: 'app', + skipInstall: true, + testRunner: 'vitest', + }, + tree, + ); + }); + + it('should add dependencies and update tsconfig.spec.json', async () => { + const options = { + project: 'app', + package: '@vitest/browser-playwright', + skipInstall: true, + }; + + const resultTree = await schematicRunner.runSchematic('vitest-browser', options, tree); + + const packageJson = parse(resultTree.readContent('/package.json')); + expect(packageJson.devDependencies['@vitest/browser-playwright']).toBeDefined(); + expect(packageJson.devDependencies['playwright']).toBeDefined(); + + const tsConfig = parse(resultTree.readContent('/projects/app/tsconfig.spec.json')); + expect(tsConfig.compilerOptions.types).toContain('vitest/globals'); + expect(tsConfig.compilerOptions.types).toContain('@vitest/browser-playwright'); + expect(tsConfig.compilerOptions.types).not.toContain('jasmine'); + }); + + it('should add webdriverio dependency when @vitest/browser-webdriverio is used', async () => { + const options = { + project: 'app', + package: '@vitest/browser-webdriverio', + skipInstall: true, + }; + + const resultTree = await schematicRunner.runSchematic('vitest-browser', options, tree); + + const packageJson = parse(resultTree.readContent('/package.json')); + expect(packageJson.devDependencies['@vitest/browser-webdriverio']).toBeDefined(); + expect(packageJson.devDependencies['webdriverio']).toBeDefined(); + }); + + it('should update tsconfig.spec.json for a library project', async () => { + tree = await schematicRunner.runSchematic( + 'library', + { + name: 'lib', + skipInstall: true, + }, + tree, + ); + + const options = { + project: 'lib', + package: '@vitest/browser-playwright', + skipInstall: true, + }; + + const resultTree = await schematicRunner.runSchematic('vitest-browser', options, tree); + + const tsConfig = parse(resultTree.readContent('/projects/lib/tsconfig.spec.json')); + expect(tsConfig.compilerOptions.types).toContain('vitest/globals'); + expect(tsConfig.compilerOptions.types).toContain('@vitest/browser-playwright'); + // Library schematic might put jasmine types by default. + expect(tsConfig.compilerOptions.types).not.toContain('jasmine'); + }); + + it('should throw if project does not exist', async () => { + const options = { + project: 'invalid', + package: '@vitest/browser-playwright', + }; + + await expectAsync( + schematicRunner.runSchematic('vitest-browser', options, tree), + ).toBeRejectedWithError('Project "invalid" does not exist.'); + }); + + it('should throw if project uses Karma', async () => { + const angularJson = parse(tree.readContent('/angular.json')); + const project = angularJson.projects.app; + const targets = project.architect || project.targets; + + targets.test.options ??= {}; + targets.test.options.runner = 'karma'; + tree.overwrite('/angular.json', JSON.stringify(angularJson)); + + const options = { + project: 'app', + package: '@vitest/browser-playwright', + }; + + await expectAsync( + schematicRunner.runSchematic('vitest-browser', options, tree), + ).toBeRejectedWithError( + 'Project "app" is configured to use Karma. Please migrate to Vitest before adding browser testing support.', + ); + }); +});