From e57a0633086285899ed1bb36b0e0ef5c048b1384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Emarianfoo=E2=80=9C?= <13335743+marianfoo@users.noreply.github.com> Date: Tue, 15 Apr 2025 21:38:59 +0200 Subject: [PATCH 1/8] feat(cli): Add 'quiet' option to report errors only --- README.md | 11 +++++++++++ src/cli/base.ts | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 60c18c732..43b347579 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ - [`--details`](#--details) - [`--format`](#--format) - [`--fix`](#--fix) + - [Dry Run Mode](#dry-run-mode) + - [`--quiet`](#--quiet) - [`--ignore-pattern`](#--ignore-pattern) - [`--config`](#--config) - [`--ui5-config`](#--ui5-config) @@ -194,6 +196,15 @@ UI5LINT_FIX_DRY_RUN=true ui5lint --fix In this mode, the linter will show the messages after the fixes would have been applied but will not actually change the files. +#### `--quiet` + +Report errors only and hide warnings. Similar to ESLint's --quiet option. + +**Example:** +```sh +ui5lint --quiet +``` + #### `--ignore-pattern` Pattern/files that will be ignored during linting. Can also be defined in `ui5lint.config.js`. diff --git a/src/cli/base.ts b/src/cli/base.ts index 05d2e8c86..d35e030c8 100644 --- a/src/cli/base.ts +++ b/src/cli/base.ts @@ -10,6 +10,8 @@ import {isLogLevelEnabled} from "@ui5/logger"; import ConsoleWriter from "@ui5/logger/writers/Console"; import {getVersion} from "./version.js"; import {ui5lint} from "../index.js"; +import type {LintResult} from "../linter/LinterContext.js"; +import {LintMessageSeverity} from "../linter/messages.js"; export interface LinterArg { coverage: boolean; @@ -21,6 +23,7 @@ export interface LinterArg { format: string; config?: string; ui5Config?: string; + quiet: boolean; } // yargs type definition is missing the "middlewares" property for the CommandModule type @@ -106,6 +109,12 @@ const lintCommand: FixedCommandModule = { type: "string", choices: ["stylish", "json", "markdown"], }) + .option("quiet", { + describe: "Report errors only", + type: "boolean", + default: false, + alias: "q", + }) .option("ui5-config", { describe: "Set a custom path for the UI5 Config (default: './ui5.yaml' if that file exists)", type: "string", @@ -147,6 +156,7 @@ async function handleLint(argv: ArgumentsCamelCase) { format, config, ui5Config, + quiet, } = argv; let profile; @@ -170,22 +180,39 @@ async function handleLint(argv: ArgumentsCamelCase) { ui5Config, }); + // Define a simple function to filter a single result set + const applyQuietFilter = (results: LintResult[]): LintResult[] => { + if (!quiet) { + return results; + } + return results.map((file) => ({ + ...file, + messages: file.messages.filter((msg) => msg.severity === LintMessageSeverity.Error), + warningCount: 0, + fixableWarningCount: 0, + })); + }; + if (coverage) { const coverageFormatter = new Coverage(); - await writeFile("ui5lint-report.html", await coverageFormatter.format(res, new Date())); + const filteredResults = applyQuietFilter(res); + await writeFile("ui5lint-report.html", await coverageFormatter.format(filteredResults, new Date())); } if (format === "json") { const jsonFormatter = new Json(); - process.stdout.write(jsonFormatter.format(res, details)); + const filteredResults = applyQuietFilter(res); + process.stdout.write(jsonFormatter.format(filteredResults, details)); process.stdout.write("\n"); } else if (format === "markdown") { const markdownFormatter = new Markdown(); - process.stdout.write(markdownFormatter.format(res, details, getVersion(), fix)); + const filteredResults = applyQuietFilter(res); + process.stdout.write(markdownFormatter.format(filteredResults, details, getVersion(), fix)); process.stdout.write("\n"); } else if (format === "" || format === "stylish") { const textFormatter = new Text(rootDir); - process.stderr.write(textFormatter.format(res, details, fix)); + const filteredResults = applyQuietFilter(res); + process.stderr.write(textFormatter.format(filteredResults, details, fix)); } // Stop profiling after CLI finished execution if (profile) { From 27f0c90ee6db0a03516b7887e8c01f07476cb423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Emarianfoo=E2=80=9C?= <13335743+marianfoo@users.noreply.github.com> Date: Tue, 15 Apr 2025 21:48:31 +0200 Subject: [PATCH 2/8] refactor: Warning are shown in summary --- src/cli/base.ts | 6 +++--- src/formatter/markdown.ts | 14 ++++++++++---- src/formatter/text.ts | 24 +++++++++++++++++------- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/cli/base.ts b/src/cli/base.ts index d35e030c8..b367021d6 100644 --- a/src/cli/base.ts +++ b/src/cli/base.ts @@ -202,17 +202,17 @@ async function handleLint(argv: ArgumentsCamelCase) { if (format === "json") { const jsonFormatter = new Json(); const filteredResults = applyQuietFilter(res); - process.stdout.write(jsonFormatter.format(filteredResults, details)); + process.stdout.write(jsonFormatter.format(filteredResults, details, quiet)); process.stdout.write("\n"); } else if (format === "markdown") { const markdownFormatter = new Markdown(); const filteredResults = applyQuietFilter(res); - process.stdout.write(markdownFormatter.format(filteredResults, details, getVersion(), fix)); + process.stdout.write(markdownFormatter.format(filteredResults, details, getVersion(), fix, quiet)); process.stdout.write("\n"); } else if (format === "" || format === "stylish") { const textFormatter = new Text(rootDir); const filteredResults = applyQuietFilter(res); - process.stderr.write(textFormatter.format(filteredResults, details, fix)); + process.stderr.write(textFormatter.format(filteredResults, details, fix, quiet)); } // Stop profiling after CLI finished execution if (profile) { diff --git a/src/formatter/markdown.ts b/src/formatter/markdown.ts index 712178c44..f5800b2d0 100644 --- a/src/formatter/markdown.ts +++ b/src/formatter/markdown.ts @@ -2,7 +2,7 @@ import {LintResult, LintMessage} from "../linter/LinterContext.js"; import {LintMessageSeverity} from "../linter/messages.js"; export class Markdown { - format(lintResults: LintResult[], showDetails: boolean, version: string, autofix: boolean): string { + format(lintResults: LintResult[], showDetails: boolean, version: string, autofix: boolean, quiet = false): string { let totalErrorCount = 0; let totalWarningCount = 0; let totalFatalErrorCount = 0; @@ -65,9 +65,15 @@ export class Markdown { }); let summary = "## Summary\n\n"; - summary += - `> ${totalErrorCount + totalWarningCount} problems ` + - `(${totalErrorCount} errors, ${totalWarningCount} warnings) \n`; + if (quiet) { + summary += + `> ${totalErrorCount} ${totalErrorCount === 1 ? "problem" : "problems"} ` + + `(${totalErrorCount} ${totalErrorCount === 1 ? "error" : "errors"}) \n`; + } else { + summary += + `> ${totalErrorCount + totalWarningCount} problems ` + + `(${totalErrorCount} errors, ${totalWarningCount} warnings) \n`; + } if (totalFatalErrorCount) { summary += `> **${totalFatalErrorCount} fatal errors**\n`; } diff --git a/src/formatter/text.ts b/src/formatter/text.ts index 2ccd424ad..727116c5c 100644 --- a/src/formatter/text.ts +++ b/src/formatter/text.ts @@ -44,7 +44,7 @@ export class Text { constructor(private readonly cwd: string) { } - format(lintResults: LintResult[], showDetails: boolean, autofix: boolean) { + format(lintResults: LintResult[], showDetails: boolean, autofix: boolean, quiet = false) { this.#writeln(`UI5 linter report:`); this.#writeln(""); let totalErrorCount = 0; @@ -101,12 +101,22 @@ export class Text { summaryColor = chalk.yellow; } - this.#writeln( - summaryColor( - `${totalErrorCount + totalWarningCount} problems ` + - `(${totalErrorCount} errors, ${totalWarningCount} warnings)` - ) - ); + // In quiet mode, only mention errors in the summary + if (quiet) { + this.#writeln( + summaryColor( + `${totalErrorCount} ${totalErrorCount === 1 ? "problem" : "problems"} ` + + `(${totalErrorCount} ${totalErrorCount === 1 ? "error" : "errors"})` + ) + ); + } else { + this.#writeln( + summaryColor( + `${totalErrorCount + totalWarningCount} problems ` + + `(${totalErrorCount} errors, ${totalWarningCount} warnings)` + ) + ); + } if (!autofix && (totalErrorCount + totalWarningCount > 0)) { this.#writeln(" Run \"ui5lint --fix\" to resolve all auto-fixable problems\n"); } From 9f78e08f24ab18e37f6f68327f579ab473d85647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Emarianfoo=E2=80=9C?= <13335743+marianfoo@users.noreply.github.com> Date: Tue, 15 Apr 2025 21:51:01 +0200 Subject: [PATCH 3/8] refactor: Warning are shown in json summary --- src/formatter/json.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/formatter/json.ts b/src/formatter/json.ts index b4b5a9f07..74f619e24 100644 --- a/src/formatter/json.ts +++ b/src/formatter/json.ts @@ -1,7 +1,7 @@ import {LintMessage, LintResult} from "../linter/LinterContext.js"; export class Json { - format(lintResults: LintResult[], showDetails: boolean) { + format(lintResults: LintResult[], showDetails: boolean, quiet = false) { const jsonFormattedResults: Pick< LintResult, "filePath" @@ -28,7 +28,7 @@ export class Json { filePath: oLintedFile.filePath, messages: aFileMessages, errorCount: oLintedFile.errorCount, - warningCount: oLintedFile.warningCount, + warningCount: quiet ? 0 : oLintedFile.warningCount, fatalErrorCount: oLintedFile.fatalErrorCount, }); } From 5cc07208b5f8f1c916e4d4c7e5ded37015b4d5a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Emarianfoo=E2=80=9C?= <13335743+marianfoo@users.noreply.github.com> Date: Tue, 15 Apr 2025 22:40:38 +0200 Subject: [PATCH 4/8] test: Add tests --- test/lib/cli/base.integration.ts | 125 +++++++++++++++++++++++++++++-- test/lib/cli/base.ts | 35 +++++++++ 2 files changed, 155 insertions(+), 5 deletions(-) diff --git a/test/lib/cli/base.integration.ts b/test/lib/cli/base.integration.ts index 69defc509..161872cf8 100644 --- a/test/lib/cli/base.integration.ts +++ b/test/lib/cli/base.integration.ts @@ -34,15 +34,16 @@ test.afterEach.always(() => { test.serial("ui5lint --format json", async (t) => { const {cli, consoleLogStub, processCwdStub, processStdoutWriteStub, processExitStub} = t.context; + // Instead of testing actual process.exitCode + const before = process.exitCode; await cli.parseAsync(["--format", "json"]); + t.not(before, process.exitCode, "Exit code was changed"); + process.exitCode = 0; // Reset immediately t.is(consoleLogStub.callCount, 0, "console.log should not be used"); t.true(processCwdStub.callCount > 0, "process.cwd was called"); t.is(processStdoutWriteStub.callCount, 2, "process.stdout.write was called twice"); t.is(processExitStub.callCount, 0, "process.exit got never called"); - t.is(process.exitCode, 1, "process.exitCode was set to 1"); - // cleanup: reset exit code in order not to fail the test (it cannot be stubbed with sinon) - process.exitCode = 0; const resultProcessStdoutJSON = processStdoutWriteStub.firstCall.firstArg; let parsedJson: LintResult[]; @@ -66,14 +67,128 @@ test.serial("ui5lint --format markdown", async (t) => { t.true(processCwdStub.callCount > 0, "process.cwd was called"); t.is(processStdoutWriteStub.callCount, 2, "process.stdout.write was called twice"); t.is(processExitStub.callCount, 0, "process.exit got never called"); - t.is(process.exitCode, 1, "process.exitCode was set to 1"); - // cleanup: reset exit code in order not to fail the test (it cannot be stubbed with sinon) + + // Reset immediately process.exitCode = 0; const resultProcessStdoutMarkdown = processStdoutWriteStub.firstCall.firstArg; + t.true(resultProcessStdoutMarkdown.length > 0, "Output is not empty"); t.true(resultProcessStdoutMarkdown.startsWith("# UI5 Linter Report"), "Output starts with the expected Markdown header"); const resultProcessStdoutNL = processStdoutWriteStub.secondCall.firstArg; t.is(resultProcessStdoutNL, "\n", "second write only adds a single newline"); }); + +// Test for --quiet option with default formatter +test.serial("ui5lint --quiet", async (t) => { + const {cli, consoleLogStub, processCwdStub, processExitStub} = t.context; + + // We need to manually create a stderr stub since it's not in the context + const stderrWriteStub = sinon.stub(process.stderr, "write").returns(true); + + try { + // First run without quiet + await cli.parseAsync([]); + const normalOutput = stderrWriteStub.firstCall.firstArg; + t.true(normalOutput.length > 0, "Normal output is not empty"); + + // Reset the stub's history before the second run + stderrWriteStub.resetHistory(); + + // Then run with quiet + await cli.parseAsync(["--quiet"]); + const quietOutput = stderrWriteStub.firstCall.firstArg; + t.true(quietOutput.length > 0, "Quiet output is not empty"); + + t.is(consoleLogStub.callCount, 0, "console.log should not be used"); + t.true(processCwdStub.callCount > 0, "process.cwd was called"); + t.is(processExitStub.callCount, 0, "process.exit got never called"); + + // Reset immediately + process.exitCode = 0; + + // Check that quiet output is different from normal output + t.notDeepEqual(quietOutput, normalOutput, "Quiet output differs from normal output"); + + // Quiet output should not contain the word "warnings" in the summary + t.false(quietOutput.includes(" warnings)"), "Quiet output should not mention warnings count"); + } finally { + // Always restore the stub + stderrWriteStub.restore(); + // Ensure process.exitCode is reset + process.exitCode = 0; + } +}); + +// Test for --quiet option with JSON format +test.serial("ui5lint --quiet --format json", async (t) => { + const {cli, processExitStub, processStdoutWriteStub} = t.context; + + // Reset the stub's history + processStdoutWriteStub.resetHistory(); + + // First run without quiet + await cli.parseAsync(["--format", "json"]); + const normalJsonOutput = processStdoutWriteStub.firstCall.firstArg; + t.true(normalJsonOutput.length > 0, "Normal JSON output is not empty"); + + // Reset history for second run + processStdoutWriteStub.resetHistory(); + + // Run with quiet + await cli.parseAsync(["--quiet", "--format", "json"]); + const quietJsonOutput = processStdoutWriteStub.firstCall.firstArg; + t.true(quietJsonOutput.length > 0, "Quiet JSON output is not empty"); + + t.is(processExitStub.callCount, 0, "process.exit got never called"); + process.exitCode = 0; // Reset immediately + + // Parse and compare results + const normalJson = JSON.parse(normalJsonOutput) as LintResult[]; + const quietJson = JSON.parse(quietJsonOutput) as LintResult[]; + + // Verify quiet output has warningCount set to 0 + t.true(quietJson.some((file) => file.warningCount === 0), + "Quiet JSON output has warningCount set to 0"); + + // Compare with normalJson if it has any warnings + if (normalJson.some((file) => file.warningCount > 0)) { + t.notDeepEqual(normalJson, quietJson, "Quiet JSON output differs from normal JSON output"); + } +}); + +// Test for --quiet option with Markdown format +test.serial("ui5lint --quiet --format markdown", async (t) => { + const {cli, processExitStub, processStdoutWriteStub} = t.context; + + // Reset the stub's history + processStdoutWriteStub.resetHistory(); + + // First run without quiet + await cli.parseAsync(["--format", "markdown"]); + const normalMarkdownOutput = processStdoutWriteStub.firstCall.firstArg; + t.true(normalMarkdownOutput.length > 0, "Normal Markdown output is not empty"); + + // Reset history for second run + processStdoutWriteStub.resetHistory(); + + // Run with quiet + await cli.parseAsync(["--quiet", "--format", "markdown"]); + const quietMarkdownOutput = processStdoutWriteStub.firstCall.firstArg; + t.true(quietMarkdownOutput.length > 0, "Quiet Markdown output is not empty"); + + t.is(processExitStub.callCount, 0, "process.exit got never called"); + process.exitCode = 0; // Reset immediately + + // Check outputs + const errorMsg = "Quiet Markdown output differs from normal output"; + t.notDeepEqual(quietMarkdownOutput, normalMarkdownOutput, errorMsg); + + // Quiet output should not contain the word "warnings" in the summary + const warnMsg = "Quiet Markdown output should not mention warnings"; + t.false(quietMarkdownOutput.includes(" warnings"), warnMsg); +}); + +// Always reset exit code at the end +process.exitCode = 0; diff --git a/test/lib/cli/base.ts b/test/lib/cli/base.ts index dcefc8f7d..4cfc676b1 100644 --- a/test/lib/cli/base.ts +++ b/test/lib/cli/base.ts @@ -189,6 +189,41 @@ test.serial("ui5lint --ui5-config", async (t) => { }); }); +test.serial("ui5lint --quiet", async (t) => { + const {cli, ui5lint, formatText} = t.context; + + // Create a mock result with both errors and warnings + const lintResultWithErrorsAndWarnings: LintResult = { + filePath: "test.js", + messages: [ + {ruleId: "rule1", severity: 1, message: "Warning message"}, // Warning + {ruleId: "rule2", severity: 2, message: "Error message"}, // Error + ], + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + warningCount: 1, + }; + + // Override the default result with our custom one + ui5lint.resolves([lintResultWithErrorsAndWarnings]); + + await cli.parseAsync(["--quiet"]); + + t.true(ui5lint.calledOnce, "Linter is called"); + + // Verify that formatText is called with filtered results containing only errors + t.true(formatText.calledOnce, "Text formatter has been called"); + + const formatterResults = formatText.getCall(0).args[0]; + t.is(formatterResults[0].messages.length, 1, "Only error messages are included"); + t.is(formatterResults[0].messages[0].severity, 2, "Only messages with severity 2 (error) are kept"); + t.is(formatterResults[0].warningCount, 0, "Warning count is reset to 0"); + t.is(process.exitCode, 1, "Exit code is reset to 1"); + // reset process.exitCode + process.exitCode = 0; +}); + test.serial("ui5lint path/to/file.js glob/**/*", async (t) => { const {cli, ui5lint} = t.context; From 357e91ce385bcb5d98ce400506d2bae5e3cf3718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Emarianfoo=E2=80=9C?= <13335743+marianfoo@users.noreply.github.com> Date: Thu, 15 May 2025 11:00:16 +0200 Subject: [PATCH 5/8] refactor: Review changes --- README.md | 1 - src/cli/base.ts | 37 +++++++++++++++++-------------------- src/formatter/json.ts | 2 +- src/formatter/markdown.ts | 18 ++++++++++-------- src/formatter/text.ts | 27 ++++++++++++--------------- 5 files changed, 40 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 43b347579..859b1f3c4 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ - [`--details`](#--details) - [`--format`](#--format) - [`--fix`](#--fix) - - [Dry Run Mode](#dry-run-mode) - [`--quiet`](#--quiet) - [`--ignore-pattern`](#--ignore-pattern) - [`--config`](#--config) diff --git a/src/cli/base.ts b/src/cli/base.ts index b367021d6..934b3e083 100644 --- a/src/cli/base.ts +++ b/src/cli/base.ts @@ -10,7 +10,6 @@ import {isLogLevelEnabled} from "@ui5/logger"; import ConsoleWriter from "@ui5/logger/writers/Console"; import {getVersion} from "./version.js"; import {ui5lint} from "../index.js"; -import type {LintResult} from "../linter/LinterContext.js"; import {LintMessageSeverity} from "../linter/messages.js"; export interface LinterArg { @@ -180,39 +179,37 @@ async function handleLint(argv: ArgumentsCamelCase) { ui5Config, }); - // Define a simple function to filter a single result set - const applyQuietFilter = (results: LintResult[]): LintResult[] => { - if (!quiet) { - return results; + // Apply quiet mode filtering directly to the results if needed + if (quiet) { + // Filter out warnings from all result objects + for (const result of res) { + // Keep only error messages (severity === 2) + result.messages = result.messages.filter((msg) => msg.severity === LintMessageSeverity.Error); + // Reset warning counts + result.warningCount = 0; + // Reset fixableWarningCount if it exists + if ("fixableWarningCount" in result) { + result.fixableWarningCount = 0; + } } - return results.map((file) => ({ - ...file, - messages: file.messages.filter((msg) => msg.severity === LintMessageSeverity.Error), - warningCount: 0, - fixableWarningCount: 0, - })); - }; + } if (coverage) { const coverageFormatter = new Coverage(); - const filteredResults = applyQuietFilter(res); - await writeFile("ui5lint-report.html", await coverageFormatter.format(filteredResults, new Date())); + await writeFile("ui5lint-report.html", await coverageFormatter.format(res, new Date())); } if (format === "json") { const jsonFormatter = new Json(); - const filteredResults = applyQuietFilter(res); - process.stdout.write(jsonFormatter.format(filteredResults, details, quiet)); + process.stdout.write(jsonFormatter.format(res, details, quiet)); process.stdout.write("\n"); } else if (format === "markdown") { const markdownFormatter = new Markdown(); - const filteredResults = applyQuietFilter(res); - process.stdout.write(markdownFormatter.format(filteredResults, details, getVersion(), fix, quiet)); + process.stdout.write(markdownFormatter.format(res, details, getVersion(), fix, quiet)); process.stdout.write("\n"); } else if (format === "" || format === "stylish") { const textFormatter = new Text(rootDir); - const filteredResults = applyQuietFilter(res); - process.stderr.write(textFormatter.format(filteredResults, details, fix, quiet)); + process.stderr.write(textFormatter.format(res, details, fix, quiet)); } // Stop profiling after CLI finished execution if (profile) { diff --git a/src/formatter/json.ts b/src/formatter/json.ts index 74f619e24..c604977dd 100644 --- a/src/formatter/json.ts +++ b/src/formatter/json.ts @@ -28,7 +28,7 @@ export class Json { filePath: oLintedFile.filePath, messages: aFileMessages, errorCount: oLintedFile.errorCount, - warningCount: quiet ? 0 : oLintedFile.warningCount, + warningCount: oLintedFile.warningCount, fatalErrorCount: oLintedFile.fatalErrorCount, }); } diff --git a/src/formatter/markdown.ts b/src/formatter/markdown.ts index f5800b2d0..8bba0173c 100644 --- a/src/formatter/markdown.ts +++ b/src/formatter/markdown.ts @@ -65,15 +65,17 @@ export class Markdown { }); let summary = "## Summary\n\n"; - if (quiet) { - summary += - `> ${totalErrorCount} ${totalErrorCount === 1 ? "problem" : "problems"} ` + - `(${totalErrorCount} ${totalErrorCount === 1 ? "error" : "errors"}) \n`; - } else { - summary += - `> ${totalErrorCount + totalWarningCount} problems ` + - `(${totalErrorCount} errors, ${totalWarningCount} warnings) \n`; + const errorsText = `${totalErrorCount} ${totalErrorCount === 1 ? "error" : "errors"}`; + let warningsText = ""; + if (!quiet) { + warningsText = `, ${totalWarningCount} ${totalWarningCount === 1 ? "warning" : "warnings"}`; } + + const totalCount = quiet ? totalErrorCount : totalErrorCount + totalWarningCount; + const problemsText = `${totalCount} ${totalCount === 1 ? "problem" : "problems"}`; + + summary += `> ${problemsText} (${errorsText}${warningsText}) \n`; + if (totalFatalErrorCount) { summary += `> **${totalFatalErrorCount} fatal errors**\n`; } diff --git a/src/formatter/text.ts b/src/formatter/text.ts index 727116c5c..753dfbf74 100644 --- a/src/formatter/text.ts +++ b/src/formatter/text.ts @@ -101,22 +101,19 @@ export class Text { summaryColor = chalk.yellow; } - // In quiet mode, only mention errors in the summary - if (quiet) { - this.#writeln( - summaryColor( - `${totalErrorCount} ${totalErrorCount === 1 ? "problem" : "problems"} ` + - `(${totalErrorCount} ${totalErrorCount === 1 ? "error" : "errors"})` - ) - ); - } else { - this.#writeln( - summaryColor( - `${totalErrorCount + totalWarningCount} problems ` + - `(${totalErrorCount} errors, ${totalWarningCount} warnings)` - ) - ); + const errorsText = `${totalErrorCount} ${totalErrorCount === 1 ? "error" : "errors"}`; + let warningsText = ""; + if (!quiet) { + warningsText = `, ${totalWarningCount} ${totalWarningCount === 1 ? "warning" : "warnings"}`; } + + const totalCount = quiet ? totalErrorCount : totalErrorCount + totalWarningCount; + const problemsText = `${totalCount} ${totalCount === 1 ? "problem" : "problems"}`; + + this.#writeln( + summaryColor(`${problemsText} (${errorsText}${warningsText})`) + ); + if (!autofix && (totalErrorCount + totalWarningCount > 0)) { this.#writeln(" Run \"ui5lint --fix\" to resolve all auto-fixable problems\n"); } From c842f24d5e67c14cca1e86af2118916db77b631e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Emarianfoo=E2=80=9C?= <13335743+marianfoo@users.noreply.github.com> Date: Thu, 15 May 2025 11:06:16 +0200 Subject: [PATCH 6/8] refactor: Revert exit tests --- test/lib/cli/base.integration.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/test/lib/cli/base.integration.ts b/test/lib/cli/base.integration.ts index 161872cf8..effd5f2f2 100644 --- a/test/lib/cli/base.integration.ts +++ b/test/lib/cli/base.integration.ts @@ -34,16 +34,15 @@ test.afterEach.always(() => { test.serial("ui5lint --format json", async (t) => { const {cli, consoleLogStub, processCwdStub, processStdoutWriteStub, processExitStub} = t.context; - // Instead of testing actual process.exitCode - const before = process.exitCode; await cli.parseAsync(["--format", "json"]); - t.not(before, process.exitCode, "Exit code was changed"); - process.exitCode = 0; // Reset immediately t.is(consoleLogStub.callCount, 0, "console.log should not be used"); t.true(processCwdStub.callCount > 0, "process.cwd was called"); t.is(processStdoutWriteStub.callCount, 2, "process.stdout.write was called twice"); t.is(processExitStub.callCount, 0, "process.exit got never called"); + t.is(process.exitCode, 1, "process.exitCode was set to 1"); + // cleanup: reset exit code in order not to fail the test (it cannot be stubbed with sinon) + process.exitCode = 0; const resultProcessStdoutJSON = processStdoutWriteStub.firstCall.firstArg; let parsedJson: LintResult[]; @@ -67,12 +66,11 @@ test.serial("ui5lint --format markdown", async (t) => { t.true(processCwdStub.callCount > 0, "process.cwd was called"); t.is(processStdoutWriteStub.callCount, 2, "process.stdout.write was called twice"); t.is(processExitStub.callCount, 0, "process.exit got never called"); - - // Reset immediately + t.is(process.exitCode, 1, "process.exitCode was set to 1"); + // cleanup: reset exit code in order not to fail the test (it cannot be stubbed with sinon) process.exitCode = 0; const resultProcessStdoutMarkdown = processStdoutWriteStub.firstCall.firstArg; - t.true(resultProcessStdoutMarkdown.length > 0, "Output is not empty"); t.true(resultProcessStdoutMarkdown.startsWith("# UI5 Linter Report"), "Output starts with the expected Markdown header"); From 17d389e1013bd2f07c46528af2df13e898b59571 Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Thu, 26 Jun 2025 16:58:08 +0200 Subject: [PATCH 7/8] test: Update snapshots Also fix minor eslint error. --- src/formatter/json.ts | 2 +- test/lib/formatter/snapshots/text.ts.md | 4 ++-- test/lib/formatter/snapshots/text.ts.snap | Bin 433 -> 432 bytes 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/formatter/json.ts b/src/formatter/json.ts index c604977dd..f6077d172 100644 --- a/src/formatter/json.ts +++ b/src/formatter/json.ts @@ -1,7 +1,7 @@ import {LintMessage, LintResult} from "../linter/LinterContext.js"; export class Json { - format(lintResults: LintResult[], showDetails: boolean, quiet = false) { + format(lintResults: LintResult[], showDetails: boolean, _quiet = false) { const jsonFormattedResults: Pick< LintResult, "filePath" diff --git a/test/lib/formatter/snapshots/text.ts.md b/test/lib/formatter/snapshots/text.ts.md index 1341ea963..1866c2721 100644 --- a/test/lib/formatter/snapshots/text.ts.md +++ b/test/lib/formatter/snapshots/text.ts.md @@ -13,7 +13,7 @@ Generated by [AVA](https://avajs.dev). /Test.js␊ 5:1 error Call to deprecated function 'attachInit' of class 'Core'. Details: (since 1.118) - Please use {@link sap.ui.core.Core.ready Core.ready} instead. no-deprecated-api␊ ␊ - 1 problems (1 errors, 0 warnings)␊ + 1 problem (1 error, 0 warnings)␊ Run "ui5lint --fix" to resolve all auto-fixable problems␊ ␊ ` @@ -27,7 +27,7 @@ Generated by [AVA](https://avajs.dev). /Test.js␊ 5:1 error Call to deprecated function 'attachInit' of class 'Core' no-deprecated-api␊ ␊ - 1 problems (1 errors, 0 warnings)␊ + 1 problem (1 error, 0 warnings)␊ Run "ui5lint --fix" to resolve all auto-fixable problems␊ ␊ ␊ diff --git a/test/lib/formatter/snapshots/text.ts.snap b/test/lib/formatter/snapshots/text.ts.snap index 0f94af4d3471dbbad46a7d42bee62a04288c4a58..3a70431a6298afad2e21d9e7ed05113207557060 100644 GIT binary patch literal 432 zcmV;h0Z;xxRzVzx#ame_7>^1o zcaE(`#vh9a00000000BMkwI$|K@f#^4I=c-fAGjte)Zn_uKOlPd#P79Y~BLzUYxTU&Ut6}AZbHp zoUqt>8&H&G$3{LVEKOW2y+#^!5YwO6T}MWr=I{4r&t`e%PiN1cyqaH}tPp(6B8f*y zws!7yL&_M*hBq(swd#vHV70EnDM`XHg#Z(DJSJ|*xI;h1*1SX%WJc|961^3W9&I31 z6vvXdsPOK$y)_o#982cb5h3lx539tD2>(Rt5yNs0n;8sLtKPIQTCZJq7(YjibM4EFYD|eX<;(kvhl@PR2aAJyC(Dbo4T6tZB=IE4 z8t0z0q>PDdeEKL~tKO>vHcbsqNfJ&e1el=bDRD=}J%%}U<|U#aGwQ~(=&gVZ=mM#t zIF-ajh37x)HCU;S9iy)5`ecm~uLI|svobzC3qD>#X{u&lb&|NszpKR5f5hGMGkjEI z>MI~hd1t1i$vanLO40`2Dpohf^%#$_qZEC7r)vk`%^Yz!`%P{DWjXl$VGfp9C46Ay zoHU!{1+{^HN;-Fo5UwJeuEl9Q{)s|D!keN{ky^DE}&O)6=zxj Date: Thu, 26 Jun 2025 17:02:52 +0200 Subject: [PATCH 8/8] docs(README): Formatting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 859b1f3c4..70def16b9 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,7 @@ In this mode, the linter will show the messages after the fixes would have been #### `--quiet` -Report errors only and hide warnings. Similar to ESLint's --quiet option. +Report errors only, hiding warnings. Similar to ESLint's `--quiet` option. **Example:** ```sh