Unverified Commit 3cfbb958 authored by Nguyen Phuc Loi's avatar Nguyen Phuc Loi Committed by GitHub

[flutter_driver] support log communication for WebFlutterDriver (#81150)

parent af3337b6
......@@ -147,7 +147,12 @@ abstract class FlutterDriver {
Map<String, dynamic>? headers,
}) async {
if (Platform.environment['FLUTTER_WEB_TEST'] != null) {
return WebFlutterDriver.connectWeb(hostUrl: dartVmServiceUrl, timeout: timeout);
return WebFlutterDriver.connectWeb(
hostUrl: dartVmServiceUrl,
timeout: timeout,
printCommunication: printCommunication,
logCommunicationToFile: logCommunicationToFile,
);
}
return VMServiceFlutterDriver.connect(
dartVmServiceUrl: dartVmServiceUrl,
......
......@@ -4,15 +4,19 @@
import 'dart:convert';
import 'dart:io';
import 'package:file/file.dart';
import 'package:matcher/matcher.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
import 'package:vm_service/vm_service.dart' as vms;
import 'package:webdriver/async_io.dart' as async_io;
import 'package:webdriver/support/async.dart';
import '../common/error.dart';
import '../common/message.dart';
import 'common.dart';
import 'driver.dart';
import 'timeline.dart';
......@@ -24,12 +28,22 @@ import 'timeline.dart';
class WebFlutterDriver extends FlutterDriver {
/// Creates a driver that uses a connection provided by the given
/// [_connection].
WebFlutterDriver.connectedTo(this._connection) :
_startTime = DateTime.now();
WebFlutterDriver.connectedTo(
this._connection, {
bool printCommunication = false,
bool logCommunicationToFile = true,
}) : _printCommunication = printCommunication,
_logCommunicationToFile = logCommunicationToFile,
_startTime = DateTime.now(),
_driverId = _nextDriverId++;
final FlutterWebConnection _connection;
DateTime _startTime;
bool _accessibilityEnabled = false;
static int _nextDriverId = 0;
/// The unique ID of this driver instance.
final int _driverId;
/// Start time for tracing.
@visibleForTesting
......@@ -44,14 +58,26 @@ class WebFlutterDriver extends FlutterDriver {
@override
async_io.WebDriver get webDriver => _connection._driver;
/// Whether to print communication between host and app to `stdout`.
final bool _printCommunication;
/// Whether to log communication between host and app to `flutter_driver_commands.log`.
final bool _logCommunicationToFile;
/// Creates a driver that uses a connection provided by the given
/// [hostUrl] which would fallback to environment variable VM_SERVICE_URL.
/// Driver also depends on environment variables DRIVER_SESSION_ID,
/// BROWSER_SUPPORTS_TIMELINE, DRIVER_SESSION_URI, DRIVER_SESSION_SPEC,
/// DRIVER_SESSION_CAPABILITIES and ANDROID_CHROME_ON_EMULATOR for
/// configurations.
static Future<FlutterDriver> connectWeb(
{String? hostUrl, Duration? timeout}) async {
///
/// See [FlutterDriver.connect] for more documentation.
static Future<FlutterDriver> connectWeb({
String? hostUrl,
bool printCommunication = false,
bool logCommunicationToFile = true,
Duration? timeout,
}) async {
hostUrl ??= Platform.environment['VM_SERVICE_URL'];
final Map<String, dynamic> settings = <String, dynamic>{
'support-timeline-action': Platform.environment['SUPPORT_TIMELINE_ACTION'] == 'true',
......@@ -63,7 +89,11 @@ class WebFlutterDriver extends FlutterDriver {
};
final FlutterWebConnection connection = await FlutterWebConnection.connect
(hostUrl!, settings, timeout: timeout);
return WebFlutterDriver.connectedTo(connection);
return WebFlutterDriver.connectedTo(
connection,
printCommunication: printCommunication,
logCommunicationToFile: logCommunicationToFile,
);
}
@override
......@@ -86,9 +116,11 @@ class WebFlutterDriver extends FlutterDriver {
Future<Map<String, dynamic>> sendCommand(Command command) async {
Map<String, dynamic> response;
final Map<String, String> serialized = command.serialize();
_logCommunication('>>> $serialized');
try {
final dynamic data = await _connection.sendCommand("window.\$flutterDriver('${jsonEncode(serialized)}')", command.timeout);
response = data != null ? (json.decode(data as String) as Map<String, dynamic>?)! : <String, dynamic>{};
_logCommunication('<<< $response');
} catch (error, stackTrace) {
throw DriverError("Failed to respond to $command due to remote error\n : \$flutterDriver('${jsonEncode(serialized)}')",
error,
......@@ -108,6 +140,17 @@ class WebFlutterDriver extends FlutterDriver {
throw UnimplementedError();
}
void _logCommunication(String message) {
if (_printCommunication) {
driverLog('WebFlutterDriver', message);
}
if (_logCommunicationToFile) {
final File file = fs.file(path.join(testOutputsDirectory, 'flutter_driver_commands_$_driverId.log'));
file.createSync(recursive: true); // no-op if file exists
file.writeAsStringSync('${DateTime.now()} $message\n', mode: FileMode.append, flush: true);
}
}
@override
Future<List<int>> screenshot() async {
await Future<void>.delayed(const Duration(seconds: 2));
......
......@@ -4,14 +4,17 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:fake_async/fake_async.dart';
import 'package:flutter_driver/src/common/error.dart';
import 'package:flutter_driver/src/common/health.dart';
import 'package:flutter_driver/src/common/layer_tree.dart';
import 'package:flutter_driver/src/common/wait.dart';
import 'package:flutter_driver/src/driver/common.dart';
import 'package:flutter_driver/src/driver/driver.dart';
import 'package:flutter_driver/src/driver/timeline.dart';
import 'package:path/path.dart' as path;
import 'package:vm_service/vm_service.dart' as vms;
import '../../common.dart';
......@@ -24,10 +27,87 @@ const String _kWebScriptSuffix = "')";
void main() {
final List<String> log = <String>[];
driverLog = (String source, String message) {
log.add('$source: $message');
};
group('VMServiceFlutterDriver with logCommunicationToFile', () {
late FakeVmService fakeClient;
late FakeVM fakeVM;
late FakeIsolate fakeIsolate;
late VMServiceFlutterDriver driver;
int driverId = -1;
setUp(() {
fakeIsolate = FakeIsolate();
fakeVM = FakeVM(fakeIsolate);
fakeClient = FakeVmService(fakeVM);
fakeClient.responses['waitFor'] = makeFakeResponse(<String, dynamic>{'status':'ok'});
driverId += 1;
});
group('logCommunicationToFile', () {
test('logCommunicationToFile = true', () async {
driver = VMServiceFlutterDriver.connectedTo(fakeClient, fakeIsolate);
await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout);
final File file = File(path.join(testOutputsDirectory, 'flutter_driver_commands_$driverId.log'));
final bool exists = file.existsSync();
expect(exists, true, reason: 'Not found ${file.path}');
final String commandLog = await file.readAsString();
const String waitForCommandLog = '>>> {command: waitFor, timeout: $_kSerializedTestTimeout, finderType: ByTooltipMessage, text: foo}';
const String responseLog = '<<< {isError: false, response: {status: ok}}';
expect(commandLog.contains(waitForCommandLog), true, reason: '$commandLog not contains $waitForCommandLog');
expect(commandLog.contains(responseLog), true, reason: '$commandLog not contains $responseLog');
});
test('logCommunicationToFile = false', () async {
driver = VMServiceFlutterDriver.connectedTo(fakeClient, fakeIsolate, logCommunicationToFile: false);
await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout);
final File file = File(path.join(testOutputsDirectory, 'flutter_driver_commands_$driverId.log'));
final bool exists = file.existsSync();
expect(exists, false, reason: 'because ${file.path} exists');
});
});
});
group('VMServiceFlutterDriver with printCommunication', () {
late FakeVmService fakeClient;
late FakeVM fakeVM;
late FakeIsolate fakeIsolate;
late VMServiceFlutterDriver driver;
setUp(() async {
log.clear();
fakeIsolate = FakeIsolate();
fakeVM = FakeVM(fakeIsolate);
fakeClient = FakeVmService(fakeVM);
fakeClient.responses['waitFor'] = makeFakeResponse(<String, dynamic>{'status':'ok'});
});
test('printCommunication = true', () async {
driver = VMServiceFlutterDriver.connectedTo(fakeClient, fakeIsolate, printCommunication: true);
await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout);
expect(log, <String>[
'VMServiceFlutterDriver: >>> {command: waitFor, timeout: $_kSerializedTestTimeout, finderType: ByTooltipMessage, text: foo}',
'VMServiceFlutterDriver: <<< {isError: false, response: {status: ok}}'
]);
});
test('printCommunication = false', () async {
driver = VMServiceFlutterDriver.connectedTo(fakeClient, fakeIsolate, printCommunication: false);
await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout);
expect(log, <String>[]);
});
});
group('VMServiceFlutterDriver.connect', () {
late FakeVmService fakeClient;
late FakeVM fakeVM;
......@@ -188,8 +268,8 @@ void main() {
group('VMServiceFlutterDriver', () {
late FakeVmService fakeClient;
FakeVM fakeVM;
FakeIsolate fakeIsolate;
late FakeVM fakeVM;
late FakeIsolate fakeIsolate;
late VMServiceFlutterDriver driver;
setUp(() {
......@@ -563,8 +643,8 @@ void main() {
group('VMServiceFlutterDriver with custom timeout', () {
late FakeVmService fakeClient;
FakeVM fakeVM;
FakeIsolate fakeIsolate;
late FakeVM fakeVM;
late FakeIsolate fakeIsolate;
late VMServiceFlutterDriver driver;
setUp(() {
......@@ -592,6 +672,70 @@ void main() {
});
});
group('WebFlutterDriver with logCommunicationToFile', () {
late FakeFlutterWebConnection fakeConnection;
late WebFlutterDriver driver;
int driverId = -1;
setUp(() {
fakeConnection = FakeFlutterWebConnection();
fakeConnection.supportsTimelineAction = true;
fakeConnection.responses['waitFor'] = jsonEncode(makeFakeResponse(<String, dynamic>{'status': 'ok'}));
driverId += 1;
});
test('logCommunicationToFile = true', () async {
driver = WebFlutterDriver.connectedTo(fakeConnection);
await driver.waitFor(find.byTooltip('logCommunicationToFile test'), timeout: _kTestTimeout);
final File file = File(path.join(testOutputsDirectory, 'flutter_driver_commands_$driverId.log'));
final bool exists = file.existsSync();
expect(exists, true, reason: 'Not found ${file.path}');
final String commandLog = await file.readAsString();
const String waitForCommandLog = '>>> {command: waitFor, timeout: 1234, finderType: ByTooltipMessage, text: logCommunicationToFile test}';
const String responseLog = '<<< {isError: false, response: {status: ok}, type: Response}';
expect(commandLog.contains(waitForCommandLog), true, reason: '$commandLog not contains $waitForCommandLog');
expect(commandLog.contains(responseLog), true, reason: '$commandLog not contains $responseLog');
});
test('logCommunicationToFile = false', () async {
driver = WebFlutterDriver.connectedTo(fakeConnection, logCommunicationToFile: false);
await driver.waitFor(find.byTooltip('logCommunicationToFile test'), timeout: _kTestTimeout);
final File file = File(path.join(testOutputsDirectory, 'flutter_driver_commands_$driverId.log'));
final bool exists = file.existsSync();
expect(exists, false, reason: 'because ${file.path} exists');
});
});
group('WebFlutterDriver with printCommunication', () {
late FakeFlutterWebConnection fakeConnection;
late WebFlutterDriver driver;
setUp(() {
log.clear();
fakeConnection = FakeFlutterWebConnection();
fakeConnection.supportsTimelineAction = true;
fakeConnection.responses['waitFor'] = jsonEncode(makeFakeResponse(<String, dynamic>{'status': 'ok'}));
});
test('printCommunication = true', () async {
driver = WebFlutterDriver.connectedTo(fakeConnection, printCommunication: true);
await driver.waitFor(find.byTooltip('printCommunication test'), timeout: _kTestTimeout);
expect(log, <String>[
'WebFlutterDriver: >>> {command: waitFor, timeout: 1234, finderType: ByTooltipMessage, text: printCommunication test}',
'WebFlutterDriver: <<< {isError: false, response: {status: ok}, type: Response}',
]);
});
test('printCommunication = false', () async {
driver = WebFlutterDriver.connectedTo(fakeConnection, printCommunication: false);
await driver.waitFor(find.byTooltip('printCommunication test'), timeout: _kTestTimeout);
expect(log, <String>[]);
});
});
group('WebFlutterDriver', () {
late FakeFlutterWebConnection fakeConnection;
late WebFlutterDriver driver;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment