| // 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()) |
| ])); |
| } |
| }); |
| } |
| } |