Skip to content

Commit df88c81

Browse files
[webview_flutter] Improve flaky scroll tests (#7621)
WebView portion of flutter/flutter#154826 Changes the scroll tests to wait for the `onScrollChange` callback to verify the scroll position rather than using `getScrollPosition` immediately. Also split `getScrollPosition`, `scrollTo`, and `scrollBy` into their own tests.
1 parent 100a074 commit df88c81

File tree

3 files changed

+276
-130
lines changed

3 files changed

+276
-130
lines changed

packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart

Lines changed: 86 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ Future<void> main() async {
491491
});
492492

493493
group('Programmatic Scroll', () {
494-
testWidgets('setAndGetScrollPosition', (WidgetTester tester) async {
494+
Future<WebViewController> pumpScrollTestPage(WidgetTester tester) async {
495495
const String scrollTestPage = '''
496496
<!DOCTYPE html>
497497
<html>
@@ -518,58 +518,105 @@ Future<void> main() async {
518518

519519
final Completer<void> pageLoaded = Completer<void>();
520520
final WebViewController controller = WebViewController();
521-
ScrollPositionChange? recordedPosition;
522-
await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
523521
await controller.setNavigationDelegate(NavigationDelegate(
524522
onPageFinished: (_) => pageLoaded.complete(),
525523
));
526-
await controller.setOnScrollPositionChange(
527-
(ScrollPositionChange contentOffsetChange) {
528-
recordedPosition = contentOffsetChange;
529-
});
530524

531525
await controller.loadRequest(Uri.parse(
532526
'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
533527
));
534528

535529
await tester.pumpWidget(WebViewWidget(controller: controller));
536-
537530
await pageLoaded.future;
538531

539-
await tester.pumpAndSettle(const Duration(seconds: 3));
540-
541-
Offset scrollPos = await controller.getScrollPosition();
542-
543-
// Check scrollTo()
544-
const int X_SCROLL = 123;
545-
const int Y_SCROLL = 321;
546-
// Get the initial position; this ensures that scrollTo is actually
547-
// changing something, but also gives the native view's scroll position
548-
// time to settle.
549-
expect(scrollPos.dx, isNot(X_SCROLL));
550-
expect(scrollPos.dy, isNot(Y_SCROLL));
551-
expect(recordedPosition?.x, isNot(X_SCROLL));
552-
expect(recordedPosition?.y, isNot(Y_SCROLL));
553-
554-
await controller.scrollTo(X_SCROLL, Y_SCROLL);
555-
scrollPos = await controller.getScrollPosition();
556-
expect(scrollPos.dx, X_SCROLL);
557-
expect(scrollPos.dy, Y_SCROLL);
558-
expect(recordedPosition?.x, X_SCROLL);
559-
expect(recordedPosition?.y, Y_SCROLL);
560-
561-
// Check scrollBy() (on top of scrollTo())
562-
await controller.scrollBy(X_SCROLL, Y_SCROLL);
563-
scrollPos = await controller.getScrollPosition();
564-
expect(scrollPos.dx, X_SCROLL * 2);
565-
expect(scrollPos.dy, Y_SCROLL * 2);
566-
expect(recordedPosition?.x, X_SCROLL * 2);
567-
expect(recordedPosition?.y, Y_SCROLL * 2);
532+
return controller;
533+
}
534+
535+
testWidgets('getScrollPosition', (WidgetTester tester) async {
536+
final WebViewController controller = await pumpScrollTestPage(tester);
537+
await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
538+
539+
const Offset testScrollPosition = Offset(123, 321);
540+
541+
// Ensure the start scroll position is not equal to the test position.
542+
expect(await controller.getScrollPosition(), isNot(testScrollPosition));
543+
544+
final Completer<void> testScrollPositionCompleter = Completer<void>();
545+
await controller.setOnScrollPositionChange(
546+
(ScrollPositionChange contentOffsetChange) {
547+
if (Offset(contentOffsetChange.x, contentOffsetChange.y) ==
548+
testScrollPosition) {
549+
testScrollPositionCompleter.complete();
550+
}
551+
},
552+
);
553+
554+
await controller.scrollTo(
555+
testScrollPosition.dx.toInt(),
556+
testScrollPosition.dy.toInt(),
557+
);
558+
await testScrollPositionCompleter.future;
559+
560+
expect(await controller.getScrollPosition(), testScrollPosition);
561+
});
562+
563+
testWidgets('scrollTo', (WidgetTester tester) async {
564+
final WebViewController controller = await pumpScrollTestPage(tester);
565+
566+
const Offset testScrollPosition = Offset(123, 321);
567+
568+
// Ensure the start scroll position is not equal to the test position.
569+
expect(await controller.getScrollPosition(), isNot(testScrollPosition));
570+
571+
late ScrollPositionChange lastPositionChange;
572+
await controller.setOnScrollPositionChange(
573+
expectAsyncUntil1(
574+
(ScrollPositionChange contentOffsetChange) {
575+
lastPositionChange = contentOffsetChange;
576+
},
577+
() {
578+
return Offset(lastPositionChange.x, lastPositionChange.y) ==
579+
testScrollPosition;
580+
},
581+
),
582+
);
583+
584+
await controller.scrollTo(
585+
testScrollPosition.dx.toInt(),
586+
testScrollPosition.dy.toInt(),
587+
);
588+
});
589+
590+
testWidgets('scrollBy', (WidgetTester tester) async {
591+
final WebViewController controller = await pumpScrollTestPage(tester);
592+
593+
const Offset testScrollPosition = Offset(123, 321);
594+
595+
// Ensure the start scroll position is not equal to the test position.
596+
expect(await controller.getScrollPosition(), isNot(testScrollPosition));
597+
598+
late ScrollPositionChange lastPositionChange;
599+
await controller.setOnScrollPositionChange(
600+
expectAsyncUntil1(
601+
(ScrollPositionChange contentOffsetChange) {
602+
lastPositionChange = contentOffsetChange;
603+
},
604+
() {
605+
return Offset(lastPositionChange.x, lastPositionChange.y) ==
606+
testScrollPosition;
607+
},
608+
),
609+
);
610+
611+
await controller.scrollTo(0, 0);
612+
await controller.scrollBy(
613+
testScrollPosition.dx.toInt(),
614+
testScrollPosition.dy.toInt(),
615+
);
568616
});
569617
},
570618
// Scroll position is currently not implemented for macOS.
571-
// Flakes on iOS: https://github.com/flutter/flutter/issues/154826
572-
skip: Platform.isMacOS || Platform.isIOS);
619+
skip: Platform.isMacOS);
573620

574621
group('NavigationDelegate', () {
575622
const String blankPage = '<!DOCTYPE html><head></head><body></body></html>';

packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart

Lines changed: 94 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -758,8 +758,9 @@ Future<void> main() async {
758758
});
759759

760760
group('Programmatic Scroll', () {
761-
testWidgets('setAndGetAndListenScrollPosition',
762-
(WidgetTester tester) async {
761+
Future<PlatformWebViewController> pumpScrollTestPage(
762+
WidgetTester tester,
763+
) async {
763764
const String scrollTestPage = '''
764765
<!DOCTYPE html>
765766
<html>
@@ -785,28 +786,20 @@ Future<void> main() async {
785786
base64Encode(const Utf8Encoder().convert(scrollTestPage));
786787

787788
final Completer<void> pageLoaded = Completer<void>();
788-
ScrollPositionChange? recordedPosition;
789789
final PlatformWebViewController controller = PlatformWebViewController(
790790
const PlatformWebViewControllerCreationParams(),
791791
);
792-
await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
793792
final PlatformNavigationDelegate delegate = PlatformNavigationDelegate(
794793
const PlatformNavigationDelegateCreationParams(),
795794
);
796795
await delegate.setOnPageFinished((_) => pageLoaded.complete());
797796
await controller.setPlatformNavigationDelegate(delegate);
798-
await controller.setOnScrollPositionChange(
799-
(ScrollPositionChange contentOffsetChange) {
800-
recordedPosition = contentOffsetChange;
801-
});
802797

803-
await controller.loadRequest(
804-
LoadRequestParams(
805-
uri: Uri.parse(
806-
'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
807-
),
798+
await controller.loadRequest(LoadRequestParams(
799+
uri: Uri.parse(
800+
'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
808801
),
809-
);
802+
));
810803

811804
await tester.pumpWidget(Builder(
812805
builder: (BuildContext context) {
@@ -815,37 +808,95 @@ Future<void> main() async {
815808
).build(context);
816809
},
817810
));
818-
819811
await pageLoaded.future;
820812

821-
await tester.pumpAndSettle(const Duration(seconds: 3));
822-
823-
Offset scrollPos = await controller.getScrollPosition();
824-
825-
// Check scrollTo()
826-
const int X_SCROLL = 123;
827-
const int Y_SCROLL = 321;
828-
// Get the initial position; this ensures that scrollTo is actually
829-
// changing something, but also gives the native view's scroll position
830-
// time to settle.
831-
expect(scrollPos.dx, isNot(X_SCROLL));
832-
expect(scrollPos.dy, isNot(Y_SCROLL));
833-
expect(recordedPosition, null);
834-
835-
await controller.scrollTo(X_SCROLL, Y_SCROLL);
836-
scrollPos = await controller.getScrollPosition();
837-
expect(scrollPos.dx, X_SCROLL);
838-
expect(scrollPos.dy, Y_SCROLL);
839-
expect(recordedPosition?.x, X_SCROLL);
840-
expect(recordedPosition?.y, Y_SCROLL);
841-
842-
// Check scrollBy() (on top of scrollTo())
843-
await controller.scrollBy(X_SCROLL, Y_SCROLL);
844-
scrollPos = await controller.getScrollPosition();
845-
expect(scrollPos.dx, X_SCROLL * 2);
846-
expect(scrollPos.dy, Y_SCROLL * 2);
847-
expect(recordedPosition?.x, X_SCROLL * 2);
848-
expect(recordedPosition?.y, Y_SCROLL * 2);
813+
return controller;
814+
}
815+
816+
testWidgets('getScrollPosition', (WidgetTester tester) async {
817+
final PlatformWebViewController controller =
818+
await pumpScrollTestPage(tester);
819+
await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
820+
821+
const Offset testScrollPosition = Offset(123, 321);
822+
823+
// Ensure the start scroll position is not equal to the test position.
824+
expect(await controller.getScrollPosition(), isNot(testScrollPosition));
825+
826+
final Completer<void> testScrollPositionCompleter = Completer<void>();
827+
await controller.setOnScrollPositionChange(
828+
(ScrollPositionChange contentOffsetChange) {
829+
if (Offset(contentOffsetChange.x, contentOffsetChange.y) ==
830+
testScrollPosition) {
831+
testScrollPositionCompleter.complete();
832+
}
833+
},
834+
);
835+
836+
await controller.scrollTo(
837+
testScrollPosition.dx.toInt(),
838+
testScrollPosition.dy.toInt(),
839+
);
840+
await testScrollPositionCompleter.future;
841+
842+
expect(await controller.getScrollPosition(), testScrollPosition);
843+
});
844+
845+
testWidgets('scrollTo', (WidgetTester tester) async {
846+
final PlatformWebViewController controller =
847+
await pumpScrollTestPage(tester);
848+
849+
const Offset testScrollPosition = Offset(123, 321);
850+
851+
// Ensure the start scroll position is not equal to the test position.
852+
expect(await controller.getScrollPosition(), isNot(testScrollPosition));
853+
854+
late ScrollPositionChange lastPositionChange;
855+
await controller.setOnScrollPositionChange(
856+
expectAsyncUntil1(
857+
(ScrollPositionChange contentOffsetChange) {
858+
lastPositionChange = contentOffsetChange;
859+
},
860+
() {
861+
return Offset(lastPositionChange.x, lastPositionChange.y) ==
862+
testScrollPosition;
863+
},
864+
),
865+
);
866+
867+
await controller.scrollTo(
868+
testScrollPosition.dx.toInt(),
869+
testScrollPosition.dy.toInt(),
870+
);
871+
});
872+
873+
testWidgets('scrollBy', (WidgetTester tester) async {
874+
final PlatformWebViewController controller =
875+
await pumpScrollTestPage(tester);
876+
877+
const Offset testScrollPosition = Offset(123, 321);
878+
879+
// Ensure the start scroll position is not equal to the test position.
880+
expect(await controller.getScrollPosition(), isNot(testScrollPosition));
881+
882+
late ScrollPositionChange lastPositionChange;
883+
await controller.setOnScrollPositionChange(
884+
expectAsyncUntil1(
885+
(ScrollPositionChange contentOffsetChange) {
886+
lastPositionChange = contentOffsetChange;
887+
},
888+
() {
889+
return Offset(lastPositionChange.x, lastPositionChange.y) ==
890+
testScrollPosition;
891+
},
892+
),
893+
);
894+
895+
await controller.scrollTo(0, 0);
896+
await controller.scrollBy(
897+
testScrollPosition.dx.toInt(),
898+
testScrollPosition.dy.toInt(),
899+
);
849900
});
850901
});
851902

0 commit comments

Comments
 (0)