mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
add waitUntilNoTransientCallbacks to driver API (#8475)
This commit is contained in:
parent
c7695b4ad4
commit
bfef36f710
|
@ -75,7 +75,7 @@ Future<Null> main() async {
|
|||
await _runFlutterTest(p.join(flutterRoot, 'packages', 'flutter'),
|
||||
options: coverageFlags,
|
||||
);
|
||||
await _runAllDartTests(p.join(flutterRoot, 'packages', 'flutter_driver'));
|
||||
await _runFlutterTest(p.join(flutterRoot, 'packages', 'flutter_driver'));
|
||||
await _runFlutterTest(p.join(flutterRoot, 'packages', 'flutter_test'));
|
||||
await _runFlutterTest(p.join(flutterRoot, 'packages', 'flutter_markdown'));
|
||||
await _runAllDartTests(p.join(flutterRoot, 'packages', 'flutter_tools'),
|
||||
|
|
|
@ -339,6 +339,15 @@ class FlutterDriver {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// Waits until there are no more transient callbacks in the queue.
|
||||
///
|
||||
/// Use this method when you need to wait for the moment when the application
|
||||
/// becomes "stable", for example, prior to taking a [screenshot].
|
||||
Future<Null> waitUntilNoTransientCallbacks({Duration timeout}) async {
|
||||
await _sendCommand(new WaitUntilNoTransientCallbacks(timeout: timeout));
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Tell the driver to perform a scrolling action.
|
||||
///
|
||||
/// A scrolling action begins with a "pointer down" event, which commonly maps
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart' show RendererBinding;
|
||||
|
@ -27,7 +29,7 @@ class _DriverBinding extends WidgetsFlutterBinding { // TODO(ianh): refactor so
|
|||
@override
|
||||
void initServiceExtensions() {
|
||||
super.initServiceExtensions();
|
||||
_FlutterDriverExtension extension = new _FlutterDriverExtension._();
|
||||
FlutterDriverExtension extension = new FlutterDriverExtension();
|
||||
registerServiceExtension(
|
||||
name: _extensionMethodName,
|
||||
callback: extension.call
|
||||
|
@ -57,10 +59,11 @@ typedef Command CommandDeserializerCallback(Map<String, String> params);
|
|||
/// Runs the finder and returns the [Element] found, or `null`.
|
||||
typedef Finder FinderConstructor(SerializableFinder finder);
|
||||
|
||||
class _FlutterDriverExtension {
|
||||
@visibleForTesting
|
||||
class FlutterDriverExtension {
|
||||
static final Logger _log = new Logger('FlutterDriverExtension');
|
||||
|
||||
_FlutterDriverExtension._() {
|
||||
FlutterDriverExtension() {
|
||||
_commandHandlers.addAll(<String, CommandHandlerCallback>{
|
||||
'get_health': _getHealth,
|
||||
'get_render_tree': _getRenderTree,
|
||||
|
@ -72,6 +75,7 @@ class _FlutterDriverExtension {
|
|||
'setInputText': _setInputText,
|
||||
'submitInputText': _submitInputText,
|
||||
'waitFor': _waitFor,
|
||||
'waitUntilNoTransientCallbacks': _waitUntilNoTransientCallbacks,
|
||||
});
|
||||
|
||||
_commandDeserializers.addAll(<String, CommandDeserializerCallback>{
|
||||
|
@ -85,6 +89,7 @@ class _FlutterDriverExtension {
|
|||
'setInputText': (Map<String, String> params) => new SetInputText.deserialize(params),
|
||||
'submitInputText': (Map<String, String> params) => new SubmitInputText.deserialize(params),
|
||||
'waitFor': (Map<String, String> params) => new WaitFor.deserialize(params),
|
||||
'waitUntilNoTransientCallbacks': (Map<String, String> params) => new WaitUntilNoTransientCallbacks.deserialize(params),
|
||||
});
|
||||
|
||||
_finders.addAll(<String, FinderConstructor>{
|
||||
|
@ -123,7 +128,7 @@ class _FlutterDriverExtension {
|
|||
throw 'Extension $_extensionMethod does not support command $commandKind';
|
||||
Command command = commandDeserializer(params);
|
||||
Result response = await commandHandler(command).timeout(command.timeout);
|
||||
return _makeResponse(response.toJson());
|
||||
return _makeResponse(response?.toJson());
|
||||
} on TimeoutException catch (error, stackTrace) {
|
||||
String msg = 'Timeout while executing $commandKind: $error\n$stackTrace';
|
||||
_log.error(msg);
|
||||
|
@ -221,6 +226,11 @@ class _FlutterDriverExtension {
|
|||
return null;
|
||||
}
|
||||
|
||||
Future<Null> _waitUntilNoTransientCallbacks(Command command) async {
|
||||
if (SchedulerBinding.instance.transientCallbackCount != 0)
|
||||
await _waitUntilFrame(() => SchedulerBinding.instance.transientCallbackCount == 0);
|
||||
}
|
||||
|
||||
Future<ScrollResult> _scroll(Command command) async {
|
||||
Scroll scrollCommand = command;
|
||||
Finder target = await _waitForElement(_createFinder(scrollCommand.finder));
|
||||
|
|
|
@ -62,6 +62,18 @@ class WaitFor extends CommandWithTarget {
|
|||
WaitFor.deserialize(Map<String, String> json) : super.deserialize(json);
|
||||
}
|
||||
|
||||
/// Waits until there are no more transient callbacks in the queue.
|
||||
class WaitUntilNoTransientCallbacks extends Command {
|
||||
@override
|
||||
final String kind = 'waitUntilNoTransientCallbacks';
|
||||
|
||||
WaitUntilNoTransientCallbacks({Duration timeout}) : super(timeout: timeout);
|
||||
|
||||
/// Deserializes the command from JSON generated by [serialize].
|
||||
WaitUntilNoTransientCallbacks.deserialize(Map<String, String> json)
|
||||
: super.deserialize(json);
|
||||
}
|
||||
|
||||
/// The result of a [WaitFor] command.
|
||||
class WaitForResult extends Result {
|
||||
/// Deserializes the result from JSON.
|
||||
|
|
|
@ -11,6 +11,7 @@ dependencies:
|
|||
file: 2.3.0
|
||||
json_rpc_2: '^2.0.0'
|
||||
matcher: '>=0.12.0 <1.0.0'
|
||||
meta: ^1.0.4
|
||||
path: '^1.4.0'
|
||||
web_socket_channel: '^1.0.0'
|
||||
vm_service_client: '0.2.2+4'
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'flutter_driver_test.dart' as flutter_driver_test;
|
||||
import 'src/retry_test.dart' as retry_test;
|
||||
import 'src/timeline_summary_test.dart' as timeline_summary_test;
|
||||
import 'src/timeline_test.dart' as timeline_test;
|
||||
|
||||
void main() {
|
||||
flutter_driver_test.main();
|
||||
retry_test.main();
|
||||
timeline_summary_test.main();
|
||||
timeline_test.main();
|
||||
}
|
|
@ -9,7 +9,7 @@ import 'package:flutter_driver/src/error.dart';
|
|||
import 'package:flutter_driver/src/health.dart';
|
||||
import 'package:flutter_driver/src/timeline.dart';
|
||||
import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:mockito/mockito_no_mirrors.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:vm_service_client/vm_service_client.dart';
|
||||
|
||||
|
@ -199,6 +199,19 @@ void main() {
|
|||
});
|
||||
});
|
||||
|
||||
group('waitUntilNoTransientCallbacks', () {
|
||||
test('sends the waitUntilNoTransientCallbacks command', () async {
|
||||
when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
|
||||
expect(i.positionalArguments[1], <String, dynamic>{
|
||||
'command': 'waitUntilNoTransientCallbacks',
|
||||
'timeout': '1000',
|
||||
});
|
||||
return makeMockResponse(<String, dynamic>{});
|
||||
});
|
||||
await driver.waitUntilNoTransientCallbacks(timeout: const Duration(seconds: 1));
|
||||
});
|
||||
});
|
||||
|
||||
group('traceAction', () {
|
||||
test('traces action', () async {
|
||||
bool actionCalled = false;
|
||||
|
|
61
packages/flutter_driver/test/src/extension_test.dart
Normal file
61
packages/flutter_driver/test/src/extension_test.dart
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_driver/src/extension.dart';
|
||||
import 'package:flutter_driver/src/find.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group('waitUntilNoTransientCallbacks', () {
|
||||
FlutterDriverExtension extension;
|
||||
Map<String, dynamic> result;
|
||||
|
||||
setUp(() {
|
||||
result = null;
|
||||
extension = new FlutterDriverExtension();
|
||||
});
|
||||
|
||||
testWidgets('returns immediately when transient callback queue is empty', (WidgetTester tester) async {
|
||||
extension.call(new WaitUntilNoTransientCallbacks().serialize())
|
||||
.then<Null>(expectAsync1((Map<String, dynamic> r) {
|
||||
result = r;
|
||||
}));
|
||||
|
||||
await tester.idle();
|
||||
expect(
|
||||
result,
|
||||
<String, dynamic>{
|
||||
'isError': false,
|
||||
'response': null,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('waits until no transient callbacks', (WidgetTester tester) async {
|
||||
SchedulerBinding.instance.scheduleFrameCallback((_) {
|
||||
// Intentionally blank. We only care about existence of a callback.
|
||||
});
|
||||
|
||||
extension.call(new WaitUntilNoTransientCallbacks().serialize())
|
||||
.then<Null>(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': null,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -11,7 +11,7 @@ import 'package:flutter_tools/src/base/platform.dart';
|
|||
import 'package:flutter_tools/src/commands/drive.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/ios/simulators.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:mockito/mockito_no_mirrors.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'src/common.dart';
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_tools/src/commands/install.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:mockito/mockito_no_mirrors.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'src/common.dart';
|
||||
|
|
|
@ -20,7 +20,7 @@ import 'package:flutter_tools/src/ios/simulators.dart';
|
|||
import 'package:flutter_tools/src/run_hot.dart';
|
||||
import 'package:flutter_tools/src/usage.dart';
|
||||
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:mockito/mockito_no_mirrors.dart';
|
||||
import 'package:process/process.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:file/file.dart';
|
|||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/ios/devices.dart';
|
||||
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:mockito/mockito_no_mirrors.dart';
|
||||
import 'package:process/process.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:flutter_tools/src/doctor.dart';
|
|||
import 'package:flutter_tools/src/ios/ios_workflow.dart';
|
||||
import 'package:flutter_tools/src/ios/mac.dart';
|
||||
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:mockito/mockito_no_mirrors.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../context.dart';
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:file/file.dart';
|
|||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/ios/mac.dart';
|
||||
import 'package:flutter_tools/src/ios/simulators.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:mockito/mockito_no_mirrors.dart';
|
||||
import 'package:process/process.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import 'package:flutter_tools/src/device.dart';
|
|||
import 'package:flutter_tools/src/ios/devices.dart';
|
||||
import 'package:flutter_tools/src/ios/simulators.dart';
|
||||
import 'package:flutter_tools/src/runner/flutter_command.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:mockito/mockito_no_mirrors.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class MockApplicationPackageStore extends ApplicationPackageStore {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_tools/src/commands/stop.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:mockito/mockito_no_mirrors.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'src/common.dart';
|
||||
|
|
Loading…
Reference in a new issue