Skip to content

Commit 72bbcc2

Browse files
authored
Add tests (#17)
1 parent 9361738 commit 72bbcc2

File tree

10 files changed

+267
-15
lines changed

10 files changed

+267
-15
lines changed

.eslintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"extends": "simenb-base"
2+
"extends": ["simenb-base", "simenb-ava"]
33
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ index.js
33
node_modules/
44
.idea/
55
typings/
6+
.nyc_output/
7+
coverage/
68
*.log

.travis.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,15 @@
11
language: node_js
2-
node_js: node
2+
node_js:
3+
- node
4+
- 5
5+
- 4
6+
- 0.12
7+
- '0.10'
8+
os:
9+
- linux
10+
- osx
11+
before_install: if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi
12+
script: npm run travis
13+
after_success:
14+
- npm install -g codecov
15+
- codecov

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Latest version of this document will always be available on https://github.com/S
88
### Added
99
- A Changelog!
1010
- A first attempt to add typings
11+
- Tests for 100% coverage (#17)
1112

1213
### Fixes
1314
- Fix wrong documentation

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
> Add a JavaScript or CSS asset to the HTML generated by `html-webpack-plugin`
33
44
[![NPM Version][npm-image]][npm-url]
5-
[![Build Status][travis-image]][travis-url]
5+
[![Linux & Mac Build Status][travis-image]][travis-url]
6+
[![Windows Build Status][appveyor-image]][appveyor-url]
7+
[![Code Coverage branch][codecov-image]][codecov-url]
68

79
[![Dependency Status][david-image]][david-url]
810
[![Dev Dependency Status][david-dev-image]][david-dev-url]
@@ -158,6 +160,10 @@ var webpackConfig = {
158160
[npm-image]: https://img.shields.io/npm/v/add-asset-html-webpack-plugin.svg
159161
[travis-url]: https://travis-ci.org/SimenB/add-asset-html-webpack-plugin
160162
[travis-image]: https://img.shields.io/travis/SimenB/add-asset-html-webpack-plugin/master.svg
163+
[appveyor-url]: https://ci.appveyor.com/project/SimenB/add-asset-html-webpack-plugin
164+
[appveyor-image]: https://ci.appveyor.com/api/projects/status/dim5hcl49h3pi332/branch/master?svg=true
165+
[codecov-url]: https://codecov.io/gh/SimenB/add-asset-html-webpack-plugin
166+
[codecov-image]: https://img.shields.io/codecov/c/github/SimenB/add-asset-html-webpack-plugin/master.svg
161167
[david-url]: https://david-dm.org/SimenB/add-asset-html-webpack-plugin
162168
[david-image]: https://img.shields.io/david/SimenB/add-asset-html-webpack-plugin.svg
163169
[david-dev-url]: https://david-dm.org/SimenB/add-asset-html-webpack-plugin#info=devDependencies

addAssetHtmlPlugin.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import Promise from 'bluebird';
66
function resolvePublicPath(compilation, filename, publicPath) {
77
let resolvedPublicPath;
88
if (typeof publicPath === 'undefined') {
9+
/* istanbul ignore else */
910
resolvedPublicPath = typeof compilation.options.output.publicPath !== 'undefined'
1011
? compilation.options.output.publicPath
11-
: path.relative(path.dirname(filename), '.');
12+
: path.relative(path.dirname(filename), '.'); // TODO: How to test this? I haven't written this logic, unsure what it does
1213
} else {
1314
resolvedPublicPath = publicPath;
1415
}
@@ -19,9 +20,12 @@ function resolvePublicPath(compilation, filename, publicPath) {
1920
return resolvedPublicPath;
2021
}
2122

22-
function addFileToAssets(htmlPluginData, compilation,
23-
{ filename, typeOfAsset = 'js', includeSourcemap = true, hash = false, publicPath } = {}) {
24-
if (!filename) return compilation.errors.push(new Error('No filename defined'));
23+
function addFileToAssets(compilation, htmlPluginData, { filename, typeOfAsset = 'js', includeSourcemap = true, hash = false, publicPath }) {
24+
if (!filename) {
25+
const error = new Error('No filename defined');
26+
compilation.errors.push(error);
27+
return Promise.reject(error);
28+
}
2529

2630
return htmlPluginData.plugin.addFileToAssets(filename, compilation)
2731
.then(addedFilename => {
@@ -31,8 +35,14 @@ function addFileToAssets(htmlPluginData, compilation,
3135
md5.update(compilation.assets[addedFilename].source());
3236
suffix = `?${md5.digest('hex').substr(0, 20)}`;
3337
}
34-
return htmlPluginData.assets[typeOfAsset]
35-
.unshift(`${resolvePublicPath(compilation, addedFilename, publicPath)}${addedFilename}${suffix}`);
38+
39+
// TODO: No need to call this if `publicPath` is provided
40+
const resolvedPublicPath = resolvePublicPath(compilation, addedFilename, publicPath);
41+
const resolvedPath = `${resolvedPublicPath}${addedFilename}${suffix}`;
42+
43+
htmlPluginData.assets[typeOfAsset].unshift(resolvedPath);
44+
45+
return resolvedPath;
3646
})
3747
.then(() => {
3848
if (includeSourcemap) {
@@ -42,16 +52,23 @@ function addFileToAssets(htmlPluginData, compilation,
4252
});
4353
}
4454

55+
// Visible for testing
56+
export function addAllAssetsToCompilation(assets, compilation, htmlPluginData, callback) {
57+
return Promise.mapSeries(assets, asset => addFileToAssets(compilation, htmlPluginData, asset))
58+
.then(() => callback(null, htmlPluginData))
59+
.catch(e => callback(e, htmlPluginData));
60+
}
61+
4562
export default class AddAssetHtmlPlugin {
4663
constructor(assets = []) {
4764
this.assets = Array.isArray(assets) ? assets.slice().reverse() : [assets];
4865
}
4966

67+
/* istanbul ignore next: this would be integration tests */
5068
apply(compiler) {
5169
compiler.plugin('compilation', compilation => {
5270
compilation.plugin('html-webpack-plugin-before-html-generation', (htmlPluginData, callback) => {
53-
Promise.mapSeries(this.assets, asset => addFileToAssets(htmlPluginData, compilation, asset))
54-
.then(() => callback(null, htmlPluginData));
71+
addAllAssetsToCompilation(this.assets, compilation, htmlPluginData, callback);
5572
});
5673
});
5774
}

appveyor.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
init:
2+
- git config --global core.autocrlf true
3+
4+
environment:
5+
matrix:
6+
- nodejs_version: "0.10"
7+
- nodejs_version: "0.12"
8+
- nodejs_version: 4
9+
- nodejs_version: 5
10+
- nodejs_version: 6
11+
12+
# Install scripts. (runs after repo cloning)
13+
install:
14+
- ps: Install-Product node $env:nodejs_version
15+
- ps: >-
16+
if ($env:nodejs_version -eq "0.10" -or $env:nodejs_version -eq "0.12" -or $env:nodejs_version -eq "4") {
17+
npm -g install npm@3
18+
$env:PATH="$env:APPDATA\npm;$env:PATH"
19+
}
20+
- npm install
21+
22+
# Post-install test scripts.
23+
test_script:
24+
# Output useful info for debugging.
25+
- node --version && npm --version
26+
# run tests
27+
- npm test
28+
29+
# Don't actually build.
30+
build: off
31+
32+
matrix:
33+
fast_finish: true

package.json

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@
77
"index.d.ts"
88
],
99
"scripts": {
10+
"clean": "del index.js coverage/ .nyc_output/",
1011
"compile": "babel addAssetHtmlPlugin.js -o index.js",
12+
"cover": "nyc ava",
1113
"lint": "node-version-gte-4 && eslint . || node-version-lt-4",
1214
"prepublish": "not-in-install && npm run compile || in-install",
1315
"postpublish": "git push --follow-tags",
1416
"pretest": "npm run lint",
15-
"test": "echo No tests defined"
17+
"test": "ava",
18+
"pretravis": "npm run lint && npm run clean",
19+
"travis": "nyc --check-coverage --lines 100 --functions 100 --branches 100 --statements 100 ava",
20+
"posttravis": "nyc report --reporter=lcov"
1621
},
1722
"repository": "SimenB/add-asset-html-webpack-plugin",
1823
"keywords": [
@@ -30,16 +35,27 @@
3035
"bluebird": "^3.3.5"
3136
},
3237
"devDependencies": {
33-
"babel-cli": "^6.10.1",
38+
"ava": "^0.15.2",
39+
"babel-cli": "^6.11.4",
3440
"babel-plugin-add-module-exports": "^0.2.1",
3541
"babel-preset-es2015": "^6.9.0",
42+
"babel-register": "^6.11.6",
43+
"del-cli": "^0.2.0",
3644
"eslint": "^3.1.1",
45+
"eslint-config-simenb-ava": "^1.1.1",
3746
"eslint-config-simenb-base": "^2.0.0",
3847
"html-webpack-plugin": "^2.10.0",
3948
"in-publish": "^2.0.0",
40-
"node-version-check": "^2.0.2"
49+
"node-version-check": "^2.0.2",
50+
"nyc": "^7.1.0",
51+
"sinon": "^1.17.5"
4152
},
4253
"peerDependencies": {
4354
"html-webpack-plugin": "^2.10.0"
55+
},
56+
"ava": {
57+
"require": [
58+
"babel-register"
59+
]
4460
}
4561
}

test.js

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/* eslint import/no-extraneous-dependencies: ["error", { "devDependencies": true }] */
2+
3+
import path from 'path';
4+
import test from 'ava';
5+
import sinon from 'sinon';
6+
import Promise from 'bluebird';
7+
import AddAssetHtmlPlugin, { addAllAssetsToCompilation } from './addAssetHtmlPlugin';
8+
9+
const pluginMock = {
10+
plugin: {
11+
addFileToAssets: filename => Promise.resolve(path.basename(filename)),
12+
},
13+
};
14+
15+
test('assets should always be an array', t => {
16+
t.true(Array.isArray(new AddAssetHtmlPlugin({}).assets));
17+
t.true(Array.isArray(new AddAssetHtmlPlugin([]).assets));
18+
t.true(Array.isArray(new AddAssetHtmlPlugin().assets));
19+
});
20+
21+
test('assets should should be reversed', t => {
22+
t.deepEqual(new AddAssetHtmlPlugin(['a', 'b']).assets, ['b', 'a']);
23+
});
24+
25+
test('should invoke callback on success', async t => {
26+
const callback = sinon.stub();
27+
28+
await addAllAssetsToCompilation([], {}, pluginMock, callback);
29+
30+
t.true(callback.calledOnce);
31+
t.true(callback.calledWithExactly(null, pluginMock));
32+
});
33+
34+
test('should invoke callback on error', async t => {
35+
const callback = sinon.stub();
36+
const compilation = { errors: [] };
37+
38+
await addAllAssetsToCompilation([{}], compilation, pluginMock, callback);
39+
40+
t.true(compilation.errors.length === 1);
41+
t.true(compilation.errors[0].message === 'No filename defined');
42+
43+
t.true(callback.calledOnce);
44+
t.true(callback.calledWithExactly(compilation.errors[0], pluginMock));
45+
});
46+
47+
test("should add file using compilation's publicPath", async t => {
48+
const callback = sinon.stub();
49+
const compilation = { options: { output: { publicPath: 'vendor/' } } };
50+
const pluginData = Object.assign({ assets: { js: [], css: [] } }, pluginMock);
51+
52+
await addAllAssetsToCompilation([{ filename: path.join(__dirname, 'my-file.js') }], compilation, pluginData, callback);
53+
54+
t.deepEqual(pluginData.assets.css, []);
55+
t.deepEqual(pluginData.assets.js, ['vendor/my-file.js']);
56+
57+
t.true(callback.calledOnce);
58+
t.true(callback.calledWithExactly(null, pluginData));
59+
});
60+
61+
test('should used passed in publicPath', async t => {
62+
const callback = sinon.stub();
63+
const compilation = { options: { output: { publicPath: 'vendor/' } } };
64+
const pluginData = Object.assign({ assets: { js: [], css: [] } }, pluginMock);
65+
66+
await addAllAssetsToCompilation([{ filename: 'my-file.js', publicPath: 'pp' }], compilation, pluginData, callback);
67+
68+
t.deepEqual(pluginData.assets.css, []);
69+
t.deepEqual(pluginData.assets.js, ['pp/my-file.js']);
70+
71+
t.true(callback.calledOnce);
72+
t.true(callback.calledWithExactly(null, pluginData));
73+
});
74+
75+
// No idea what this does, actually... Coverage currently hits it, but the logic is untested.
76+
test.todo('should handle missing `publicPath`');
77+
78+
test('should add file missing "/" to public path', async t => {
79+
const callback = sinon.stub();
80+
const compilation = { options: { output: { publicPath: 'vendor' } } };
81+
const pluginData = Object.assign({ assets: { js: [], css: [] } }, pluginMock);
82+
83+
await addAllAssetsToCompilation([{ filename: 'my-file.js' }], compilation, pluginData, callback);
84+
85+
t.deepEqual(pluginData.assets.css, []);
86+
t.deepEqual(pluginData.assets.js, ['vendor/my-file.js']);
87+
88+
t.true(callback.calledOnce);
89+
t.true(callback.calledWithExactly(null, pluginData));
90+
});
91+
92+
test('should add sourcemap to compilation', async t => {
93+
const callback = sinon.stub();
94+
const addFileToAssetsStub = sinon.stub();
95+
const compilation = { options: { output: {} } };
96+
const pluginData = { assets: { js: [], css: [] }, plugin: { addFileToAssets: addFileToAssetsStub } };
97+
addFileToAssetsStub.returns(Promise.resolve('my-file.js'));
98+
99+
await addAllAssetsToCompilation([{ filename: 'my-file.js' }], compilation, pluginData, callback);
100+
101+
t.deepEqual(pluginData.assets.css, []);
102+
t.deepEqual(pluginData.assets.js, ['my-file.js']);
103+
104+
t.true(callback.calledOnce);
105+
t.true(callback.calledWithExactly(null, pluginData));
106+
107+
t.true(addFileToAssetsStub.calledTwice);
108+
t.true(addFileToAssetsStub.getCall(0).args[0] === 'my-file.js');
109+
t.true(addFileToAssetsStub.getCall(1).args[0] === 'my-file.js.map');
110+
});
111+
112+
test('should skip adding sourcemap to compilation if set to false', async t => {
113+
const callback = sinon.stub();
114+
const addFileToAssetsStub = sinon.stub();
115+
const compilation = { options: { output: {} } };
116+
const pluginData = { assets: { js: [], css: [] }, plugin: { addFileToAssets: addFileToAssetsStub } };
117+
addFileToAssetsStub.returns(Promise.resolve('my-file.js'));
118+
119+
await addAllAssetsToCompilation([{ filename: 'my-file.js', includeSourcemap: false }], compilation, pluginData, callback);
120+
121+
t.deepEqual(pluginData.assets.css, []);
122+
t.deepEqual(pluginData.assets.js, ['my-file.js']);
123+
124+
t.true(callback.calledOnce);
125+
t.true(callback.calledWithExactly(null, pluginData));
126+
127+
t.true(addFileToAssetsStub.calledOnce);
128+
t.true(addFileToAssetsStub.getCall(0).args[0] === 'my-file.js');
129+
});
130+
131+
test('should include hash of file content if option is set', async t => {
132+
const callback = sinon.stub();
133+
const compilation = {
134+
options: { output: {} },
135+
assets: { 'my-file.js': { source: () => 'some source code is cool to have;' } },
136+
};
137+
const pluginData = Object.assign({ assets: { js: [], css: [] } }, pluginMock);
138+
139+
await addAllAssetsToCompilation([{ filename: 'my-file.js', hash: true }], compilation, pluginData, callback);
140+
141+
t.deepEqual(pluginData.assets.css, []);
142+
t.deepEqual(pluginData.assets.js, ['my-file.js?5329c141291f07ab06c6']);
143+
144+
t.true(callback.calledOnce);
145+
t.true(callback.calledWithExactly(null, pluginData));
146+
});
147+
148+
test('should add to css if `typeOfAsset` is css', async t => {
149+
const callback = sinon.stub();
150+
const compilation = {
151+
options: { output: {} },
152+
assets: { 'my-file.js': { source: () => 'some source code is cool to have;' } },
153+
};
154+
const pluginData = Object.assign({ assets: { js: [], css: [] } }, pluginMock);
155+
156+
await addAllAssetsToCompilation([{ filename: 'my-file.css', typeOfAsset: 'css' }], compilation, pluginData, callback);
157+
158+
t.deepEqual(pluginData.assets.css, ['my-file.css']);
159+
t.deepEqual(pluginData.assets.js, []);
160+
161+
t.true(callback.calledOnce);
162+
t.true(callback.calledWithExactly(null, pluginData));
163+
});

typings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"node": "registry:env/node#6.0.0+20160723033700"
44
},
55
"dependencies": {
6-
"bluebird": "registry:npm/bluebird#3.3.4+20160723033700"
6+
"bluebird": "registry:npm/bluebird#3.3.4+20160723033700",
7+
"sinon": "registry:npm/sinon#1.16.0+20160723033700"
78
}
89
}

0 commit comments

Comments
 (0)