Commit b8a84830 authored by Clement Ho's avatar Clement Ho

Merge branch '45806-improve-karma-performance' into 'master'

Fix memory and performance issues in Karma test suite

Closes #45900 and #45806

See merge request gitlab-org/gitlab-ce!18658
parents 71d53ebc 6b1e4ad5
var path = require('path');
var webpack = require('webpack');
var argumentsParser = require('commander');
var webpackConfig = require('./webpack.config.js');
var ROOT_PATH = path.resolve(__dirname, '..');
const path = require('path');
const glob = require('glob');
const chalk = require('chalk');
const webpack = require('webpack');
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
if (webpackConfig.plugins) {
......@@ -15,33 +23,70 @@ if (webpackConfig.plugins) {
});
}
var testFiles = argumentsParser
const specFilters = argumentsParser
.option(
'-f, --filter-spec [filter]',
'Filter run spec files by path. Multiple filters are like a logical OR.',
(val, memo) => {
memo.push(val);
(filter, memo) => {
memo.push(filter, filter.replace(/\/?$/, '/**/*.js'));
return memo;
},
[]
)
.parse(process.argv).filterSpec;
webpackConfig.plugins.push(
new webpack.DefinePlugin({
'process.env.TEST_FILES': JSON.stringify(testFiles),
})
);
if (specFilters.length) {
const specsPath = /^(?:\.[\\\/])?spec[\\\/]javascripts[\\\/]/;
// resolve filters
let filteredSpecFiles = specFilters.map(filter =>
glob
.sync(filter, {
root: ROOT_PATH,
matchBase: true,
})
.filter(path => path.endsWith('spec.js'))
);
// 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.devtool = process.env.BABEL_ENV !== 'coverage' && 'cheap-inline-source-map';
webpackConfig.entry = undefined;
webpackConfig.devtool = 'cheap-inline-source-map';
// Karma configuration
module.exports = function(config) {
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,
browsers: ['ChromeHeadlessCustom'],
customLaunchers: {
......
......@@ -69,6 +69,9 @@ const config = {
test: /\.js$/,
exclude: /(node_modules|vendor\/assets)/,
loader: 'babel-loader',
options: {
cacheDirectory: path.join(ROOT_PATH, 'tmp/cache/babel-loader'),
},
},
{
test: /\.vue$/,
......
......@@ -62,6 +62,7 @@ describe('.methodName', () => {
});
});
```
#### Testing promises
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
// Good
it('tests a promise', (done) => {
it('tests a promise', done => {
promise
.then((data) => {
.then(data => {
expect(data).toBe(asExpected);
})
.then(done)
......@@ -79,10 +80,10 @@ it('tests a promise', (done) => {
});
// Good
it('tests a promise rejection', (done) => {
it('tests a promise rejection', done => {
promise
.then(done.fail)
.catch((error) => {
.catch(error => {
expect(error).toBe(expectedError);
})
.then(done)
......@@ -91,38 +92,37 @@ it('tests a promise rejection', (done) => {
// Bad (missing done callback)
it('tests a promise', () => {
promise
.then((data) => {
expect(data).toBe(asExpected);
})
promise.then(data => {
expect(data).toBe(asExpected);
});
});
// Bad (missing catch)
it('tests a promise', (done) => {
it('tests a promise', done => {
promise
.then((data) => {
.then(data => {
expect(data).toBe(asExpected);
})
.then(done)
.then(done);
});
// Bad (use done.fail in asynchronous tests)
it('tests a promise', (done) => {
it('tests a promise', done => {
promise
.then((data) => {
.then(data => {
expect(data).toBe(asExpected);
})
.then(done)
.catch(fail)
.catch(fail);
});
// Bad (missing catch)
it('tests a promise rejection', (done) => {
it('tests a promise rejection', done => {
promise
.catch((error) => {
.catch(error => {
expect(error).toBe(expectedError);
})
.then(done)
.then(done);
});
```
......@@ -139,7 +139,7 @@ documentation for these methods can be found in the [jasmine introduction page](
Sometimes you may need to spy on a method that is directly imported by another
module. GitLab has a custom `spyOnDependency` method which utilizes
[babel-plugin-rewire](https://github.com/speedskater/babel-plugin-rewire) to
achieve this. It can be used like so:
achieve this. It can be used like so:
```javascript
// my_module.js
......@@ -181,8 +181,8 @@ See this [section][vue-test].
`rake karma` runs the frontend-only (JavaScript) tests.
It consists of two subtasks:
- `rake karma:fixtures` (re-)generates fixtures
- `rake karma:tests` actually executes the tests
* `rake karma:fixtures` (re-)generates fixtures
* `rake karma:tests` actually executes the tests
As long as the fixtures don't change, `rake karma:tests` (or `yarn karma`)
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
```
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
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
features are still unavailable:
- Array.from
- Array.first
- Async functions
- Generators
- Array destructuring
- For..Of
- Symbol/Symbol.iterator
- Spread
* Array.from
* Array.first
* Async functions
* Generators
* Array destructuring
* For..Of
* Symbol/Symbol.iterator
* Spread
Until these are polyfilled appropriately, they should not be used. Please
update this list with additional unsupported features.
......@@ -295,11 +303,11 @@ Scenario: Developer can approve merge request
[jasmine-focus]: https://jasmine.github.io/2.5/focused_specs.html
[jasmine-jquery]: https://github.com/velesin/jasmine-jquery
[karma]: http://karma-runner.github.io/
[vue-test]:https://docs.gitlab.com/ce/development/fe_guide/vue.html#testing-vue-components
[RSpec]: https://github.com/rspec/rspec-rails#feature-specs
[Capybara]: https://github.com/teamcapybara/capybara
[Karma]: http://karma-runner.github.io/
[Jasmine]: https://jasmine.github.io/
[vue-test]: https://docs.gitlab.com/ce/development/fe_guide/vue.html#testing-vue-components
[rspec]: https://github.com/rspec/rspec-rails#feature-specs
[capybara]: https://github.com/teamcapybara/capybara
[karma]: http://karma-runner.github.io/
[jasmine]: https://jasmine.github.io/
---
......
......@@ -84,21 +84,11 @@ beforeEach(() => {
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
const testsContext = require.context('.', true, /_spec$/);
testsContext.keys().forEach(function(path) {
try {
if (testFiles.length === 0 || testFiles.some(p => path.includes(p))) {
testsContext(path);
}
testsContext(path);
} catch (err) {
console.error('[ERROR] Unable to load spec: ', path);
describe('Test bundle', function() {
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment