Unverified Commit d2fa384c authored by Angjie Li's avatar Angjie Li Committed by GitHub

Allow Developers to enable Accessibility testing on WebFlutterDriver and get...

Allow Developers to enable Accessibility testing on WebFlutterDriver and get the underlying webDriver (#65051)
parent 46eacc54
......@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
import 'package:webdriver/async_io.dart';
/// The following test is used as a simple smoke test for verfying Flutter
/// The following test is used as a simple smoke test for verifying Flutter
/// Framework and Flutter Web Engine integration.
void main() {
group('Hello World App', () {
......@@ -28,5 +31,20 @@ void main() {
test('title is correct', () async {
expect(await driver.getText(titleFinder), 'Hello, world!');
});
test('enable accessibility', () async {
await driver.enableAccessibility();
await Future<void>.delayed(const Duration(seconds: 2));
// Elements with tag "flt-semantics" would show up after enabling
// accessibility.
//
// The tag used here is based on
// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/semantics/semantics.dart#L534
final WebElement element = await driver.webDriver.findElement(const By.tagName('flt-semantics'));
expect(element, isNotNull);
});
});
}
......@@ -8,6 +8,7 @@ import 'dart:io';
import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
import 'package:meta/meta.dart';
import 'package:vm_service_client/vm_service_client.dart';
import 'package:webdriver/async_io.dart' as async_io;
import '../common/diagnostics_tree.dart';
import '../common/error.dart';
......@@ -169,6 +170,14 @@ abstract class FlutterDriver {
/// Getter of serviceClient.
VMServiceClient get serviceClient => throw UnimplementedError();
/// Getter of webDriver.
async_io.WebDriver get webDriver => throw UnimplementedError();
/// Enables accessibility feature.
Future<void> enableAccessibility() async {
throw UnimplementedError();
}
/// Sends [command] to the Flutter Driver extensions.
/// This must be implemented by subclass.
///
......
......@@ -12,6 +12,7 @@ import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
import 'package:meta/meta.dart';
import 'package:path/path.dart' as p;
import 'package:vm_service_client/vm_service_client.dart';
import 'package:webdriver/async_io.dart' as async_io;
import 'package:web_socket_channel/io.dart';
import '../../flutter_driver.dart';
......@@ -303,6 +304,9 @@ class VMServiceFlutterDriver extends FlutterDriver {
@override
VMServiceClient get serviceClient => _serviceClient;
@override
async_io.WebDriver get webDriver => throw UnsupportedError('VMServiceFlutterDriver does not support webDriver');
/// The main isolate hosting the Flutter application.
///
/// If you used the [registerExtension] API to instrument your application,
......@@ -316,6 +320,11 @@ class VMServiceFlutterDriver extends FlutterDriver {
/// Whether to log communication between host and app to `flutter_driver_commands.log`.
final bool _logCommunicationToFile;
@override
Future<void> enableAccessibility() async {
throw UnsupportedError('VMServiceFlutterDriver does not support enableAccessibility');
}
@override
Future<Map<String, dynamic>> sendCommand(Command command) async {
Map<String, dynamic> response;
......
......@@ -30,6 +30,7 @@ class WebFlutterDriver extends FlutterDriver {
final FlutterWebConnection _connection;
DateTime _startTime;
bool _accessibilityEnabled = false;
/// Start time for tracing.
@visibleForTesting
......@@ -41,11 +42,15 @@ class WebFlutterDriver extends FlutterDriver {
@override
VMServiceClient get serviceClient => throw UnsupportedError('WebFlutterDriver does not support serviceClient');
@override
async_io.WebDriver get webDriver => _connection._driver;
/// 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
/// and ANDROID_CHROME_ON_EMULATOR for configurations.
/// 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 {
hostUrl ??= Platform.environment['VM_SERVICE_URL'];
......@@ -55,12 +60,29 @@ class WebFlutterDriver extends FlutterDriver {
'session-uri': Platform.environment['DRIVER_SESSION_URI'],
'session-spec': Platform.environment['DRIVER_SESSION_SPEC'],
'android-chrome-on-emulator': Platform.environment['ANDROID_CHROME_ON_EMULATOR'] == 'true',
'session-capabilities': Platform.environment['DRIVER_SESSION_CAPABILITIES'],
};
final FlutterWebConnection connection = await FlutterWebConnection.connect
(hostUrl, settings, timeout: timeout);
return WebFlutterDriver.connectedTo(connection);
}
@override
Future<void> enableAccessibility() async {
if (!_accessibilityEnabled) {
// Clicks the button to enable accessibility via Javascript for Desktop Web.
//
// The tag used in the script is based on
// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart#L193
//
// TODO(angjieli): Support Mobile Web. (https://github.com/flutter/flutter/issues/65192)
await webDriver.execute(
'document.querySelector(\'flt-semantics-placeholder\').click();',
<String>[]);
_accessibilityEnabled = true;
}
}
@override
Future<Map<String, dynamic>> sendCommand(Command command) async {
Map<String, dynamic> response;
......@@ -177,12 +199,14 @@ class FlutterWebConnection {
String url,
Map<String, dynamic> settings,
{Duration timeout}) async {
// Use sync WebDriver because async version will create a 15 seconds
// overhead when quitting.
final async_io.WebDriver driver = await async_io.fromExistingSession(
settings['session-id'].toString(),
uri: Uri.parse(settings['session-uri'].toString()),
spec: _convertToSpec(settings['session-spec'].toString().toLowerCase()));
final String sessionId = settings['session-id'].toString();
final Uri sessionUri = Uri.parse(settings['session-uri'].toString());
final async_io.WebDriver driver = async_io.WebDriver(
sessionUri,
sessionId,
json.decode(settings['session-capabilities'] as String) as Map<String, dynamic>,
async_io.AsyncIoRequestClient(sessionUri.resolve('session/$sessionId/')),
_convertToSpec(settings['session-spec'].toString().toLowerCase()));
if (settings['android-chrome-on-emulator'] == true) {
final Uri localUri = Uri.parse(url);
// Converts to Android Emulator Uri.
......
......@@ -768,6 +768,16 @@ void main() {
expect(driver.waitFor(find.byTooltip('foo')), throwsDriverError);
});
});
group('VMServiceFlutterDriver Unsupported error', () {
test('enableAccessibility', () async {
expect(driver.enableAccessibility(), throwsA(isA<UnsupportedError>()));
});
test('webDriver', () async {
expect(() => driver.webDriver, throwsA(isA<UnsupportedError>()));
});
});
});
group('VMServiceFlutterDriver with custom timeout', () {
......@@ -1117,7 +1127,7 @@ void main() {
await driver.checkHealth();
});
group('WebFlutterDriver Unimplemented error', () {
group('WebFlutterDriver Unimplemented/Unsupported error', () {
test('forceGC', () async {
expect(driver.forceGC(),
throwsA(isA<UnimplementedError>()));
......
......@@ -18,6 +18,7 @@ import '../base/common.dart';
import '../base/file_system.dart';
import '../base/process.dart';
import '../build_info.dart';
import '../convert.dart';
import '../dart/package_map.dart';
import '../device.dart';
import '../globals.dart' as globals;
......@@ -306,6 +307,7 @@ $ex
'DRIVER_SESSION_ID': driver.id,
'DRIVER_SESSION_URI': driver.uri.toString(),
'DRIVER_SESSION_SPEC': driver.spec.toString(),
'DRIVER_SESSION_CAPABILITIES': json.encode(driver.capabilities),
'SUPPORT_TIMELINE_ACTION': (browser == Browser.chrome).toString(),
'FLUTTER_WEB_TEST': 'true',
'ANDROID_CHROME_ON_EMULATOR': (isAndroidChrome && useEmulator).toString(),
......
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