[web] Make web integration tests shadow DOM aware. (#82926)
This commit is contained in:
parent
fa1c6a2841
commit
c94f0bea65
@ -904,6 +904,7 @@ Future<void> _runFlutterDriverWebTest({
|
|||||||
await runCommand(
|
await runCommand(
|
||||||
flutter,
|
flutter,
|
||||||
<String>[
|
<String>[
|
||||||
|
...?flutterTestArgs,
|
||||||
'drive',
|
'drive',
|
||||||
'--target=$target',
|
'--target=$target',
|
||||||
'--browser-name=chrome',
|
'--browser-name=chrome',
|
||||||
@ -1080,6 +1081,7 @@ Future<void> _runGalleryE2eWebTest(String buildMode, { bool canvasKit = false })
|
|||||||
await runCommand(
|
await runCommand(
|
||||||
flutter,
|
flutter,
|
||||||
<String>[
|
<String>[
|
||||||
|
...?flutterTestArgs,
|
||||||
'drive',
|
'drive',
|
||||||
if (canvasKit)
|
if (canvasKit)
|
||||||
'--dart-define=FLUTTER_WEB_USE_SKIA=true',
|
'--dart-define=FLUTTER_WEB_USE_SKIA=true',
|
||||||
@ -1163,6 +1165,7 @@ Future<void> _runWebReleaseTest(String target, {
|
|||||||
await runCommand(
|
await runCommand(
|
||||||
flutter,
|
flutter,
|
||||||
<String>[
|
<String>[
|
||||||
|
...?flutterTestArgs,
|
||||||
'build',
|
'build',
|
||||||
'web',
|
'web',
|
||||||
'--release',
|
'--release',
|
||||||
|
@ -15,6 +15,15 @@ import 'package:web_e2e_tests/text_editing_main.dart' as app;
|
|||||||
void main() {
|
void main() {
|
||||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
/// Locate elements in the correct root of the application, whether it is
|
||||||
|
/// `document` or the new `shadowRoot` of `flt-class-pane`.
|
||||||
|
List<Node> findElements(String selector) {
|
||||||
|
final ShadowRoot? shadowRoot = document.querySelector('flt-glass-pane')?.shadowRoot;
|
||||||
|
return (shadowRoot != null) ?
|
||||||
|
shadowRoot.querySelectorAll(selector):
|
||||||
|
document.querySelectorAll(selector);
|
||||||
|
}
|
||||||
|
|
||||||
testWidgets('Focused text field creates a native input element',
|
testWidgets('Focused text field creates a native input element',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
app.main();
|
app.main();
|
||||||
@ -29,10 +38,9 @@ void main() {
|
|||||||
await tester.tap(find.byKey(const Key('input')));
|
await tester.tap(find.byKey(const Key('input')));
|
||||||
|
|
||||||
// A native input element will be appended to the DOM.
|
// A native input element will be appended to the DOM.
|
||||||
final List<Node> nodeList = document.getElementsByTagName('input');
|
final List<Node> nodeList = findElements('input');
|
||||||
expect(nodeList.length, equals(1));
|
expect(nodeList.length, equals(1));
|
||||||
final InputElement input =
|
final InputElement input = nodeList[0] as InputElement;
|
||||||
document.getElementsByTagName('input')[0] as InputElement;
|
|
||||||
// The element's value will be the same as the textFormField's value.
|
// The element's value will be the same as the textFormField's value.
|
||||||
expect(input.value, 'Text1');
|
expect(input.value, 'Text1');
|
||||||
|
|
||||||
@ -57,10 +65,9 @@ void main() {
|
|||||||
await tester.tap(find.byKey(const Key('empty-input')));
|
await tester.tap(find.byKey(const Key('empty-input')));
|
||||||
|
|
||||||
// A native input element will be appended to the DOM.
|
// A native input element will be appended to the DOM.
|
||||||
final List<Node> nodeList = document.getElementsByTagName('input');
|
final List<Node> nodeList = findElements('input');
|
||||||
expect(nodeList.length, equals(1));
|
expect(nodeList.length, equals(1));
|
||||||
final InputElement input =
|
final InputElement input = nodeList[0] as InputElement;
|
||||||
document.getElementsByTagName('input')[0] as InputElement;
|
|
||||||
// The element's value will be empty.
|
// The element's value will be empty.
|
||||||
expect(input.value, '');
|
expect(input.value, '');
|
||||||
|
|
||||||
@ -92,8 +99,7 @@ void main() {
|
|||||||
await tester.tap(find.byKey(const Key('input2')));
|
await tester.tap(find.byKey(const Key('input2')));
|
||||||
|
|
||||||
// // Press Tab. This should trigger `onFieldSubmitted` of TextField.
|
// // Press Tab. This should trigger `onFieldSubmitted` of TextField.
|
||||||
final InputElement input =
|
final InputElement input = findElements('input')[0] as InputElement;
|
||||||
document.getElementsByTagName('input')[0] as InputElement;
|
|
||||||
dispatchKeyboardEvent(input, 'keydown', <String, dynamic>{
|
dispatchKeyboardEvent(input, 'keydown', <String, dynamic>{
|
||||||
'keyCode': 13, // Enter.
|
'keyCode': 13, // Enter.
|
||||||
'cancelable': true,
|
'cancelable': true,
|
||||||
@ -121,10 +127,9 @@ void main() {
|
|||||||
await tester.tap(find.byKey(const Key('input')));
|
await tester.tap(find.byKey(const Key('input')));
|
||||||
|
|
||||||
// A native input element will be appended to the DOM.
|
// A native input element will be appended to the DOM.
|
||||||
final List<Node> nodeList = document.getElementsByTagName('input');
|
final List<Node> nodeList = findElements('input');
|
||||||
expect(nodeList.length, equals(1));
|
expect(nodeList.length, equals(1));
|
||||||
final InputElement input =
|
final InputElement input = nodeList[0] as InputElement;
|
||||||
document.getElementsByTagName('input')[0] as InputElement;
|
|
||||||
|
|
||||||
// Press Tab. The focus should move to the next TextFormField.
|
// Press Tab. The focus should move to the next TextFormField.
|
||||||
dispatchKeyboardEvent(input, 'keydown', <String, dynamic>{
|
dispatchKeyboardEvent(input, 'keydown', <String, dynamic>{
|
||||||
@ -132,14 +137,14 @@ void main() {
|
|||||||
'code': 'Tab',
|
'code': 'Tab',
|
||||||
'bubbles': true,
|
'bubbles': true,
|
||||||
'cancelable': true,
|
'cancelable': true,
|
||||||
|
'composed': true,
|
||||||
});
|
});
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
// A native input element for the next TextField should be attached to the
|
// A native input element for the next TextField should be attached to the
|
||||||
// DOM.
|
// DOM.
|
||||||
final InputElement input2 =
|
final InputElement input2 = findElements('input')[0] as InputElement;
|
||||||
document.getElementsByTagName('input')[0] as InputElement;
|
|
||||||
expect(input2.value, 'Text2');
|
expect(input2.value, 'Text2');
|
||||||
}, semanticsEnabled: false);
|
}, semanticsEnabled: false);
|
||||||
|
|
||||||
@ -156,10 +161,9 @@ void main() {
|
|||||||
await tester.tap(find.byKey(const Key('input')));
|
await tester.tap(find.byKey(const Key('input')));
|
||||||
|
|
||||||
// A native input element will be appended to the DOM.
|
// A native input element will be appended to the DOM.
|
||||||
final List<Node> nodeList = document.getElementsByTagName('input');
|
final List<Node> nodeList = findElements('input');
|
||||||
expect(nodeList.length, equals(1));
|
expect(nodeList.length, equals(1));
|
||||||
final InputElement input =
|
final InputElement input = nodeList[0] as InputElement;
|
||||||
document.getElementsByTagName('input')[0] as InputElement;
|
|
||||||
|
|
||||||
// Press and release CapsLock.
|
// Press and release CapsLock.
|
||||||
dispatchKeyboardEvent(input, 'keydown', <String, dynamic>{
|
dispatchKeyboardEvent(input, 'keydown', <String, dynamic>{
|
||||||
@ -167,12 +171,14 @@ void main() {
|
|||||||
'code': 'CapsLock',
|
'code': 'CapsLock',
|
||||||
'bubbles': true,
|
'bubbles': true,
|
||||||
'cancelable': true,
|
'cancelable': true,
|
||||||
|
'composed': true,
|
||||||
});
|
});
|
||||||
dispatchKeyboardEvent(input, 'keyup', <String, dynamic>{
|
dispatchKeyboardEvent(input, 'keyup', <String, dynamic>{
|
||||||
'key': 'CapsLock',
|
'key': 'CapsLock',
|
||||||
'code': 'CapsLock',
|
'code': 'CapsLock',
|
||||||
'bubbles': true,
|
'bubbles': true,
|
||||||
'cancelable': true,
|
'cancelable': true,
|
||||||
|
'composed': true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Press Tab. The focus should move to the next TextFormField.
|
// Press Tab. The focus should move to the next TextFormField.
|
||||||
@ -181,14 +187,14 @@ void main() {
|
|||||||
'code': 'Tab',
|
'code': 'Tab',
|
||||||
'bubbles': true,
|
'bubbles': true,
|
||||||
'cancelable': true,
|
'cancelable': true,
|
||||||
|
'composed': true,
|
||||||
});
|
});
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
// A native input element for the next TextField should be attached to the
|
// A native input element for the next TextField should be attached to the
|
||||||
// DOM.
|
// DOM.
|
||||||
final InputElement input2 =
|
final InputElement input2 = findElements('input')[0] as InputElement;
|
||||||
document.getElementsByTagName('input')[0] as InputElement;
|
|
||||||
expect(input2.value, 'Text2');
|
expect(input2.value, 'Text2');
|
||||||
}, semanticsEnabled: false);
|
}, semanticsEnabled: false);
|
||||||
|
|
||||||
@ -216,7 +222,7 @@ void main() {
|
|||||||
await gesture.up();
|
await gesture.up();
|
||||||
|
|
||||||
// A native input element will be appended to the DOM.
|
// A native input element will be appended to the DOM.
|
||||||
final List<Node> nodeList = document.getElementsByTagName('textarea');
|
final List<Node> nodeList = findElements('textarea');
|
||||||
expect(nodeList.length, equals(1));
|
expect(nodeList.length, equals(1));
|
||||||
final TextAreaElement input = nodeList[0] as TextAreaElement;
|
final TextAreaElement input = nodeList[0] as TextAreaElement;
|
||||||
// The element's value should contain the selectable text.
|
// The element's value should contain the selectable text.
|
||||||
|
@ -8,6 +8,8 @@ import 'package:flutter_driver/flutter_driver.dart';
|
|||||||
import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
|
import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
|
||||||
import 'package:webdriver/async_io.dart';
|
import 'package:webdriver/async_io.dart';
|
||||||
|
|
||||||
|
// TODO(web): Migrate this test to a normal integration_test with a WidgetTester.
|
||||||
|
|
||||||
/// The following test is used as a simple smoke test for verifying Flutter
|
/// The following test is used as a simple smoke test for verifying Flutter
|
||||||
/// Framework and Flutter Web Engine integration.
|
/// Framework and Flutter Web Engine integration.
|
||||||
void main() {
|
void main() {
|
||||||
@ -35,12 +37,23 @@ void main() {
|
|||||||
|
|
||||||
await Future<void>.delayed(const Duration(seconds: 2));
|
await Future<void>.delayed(const Duration(seconds: 2));
|
||||||
|
|
||||||
|
// A flutter web app may be rendered directly on the body of the page, or
|
||||||
|
// inside the shadow root of the flt-glass-pane (after Flutter 2.4). To
|
||||||
|
// make this test backwards compatible, we first need to locate the correct
|
||||||
|
// root for the app.
|
||||||
|
//
|
||||||
|
// It's either the shadowRoot within flt-glass-pane, or [driver.webDriver].
|
||||||
|
final SearchContext appRoot = await driver.webDriver.execute(
|
||||||
|
'return document.querySelector("flt-glass-pane")?.shadowRoot;',
|
||||||
|
<dynamic>[],
|
||||||
|
) as SearchContext? ?? driver.webDriver;
|
||||||
|
|
||||||
// Elements with tag "flt-semantics" would show up after enabling
|
// Elements with tag "flt-semantics" would show up after enabling
|
||||||
// accessibility.
|
// accessibility.
|
||||||
//
|
//
|
||||||
// The tag used here is based on
|
// The tag used here is based on
|
||||||
// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/semantics/semantics.dart#L534
|
// 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'));
|
final WebElement element = await appRoot.findElement(const By.cssSelector('flt-semantics'));
|
||||||
|
|
||||||
expect(element, isNotNull);
|
expect(element, isNotNull);
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user