Skip to content

Separate Functional and Unit Test Modes #166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 17, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ There are three modes available to build a Dojo application, `dist`, `dev` and `
dojo build app --mode dist
```

The built application files are written to the `output/{mode selected}` directory
The built application files are written to the `output/{dist/dev}` directory. The built test files are written to the `output/test/{unit|functionl}` directory.

Note: `dist` is the default mode and so can be run without any arguments, `dojo build app`.

Expand All @@ -51,9 +51,13 @@ The `dist` mode creates a production-ready build.

The `dev` mode creates an application build that has been optimized for debugging and development.

#### Test mode
#### Unit mode

The `test` mode creates bundles that can be used to run the unit and functional tests of the application.
The `unit` mode creates bundles that can be used to run the unit tests of the application.

#### Functional mode

The `functional` mode creates bundles that can be used to run the functional tests of the application.

### Serving the Application

Expand Down Expand Up @@ -126,14 +130,16 @@ Ejecting `@dojo/cli-build-app` will produce the following files under the `confi
- `build-options.json`: the build-specific config options removed from the `.dojorc`
- `ejected.config.js`: the root webpack config that passes the build options to the appropriate mode-specific config based on the `--env.mode` flag's value.
- `base.config.js`: a common configuration used by the mode-specific configs.
- `base.test.config.js`: a common configuration used by the unit and functional modes.
- `dev.config.js`: the configuration used during development.
- `dist.config.js`: the production configuration.
- `test.config.js`: the configuration used when running tests.
- `unit.config.js`: the configuration used when running unit tests.
- `functional.config.js`: the configuration used when running functional tests.

As already noted, the dojorc's `build-app` options are moved to `config/build-app/build-options.json` after ejecting. Further, the modes are specified using webpack's `env` flag (e.g., `--env.mode=dev`), defaulting to `dist`. You can run a build using webpack with:

```bash
node_modules/.bin/webpack --config=config/build-app/ejected.config.js --env.mode={dev|dist|test}
node_modules/.bin/webpack --config=config/build-app/ejected.config.js --env.mode={dev|dist|unit|functional}
```

### Configuration
Expand Down
8 changes: 5 additions & 3 deletions src/base.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,16 @@ export default function webpackConfigFactory(args: any): WebpackConfiguration {
},
[] as string[]
);
const singleBundle =
args.singleBundle || args.mode === 'unit' || args.mode === 'functional' || args.mode === 'test';

const customTransformers: any[] = [];

if (lazyModules.length > 0 && !args.singleBundle) {
if (lazyModules.length > 0 && !singleBundle) {
customTransformers.push(registryTransformer(basePath, lazyModules));
}

if (!args.legacy && !args.singleBundle) {
if (!args.legacy && !singleBundle) {
customTransformers.push(importTransformer(basePath, args.bundles));
}

Expand Down Expand Up @@ -261,7 +263,7 @@ export default function webpackConfigFactory(args: any): WebpackConfiguration {
devtool: 'source-map',
watchOptions: { ignored: /node_modules/ },
plugins: removeEmpty([
args.singleBundle &&
singleBundle &&
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1
}),
Expand Down
39 changes: 4 additions & 35 deletions src/test.config.ts → src/base.test.config.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,10 @@
import baseConfigFactory from './base.config';
import * as path from 'path';
import * as webpack from 'webpack';
import * as globby from 'globby';
import * as CleanWebpackPlugin from 'clean-webpack-plugin';
import { WebpackConfiguration } from './interfaces';
import * as ExtractTextPlugin from 'extract-text-webpack-plugin';

const basePath = process.cwd();

function webpackConfig(args: any): webpack.Configuration {
function webpackConfig(args: any): WebpackConfiguration {
const config = baseConfigFactory(args);
const { plugins, output, module } = config;
config.entry = () => {
const unit = globby
.sync([`${basePath}/tests/unit/**/*.ts`])
.map((filename: string) => filename.replace(/\.ts$/, ''));

const functional = globby
.sync([`${basePath}/tests/functional/**/*.ts`])
.map((filename: string) => filename.replace(/\.ts$/, ''));

const tests: any = {};

if (unit.length) {
tests.unit = unit;
}

if (functional.length) {
tests.functional = functional;
}

return tests;
};
const { plugins, module } = config;
const externals: any[] = (config.externals as any[]) || [];

const instrumenterOptions = args.legacy ? {} : { esModules: true };
Expand All @@ -41,8 +15,7 @@ function webpackConfig(args: any): webpack.Configuration {
(plugin as any).options = { ...(plugin as any).options, disable: true };
}
return plugin;
}),
new CleanWebpackPlugin(['test'], { root: output.path, verbose: false })
})
];

