Fix memory and performance issues in Karma test suite
This commit is contained in:
parent
71d53ebcf8
commit
6b1e4ad5e8
|
@ -1,8 +1,16 @@
|
||||||
var path = require('path');
|
const path = require('path');
|
||||||
var webpack = require('webpack');
|
const glob = require('glob');
|
||||||
var argumentsParser = require('commander');
|
const chalk = require('chalk');
|
||||||
var webpackConfig = require('./webpack.config.js');
|
const webpack = require('webpack');
|
||||||
var ROOT_PATH = path.resolve(__dirname, '..');
|
const argumentsParser = require('commander');
|
||||||
|
const webpackConfig = require('./webpack.config.js');
|
||||||
|
|
||||||
|
const ROOT_PATH = path.resolve(__dirname, '..');
|
||||||
|
|
||||||
|
function fatalError(message) {
|
||||||
|
console.error(chalk.red(`\nError: ${message}\n`));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
// remove problematic plugins
|
// remove problematic plugins
|
||||||
if (webpackConfig.plugins) {
|
if (webpackConfig.plugins) {
|
||||||
|
@ -15,33 +23,70 @@ if (webpackConfig.plugins) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var testFiles = argumentsParser
|
const specFilters = argumentsParser
|
||||||
.option(
|
.option(
|
||||||
'-f, --filter-spec [filter]',
|
'-f, --filter-spec [filter]',
|
||||||
'Filter run spec files by path. Multiple filters are like a logical OR.',
|
'Filter run spec files by path. Multiple filters are like a logical OR.',
|
||||||
(val, memo) => {
|
(filter, memo) => {
|
||||||
memo.push(val);
|
memo.push(filter, filter.replace(/\/?$/, '/**/*.js'));
|
||||||
return memo;
|
return memo;
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
.parse(process.argv).filterSpec;
|
.parse(process.argv).filterSpec;
|
||||||
|
|
||||||
webpackConfig.plugins.push(
|
if (specFilters.length) {
|
||||||
new webpack.DefinePlugin({
|
const specsPath = /^(?:\.[\\\/])?spec[\\\/]javascripts[\\\/]/;
|
||||||
'process.env.TEST_FILES': JSON.stringify(testFiles),
|
|
||||||
|
// resolve filters
|
||||||
|
let filteredSpecFiles = specFilters.map(filter =>
|
||||||
|
glob
|
||||||
|
.sync(filter, {
|
||||||
|
root: ROOT_PATH,
|
||||||
|
matchBase: true,
|
||||||
})
|
})
|
||||||
|
.filter(path => path.endsWith('spec.js'))
|
||||||
);
|
);
|
||||||
|
|
||||||
webpackConfig.devtool = process.env.BABEL_ENV !== 'coverage' && 'cheap-inline-source-map';
|
// flatten
|
||||||
|
filteredSpecFiles = Array.prototype.concat.apply([], filteredSpecFiles);
|
||||||
|
|
||||||
|
// remove duplicates
|
||||||
|
filteredSpecFiles = [...new Set(filteredSpecFiles)];
|
||||||
|
|
||||||
|
if (filteredSpecFiles.length < 1) {
|
||||||
|
fatalError('Your filter did not match any test files.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filteredSpecFiles.every(file => specsPath.test(file))) {
|
||||||
|
fatalError('Test files must be located within /spec/javascripts.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const newContext = filteredSpecFiles.reduce((context, file) => {
|
||||||
|
const relativePath = file.replace(specsPath, '');
|
||||||
|
context[file] = `./${relativePath}`;
|
||||||
|
return context;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
webpackConfig.plugins.push(
|
||||||
|
new webpack.ContextReplacementPlugin(
|
||||||
|
/spec[\\\/]javascripts$/,
|
||||||
|
path.join(ROOT_PATH, 'spec/javascripts'),
|
||||||
|
newContext
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
webpackConfig.entry = undefined;
|
||||||
|
webpackConfig.devtool = 'cheap-inline-source-map';
|
||||||
|
|
||||||
// Karma configuration
|
// Karma configuration
|
||||||
module.exports = function(config) {
|
module.exports = function(config) {
|
||||||
process.env.TZ = 'Etc/UTC';
|
process.env.TZ = 'Etc/UTC';
|
||||||
|
|
||||||
var progressReporter = process.env.CI ? 'mocha' : 'progress';
|
const progressReporter = process.env.CI ? 'mocha' : 'progress';
|
||||||
|
|
||||||
var karmaConfig = {
|
const karmaConfig = {
|
||||||
basePath: ROOT_PATH,
|
basePath: ROOT_PATH,
|
||||||
browsers: ['ChromeHeadlessCustom'],
|
browsers: ['ChromeHeadlessCustom'],
|
||||||
customLaunchers: {
|
customLaunchers: {
|
||||||
|
|
|
@ -69,6 +69,9 @@ const config = {
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
exclude: /(node_modules|vendor\/assets)/,
|
exclude: /(node_modules|vendor\/assets)/,
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
cacheDirectory: path.join(ROOT_PATH, 'tmp/cache/babel-loader'),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.vue$/,
|
test: /\.vue$/,
|
||||||
|
|
|
@ -62,6 +62,7 @@ describe('.methodName', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Testing promises
|
#### Testing promises
|
||||||
|
|
||||||
When testing Promises you should always make sure that the test is asynchronous and rejections are handled.
|
When testing Promises you should always make sure that the test is asynchronous and rejections are handled.
|
||||||
|
@ -69,9 +70,9 @@ Your Promise chain should therefore end with a call of the `done` callback and `
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Good
|
// Good
|
||||||
it('tests a promise', (done) => {
|
it('tests a promise', done => {
|
||||||
promise
|
promise
|
||||||
.then((data) => {
|
.then(data => {
|
||||||
expect(data).toBe(asExpected);
|
expect(data).toBe(asExpected);
|
||||||
})
|
})
|
||||||
.then(done)
|
.then(done)
|
||||||
|
@ -79,10 +80,10 @@ it('tests a promise', (done) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Good
|
// Good
|
||||||
it('tests a promise rejection', (done) => {
|
it('tests a promise rejection', done => {
|
||||||
promise
|
promise
|
||||||
.then(done.fail)
|
.then(done.fail)
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
expect(error).toBe(expectedError);
|
expect(error).toBe(expectedError);
|
||||||
})
|
})
|
||||||
.then(done)
|
.then(done)
|
||||||
|
@ -91,38 +92,37 @@ it('tests a promise rejection', (done) => {
|
||||||
|
|
||||||
// Bad (missing done callback)
|
// Bad (missing done callback)
|
||||||
it('tests a promise', () => {
|
it('tests a promise', () => {
|
||||||
promise
|
promise.then(data => {
|
||||||
.then((data) => {
|
|
||||||
expect(data).toBe(asExpected);
|
expect(data).toBe(asExpected);
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bad (missing catch)
|
// Bad (missing catch)
|
||||||
it('tests a promise', (done) => {
|
it('tests a promise', done => {
|
||||||
promise
|
promise
|
||||||
.then((data) => {
|
.then(data => {
|
||||||
expect(data).toBe(asExpected);
|
expect(data).toBe(asExpected);
|
||||||
})
|
})
|
||||||
.then(done)
|
.then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bad (use done.fail in asynchronous tests)
|
// Bad (use done.fail in asynchronous tests)
|
||||||
it('tests a promise', (done) => {
|
it('tests a promise', done => {
|
||||||
promise
|
promise
|
||||||
.then((data) => {
|
.then(data => {
|
||||||
expect(data).toBe(asExpected);
|
expect(data).toBe(asExpected);
|
||||||
})
|
})
|
||||||
.then(done)
|
.then(done)
|
||||||
.catch(fail)
|
.catch(fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bad (missing catch)
|
// Bad (missing catch)
|
||||||
it('tests a promise rejection', (done) => {
|
it('tests a promise rejection', done => {
|
||||||
promise
|
promise
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
expect(error).toBe(expectedError);
|
expect(error).toBe(expectedError);
|
||||||
})
|
})
|
||||||
.then(done)
|
.then(done);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -181,8 +181,8 @@ See this [section][vue-test].
|
||||||
`rake karma` runs the frontend-only (JavaScript) tests.
|
`rake karma` runs the frontend-only (JavaScript) tests.
|
||||||
It consists of two subtasks:
|
It consists of two subtasks:
|
||||||
|
|
||||||
- `rake karma:fixtures` (re-)generates fixtures
|
* `rake karma:fixtures` (re-)generates fixtures
|
||||||
- `rake karma:tests` actually executes the tests
|
* `rake karma:tests` actually executes the tests
|
||||||
|
|
||||||
As long as the fixtures don't change, `rake karma:tests` (or `yarn karma`)
|
As long as the fixtures don't change, `rake karma:tests` (or `yarn karma`)
|
||||||
is sufficient (and saves you some time).
|
is sufficient (and saves you some time).
|
||||||
|
@ -217,6 +217,14 @@ yarn karma-start --filter-spec profile/account/components/
|
||||||
yarn karma-start -f vue_shared -f vue_mr_widget
|
yarn karma-start -f vue_shared -f vue_mr_widget
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also use glob syntax to match files. Remember to put quotes around the
|
||||||
|
glob otherwise your shell may split it into multiple arguments:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all specs named `file_spec` within the IDE subdirectory
|
||||||
|
yarn karma -f 'spec/javascripts/ide/**/file_spec.js'
|
||||||
|
```
|
||||||
|
|
||||||
## RSpec feature integration tests
|
## RSpec feature integration tests
|
||||||
|
|
||||||
Information on setting up and running RSpec integration tests with
|
Information on setting up and running RSpec integration tests with
|
||||||
|
@ -231,14 +239,14 @@ supported by the PhantomJS test runner which is used for both Karma and RSpec
|
||||||
tests. We polyfill some JavaScript objects for older browsers, but some
|
tests. We polyfill some JavaScript objects for older browsers, but some
|
||||||
features are still unavailable:
|
features are still unavailable:
|
||||||
|
|
||||||
- Array.from
|
* Array.from
|
||||||
- Array.first
|
* Array.first
|
||||||
- Async functions
|
* Async functions
|
||||||
- Generators
|
* Generators
|
||||||
- Array destructuring
|
* Array destructuring
|
||||||
- For..Of
|
* For..Of
|
||||||
- Symbol/Symbol.iterator
|
* Symbol/Symbol.iterator
|
||||||
- Spread
|
* Spread
|
||||||
|
|
||||||
Until these are polyfilled appropriately, they should not be used. Please
|
Until these are polyfilled appropriately, they should not be used. Please
|
||||||
update this list with additional unsupported features.
|
update this list with additional unsupported features.
|
||||||
|
@ -296,10 +304,10 @@ Scenario: Developer can approve merge request
|
||||||
[jasmine-jquery]: https://github.com/velesin/jasmine-jquery
|
[jasmine-jquery]: https://github.com/velesin/jasmine-jquery
|
||||||
[karma]: http://karma-runner.github.io/
|
[karma]: http://karma-runner.github.io/
|
||||||
[vue-test]: https://docs.gitlab.com/ce/development/fe_guide/vue.html#testing-vue-components
|
[vue-test]: https://docs.gitlab.com/ce/development/fe_guide/vue.html#testing-vue-components
|
||||||
[RSpec]: https://github.com/rspec/rspec-rails#feature-specs
|
[rspec]: https://github.com/rspec/rspec-rails#feature-specs
|
||||||
[Capybara]: https://github.com/teamcapybara/capybara
|
[capybara]: https://github.com/teamcapybara/capybara
|
||||||
[Karma]: http://karma-runner.github.io/
|
[karma]: http://karma-runner.github.io/
|
||||||
[Jasmine]: https://jasmine.github.io/
|
[jasmine]: https://jasmine.github.io/
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -98,10 +98,11 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"axios-mock-adapter": "^1.10.0",
|
"axios-mock-adapter": "^1.10.0",
|
||||||
"babel-eslint": "^8.0.2",
|
"babel-eslint": "^8.0.2",
|
||||||
"babel-plugin-istanbul": "^4.1.5",
|
"babel-plugin-istanbul": "^4.1.6",
|
||||||
"babel-plugin-rewire": "^1.1.0",
|
"babel-plugin-rewire": "^1.1.0",
|
||||||
"babel-template": "^6.26.0",
|
"babel-template": "^6.26.0",
|
||||||
"babel-types": "^6.26.0",
|
"babel-types": "^6.26.0",
|
||||||
|
"chalk": "^2.4.1",
|
||||||
"commander": "^2.15.1",
|
"commander": "^2.15.1",
|
||||||
"eslint": "^3.18.0",
|
"eslint": "^3.18.0",
|
||||||
"eslint-config-airbnb-base": "^10.0.1",
|
"eslint-config-airbnb-base": "^10.0.1",
|
||||||
|
@ -116,13 +117,13 @@
|
||||||
"istanbul": "^0.4.5",
|
"istanbul": "^0.4.5",
|
||||||
"jasmine-core": "^2.9.0",
|
"jasmine-core": "^2.9.0",
|
||||||
"jasmine-jquery": "^2.1.1",
|
"jasmine-jquery": "^2.1.1",
|
||||||
"karma": "^2.0.0",
|
"karma": "^2.0.2",
|
||||||
"karma-chrome-launcher": "^2.2.0",
|
"karma-chrome-launcher": "^2.2.0",
|
||||||
"karma-coverage-istanbul-reporter": "^1.4.1",
|
"karma-coverage-istanbul-reporter": "^1.4.2",
|
||||||
"karma-jasmine": "^1.1.1",
|
"karma-jasmine": "^1.1.1",
|
||||||
"karma-mocha-reporter": "^2.2.5",
|
"karma-mocha-reporter": "^2.2.5",
|
||||||
"karma-sourcemap-loader": "^0.3.7",
|
"karma-sourcemap-loader": "^0.3.7",
|
||||||
"karma-webpack": "2.0.7",
|
"karma-webpack": "3.0.0",
|
||||||
"nodemon": "^1.15.1",
|
"nodemon": "^1.15.1",
|
||||||
"prettier": "1.11.1",
|
"prettier": "1.11.1",
|
||||||
"webpack-dev-server": "^2.11.2"
|
"webpack-dev-server": "^2.11.2"
|
||||||
|
|
|
@ -84,21 +84,11 @@ beforeEach(() => {
|
||||||
|
|
||||||
const axiosDefaultAdapter = getDefaultAdapter();
|
const axiosDefaultAdapter = getDefaultAdapter();
|
||||||
|
|
||||||
let testFiles = process.env.TEST_FILES || [];
|
|
||||||
if (testFiles.length > 0) {
|
|
||||||
testFiles = testFiles.map(path => path.replace(/^spec\/javascripts\//, '').replace(/\.js$/, ''));
|
|
||||||
console.log(`Running only tests matching: ${testFiles}`);
|
|
||||||
} else {
|
|
||||||
console.log('Running all tests');
|
|
||||||
}
|
|
||||||
|
|
||||||
// render all of our tests
|
// render all of our tests
|
||||||
const testsContext = require.context('.', true, /_spec$/);
|
const testsContext = require.context('.', true, /_spec$/);
|
||||||
testsContext.keys().forEach(function(path) {
|
testsContext.keys().forEach(function(path) {
|
||||||
try {
|
try {
|
||||||
if (testFiles.length === 0 || testFiles.some(p => path.includes(p))) {
|
|
||||||
testsContext(path);
|
testsContext(path);
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[ERROR] Unable to load spec: ', path);
|
console.error('[ERROR] Unable to load spec: ', path);
|
||||||
describe('Test bundle', function() {
|
describe('Test bundle', function() {
|
||||||
|
|
Loading…
Reference in New Issue