Skip to content

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.

  1. When the runnerConfig property is set to true, Angular CLI looks for a vitest-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.*.
  2. Because Angular CLI does not allow running tests for the entire workspace, it always sets cwd for 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:

  1. Set runnerConfig to true. Wallaby then tries to find a vitest.wallaby.config.ts file in the project root and uses it as the configuration for that project, while Angular CLI uses a vitest-base.config.* file if it exists.

vitest.wallaby.config.ts

import { defineConfig } from 'vitest/config';
export default defineConfig(() => ({
test: {
setupFiles: [ './src/test-setup.ts' ],
}
}));
  1. Create a vitest.config.ts file in the project root and set runnerConfig to {project.root}/vitest.config.ts. Then the same file is used for both ng test and 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();
}
};
};