fix: improve error reporting on GitHub (#2629)
Group success and skip so it's easy to see the failures!
Update skip icon to ⏭️ so they are easier to scan
Fixes https://github.com/dart-lang/test/issues/2628
diff --git a/pkgs/test/CHANGELOG.md b/pkgs/test/CHANGELOG.md
index a2aefbb..8246b5e 100644
--- a/pkgs/test/CHANGELOG.md
+++ b/pkgs/test/CHANGELOG.md
@@ -3,8 +3,12 @@
* Ignore an error locating the SDK directory on platforms where the
`resolvedExecutable` is unexpectedly `null`.
* Fix a bug where `-c exe` tests would hang on exit on windows.
-* Allow `analyzer` version `13.x.x`.
+* `GithubReporter`:
+ * Group contiguous passing and skipped tests into collapsible groups to
+ reduce log noise in GitHub Actions.
+ * Updated skipped icon to ⏭️.
* Increase SDK constraint to ^3.10.0.
+* Allow `analyzer` version `13.x.x`.
## 1.31.0
diff --git a/pkgs/test/test/runner/github_reporter_test.dart b/pkgs/test/test/runner/github_reporter_test.dart
index f52ceb5..faa1ba6 100644
--- a/pkgs/test/test/runner/github_reporter_test.dart
+++ b/pkgs/test/test/runner/github_reporter_test.dart
@@ -30,9 +30,11 @@
test('success 2', () {});
test('success 3', () {});''',
'''
+ ::group::✅ Passing tests
✅ success 1
✅ success 2
✅ success 3
+ ::endgroup::
🎉 3 tests passed.''',
);
});
@@ -42,8 +44,10 @@
'''
test('success 1', () {});''',
[
+ '::group::✅ Passing tests',
'✅ [VM, Kernel] success 1',
'✅ [Chrome, Dart2Js] success 1',
+ '::endgroup::',
'🎉 2 tests passed.',
],
args: ['-p', 'vm,chrome'],
@@ -104,12 +108,16 @@
oh no
test.dart 6:33 main.<fn>
::endgroup::
+ ::group::✅ Passing tests
✅ success 1
+ ::endgroup::
::group::❌ failure 2 (failed)
oh no
test.dart 8:33 main.<fn>
::endgroup::
+ ::group::✅ Passing tests
✅ success 2
+ ::endgroup::
::error::2 tests passed, 2 failed.''',
);
});
@@ -148,7 +156,9 @@
third error
test.dart 12:34 main.<fn>.<fn>
::endgroup::
+ ::group::✅ Passing tests
✅ wait
+ ::endgroup::
::error::1 test passed, 1 failed.''',
);
});
@@ -164,7 +174,9 @@
test('second test so that the first failure is reported', () {});''',
'''
+ ::group::✅ Passing tests
✅ fail after completion
+ ::endgroup::
::group::❌ fail after completion (failed after test completion)
foo
test.dart 8:62 main.<fn>.<fn>
@@ -175,7 +187,9 @@
of pending async work.
test.dart 8:62 main.<fn>.<fn>
::endgroup::
+ ::group::✅ Passing tests
✅ second test so that the first failure is reported
+ ::endgroup::
::error::1 test passed, 1 failed.''',
);
});
@@ -223,12 +237,16 @@
return testDone.future;
});''',
'''
+ ::group::✅ Passing tests
✅ test
+ ::endgroup::
one
two
three
four
+ ::group::✅ Passing tests
✅ wait
+ ::endgroup::
🎉 2 tests passed.''',
);
});
@@ -242,9 +260,11 @@
test('skip 2', () {}, skip: true);
test('skip 3', () {}, skip: true);''',
'''
- ❎ skip 1 (skipped)
- ❎ skip 2 (skipped)
- ❎ skip 3 (skipped)
+ ::group::⏭️ Skipped tests
+ ⏭️ skip 1 (skipped)
+ ⏭️ skip 2 (skipped)
+ ⏭️ skip 3 (skipped)
+ ::endgroup::
🎉 0 tests passed, 3 skipped.''',
);
});
@@ -258,9 +278,11 @@
test('test 3', () {});
}, skip: true);''',
'''
- ❎ skip test 1 (skipped)
- ❎ skip test 2 (skipped)
- ❎ skip test 3 (skipped)
+ ::group::⏭️ Skipped tests
+ ⏭️ skip test 1 (skipped)
+ ⏭️ skip test 2 (skipped)
+ ⏭️ skip test 3 (skipped)
+ ::endgroup::
🎉 0 tests passed, 3 skipped.''',
);
});
@@ -273,10 +295,18 @@
test('skip 2', () {}, skip: true);
test('success 2', () {});''',
'''
- ❎ skip 1 (skipped)
+ ::group::⏭️ Skipped tests
+ ⏭️ skip 1 (skipped)
+ ::endgroup::
+ ::group::✅ Passing tests
✅ success 1
- ❎ skip 2 (skipped)
+ ::endgroup::
+ ::group::⏭️ Skipped tests
+ ⏭️ skip 2 (skipped)
+ ::endgroup::
+ ::group::✅ Passing tests
✅ success 2
+ ::endgroup::
🎉 2 tests passed, 2 skipped.''',
);
});
@@ -295,14 +325,22 @@
oh no
test.dart 6:35 main.<fn>
::endgroup::
- ❎ skip 1 (skipped)
+ ::group::⏭️ Skipped tests
+ ⏭️ skip 1 (skipped)
+ ::endgroup::
+ ::group::✅ Passing tests
✅ success 1
+ ::endgroup::
::group::❌ failure 2 (failed)
oh no
test.dart 9:35 main.<fn>
::endgroup::
- ❎ skip 2 (skipped)
+ ::group::⏭️ Skipped tests
+ ⏭️ skip 2 (skipped)
+ ::endgroup::
+ ::group::✅ Passing tests
✅ success 2
+ ::endgroup::
::error::2 tests passed, 2 failed, 2 skipped.''',
);
});
@@ -313,10 +351,10 @@
test('skip 1', () {}, skip: 'some reason');
test('skip 2', () {}, skip: 'or another');''',
'''
- ::group::❎ skip 1 (skipped)
+ ::group::⏭️ Skipped tests
+ ⏭️ skip 1 (skipped)
Skip: some reason
- ::endgroup::
- ::group::❎ skip 2 (skipped)
+ ⏭️ skip 2 (skipped)
Skip: or another
::endgroup::
🎉 0 tests passed, 2 skipped.''',
@@ -333,7 +371,9 @@
test('test 1', () {});
});''',
'''
+ ::group::✅ Passing tests
✅ one test 1
+ ::endgroup::
🎉 1 test passed.''',
);
});
@@ -350,7 +390,9 @@
::group::✅ one (setUpAll)
one
::endgroup::
+ ::group::✅ Passing tests
✅ one test 1
+ ::endgroup::
::group::✅ one (tearDownAll)
two
::endgroup::
diff --git a/pkgs/test_core/CHANGELOG.md b/pkgs/test_core/CHANGELOG.md
index be69f08..058346f 100644
--- a/pkgs/test_core/CHANGELOG.md
+++ b/pkgs/test_core/CHANGELOG.md
@@ -6,6 +6,10 @@
environment variables for color output detection.
* Fix a bug where `-c exe` tests would hang on exit on windows.
* Update `parse_metadata.dart` to be compatible with `analyzer >=8.0.0 <14.0.0`.
+* `GithubReporter`:
+ * Group contiguous passing and skipped tests into collapsible groups to
+ reduce log noise in GitHub Actions.
+ * Updated skipped icon to ⏭️.
## 0.6.17
diff --git a/pkgs/test_core/lib/src/runner/reporter/github.dart b/pkgs/test_core/lib/src/runner/reporter/github.dart
index 39094b5..48d42db 100644
--- a/pkgs/test_core/lib/src/runner/reporter/github.dart
+++ b/pkgs/test_core/lib/src/runner/reporter/github.dart
@@ -44,6 +44,9 @@
final Set<LiveTest> _completedTests = {};
+ /// The github markdown `::group::` that is currently open.
+ var _activeGroup = _ReportGroup.ungrouped;
+
/// Watches the tests run by [engine] and prints their results as JSON.
static GithubReporter watch(
Engine engine,
@@ -112,6 +115,10 @@
if (_completedTests.contains(liveTest)) {
// The test has already completed and it's previous messages were
// written out; ensure this post-completion output is not lost.
+ if (!_activeGroup.isUngrouped) {
+ _sink.writeln(_GithubMarkup.endGroup);
+ _activeGroup = _ReportGroup.ungrouped;
+ }
_sink.writeln(message.text);
} else {
_testMessages.putIfAbsent(liveTest, () => []).add(message);
@@ -166,9 +173,32 @@
'[${test.suite.platform.runtime.name}, '
'${test.suite.platform.compiler.name}] $name';
}
- if (messages.isEmpty && errors.isEmpty) {
+ if (skipped) {
+ if (_activeGroup.isPassing) {
+ _sink.writeln(_GithubMarkup.endGroup);
+ }
+ if (!_activeGroup.isSkipped) {
+ _sink.writeln(_GithubMarkup.startGroup('⏭️ Skipped tests'));
+ _activeGroup = _ReportGroup.skipped;
+ }
+ _sink.writeln('$prefix $name$statusSuffix');
+ for (var message in messages) {
+ _sink.writeln(message.text);
+ }
+ } else if (messages.isEmpty && errors.isEmpty) {
+ if (_activeGroup.isSkipped) {
+ _sink.writeln(_GithubMarkup.endGroup);
+ }
+ if (!_activeGroup.isPassing) {
+ _sink.writeln(_GithubMarkup.startGroup('✅ Passing tests'));
+ _activeGroup = _ReportGroup.passing;
+ }
_sink.writeln('$prefix $name$statusSuffix');
} else {
+ if (!_activeGroup.isUngrouped) {
+ _sink.writeln(_GithubMarkup.endGroup);
+ _activeGroup = _ReportGroup.ungrouped;
+ }
_sink.writeln(_GithubMarkup.startGroup('$prefix $name$statusSuffix'));
for (var message in messages) {
_sink.writeln(message.text);
@@ -201,6 +231,10 @@
'${test.suite.platform.compiler.name}] $name';
}
+ if (!_activeGroup.isUngrouped) {
+ _sink.writeln(_GithubMarkup.endGroup);
+ _activeGroup = _ReportGroup.ungrouped;
+ }
_sink.writeln(_GithubMarkup.startGroup('$prefix $name$statusSuffix'));
_sink.writeln('$error');
_sink.writeln(stackTrace.toString().trimRight());
@@ -211,6 +245,11 @@
void _onDone(bool? success) {
_cancel();
+ if (!_activeGroup.isUngrouped) {
+ _sink.writeln(_GithubMarkup.endGroup);
+ _activeGroup = _ReportGroup.ungrouped;
+ }
+
_sink.writeln();
final hadFailures = _engine.failed.isNotEmpty;
@@ -236,7 +275,7 @@
abstract class _GithubMarkup {
// Char sets avilable at https://www.compart.com/en/unicode/.
static const String passed = '✅';
- static const String skipped = '❎';
+ static const String skipped = '⏭️';
static const String failed = '❌';
// The 'synthetic' icon is currently not used but is something to consider in
// order to draw a distinction between user tests and test-like supporting
@@ -251,3 +290,13 @@
static String error(String message) => '::error::$message';
}
+
+enum _ReportGroup {
+ passing,
+ skipped,
+ ungrouped;
+
+ bool get isPassing => this == _ReportGroup.passing;
+ bool get isSkipped => this == _ReportGroup.skipped;
+ bool get isUngrouped => this == _ReportGroup.ungrouped;
+}