diff --git a/package.json b/package.json index 6b10bfaaf..cd9ea7906 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "dev": "babel-node src", "lint": "eslint src tests", "pretest": "npm run -s build", - "test": "tape -r babel-register tests/*.test.js", + "test": "tape -r babel-register tests/*.test.js | tap-diff", "test:build": "babel-node src build --cwd examples/root", "test:serve": "npm run -s test:build && babel-node src serve --port 3000 --cwd examples/root", "test:serve:config": "npm run -s test:build && babel-node src serve --server config --cwd examples/root", @@ -80,6 +80,7 @@ "html-looks-like": "^1.0.2", "html-webpack-exclude-assets-plugin": "0.0.5", "lodash": "^4.17.4", + "tap-diff": "^0.1.1", "tape": "^4.6.3", "uuid": "^3.0.1" }, diff --git a/src/lib/push-manifest.js b/src/lib/push-manifest.js index bf07203fc..d817e7ec4 100644 --- a/src/lib/push-manifest.js +++ b/src/lib/push-manifest.js @@ -4,7 +4,7 @@ module.exports = class PushManifestPlugin { let manifest = {}; for (let filename in compilation.assets) { - if (/route-/.test(filename)) { + if (/route-/.test(filename) && !/\.map$/.test(filename)) { let path = filename.replace(/route-/, '/').replace(/\.chunk(\.\w+)?\.js$/, '').replace(/\/home/, '/'); manifest[path] = { "style.css": { diff --git a/src/lib/webpack-config.js b/src/lib/webpack-config.js index 097eb954d..08f86595b 100644 --- a/src/lib/webpack-config.js +++ b/src/lib/webpack-config.js @@ -405,6 +405,7 @@ const production = config => addPlugins([ comments: false }, mangle: true, + sourceMap: true, compress: { unsafe_comps: true, properties: true, diff --git a/tests/build.snapshot.js b/tests/build.snapshot.js index 5a906faf4..cea185dd1 100644 --- a/tests/build.snapshot.js +++ b/tests/build.snapshot.js @@ -1,25 +1,12 @@ -export const normalize = obj => { - let keys = Object.keys(obj); - - if (keys.length === 1 && keys[0] === 'size' && typeof keys[0] === 'number') { - return { size: Math.round(obj.size / 10) * 10 }; - } - - return keys.reduce((agg, key) => { - let newKey = key.replace(/\.chunk\.\w+\./, '.chunk.*.'); - agg[newKey] = normalize(obj[key]); - return agg; - }, {}); -}; - const smallBuildCommons = { assets: { 'favicon.ico': { size: 15086 }, 'icon.png': { size: 51484 } }, - 'polyfills.js': { size: 4068 }, + 'polyfills.js': { size: 4620 }, + 'polyfills.js.map': { size: 31760 }, 'favicon.ico': { size: 15086 }, - 'sw.js': { size: 3378 }, + 'sw.js': { size: 3330 }, 'manifest.json': { size: 298 }, 'push-manifest.json': { size: 2 }, }; @@ -36,44 +23,54 @@ const fullBuildCommons = { 'mstile-150x150.png': { size: 9050 } } }, - 'polyfills.js': { size: 4066 }, + 'polyfills.js': { size: 4620 }, 'push-manifest.json': { size: 303 }, 'favicon.ico': { size: 15086 }, 'manifest.json': { size: 426 }, - 'sw.js': { size: 3905 } + 'sw.js': { size: 3850 } }; -export const expectedOutputs = normalize({ +export default { empty: { ...smallBuildCommons, - 'bundle.js': { size: 10694 }, - 'index.html': { size: 534 }, + 'bundle.js': { size: 9810 }, + 'bundle.js.map': { size: 44660 }, + 'index.html': { size: 630 }, 'style.css': { size: 131 }, 'style.css.map': { size: 359 }, }, simple: { ...smallBuildCommons, - 'bundle.js': { size: 11336 }, - 'index.html': { size: 548 }, + 'bundle.js': { size: 10460 }, + 'bundle.js.map': { size: 48670 }, + 'index.html': { size: 640 }, 'style.css': { size: 296}, 'style.css.map': { size: 621 }, }, root: { ...fullBuildCommons, - 'bundle.js': { size: 18739 }, - 'route-home.chunk.*.js': { size: 959 }, - 'route-profile.chunk.*.js': { size: 1595 }, - 'index.html': { size: 775 }, + 'bundle.js': { size: 18460 }, + 'bundle.js.map': { size: 101500 }, + 'route-home.chunk.*.js': { size: 1020 }, + 'route-home.chunk.*.js.map': { size: 4980 }, + 'route-profile.chunk.*.js': { size: 1660 }, + 'route-profile.chunk.*.js.map': { size: 8610 }, + 'polyfills.js.map': { size: 31750 }, + 'index.html': { size: 870 }, 'style.css': { size: 1065 }, 'style.css.map': { size: 2246 }, }, 'default': { ...fullBuildCommons, - 'bundle.js': { size: 19661 }, - 'route-home.chunk.*.js': { size: 961 }, - 'route-profile.chunk.*.js': { size: 1597 }, - 'index.html': { size: 775 }, + 'bundle.js': { size: 19300 }, + 'bundle.js.map': { size: 105590 }, + 'route-home.chunk.*.js': { size: 1000 }, + 'route-home.chunk.*.js.map': { size: 4980 }, + 'route-profile.chunk.*.js': { size: 1650 }, + 'route-profile.chunk.*.js.map': { size: 8610 }, + 'polyfills.js.map': { size: 31800 }, + 'index.html': { size: 850 }, 'style.css': { size: 1065 }, 'style.css.map': { size: 2345 }, } -}); +}; diff --git a/tests/build.test.js b/tests/build.test.js index fa7eb008e..20e8d2b7d 100644 --- a/tests/build.test.js +++ b/tests/build.test.js @@ -3,7 +3,8 @@ import { resolve } from 'path'; import { create, build } from './lib/cli'; import lsr from './lib/lsr'; import { setup, clean } from './lib/output'; -import { normalize, expectedOutputs } from './build.snapshot'; +import expectedOutputs from './build.snapshot'; +import filesMatchSnapshot from './lib/filesMatchSnapshot'; const options = { timeout: 45 * 1000 }; @@ -18,7 +19,7 @@ test('preact build - before', async () => { let output = await lsr(resolve(app, 'build')); - t.isEquivalent(normalize(output), expectedOutputs[template]); + filesMatchSnapshot(t, output, expectedOutputs[template]); }) ); diff --git a/tests/lib/filesMatchSnapshot.js b/tests/lib/filesMatchSnapshot.js new file mode 100644 index 000000000..63f4e0b78 --- /dev/null +++ b/tests/lib/filesMatchSnapshot.js @@ -0,0 +1,66 @@ +import { join } from 'path'; + +const minimumSizeDifference = 10; +const percentageThreshold = 0.05; +const minimumFileSize = 1; + +export default (test, actual, expected) => { + let normalizedExpected = normalize(expected); + let normalizedActual = normalize(actual); + + let expectedBoundaries = Object.keys(normalizedExpected) + .reduce((acc, path) => Object.assign(acc, { + [path]: { + min: boundary('down', normalizedExpected[path].size), + max: boundary('up', normalizedExpected[path].size) + } + }), {}); + + let comparisonResult = Object.keys(normalizedActual).reduce((acc, path) => { + let expectedValues = expectedBoundaries[path]; + let actualValue = normalizedActual[path].size; + + if (expectedValues && expectedValues.min <= actualValue && actualValue <= expectedValues.max) { + return Object.assign(acc, { [path]: expectedValues }); + } + + return Object.assign(acc, { [path]: actualValue }); + }, {}); + + test.deepEquals(comparisonResult, expectedBoundaries); +}; + +const boundary = (direction, val) => { + let round = direction === 'up' ? Math.ceil : Math.floor; + let rounded = Math.max(minimumSizeDifference, round(val * percentageThreshold)); + rounded = direction === 'up' ? rounded : -rounded; + return Math.max(minimumFileSize, val + rounded); +}; + +const normalize = obj => { + let flat = flatten(obj, o => Object.keys(o).length === 1 && typeof o.size === 'number'); + + return Object.keys(flat).reduce((agg, key) => { + let newKey = key.replace(/\.chunk\.\w+\./, '.chunk.*.'); + agg[newKey] = flat[key]; + return agg; + }, {}); +}; + +const flatten = (obj, stop, path = '') => Object.keys(obj) + .reduce((agg, key) => { + let combinedPath = path.length ? join(path, key) : key; + + if (stop(obj[key])) { + agg[combinedPath] = obj[key]; + return agg; + } + + let flat = flatten(obj[key], stop, combinedPath); + + Object.keys(flat).forEach(path => { + agg[path] = flat[path]; + }); + return agg; + }, {}); +