diff --git a/README.md b/README.md index 7fc5e76..8761f6e 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,8 @@ Check [example](https://github.com/talesbarreto/pull_request_coverage/tree/main/ - **report-fully-covered-files** (`true`): Prints the file path of each fully covered file as a celebratory message =) +- **report-only** (`false`): When set to `true`, suppresses intermediate file reports and outputs only the final summary table. This is useful for logging, CI summaries, or other scenarios where detailed file-by-file analysis is not needed. Works with both `cli` and `markdown` output modes. + - **show-uncovered-code** (`true`): When set to `true`, the source code of the uncovered lines will be printed in red font color, making it easier to identify the missing tests. If this parameter is set to `false`, only the file path will be logged. - **use-colorful-output** (`true`): By default, `pull_request_coverage` utilizes a colorful font to highlight uncovered lines. You can disable this feature by setting this parameter to `false`. Please note that this option is only available when the output mode is set to `cli`. diff --git a/lib/src/data/user_settings/user_settings_repository_impl.dart b/lib/src/data/user_settings/user_settings_repository_impl.dart index b7c9fa3..169fb62 100644 --- a/lib/src/data/user_settings/user_settings_repository_impl.dart +++ b/lib/src/data/user_settings/user_settings_repository_impl.dart @@ -103,6 +103,7 @@ class UserSettingsRepositoryImpl implements UserSettingsRepository { showUncoveredCode: arg.getBooleanOrDefault(UserSettingsRegister.showUncoveredCode), useColorfulOutput: arg.getBooleanOrDefault(UserSettingsRegister.useColorfulOutput), reportFullyCoveredFiles: arg.getBooleanOrDefault(UserSettingsRegister.reportFullyCoveredFiles), + reportOnly: arg.getBooleanOrDefault(UserSettingsRegister.reportOnly), outputMode: arg.getString(UserSettingsRegister.outputMode) == "markdown" ? OutputMode.markdown : OutputMode.cli, fractionalDigits: arg.getInt(UserSettingsRegister.fractionDigits) ?? 2, markdownMode: diff --git a/lib/src/domain/user_settings/models/user_settings.dart b/lib/src/domain/user_settings/models/user_settings.dart index bf97ba9..33c2385 100644 --- a/lib/src/domain/user_settings/models/user_settings.dart +++ b/lib/src/domain/user_settings/models/user_settings.dart @@ -16,6 +16,7 @@ class UserSettings { final bool ignoreKnownGeneratedFiles; final bool reportFullyCoveredFiles; final bool useEmojis; + final bool reportOnly; final OutputMode outputMode; final MarkdownMode markdownMode; final int fractionalDigits; @@ -40,6 +41,7 @@ class UserSettings { this.useColorfulOutput = true, this.ignoreKnownGeneratedFiles = true, this.reportFullyCoveredFiles = true, + this.reportOnly = false, this.stdinTimeout = const Duration(seconds: 1), this.deprecatedFilterSet = false, this.lineFilters = const [], diff --git a/lib/src/domain/user_settings/user_settings_register.dart b/lib/src/domain/user_settings/user_settings_register.dart index 11d5e37..da7698a 100644 --- a/lib/src/domain/user_settings/user_settings_register.dart +++ b/lib/src/domain/user_settings/user_settings_register.dart @@ -30,6 +30,7 @@ class UserSettingsRegister { useColorfulOutput, showUncoveredCode, reportFullyCoveredFiles, + reportOnly, outputMode, markdownMode, fractionDigits, @@ -94,6 +95,12 @@ class UserSettingsRegister { defaultValue: true, ); + static const reportOnly = UserSettingsRegister( + names: ["report-only"], + description: "Suppress intermediate file reports and output only the final summary table", + defaultValue: false, + ); + static const outputMode = UserSettingsRegister( names: ["output-mode"], defaultValue: "cli", diff --git a/lib/src/presentation/output_generator/cli_output_generator.dart b/lib/src/presentation/output_generator/cli_output_generator.dart index 122d0fb..ed0b3bf 100644 --- a/lib/src/presentation/output_generator/cli_output_generator.dart +++ b/lib/src/presentation/output_generator/cli_output_generator.dart @@ -22,6 +22,9 @@ class CliOutputGenerator implements OutputGenerator { required this.print, }); + @override + bool get reportOnly => userSettings.reportOnly; + final _missingTestFilesReport = StringBuffer(); String? _getLine(FileLine fileLine) { @@ -74,6 +77,8 @@ class CliOutputGenerator implements OutputGenerator { @override void addFileReport(FileReport fileReport) { + if (reportOnly) return; + final stringBuffer = StringBuffer(); if (fileReport.linesMissingTestsCount > 0 || userSettings.reportFullyCoveredFiles) { stringBuffer.write(_getFileHeader(fileReport)); diff --git a/lib/src/presentation/output_generator/markdown_output_generator.dart b/lib/src/presentation/output_generator/markdown_output_generator.dart index 752eb7a..d465848 100644 --- a/lib/src/presentation/output_generator/markdown_output_generator.dart +++ b/lib/src/presentation/output_generator/markdown_output_generator.dart @@ -20,6 +20,9 @@ class MarkdownOutputGenerator implements OutputGenerator { required this.print, }); + @override + bool get reportOnly => userSettings.reportOnly; + final _missingTestFilesReport = StringBuffer(); String? _getSourceCodeHeader() => userSettings.markdownMode == MarkdownMode.diff ? "```diff\n" : "```dart\n"; @@ -87,6 +90,8 @@ class MarkdownOutputGenerator implements OutputGenerator { @override Future addFileReport(FileReport fileReport) async { + if (reportOnly) return; + final stringBuffer = StringBuffer(); if (fileReport.linesMissingTestsCount > 0 || userSettings.reportFullyCoveredFiles) { stringBuffer.writeln(_getFileHeader(fileReport)); diff --git a/lib/src/presentation/output_generator/output_generator.dart b/lib/src/presentation/output_generator/output_generator.dart index 3dd4835..6dc1ea3 100644 --- a/lib/src/presentation/output_generator/output_generator.dart +++ b/lib/src/presentation/output_generator/output_generator.dart @@ -9,6 +9,8 @@ abstract class OutputGenerator { static const successEmoji = "✅"; static const failEmoji = "❌"; + bool get reportOnly; + void addFileReport(FileReport report); void terminate(AnalysisResult analysisResult); diff --git a/test/src/presentation/output_generator/text_output_generator_test.dart b/test/src/presentation/output_generator/text_output_generator_test.dart index ac0e9bc..f32c978 100644 --- a/test/src/presentation/output_generator/text_output_generator_test.dart +++ b/test/src/presentation/output_generator/text_output_generator_test.dart @@ -1,4 +1,5 @@ import 'package:pull_request_coverage/src/domain/analyzer/models/analysis_result.dart'; +import 'package:pull_request_coverage/src/domain/analyzer/models/file_report.dart'; import 'package:pull_request_coverage/src/domain/user_settings/models/user_settings.dart'; import 'package:pull_request_coverage/src/presentation/output_generator/cli_output_generator.dart'; import 'package:pull_request_coverage/src/presentation/output_generator/markdown_output_generator.dart'; @@ -98,6 +99,55 @@ void main() { ); }); }); + + group("When `reportOnly` is true", () { + testGenerator( + "should suppress file reports", + UserSettings(reportOnly: true), + (generator, output) { + const fileReport = FileReport( + filePath: "test/file.dart", + chunks: [], + newLinesCount: 10, + linesThatShouldBeTestedCount: 10, + linesMissingTestsCount: 5, + untestedAndIgnoredLines: 0, + ); + + generator.addFileReport(fileReport); + + // Verify no output was produced from addFileReport + expect(output.toString(), isEmpty); + }, + ); + + testGenerator( + "should only output summary table on terminate", + UserSettings(reportOnly: true), + (generator, output) { + const fileReport = FileReport( + filePath: "test/file.dart", + chunks: [], + newLinesCount: 10, + linesThatShouldBeTestedCount: 10, + linesMissingTestsCount: 5, + untestedAndIgnoredLines: 0, + ); + + const result = AnalysisResult( + linesThatShouldBeTested: 10, + linesMissingTests: 5, + untestedIgnoredLines: 0, + ); + + generator.addFileReport(fileReport); + generator.terminate(result); + + // Verify only the result table was output + expect(output.toString(), equals(FakeGetResultTable.table)); + }, + ); + }); } class FakeGetResultTable implements GetResultTable {