
* Randomize tests, exclude tests that fail with randomization. * Disable some more tool tests
1251 lines
46 KiB
Dart
1251 lines
46 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// TODO(gspencergoog): Remove this tag once this test's state leaks/test
|
|
// dependencies have been fixed.
|
|
// https://github.com/flutter/flutter/issues/85160
|
|
// Fails with "flutter test --test-randomize-ordering-seed=20210721"
|
|
@Tags(<String>['no-shuffle'])
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/scheduler.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_driver/flutter_driver.dart';
|
|
import 'package:flutter_driver/src/extension/extension.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'stubs/stub_command.dart';
|
|
import 'stubs/stub_command_extension.dart';
|
|
import 'stubs/stub_finder.dart';
|
|
import 'stubs/stub_finder_extension.dart';
|
|
|
|
Future<void> silenceDriverLogger(AsyncCallback callback) async {
|
|
final DriverLogCallback oldLogger = driverLog;
|
|
driverLog = (String source, String message) { };
|
|
try {
|
|
await callback();
|
|
} finally {
|
|
driverLog = oldLogger;
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
group('waitUntilNoTransientCallbacks', () {
|
|
late FlutterDriverExtension driverExtension;
|
|
Map<String, dynamic>? result;
|
|
int messageId = 0;
|
|
final List<String?> log = <String?>[];
|
|
|
|
setUp(() {
|
|
result = null;
|
|
driverExtension = FlutterDriverExtension((String? message) async { log.add(message); return (messageId += 1).toString(); }, false, true);
|
|
});
|
|
|
|
testWidgets('returns immediately when transient callback queue is empty', (WidgetTester tester) async {
|
|
driverExtension.call(const WaitForCondition(NoTransientCallbacks()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
await tester.idle();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets('waits until no transient callbacks', (WidgetTester tester) async {
|
|
SchedulerBinding.instance!.scheduleFrameCallback((_) {
|
|
// Intentionally blank. We only care about existence of a callback.
|
|
});
|
|
|
|
driverExtension.call(const WaitForCondition(NoTransientCallbacks()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
// Nothing should happen until the next frame.
|
|
await tester.idle();
|
|
expect(result, isNull);
|
|
|
|
// NOW we should receive the result.
|
|
await tester.pump();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets('handler', (WidgetTester tester) async {
|
|
expect(log, isEmpty);
|
|
final Map<String, dynamic> response = await driverExtension.call(const RequestData('hello').serialize());
|
|
final RequestDataResult result = RequestDataResult.fromJson(response['response'] as Map<String, dynamic>);
|
|
expect(log, <String>['hello']);
|
|
expect(result.message, '1');
|
|
});
|
|
});
|
|
|
|
group('waitForCondition', () {
|
|
late FlutterDriverExtension driverExtension;
|
|
Map<String, dynamic>? result;
|
|
int messageId = 0;
|
|
final List<String?> log = <String?>[];
|
|
|
|
setUp(() {
|
|
result = null;
|
|
driverExtension = FlutterDriverExtension((String? message) async { log.add(message); return (messageId += 1).toString(); }, false, true);
|
|
});
|
|
|
|
testWidgets('waiting for NoTransientCallbacks returns immediately when transient callback queue is empty', (WidgetTester tester) async {
|
|
driverExtension.call(const WaitForCondition(NoTransientCallbacks()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
await tester.idle();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets('waiting for NoTransientCallbacks returns until no transient callbacks', (WidgetTester tester) async {
|
|
SchedulerBinding.instance!.scheduleFrameCallback((_) {
|
|
// Intentionally blank. We only care about existence of a callback.
|
|
});
|
|
|
|
driverExtension.call(const WaitForCondition(NoTransientCallbacks()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
// Nothing should happen until the next frame.
|
|
await tester.idle();
|
|
expect(result, isNull);
|
|
|
|
// NOW we should receive the result.
|
|
await tester.pump();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets('waiting for NoPendingFrame returns immediately when frame is synced', (
|
|
WidgetTester tester) async {
|
|
driverExtension.call(const WaitForCondition(NoPendingFrame()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
await tester.idle();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets('waiting for NoPendingFrame returns until no pending scheduled frame', (WidgetTester tester) async {
|
|
SchedulerBinding.instance!.scheduleFrame();
|
|
|
|
driverExtension.call(const WaitForCondition(NoPendingFrame()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
// Nothing should happen until the next frame.
|
|
await tester.idle();
|
|
expect(result, isNull);
|
|
|
|
// NOW we should receive the result.
|
|
await tester.pump();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'waiting for combined conditions returns immediately', (WidgetTester tester) async {
|
|
const SerializableWaitCondition combinedCondition =
|
|
CombinedCondition(<SerializableWaitCondition>[NoTransientCallbacks(), NoPendingFrame()]);
|
|
driverExtension.call(const WaitForCondition(combinedCondition).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
await tester.idle();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'waiting for combined conditions returns until no transient callbacks', (WidgetTester tester) async {
|
|
SchedulerBinding.instance!.scheduleFrame();
|
|
SchedulerBinding.instance!.scheduleFrameCallback((_) {
|
|
// Intentionally blank. We only care about existence of a callback.
|
|
});
|
|
|
|
const SerializableWaitCondition combinedCondition =
|
|
CombinedCondition(<SerializableWaitCondition>[NoTransientCallbacks(), NoPendingFrame()]);
|
|
driverExtension.call(const WaitForCondition(combinedCondition).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
// Nothing should happen until the next frame.
|
|
await tester.idle();
|
|
expect(result, isNull);
|
|
|
|
// NOW we should receive the result.
|
|
await tester.pump();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'waiting for combined conditions returns until no pending scheduled frame', (WidgetTester tester) async {
|
|
SchedulerBinding.instance!.scheduleFrame();
|
|
SchedulerBinding.instance!.scheduleFrameCallback((_) {
|
|
// Intentionally blank. We only care about existence of a callback.
|
|
});
|
|
|
|
const SerializableWaitCondition combinedCondition =
|
|
CombinedCondition(<SerializableWaitCondition>[NoPendingFrame(), NoTransientCallbacks()]);
|
|
driverExtension.call(const WaitForCondition(combinedCondition).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
// Nothing should happen until the next frame.
|
|
await tester.idle();
|
|
expect(result, isNull);
|
|
|
|
// NOW we should receive the result.
|
|
await tester.pump();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'waiting for NoPendingPlatformMessages returns immediately when there are no platform messages', (WidgetTester tester) async {
|
|
driverExtension
|
|
.call(const WaitForCondition(NoPendingPlatformMessages()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
await tester.idle();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'waiting for NoPendingPlatformMessages returns until a single method channel call returns', (WidgetTester tester) async {
|
|
const MethodChannel channel = MethodChannel('helloChannel', JSONMethodCodec());
|
|
const MessageCodec<dynamic> jsonMessage = JSONMessageCodec();
|
|
tester.binding.defaultBinaryMessenger.setMockMessageHandler(
|
|
'helloChannel', (ByteData? message) {
|
|
return Future<ByteData>.delayed(
|
|
const Duration(milliseconds: 10),
|
|
() => jsonMessage.encodeMessage(<dynamic>['hello world'])!);
|
|
});
|
|
channel.invokeMethod<String>('sayHello', 'hello');
|
|
|
|
driverExtension
|
|
.call(const WaitForCondition(NoPendingPlatformMessages()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
// The channel message are delayed for 10 milliseconds, so nothing happens yet.
|
|
await tester.pump(const Duration(milliseconds: 5));
|
|
expect(result, isNull);
|
|
|
|
// Now we receive the result.
|
|
await tester.pump(const Duration(milliseconds: 5));
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'waiting for NoPendingPlatformMessages returns until both method channel calls return', (WidgetTester tester) async {
|
|
const MessageCodec<dynamic> jsonMessage = JSONMessageCodec();
|
|
// Configures channel 1
|
|
const MethodChannel channel1 = MethodChannel('helloChannel1', JSONMethodCodec());
|
|
tester.binding.defaultBinaryMessenger.setMockMessageHandler(
|
|
'helloChannel1', (ByteData? message) {
|
|
return Future<ByteData>.delayed(
|
|
const Duration(milliseconds: 10),
|
|
() => jsonMessage.encodeMessage(<dynamic>['hello world'])!);
|
|
});
|
|
|
|
// Configures channel 2
|
|
const MethodChannel channel2 = MethodChannel('helloChannel2', JSONMethodCodec());
|
|
tester.binding.defaultBinaryMessenger.setMockMessageHandler(
|
|
'helloChannel2', (ByteData? message) {
|
|
return Future<ByteData>.delayed(
|
|
const Duration(milliseconds: 20),
|
|
() => jsonMessage.encodeMessage(<dynamic>['hello world'])!);
|
|
});
|
|
|
|
channel1.invokeMethod<String>('sayHello', 'hello');
|
|
channel2.invokeMethod<String>('sayHello', 'hello');
|
|
|
|
driverExtension
|
|
.call(const WaitForCondition(NoPendingPlatformMessages()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
// Neither of the channel responses is received, so nothing happens yet.
|
|
await tester.pump(const Duration(milliseconds: 5));
|
|
expect(result, isNull);
|
|
|
|
// Result of channel 1 is received, but channel 2 is still pending, so still waiting.
|
|
await tester.pump(const Duration(milliseconds: 10));
|
|
expect(result, isNull);
|
|
|
|
// Both of the results are received. Now we receive the result.
|
|
await tester.pump(const Duration(milliseconds: 30));
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'waiting for NoPendingPlatformMessages returns until new method channel call returns', (WidgetTester tester) async {
|
|
const MessageCodec<dynamic> jsonMessage = JSONMessageCodec();
|
|
// Configures channel 1
|
|
const MethodChannel channel1 = MethodChannel('helloChannel1', JSONMethodCodec());
|
|
tester.binding.defaultBinaryMessenger.setMockMessageHandler(
|
|
'helloChannel1', (ByteData? message) {
|
|
return Future<ByteData>.delayed(
|
|
const Duration(milliseconds: 10),
|
|
() => jsonMessage.encodeMessage(<dynamic>['hello world'])!);
|
|
});
|
|
|
|
// Configures channel 2
|
|
const MethodChannel channel2 = MethodChannel('helloChannel2', JSONMethodCodec());
|
|
tester.binding.defaultBinaryMessenger.setMockMessageHandler(
|
|
'helloChannel2', (ByteData? message) {
|
|
return Future<ByteData>.delayed(
|
|
const Duration(milliseconds: 20),
|
|
() => jsonMessage.encodeMessage(<dynamic>['hello world'])!);
|
|
});
|
|
|
|
channel1.invokeMethod<String>('sayHello', 'hello');
|
|
|
|
// Calls the waiting API before the second channel message is sent.
|
|
driverExtension
|
|
.call(const WaitForCondition(NoPendingPlatformMessages()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
// The first channel message is not received, so nothing happens yet.
|
|
await tester.pump(const Duration(milliseconds: 5));
|
|
expect(result, isNull);
|
|
|
|
channel2.invokeMethod<String>('sayHello', 'hello');
|
|
|
|
// Result of channel 1 is received, but channel 2 is still pending, so still waiting.
|
|
await tester.pump(const Duration(milliseconds: 15));
|
|
expect(result, isNull);
|
|
|
|
// Both of the results are received. Now we receive the result.
|
|
await tester.pump(const Duration(milliseconds: 10));
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'waiting for NoPendingPlatformMessages returns until both old and new method channel calls return', (WidgetTester tester) async {
|
|
const MessageCodec<dynamic> jsonMessage = JSONMessageCodec();
|
|
// Configures channel 1
|
|
const MethodChannel channel1 = MethodChannel('helloChannel1', JSONMethodCodec());
|
|
tester.binding.defaultBinaryMessenger.setMockMessageHandler(
|
|
'helloChannel1', (ByteData? message) {
|
|
return Future<ByteData>.delayed(
|
|
const Duration(milliseconds: 20),
|
|
() => jsonMessage.encodeMessage(<dynamic>['hello world'])!);
|
|
});
|
|
|
|
// Configures channel 2
|
|
const MethodChannel channel2 = MethodChannel('helloChannel2', JSONMethodCodec());
|
|
tester.binding.defaultBinaryMessenger.setMockMessageHandler(
|
|
'helloChannel2', (ByteData? message) {
|
|
return Future<ByteData>.delayed(
|
|
const Duration(milliseconds: 10),
|
|
() => jsonMessage.encodeMessage(<dynamic>['hello world'])!);
|
|
});
|
|
|
|
channel1.invokeMethod<String>('sayHello', 'hello');
|
|
|
|
driverExtension
|
|
.call(const WaitForCondition(NoPendingPlatformMessages()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
// The first channel message is not received, so nothing happens yet.
|
|
await tester.pump(const Duration(milliseconds: 5));
|
|
expect(result, isNull);
|
|
|
|
channel2.invokeMethod<String>('sayHello', 'hello');
|
|
|
|
// Result of channel 2 is received, but channel 1 is still pending, so still waiting.
|
|
await tester.pump(const Duration(milliseconds: 10));
|
|
expect(result, isNull);
|
|
|
|
// Now we receive the result.
|
|
await tester.pump(const Duration(milliseconds: 5));
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
});
|
|
|
|
group('getSemanticsId', () {
|
|
late FlutterDriverExtension driverExtension;
|
|
setUp(() {
|
|
driverExtension = FlutterDriverExtension((String? arg) async => '', true, true);
|
|
});
|
|
|
|
testWidgets('works when semantics are enabled', (WidgetTester tester) async {
|
|
final SemanticsHandle semantics = RendererBinding.instance!.pipelineOwner.ensureSemantics();
|
|
await tester.pumpWidget(
|
|
const Text('hello', textDirection: TextDirection.ltr));
|
|
|
|
final Map<String, String> arguments = GetSemanticsId(const ByText('hello')).serialize();
|
|
final Map<String, dynamic> response = await driverExtension.call(arguments);
|
|
final GetSemanticsIdResult result = GetSemanticsIdResult.fromJson(response['response'] as Map<String, dynamic>);
|
|
|
|
expect(result.id, 1);
|
|
semantics.dispose();
|
|
});
|
|
|
|
testWidgets('throws state error if no data is found', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const Text('hello', textDirection: TextDirection.ltr));
|
|
|
|
final Map<String, String> arguments = GetSemanticsId(const ByText('hello')).serialize();
|
|
final Map<String, dynamic> response = await driverExtension.call(arguments);
|
|
|
|
expect(response['isError'], true);
|
|
expect(response['response'], contains('Bad state: No semantics data found'));
|
|
}, semanticsEnabled: false);
|
|
|
|
testWidgets('throws state error multiple matches are found', (WidgetTester tester) async {
|
|
final SemanticsHandle semantics = RendererBinding.instance!.pipelineOwner.ensureSemantics();
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: ListView(children: const <Widget>[
|
|
SizedBox(width: 100.0, height: 100.0, child: Text('hello')),
|
|
SizedBox(width: 100.0, height: 100.0, child: Text('hello')),
|
|
]),
|
|
),
|
|
);
|
|
|
|
final Map<String, String> arguments = GetSemanticsId(const ByText('hello')).serialize();
|
|
final Map<String, dynamic> response = await driverExtension.call(arguments);
|
|
|
|
expect(response['isError'], true);
|
|
expect(response['response'], contains('Bad state: Found more than one element with the same ID'));
|
|
semantics.dispose();
|
|
});
|
|
});
|
|
|
|
testWidgets('getOffset', (WidgetTester tester) async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension((String? arg) async => '', true, true);
|
|
|
|
Future<Offset> getOffset(OffsetType offset) async {
|
|
final Map<String, String> arguments = GetOffset(ByValueKey(1), offset).serialize();
|
|
final Map<String, dynamic> response = await driverExtension.call(arguments);
|
|
final GetOffsetResult result = GetOffsetResult.fromJson(response['response'] as Map<String, dynamic>);
|
|
return Offset(result.dx, result.dy);
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
Align(
|
|
alignment: Alignment.topLeft,
|
|
child: Transform.translate(
|
|
offset: const Offset(40, 30),
|
|
child: const SizedBox(
|
|
key: ValueKey<int>(1),
|
|
width: 100,
|
|
height: 120,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(await getOffset(OffsetType.topLeft), const Offset(40, 30));
|
|
expect(await getOffset(OffsetType.topRight), const Offset(40 + 100.0, 30));
|
|
expect(await getOffset(OffsetType.bottomLeft), const Offset(40, 30 + 120.0));
|
|
expect(await getOffset(OffsetType.bottomRight), const Offset(40 + 100.0, 30 + 120.0));
|
|
expect(await getOffset(OffsetType.center), const Offset(40 + (100 / 2), 30 + (120 / 2)));
|
|
});
|
|
|
|
testWidgets('getText', (WidgetTester tester) async {
|
|
await silenceDriverLogger(() async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension((String? arg) async => '', true, true);
|
|
|
|
Future<String?> getTextInternal(SerializableFinder search) async {
|
|
final Map<String, String> arguments = GetText(search, timeout: const Duration(seconds: 1)).serialize();
|
|
final Map<String, dynamic> result = await driverExtension.call(arguments);
|
|
if (result['isError'] as bool) {
|
|
return null;
|
|
}
|
|
return GetTextResult.fromJson(result['response'] as Map<String, dynamic>).text;
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(body:Column(
|
|
key: const ValueKey<String>('column'),
|
|
children: <Widget>[
|
|
const Text('Hello1', key: ValueKey<String>('text1')),
|
|
SizedBox(
|
|
height: 25.0,
|
|
child: RichText(
|
|
key: const ValueKey<String>('text2'),
|
|
text: const TextSpan(text: 'Hello2'),
|
|
),
|
|
),
|
|
SizedBox(
|
|
height: 25.0,
|
|
child: EditableText(
|
|
key: const ValueKey<String>('text3'),
|
|
controller: TextEditingController(text: 'Hello3'),
|
|
focusNode: FocusNode(),
|
|
style: const TextStyle(),
|
|
cursorColor: Colors.red,
|
|
backgroundCursorColor: Colors.black,
|
|
),
|
|
),
|
|
SizedBox(
|
|
height: 25.0,
|
|
child: TextField(
|
|
key: const ValueKey<String>('text4'),
|
|
controller: TextEditingController(text: 'Hello4'),
|
|
),
|
|
),
|
|
SizedBox(
|
|
height: 25.0,
|
|
child: TextFormField(
|
|
key: const ValueKey<String>('text5'),
|
|
controller: TextEditingController(text: 'Hello5'),
|
|
),
|
|
),
|
|
SizedBox(
|
|
height: 25.0,
|
|
child: RichText(
|
|
key: const ValueKey<String>('text6'),
|
|
text: const TextSpan(children: <TextSpan>[
|
|
TextSpan(text: 'Hello'),
|
|
TextSpan(text: ', '),
|
|
TextSpan(text: 'World'),
|
|
TextSpan(text: '!'),
|
|
]),
|
|
),
|
|
),
|
|
],
|
|
))
|
|
)
|
|
);
|
|
|
|
expect(await getTextInternal(ByValueKey('text1')), 'Hello1');
|
|
expect(await getTextInternal(ByValueKey('text2')), 'Hello2');
|
|
expect(await getTextInternal(ByValueKey('text3')), 'Hello3');
|
|
expect(await getTextInternal(ByValueKey('text4')), 'Hello4');
|
|
expect(await getTextInternal(ByValueKey('text5')), 'Hello5');
|
|
expect(await getTextInternal(ByValueKey('text6')), 'Hello, World!');
|
|
|
|
// Check if error thrown for other types
|
|
final Map<String, String> arguments = GetText(ByValueKey('column'), timeout: const Duration(seconds: 1)).serialize();
|
|
final Map<String, dynamic> response = await driverExtension.call(arguments);
|
|
expect(response['isError'], true);
|
|
expect(response['response'], contains('is currently not supported by getText'));
|
|
});
|
|
});
|
|
|
|
testWidgets('descendant finder', (WidgetTester tester) async {
|
|
await silenceDriverLogger(() async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension((String? arg) async => '', true, true);
|
|
|
|
Future<String?> getDescendantText({ String? of, bool matchRoot = false}) async {
|
|
final Map<String, String> arguments = GetText(Descendant(
|
|
of: ByValueKey(of),
|
|
matching: ByValueKey('text2'),
|
|
matchRoot: matchRoot,
|
|
), timeout: const Duration(seconds: 1)).serialize();
|
|
final Map<String, dynamic> result = await driverExtension.call(arguments);
|
|
if (result['isError'] as bool) {
|
|
return null;
|
|
}
|
|
return GetTextResult.fromJson(result['response'] as Map<String, dynamic>).text;
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Column(
|
|
key: const ValueKey<String>('column'),
|
|
children: const <Widget>[
|
|
Text('Hello1', key: ValueKey<String>('text1')),
|
|
Text('Hello2', key: ValueKey<String>('text2')),
|
|
Text('Hello3', key: ValueKey<String>('text3')),
|
|
],
|
|
)
|
|
)
|
|
);
|
|
|
|
expect(await getDescendantText(of: 'column'), 'Hello2');
|
|
expect(await getDescendantText(of: 'column', matchRoot: true), 'Hello2');
|
|
expect(await getDescendantText(of: 'text2', matchRoot: true), 'Hello2');
|
|
|
|
// Find nothing
|
|
Future<String?> result = getDescendantText(of: 'text1', matchRoot: true);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(await result, null);
|
|
|
|
result = getDescendantText(of: 'text2');
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(await result, null);
|
|
});
|
|
});
|
|
|
|
testWidgets('descendant finder firstMatchOnly', (WidgetTester tester) async {
|
|
await silenceDriverLogger(() async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension((String? arg) async => '', true, true);
|
|
|
|
Future<String?> getDescendantText() async {
|
|
final Map<String, String> arguments = GetText(Descendant(
|
|
of: ByValueKey('column'),
|
|
matching: const ByType('Text'),
|
|
firstMatchOnly: true,
|
|
), timeout: const Duration(seconds: 1)).serialize();
|
|
final Map<String, dynamic> result = await driverExtension.call(arguments);
|
|
if (result['isError'] as bool) {
|
|
return null;
|
|
}
|
|
return GetTextResult.fromJson(result['response'] as Map<String, dynamic>).text;
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Column(
|
|
key: const ValueKey<String>('column'),
|
|
children: const <Widget>[
|
|
Text('Hello1', key: ValueKey<String>('text1')),
|
|
Text('Hello2', key: ValueKey<String>('text2')),
|
|
Text('Hello3', key: ValueKey<String>('text3')),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(await getDescendantText(), 'Hello1');
|
|
});
|
|
});
|
|
|
|
testWidgets('ancestor finder', (WidgetTester tester) async {
|
|
await silenceDriverLogger(() async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension((String? arg) async => '', true, true);
|
|
|
|
Future<Offset?> getAncestorTopLeft({ String? of, String? matching, bool matchRoot = false}) async {
|
|
final Map<String, String> arguments = GetOffset(Ancestor(
|
|
of: ByValueKey(of),
|
|
matching: ByValueKey(matching),
|
|
matchRoot: matchRoot,
|
|
), OffsetType.topLeft, timeout: const Duration(seconds: 1)).serialize();
|
|
final Map<String, dynamic> response = await driverExtension.call(arguments);
|
|
if (response['isError'] as bool) {
|
|
return null;
|
|
}
|
|
final GetOffsetResult result = GetOffsetResult.fromJson(response['response'] as Map<String, dynamic>);
|
|
return Offset(result.dx, result.dy);
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Center(
|
|
child: SizedBox(
|
|
key: const ValueKey<String>('parent'),
|
|
height: 100,
|
|
width: 100,
|
|
child: Center(
|
|
child: Row(
|
|
children: const <Widget>[
|
|
SizedBox(
|
|
key: ValueKey<String>('leftchild'),
|
|
width: 25,
|
|
height: 25,
|
|
),
|
|
SizedBox(
|
|
key: ValueKey<String>('righttchild'),
|
|
width: 25,
|
|
height: 25,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
)
|
|
),
|
|
)
|
|
);
|
|
|
|
expect(
|
|
await getAncestorTopLeft(of: 'leftchild', matching: 'parent'),
|
|
const Offset((800 - 100) / 2, (600 - 100) / 2),
|
|
);
|
|
expect(
|
|
await getAncestorTopLeft(of: 'leftchild', matching: 'parent', matchRoot: true),
|
|
const Offset((800 - 100) / 2, (600 - 100) / 2),
|
|
);
|
|
expect(
|
|
await getAncestorTopLeft(of: 'parent', matching: 'parent', matchRoot: true),
|
|
const Offset((800 - 100) / 2, (600 - 100) / 2),
|
|
);
|
|
|
|
// Find nothing
|
|
Future<Offset?> result = getAncestorTopLeft(of: 'leftchild', matching: 'leftchild');
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(await result, null);
|
|
|
|
result = getAncestorTopLeft(of: 'leftchild', matching: 'righttchild');
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(await result, null);
|
|
});
|
|
});
|
|
|
|
testWidgets('ancestor finder firstMatchOnly', (WidgetTester tester) async {
|
|
await silenceDriverLogger(() async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension((String? arg) async => '', true, true);
|
|
|
|
Future<Offset?> getAncestorTopLeft() async {
|
|
final Map<String, String> arguments = GetOffset(Ancestor(
|
|
of: ByValueKey('leaf'),
|
|
matching: const ByType('SizedBox'),
|
|
firstMatchOnly: true,
|
|
), OffsetType.topLeft, timeout: const Duration(seconds: 1)).serialize();
|
|
final Map<String, dynamic> response = await driverExtension.call(arguments);
|
|
if (response['isError'] as bool) {
|
|
return null;
|
|
}
|
|
final GetOffsetResult result = GetOffsetResult.fromJson(response['response'] as Map<String, dynamic>);
|
|
return Offset(result.dx, result.dy);
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Center(
|
|
child: SizedBox(
|
|
height: 200,
|
|
width: 200,
|
|
child: Center(
|
|
child: SizedBox(
|
|
height: 100,
|
|
width: 100,
|
|
child: Center(
|
|
child: SizedBox(
|
|
key: ValueKey<String>('leaf'),
|
|
height: 50,
|
|
width: 50,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(
|
|
await getAncestorTopLeft(),
|
|
const Offset((800 - 100) / 2, (600 - 100) / 2),
|
|
);
|
|
});
|
|
});
|
|
|
|
testWidgets('GetDiagnosticsTree', (WidgetTester tester) async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension((String? arg) async => '', true, true);
|
|
|
|
Future<Map<String, dynamic>> getDiagnosticsTree(DiagnosticsType type, SerializableFinder finder, { int depth = 0, bool properties = true }) async {
|
|
final Map<String, String> arguments = GetDiagnosticsTree(finder, type, subtreeDepth: depth, includeProperties: properties).serialize();
|
|
final Map<String, dynamic> response = await driverExtension.call(arguments);
|
|
final DiagnosticsTreeResult result = DiagnosticsTreeResult(response['response'] as Map<String, dynamic>);
|
|
return result.json;
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
const Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: Text('Hello World', key: ValueKey<String>('Text'))
|
|
),
|
|
),
|
|
);
|
|
|
|
// Widget
|
|
Map<String, dynamic> result = await getDiagnosticsTree(DiagnosticsType.widget, ByValueKey('Text'), depth: 0);
|
|
expect(result['children'], isNull); // depth: 0
|
|
expect(result['widgetRuntimeType'], 'Text');
|
|
|
|
List<Map<String, dynamic>> properties = (result['properties']! as List<Object>).cast<Map<String, dynamic>>();
|
|
Map<String, dynamic> stringProperty = properties.singleWhere((Map<String, dynamic> property) => property['name'] == 'data');
|
|
expect(stringProperty['description'], '"Hello World"');
|
|
expect(stringProperty['propertyType'], 'String');
|
|
|
|
result = await getDiagnosticsTree(DiagnosticsType.widget, ByValueKey('Text'), depth: 0, properties: false);
|
|
expect(result['widgetRuntimeType'], 'Text');
|
|
expect(result['properties'], isNull); // properties: false
|
|
|
|
result = await getDiagnosticsTree(DiagnosticsType.widget, ByValueKey('Text'), depth: 1);
|
|
List<Map<String, dynamic>> children = (result['children']! as List<Object>).cast<Map<String, dynamic>>();
|
|
expect(children.single['children'], isNull);
|
|
|
|
result = await getDiagnosticsTree(DiagnosticsType.widget, ByValueKey('Text'), depth: 100);
|
|
children = (result['children']! as List<Object>).cast<Map<String, dynamic>>();
|
|
expect(children.single['children'], isEmpty);
|
|
|
|
// RenderObject
|
|
result = await getDiagnosticsTree(DiagnosticsType.renderObject, ByValueKey('Text'), depth: 0);
|
|
expect(result['children'], isNull); // depth: 0
|
|
expect(result['properties'], isNotNull);
|
|
expect(result['description'], startsWith('RenderParagraph'));
|
|
|
|
result = await getDiagnosticsTree(DiagnosticsType.renderObject, ByValueKey('Text'), depth: 0, properties: false);
|
|
expect(result['properties'], isNull); // properties: false
|
|
expect(result['description'], startsWith('RenderParagraph'));
|
|
|
|
result = await getDiagnosticsTree(DiagnosticsType.renderObject, ByValueKey('Text'), depth: 1);
|
|
children = (result['children']! as List<Object>).cast<Map<String, dynamic>>();
|
|
final Map<String, dynamic> textSpan = children.single;
|
|
expect(textSpan['description'], 'TextSpan');
|
|
properties = (textSpan['properties']! as List<Object>).cast<Map<String, dynamic>>();
|
|
stringProperty = properties.singleWhere((Map<String, dynamic> property) => property['name'] == 'text');
|
|
expect(stringProperty['description'], '"Hello World"');
|
|
expect(stringProperty['propertyType'], 'String');
|
|
expect(children.single['children'], isNull);
|
|
|
|
result = await getDiagnosticsTree(DiagnosticsType.renderObject, ByValueKey('Text'), depth: 100);
|
|
children = (result['children']! as List<Object>).cast<Map<String, dynamic>>();
|
|
expect(children.single['children'], isEmpty);
|
|
});
|
|
|
|
group('enableTextEntryEmulation', () {
|
|
late FlutterDriverExtension driverExtension;
|
|
|
|
Future<Map<String, dynamic>> enterText() async {
|
|
final Map<String, String> arguments = const EnterText('foo').serialize();
|
|
final Map<String, dynamic> result = await driverExtension.call(arguments);
|
|
return result;
|
|
}
|
|
|
|
const Widget testWidget = MaterialApp(
|
|
home: Material(
|
|
child: Center(
|
|
child: TextField(
|
|
key: ValueKey<String>('foo'),
|
|
autofocus: true,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
testWidgets('enableTextEntryEmulation false', (WidgetTester tester) async {
|
|
driverExtension = FlutterDriverExtension((String? arg) async => '', true, false);
|
|
|
|
await tester.pumpWidget(testWidget);
|
|
|
|
final Map<String, dynamic> enterTextResult = await enterText();
|
|
expect(enterTextResult['isError'], isTrue);
|
|
});
|
|
|
|
testWidgets('enableTextEntryEmulation true', (WidgetTester tester) async {
|
|
driverExtension = FlutterDriverExtension((String? arg) async => '', true, true);
|
|
|
|
await tester.pumpWidget(testWidget);
|
|
|
|
final Map<String, dynamic> enterTextResult = await enterText();
|
|
expect(enterTextResult['isError'], isFalse);
|
|
});
|
|
});
|
|
|
|
group('extension finders', () {
|
|
final Widget debugTree = Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: Column(
|
|
key: const ValueKey<String>('Column'),
|
|
children: <Widget>[
|
|
const Text('Foo', key: ValueKey<String>('Text1')),
|
|
const Text('Bar', key: ValueKey<String>('Text2')),
|
|
TextButton(
|
|
key: const ValueKey<String>('Button'),
|
|
onPressed: () {},
|
|
child: const Text('Whatever'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
testWidgets('unknown extension finder', (WidgetTester tester) async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension(
|
|
(String? arg) async => '',
|
|
true,
|
|
true,
|
|
finders: <FinderExtension>[],
|
|
);
|
|
|
|
Future<Map<String, dynamic>> getText(SerializableFinder finder) async {
|
|
final Map<String, String> arguments = GetText(finder, timeout: const Duration(seconds: 1)).serialize();
|
|
return driverExtension.call(arguments);
|
|
}
|
|
|
|
await tester.pumpWidget(debugTree);
|
|
|
|
final Map<String, dynamic> result = await getText(StubFinder('Text1'));
|
|
expect(result['isError'], true);
|
|
expect(result['response'] is String, true);
|
|
expect(result['response'] as String?, contains('Unsupported search specification type Stub'));
|
|
});
|
|
|
|
testWidgets('simple extension finder', (WidgetTester tester) async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension(
|
|
(String? arg) async => '',
|
|
true,
|
|
true,
|
|
finders: <FinderExtension>[
|
|
StubFinderExtension(),
|
|
],
|
|
);
|
|
|
|
Future<GetTextResult> getText(SerializableFinder finder) async {
|
|
final Map<String, String> arguments = GetText(finder, timeout: const Duration(seconds: 1)).serialize();
|
|
final Map<String, dynamic> response = await driverExtension.call(arguments);
|
|
return GetTextResult.fromJson(response['response'] as Map<String, dynamic>);
|
|
}
|
|
|
|
await tester.pumpWidget(debugTree);
|
|
|
|
final GetTextResult result = await getText(StubFinder('Text1'));
|
|
expect(result.text, 'Foo');
|
|
});
|
|
|
|
testWidgets('complex extension finder', (WidgetTester tester) async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension(
|
|
(String? arg) async => '',
|
|
true,
|
|
true,
|
|
finders: <FinderExtension>[
|
|
StubFinderExtension(),
|
|
],
|
|
);
|
|
|
|
Future<GetTextResult> getText(SerializableFinder finder) async {
|
|
final Map<String, String> arguments = GetText(finder, timeout: const Duration(seconds: 1)).serialize();
|
|
final Map<String, dynamic> response = await driverExtension.call(arguments);
|
|
return GetTextResult.fromJson(response['response'] as Map<String, dynamic>);
|
|
}
|
|
|
|
await tester.pumpWidget(debugTree);
|
|
|
|
final GetTextResult result = await getText(Descendant(of: StubFinder('Column'), matching: StubFinder('Text1')));
|
|
expect(result.text, 'Foo');
|
|
});
|
|
|
|
testWidgets('extension finder with command', (WidgetTester tester) async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension(
|
|
(String? arg) async => '',
|
|
true,
|
|
true,
|
|
finders: <FinderExtension>[
|
|
StubFinderExtension(),
|
|
],
|
|
);
|
|
|
|
Future<Map<String, dynamic>> tap(SerializableFinder finder) async {
|
|
final Map<String, String> arguments = Tap(finder, timeout: const Duration(seconds: 1)).serialize();
|
|
return driverExtension.call(arguments);
|
|
}
|
|
|
|
await tester.pumpWidget(debugTree);
|
|
|
|
final Map<String, dynamic> result = await tap(StubFinder('Button'));
|
|
expect(result['isError'], false);
|
|
});
|
|
});
|
|
|
|
group('extension commands', () {
|
|
int invokes = 0;
|
|
void stubCallback() => invokes++;
|
|
|
|
final Widget debugTree = Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: Column(
|
|
children: <Widget>[
|
|
TextButton(
|
|
key: const ValueKey<String>('Button'),
|
|
onPressed: stubCallback,
|
|
child: const Text('Whatever'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
setUp(() {
|
|
invokes = 0;
|
|
});
|
|
|
|
testWidgets('unknown extension command', (WidgetTester tester) async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension(
|
|
(String? arg) async => '',
|
|
true,
|
|
true,
|
|
commands: <CommandExtension>[],
|
|
);
|
|
|
|
Future<Map<String, dynamic>> invokeCommand(SerializableFinder finder, int times) async {
|
|
final Map<String, String> arguments = StubNestedCommand(finder, times).serialize();
|
|
return driverExtension.call(arguments);
|
|
}
|
|
|
|
await tester.pumpWidget(debugTree);
|
|
|
|
final Map<String, dynamic> result = await invokeCommand(ByValueKey('Button'), 10);
|
|
expect(result['isError'], true);
|
|
expect(result['response'] is String, true);
|
|
expect(result['response'] as String?, contains('Unsupported command kind StubNestedCommand'));
|
|
});
|
|
|
|
testWidgets('nested command', (WidgetTester tester) async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension(
|
|
(String? arg) async => '',
|
|
true,
|
|
true,
|
|
commands: <CommandExtension>[
|
|
StubNestedCommandExtension(),
|
|
],
|
|
);
|
|
|
|
Future<StubCommandResult> invokeCommand(SerializableFinder finder, int times) async {
|
|
await driverExtension.call(const SetFrameSync(false).serialize()); // disable frame sync for test to avoid lock
|
|
final Map<String, String> arguments = StubNestedCommand(finder, times, timeout: const Duration(seconds: 1)).serialize();
|
|
final Map<String, dynamic> response = await driverExtension.call(arguments);
|
|
final Map<String, dynamic> commandResponse = response['response'] as Map<String, dynamic>;
|
|
return StubCommandResult(commandResponse['resultParam'] as String);
|
|
}
|
|
|
|
await tester.pumpWidget(debugTree);
|
|
|
|
const int times = 10;
|
|
final StubCommandResult result = await invokeCommand(ByValueKey('Button'), times);
|
|
expect(result.resultParam, 'stub response');
|
|
expect(invokes, times);
|
|
});
|
|
|
|
testWidgets('prober command', (WidgetTester tester) async {
|
|
final FlutterDriverExtension driverExtension = FlutterDriverExtension(
|
|
(String? arg) async => '',
|
|
true,
|
|
true,
|
|
commands: <CommandExtension>[
|
|
StubProberCommandExtension(),
|
|
],
|
|
);
|
|
|
|
Future<StubCommandResult> invokeCommand(SerializableFinder finder, int times) async {
|
|
await driverExtension.call(const SetFrameSync(false).serialize()); // disable frame sync for test to avoid lock
|
|
final Map<String, String> arguments = StubProberCommand(finder, times, timeout: const Duration(seconds: 1)).serialize();
|
|
final Map<String, dynamic> response = await driverExtension.call(arguments);
|
|
final Map<String, dynamic> commandResponse = response['response'] as Map<String, dynamic>;
|
|
return StubCommandResult(commandResponse['resultParam'] as String);
|
|
}
|
|
|
|
await tester.pumpWidget(debugTree);
|
|
|
|
const int times = 10;
|
|
final StubCommandResult result = await invokeCommand(ByValueKey('Button'), times);
|
|
expect(result.resultParam, 'stub response');
|
|
expect(invokes, times);
|
|
});
|
|
});
|
|
|
|
group('waitForTappable', () {
|
|
late FlutterDriverExtension driverExtension;
|
|
|
|
Future<Map<String, dynamic>> waitForTappable() async {
|
|
final SerializableFinder finder = ByValueKey('widgetOne');
|
|
final Map<String, String> arguments = WaitForTappable(finder).serialize();
|
|
final Map<String, dynamic> result = await driverExtension.call(arguments);
|
|
return result;
|
|
}
|
|
|
|
final Widget testWidget = MaterialApp(
|
|
home: Material(
|
|
child: Column(children: const<Widget> [
|
|
Text('Hello ', key: Key('widgetOne')),
|
|
SizedBox(
|
|
height: 0,
|
|
width: 0,
|
|
child: Text('World!', key: Key('widgetTwo')),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
testWidgets('returns true when widget is tappable', (
|
|
WidgetTester tester) async {
|
|
driverExtension = FlutterDriverExtension((String? arg) async => '', true, false);
|
|
|
|
await tester.pumpWidget(testWidget);
|
|
|
|
final Map<String, dynamic> waitForTappableResult = await waitForTappable();
|
|
expect(waitForTappableResult['isError'], isFalse);
|
|
});
|
|
});
|
|
|
|
group('waitUntilFrameSync', () {
|
|
late FlutterDriverExtension driverExtension;
|
|
Map<String, dynamic>? result;
|
|
|
|
setUp(() {
|
|
driverExtension = FlutterDriverExtension((String? arg) async => '', true, true);
|
|
result = null;
|
|
});
|
|
|
|
testWidgets('returns immediately when frame is synced', (
|
|
WidgetTester tester) async {
|
|
driverExtension.call(const WaitForCondition(NoPendingFrame()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
await tester.idle();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'waits until no transient callbacks', (WidgetTester tester) async {
|
|
SchedulerBinding.instance!.scheduleFrameCallback((_) {
|
|
// Intentionally blank. We only care about existence of a callback.
|
|
});
|
|
|
|
driverExtension.call(const WaitForCondition(NoPendingFrame()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
// Nothing should happen until the next frame.
|
|
await tester.idle();
|
|
expect(result, isNull);
|
|
|
|
// NOW we should receive the result.
|
|
await tester.pump();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'waits until no pending scheduled frame', (WidgetTester tester) async {
|
|
SchedulerBinding.instance!.scheduleFrame();
|
|
|
|
driverExtension.call(const WaitForCondition(NoPendingFrame()).serialize())
|
|
.then<void>(expectAsync1((Map<String, dynamic> r) {
|
|
result = r;
|
|
}));
|
|
|
|
// Nothing should happen until the next frame.
|
|
await tester.idle();
|
|
expect(result, isNull);
|
|
|
|
// NOW we should receive the result.
|
|
await tester.pump();
|
|
expect(
|
|
result,
|
|
<String, dynamic>{
|
|
'isError': false,
|
|
'response': <String, dynamic>{},
|
|
},
|
|
);
|
|
});
|
|
});
|
|
}
|