From 7aefa64b0aa6092ea2446865d96a7459fe018cd2 Mon Sep 17 00:00:00 2001 From: Ben Kucera <14625260+Bkucera@users.noreply.github.com> Date: Tue, 4 Jun 2019 12:33:55 -0400 Subject: [PATCH] refactor test, evenly truncate details text, properly handle undefined stderr.columns --- lib/ProgressPlugin.js | 32 ++++++--- test/ProgressPlugin.test.js | 129 ++++++++++++++++++++--------------- test/helpers/captureStdio.js | 31 +++++++++ test/support/utils.js | 44 ------------ 4 files changed, 130 insertions(+), 106 deletions(-) create mode 100644 test/helpers/captureStdio.js delete mode 100644 test/support/utils.js diff --git a/lib/ProgressPlugin.js b/lib/ProgressPlugin.js index 1ccb8320b..87f7f9925 100644 --- a/lib/ProgressPlugin.js +++ b/lib/ProgressPlugin.js @@ -18,8 +18,8 @@ const createDefaultHandler = profile => { const defaultHandler = (percentage, msg, ...args) => { let state = msg; - const details = args; - const maxLength = Math.min(process.stderr.columns, 40); + const details = args.filter(v => v.length); + const maxLineLength = process.stderr.columns || Infinity; if (percentage < 1) { percentage = Math.floor(percentage * 100); msg = `${percentage}% ${msg}`; @@ -29,12 +29,28 @@ const createDefaultHandler = profile => { if (percentage < 10) { msg = ` ${msg}`; } - for (let detail of details) { - if (!detail) continue; - if (msg.length + detail.length > maxLength) { - detail = `…${detail.substr(-(maxLength - msg.length - 2))}`; + + if (details.length) { + const maxTotalDetailsLength = maxLineLength - msg.length; + const totalDetailsLength = details.reduce( + (a, b) => a + b.length, + details.length // account for added space before each detail text + ); + const maxDetailLength = + totalDetailsLength < maxTotalDetailsLength + ? Infinity + : Math.floor(maxTotalDetailsLength / details.length); + + for (let detail of details) { + if (!detail) continue; + if (detail.length + 1 > maxDetailLength) { + const truncatePrefix = "..."; + detail = `${truncatePrefix}${detail.substr( + -(maxDetailLength - truncatePrefix.length - 1) + )}`; + } + msg += ` ${detail}`; } - msg += ` ${detail}`; } } if (profile) { @@ -56,7 +72,7 @@ const createDefaultHandler = profile => { } if (lastMessage !== msg) { goToLineStart(msg); - msg = msg.substring(0, maxLength); + msg = msg.substring(0, maxLineLength); process.stderr.write(msg); lastMessage = msg; } diff --git a/test/ProgressPlugin.test.js b/test/ProgressPlugin.test.js index 4af888bf6..dbd6b377f 100644 --- a/test/ProgressPlugin.test.js +++ b/test/ProgressPlugin.test.js @@ -1,9 +1,61 @@ "use strict"; +const _ = require("lodash"); const path = require("path"); const MemoryFs = require("memory-fs"); const webpack = require("../"); -const { Stdio, RunCompilerAsync } = require("./support/utils"); +const captureStdio = require("./helpers/captureStdio"); + +describe("ProgressPlugin", function() { + let _env; + let stderr; + + beforeEach(() => { + _env = process.env; + stderr = captureStdio(process.stderr); + }); + afterEach(() => { + process.env = _env; + stderr && stderr.restore(); + }); + + it("should not contain NaN as a percentage when it is applied to MultiCompiler", () => { + const compiler = createMultiCompiler(); + + return RunCompilerAsync(compiler).then(() => { + expect(stderr.toString()).toContain("%"); + expect(stderr.toString()).not.toContain("NaN"); + }); + }); + + it("should not print lines longer than stderr.columns", () => { + const compiler = createSimpleCompiler(); + process.stderr.columns = 30; + + return RunCompilerAsync(compiler).then(() => { + const logs = getLogs(stderr.toString()); + + expect(logs.length).toBeGreaterThan(20); + logs.forEach(log => expect(log.length).toBeLessThanOrEqual(30)); + expect(logs).toContain( + " 10% building ...ules ...tive", + "trims each detail string equally" + ); + }); + }); + + it("should handle when stderr.columns is undefined", () => { + const compiler = createSimpleCompiler(); + + process.stderr.columns = undefined; + return RunCompilerAsync(compiler).then(() => { + const logs = getLogs(stderr.toString()); + + expect(logs.length).toBeGreaterThan(20); + expect(_.maxBy(logs, "length").length).toBeGreaterThan(50); + }); + }); +}); const createMultiCompiler = () => { const compiler = webpack([ @@ -17,65 +69,34 @@ const createMultiCompiler = () => { } ]); compiler.outputFileSystem = new MemoryFs(); + + new webpack.ProgressPlugin().apply(compiler); + return compiler; }; -describe("ProgressPlugin", function() { - let _env; - let stderr; - - beforeEach(() => { - _env = process.env; - stderr = Stdio.capture(process.stderr); - }); - afterEach(() => { - process.env = _env; - stderr && stderr.restore(); +const createSimpleCompiler = () => { + const compiler = webpack({ + context: path.join(__dirname, "fixtures"), + entry: "./a.js" }); - it("should not contain NaN as a percentage when it is applied to MultiCompiler", () => { - const compiler = createMultiCompiler(); + compiler.outputFileSystem = new MemoryFs(); - new webpack.ProgressPlugin().apply(compiler); + new webpack.ProgressPlugin().apply(compiler); - return RunCompilerAsync(compiler).then(() => { - expect(stderr.toString()).not.toContain("NaN"); + return compiler; +}; + +const getLogs = logsStr => logsStr.split(/\u0008+/).filter(v => !(v === " ")); + +const RunCompilerAsync = compiler => + new Promise((resolve, reject) => { + compiler.run(err => { + if (err) { + reject(err); + } else { + resolve(); + } }); }); - - it("should not print lines longer than stderr.columns or 40", () => { - const compiler = webpack({ - context: path.join(__dirname, "fixtures"), - entry: "./a.js" - }); - - compiler.outputFileSystem = new MemoryFs(); - - new webpack.ProgressPlugin().apply(compiler); - - process.stderr.columns = 10; - - return RunCompilerAsync(compiler) - .then(() => { - const logs = stderr - .toString() - .split(/+/) - .filter(v => !(v === " ")); - - expect(logs.length).toBeGreaterThan(20); - logs.map(v => expect(v.length).toBeLessThanOrEqual(10)); - - process.stderr.columns = undefined; - }) - .then(() => RunCompilerAsync(compiler)) - .then(() => { - const logs = stderr - .toString() - .split(/+/) - .filter(v => !(v === " ")); - - expect(logs.length).toBeGreaterThan(20); - logs.map(v => expect(v.length).toBeLessThanOrEqual(40)); - }); - }); -}); diff --git a/test/helpers/captureStdio.js b/test/helpers/captureStdio.js new file mode 100644 index 000000000..e91fef04b --- /dev/null +++ b/test/helpers/captureStdio.js @@ -0,0 +1,31 @@ +const stripAnsi = require("strip-ansi"); + +module.exports = stdio => { + let logs = []; + + const write = stdio.write; + + stdio.write = function(str) { + logs.push(str); + + write.apply(this, arguments); + }; + + return { + data: logs, + + reset: () => (logs = []), + + toString: () => { + return logs.map(v => stripAnsi(v)).join(""); + }, + + toStringRaw: () => { + return logs.join(""); + }, + + restore() { + stdio.write = write; + } + }; +}; diff --git a/test/support/utils.js b/test/support/utils.js deleted file mode 100644 index 0fa3895fc..000000000 --- a/test/support/utils.js +++ /dev/null @@ -1,44 +0,0 @@ -const stripAnsi = require("strip-ansi"); - -module.exports = { - Stdio: { - capture(stdio) { - const logs = []; - - const write = stdio.write; - - stdio.write = function(str) { - logs.push(str); - - write.apply(this, arguments); - }; - - return { - data: logs, - - toString: () => { - return logs.map(v => stripAnsi(v)).join(""); - }, - - toStringRaw: () => { - logs.join(""); - }, - - restore() { - stdio.write = write; - } - }; - } - }, - - RunCompilerAsync: compiler => - new Promise((resolve, reject) => { - compiler.run(err => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }) -};