[pointer_interceptor] fix integration test (#1675)
diff --git a/packages/pointer_interceptor/CHANGELOG.md b/packages/pointer_interceptor/CHANGELOG.md
index dbf6a01..74dc5e6 100644
--- a/packages/pointer_interceptor/CHANGELOG.md
+++ b/packages/pointer_interceptor/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.9.3+1
+
+* Updates example code and integration tests to accomodate hit-testing changes in the Flutter web engine.
+
## 0.9.3
* Require minimal version of flutter SDK to be `2.10`
diff --git a/packages/pointer_interceptor/example/integration_test/widget_test.dart b/packages/pointer_interceptor/example/integration_test/widget_test.dart
index c1c054a..45d3850 100644
--- a/packages/pointer_interceptor/example/integration_test/widget_test.dart
+++ b/packages/pointer_interceptor/example/integration_test/widget_test.dart
@@ -11,37 +11,29 @@
import 'package:pointer_interceptor_example/main.dart' as app;
+final Finder nonClickableButtonFinder =
+ find.byKey(const Key('transparent-button'));
+final Finder clickableWrappedButtonFinder =
+ find.byKey(const Key('wrapped-transparent-button'));
+final Finder clickableButtonFinder = find.byKey(const Key('clickable-button'));
+final Finder backgroundFinder =
+ find.byKey(const ValueKey<String>('background-widget'));
+
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
- group('Widget', () {
- final Finder nonClickableButtonFinder =
- find.byKey(const Key('transparent-button'));
- final Finder clickableWrappedButtonFinder =
- find.byKey(const Key('wrapped-transparent-button'));
- final Finder clickableButtonFinder =
- find.byKey(const Key('clickable-button'));
-
+ group('Without semantics', () {
testWidgets(
'on wrapped elements, the browser does not hit the background-html-view',
(WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
- final html.Element? element =
- _getHtmlElementFromFinder(clickableButtonFinder, tester);
+ final html.Element element =
+ _getHtmlElementAtCenter(clickableButtonFinder, tester);
- if (html.document.querySelector('flt-glass-pane')?.shadowRoot != null) {
- // In flutter master...
- expect(element?.id, isNot('background-html-view'));
- } else {
- // In previous versions (--web-renderer=html only)...
- expect(element?.tagName.toLowerCase(), 'flt-platform-view');
- final html.Element? platformViewRoot =
- element?.shadowRoot?.getElementById('background-html-view');
- expect(platformViewRoot, isNull);
- }
- });
+ expect(element.id, isNot('background-html-view'));
+ }, semanticsEnabled: false);
testWidgets(
'on wrapped elements with intercepting set to false, the browser hits the background-html-view',
@@ -49,20 +41,11 @@
app.main();
await tester.pumpAndSettle();
- final html.Element? element =
- _getHtmlElementFromFinder(clickableWrappedButtonFinder, tester);
+ final html.Element element =
+ _getHtmlElementAtCenter(clickableWrappedButtonFinder, tester);
- if (html.document.querySelector('flt-glass-pane')?.shadowRoot != null) {
- // In flutter master...
- expect(element?.id, 'background-html-view');
- } else {
- // In previous versions (--web-renderer=html only)...
- expect(element?.tagName.toLowerCase(), 'flt-platform-view');
- final html.Element? platformViewRoot =
- element?.shadowRoot?.getElementById('background-html-view');
- expect(platformViewRoot, isNotNull);
- }
- });
+ expect(element.id, 'background-html-view');
+ }, semanticsEnabled: false);
testWidgets(
'on unwrapped elements, the browser hits the background-html-view',
@@ -70,27 +53,99 @@
app.main();
await tester.pumpAndSettle();
- final html.Element? element =
- _getHtmlElementFromFinder(nonClickableButtonFinder, tester);
+ final html.Element element =
+ _getHtmlElementAtCenter(nonClickableButtonFinder, tester);
- if (html.document.querySelector('flt-glass-pane')?.shadowRoot != null) {
- // In flutter master...
- expect(element?.id, 'background-html-view');
- } else {
- // In previous versions (--web-renderer=html only)...
- expect(element?.tagName.toLowerCase(), 'flt-platform-view');
- final html.Element? platformViewRoot =
- element?.shadowRoot?.getElementById('background-html-view');
- expect(platformViewRoot, isNotNull);
- }
+ expect(element.id, 'background-html-view');
+ }, semanticsEnabled: false);
+
+ testWidgets('on background directly', (WidgetTester tester) async {
+ app.main();
+ await tester.pumpAndSettle();
+
+ final html.Element element =
+ _getHtmlElementAt(tester.getTopLeft(backgroundFinder));
+
+ expect(element.id, 'background-html-view');
+ }, semanticsEnabled: false);
+ });
+
+ group('With semantics', () {
+ testWidgets('finds semantics of wrapped widgets',
+ (WidgetTester tester) async {
+ app.main();
+ await tester.pumpAndSettle();
+
+ final html.Element element =
+ _getHtmlElementAtCenter(clickableButtonFinder, tester);
+
+ expect(element.tagName.toLowerCase(), 'flt-semantics');
+ expect(element.getAttribute('aria-label'), 'Works As Expected');
+ });
+
+ testWidgets(
+ 'finds semantics of wrapped widgets with intercepting set to false',
+ (WidgetTester tester) async {
+ app.main();
+ await tester.pumpAndSettle();
+
+ final html.Element element =
+ _getHtmlElementAtCenter(clickableWrappedButtonFinder, tester);
+
+ expect(element.tagName.toLowerCase(), 'flt-semantics');
+ expect(element.getAttribute('aria-label'),
+ 'Never calls onPressed transparent');
+ });
+
+ testWidgets('finds semantics of unwrapped elements',
+ (WidgetTester tester) async {
+ app.main();
+ await tester.pumpAndSettle();
+
+ final html.Element element =
+ _getHtmlElementAtCenter(nonClickableButtonFinder, tester);
+
+ expect(element.tagName.toLowerCase(), 'flt-semantics');
+ expect(element.getAttribute('aria-label'), 'Never calls onPressed');
+ });
+
+ // Notice that, when hit-testing the background platform view, instead of
+ // finding a semantics node, the platform view itself is found. This is
+ // because the platform view does not add interactive semantics nodes into
+ // the framework's semantics tree. Instead, its semantics is determined by
+ // the HTML content of the platform view itself. Flutter's semantics tree
+ // simply allows the hit test to land on the platform view by making itself
+ // hit test transparent.
+ testWidgets('on background directly', (WidgetTester tester) async {
+ app.main();
+ await tester.pumpAndSettle();
+
+ final html.Element element =
+ _getHtmlElementAt(tester.getTopLeft(backgroundFinder));
+
+ expect(element.id, 'background-html-view');
});
});
}
-// This functions locates a widget from a Finder, and asks the browser what's the
-// DOM element in the center of the coordinates of the widget. (Returns *which*
-// DOM element will handle Mouse interactions first at those coordinates.)
-html.Element? _getHtmlElementFromFinder(Finder finder, WidgetTester tester) {
+// Calls [_getHtmlElementAt] passing it the center of the widget identified by
+// the `finder`.
+html.Element _getHtmlElementAtCenter(Finder finder, WidgetTester tester) {
final Offset point = tester.getCenter(finder);
- return html.document.elementFromPoint(point.dx.toInt(), point.dy.toInt());
+ return _getHtmlElementAt(point);
+}
+
+// Locates the DOM element at the given `point` using `elementFromPoint`.
+//
+// `elementFromPoint` is an approximate proxy for a hit test, although it's
+// sensitive to the presence of shadow roots and browser quirks (not all
+// browsers agree on what it should return in all situations). Since this test
+// runs only in Chromium, it relies on Chromium's behavior.
+html.Element _getHtmlElementAt(Offset point) {
+ // Probe at the shadow so the browser reports semantics nodes in addition to
+ // platform view elements. If probed from `html.document` the browser hides
+ // the contents of <flt-glass-name> as an implementation detail.
+ final html.ShadowRoot glassPaneShadow =
+ html.document.querySelector('flt-glass-pane')!.shadowRoot!;
+ return glassPaneShadow.elementFromPoint(point.dx.toInt(), point.dy.toInt())!;
}
diff --git a/packages/pointer_interceptor/example/lib/main.dart b/packages/pointer_interceptor/example/lib/main.dart
index 234d31d..b30ea01 100644
--- a/packages/pointer_interceptor/example/lib/main.dart
+++ b/packages/pointer_interceptor/example/lib/main.dart
@@ -116,6 +116,7 @@
alignment: Alignment.center,
children: <Widget>[
HtmlElement(
+ key: const ValueKey<String>('background-widget'),
onClick: () {
_clickedOn('html-element');
},
@@ -134,7 +135,8 @@
intercepting: false,
child: ElevatedButton(
key: const Key('wrapped-transparent-button'),
- child: const Text('Never calls onPressed'),
+ child:
+ const Text('Never calls onPressed transparent'),
onPressed: () {
_clickedOn('wrapped-transparent-button');
},
diff --git a/packages/pointer_interceptor/pubspec.yaml b/packages/pointer_interceptor/pubspec.yaml
index 753dfd1..2f5d762 100644
--- a/packages/pointer_interceptor/pubspec.yaml
+++ b/packages/pointer_interceptor/pubspec.yaml
@@ -2,7 +2,7 @@
description: A widget to prevent clicks from being swallowed by underlying HtmlElementViews on the web.
repository: https://github.com/flutter/packages/tree/main/packages/pointer_interceptor
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pointer_interceptor%22
-version: 0.9.3
+version: 0.9.3+1
environment:
sdk: ">=2.12.0 <3.0.0"