diff --git a/pkgs/checks/lib/checks.dart b/pkgs/checks/lib/checks.dart index db8b18e86..a390e75c9 100644 --- a/pkgs/checks/lib/checks.dart +++ b/pkgs/checks/lib/checks.dart @@ -7,7 +7,7 @@ export 'src/checks.dart' export 'src/extensions/async.dart' show FutureChecks, StreamChecks, WithQueueExtension; export 'src/extensions/core.dart' - show BoolChecks, ComparableChecks, CoreChecks, NullableChecks; + show BoolChecks, ComparableChecks, CoreChecks, NullableChecks, equals; export 'src/extensions/function.dart' show FunctionChecks; export 'src/extensions/iterable.dart' show IterableChecks; export 'src/extensions/map.dart' show MapChecks; diff --git a/pkgs/checks/lib/src/extensions/async.dart b/pkgs/checks/lib/src/extensions/async.dart index 1fd360bf3..8f2f07368 100644 --- a/pkgs/checks/lib/src/extensions/async.dart +++ b/pkgs/checks/lib/src/extensions/async.dart @@ -230,8 +230,8 @@ extension StreamChecks on Subject> { /// /// ```dart /// await check(someStream).withQueue.inOrder([ - /// (s) => s.emits((e) => e.equals(0)), - /// (s) => s.emits((e) => e.equals(1)), + /// (s) => s.emits(equals(0)), + /// (s) => s.emits(equals(1)), // ]); /// ``` /// diff --git a/pkgs/checks/lib/src/extensions/core.dart b/pkgs/checks/lib/src/extensions/core.dart index 6212f9155..da1eac151 100644 --- a/pkgs/checks/lib/src/extensions/core.dart +++ b/pkgs/checks/lib/src/extensions/core.dart @@ -102,6 +102,19 @@ extension CoreChecks on Subject { } } +/// Returns a [Condition] checking that the actual value is equal to [expected] +/// by operator `==`. +/// +/// This is a shortcut for `(Subject it) => it..equals(expected)`. +Condition equals(T expected) => (Subject subject) { + if (subject is Subject && expected is String) { + // String specializes `equals` with a better failure + (subject as Subject).equals(expected); + } else { + subject.equals(expected); + } + }; + extension BoolChecks on Subject { void isTrue() { context.expect( diff --git a/pkgs/checks/test/describe_test.dart b/pkgs/checks/test/describe_test.dart index d117a7b10..fedd620d4 100644 --- a/pkgs/checks/test/describe_test.dart +++ b/pkgs/checks/test/describe_test.dart @@ -12,7 +12,7 @@ void main() { check(describe((_) {})).isEmpty(); }); test('includes condition clauses', () { - check(describe((it) => it.equals(1))).deepEquals([' equals <1>']); + check(describe(equals(1))).deepEquals([' equals <1>']); }); test('includes nested clauses', () { check(describe((it) => it.length.equals(1))).deepEquals([ diff --git a/pkgs/checks/test/extensions/async_test.dart b/pkgs/checks/test/extensions/async_test.dart index 43ba5d73c..3484fb2a6 100644 --- a/pkgs/checks/test/extensions/async_test.dart +++ b/pkgs/checks/test/extensions/async_test.dart @@ -16,11 +16,11 @@ void main() { group('FutureChecks', () { group('completes', () { test('succeeds for a future that completes to a value', () async { - await check(_futureSuccess()).completes((it) => it.equals(42)); + await check(_futureSuccess()).completes(equals(42)); }); test('rejects futures which complete as errors', () async { await check(_futureFail()).isRejectedByAsync( - (it) => it.completes((it) => it.equals(1)), + (it) => it.completes(equals(1)), actual: ['a future that completes as an error'], which: ['threw at:', 'fake trace'], ); @@ -29,7 +29,7 @@ void main() { await check((Subject it) => it.completes()) .hasAsyncDescriptionWhich( (it) => it.deepEquals([' completes to a value'])); - await check((Subject it) => it.completes((it) => it.equals(42))) + await check((Subject it) => it.completes(equals(42))) .hasAsyncDescriptionWhich((it) => it.deepEquals([ ' completes to a value that:', ' equals <42>', @@ -127,7 +127,7 @@ fake trace'''); group('StreamChecks', () { group('emits', () { test('succeeds for a stream that emits a value', () async { - await check(_countingStream(5)).emits((it) => it.equals(0)); + await check(_countingStream(5)).emits(equals(0)); }); test('fails for a stream that closes without emitting', () async { await check(_countingStream(0)).isRejectedByAsync( @@ -147,8 +147,7 @@ fake trace'''); await check((Subject> it) => it.emits()) .hasAsyncDescriptionWhich( (it) => it.deepEquals([' emits a value'])); - await check((Subject> it) => - it.emits((it) => it.equals(42))) + await check((Subject> it) => it.emits(equals(42))) .hasAsyncDescriptionWhich((it) => it.deepEquals([ ' emits a value that:', ' equals <42>', @@ -209,26 +208,26 @@ fake trace'''); test('uses a transaction', () async { final queue = _countingStream(1); await softCheckAsync>(queue, (it) => it.emitsError()); - await check(queue).emits((it) => it.equals(0)); + await check(queue).emits(equals(0)); }); }); group('emitsThrough', () { test('succeeds for a stream that eventuall emits a matching value', () async { - await check(_countingStream(5)).emitsThrough((it) => it.equals(4)); + await check(_countingStream(5)).emitsThrough(equals(4)); }); test('fails for a stream that closes without emitting a matching value', () async { await check(_countingStream(4)).isRejectedByAsync( - (it) => it.emitsThrough((it) => it.equals(5)), + (it) => it.emitsThrough(equals(5)), actual: ['a stream'], which: ['ended after emitting 4 elements with none matching'], ); }); test('can be described', () async { - await check((Subject> it) => - it.emitsThrough((it) => it.equals(42))) + await check( + (Subject> it) => it.emitsThrough(equals(42))) .hasAsyncDescriptionWhich((it) => it.deepEquals([ ' emits any values then emits a value that:', ' equals <42>' @@ -236,24 +235,22 @@ fake trace'''); }); test('uses a transaction', () async { final queue = _countingStream(1); - await softCheckAsync( - queue, - (Subject> it) => - it.emitsThrough((it) => it.equals(42))); - check(queue).emits((it) => it.equals(0)); + await softCheckAsync(queue, + (Subject> it) => it.emitsThrough(equals(42))); + check(queue).emits(equals(0)); }); test('consumes events', () async { final queue = _countingStream(3); - await check(queue).emitsThrough((it) => it.equals(1)); - await check(queue).emits((it) => it.equals(2)); + await check(queue).emitsThrough(equals(1)); + await check(queue).emits(equals(2)); }); }); group('emitsInOrder', () { test('succeeds for happy case', () async { await check(_countingStream(2)).inOrder([ - (it) => it.emits((it) => it.equals(0)), - (it) => it.emits((it) => it.equals(1)), + (it) => it.emits(equals(0)), + (it) => it.emits(equals(1)), (it) => it.isDone(), ]); }); @@ -270,8 +267,7 @@ fake trace'''); }); test('nestes the report for deep failures', () async { await check(_countingStream(2)).isRejectedByAsync( - (it) => it.inOrder( - [(it) => it.emits(), (it) => it.emits((it) => it.equals(2))]), + (it) => it.inOrder([(it) => it.emits(), (it) => it.emits(equals(2))]), actual: ['a stream'], which: [ 'satisfied 1 conditions then', @@ -294,21 +290,21 @@ fake trace'''); await softCheckAsync>( queue, (it) => it.inOrder([ - (it) => it.emits((it) => it.equals(0)), - (it) => it.emits((it) => it.equals(1)), - (it) => it.emits((it) => it.equals(42)), + (it) => it.emits(equals(0)), + (it) => it.emits(equals(1)), + (it) => it.emits(equals(42)), ])); await check(queue).inOrder([ - (it) => it.emits((it) => it.equals(0)), - (it) => it.emits((it) => it.equals(1)), - (it) => it.emits((it) => it.equals(2)), + (it) => it.emits(equals(0)), + (it) => it.emits(equals(1)), + (it) => it.emits(equals(2)), (it) => it.isDone(), ]); }); test('consumes events', () async { final queue = _countingStream(3); await check(queue).inOrder([(it) => it.emits(), (it) => it.emits()]); - await check(queue).emits((it) => it.equals(2)); + await check(queue).emits(equals(2)); }); }); @@ -316,18 +312,17 @@ fake trace'''); test( 'succeeds for a stream that closes without emitting a matching value', () async { - await check(_countingStream(5)).neverEmits((it) => it.equals(5)); + await check(_countingStream(5)).neverEmits(equals(5)); }); test('fails for a stream that emits a matching value', () async { await check(_countingStream(6)).isRejectedByAsync( - (it) => it.neverEmits((it) => it.equals(5)), + (it) => it.neverEmits(equals(5)), actual: ['a stream'], which: ['emitted <5>', 'following 5 other items'], ); }); test('can be described', () async { - await check((Subject> it) => - it.neverEmits((it) => it.equals(42))) + await check((Subject> it) => it.neverEmits(equals(42))) .hasAsyncDescriptionWhich((it) => it.deepEquals([ ' never emits a value that:', ' equals <42>', @@ -336,10 +331,10 @@ fake trace'''); test('uses a transaction', () async { final queue = _countingStream(2); await softCheckAsync>( - queue, (it) => it.neverEmits((it) => it.equals(1))); + queue, (it) => it.neverEmits(equals(1))); await check(queue).inOrder([ - (it) => it.emits((it) => it.equals(0)), - (it) => it.emits((it) => it.equals(1)), + (it) => it.emits(equals(0)), + (it) => it.emits(equals(1)), (it) => it.isDone(), ]); }); @@ -347,31 +342,30 @@ fake trace'''); group('mayEmit', () { test('succeeds for a stream that emits a matching value', () async { - await check(_countingStream(1)).mayEmit((it) => it.equals(0)); + await check(_countingStream(1)).mayEmit(equals(0)); }); test('succeeds for a stream that emits an error', () async { - await check(_countingStream(1, errorAt: 0)) - .mayEmit((it) => it.equals(0)); + await check(_countingStream(1, errorAt: 0)).mayEmit(equals(0)); }); test('succeeds for a stream that closes', () async { - await check(_countingStream(0)).mayEmit((it) => it.equals(42)); + await check(_countingStream(0)).mayEmit(equals(42)); }); test('consumes a matching event', () async { final queue = _countingStream(2); await softCheckAsync>( - queue, (it) => it.mayEmit((it) => it.equals(0))); - await check(queue).emits((it) => it.equals(1)); + queue, (it) => it.mayEmit(equals(0))); + await check(queue).emits(equals(1)); }); test('does not consume a non-matching event', () async { final queue = _countingStream(2); await softCheckAsync>( - queue, (it) => it.mayEmit((it) => it.equals(1))); - await check(queue).emits((it) => it.equals(0)); + queue, (it) => it.mayEmit(equals(1))); + await check(queue).emits(equals(0)); }); test('does not consume an error', () async { final queue = _countingStream(1, errorAt: 0); await softCheckAsync>( - queue, (it) => it.mayEmit((it) => it.equals(0))); + queue, (it) => it.mayEmit(equals(0))); await check(queue).emitsError( (it) => it.has((e) => e.message, 'message').equals('Error at 1')); }); @@ -379,31 +373,30 @@ fake trace'''); group('mayEmitMultiple', () { test('succeeds for a stream that emits a matching value', () async { - await check(_countingStream(1)).mayEmitMultiple((it) => it.equals(0)); + await check(_countingStream(1)).mayEmitMultiple(equals(0)); }); test('succeeds for a stream that emits an error', () async { - await check(_countingStream(1, errorAt: 0)) - .mayEmitMultiple((it) => it.equals(0)); + await check(_countingStream(1, errorAt: 0)).mayEmitMultiple(equals(0)); }); test('succeeds for a stream that closes', () async { - await check(_countingStream(0)).mayEmitMultiple((it) => it.equals(42)); + await check(_countingStream(0)).mayEmitMultiple(equals(42)); }); test('consumes matching events', () async { final queue = _countingStream(3); await softCheckAsync>( queue, (it) => it.mayEmitMultiple((it) => it.isLessThan(2))); - await check(queue).emits((it) => it.equals(2)); + await check(queue).emits(equals(2)); }); test('consumes no events if no events match', () async { final queue = _countingStream(2); await softCheckAsync>( queue, (it) => it.mayEmitMultiple((it) => it.isLessThan(0))); - await check(queue).emits((it) => it.equals(0)); + await check(queue).emits(equals(0)); }); test('does not consume an error', () async { final queue = _countingStream(1, errorAt: 0); await softCheckAsync>( - queue, (it) => it.mayEmitMultiple((it) => it.equals(0))); + queue, (it) => it.mayEmitMultiple(equals(0))); await check(queue).emitsError( (it) => it.has((e) => e.message, 'message').equals('Error at 1')); }); @@ -428,7 +421,7 @@ fake trace'''); test('uses a transaction', () async { final queue = _countingStream(1); await softCheckAsync>(queue, (it) => it.isDone()); - await check(queue).emits((it) => it.equals(0)); + await check(queue).emits(equals(0)); }); test('can be described', () async { await check((Subject> it) => it.isDone()) @@ -438,16 +431,14 @@ fake trace'''); group('emitsAnyOf', () { test('succeeds for a stream that matches one condition', () async { - await check(_countingStream(1)).anyOf([ - (it) => it.emits((it) => it.equals(42)), - (it) => it.emits((it) => it.equals(0)) - ]); + await check(_countingStream(1)) + .anyOf([(it) => it.emits(equals(42)), (it) => it.emits(equals(0))]); }); test('fails for a stream that matches no conditions', () async { await check(_countingStream(0)).isRejectedByAsync( (it) => it.anyOf([ (it) => it.emits(), - (it) => it.emitsThrough((it) => it.equals(1)), + (it) => it.emitsThrough(equals(1)), ]), actual: [ 'a stream' @@ -463,8 +454,8 @@ fake trace'''); test('includes nested details for nested failures', () async { await check(_countingStream(1)).isRejectedByAsync( (it) => it.anyOf([ - (it) => it.emits((it) => it.equals(42)), - (it) => it.emitsThrough((it) => it.equals(10)), + (it) => it.emits(equals(42)), + (it) => it.emitsThrough(equals(10)), ]), actual: [ 'a stream' @@ -490,18 +481,16 @@ fake trace'''); await softCheckAsync>( queue, (it) => it.anyOf([ - (it) => it.emits((it) => it.equals(10)), - (it) => it.emitsThrough((it) => it.equals(42)), + (it) => it.emits(equals(10)), + (it) => it.emitsThrough(equals(42)), ])); - await check(queue).emits((it) => it.equals(0)); + await check(queue).emits(equals(0)); }); test('consumes events', () async { final queue = _countingStream(3); - await check(queue).anyOf([ - (it) => it.emits((it) => it.equals(1)), - (it) => it.emitsThrough((it) => it.equals(1)) - ]); - await check(queue).emits((it) => it.equals(2)); + await check(queue).anyOf( + [(it) => it.emits(equals(1)), (it) => it.emitsThrough(equals(1))]); + await check(queue).emits(equals(2)); }); }); }); diff --git a/pkgs/checks/test/extensions/iterable_test.dart b/pkgs/checks/test/extensions/iterable_test.dart index 3d31a8108..73c0881b0 100644 --- a/pkgs/checks/test/extensions/iterable_test.dart +++ b/pkgs/checks/test/extensions/iterable_test.dart @@ -66,8 +66,8 @@ void main() { .isRejectedBy((it) => it.contains(2), which: ['does not contain <2>']); }); test('any', () { - check(_testIterable).any((it) => it.equals(1)); - check(_testIterable).isRejectedBy((it) => it.any((it) => it.equals(2)), + check(_testIterable).any(equals(1)); + check(_testIterable).isRejectedBy((it) => it.any(equals(2)), which: ['Contains no matching element']); }); @@ -152,14 +152,14 @@ void main() { group('unorderedMatches', () { test('success for happy case', () { - check(_testIterable).unorderedMatches( - _testIterable.toList().reversed.map((i) => (it) => it.equals(i))); + check(_testIterable) + .unorderedMatches(_testIterable.toList().reversed.map(equals)); }); test('reports unmatched elements', () { check(_testIterable).isRejectedBy( - (it) => it.unorderedMatches(_testIterable - .followedBy([42, 100]).map((i) => (it) => it.equals(i))), + (it) => it.unorderedMatches( + _testIterable.followedBy([42, 100]).map(equals)), which: [ 'has no element matching the condition at index 2:', ' equals <42>', @@ -169,8 +169,7 @@ void main() { test('reports unexpected elements', () { check(_testIterable.followedBy([42, 100])).isRejectedBy( - (it) => it - .unorderedMatches(_testIterable.map((i) => (it) => it.equals(i))), + (it) => it.unorderedMatches(_testIterable.map(equals)), which: [ 'has an unmatched element at index 2: <42>', 'and 1 other unmatched elements' diff --git a/pkgs/checks/test/extensions/map_test.dart b/pkgs/checks/test/extensions/map_test.dart index 99d1b4f05..1b2684c07 100644 --- a/pkgs/checks/test/extensions/map_test.dart +++ b/pkgs/checks/test/extensions/map_test.dart @@ -83,9 +83,9 @@ void main() { }); }); test('containsKeyThat', () { - check(_testMap).containsKeyThat((it) => it.equals('a')); + check(_testMap).containsKeyThat(equals('a')); check(_testMap).isRejectedBy( - (it) => it.containsKeyThat((it) => it.equals('c')), + (it) => it.containsKeyThat(equals('c')), which: ['Contains no matching key'], ); }); @@ -109,9 +109,9 @@ void main() { }); }); test('containsValueThat', () { - check(_testMap).containsValueThat((it) => it.equals(1)); + check(_testMap).containsValueThat(equals(1)); check(_testMap).isRejectedBy( - (it) => it.containsValueThat((it) => it.equals(3)), + (it) => it.containsValueThat(equals(3)), which: ['Contains no matching value'], ); });