blob: f523115b4fc37136902c88baee0e1167cf39c7b2 [file] [log] [blame]
// Copyright (c) 2023, 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.
import 'dart:async';
import 'dart:convert';
import 'package:async/async.dart' hide Result;
import 'package:checks/checks.dart';
import 'package:checks/context.dart';
import 'package:test/scaffolding.dart';
import 'package:test_api/hooks.dart';
import 'package:test_api/hooks_testing.dart';
void main() {
group('Context', () {
test('expectAsync holds test open', () async {
late void Function() callback;
final monitor = TestCaseMonitor.start(() {
check(null).context.expectAsync(() => [''], (actual) async {
final completer = Completer<void>();
callback = completer.complete;
await completer.future;
return null;
});
});
await pumpEventQueue();
check(monitor).state.equals(State.running);
callback();
await monitor.onDone;
check(monitor).didPass();
});
test('expectAsync does not hold test open past exception', () async {
late void Function() callback;
final monitor = TestCaseMonitor.start(() {
check(null).context.expectAsync(() => [''], (actual) async {
final completer = Completer<void>();
callback = completer.complete;
await completer.future;
throw 'oh no!';
});
});
await pumpEventQueue();
check(monitor).state.equals(State.running);
callback();
await monitor.onDone;
check(monitor)
..state.equals(State.failed)
..errors.single.has((e) => e.error, 'error').equals('oh no!');
});
test('nestAsync holds test open', () async {
late void Function() callback;
final monitor = TestCaseMonitor.start(() {
check(null).context.nestAsync(() => [''], (actual) async {
final completer = Completer<void>();
callback = completer.complete;
await completer.future;
return Extracted.value(null);
}, null);
});
await pumpEventQueue();
check(monitor).state.equals(State.running);
callback();
await monitor.onDone;
check(monitor).didPass();
});
test('nestAsync holds test open past async condition', () async {
late void Function() callback;
final monitor = TestCaseMonitor.start(() {
check(null).context.nestAsync(() => [''], (actual) async {
return Extracted.value(null);
}, (it) async {
final completer = Completer<void>();
callback = completer.complete;
await completer.future;
});
});
await pumpEventQueue();
check(monitor).state.equals(State.running);
callback();
await monitor.onDone;
check(monitor).didPass();
});
test('nestAsync does not hold test open past exception', () async {
late void Function() callback;
final monitor = TestCaseMonitor.start(() {
check(null).context.nestAsync(() => [''], (actual) async {
final completer = Completer<void>();
callback = completer.complete;
await completer.future;
throw 'oh no!';
}, null);
});
await pumpEventQueue();
check(monitor).state.equals(State.running);
callback();
await monitor.onDone;
check(monitor)
..state.equals(State.failed)
..errors.single.has((e) => e.error, 'error').equals('oh no!');
});
test('expectUnawaited can fail the test after it completes', () async {
late void Function() callback;
final monitor = await TestCaseMonitor.run(() {
check(null).context.expectUnawaited(() => [''], (actual, reject) {
final completer = Completer<void>()
..future.then((_) {
reject(Rejection(which: ['foo']));
});
callback = completer.complete;
});
});
check(monitor).state.equals(State.passed);
callback();
await pumpEventQueue();
check(monitor)
..state.equals(State.failed)
..errors.unorderedMatches([
(it) => it
.has((e) => e.error, 'error')
.isA<TestFailure>()
.has((f) => f.message, 'message')
.isNotNull()
.endsWith('Which: foo'),
(it) => it
.has((e) => e.error, 'error')
.isA<String>()
.startsWith('This test failed after it had already completed.')
]);
});
});
group('SkipExtension', () {
test('marks the test as skipped', () async {
final monitor = await TestCaseMonitor.run(() {
check(null).skip('skip').isNotNull();
});
check(monitor).state.equals(State.skipped);
});
});
}
extension _MonitorChecks on Subject<TestCaseMonitor> {
Subject<State> get state => has((m) => m.state, 'state');
Subject<Iterable<AsyncError>> get errors => has((m) => m.errors, 'errors');
Subject<StreamQueue<AsyncError>> get onError =>
has((m) => m.onError, 'onError').withQueue;
/// Expects that the monitored test is completed as success with no errors.
///
/// Sets up an unawaited expectation that the test does not emit errors in the
/// future in addition to checking there have been no errors yet.
void didPass() {
errors.isEmpty();
state.equals(State.passed);
onError.context.expectUnawaited(() => ['emits no further errors'],
(actual, reject) async {
await for (var error in actual.rest) {
reject(Rejection(which: [
...prefixFirst('threw late error', literal(error.error)),
...const LineSplitter().convert(
TestHandle.current.formatStackTrace(error.stackTrace).toString())
]));
}
});
}
}