/
githubmirror
/
webpack
Обзор
Документация
Войти
/
githubmirror
/
webpack
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v5.107.0
test/ProgressPlugin.test.js
368 строк
10 KB
hai-x
feat: add progressBar for ProgressPlugin (#20853)
07 май 2026, 16:46
Не верифицирован
07 май 2026, 16:46
5d1f28f
Код
Авторство
О чём код?
"use strict"; require("./helpers/warmup-webpack"); const path = require("path"); const _ = require("lodash"); const { Volume, createFsFromVolume } = require("memfs"); const webpack = require(".."); const captureStdio = require("./helpers/captureStdio"); const createMultiCompiler = (progressOptions, configOptions) => { const compiler = webpack( Object.assign( [ { context: path.join(__dirname, "fixtures"), entry: "./a.js" }, { context: path.join(__dirname, "fixtures"), entry: "./b.js" } ], configOptions ) ); compiler.outputFileSystem = createFsFromVolume(new Volume()); new webpack.ProgressPlugin(progressOptions).apply(compiler); return compiler; }; const createSimpleCompiler = (progressOptions) => { const compiler = webpack({ context: path.join(__dirname, "fixtures"), entry: "./a.js", infrastructureLogging: { debug: /Progress/ }, plugins: [ new webpack.ProgressPlugin({ activeModules: true, ...progressOptions }) ] }); compiler.outputFileSystem = createFsFromVolume(new Volume()); return compiler; }; const createSimpleCompilerWithCustomHandler = (options) => { const compiler = webpack({ context: path.join(__dirname, "fixtures"), entry: "./a.js" }); compiler.outputFileSystem = createFsFromVolume(new Volume()); const logger = compiler.getInfrastructureLogger("custom test logger"); new webpack.ProgressPlugin({ activeModules: true, ...options, handler: (...args) => logger.status(args) }).apply(compiler); return compiler; }; const getLogs = (logsStr) => logsStr.split(/\r/).filter((v) => v !== " "); const runCompilerAsync = (compiler) => new Promise((resolve, reject) => { compiler.run((err) => { if (err) { reject(err); } else { resolve(); } }); }); describe("ProgressPlugin", () => { let stderr; let stdout; beforeEach(() => { stderr = captureStdio(process.stderr, true); stdout = captureStdio(process.stdout, true); }); afterEach(() => { // eslint-disable-next-line no-unused-expressions stderr && stderr.restore(); // eslint-disable-next-line no-unused-expressions stdout && stdout.restore(); }); const nanTest = (createCompiler) => () => { const compiler = createCompiler(); return runCompilerAsync(compiler).then(() => { expect(stderr.toString()).toContain("%"); expect(stderr.toString()).not.toContain("NaN"); }); }; it( "should not contain NaN as a percentage when it is applied to Compiler", nanTest(createSimpleCompiler) ); it( "should not contain NaN as a percentage when it is applied to MultiCompiler", nanTest(createMultiCompiler) ); it( "should not contain NaN as a percentage when it is applied to MultiCompiler (parallelism: 1)", nanTest(() => createMultiCompiler(undefined, { parallelism: 1 })) ); it("should start print only on call run/watch", (done) => { const compiler = createSimpleCompiler(); const logs = getLogs(stderr.toString()); expect(logs.join("")).toHaveLength(0); compiler.close(done); }); it("should print profile information", () => { const compiler = createSimpleCompiler({ profile: true }); return runCompilerAsync(compiler).then(() => { const logs = getLogs(stderr.toString()); expect(logs).toContainEqual( expect.stringMatching( /\[webpack\.Progress\] {2}| {2}| \d+ ms module ids > DeterministicModuleIdsPlugin\n$/ ) ); expect(logs).toContainEqual( expect.stringMatching( /\[webpack\.Progress\] {2}| \d+ ms building > \.\.\. entries \.\.\. dependencies \.\.\. modules\n$/ ) ); expect(logs).toContainEqual( expect.stringMatching(/\[webpack\.Progress\] \d+ ms building\n$/) ); expect(logs).toContainEqual( expect.stringMatching( /\[webpack\.Progress\] {2}| \d+ ms sealing > module ids\n$/ ) ); expect(logs).toContainEqual( expect.stringMatching(/\[webpack\.Progress\] \d+ ms sealing\n$/) ); }); }); const monotonicTest = (createCompiler) => () => { const handlerCalls = []; const compiler = createCompiler({ handler: (p, ...args) => { handlerCalls.push({ value: p, text: `${p}% ${args.join(" ")}` }); } }); return runCompilerAsync(compiler).then(() => { let lastLine = handlerCalls[0]; for (const line of handlerCalls) { if (line.value < lastLine.value) { throw new Error( `Progress value is not monotonic increasing:\n${lastLine.text}\n${line.text}` ); } lastLine = line; } }); }; it( "should have monotonic increasing progress", monotonicTest(createSimpleCompiler) ); it( "should have monotonic increasing progress (multi compiler)", monotonicTest(createMultiCompiler) ); it( "should have monotonic increasing progress (multi compiler, parallelism)", monotonicTest((o) => createMultiCompiler(o, { parallelism: 1 })) ); it("should not print lines longer than stderr.columns", () => { const compiler = createSimpleCompiler(); process.stderr.columns = 36; return runCompilerAsync(compiler).then(() => { const logs = getLogs(stderr.toString()); expect(logs.length).toBeGreaterThan(20); for (const log of logs) { expect(log.length).toBeLessThanOrEqual(35); } // cspell:ignore mization nsPlugin expect(logs).toContain( "75% sealing ...mization ...nsPlugin", "trims each detail string equally" ); expect(logs).toContain("92% sealing asset processing"); expect(logs).toContain("100%"); }); }); 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).not.toBeGreaterThan(40); }); }); it("should contain the new compiler hooks", () => { const compiler = createSimpleCompiler(); process.stderr.columns = undefined; return runCompilerAsync(compiler).then(() => { const logs = getLogs(stderr.toString()); expect(logs).toContain("4% setup normal module factory"); expect(logs).toContain("5% setup context module factory"); }); }); it("should display all type of percentage when it is applied to SingleCompiler", () => { const compiler = createSimpleCompiler({ entries: true, modules: true, dependencies: true, activeModules: true }); process.stderr.columns = 70; return runCompilerAsync(compiler).then(() => { const logs = stderr.toString(); expect(logs).toEqual(expect.stringMatching(/\d+\/\d+ entries/)); expect(logs).toEqual(expect.stringMatching(/\d+\/\d+ dependencies/)); expect(logs).toEqual(expect.stringMatching(/\d+\/\d+ modules/)); expect(logs).toEqual(expect.stringMatching(/\d+ active/)); }); }); it("should respect falsy boolean options", () => { const plugin = new webpack.ProgressPlugin({ modules: false, dependencies: false, entries: false, activeModules: false, profile: false }); expect(plugin.showModules).toBe(false); expect(plugin.showDependencies).toBe(false); expect(plugin.showEntries).toBe(false); expect(plugin.showActiveModules).toBe(false); expect(plugin.profile).toBe(false); }); it("should normalize progressBar option", () => { expect(new webpack.ProgressPlugin({}).progressBar).toBe(false); expect(new webpack.ProgressPlugin({ progressBar: false }).progressBar).toBe( false ); expect( new webpack.ProgressPlugin({ progressBar: true }).progressBar ).toEqual({ name: "Build", color: "green" }); expect( new webpack.ProgressPlugin({ progressBar: { name: "Custom" } }) .progressBar ).toEqual({ name: "Custom", color: "green" }); expect( new webpack.ProgressPlugin({ progressBar: { color: "red" } }).progressBar ).toEqual({ name: "Build", color: "red" }); expect( new webpack.ProgressPlugin({ progressBar: { name: "Custom", color: "cyan" } }).progressBar ).toEqual({ name: "Custom", color: "cyan" }); }); it("should render progress bar with block characters when enabled", () => { const compiler = createSimpleCompiler({ progressBar: { name: "TestBar", color: "magenta" } }); process.stderr.columns = 70; return runCompilerAsync(compiler).then(() => { const logs = stderr.toString(); expect(logs).toContain("━"); expect(logs).toContain("●"); expect(logs).toContain("TestBar"); expect(logs).toEqual(expect.stringMatching(/\(\d+%\)/)); }); }); it("should render progress bar when applied to MultiCompiler", () => { const compiler = createMultiCompiler({ progressBar: { name: "MultiBar", color: "cyan" } }); process.stderr.columns = 200; return runCompilerAsync(compiler).then(() => { const logs = stderr.toString(); expect(logs).toContain("━"); expect(logs).toContain("●"); expect(logs).toContain("MultiBar"); expect(logs).toEqual(expect.stringMatching(/\(\d+%\)/)); }); }); it("should render progress bars for two parallel compilers", () => { const compilerA = createSimpleCompiler({ progressBar: { name: "BarA", color: "red" } }); const compilerB = createSimpleCompiler({ progressBar: { name: "BarB", color: "blue" } }); process.stderr.columns = 70; return Promise.all([ runCompilerAsync(compilerA), runCompilerAsync(compilerB) ]).then(() => { const logs = stderr.toString(); expect(logs).toContain("BarA"); expect(logs).toContain("BarB"); expect(logs).toContain("━"); expect(logs).toContain("●"); }); }); it("should get the custom handler text from the log", () => { const compiler = createSimpleCompilerWithCustomHandler(); process.stderr.columns = 70; return runCompilerAsync(compiler).then(() => { const logs = stderr.toString(); expect(logs).toEqual( expect.stringMatching(/\d+\/\d+ [custom test logger]/) ); expect(logs).toEqual(expect.stringMatching(/\d+ active/)); expect(logs).toEqual(expect.stringMatching(/\d+\/\d+ modules/)); }); }); });