blob: 28bacafed1ad125475145215849cd75adac67e8b [file] [log] [blame]
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@TestOn('vm')
import 'dart:async';
import 'package:test/test.dart';
import 'package:test_descriptor/test_descriptor.dart' as d;
import '../io.dart';
void main() {
setUpAll(precompileTestExecutable);
test('reports when no tests are run', () async {
await d.file('test.dart', 'void main() {}').create();
var test = await runTest(['test.dart'], reporter: 'github');
expect(test.stdout, emitsThrough(contains('0 tests passed')));
await test.shouldExit(79);
});
test('runs several successful tests and reports when each completes', () {
return _expectReport('''
test('success 1', () {});
test('success 2', () {});
test('success 3', () {});''', '''
::group::✅ success 1
::endgroup::
::group::✅ success 2
::endgroup::
::group::✅ success 3
::endgroup::
🎉 3 tests passed.''');
});
test('includes the platform name when multiple platforms are ran', () {
return _expectReportLines('''
test('success 1', () {});''', [
'::group::✅ [VM] success 1',
'::endgroup::',
'::group::✅ [Chrome] success 1',
'::endgroup::',
'🎉 2 tests passed.',
], args: [
'-p',
'vm,chrome'
]);
});
test('runs several failing tests and reports when each fails', () {
return _expectReport('''
test('failure 1', () => throw TestFailure('oh no'));
test('failure 2', () => throw TestFailure('oh no'));
test('failure 3', () => throw TestFailure('oh no'));''', '''
::group::❌ failure 1 (failed)
oh no
test.dart 6:33 main.<fn>
::endgroup::
::group::❌ failure 2 (failed)
oh no
test.dart 7:33 main.<fn>
::endgroup::
::group::❌ failure 3 (failed)
oh no
test.dart 8:33 main.<fn>
::endgroup::
::error::0 tests passed, 3 failed.''');
});
test('includes the full stack trace with --verbose-trace', () async {
await d.file('test.dart', '''
import 'dart:async';
import 'package:test/test.dart';
void main() {
test("failure", () => throw "oh no");
}
''').create();
var test =
await runTest(['--verbose-trace', 'test.dart'], reporter: 'github');
expect(test.stdout, emitsThrough(contains('dart:async')));
await test.shouldExit(1);
});
test('runs failing tests along with successful tests', () {
return _expectReport('''
test('failure 1', () => throw TestFailure('oh no'));
test('success 1', () {});
test('failure 2', () => throw TestFailure('oh no'));
test('success 2', () {});''', '''
::group::❌ failure 1 (failed)
oh no
test.dart 6:33 main.<fn>
::endgroup::
::group::✅ success 1
::endgroup::
::group::❌ failure 2 (failed)
oh no
test.dart 8:33 main.<fn>
::endgroup::
::group::✅ success 2
::endgroup::
::error::2 tests passed, 2 failed.''');
});
test('always prints the full test name', () {
return _expectReport(
'''
test(
'really gosh dang long test name. Even longer than that. No, yet '
'longer. A little more... okay, that should do it.',
() {});''',
'''
::group::✅ really gosh dang long test name. Even longer than that. No, yet longer. A little more... okay, that should do it.
::endgroup::''',
useContains: true,
);
});
test('gracefully handles multiple test failures in a row', () {
return _expectReport('''
// This completer ensures that the test isolate isn't killed until all
// errors have been thrown.
var completer = Completer();
test('failures', () {
Future.microtask(() => throw 'first error');
Future.microtask(() => throw 'second error');
Future.microtask(() => throw 'third error');
Future.microtask(completer.complete);
});
test('wait', () => completer.future);''', '''
::group::❌ failures (failed)
first error
test.dart 10:34 main.<fn>.<fn>
second error
test.dart 11:34 main.<fn>.<fn>
third error
test.dart 12:34 main.<fn>.<fn>
::endgroup::
::group::✅ wait
::endgroup::
::error::1 test passed, 1 failed.''');
});
group('print:', () {
test('handles multiple prints', () {
return _expectReport(
'''
test('test', () {
print("one");
print("two");
print("three");
print("four");
});''',
'''
::group::✅ test
one
two
three
four
::endgroup::''',
useContains: true,
);
});
test('handles a print after the test completes', () {
return _expectReport('''
// This completer ensures that the test isolate isn't killed until all
// prints have happened.
var testDone = Completer();
var waitStarted = Completer();
test('test', () async {
waitStarted.future.then((_) {
Future(() => print("one"));
Future(() => print("two"));
Future(() => print("three"));
Future(() => print("four"));
Future(testDone.complete);
});
});
test('wait', () {
waitStarted.complete();
return testDone.future;
});''', '''
::group::✅ test
::endgroup::
one
two
three
four
::group::✅ wait
::endgroup::
🎉 2 tests passed.''');
});
});
group('skip:', () {
test('displays skipped tests', () {
return _expectReport('''
test('skip 1', () {}, skip: true);
test('skip 2', () {}, skip: true);
test('skip 3', () {}, skip: true);''', '''
::group::❎ skip 1 (skipped)
::endgroup::
::group::❎ skip 2 (skipped)
::endgroup::
::group::❎ skip 3 (skipped)
::endgroup::
🎉 0 tests passed, 3 skipped.''');
});
test('displays a skipped group', () {
return _expectReport('''
group('skip', () {
test('test 1', () {});
test('test 2', () {});
test('test 3', () {});
}, skip: true);''', '''
::group::❎ skip test 1 (skipped)
::endgroup::
::group::❎ skip test 2 (skipped)
::endgroup::
::group::❎ skip test 3 (skipped)
::endgroup::
🎉 0 tests passed, 3 skipped.''');
});
test('runs skipped tests along with successful tests', () {
return _expectReport('''
test('skip 1', () {}, skip: true);
test('success 1', () {});
test('skip 2', () {}, skip: true);
test('success 2', () {});''', '''
::group::❎ skip 1 (skipped)
::endgroup::
::group::✅ success 1
::endgroup::
::group::❎ skip 2 (skipped)
::endgroup::
::group::✅ success 2
::endgroup::
🎉 2 tests passed, 2 skipped.''');
});
test('runs skipped tests along with successful and failing tests', () {
return _expectReport('''
test('failure 1', () => throw TestFailure('oh no'));
test('skip 1', () {}, skip: true);
test('success 1', () {});
test('failure 2', () => throw TestFailure('oh no'));
test('skip 2', () {}, skip: true);
test('success 2', () {});''', '''
::group::❌ failure 1 (failed)
oh no
test.dart 6:35 main.<fn>
::endgroup::
::group::❎ skip 1 (skipped)
::endgroup::
::group::✅ success 1
::endgroup::
::group::❌ failure 2 (failed)
oh no
test.dart 9:35 main.<fn>
::endgroup::
::group::❎ skip 2 (skipped)
::endgroup::
::group::✅ success 2
::endgroup::
::error::2 tests passed, 2 failed, 2 skipped.''');
});
test('displays the skip reason if available', () {
return _expectReport('''
test('skip 1', () {}, skip: 'some reason');
test('skip 2', () {}, skip: 'or another');''', '''
::group::❎ skip 1 (skipped)
Skip: some reason
::endgroup::
::group::❎ skip 2 (skipped)
Skip: or another
::endgroup::
🎉 0 tests passed, 2 skipped.''');
});
});
test('loadSuite, setUpAll, and tearDownAll hidden if no content', () {
return _expectReport('''
group('one', () {
setUpAll(() {/* nothing to do here */});
tearDownAll(() {/* nothing to do here */});
test('test 1', () {});
});''', '''
::group::✅ one test 1
::endgroup::
🎉 1 test passed.''');
});
test('setUpAll and tearDownAll show when they have content', () {
return _expectReport('''
group('one', () {
setUpAll(() { print('one'); });
tearDownAll(() { print('two'); });
test('test 1', () {});
});''', '''
::group::✅ one (setUpAll)
one
::endgroup::
::group::✅ one test 1
::endgroup::
::group::✅ one (tearDownAll)
two
::endgroup::
🎉 1 test passed.''');
});
}
/// Expects exactly [expected] to appear in the test output.
///
/// If [useContains] is passed, then the output only must contain [expected].
Future<void> _expectReport(
String tests,
String expected, {
List<String> args = const [],
bool useContains = false,
}) async {
expected = expected.split('\n').map(_unindent).join('\n');
var actual = (await _reportLines(tests, args)).join('\n');
expect(actual, useContains ? contains(expected) : equals(expected));
}
/// Expects all of [expected] lines to appear in the test output, but additional
/// output is allowed.
Future<void> _expectReportLines(
String tests,
List<String> expected, {
List<String> args = const [],
}) async {
expected = [for (var line in expected) _unindent(line)];
var actual = await _reportLines(tests, args);
expect(actual, containsAllInOrder(expected));
}
/// All the output lines from running [tests].
Future<List<String>> _reportLines(String tests, List<String> args) async {
await d.file('test.dart', '''
import 'dart:async';
import 'package:test/test.dart';
void main() {
$tests
}
''').create();
var test = await runTest([
'test.dart',
...args,
], reporter: 'github');
await test.shouldExit();
var stdoutLines = await test.stdoutStream().toList();
return stdoutLines
.map((line) => line.trim())
.where((line) => line.isNotEmpty)
.toList();
}
/// Removes all leading space from [line].
String _unindent(String line) => line.trimLeft();