module.rules = module.rules.map((rule) => {
Expand Down Expand Up @@ -74,10 +47,6 @@ function webpackConfig(args: any): webpack.Configuration {
externals.push(/^intern/);
config.externals = externals;
config.devtool = 'inline-source-map';
config.output = {
...output,
path: path.join(output.path, 'test')
};
return config;
}

Expand Down
11 changes: 7 additions & 4 deletions src/ejected.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import * as webpack from 'webpack';

import devConfigFactory from './dev.config';
import distConfigFactory from './dist.config';
import testConfigFactory from './test.config';
import unitConfigFactory from './unit.config';
import functionalConfigFactory from './functional.config';

export interface EnvOptions {
mode?: 'dev' | 'dist' | 'test';
mode?: 'dev' | 'dist' | 'unit' | 'functional';
}

function webpackConfig(env: EnvOptions = {}): webpack.Configuration {
Expand All @@ -14,8 +15,10 @@ function webpackConfig(env: EnvOptions = {}): webpack.Configuration {
let config: webpack.Configuration;
if (mode === 'dev') {
config = devConfigFactory(rc);
} else if (mode === 'test') {
config = testConfigFactory(rc);
} else if (mode === 'unit') {
config = unitConfigFactory(rc);
} else if (mode === 'functional') {
config = functionalConfigFactory(rc);
} else {
config = distConfigFactory(rc);
}
Expand Down
36 changes: 36 additions & 0 deletions src/functional.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import baseTestConfigFactory from './base.test.config';
import * as path from 'path';
import { WebpackConfiguration } from './interfaces';
import * as globby from 'globby';
import * as CleanWebpackPlugin from 'clean-webpack-plugin';

const basePath = process.cwd();

function webpackConfig(args: any): WebpackConfiguration {
const config = baseTestConfigFactory(args);
const { output, plugins } = config;
config.entry = () => {
const functional = globby
.sync([`${basePath}/tests/functional/**/*.ts`])
.map((filename: string) => filename.replace(/\.ts$/, ''));

const tests: any = {};

if (functional.length) {
tests.functional = functional;
}

return tests;
};
config.plugins = [
...plugins,
new CleanWebpackPlugin(['functional'], { root: path.join(output.path, 'test'), verbose: false })
];
config.output = {
...output,
path: path.join(output.path, 'test', 'functional')
};
return config;
}

export default webpackConfig;
28 changes: 20 additions & 8 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import * as history from 'connect-history-api-fallback';

const pkgDir = require('pkg-dir');
import devConfigFactory from './dev.config';
import testConfigFactory from './test.config';
import unitConfigFactory from './unit.config';
import functionalConfigFactory from './functional.config';
import distConfigFactory from './dist.config';
import logger from './logger';
import { moveBuildOptions } from './util/eject';
Expand All @@ -21,6 +22,8 @@ const fixMultipleWatchTrigger = require('webpack-mild-compile');
const hotMiddleware = require('webpack-hot-middleware');
const webpackMiddleware = require('webpack-dev-middleware');

const testModes = ['test', 'unit', 'functional'];

function createCompiler(config: webpack.Configuration) {
const compiler = webpack(config);
fixMultipleWatchTrigger(compiler);
Expand Down Expand Up @@ -57,6 +60,11 @@ function build(config: webpack.Configuration, args: any) {
return;
}
}
if (args.mode === 'test') {
console.warn(
'Using `--mode=test` is deprecated and has only built the unit test bundle. This mode will be removed in the next major release, please use `unit` or `functional` explicitly instead.'
);
}
resolve(args.serve || process.exit(0));
});
});
Expand Down Expand Up @@ -214,7 +222,7 @@ const command: Command = {
describe: 'the output mode',
alias: 'm',
default: 'dist',
choices: ['dist', 'dev', 'test']
choices: ['dist', 'dev', 'test', 'unit', 'functional']
});

