add waitUntilNoTransientCallbacks to driver API (#8475)

This commit is contained in:
Yegor 2017-02-28 15:58:28 -08:00 committed by GitHub
parent c7695b4ad4
commit bfef36f710
16 changed files with 120 additions and 29 deletions

View file

@ -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'),

View file

@ -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

View file

@ -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));

View file

@ -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.

View file

@ -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'

View file

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

View file

@ -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;

View 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,
},
);
});
});
}

View file

@ -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';

View file

@ -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';

View file

@ -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';

View file

@ -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';

View file

@ -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';

View file

@ -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';

View file

@ -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 {

View file

@ -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';