Closed soupman99 closed 4 years ago
Wallaby's automatic configuration for Angular currently only supports the @angular-devkit/build-angular:karma
builder, whereas your project is using @angular-builders/custom-webpack:karma
.
To configure Wallaby for your project, you may follow our Angular CLI tutorial and in the Select configuration mode section, select Manual
(instead of Automatic
).
npm install wallaby-webpack --save-dev
wallaby.js
in your project root, and change project.architect.test.builder === '@angular-devkit/build-angular:karma'
to project.architect.test.builder === '@angular-builders/custom-webpack:karma'
.The working wallaby.js configuration for your project can be found below. Please note that it is using Chrome, not electron to run your tests. You may also update your configuration to run your tests using electron (described in our 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-builders/custom-webpack: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();
}
};
};
Thanks for this! It works great. An additional question that I've uncovered is how to incorporate tests for my actual electron code as well but I can't seem to get it working.
I've created a repo here: https://github.com/soupman99/wallaby-electron-angular
When I run wallaby my test in electron/test/example.spec.ts don't actually get executed.
However, when I remove let testClass = new TestElectron()
the code executes as expected.
Is this due to wallaby not being able to find electron/test/example.ts
?
Your existing Angular tests are running with Wallaby using Webpack, which means that if you want to run your tests in the electron
directory at the same time, you'll need to configure the electron files to be bundled with Webpack and executed the same way as the Angular project. You can read a little more about how to configure Wallaby to run with webpack here but I have updated your configuration for you to make this work (see below).
The configuration that I had to change/fix was:
1) Set load: false
to all source files and tests processed by webpack (except external files), as they should not be loaded in browser, not by Wallaby.
2) Update patterns to include source files in files
section, and exclude .spec files. (note: I also excluded main.ts as I figured it's not meant to be run/used when testing).
3) Update webpack entryPatterns
to run your test on startup.
Wallaby.js Configuration
module.exports = function(wallaby) {
const wallabyWebpack = require('wallaby-webpack');
const path = require('path');
const fs = require('fs');
const specPattern = '/**/*spec.+(ts|js)';
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-builders/custom-webpack: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|pug)',
load: false
})),
...projects.map(project => ({
pattern: project.sourceRoot + specPattern,
ignore: true
})),
...projects.map(project => ({
pattern: project.sourceRoot + '/**/*.d.ts',
ignore: true
})),
- {pattern: '/electron/**/*.spec.+(ts|js)', ignore: true, load:true},
+ { pattern: "electron/**/*.+(ts|js)", ignore: false, load: false },
+ { pattern: "electron/**/*spec.+(ts|js)", ignore: true },
+ { pattern: "electron/main.ts", ignore: true },
],
tests: [
...projects.map(project => ({
pattern: project.sourceRoot + specPattern,
load: false
})),
- {pattern: '/electron/**/*.spec.+(ts|js)', load: true},
+ { pattern: "electron/**/*spec.+(ts|js)", 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),
+ 'electron/**/*spec.+(ts|js)',
...projects.map(project => project.sourceRoot + specPattern.replace(/ts$/, 'js')),
'electron/**/*spec.+(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: /\.(pug|jade)$/, exclude: /\.(include|partial)\.(pug|jade)$/, loaders: ['apply-loader', { loader: 'pug-loader' }]},
// {test: /\.(include|partial)\.(pug|jade)$/, loader: 'pug-loader'},
{ 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();
}
};
};
Sorry to keep being "that guy"
I've switched over to jest and it seems like I'm very close to having things resolved once and for all.
I've got jest working with angular-cli but not with wallaby.
My electron tests pass when I run wallaby but my angular tests fail with the error
Zone is needed for the async() test helper but could not be found.
I added src/wallaby-test.ts
but still no luck. Am I missing an import somewhere?
I've updated my example repo: https://github.com/soupman99/wallaby-electron-angular
If you're using jest, the best way to get Wallaby up and running is to make sure that npx jest
works from your project root.
Currently you're relying on Angular's ng
command to run your tests which is configuring jest for you under the covers.
I edited ./node_modules/@angular-builders/jest/dist/index.js
to dump the jest configuration that is being passed to jest by the angular jest test runner so that I could create a jest.config.js
file in your project root.
jest.config.js
module.exports = {
globals: {
"ts-jest": {
stringifyContentPathRegex: "\\.html$",
astTransformers: [
"jest-preset-angular/build/InlineFilesTransformer",
"jest-preset-angular/build/StripStylesTransformer",
],
},
},
preset: "jest-preset-angular",
testURL: "https://github.com/@angular-cli-builders",
setupFilesAfterEnv: [
"<rootDir>/node_modules/@angular-builders/jest/dist/jest-config/setup.js",
],
moduleNameMapper: {
"\\.(jpg|jpeg|png)$":
"<rootDir>/node_modules/@angular-builders/jest/dist/jest-config/mock-module.js",
},
testMatch: ["<rootDir>/**/*(*.)@(spec|test).[tj]s?(x)"],
};
After creating the jest configuration, delete your wallaby.js
configuration file and switch to use our automatic configuration feature. At that point everything will work for you.
@smcenlly that works like a charm! Thanks you so much!
I'm trying to add wallaby to an existing starter project.
https://github.com/maximegris/angular-electron
When I try to use auto configuration I get this error
[Error] Failed to load configuration file: Automatic Angular CLI configuration error: None of your projects are configured with @angular-devkit/build-angular:karma builder for test architect. [Error] We've also tried to automatically configure Wallaby.js for other frameworks [Error] Automatic Jest configuration error: Module jest-cli is not found in '/Users/justin/Desktop/_temp/angular-electron'.
How would I go about incorporating wallaby?