From 6da4051ba415e811d3493bd260d4e17d865a6b4d Mon Sep 17 00:00:00 2001 From: Pascal Welsch Date: Tue, 12 Nov 2024 21:25:46 +0100 Subject: [PATCH] Show partial tap warning in timline (#69) --- lib/src/act/act.dart | 44 +++++++++-------- lib/src/screenshot/screenshot_annotator.dart | 14 +++--- lib/src/spot/snapshot.dart | 5 +- test/act/find_tappable_area_test.dart | 51 +++++++++++++++++++- 4 files changed, 84 insertions(+), 30 deletions(-) diff --git a/lib/src/act/act.dart b/lib/src/act/act.dart index 2881309a..bbd64461 100644 --- a/lib/src/act/act.dart +++ b/lib/src/act/act.dart @@ -106,7 +106,12 @@ class Act { ); return; } - _reportPartialCoverage(pokablePositions, snapshot); + final partialWarning = + _createPartialCoverageMessage(pokablePositions, snapshot); + if (partialWarning != null) { + // ignore: avoid_print + print(partialWarning); + } final positionToTap = pokablePositions.mostCenterHittablePosition!; final binding = TestWidgetsFlutterBinding.instance; @@ -117,11 +122,12 @@ class Act { CrosshairAnnotator(centerPosition: positionToTap), ], ); + final partial = partialWarning == null ? '' : '\n$partialWarning'; timeline.addEvent( - details: 'Tap ${selector.toStringBreadcrumb()}', eventType: 'Tap Event', + details: 'Tap ${selector.toStringBreadcrumb()}$partial', screenshot: screenshot, - color: Colors.blue, + color: partialWarning == null ? Colors.blue : Colors.purple, ); } @@ -190,7 +196,7 @@ class Act { ); return; } - _reportPartialCoverage(pokablePositions, snapshot); + _createPartialCoverageMessage(pokablePositions, snapshot); final targetName = dragTarget.toStringBreadcrumb(); bool isTargetVisible() { @@ -330,30 +336,28 @@ class Act { } /// Throws a warning when the widget is only partially reacting to tap events - void _reportPartialCoverage( + String? _createPartialCoverageMessage( _PokablePositions pokablePositions, WidgetSnapshot snapshot, ) { final roundUp = pokablePositions.percent.ceil(); if (roundUp > 80) { // Don't be pedantic when the widget is almost fully tappable - return; + return null; } // ignore: avoid_print - print( - "Warning: The '${snapshot.discoveredWidget!.toStringShort()}' is only partially reacting to tap events. " - "Only ~$roundUp% of the widget reacts to hitTest events.\n" - "\n" - "Possible causes:" - " - The widget is partially positioned out of view\n" - " - It is covered by another widget.\n" - " - It is too small (<8x8)\n" - "\n" - "Possible solutions:\n" - " - Scroll the widget into view using act.dragUntilVisible()\n" - " - Make sure no other Widget is overlapping on small screens\n" - " - Increase the Widget size\n", - ); + return "Warning: The '${snapshot.discoveredWidget!.toStringShort()}' is only partially reacting to tap events. " + "Only ~$roundUp% of the widget reacts to hitTest events.\n" + "\n" + "Possible causes:\n" + " - The widget is partially positioned out of view\n" + " - It is covered by another widget.\n" + " - It is too small (<8x8)\n" + "\n" + "Possible solutions:\n" + " - Scroll the widget into view using act.dragUntilVisible()\n" + " - Make sure no other Widget is overlapping on small screens\n" + " - Increase the Widget size\n"; } /// Checks if the widget is visible and not covered by another widget diff --git a/lib/src/screenshot/screenshot_annotator.dart b/lib/src/screenshot/screenshot_annotator.dart index 67b9c68c..00893b29 100644 --- a/lib/src/screenshot/screenshot_annotator.dart +++ b/lib/src/screenshot/screenshot_annotator.dart @@ -108,12 +108,12 @@ class HighlightAnnotator implements ScreenshotAnnotator { /// Highlight plain rectangles on the screenshot with optional labels. HighlightAnnotator.rects( this.rects, { - this.color = const Color(0xFFFF00FF), + this.color, this.labels, }) : assert(labels == null || rects.length == labels.length); /// Highlight elements on the screenshot - factory HighlightAnnotator.elements(List elements) { + factory HighlightAnnotator.elements(List elements, {Color? color}) { final binding = TestWidgetsFlutterBinding.instance; final view = binding.platformDispatcher.implicitView; final devicePixelRatio = view?.devicePixelRatio ?? 1.0; @@ -137,12 +137,12 @@ class HighlightAnnotator implements ScreenshotAnnotator { ); labels.add('${element.toStringShort()} #$index'); } - return HighlightAnnotator.rects(rects, labels: labels); + return HighlightAnnotator.rects(rects, labels: labels, color: color); } /// Highlight a single element on the screenshot - factory HighlightAnnotator.element(Element element) { - return HighlightAnnotator.elements([element]); + factory HighlightAnnotator.element(Element element, {Color? color}) { + return HighlightAnnotator.elements([element], color: color); } /// The rectangles to highlight. They are captured at the point of creation, @@ -153,7 +153,7 @@ class HighlightAnnotator implements ScreenshotAnnotator { final List? labels; /// The color to use for highlighting. - final Color color; + final Color? color; @override String get name => 'Highlight Elements Annotator'; @@ -173,6 +173,8 @@ class HighlightAnnotator implements ScreenshotAnnotator { canvas.drawImage(image, Offset.zero, Paint()); + final color = this.color ?? const Color(0xFFFF00FF); + final paint = Paint() ..color = color ..style = PaintingStyle.stroke diff --git a/lib/src/spot/snapshot.dart b/lib/src/spot/snapshot.dart index bf724a90..a413ea2d 100644 --- a/lib/src/spot/snapshot.dart +++ b/lib/src/spot/snapshot.dart @@ -591,12 +591,13 @@ void _tryMatchingLessSpecificCriteria(WidgetSnapshot snapshot) { annotators: [ HighlightAnnotator.elements( lessSpecificSnapshot.discoveredElements, + color: Colors.cyan, ), ], ); timeline.addEvent( - eventType: 'Assertion', - color: Colors.grey, + eventType: 'Assertion Failed', + color: Colors.red, screenshot: screenshot, details: '$errorBuilder\nFound in widget Tree:\n$significantTree', ); diff --git a/test/act/find_tappable_area_test.dart b/test/act/find_tappable_area_test.dart index c60d33f2..44780705 100644 --- a/test/act/find_tappable_area_test.dart +++ b/test/act/find_tappable_area_test.dart @@ -96,7 +96,8 @@ void main() { lines, startsWith( "Warning: The '_TestButton' is only partially reacting to tap events. Only ~7% of the widget reacts to hitTest events.\n" - 'Possible causes: - The widget is partially positioned out of view\n' + 'Possible causes:\n' + ' - The widget is partially positioned out of view\n' ' - It is covered by another widget.\n' ' - It is too small (<8x8)\n' 'Possible solutions:\n' @@ -107,6 +108,51 @@ void main() { ); }); + testWidgets( + 'Warn about using and finding alternative tappable area in timeline', + (tester) async { + bool tapped = false; + await tester.pumpWidget( + MaterialApp( + home: PokeTestWidget( + columns: 5, + rows: 5, + pokableAtColumnIndex: 4, + pokableAtRowIndex: 4, + widgetToCover: _TestButton( + onTap: () { + tapped = !tapped; + }, + ), + ), + ), + ); + final WidgetSelector button = spot<_TestButton>()..existsOnce(); + + await act.tap(button); + + expect(tapped, isTrue); + final last = timeline.events.last; + expect(last.eventType.label, 'Tap Event'); + expect( + last.details, + contains( + "Warning: The '_TestButton' is only partially reacting to tap events. Only ~7% of the widget reacts to hitTest events.\n" + '\n' + 'Possible causes:\n' + ' - The widget is partially positioned out of view\n' + ' - It is covered by another widget.\n' + ' - It is too small (<8x8)\n' + '\n' + 'Possible solutions:\n' + ' - Scroll the widget into view using act.dragUntilVisible()\n' + ' - Make sure no other Widget is overlapping on small screens\n' + ' - Increase the Widget size', + ), + ); + expect(last.eventType.color, Colors.purple); + }); + testWidgets('Center of widget not tappable, finds alternative tappable area.', (tester) async { bool tapped = false; @@ -150,7 +196,8 @@ void main() { lines, contains( "Warning: The '_TestButton' is only partially reacting to tap events. Only ~67% of the widget reacts to hitTest events.\n" - 'Possible causes: - The widget is partially positioned out of view\n' + 'Possible causes:\n' + ' - The widget is partially positioned out of view\n' ' - It is covered by another widget.\n' ' - It is too small (<8x8)\n' 'Possible solutions:\n'