options('watch', {
Expand Down Expand Up @@ -275,15 +283,17 @@ const command: Command = {
remainingArgs = { ...remainingArgs, features: { ...remainingArgs.features, ...feature } };
if (args.mode === 'dev') {
config = devConfigFactory(remainingArgs);
} else if (args.mode === 'test') {
config = testConfigFactory(remainingArgs);
} else if (args.mode === 'unit' || args.mode === 'test') {
config = unitConfigFactory(remainingArgs);
} else if (args.mode === 'functional') {
config = functionalConfigFactory(remainingArgs);
} else {
config = distConfigFactory(remainingArgs);
}

if (args.serve) {
if (args.mode === 'test') {
return Promise.reject(new Error('Cannot use `--serve` with `--mode=test`'));
if (testModes.indexOf(args.mode) !== -1) {
return Promise.reject(new Error(`Cannot use \`--serve\` with \`--mode=${args.mode}\``));
}
return serve(config, args);
}
Expand All @@ -304,15 +314,17 @@ const command: Command = {
files: [
moveBuildOptions(`${this.group}-${this.name}`),
'./base.config.js',
'./base.test.config.js',
'./dev.config.js',
'./dist.config.js',
'./ejected.config.js',
'./test.config.js'
'./unit.config.js',
'./functional.config.js'
]
},
hints: [
`to build run ${chalk.underline(
'./node_modules/.bin/webpack --config ./config/build-app/ejected.config.js --env.mode={dev|dist|test}'
'./node_modules/.bin/webpack --config ./config/build-app/ejected.config.js --env.mode={dev|dist|unit|functional}'
)}`
],
npm: {
Expand Down
36 changes: 36 additions & 0 deletions src/unit.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import baseTestConfigFactory from './base.test.config';
import * as path from 'path';
import * as globby from 'globby';
import * as CleanWebpackPlugin from 'clean-webpack-plugin';
import { WebpackConfiguration } from './interfaces';

const basePath = process.cwd();

function webpackConfig(args: any): WebpackConfiguration {
const config = baseTestConfigFactory(args);
const { output, plugins } = config;
config.entry = () => {
const unit = globby
.sync([`${basePath}/tests/unit/**/*.ts`])
.map((filename: string) => filename.replace(/\.ts$/, ''));

const tests: any = {};

if (unit.length) {
tests.unit = unit;
}

return tests;
};
config.plugins = [
...plugins,
new CleanWebpackPlugin(['unit'], { root: path.join(output.path, 'test'), verbose: false })
];
config.output = {
...output,
path: path.join(output.path, 'test', 'unit')
};
return config;
}

export default webpackConfig;
31 changes: 23 additions & 8 deletions tests/unit/ejected.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@ const configJson: any = { bundles: {} };
let mockModule: MockModule;
let mockDevConfig: any;
let mockDistConfig: any;
let mockTestConfig: any;
let mockUnitTestConfig: any;
let mockFunctionalTestConfig: any;

describe('ejected config', () => {
beforeEach(() => {
mockModule = new MockModule('../../src/ejected.config', require);
mockModule.dependencies(['./dev.config', './dist.config', './test.config', './build-options.json']);
mockModule.dependencies([
'./dev.config',
'./dist.config',
'./unit.config',
'./functional.config',
'./build-options.json'
]);

const configs = ['dev', 'dist', 'test'].map((name) => {
const configs = ['dev', 'dist', 'unit', 'functional'].map((name) => {
const config = mockModule.getMock(`./${name}.config`);
config.default = stub();
return config.default;
Expand All @@ -23,7 +30,8 @@ describe('ejected config', () => {
Object.assign(mockModule.getMock('./build-options.json'), configJson);
mockDevConfig = configs[0];
mockDistConfig = configs[1];
mockTestConfig = configs[2];
mockUnitTestConfig = configs[2];
mockFunctionalTestConfig = configs[3];
});

afterEach(() => {
Expand All @@ -44,10 +52,17 @@ describe('ejected config', () => {
assert.isTrue(mockDistConfig.calledWith(configJson));
});

it('can run test mode', () => {
it('can run unit mode', () => {
const config = mockModule.getModuleUnderTest();
config({ mode: 'test' });
assert.isTrue(mockTestConfig.calledOnce);
assert.isTrue(mockTestConfig.calledWith(configJson));
config({ mode: 'unit' });
assert.isTrue(mockUnitTestConfig.calledOnce);
assert.isTrue(mockUnitTestConfig.calledWith(configJson));
});

it('can run functional mode', () => {
const config = mockModule.getModuleUnderTest();
config({ mode: 'functional' });
assert.isTrue(mockFunctionalTestConfig.calledOnce);
assert.isTrue(mockFunctionalTestConfig.calledWith(configJson));
});
});
Loading