Supported technologies: Angular CLI, Angular 1.x

Angular CLI

For step-by-step instructions to configure and use Wallaby.js with an Angular CLI application, please refer to our Angular CLI tutorial.

The configuration for Angular CLI generated projects is provided below. Simply copy and paste the configuration into a new wallaby.js configuration file in your Angular project root directory. You may also refer to our example repo.

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: [
      ...applications.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 => {
          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: [
          __dirname,
          'node_modules',
          ...(projects.length ? projects.filter(project => project.root).map(project => project.root) : []),
          ...(projects.length ? projects.filter(project => project.sourceRoot).map(project => project.sourceRoot) : [])
        ],
        alias: libraries.reduce((result, project) => {
          result[project.name] = path.join(wallaby.projectCacheDir, project.sourceRoot, 'public-api');
          return result;
        }, {})
      }
    }),

    setup: function() {
      window.__moduleBundler.loadTests();
    }
  };
};

Workspaces/Libraries

You can use the same configuration above if you are using workspaces or have libraries nested in your angular project.

Jest

If you are using Jest >= v24, you can try our new experimental feature that allows to use Wallaby without any configuration.

If you have Jest CLI configured for your Angular project and want to run Wallaby, you may use the config below:

module.exports = function(wallaby) {
  return {
    files: ['src/**/*.+(ts|html|json|snap|css|less|sass|scss|jpg|jpeg|gif|png|svg)', '!src/**/*.spec.ts'],

    tests: ['src/**/*.spec.ts'],

    env: {
      type: 'node'
    },

    compilers: {
      '**/*.ts?(x)': wallaby.compilers.typeScript({
        module: 'commonjs',
        getCustomTransformers: () => {
          return {
            before: [
              require('jest-preset-angular/InlineHtmlStripStylesTransformer').factory({
                compilerModule: require('typescript')
              })
            ]
          };
        }
      }),
      '**/*.html': file => ({
        code: require('ts-jest').process(file.content, file.path, {
          globals: { 'ts-jest': { stringifyContentPathRegex: '\\.html$' } }
        }),
        map: { version: 3, sources: [], names: [], mappings: [] },
        ranges: []
      })
    },

    preprocessors: {
      'src/**/*.js': [
        file =>
          require('@babel/core').transform(file.content, {
            sourceMap: true,
            compact: false,
            filename: file.path,
            presets: [require('babel-preset-jest')]
          })
      ]
    },

    setup: function(wallaby) {
      let jestConfig = require('./package.json').jest;
      delete jestConfig.preset;
      jestConfig = Object.assign(require('jest-preset-angular/jest-preset'), jestConfig);
      jestConfig.transformIgnorePatterns.push('instrumented.*.(jsx?|html)$');
      wallaby.testFramework.configure(jestConfig);
    },

    testFramework: 'jest'
  };
};

If you are setting up Jest from scratch for your Angular project, please read this blog post.

System.js notes

If you are using Angular >=2 with System.js, have a look into the docs section for some useful configuration hints. You also may find some configuration examples for Angular >=2 below.

AngularJS 1.x

You may find a very detailed step by step angular.js 1.x tutorial in our blog. The source code for the blog post is available in this repository.

Another good example of using AngularJS 1.5 with ES6, Webpack & Wallaby is available in this skeleton repository.

angular

AngularJS 1.x templates to JavaScript

If you are looking for the way to compile your AngularJS templates to JavaScript with wallaby.js for your tests, you may have a look at wallaby-ng-html2js-preprocessor preprocessor.

AngularJS 1.x files sorting

If you are looking for the way to sort your source files (due to Angular dependencies between them), so that you could use glob patterns in your config files list as opposed to listing your source files one by one, you may have a look at wallaby-angular-filesort postprocessor.