Skip to content

Commit 5300200

Browse files
authored
refactor(*): break up into individual modules (#474)
this should make it much easier to comprehend and write tests for karma-webpack. there is one change in how the KarmaWebpackController is managed, we now instantiate this in the preprocessor phase and propagate the value within the karma config object as a private variable. This allows for breaking the framework and preprocessor into separates modules and has the added benefit of being able to run multiple times in a given session without sharing mutable state. This allows integrations tests to be run in parallel as well as multiple times which was previously not possible. Fixes N/A
1 parent 8ad09d1 commit 5300200

20 files changed

+264
-200
lines changed

.eslintrc.js

+4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
module.exports = {
22
root: true,
3+
globals: {
4+
"jasmine": true,
5+
},
36
plugins: ['prettier'],
47
extends: ['@webpack-contrib/eslint-config-webpack'],
58
rules: {
69
"consistent-return": "off",
10+
"camelcase": "off",
711
"no-console": "off",
812
"no-param-reassign": "off",
913
"no-underscore-dangle": "off",

lib/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
module.exports = require('./karma-webpack');
1+
module.exports = require('./karma/plugin');
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,17 @@
1-
const path = require('path');
2-
const fs = require('fs');
3-
const os = require('os');
4-
51
const webpack = require('webpack');
62
const merge = require('webpack-merge');
73

8-
class KarmaSyncPlugin {
9-
constructor(options) {
10-
this.karmaEmitter = options.karmaEmitter;
11-
this.controller = options.controller;
12-
}
4+
const KW_WebpackPlugin = require('../webpack/plugin');
5+
const DefaultWebpackOptionsFactory = require('../webpack/defaults');
136

14-
apply(compiler) {
15-
this.compiler = compiler;
16-
17-
// webpack bundles are finished
18-
compiler.hooks.done.tap('KarmaSyncPlugin', async (stats) => {
19-
// read generated file content and store for karma preprocessor
20-
this.controller.bundlesContent = {};
21-
stats.toJson().assets.forEach((webpackFileObj) => {
22-
const filePath = `${compiler.options.output.path}/${webpackFileObj.name}`;
23-
this.controller.bundlesContent[webpackFileObj.name] = fs.readFileSync(
24-
filePath,
25-
'utf-8'
26-
);
27-
});
28-
29-
// karma refresh
30-
this.karmaEmitter.refreshFiles();
31-
});
7+
class KW_Controller {
8+
constructor() {
9+
this.isActive = false;
10+
this.bundlesContent = {};
11+
this.hasBeenBuiltAtLeastOnce = false;
12+
this.webpackOptions = DefaultWebpackOptionsFactory.create();
3213
}
33-
}
3414

35-
const defaultWebpackOptions = {
36-
mode: 'development',
37-
output: {
38-
filename: '[name].js',
39-
// eslint-disable-next-line prettier/prettier
40-
path: path.join(os.tmpdir(), '_karma_webpack_') + Math.floor(Math.random() * 1000000),
41-
},
42-
stats: {
43-
modules: false,
44-
colors: true,
45-
},
46-
watch: false,
47-
optimization: {
48-
runtimeChunk: 'single',
49-
splitChunks: {
50-
chunks: 'all',
51-
minSize: 0,
52-
cacheGroups: {
53-
commons: {
54-
name: 'commons',
55-
chunks: 'all',
56-
minChunks: 1,
57-
},
58-
},
59-
},
60-
},
61-
plugins: [],
62-
// Something like this will be auto added by this.configure()
63-
// entry: {
64-
// 'foo-one.test.js': 'path/to/test/foo-one.test.js',
65-
// 'foo-two.test.js': 'path/to/test/foo-two.test.js',
66-
// },
67-
// plugins: [
68-
// new KarmaSyncPlugin()
69-
// ],
70-
};
71-
72-
class KarmaWebpackController {
7315
set webpackOptions(options) {
7416
this.__webpackOptions = options;
7517
}
@@ -78,11 +20,15 @@ class KarmaWebpackController {
7820
return this.__webpackOptions;
7921
}
8022

23+
updateWebpackOptions(newOptions) {
24+
this.webpackOptions = merge(this.webpackOptions, newOptions);
25+
}
26+
8127
set karmaEmitter(emitter) {
8228
this.__karmaEmitter = emitter;
8329

8430
this.__webpackOptions.plugins.push(
85-
new KarmaSyncPlugin({
31+
new KW_WebpackPlugin({
8632
karmaEmitter: emitter,
8733
controller: this,
8834
})
@@ -97,13 +43,6 @@ class KarmaWebpackController {
9743
return this.webpackOptions.output.path;
9844
}
9945

100-
constructor() {
101-
this.isActive = false;
102-
this.bundlesContent = {};
103-
this.hasBeenBuiltAtLeastOnce = false;
104-
this.webpackOptions = defaultWebpackOptions;
105-
}
106-
10746
setupExitHandler(compiler) {
10847
this.karmaEmitter.once('exit', (done) => {
10948
compiler.close(() => {
@@ -113,10 +52,6 @@ class KarmaWebpackController {
11352
});
11453
}
11554

116-
updateWebpackOptions(newOptions) {
117-
this.webpackOptions = merge(this.webpackOptions, newOptions);
118-
}
119-
12055
async bundle() {
12156
if (this.isActive === false && this.hasBeenBuiltAtLeastOnce === false) {
12257
console.log('Webpack bundling...');
@@ -169,8 +104,4 @@ class KarmaWebpackController {
169104
}
170105
}
171106

172-
module.exports = {
173-
KarmaSyncPlugin,
174-
KarmaWebpackController,
175-
defaultWebpackOptions,
176-
};
107+
module.exports = KW_Controller;

lib/karma-webpack/framework.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
function KW_Framework(config) {
5+
// This controller is instantiated and set during the preprocessor phase.
6+
const controller = config.__karmaWebpackController;
7+
const commonsPath = path.join(controller.outputPath, 'commons.js');
8+
const runtimePath = path.join(controller.outputPath, 'runtime.js');
9+
10+
// make sure tmp folder exists
11+
if (!fs.existsSync(controller.outputPath)) {
12+
fs.mkdirSync(controller.outputPath);
13+
}
14+
15+
// create dummy files for commons.js and runtime.js so they get included by karma
16+
fs.closeSync(fs.openSync(commonsPath, 'w'));
17+
fs.closeSync(fs.openSync(runtimePath, 'w'));
18+
19+
// register for karma
20+
config.files.unshift({
21+
pattern: commonsPath,
22+
included: true,
23+
served: true,
24+
watched: false,
25+
});
26+
config.files.unshift({
27+
pattern: runtimePath,
28+
included: true,
29+
served: true,
30+
watched: false,
31+
});
32+
}
33+
34+
KW_Framework.$inject = ['config'];
35+
36+
module.exports = KW_Framework;

lib/karma-webpack.js renamed to lib/karma-webpack/preprocessor.js

+8-45
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,12 @@
11
const path = require('path');
2-
const fs = require('fs');
32

43
const glob = require('glob');
54
const minimatch = require('minimatch');
65

7-
const { ensureWebpackFrameworkSet } = require('./karma/karmaConfigValidator');
6+
const { ensureWebpackFrameworkSet } = require('../karma/validation');
7+
const { hash } = require('../utils/hash');
88

9-
const { hash } = require('./utils/hash');
10-
11-
const { KarmaWebpackController } = require('./KarmaWebpackController');
12-
13-
const controller = new KarmaWebpackController();
14-
15-
function registerExtraWebpackFiles(config, _controller) {
16-
const localController = _controller || controller;
17-
const commonsPath = path.join(localController.outputPath, 'commons.js');
18-
const runtimePath = path.join(localController.outputPath, 'runtime.js');
19-
20-
// make sure tmp folder exists
21-
if (!fs.existsSync(localController.outputPath)) {
22-
fs.mkdirSync(localController.outputPath);
23-
}
24-
25-
// create dummy files for commons.js and runtime.js so they get included by karma
26-
fs.closeSync(fs.openSync(commonsPath, 'w'));
27-
fs.closeSync(fs.openSync(runtimePath, 'w'));
28-
29-
// register for karma
30-
config.files.unshift({
31-
pattern: commonsPath,
32-
included: true,
33-
served: true,
34-
watched: false,
35-
});
36-
config.files.unshift({
37-
pattern: runtimePath,
38-
included: true,
39-
served: true,
40-
watched: false,
41-
});
42-
}
9+
const KW_Controller = require('./controller');
4310

4411
function getPathKey(filePath, withExtension = false) {
4512
const pathParts = path.parse(filePath);
@@ -81,7 +48,9 @@ function configToWebpackEntries(config) {
8148
return webpackEntries;
8249
}
8350

84-
function preprocessorFactory(config, emitter) {
51+
function KW_Preprocessor(config, emitter) {
52+
const controller = new KW_Controller();
53+
config.__karmaWebpackController = controller;
8554
ensureWebpackFrameworkSet(config);
8655

8756
// one time setup
@@ -118,12 +87,6 @@ function preprocessorFactory(config, emitter) {
11887
};
11988
}
12089

121-
registerExtraWebpackFiles.$inject = ['config'];
122-
preprocessorFactory.$inject = ['config', 'emitter'];
90+
KW_Preprocessor.$inject = ['config', 'emitter'];
12391

124-
module.exports = {
125-
'preprocessor:webpack': ['factory', preprocessorFactory],
126-
'framework:webpack': ['factory', registerExtraWebpackFiles],
127-
registerExtraWebpackFiles,
128-
configToWebpackEntries,
129-
};
92+
module.exports = KW_Preprocessor;

lib/karma/plugin.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const KW_Framework = require('../karma-webpack/framework');
2+
const KW_Preprocessor = require('../karma-webpack/preprocessor');
3+
4+
const KW_KarmaPlugin = {
5+
'preprocessor:webpack': ['factory', KW_Preprocessor],
6+
'framework:webpack': ['factory', KW_Framework],
7+
};
8+
9+
module.exports = KW_KarmaPlugin;
File renamed without changes.

lib/webpack/defaults.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const path = require('path');
2+
const os = require('os');
3+
4+
function create() {
5+
return {
6+
mode: 'development',
7+
output: {
8+
filename: '[name].js',
9+
// eslint-disable-next-line prettier/prettier
10+
path: path.join(os.tmpdir(), '_karma_webpack_') + Math.floor(Math.random() * 1000000),
11+
},
12+
stats: {
13+
modules: false,
14+
colors: true,
15+
},
16+
watch: false,
17+
optimization: {
18+
runtimeChunk: 'single',
19+
splitChunks: {
20+
chunks: 'all',
21+
minSize: 0,
22+
cacheGroups: {
23+
commons: {
24+
name: 'commons',
25+
chunks: 'all',
26+
minChunks: 1,
27+
},
28+
},
29+
},
30+
},
31+
plugins: [],
32+
};
33+
}
34+
35+
module.exports = { create };

lib/webpack/plugin.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const fs = require('fs');
2+
3+
class KW_WebpackPlugin {
4+
constructor(options) {
5+
this.karmaEmitter = options.karmaEmitter;
6+
this.controller = options.controller;
7+
}
8+
9+
apply(compiler) {
10+
this.compiler = compiler;
11+
12+
// webpack bundles are finished
13+
compiler.hooks.done.tap('KW_WebpackPlugin', async (stats) => {
14+
// read generated file content and store for karma preprocessor
15+
this.controller.bundlesContent = {};
16+
stats.toJson().assets.forEach((webpackFileObj) => {
17+
const filePath = `${compiler.options.output.path}/${
18+
webpackFileObj.name
19+
}`;
20+
this.controller.bundlesContent[webpackFileObj.name] = fs.readFileSync(
21+
filePath,
22+
'utf-8'
23+
);
24+
});
25+
26+
// karma refresh
27+
this.karmaEmitter.refreshFiles();
28+
});
29+
}
30+
}
31+
32+
module.exports = KW_WebpackPlugin;

test/integration/scenarios/basic-setup/basic-setup.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import karmaChromeLauncher from 'karma-chrome-launcher';
44
import karmaMocha from 'karma-mocha';
55
import karmaChai from 'karma-chai';
66

7-
import ScenarioUtils from '../../utils/ScenarioUtils';
7+
import Scenario from '../../utils/scenario';
88

99
process.env.CHROME_BIN = require('puppeteer').executablePath();
1010

@@ -36,7 +36,7 @@ describe('A basic karma-webpack setup', () => {
3636
};
3737

3838
beforeAll((done) => {
39-
ScenarioUtils.run(config)
39+
Scenario.run(config)
4040
.then((res) => {
4141
scenario = res;
4242
})

test/integration/utils/ScenarioUtils.js renamed to test/integration/utils/scenario.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const karma = require('karma');
22

3-
const ScenarioUtils = { run };
3+
const Scenario = { run };
44

55
/**
66
* This allows you to run karma with a given configuration and be returned.
@@ -20,4 +20,4 @@ function run(config) {
2020
});
2121
}
2222

23-
export default ScenarioUtils;
23+
export default Scenario;

0 commit comments

Comments
 (0)