Angular CLI
Starting Wallaby
To start Wallaby in VS Code you may run Select Configuration command and then select Automatic Configuration <project directory> option once. After that you may keep using Wallaby Start command as usual and it will start Wallaby with automatic configuration.
To start Wallaby in Sublime Text you may use the Select for Wallaby.js Automatic Configuration context menu item for your project folder in the project’s file tree. After that you may keep using Wallaby Start command as usual and it will start Wallaby with automatic configuration.
To start Wallaby in Visual Studio, you may use the Start Wallaby.js (Automatic Configuration) context menu item for your project folder in the Solution Explorer. After the first start,
the selected project will be remembered for your solution and Wallaby can be started with Tools->Start Wallaby.js (Alt-W, 1).
To start Wallaby in JetBrains IDEs, you may edit (or create a new) Wallaby Run Configuration by selecting Edit configurations from the drop-down menu in the top right-hand corner of the navigation bar, or from the Run menu. In the run configuration editor set Configuration Type filed value to Automatic.
Angular CLI v21
For Angular CLI v21 workspaces and projects, Wallaby automatically detects and configures the default Vitest runner. Wallaby runs tests in Node.js environments such as jsdom and happy-dom, or in browsers via playwright or webdriverio, and provides better incremental run performance, reliability, and live results streaming.
OXC compiler
By default, Wallaby uses Angular’s TypeScript compiler to transform your TypeScript files. In addition, Wallaby supports the experimental OXC Angular Compiler. If the @oxc-angular/vite package is installed, Wallaby automatically uses it to transform Angular TypeScript files, providing an alternative transpiler that may improve performance in some projects.
Custom Vitest configuration
Wallaby uses the Vitest Projects feature to run tests for each Angular CLI project. However, Angular CLI only partially supports custom Vitest configuration files, and its implementation differs from default Vitest behavior.
- When the
runnerConfigproperty is set totrue, Angular CLI looks for avitest-base.config.*file in the project root or workspace root and uses it as the configuration for that project. This name is not compatible with Vitest’s project configuration file pattern,vitest*.config.*. - Because Angular CLI does not allow running tests for the entire workspace, it always sets
cwdfor the custom config to your workspace root, even if you have multiple projects and the file is located in a project root.
There are two ways to work around these issues:
- Set
runnerConfigtotrue. Wallaby then tries to find avitest.wallaby.config.tsfile in the project root and uses it as the configuration for that project, while Angular CLI uses avitest-base.config.*file if it exists.
vitest.wallaby.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig(() => ({ test: { setupFiles: [ './src/test-setup.ts' ], }}));- Create a
vitest.config.tsfile in the project root and setrunnerConfigto{project.root}/vitest.config.ts. Then the same file is used for bothng testand Wallaby. If your configuration file defines paths or patterns, you may need to handle Wallaby specifically:
vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig(() => ({ test: { setupFiles: [process.env['WALLABY_ENV'] ? './src/test-setup.ts': './projects/app/src/test-setup.ts'], }}));If you have a single application project, you can set the runnerConfig for that project to vitest.config.ts and use the same configuration file for both Angular CLI and Wallaby without any modifications.
Optional workspace overrides
Merged tsconfig override
When running tests for the entire workspace, Wallaby creates a merged virtual copy of all project-level tsconfig.spec.json files. If these
files have been modified, they may not be mergeable. Wallaby lets you explicitly define the merged output file content.
tsconfig.wallaby.spec.json
{ "extends": "./tsconfig.json", "compilerOptions": { "types": ["vitest/globals"], "paths": { "lib-b": ["projects/lib-b/src/public-api.ts"], "lib-c": ["projects/lib-c/src/public-api.ts"] } }, "include": [ "projects/app-a/src/**/*.spec.ts", "projects/lib-b/src/**/*.spec.ts", "projects/lib-c/src/**/*.spec.ts", "projects/app-d/src/**/*.spec.ts" ]}Angular CLI v20
Starting with Angular CLI v20, Wallaby runs each project’s tests in their own dedicated browser instance, providing improved isolation between test environments and eliminating shared state and unintended side effects across projects, resulting in more reliable and consistent test results. It also ensures that project-specific configurations and dependencies are evaluated independently, making test behavior more closely aligned with ng test.
Optional workspace overrides
Merged tsconfig override
When running tests for the entire workspace, Wallaby creates a merged virtual copy of all project-level tsconfig.spec.json files. If these
files have been modified, they may not be mergeable. Wallaby lets you explicitly define the merged output file content.
tsconfig.wallaby.spec.json
{ "extends": "./tsconfig.json", "compilerOptions": { "types": ["jasmine", "node"], "paths": { "lib-b": ["projects/lib-b/src/public-api.ts"], "lib-b/*": ["projects/lib-b/src/*"], "lib-c": ["projects/lib-c/src/public-api.ts"], "lib-c/*": ["projects/lib-c/src/*"] } }, "files": [ "projects/app-a/src/test.ts", "projects/app-a/src/polyfills.ts", "projects/lib-b/src/test.ts", "projects/lib-b/src/polyfills.ts", "projects/lib-c/src/test.ts", "projects/lib-c/src/polyfills.ts", "projects/app-d/src/test.ts", "projects/app-d/src/polyfills.ts" ], "include": [ "projects/app-a/src/**/*.spec.ts", "projects/app-a/src/**/*.d.ts", "projects/lib-b/src/**/*.spec.ts", "projects/lib-b/src/**/*.d.ts", "projects/lib-c/src/**/*.spec.ts", "projects/lib-c/src/**/*.d.ts", "projects/app-d/src/**/*.spec.ts", "projects/app-d/src/**/*.d.ts" ]}TestBed initialization override
By default, Wallaby generates a TestBed initialization file similar to Angular CLI. If you want to customize it when running the whole workspace, you can create a test.wallaby.ts file in the workspace root.
test.wallaby.ts
import { getTestBed } from '@angular/core/testing';import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting(), { errorOnUnknownElements: true, errorOnUnknownProperties: true,});Angular CLI v8 and above
Starting with v8.2.0, you can use Wallaby’s Automatic Configuration to run tests for your Angular CLI workspace or project.
Optional workspace overrides
When running tests for the entire workspace, Wallaby creates merged virtual copies of test.ts, polyfills.ts, and tsconfig.spec.json files. If these files have been modified, they may not be mergeable. Wallaby lets you explicitly define the merged content of these files.
To explicitly override the merged contents of test.ts, polyfills.ts and tsconfig.spec.json, you must create an override file for each file name. For example, to replace the merged override of all test.ts files, you must create a file in the workspace root named test.wallaby.ts. The same is true for polyfills.ts and tsconfig.spec.json which become polyfills.wallaby.ts and tsconfig.wallaby.spec.json respectively.
Merged TestBed initialization override
test.wallaby.ts
import 'zone.js/dist/zone-testing';import { getTestBed } from '@angular/core/testing';import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
// shared imports for all your projects// imports for app-a, lib-b, lib-c, app-d etc.
// shared code for all your projects// code for app-a, lib-b, lib-c, app-d etc.
// First, initialize the Angular testing environment.getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
// shared code for all your projects// code for app-a, lib-b, lib-c, app-d etc.Merged polyfills override
polyfills.wallaby.ts
import 'zone.js/dist/zone';// shared code for all your projects// code for app-a, lib-b, lib-c, app-d etc.Merged tsconfig override
tsconfig.wallaby.spec.json
{ "extends": "./tsconfig.json", "compilerOptions": { "types": ["jasmine", "node"], "paths": { "lib-b": ["projects/lib-b/src/public-api.ts"], "lib-b/*": ["projects/lib-b/src/*"], "lib-c": ["projects/lib-c/src/public-api.ts"], "lib-c/*": ["projects/lib-c/src/*"] } }, "files": [ "projects/app-a/src/test.ts", "projects/app-a/src/polyfills.ts", "projects/lib-b/src/test.ts", "projects/lib-b/src/polyfills.ts", "projects/lib-c/src/test.ts", "projects/lib-c/src/polyfills.ts", "projects/app-d/src/test.ts", "projects/app-d/src/polyfills.ts" ], "include": [ "projects/app-a/src/**/*.spec.ts", "projects/app-a/src/**/*.d.ts", "projects/lib-b/src/**/*.spec.ts", "projects/lib-b/src/**/*.d.ts", "projects/lib-c/src/**/*.spec.ts", "projects/lib-c/src/**/*.d.ts", "projects/app-d/src/**/*.spec.ts", "projects/app-d/src/**/*.d.ts" ]}If you have created a polyfills.wallaby.ts override, then it must be added to your TypeScript files configuration section.
Optional project overrides
When running tests for a specific project, Wallaby slightly modifies your project’s test.ts file. Wallaby has its own mechanism for resolving test files and removes the code that provides webpack with the list of tests to run. If this file has been modified, Wallaby may not be able to safely find and delete this code, so Angular CLI defaults for test.ts will be used. If desired, you can provide Wallaby with a different file to run by creating a test.wallaby.ts file in the same folder as your test.ts file.
TestBed initialization override
test.wallaby.ts
import 'zone.js/dist/zone-testing';import { getTestBed } from '@angular/core/testing';import { BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing';
// your imports
declare const require: any;
// your code
// First, initialize the Angular testing environment.getTestBed().initTestEnvironment( BrowserDynamicTestingModule, platformBrowserDynamicTesting());
// your code
// ! TEST FILE IMPORTS ARE RESOLVED BY WALLABY AND !// ! SHOULD BE COMMENTED OUT OR DELETED FROM THIS FILE !//// const context = require.context('./', true, /\.spec\.ts$/);// context.keys().map(context);Chrome settings
When running tests in browser, wallaby.js uses your local version of Google Chrome (headless) as its test runner by default.
You may use an absolute or a relative path (or a command if you have it in PATH) in your
wallaby.js config file. It may be useful if you would like to use a specific version of
Chrome.
wallaby.js
module.exports = function () { return { ...
env: { kind: 'chrome', runner: '/Users/user/path/to/chrome' } };};Passing Chrome flags
By default, wallaby.js is passing the following flags to Chrome:
--headless--disable-gpu--disable-translate--disable-extensions--disable-background-networking--safebrowsing-disable-auto-update--disable-sync--metrics-recording-only--disable-default-apps--no-first-run
For Linux environments, an additional two flags are also passed:
--no-sandbox--disable-setuid-sandbox
You may pass other flags, by using the env.params.runner setting, for example:
wallaby.js
module.exports = function () { return { ...
env: { kind: 'chrome', params: { runner: '--headless --disable-gpu' } } };};Jest
If you have configured your project to use Angular Jest Builder or are using an Nx/Nrwl workspace, you can use Wallaby without any configuration.
If you have a custom Angular/Jest configuration, please refer to our Jest docs.
Angular CLI v8 and below
If you are not using Automatic Configuration to run your Angular CLI project’s tests, you must provide a configuration file that tells Wallaby how to load and run your tests. We have created a configuration file that works for Angular CLI projects that use Karma as the test runner.
Copy the configuration shown below and paste it into a file named wallaby.js in your project root. The configuration file may seem unfamiliar, but you do not need to understand every detail to use Wallaby with Angular.
You can read more about configuring Wallaby in our configuration settings docs.
module.exports = function(wallaby) { const wallabyWebpack = require('wallaby-webpack'); const path = require('path'); const fs = require('fs');
const specPattern = '/**/*spec.ts'; const angularConfig = require('./angular.json');
const projects = Object.keys(angularConfig.projects).map(key => { return { name: key, ...angularConfig.projects[key] }; }).filter(project => project.sourceRoot) .filter(project => project.projectType !== 'application' || (project.architect && project.architect.test && project.architect.test.builder === '@angular-devkit/build-angular:karma'));
const applications = projects.filter(project => project.projectType === 'application'); const libraries = projects.filter(project => project.projectType === 'library');
const tsConfigFile = projects .map(project => path.join(__dirname, project.root, 'tsconfig.spec.json')) .find(tsConfig => fs.existsSync(tsConfig));
const tsConfigSpec = tsConfigFile ? JSON.parse(fs.readFileSync(tsConfigFile)) : {};
const compilerOptions = Object.assign(require('./tsconfig.json').compilerOptions, tsConfigSpec.compilerOptions); compilerOptions.emitDecoratorMetadata = true;
return { files: [ { pattern: path.basename(__filename), load: false, instrument: false }, ...projects.map(project => ({ pattern: project.sourceRoot + '/**/*.+(ts|js|css|less|scss|sass|styl|html|json|svg)', load: false })), ...projects.map(project => ({ pattern: project.sourceRoot + specPattern, ignore: true })), ...projects.map(project => ({ pattern: project.sourceRoot + '/**/*.d.ts', ignore: true })) ],
tests: [ ...projects.map(project => ({ pattern: project.sourceRoot + specPattern, load: false })) ],
testFramework: 'jasmine',
compilers: { '**/*.ts': wallaby.compilers.typeScript({ ...compilerOptions, getCustomTransformers: program => { return { before: [ require('@ngtools/webpack/src/transformers/replace_resources').replaceResources( path => true, () => program.getTypeChecker(), false ) ] }; } }) },
preprocessors: { /* Initialize Test Environment for Wallaby */ [path.basename(__filename)]: file => ` import '@angular-devkit/build-angular/src/angular-cli-files/models/jit-polyfills'; import 'zone.js/dist/zone-testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing';
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());` },
middleware: function(app, express) { const path = require('path');
applications.forEach(application => { if ( !application.architect || !application.architect.test || !application.architect.test.options || !application.architect.test.options.assets ) { return; }
application.architect.test.options.assets.forEach(asset => { if (asset && !asset.glob) { // Only works for file assets (not globs) // (https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/asset-configuration.md#project-assets) app.use(asset.slice(application.sourceRoot.length), express.static(path.join(__dirname, asset))); } }); }); },
env: { kind: 'chrome' },
postprocessor: wallabyWebpack({ entryPatterns: [ ...applications .map(project => project.sourceRoot + '/polyfills.js') .filter(polyfills => fs.existsSync(path.join(__dirname, polyfills.replace(/js$/, 'ts')))), path.basename(__filename), ...projects.map(project => project.sourceRoot + specPattern.replace(/ts$/, 'js')) ],
module: { rules: [ { test: /\.css$/, loader: ['raw-loader'] }, { test: /\.html$/, loader: 'raw-loader' }, { test: /\.ts$/, loader: '@ngtools/webpack', include: /node_modules/, query: { tsConfigPath: 'tsconfig.json' } }, { test: /\.styl$/, loaders: ['raw-loader', 'stylus-loader'] }, { test: /\.less$/, loaders: ['raw-loader', { loader: 'less-loader' }] }, { test: /\.scss$|\.sass$/, loaders: [{ loader: 'raw-loader' }, { loader: 'sass-loader', options: { implementation: require('sass') } }] }, { test: /\.(jpg|png|svg)$/, loader: 'raw-loader' } ] },
resolve: { extensions: ['.js', '.ts'], modules: [ wallaby.projectCacheDir, ...(projects.length ? projects.filter(project => project.root) .map(project => path.join(wallaby.projectCacheDir, project.root)) : []), ...(projects.length ? projects.filter(project => project.sourceRoot) .map(project => path.join(wallaby.projectCacheDir,project.sourceRoot)) : []), 'node_modules' ], alias: libraries.reduce((result, project) => { const alias = project.name.replace(/([a-zA-Z])(?=[A-Z])/g, '$1-').toLowerCase(); result[alias] = path.join(wallaby.projectCacheDir, project.sourceRoot, 'public-api'); return result; }, {}) } }),
setup: function() { window.__moduleBundler.loadTests(); } };};