diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 74e11622d1..4559d0fa29 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -904,6 +904,7 @@ Future _runFlutterDriverWebTest({ await runCommand( flutter, [ + ...?flutterTestArgs, 'drive', '--target=$target', '--browser-name=chrome', @@ -1080,6 +1081,7 @@ Future _runGalleryE2eWebTest(String buildMode, { bool canvasKit = false }) await runCommand( flutter, [ + ...?flutterTestArgs, 'drive', if (canvasKit) '--dart-define=FLUTTER_WEB_USE_SKIA=true', @@ -1163,6 +1165,7 @@ Future _runWebReleaseTest(String target, { await runCommand( flutter, [ + ...?flutterTestArgs, 'build', 'web', '--release', diff --git a/dev/integration_tests/web_e2e_tests/test_driver/text_editing_integration.dart b/dev/integration_tests/web_e2e_tests/test_driver/text_editing_integration.dart index f752868857..3f3e149f69 100644 --- a/dev/integration_tests/web_e2e_tests/test_driver/text_editing_integration.dart +++ b/dev/integration_tests/web_e2e_tests/test_driver/text_editing_integration.dart @@ -15,6 +15,15 @@ import 'package:web_e2e_tests/text_editing_main.dart' as app; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + /// Locate elements in the correct root of the application, whether it is + /// `document` or the new `shadowRoot` of `flt-class-pane`. + List 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', (WidgetTester tester) async { app.main(); @@ -29,10 +38,9 @@ void main() { await tester.tap(find.byKey(const Key('input'))); // A native input element will be appended to the DOM. - final List nodeList = document.getElementsByTagName('input'); + final List nodeList = findElements('input'); expect(nodeList.length, equals(1)); - final InputElement input = - document.getElementsByTagName('input')[0] as InputElement; + final InputElement input = nodeList[0] as InputElement; // The element's value will be the same as the textFormField's value. expect(input.value, 'Text1'); @@ -57,10 +65,9 @@ void main() { await tester.tap(find.byKey(const Key('empty-input'))); // A native input element will be appended to the DOM. - final List nodeList = document.getElementsByTagName('input'); + final List nodeList = findElements('input'); expect(nodeList.length, equals(1)); - final InputElement input = - document.getElementsByTagName('input')[0] as InputElement; + final InputElement input = nodeList[0] as InputElement; // The element's value will be empty. expect(input.value, ''); @@ -92,8 +99,7 @@ void main() { await tester.tap(find.byKey(const Key('input2'))); // // Press Tab. This should trigger `onFieldSubmitted` of TextField. - final InputElement input = - document.getElementsByTagName('input')[0] as InputElement; + final InputElement input = findElements('input')[0] as InputElement; dispatchKeyboardEvent(input, 'keydown', { 'keyCode': 13, // Enter. 'cancelable': true, @@ -121,10 +127,9 @@ void main() { await tester.tap(find.byKey(const Key('input'))); // A native input element will be appended to the DOM. - final List nodeList = document.getElementsByTagName('input'); + final List nodeList = findElements('input'); expect(nodeList.length, equals(1)); - final InputElement input = - document.getElementsByTagName('input')[0] as InputElement; + final InputElement input = nodeList[0] as InputElement; // Press Tab. The focus should move to the next TextFormField. dispatchKeyboardEvent(input, 'keydown', { @@ -132,14 +137,14 @@ void main() { 'code': 'Tab', 'bubbles': true, 'cancelable': true, + 'composed': true, }); await tester.pumpAndSettle(); // A native input element for the next TextField should be attached to the // DOM. - final InputElement input2 = - document.getElementsByTagName('input')[0] as InputElement; + final InputElement input2 = findElements('input')[0] as InputElement; expect(input2.value, 'Text2'); }, semanticsEnabled: false); @@ -156,10 +161,9 @@ void main() { await tester.tap(find.byKey(const Key('input'))); // A native input element will be appended to the DOM. - final List nodeList = document.getElementsByTagName('input'); + final List nodeList = findElements('input'); expect(nodeList.length, equals(1)); - final InputElement input = - document.getElementsByTagName('input')[0] as InputElement; + final InputElement input = nodeList[0] as InputElement; // Press and release CapsLock. dispatchKeyboardEvent(input, 'keydown', { @@ -167,12 +171,14 @@ void main() { 'code': 'CapsLock', 'bubbles': true, 'cancelable': true, + 'composed': true, }); dispatchKeyboardEvent(input, 'keyup', { 'key': 'CapsLock', 'code': 'CapsLock', 'bubbles': true, 'cancelable': true, + 'composed': true, }); // Press Tab. The focus should move to the next TextFormField. @@ -181,14 +187,14 @@ void main() { 'code': 'Tab', 'bubbles': true, 'cancelable': true, + 'composed': true, }); await tester.pumpAndSettle(); // A native input element for the next TextField should be attached to the // DOM. - final InputElement input2 = - document.getElementsByTagName('input')[0] as InputElement; + final InputElement input2 = findElements('input')[0] as InputElement; expect(input2.value, 'Text2'); }, semanticsEnabled: false); @@ -216,7 +222,7 @@ void main() { await gesture.up(); // A native input element will be appended to the DOM. - final List nodeList = document.getElementsByTagName('textarea'); + final List nodeList = findElements('textarea'); expect(nodeList.length, equals(1)); final TextAreaElement input = nodeList[0] as TextAreaElement; // The element's value should contain the selectable text. diff --git a/examples/hello_world/test_driver/smoke_web_engine_test.dart b/examples/hello_world/test_driver/smoke_web_engine_test.dart index 875c3fb667..a909d63132 100644 --- a/examples/hello_world/test_driver/smoke_web_engine_test.dart +++ b/examples/hello_world/test_driver/smoke_web_engine_test.dart @@ -8,6 +8,8 @@ import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart' hide TypeMatcher, isInstanceOf; 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 /// Framework and Flutter Web Engine integration. void main() { @@ -35,12 +37,23 @@ void main() { await Future.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;', + [], + ) as SearchContext? ?? driver.webDriver; + // 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')); + final WebElement element = await appRoot.findElement(const By.cssSelector('flt-semantics')); expect(element, isNotNull); });