1 | const crypto = require('crypto');
|
2 | const webpack = require('webpack');
|
3 | const path = require('path');
|
4 | const expect = require('chai').expect;
|
5 |
|
6 | const expectAssetsNameToContainHash = (stats, filter = ()=>true) => {
|
7 | const {assets} = stats.compilation;
|
8 | const {hashFn} = stats;
|
9 | expect(Object.keys(assets)).to.have.length.at.least(1);
|
10 | Object.keys(assets).filter(filter).forEach(name => {
|
11 | const asset = assets[name];
|
12 | const { shortHash } = hashFn(asset.source());
|
13 | expect(name).to.contain(shortHash);
|
14 | });
|
15 | }
|
16 |
|
17 | const webpackCompile = (fixture) => {
|
18 | return new Promise((resolve, reject)=> {
|
19 | const dir = path.resolve(__dirname, fixture);
|
20 | const config = path.resolve(dir, 'webpack.config.js');
|
21 | const opts = Object.assign(require(config), { context: dir });
|
22 | webpack(opts, (err, stats) => {
|
23 |
|
24 | stats.hashFn = makeHashFn(opts.output);
|
25 | if (err) reject(err);
|
26 | else resolve(stats);
|
27 | });
|
28 | })
|
29 | }
|
30 |
|
31 |
|
32 | function makeHashFn({
|
33 | hashFunction = 'md5',
|
34 | hashDigest = 'hex',
|
35 | hashDigestLength = 20,
|
36 | hashSalt = null,
|
37 | } = {}) {
|
38 | return (input) => {
|
39 | const hashObj = crypto.createHash(hashFunction).update(input);
|
40 | if (hashSalt) hashObj.update(hashSalt);
|
41 | const fullHash = hashObj.digest(hashDigest);
|
42 | return { fullHash, shortHash: fullHash.substr(0, hashDigestLength) };
|
43 | };
|
44 | }
|
45 |
|
46 | const findAssetByName = (assets, name) => {
|
47 | const assetName = Object.keys(assets).map(n => n.split('.')).find(n => n[0] === name).join('.');
|
48 | return assets[assetName];
|
49 | }
|
50 |
|
51 | const extractHashes = (assets, filter) => {
|
52 | return Object.keys(assets)
|
53 | .map(n => n.split('.'))
|
54 | .filter(filter)
|
55 | .map(n => n[1])
|
56 | }
|
57 |
|
58 | describe('OutputHash', () => {
|
59 | it("Works with single entry points", () => webpackCompile('one-asset')
|
60 | .then((stats) => {
|
61 | expectAssetsNameToContainHash(stats);
|
62 | })
|
63 | )
|
64 |
|
65 |
|
66 | xit("Works with hashSalt", () => webpackCompile('one-asset-salt')
|
67 | .then((stats) => {
|
68 | expectAssetsNameToContainHash(stats);
|
69 | })
|
70 | )
|
71 |
|
72 | it("Works with hashFunction (sha256)", () => webpackCompile('one-asset-sha256')
|
73 | .then((stats) => {
|
74 | expectAssetsNameToContainHash(stats);
|
75 | })
|
76 | )
|
77 |
|
78 | it("Works with hashDigest (base64)", () => webpackCompile('one-asset-base64')
|
79 | .then((stats) => {
|
80 | expectAssetsNameToContainHash(stats);
|
81 | })
|
82 | )
|
83 |
|
84 | it("Works with multiple entry points", () => webpackCompile('multi-asset')
|
85 | .then((stats) => {
|
86 | expectAssetsNameToContainHash(stats);
|
87 | })
|
88 | )
|
89 |
|
90 | it("Works with common chunks", () => webpackCompile('common-chunks')
|
91 | .then((stats) => {
|
92 | expectAssetsNameToContainHash(stats);
|
93 |
|
94 | const hashes = extractHashes(stats.compilation.assets, n => n[0]!=="vendor")
|
95 | const commons = findAssetByName(stats.compilation.assets, "vendor");
|
96 |
|
97 | expect(hashes).to.have.lengthOf(1);
|
98 | hashes.forEach(hash => {
|
99 | expect(commons.source()).to.contain(hash);
|
100 | });
|
101 | })
|
102 | )
|
103 |
|
104 | it("Works with manifest file", () => webpackCompile('manifest')
|
105 | .then((stats) => {
|
106 | expectAssetsNameToContainHash(stats);
|
107 |
|
108 | const hashes = extractHashes(stats.compilation.assets, n => n[0]!=="manifest")
|
109 | const commons = findAssetByName(stats.compilation.assets, "manifest");
|
110 |
|
111 | expect(hashes).to.have.lengthOf(2);
|
112 | hashes.forEach(hash => {
|
113 | expect(commons.source()).to.contain(hash);
|
114 | });
|
115 | })
|
116 | )
|
117 |
|
118 | it("Works with a complex uglified project", () => webpackCompile('manifest-uglify')
|
119 | .then((stats) => {
|
120 | expectAssetsNameToContainHash(stats);
|
121 |
|
122 | const hashes = extractHashes(stats.compilation.assets, n => n[0]!=="manifest")
|
123 | const commons = findAssetByName(stats.compilation.assets, "manifest");
|
124 |
|
125 | expect(hashes).to.have.lengthOf(2);
|
126 | hashes.forEach(hash => {
|
127 | expect(commons.source()).to.contain(hash);
|
128 | });
|
129 | })
|
130 | )
|
131 |
|
132 | it("Works with HTML output", () => webpackCompile('html')
|
133 | .then((stats) => {
|
134 | expectAssetsNameToContainHash(stats, name => name!=="index.html");
|
135 |
|
136 | const hashes = extractHashes(stats.compilation.assets, n => n[0]!=="index")
|
137 | const commons = findAssetByName(stats.compilation.assets, "index");
|
138 |
|
139 | expect(hashes).to.have.lengthOf(2);
|
140 | hashes.forEach(hash => {
|
141 | expect(commons.source()).to.contain(hash);
|
142 | });
|
143 | })
|
144 | )
|
145 | });
|