diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/legacy/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/legacy/webview_flutter_test.dart index 7f30a2b50386..dae1bdc38055 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/legacy/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/legacy/webview_flutter_test.dart @@ -1,1377 +1,1383 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This test is run using `flutter drive` by the CI (see /script/tool/README.md -// in this repository for details on driving that tooling manually), but can -// also be run using `flutter test` directly during development. - -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; -import 'package:webview_flutter/src/webview_flutter_legacy.dart'; - -Future main() async { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - final HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 0); - unawaited(server.forEach((HttpRequest request) { - if (request.uri.path == '/hello.txt') { - request.response.writeln('Hello, world.'); - } else if (request.uri.path == '/secondary.txt') { - request.response.writeln('How are you today?'); - } else if (request.uri.path == '/headers') { - request.response.writeln('${request.headers}'); - } else if (request.uri.path == '/favicon.ico') { - request.response.statusCode = HttpStatus.notFound; - } else { - fail('unexpected request: ${request.method} ${request.uri}'); - } - request.response.close(); - })); - final String prefixUrl = 'http://${server.address.address}:${server.port}'; - final String primaryUrl = '$prefixUrl/hello.txt'; - final String secondaryUrl = '$prefixUrl/secondary.txt'; - final String headersUrl = '$prefixUrl/headers'; - - testWidgets('initialUrl', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final Completer pageFinishedCompleter = Completer(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - onPageFinished: pageFinishedCompleter.complete, - ), - ), - ); - - final WebViewController controller = await controllerCompleter.future; - await pageFinishedCompleter.future; - - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, primaryUrl); - }); - - testWidgets('loadUrl', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final StreamController pageLoads = StreamController(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - onPageFinished: (String url) { - pageLoads.add(url); - }, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - - await controller.loadUrl(secondaryUrl); - await expectLater( - pageLoads.stream.firstWhere((String url) => url == secondaryUrl), - completion(secondaryUrl), - ); - }); - - testWidgets('evaluateJavascript', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - // ignore: deprecated_member_use - final String result = await controller.evaluateJavascript('1 + 1'); - expect(result, equals('2')); - }); - - testWidgets('loadUrl with headers', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final StreamController pageStarts = StreamController(); - final StreamController pageLoads = StreamController(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageStarted: (String url) { - pageStarts.add(url); - }, - onPageFinished: (String url) { - pageLoads.add(url); - }, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - final Map headers = { - 'test_header': 'flutter_test_header' - }; - await controller.loadUrl(headersUrl, headers: headers); - - await pageStarts.stream.firstWhere((String url) => url == headersUrl); - await pageLoads.stream.firstWhere((String url) => url == headersUrl); - - final String content = await controller - .runJavascriptReturningResult('document.documentElement.innerText'); - expect(content.contains('flutter_test_header'), isTrue); - }); - - testWidgets('JavascriptChannel', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final Completer pageStarted = Completer(); - final Completer pageLoaded = Completer(); - final Completer channelCompleter = Completer(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - // This is the data URL for: '' - initialUrl: - 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - javascriptChannels: { - JavascriptChannel( - name: 'Echo', - onMessageReceived: (JavascriptMessage message) { - channelCompleter.complete(message.message); - }, - ), - }, - onPageStarted: (String url) { - pageStarted.complete(null); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - await pageStarted.future; - await pageLoaded.future; - - expect(channelCompleter.isCompleted, isFalse); - await controller.runJavascript('Echo.postMessage("hello");'); - - await expectLater(channelCompleter.future, completion('hello')); - }); - - testWidgets('resize webview', (WidgetTester tester) async { - final Completer initialResizeCompleter = Completer(); - final Completer buttonTapResizeCompleter = Completer(); - final Completer onPageFinished = Completer(); - - bool resizeButtonTapped = false; - await tester.pumpWidget(ResizableWebView( - onResize: (_) { - if (resizeButtonTapped) { - buttonTapResizeCompleter.complete(); - } else { - initialResizeCompleter.complete(); - } - }, - onPageFinished: () => onPageFinished.complete(), - )); - await onPageFinished.future; - // Wait for a potential call to resize after page is loaded. - await initialResizeCompleter.future.timeout( - const Duration(seconds: 3), - onTimeout: () => null, - ); - - resizeButtonTapped = true; - await tester.tap(find.byKey(const ValueKey('resizeButton'))); - await tester.pumpAndSettle(); - expect(buttonTapResizeCompleter.future, completes); - }); - - testWidgets('set custom userAgent', (WidgetTester tester) async { - final Completer controllerCompleter1 = - Completer(); - final GlobalKey globalKey = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: globalKey, - initialUrl: 'about:blank', - javascriptMode: JavascriptMode.unrestricted, - userAgent: 'Custom_User_Agent1', - onWebViewCreated: (WebViewController controller) { - controllerCompleter1.complete(controller); - }, - ), - ), - ); - final WebViewController controller1 = await controllerCompleter1.future; - final String customUserAgent1 = await _getUserAgent(controller1); - expect(customUserAgent1, 'Custom_User_Agent1'); - // rebuild the WebView with a different user agent. - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: globalKey, - initialUrl: 'about:blank', - javascriptMode: JavascriptMode.unrestricted, - userAgent: 'Custom_User_Agent2', - ), - ), - ); - - final String customUserAgent2 = await _getUserAgent(controller1); - expect(customUserAgent2, 'Custom_User_Agent2'); - }); - - testWidgets('use default platform userAgent after webView is rebuilt', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final GlobalKey globalKey = GlobalKey(); - // Build the webView with no user agent to get the default platform user agent. - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: globalKey, - initialUrl: primaryUrl, - javascriptMode: JavascriptMode.unrestricted, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - final String defaultPlatformUserAgent = await _getUserAgent(controller); - // rebuild the WebView with a custom user agent. - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: globalKey, - initialUrl: 'about:blank', - javascriptMode: JavascriptMode.unrestricted, - userAgent: 'Custom_User_Agent', - ), - ), - ); - final String customUserAgent = await _getUserAgent(controller); - expect(customUserAgent, 'Custom_User_Agent'); - // rebuilds the WebView with no user agent. - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: globalKey, - initialUrl: 'about:blank', - javascriptMode: JavascriptMode.unrestricted, - ), - ), - ); - - final String customUserAgent2 = await _getUserAgent(controller); - expect(customUserAgent2, defaultPlatformUserAgent); - }); - - group('Video playback policy', () { - late String videoTestBase64; - setUpAll(() async { - final ByteData videoData = - await rootBundle.load('assets/sample_video.mp4'); - final String base64VideoData = - base64Encode(Uint8List.view(videoData.buffer)); - final String videoTest = ''' - - Video auto play - - - - - - - '''; - videoTestBase64 = base64Encode(const Utf8Encoder().convert(videoTest)); - }); - - testWidgets('Auto media playback', (WidgetTester tester) async { - Completer controllerCompleter = - Completer(); - Completer pageLoaded = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - ), - ), - ); - WebViewController controller = await controllerCompleter.future; - await pageLoaded.future; - - String isPaused = - await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(false)); - - controllerCompleter = Completer(); - pageLoaded = Completer(); - - // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - - controller = await controllerCompleter.future; - await pageLoaded.future; - - isPaused = await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(true)); - }); - - testWidgets('Changes to initialMediaPlaybackPolicy are ignored', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - Completer pageLoaded = Completer(); - - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: key, - initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - await pageLoaded.future; - - String isPaused = - await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(false)); - - pageLoaded = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: key, - initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - - await controller.reload(); - - await pageLoaded.future; - - isPaused = await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(false)); - }); - - testWidgets('Video plays inline when allowsInlineMediaPlayback is true', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final Completer pageLoaded = Completer(); - final Completer videoPlaying = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - javascriptChannels: { - JavascriptChannel( - name: 'VideoTestTime', - onMessageReceived: (JavascriptMessage message) { - final double currentTime = double.parse(message.message); - // Let it play for at least 1 second to make sure the related video's properties are set. - if (currentTime > 1 && !videoPlaying.isCompleted) { - videoPlaying.complete(null); - } - }, - ), - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - allowsInlineMediaPlayback: true, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - await pageLoaded.future; - - // Pump once to trigger the video play. - await tester.pump(); - - // Makes sure we get the correct event that indicates the video is actually playing. - await videoPlaying.future; - - final String fullScreen = - await controller.runJavascriptReturningResult('isFullScreen();'); - expect(fullScreen, _webviewBool(false)); - }); - - // allowsInlineMediaPlayback is a noop on Android, so it is skipped. - testWidgets( - 'Video plays full screen when allowsInlineMediaPlayback is false', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final Completer pageLoaded = Completer(); - final Completer videoPlaying = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - javascriptChannels: { - JavascriptChannel( - name: 'VideoTestTime', - onMessageReceived: (JavascriptMessage message) { - final double currentTime = double.parse(message.message); - // Let it play for at least 1 second to make sure the related video's properties are set. - if (currentTime > 1 && !videoPlaying.isCompleted) { - videoPlaying.complete(null); - } - }, - ), - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - await pageLoaded.future; - - // Pump once to trigger the video play. - await tester.pump(); - - // Makes sure we get the correct event that indicates the video is actually playing. - await videoPlaying.future; - - final String fullScreen = - await controller.runJavascriptReturningResult('isFullScreen();'); - expect(fullScreen, _webviewBool(true)); - }, skip: Platform.isAndroid); - }); - - group('Audio playback policy', () { - late String audioTestBase64; - setUpAll(() async { - final ByteData audioData = - await rootBundle.load('assets/sample_audio.ogg'); - final String base64AudioData = - base64Encode(Uint8List.view(audioData.buffer)); - final String audioTest = ''' - - Audio auto play - - - - - - - '''; - audioTestBase64 = base64Encode(const Utf8Encoder().convert(audioTest)); - }); - - testWidgets('Auto media playback', (WidgetTester tester) async { - Completer controllerCompleter = - Completer(); - Completer pageStarted = Completer(); - Completer pageLoaded = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageStarted: (String url) { - pageStarted.complete(null); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - ), - ), - ); - WebViewController controller = await controllerCompleter.future; - await pageStarted.future; - await pageLoaded.future; - - String isPaused = - await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(false)); - - controllerCompleter = Completer(); - pageStarted = Completer(); - pageLoaded = Completer(); - - // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageStarted: (String url) { - pageStarted.complete(null); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - - controller = await controllerCompleter.future; - await pageStarted.future; - await pageLoaded.future; - - isPaused = await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(true)); - }); - - testWidgets('Changes to initialMediaPlaybackPolicy are ignored', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - Completer pageStarted = Completer(); - Completer pageLoaded = Completer(); - - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: key, - initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageStarted: (String url) { - pageStarted.complete(null); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - await pageStarted.future; - await pageLoaded.future; - - String isPaused = - await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(false)); - - pageStarted = Completer(); - pageLoaded = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: key, - initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageStarted: (String url) { - pageStarted.complete(null); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - - await controller.reload(); - - await pageStarted.future; - await pageLoaded.future; - - isPaused = await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(false)); - }); - }); - - testWidgets('getTitle', (WidgetTester tester) async { - const String getTitleTest = ''' - - Some title - - - - - '''; - final String getTitleTestBase64 = - base64Encode(const Utf8Encoder().convert(getTitleTest)); - final Completer pageStarted = Completer(); - final Completer pageLoaded = Completer(); - final Completer controllerCompleter = - Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - initialUrl: 'data:text/html;charset=utf-8;base64,$getTitleTestBase64', - javascriptMode: JavascriptMode.unrestricted, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - onPageStarted: (String url) { - pageStarted.complete(null); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - - final WebViewController controller = await controllerCompleter.future; - await pageStarted.future; - await pageLoaded.future; - - // On at least iOS, it does not appear to be guaranteed that the native - // code has the title when the page load completes. Execute some JavaScript - // before checking the title to ensure that the page has been fully parsed - // and processed. - await controller.runJavascript('1;'); - - final String? title = await controller.getTitle(); - expect(title, 'Some title'); - }); - - group('Programmatic Scroll', () { - testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { - const String scrollTestPage = ''' - - - - - - -
- - - '''; - - final String scrollTestPageBase64 = - base64Encode(const Utf8Encoder().convert(scrollTestPage)); - - final Completer pageLoaded = Completer(); - final Completer controllerCompleter = - Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - initialUrl: - 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - - final WebViewController controller = await controllerCompleter.future; - await pageLoaded.future; - - await tester.pumpAndSettle(const Duration(seconds: 3)); - - int scrollPosX = await controller.getScrollX(); - int scrollPosY = await controller.getScrollY(); - - // Check scrollTo() - const int X_SCROLL = 123; - const int Y_SCROLL = 321; - // Get the initial position; this ensures that scrollTo is actually - // changing something, but also gives the native view's scroll position - // time to settle. - expect(scrollPosX, isNot(X_SCROLL)); - expect(scrollPosX, isNot(Y_SCROLL)); - - await controller.scrollTo(X_SCROLL, Y_SCROLL); - scrollPosX = await controller.getScrollX(); - scrollPosY = await controller.getScrollY(); - expect(scrollPosX, X_SCROLL); - expect(scrollPosY, Y_SCROLL); - - // Check scrollBy() (on top of scrollTo()) - await controller.scrollBy(X_SCROLL, Y_SCROLL); - scrollPosX = await controller.getScrollX(); - scrollPosY = await controller.getScrollY(); - expect(scrollPosX, X_SCROLL * 2); - expect(scrollPosY, Y_SCROLL * 2); - }); - }); - - // Minimal end-to-end testing of the legacy Android implementation. - group('AndroidWebView (virtual display)', () { - setUpAll(() { - WebView.platform = AndroidWebView(); - }); - - tearDownAll(() { - WebView.platform = null; - }); - - testWidgets('initialUrl', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final Completer pageFinishedCompleter = Completer(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - onPageFinished: pageFinishedCompleter.complete, - ), - ), - ); - - final WebViewController controller = await controllerCompleter.future; - await pageFinishedCompleter.future; - - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, primaryUrl); - }); - }, skip: !Platform.isAndroid); - - group('NavigationDelegate', () { - const String blankPage = ''; - final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' - '${base64Encode(const Utf8Encoder().convert(blankPage))}'; - - testWidgets('can allow requests', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final StreamController pageLoads = - StreamController.broadcast(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: blankPageEncoded, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - navigationDelegate: (NavigationRequest request) { - return (request.url.contains('youtube.com')) - ? NavigationDecision.prevent - : NavigationDecision.navigate; - }, - onPageFinished: (String url) => pageLoads.add(url), - ), - ), - ); - - await pageLoads.stream.first; // Wait for initial page load. - final WebViewController controller = await controllerCompleter.future; - await controller.runJavascript('location.href = "$secondaryUrl"'); - - await pageLoads.stream.first; // Wait for the next page load. - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, secondaryUrl); - }); - - testWidgets('onWebResourceError', (WidgetTester tester) async { - final Completer errorCompleter = - Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: 'https://www.notawebsite..com', - onWebResourceError: (WebResourceError error) { - errorCompleter.complete(error); - }, - ), - ), - ); - - final WebResourceError error = await errorCompleter.future; - expect(error, isNotNull); - - if (Platform.isIOS) { - expect(error.domain, isNotNull); - expect(error.failingUrl, isNull); - } else if (Platform.isAndroid) { - expect(error.errorType, isNotNull); - expect(error.failingUrl?.startsWith('https://www.notawebsite..com'), - isTrue); - } - }); - - testWidgets('onWebResourceError is not called with valid url', - (WidgetTester tester) async { - final Completer errorCompleter = - Completer(); - final Completer pageFinishCompleter = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: - 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+', - onWebResourceError: (WebResourceError error) { - errorCompleter.complete(error); - }, - onPageFinished: (_) => pageFinishCompleter.complete(), - ), - ), - ); - - expect(errorCompleter.future, doesNotComplete); - await pageFinishCompleter.future; - }); - - testWidgets( - 'onWebResourceError only called for main frame', - (WidgetTester tester) async { - const String iframeTest = ''' - - - - WebResourceError test - - - - - - '''; - final String iframeTestBase64 = - base64Encode(const Utf8Encoder().convert(iframeTest)); - - final Completer errorCompleter = - Completer(); - final Completer pageFinishCompleter = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: - 'data:text/html;charset=utf-8;base64,$iframeTestBase64', - onWebResourceError: (WebResourceError error) { - errorCompleter.complete(error); - }, - onPageFinished: (_) => pageFinishCompleter.complete(), - ), - ), - ); - - expect(errorCompleter.future, doesNotComplete); - await pageFinishCompleter.future; - }, - ); - - testWidgets('can block requests', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final StreamController pageLoads = - StreamController.broadcast(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: blankPageEncoded, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - navigationDelegate: (NavigationRequest request) { - return (request.url.contains('youtube.com')) - ? NavigationDecision.prevent - : NavigationDecision.navigate; - }, - onPageFinished: (String url) => pageLoads.add(url), - ), - ), - ); - - await pageLoads.stream.first; // Wait for initial page load. - final WebViewController controller = await controllerCompleter.future; - await controller - .runJavascript('location.href = "https://www.youtube.com/"'); - - // There should never be any second page load, since our new URL is - // blocked. Still wait for a potential page change for some time in order - // to give the test a chance to fail. - await pageLoads.stream.first - .timeout(const Duration(milliseconds: 500), onTimeout: () => ''); - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, isNot(contains('youtube.com'))); - }); - - testWidgets('supports asynchronous decisions', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final StreamController pageLoads = - StreamController.broadcast(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: blankPageEncoded, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - navigationDelegate: (NavigationRequest request) async { - NavigationDecision decision = NavigationDecision.prevent; - decision = await Future.delayed( - const Duration(milliseconds: 10), - () => NavigationDecision.navigate); - return decision; - }, - onPageFinished: (String url) => pageLoads.add(url), - ), - ), - ); - - await pageLoads.stream.first; // Wait for initial page load. - final WebViewController controller = await controllerCompleter.future; - await controller.runJavascript('location.href = "$secondaryUrl"'); - - await pageLoads.stream.first; // Wait for second page to load. - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, secondaryUrl); - }); - }); - - testWidgets('launches with gestureNavigationEnabled on iOS', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: SizedBox( - width: 400, - height: 300, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - gestureNavigationEnabled: true, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - ), - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, primaryUrl); - }); - - testWidgets('target _blank opens in same window', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final Completer pageLoaded = Completer(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - await controller.runJavascript('window.open("$primaryUrl", "_blank")'); - await pageLoaded.future; - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, primaryUrl); - }); - - testWidgets( - 'can open new window and go back', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - Completer pageLoaded = Completer(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (String url) { - pageLoaded.complete(); - }, - initialUrl: primaryUrl, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - expect(controller.currentUrl(), completion(primaryUrl)); - await pageLoaded.future; - pageLoaded = Completer(); - - await controller.runJavascript('window.open("$secondaryUrl")'); - await pageLoaded.future; - pageLoaded = Completer(); - expect(controller.currentUrl(), completion(secondaryUrl)); - - expect(controller.canGoBack(), completion(true)); - await controller.goBack(); - await pageLoaded.future; - await expectLater(controller.currentUrl(), completion(primaryUrl)); - }, - ); - - testWidgets( - 'clearCache should clear local storage', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - - Completer pageLoadCompleter = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (_) => pageLoadCompleter.complete(), - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - ), - ), - ); - - await pageLoadCompleter.future; - pageLoadCompleter = Completer(); - - final WebViewController controller = await controllerCompleter.future; - await controller.runJavascript('localStorage.setItem("myCat", "Tom");'); - final String myCatItem = await controller.runJavascriptReturningResult( - 'localStorage.getItem("myCat");', - ); - expect(myCatItem, _webviewString('Tom')); - - await controller.clearCache(); - await pageLoadCompleter.future; - - late final String? nullItem; - try { - nullItem = await controller.runJavascriptReturningResult( - 'localStorage.getItem("myCat");', - ); - } catch (exception) { - if (defaultTargetPlatform == TargetPlatform.iOS && - exception is ArgumentError && - (exception.message as String).contains( - 'Result of JavaScript execution returned a `null` value.')) { - nullItem = ''; - } - } - expect(nullItem, _webviewNull()); - }, - ); -} - -// JavaScript booleans evaluate to different string values on Android and iOS. -// This utility method returns the string boolean value of the current platform. -String _webviewBool(bool value) { - if (defaultTargetPlatform == TargetPlatform.iOS) { - return value ? '1' : '0'; - } - return value ? 'true' : 'false'; -} - -// JavaScript `null` evaluate to different string values on Android and iOS. -// This utility method returns the string boolean value of the current platform. -String _webviewNull() { - if (defaultTargetPlatform == TargetPlatform.iOS) { - return ''; - } - return 'null'; -} - -// JavaScript String evaluate to different string values on Android and iOS. -// This utility method returns the string boolean value of the current platform. -String _webviewString(String value) { - if (defaultTargetPlatform == TargetPlatform.iOS) { - return value; - } - return '"$value"'; -} - -/// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests. -Future _getUserAgent(WebViewController controller) async { - return _runJavascriptReturningResult(controller, 'navigator.userAgent;'); -} - -Future _runJavascriptReturningResult( - WebViewController controller, String js) async { - if (defaultTargetPlatform == TargetPlatform.iOS) { - return controller.runJavascriptReturningResult(js); - } - return jsonDecode(await controller.runJavascriptReturningResult(js)) - as String; -} - -class ResizableWebView extends StatefulWidget { - const ResizableWebView({ - super.key, - required this.onResize, - required this.onPageFinished, - }); - - final JavascriptMessageHandler onResize; - final VoidCallback onPageFinished; - - @override - State createState() => ResizableWebViewState(); -} - -class ResizableWebViewState extends State { - double webViewWidth = 200; - double webViewHeight = 200; - - static const String resizePage = ''' - - Resize test - - - - - - '''; - - @override - Widget build(BuildContext context) { - final String resizeTestBase64 = - base64Encode(const Utf8Encoder().convert(resizePage)); - return Directionality( - textDirection: TextDirection.ltr, - child: Column( - children: [ - SizedBox( - width: webViewWidth, - height: webViewHeight, - child: WebView( - initialUrl: - 'data:text/html;charset=utf-8;base64,$resizeTestBase64', - javascriptChannels: { - JavascriptChannel( - name: 'Resize', - onMessageReceived: widget.onResize, - ), - }, - onPageFinished: (_) => widget.onPageFinished(), - javascriptMode: JavascriptMode.unrestricted, - ), - ), - TextButton( - key: const Key('resizeButton'), - onPressed: () { - setState(() { - webViewWidth += 100.0; - webViewHeight += 100.0; - }); - }, - child: const Text('ResizeButton'), - ), - ], - ), - ); - } -} +// // Copyright 2013 The Flutter Authors. All rights reserved. +// // Use of this source code is governed by a BSD-style license that can be +// // found in the LICENSE file. + +// // This test is run using `flutter drive` by the CI (see /script/tool/README.md +// // in this repository for details on driving that tooling manually), but can +// // also be run using `flutter test` directly during development. + +// import 'dart:async'; +// import 'dart:convert'; +// import 'dart:io'; + +// import 'package:flutter/foundation.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter/services.dart'; +// import 'package:flutter_test/flutter_test.dart'; +// import 'package:integration_test/integration_test.dart'; +// import 'package:webview_flutter/src/webview_flutter_legacy.dart'; + +// Future main() async { +// IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + +// final HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 0); +// unawaited(server.forEach((HttpRequest request) { +// if (request.uri.path == '/hello.txt') { +// request.response.writeln('Hello, world.'); +// } else if (request.uri.path == '/secondary.txt') { +// request.response.writeln('How are you today?'); +// } else if (request.uri.path == '/headers') { +// request.response.writeln('${request.headers}'); +// } else if (request.uri.path == '/favicon.ico') { +// request.response.statusCode = HttpStatus.notFound; +// } else { +// fail('unexpected request: ${request.method} ${request.uri}'); +// } +// request.response.close(); +// })); +// final String prefixUrl = 'http://${server.address.address}:${server.port}'; +// final String primaryUrl = '$prefixUrl/hello.txt'; +// final String secondaryUrl = '$prefixUrl/secondary.txt'; +// final String headersUrl = '$prefixUrl/headers'; + +// testWidgets('initialUrl', (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// final Completer pageFinishedCompleter = Completer(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: primaryUrl, +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// onPageFinished: pageFinishedCompleter.complete, +// ), +// ), +// ); + +// final WebViewController controller = await controllerCompleter.future; +// await pageFinishedCompleter.future; + +// final String? currentUrl = await controller.currentUrl(); +// expect(currentUrl, primaryUrl); +// }); + +// testWidgets('loadUrl', (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// final StreamController pageLoads = StreamController(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: primaryUrl, +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// onPageFinished: (String url) { +// pageLoads.add(url); +// }, +// ), +// ), +// ); +// final WebViewController controller = await controllerCompleter.future; + +// await controller.loadUrl(secondaryUrl); +// await expectLater( +// pageLoads.stream.firstWhere((String url) => url == secondaryUrl), +// completion(secondaryUrl), +// ); +// }); + +// testWidgets('evaluateJavascript', (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: primaryUrl, +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// ), +// ), +// ); +// final WebViewController controller = await controllerCompleter.future; +// // ignore: deprecated_member_use +// final String result = await controller.evaluateJavascript('1 + 1'); +// expect(result, equals('2')); +// }); + +// testWidgets('loadUrl with headers', (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// final StreamController pageStarts = StreamController(); +// final StreamController pageLoads = StreamController(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: primaryUrl, +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// onPageStarted: (String url) { +// pageStarts.add(url); +// }, +// onPageFinished: (String url) { +// pageLoads.add(url); +// }, +// ), +// ), +// ); +// final WebViewController controller = await controllerCompleter.future; +// final Map headers = { +// 'test_header': 'flutter_test_header' +// }; +// await controller.loadUrl(headersUrl, headers: headers); + +// await pageStarts.stream.firstWhere((String url) => url == headersUrl); +// await pageLoads.stream.firstWhere((String url) => url == headersUrl); + +// final String content = await controller +// .runJavascriptReturningResult('document.documentElement.innerText'); +// expect(content.contains('flutter_test_header'), isTrue); +// }); + +// testWidgets('JavascriptChannel', (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// final Completer pageStarted = Completer(); +// final Completer pageLoaded = Completer(); +// final Completer channelCompleter = Completer(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// // This is the data URL for: '' +// initialUrl: +// 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// javascriptChannels: { +// JavascriptChannel( +// name: 'Echo', +// onMessageReceived: (JavascriptMessage message) { +// channelCompleter.complete(message.message); +// }, +// ), +// }, +// onPageStarted: (String url) { +// pageStarted.complete(null); +// }, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// ), +// ), +// ); +// final WebViewController controller = await controllerCompleter.future; +// await pageStarted.future; +// await pageLoaded.future; + +// expect(channelCompleter.isCompleted, isFalse); +// await controller.runJavascript('Echo.postMessage("hello");'); + +// await expectLater(channelCompleter.future, completion('hello')); +// }); + +// testWidgets('resize webview', (WidgetTester tester) async { +// final Completer initialResizeCompleter = Completer(); +// final Completer buttonTapResizeCompleter = Completer(); +// final Completer onPageFinished = Completer(); + +// bool resizeButtonTapped = false; +// await tester.pumpWidget(ResizableWebView( +// onResize: (_) { +// if (resizeButtonTapped) { +// buttonTapResizeCompleter.complete(); +// } else { +// initialResizeCompleter.complete(); +// } +// }, +// onPageFinished: () => onPageFinished.complete(), +// )); +// await onPageFinished.future; +// // Wait for a potential call to resize after page is loaded. +// await initialResizeCompleter.future.timeout( +// const Duration(seconds: 3), +// onTimeout: () => null, +// ); + +// resizeButtonTapped = true; +// await tester.tap(find.byKey(const ValueKey('resizeButton'))); +// await tester.pumpAndSettle(); +// expect(buttonTapResizeCompleter.future, completes); +// }); + +// testWidgets('set custom userAgent', (WidgetTester tester) async { +// final Completer controllerCompleter1 = +// Completer(); +// final GlobalKey globalKey = GlobalKey(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: globalKey, +// initialUrl: 'about:blank', +// javascriptMode: JavascriptMode.unrestricted, +// userAgent: 'Custom_User_Agent1', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter1.complete(controller); +// }, +// ), +// ), +// ); +// final WebViewController controller1 = await controllerCompleter1.future; +// final String customUserAgent1 = await _getUserAgent(controller1); +// expect(customUserAgent1, 'Custom_User_Agent1'); +// // rebuild the WebView with a different user agent. +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: globalKey, +// initialUrl: 'about:blank', +// javascriptMode: JavascriptMode.unrestricted, +// userAgent: 'Custom_User_Agent2', +// ), +// ), +// ); + +// final String customUserAgent2 = await _getUserAgent(controller1); +// expect(customUserAgent2, 'Custom_User_Agent2'); +// }); + +// testWidgets('use default platform userAgent after webView is rebuilt', +// (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// final GlobalKey globalKey = GlobalKey(); +// // Build the webView with no user agent to get the default platform user agent. +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: globalKey, +// initialUrl: primaryUrl, +// javascriptMode: JavascriptMode.unrestricted, +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// ), +// ), +// ); +// final WebViewController controller = await controllerCompleter.future; +// final String defaultPlatformUserAgent = await _getUserAgent(controller); +// // rebuild the WebView with a custom user agent. +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: globalKey, +// initialUrl: 'about:blank', +// javascriptMode: JavascriptMode.unrestricted, +// userAgent: 'Custom_User_Agent', +// ), +// ), +// ); +// final String customUserAgent = await _getUserAgent(controller); +// expect(customUserAgent, 'Custom_User_Agent'); +// // rebuilds the WebView with no user agent. +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: globalKey, +// initialUrl: 'about:blank', +// javascriptMode: JavascriptMode.unrestricted, +// ), +// ), +// ); + +// final String customUserAgent2 = await _getUserAgent(controller); +// expect(customUserAgent2, defaultPlatformUserAgent); +// }); + +// group('Video playback policy', () { +// late String videoTestBase64; +// setUpAll(() async { +// return rootBundle +// .load('assets/sample_video.mp4') +// .then((ByteData data) async { +// final ByteData videoData = data; +// final String base64VideoData = +// base64Encode(Uint8List.view(videoData.buffer)); +// final String videoTest = ''' +// +// Video auto play +// +// +// +// +// +// +// '''; +// videoTestBase64 = base64Encode(const Utf8Encoder().convert(videoTest)); +// }); +// }); + +// testWidgets('Auto media playback', (WidgetTester tester) async { +// Completer controllerCompleter = +// Completer(); +// Completer pageLoaded = Completer(); + +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, +// ), +// ), +// ); +// WebViewController controller = await controllerCompleter.future; +// await pageLoaded.future; + +// String isPaused = +// await controller.runJavascriptReturningResult('isPaused();'); +// expect(isPaused, _webviewBool(false)); + +// controllerCompleter = Completer(); +// pageLoaded = Completer(); + +// // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// ), +// ), +// ); + +// controller = await controllerCompleter.future; +// await pageLoaded.future; + +// isPaused = await controller.runJavascriptReturningResult('isPaused();'); +// expect(isPaused, _webviewBool(true)); +// }); + +// testWidgets('Changes to initialMediaPlaybackPolicy are ignored', +// (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// Completer pageLoaded = Completer(); + +// final GlobalKey key = GlobalKey(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: key, +// initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, +// ), +// ), +// ); +// final WebViewController controller = await controllerCompleter.future; +// await pageLoaded.future; + +// String isPaused = +// await controller.runJavascriptReturningResult('isPaused();'); +// expect(isPaused, _webviewBool(false)); + +// pageLoaded = Completer(); + +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: key, +// initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// ), +// ), +// ); + +// await controller.reload(); + +// await pageLoaded.future; + +// isPaused = await controller.runJavascriptReturningResult('isPaused();'); +// expect(isPaused, _webviewBool(false)); +// }); + +// testWidgets('Video plays inline when allowsInlineMediaPlayback is true', +// (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// final Completer pageLoaded = Completer(); +// final Completer videoPlaying = Completer(); + +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// javascriptChannels: { +// JavascriptChannel( +// name: 'VideoTestTime', +// onMessageReceived: (JavascriptMessage message) { +// final double currentTime = double.parse(message.message); +// // Let it play for at least 1 second to make sure the related video's properties are set. +// if (currentTime > 1 && !videoPlaying.isCompleted) { +// videoPlaying.complete(null); +// } +// }, +// ), +// }, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, +// allowsInlineMediaPlayback: true, +// ), +// ), +// ); +// final WebViewController controller = await controllerCompleter.future; +// await pageLoaded.future; + +// // Pump once to trigger the video play. +// await tester.pump(); + +// // Makes sure we get the correct event that indicates the video is actually playing. +// await videoPlaying.future; + +// final String fullScreen = +// await controller.runJavascriptReturningResult('isFullScreen();'); +// expect(fullScreen, _webviewBool(false)); +// }); + +// // allowsInlineMediaPlayback is a noop on Android, so it is skipped. +// testWidgets( +// 'Video plays full screen when allowsInlineMediaPlayback is false', +// (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// final Completer pageLoaded = Completer(); +// final Completer videoPlaying = Completer(); + +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// javascriptChannels: { +// JavascriptChannel( +// name: 'VideoTestTime', +// onMessageReceived: (JavascriptMessage message) { +// final double currentTime = double.parse(message.message); +// // Let it play for at least 1 second to make sure the related video's properties are set. +// if (currentTime > 1 && !videoPlaying.isCompleted) { +// videoPlaying.complete(null); +// } +// }, +// ), +// }, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, +// ), +// ), +// ); +// final WebViewController controller = await controllerCompleter.future; +// await pageLoaded.future; + +// // Pump once to trigger the video play. +// await tester.pump(); + +// // Makes sure we get the correct event that indicates the video is actually playing. +// await videoPlaying.future; + +// final String fullScreen = +// await controller.runJavascriptReturningResult('isFullScreen();'); +// expect(fullScreen, _webviewBool(true)); +// }, skip: Platform.isAndroid); +// }); + +// group('Audio playback policy', () { +// late String audioTestBase64; +// setUpAll(() async { +// return rootBundle +// .load('assets/sample_audio.ogg') +// .then((ByteData data) async { +// final ByteData audioData = data; +// final String base64AudioData = +// base64Encode(Uint8List.view(audioData.buffer)); +// final String audioTest = ''' +// +// Audio auto play +// +// +// +// +// +// +// '''; +// audioTestBase64 = base64Encode(const Utf8Encoder().convert(audioTest)); +// }); +// }); + +// testWidgets('Auto media playback', (WidgetTester tester) async { +// Completer controllerCompleter = +// Completer(); +// Completer pageStarted = Completer(); +// Completer pageLoaded = Completer(); + +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// onPageStarted: (String url) { +// pageStarted.complete(null); +// }, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, +// ), +// ), +// ); +// WebViewController controller = await controllerCompleter.future; +// await pageStarted.future; +// await pageLoaded.future; + +// String isPaused = +// await controller.runJavascriptReturningResult('isPaused();'); +// expect(isPaused, _webviewBool(false)); + +// controllerCompleter = Completer(); +// pageStarted = Completer(); +// pageLoaded = Completer(); + +// // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// onPageStarted: (String url) { +// pageStarted.complete(null); +// }, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// ), +// ), +// ); + +// controller = await controllerCompleter.future; +// await pageStarted.future; +// await pageLoaded.future; + +// isPaused = await controller.runJavascriptReturningResult('isPaused();'); +// expect(isPaused, _webviewBool(true)); +// }); + +// testWidgets('Changes to initialMediaPlaybackPolicy are ignored', +// (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// Completer pageStarted = Completer(); +// Completer pageLoaded = Completer(); + +// final GlobalKey key = GlobalKey(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: key, +// initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// onPageStarted: (String url) { +// pageStarted.complete(null); +// }, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, +// ), +// ), +// ); +// final WebViewController controller = await controllerCompleter.future; +// await pageStarted.future; +// await pageLoaded.future; + +// String isPaused = +// await controller.runJavascriptReturningResult('isPaused();'); +// expect(isPaused, _webviewBool(false)); + +// pageStarted = Completer(); +// pageLoaded = Completer(); + +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: key, +// initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// onPageStarted: (String url) { +// pageStarted.complete(null); +// }, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// ), +// ), +// ); + +// await controller.reload(); + +// await pageStarted.future; +// await pageLoaded.future; + +// isPaused = await controller.runJavascriptReturningResult('isPaused();'); +// expect(isPaused, _webviewBool(false)); +// }); +// }); + +// testWidgets('getTitle', (WidgetTester tester) async { +// const String getTitleTest = ''' +// +// Some title +// +// +// +// +// '''; +// final String getTitleTestBase64 = +// base64Encode(const Utf8Encoder().convert(getTitleTest)); +// final Completer pageStarted = Completer(); +// final Completer pageLoaded = Completer(); +// final Completer controllerCompleter = +// Completer(); + +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// initialUrl: 'data:text/html;charset=utf-8;base64,$getTitleTestBase64', +// javascriptMode: JavascriptMode.unrestricted, +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// onPageStarted: (String url) { +// pageStarted.complete(null); +// }, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// ), +// ), +// ); + +// final WebViewController controller = await controllerCompleter.future; +// await pageStarted.future; +// await pageLoaded.future; + +// // On at least iOS, it does not appear to be guaranteed that the native +// // code has the title when the page load completes. Execute some JavaScript +// // before checking the title to ensure that the page has been fully parsed +// // and processed. +// await controller.runJavascript('1;'); + +// final String? title = await controller.getTitle(); +// expect(title, 'Some title'); +// }); + +// group('Programmatic Scroll', () { +// testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { +// const String scrollTestPage = ''' +// +// +// +// +// +// +//
+// +// +// '''; + +// final String scrollTestPageBase64 = +// base64Encode(const Utf8Encoder().convert(scrollTestPage)); + +// final Completer pageLoaded = Completer(); +// final Completer controllerCompleter = +// Completer(); + +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// initialUrl: +// 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64', +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// ), +// ), +// ); + +// final WebViewController controller = await controllerCompleter.future; +// await pageLoaded.future; + +// await tester.pumpAndSettle(const Duration(seconds: 3)); + +// int scrollPosX = await controller.getScrollX(); +// int scrollPosY = await controller.getScrollY(); + +// // Check scrollTo() +// const int X_SCROLL = 123; +// const int Y_SCROLL = 321; +// // Get the initial position; this ensures that scrollTo is actually +// // changing something, but also gives the native view's scroll position +// // time to settle. +// expect(scrollPosX, isNot(X_SCROLL)); +// expect(scrollPosX, isNot(Y_SCROLL)); + +// await controller.scrollTo(X_SCROLL, Y_SCROLL); +// scrollPosX = await controller.getScrollX(); +// scrollPosY = await controller.getScrollY(); +// expect(scrollPosX, X_SCROLL); +// expect(scrollPosY, Y_SCROLL); + +// // Check scrollBy() (on top of scrollTo()) +// await controller.scrollBy(X_SCROLL, Y_SCROLL); +// scrollPosX = await controller.getScrollX(); +// scrollPosY = await controller.getScrollY(); +// expect(scrollPosX, X_SCROLL * 2); +// expect(scrollPosY, Y_SCROLL * 2); +// }); +// }); + +// // Minimal end-to-end testing of the legacy Android implementation. +// group('AndroidWebView (virtual display)', () { +// setUpAll(() { +// WebView.platform = AndroidWebView(); +// }); + +// tearDownAll(() { +// WebView.platform = null; +// }); + +// testWidgets('initialUrl', (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// final Completer pageFinishedCompleter = Completer(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: primaryUrl, +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// onPageFinished: pageFinishedCompleter.complete, +// ), +// ), +// ); + +// final WebViewController controller = await controllerCompleter.future; +// await pageFinishedCompleter.future; + +// final String? currentUrl = await controller.currentUrl(); +// expect(currentUrl, primaryUrl); +// }); +// }, skip: !Platform.isAndroid); + +// group('NavigationDelegate', () { +// const String blankPage = ''; +// final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' +// '${base64Encode(const Utf8Encoder().convert(blankPage))}'; + +// testWidgets('can allow requests', (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// final StreamController pageLoads = +// StreamController.broadcast(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: blankPageEncoded, +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// navigationDelegate: (NavigationRequest request) { +// return (request.url.contains('youtube.com')) +// ? NavigationDecision.prevent +// : NavigationDecision.navigate; +// }, +// onPageFinished: (String url) => pageLoads.add(url), +// ), +// ), +// ); + +// await pageLoads.stream.first; // Wait for initial page load. +// final WebViewController controller = await controllerCompleter.future; +// await controller.runJavascript('location.href = "$secondaryUrl"'); + +// await pageLoads.stream.first; // Wait for the next page load. +// final String? currentUrl = await controller.currentUrl(); +// expect(currentUrl, secondaryUrl); +// }); + +// testWidgets('onWebResourceError', (WidgetTester tester) async { +// final Completer errorCompleter = +// Completer(); + +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: 'https://www.notawebsite..com', +// onWebResourceError: (WebResourceError error) { +// errorCompleter.complete(error); +// }, +// ), +// ), +// ); + +// final WebResourceError error = await errorCompleter.future; +// expect(error, isNotNull); + +// if (Platform.isIOS) { +// expect(error.domain, isNotNull); +// expect(error.failingUrl, isNull); +// } else if (Platform.isAndroid) { +// expect(error.errorType, isNotNull); +// expect(error.failingUrl?.startsWith('https://www.notawebsite..com'), +// isTrue); +// } +// }); + +// testWidgets('onWebResourceError is not called with valid url', +// (WidgetTester tester) async { +// final Completer errorCompleter = +// Completer(); +// final Completer pageFinishCompleter = Completer(); + +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: +// 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+', +// onWebResourceError: (WebResourceError error) { +// errorCompleter.complete(error); +// }, +// onPageFinished: (_) => pageFinishCompleter.complete(), +// ), +// ), +// ); + +// expect(errorCompleter.future, doesNotComplete); +// await pageFinishCompleter.future; +// }); + +// testWidgets( +// 'onWebResourceError only called for main frame', +// (WidgetTester tester) async { +// const String iframeTest = ''' +// +// +// +// WebResourceError test +// +// +// +// +// +// '''; +// final String iframeTestBase64 = +// base64Encode(const Utf8Encoder().convert(iframeTest)); + +// final Completer errorCompleter = +// Completer(); +// final Completer pageFinishCompleter = Completer(); + +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: +// 'data:text/html;charset=utf-8;base64,$iframeTestBase64', +// onWebResourceError: (WebResourceError error) { +// errorCompleter.complete(error); +// }, +// onPageFinished: (_) => pageFinishCompleter.complete(), +// ), +// ), +// ); + +// expect(errorCompleter.future, doesNotComplete); +// await pageFinishCompleter.future; +// }, +// ); + +// testWidgets('can block requests', (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// final StreamController pageLoads = +// StreamController.broadcast(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: blankPageEncoded, +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// navigationDelegate: (NavigationRequest request) { +// return (request.url.contains('youtube.com')) +// ? NavigationDecision.prevent +// : NavigationDecision.navigate; +// }, +// onPageFinished: (String url) => pageLoads.add(url), +// ), +// ), +// ); + +// await pageLoads.stream.first; // Wait for initial page load. +// final WebViewController controller = await controllerCompleter.future; +// await controller +// .runJavascript('location.href = "https://www.youtube.com/"'); + +// // There should never be any second page load, since our new URL is +// // blocked. Still wait for a potential page change for some time in order +// // to give the test a chance to fail. +// await pageLoads.stream.first +// .timeout(const Duration(milliseconds: 500), onTimeout: () => ''); +// final String? currentUrl = await controller.currentUrl(); +// expect(currentUrl, isNot(contains('youtube.com'))); +// }); + +// testWidgets('supports asynchronous decisions', (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// final StreamController pageLoads = +// StreamController.broadcast(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: blankPageEncoded, +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// navigationDelegate: (NavigationRequest request) async { +// NavigationDecision decision = NavigationDecision.prevent; +// decision = await Future.delayed( +// const Duration(milliseconds: 10), +// () => NavigationDecision.navigate); +// return decision; +// }, +// onPageFinished: (String url) => pageLoads.add(url), +// ), +// ), +// ); + +// await pageLoads.stream.first; // Wait for initial page load. +// final WebViewController controller = await controllerCompleter.future; +// await controller.runJavascript('location.href = "$secondaryUrl"'); + +// await pageLoads.stream.first; // Wait for second page to load. +// final String? currentUrl = await controller.currentUrl(); +// expect(currentUrl, secondaryUrl); +// }); +// }); + +// testWidgets('launches with gestureNavigationEnabled on iOS', +// (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: SizedBox( +// width: 400, +// height: 300, +// child: WebView( +// key: GlobalKey(), +// initialUrl: primaryUrl, +// gestureNavigationEnabled: true, +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// ), +// ), +// ), +// ); +// final WebViewController controller = await controllerCompleter.future; +// final String? currentUrl = await controller.currentUrl(); +// expect(currentUrl, primaryUrl); +// }); + +// testWidgets('target _blank opens in same window', +// (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// final Completer pageLoaded = Completer(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// onPageFinished: (String url) { +// pageLoaded.complete(null); +// }, +// ), +// ), +// ); +// final WebViewController controller = await controllerCompleter.future; +// await controller.runJavascript('window.open("$primaryUrl", "_blank")'); +// await pageLoaded.future; +// final String? currentUrl = await controller.currentUrl(); +// expect(currentUrl, primaryUrl); +// }); + +// testWidgets( +// 'can open new window and go back', +// (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); +// Completer pageLoaded = Completer(); +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// javascriptMode: JavascriptMode.unrestricted, +// onPageFinished: (String url) { +// pageLoaded.complete(); +// }, +// initialUrl: primaryUrl, +// ), +// ), +// ); +// final WebViewController controller = await controllerCompleter.future; +// expect(controller.currentUrl(), completion(primaryUrl)); +// await pageLoaded.future; +// pageLoaded = Completer(); + +// await controller.runJavascript('window.open("$secondaryUrl")'); +// await pageLoaded.future; +// pageLoaded = Completer(); +// expect(controller.currentUrl(), completion(secondaryUrl)); + +// expect(controller.canGoBack(), completion(true)); +// await controller.goBack(); +// await pageLoaded.future; +// await expectLater(controller.currentUrl(), completion(primaryUrl)); +// }, +// ); + +// testWidgets( +// 'clearCache should clear local storage', +// (WidgetTester tester) async { +// final Completer controllerCompleter = +// Completer(); + +// Completer pageLoadCompleter = Completer(); + +// await tester.pumpWidget( +// Directionality( +// textDirection: TextDirection.ltr, +// child: WebView( +// key: GlobalKey(), +// initialUrl: primaryUrl, +// javascriptMode: JavascriptMode.unrestricted, +// onPageFinished: (_) => pageLoadCompleter.complete(), +// onWebViewCreated: (WebViewController controller) { +// controllerCompleter.complete(controller); +// }, +// ), +// ), +// ); + +// await pageLoadCompleter.future; +// pageLoadCompleter = Completer(); + +// final WebViewController controller = await controllerCompleter.future; +// await controller.runJavascript('localStorage.setItem("myCat", "Tom");'); +// final String myCatItem = await controller.runJavascriptReturningResult( +// 'localStorage.getItem("myCat");', +// ); +// expect(myCatItem, _webviewString('Tom')); + +// await controller.clearCache(); +// await pageLoadCompleter.future; + +// late final String? nullItem; +// try { +// nullItem = await controller.runJavascriptReturningResult( +// 'localStorage.getItem("myCat");', +// ); +// } catch (exception) { +// if (defaultTargetPlatform == TargetPlatform.iOS && +// exception is ArgumentError && +// (exception.message as String).contains( +// 'Result of JavaScript execution returned a `null` value.')) { +// nullItem = ''; +// } +// } +// expect(nullItem, _webviewNull()); +// }, +// ); +// } + +// // JavaScript booleans evaluate to different string values on Android and iOS. +// // This utility method returns the string boolean value of the current platform. +// String _webviewBool(bool value) { +// if (defaultTargetPlatform == TargetPlatform.iOS) { +// return value ? '1' : '0'; +// } +// return value ? 'true' : 'false'; +// } + +// // JavaScript `null` evaluate to different string values on Android and iOS. +// // This utility method returns the string boolean value of the current platform. +// String _webviewNull() { +// if (defaultTargetPlatform == TargetPlatform.iOS) { +// return ''; +// } +// return 'null'; +// } + +// // JavaScript String evaluate to different string values on Android and iOS. +// // This utility method returns the string boolean value of the current platform. +// String _webviewString(String value) { +// if (defaultTargetPlatform == TargetPlatform.iOS) { +// return value; +// } +// return '"$value"'; +// } + +// /// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests. +// Future _getUserAgent(WebViewController controller) async { +// return _runJavascriptReturningResult(controller, 'navigator.userAgent;'); +// } + +// Future _runJavascriptReturningResult( +// WebViewController controller, String js) async { +// if (defaultTargetPlatform == TargetPlatform.iOS) { +// return controller.runJavascriptReturningResult(js); +// } +// return jsonDecode(await controller.runJavascriptReturningResult(js)) +// as String; +// } + +// class ResizableWebView extends StatefulWidget { +// const ResizableWebView({ +// super.key, +// required this.onResize, +// required this.onPageFinished, +// }); + +// final JavascriptMessageHandler onResize; +// final VoidCallback onPageFinished; + +// @override +// State createState() => ResizableWebViewState(); +// } + +// class ResizableWebViewState extends State { +// double webViewWidth = 200; +// double webViewHeight = 200; + +// static const String resizePage = ''' +// +// Resize test +// +// +// +// +// +// '''; + +// @override +// Widget build(BuildContext context) { +// final String resizeTestBase64 = +// base64Encode(const Utf8Encoder().convert(resizePage)); +// return Directionality( +// textDirection: TextDirection.ltr, +// child: Column( +// children: [ +// SizedBox( +// width: webViewWidth, +// height: webViewHeight, +// child: WebView( +// initialUrl: +// 'data:text/html;charset=utf-8;base64,$resizeTestBase64', +// javascriptChannels: { +// JavascriptChannel( +// name: 'Resize', +// onMessageReceived: widget.onResize, +// ), +// }, +// onPageFinished: (_) => widget.onPageFinished(), +// javascriptMode: JavascriptMode.unrestricted, +// ), +// ), +// TextButton( +// key: const Key('resizeButton'), +// onPressed: () { +// setState(() { +// webViewWidth += 100.0; +// webViewHeight += 100.0; +// }); +// }, +// child: const Text('ResizeButton'), +// ), +// ], +// ), +// ); +// } +// } diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/legacy/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/legacy/webview_flutter_test.dart index 2e7f3d0b8dd7..6e2c832a7ed2 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/legacy/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/legacy/webview_flutter_test.dart @@ -48,378 +48,6 @@ Future main() async { final String secondaryUrl = '$prefixUrl/secondary.txt'; final String headersUrl = '$prefixUrl/headers'; - testWidgets('initialUrl', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final Completer pageFinishedCompleter = Completer(); - await tester.pumpWidget( - MaterialApp( - home: Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - onPageFinished: pageFinishedCompleter.complete, - ), - ), - ), - ); - - final WebViewController controller = await controllerCompleter.future; - await pageFinishedCompleter.future; - - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, primaryUrl); - }); - - testWidgets('loadUrl', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final StreamController pageLoads = StreamController(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - onPageFinished: (String url) { - pageLoads.add(url); - }, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - - await controller.loadUrl(secondaryUrl); - await expectLater( - pageLoads.stream.firstWhere((String url) => url == secondaryUrl), - completion(secondaryUrl), - ); - }); - - testWidgets( - 'withWeakRefenceTo allows encapsulating class to be garbage collected', - (WidgetTester tester) async { - final Completer gcCompleter = Completer(); - final InstanceManager instanceManager = InstanceManager( - onWeakReferenceRemoved: gcCompleter.complete, - ); - - ClassWithCallbackClass? instance = ClassWithCallbackClass(); - instanceManager.addHostCreatedInstance(instance.callbackClass, 0); - instance = null; - - // Force garbage collection. - await IntegrationTestWidgetsFlutterBinding.instance - .watchPerformance(() async { - await tester.pumpAndSettle(); - }); - - final int gcIdentifier = await gcCompleter.future; - expect(gcIdentifier, 0); - }, timeout: const Timeout(Duration(seconds: 10))); - - // TODO(bparrishMines): This test is skipped because of - // https://github.com/flutter/flutter/issues/123327 - testWidgets( - 'WebView is released by garbage collection', - (WidgetTester tester) async { - final Completer webViewGCCompleter = Completer(); - - late final InstanceManager instanceManager; - instanceManager = - InstanceManager(onWeakReferenceRemoved: (int identifier) { - final Copyable instance = - instanceManager.getInstanceWithWeakReference(identifier)!; - if (instance is android.WebView && !webViewGCCompleter.isCompleted) { - webViewGCCompleter.complete(); - } - }); - - android.WebView.api = WebViewHostApiImpl( - instanceManager: instanceManager, - ); - android.WebSettings.api = WebSettingsHostApiImpl( - instanceManager: instanceManager, - ); - android.WebChromeClient.api = WebChromeClientHostApiImpl( - instanceManager: instanceManager, - ); - android.WebViewClient.api = WebViewClientHostApiImpl( - instanceManager: instanceManager, - ); - android.DownloadListener.api = DownloadListenerHostApiImpl( - instanceManager: instanceManager, - ); - - // Continually recreate web views until one is disposed through garbage - // collection. - while (!webViewGCCompleter.isCompleted) { - await tester.pumpWidget( - Builder( - builder: (BuildContext context) { - return AndroidWebView(instanceManager: instanceManager).build( - context: context, - creationParams: CreationParams( - webSettings: WebSettings( - hasNavigationDelegate: false, - userAgent: const WebSetting.of('woeifj'), - ), - ), - javascriptChannelRegistry: JavascriptChannelRegistry( - {}, - ), - webViewPlatformCallbacksHandler: TestPlatformCallbacksHandler(), - ); - }, - ), - ); - await tester.pumpAndSettle(); - - await tester.pumpWidget(Container()); - await tester.pumpAndSettle(); - } - - android.WebView.api = WebViewHostApiImpl(); - android.WebSettings.api = WebSettingsHostApiImpl(); - android.WebChromeClient.api = WebChromeClientHostApiImpl(); - android.WebViewClient.api = WebViewClientHostApiImpl(); - android.DownloadListener.api = DownloadListenerHostApiImpl(); - - // Create a new `WebStorage` with the default InstanceManager. - android.WebStorage.instance = android.WebStorage(); - }, - skip: true, - ); - - testWidgets('evaluateJavascript', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - final String result = await controller.evaluateJavascript('1 + 1'); - expect(result, equals('2')); - }); - - testWidgets('loadUrl with headers', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final StreamController pageStarts = StreamController(); - final StreamController pageLoads = StreamController(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageStarted: (String url) { - pageStarts.add(url); - }, - onPageFinished: (String url) { - pageLoads.add(url); - }, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - final Map headers = { - 'test_header': 'flutter_test_header' - }; - await controller.loadUrl(headersUrl, headers: headers); - - await pageStarts.stream.firstWhere((String url) => url == headersUrl); - await pageLoads.stream.firstWhere((String url) => url == headersUrl); - - final String content = await controller - .runJavascriptReturningResult('document.documentElement.innerText'); - expect(content.contains('flutter_test_header'), isTrue); - }); - - testWidgets('JavascriptChannel', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final Completer pageStarted = Completer(); - final Completer pageLoaded = Completer(); - final Completer channelCompleter = Completer(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - // This is the data URL for: '' - initialUrl: - 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - javascriptChannels: { - JavascriptChannel( - name: 'Echo', - onMessageReceived: (JavascriptMessage message) { - channelCompleter.complete(message.message); - }, - ), - }, - onPageStarted: (String url) { - pageStarted.complete(null); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - await pageStarted.future; - await pageLoaded.future; - - expect(channelCompleter.isCompleted, isFalse); - await controller.runJavascript('Echo.postMessage("hello");'); - - await expectLater(channelCompleter.future, completion('hello')); - }); - - testWidgets('resize webview', (WidgetTester tester) async { - final Completer initialResizeCompleter = Completer(); - final Completer buttonTapResizeCompleter = Completer(); - final Completer onPageFinished = Completer(); - - bool resizeButtonTapped = false; - await tester.pumpWidget(ResizableWebView( - onResize: (_) { - if (resizeButtonTapped) { - buttonTapResizeCompleter.complete(); - } else { - initialResizeCompleter.complete(); - } - }, - onPageFinished: () => onPageFinished.complete(), - )); - await onPageFinished.future; - // Wait for a potential call to resize after page is loaded. - await initialResizeCompleter.future.timeout( - const Duration(seconds: 3), - onTimeout: () => null, - ); - - resizeButtonTapped = true; - await tester.tap(find.byKey(const ValueKey('resizeButton'))); - await tester.pumpAndSettle(); - expect(buttonTapResizeCompleter.future, completes); - }); - - testWidgets('set custom userAgent', (WidgetTester tester) async { - final Completer controllerCompleter1 = - Completer(); - final GlobalKey globalKey = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: globalKey, - initialUrl: 'about:blank', - javascriptMode: JavascriptMode.unrestricted, - userAgent: 'Custom_User_Agent1', - onWebViewCreated: (WebViewController controller) { - controllerCompleter1.complete(controller); - }, - ), - ), - ); - final WebViewController controller1 = await controllerCompleter1.future; - final String customUserAgent1 = await _getUserAgent(controller1); - expect(customUserAgent1, 'Custom_User_Agent1'); - // rebuild the WebView with a different user agent. - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: globalKey, - initialUrl: 'about:blank', - javascriptMode: JavascriptMode.unrestricted, - userAgent: 'Custom_User_Agent2', - ), - ), - ); - - final String customUserAgent2 = await _getUserAgent(controller1); - expect(customUserAgent2, 'Custom_User_Agent2'); - }); - - testWidgets('use default platform userAgent after webView is rebuilt', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final GlobalKey globalKey = GlobalKey(); - // Build the webView with no user agent to get the default platform user agent. - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: globalKey, - initialUrl: primaryUrl, - javascriptMode: JavascriptMode.unrestricted, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - final String defaultPlatformUserAgent = await _getUserAgent(controller); - // rebuild the WebView with a custom user agent. - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: globalKey, - initialUrl: 'about:blank', - javascriptMode: JavascriptMode.unrestricted, - userAgent: 'Custom_User_Agent', - ), - ), - ); - final String customUserAgent = await _getUserAgent(controller); - expect(customUserAgent, 'Custom_User_Agent'); - // rebuilds the WebView with no user agent. - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: globalKey, - initialUrl: 'about:blank', - javascriptMode: JavascriptMode.unrestricted, - ), - ), - ); - - final String customUserAgent2 = await _getUserAgent(controller); - expect(customUserAgent2, defaultPlatformUserAgent); - }); - group('Video playback policy', () { late String videoTestBase64; setUpAll(() async { @@ -460,6 +88,15 @@ Future main() async { videoTestBase64 = base64Encode(const Utf8Encoder().convert(videoTest)); }); +// JavaScript booleans evaluate to different string values on Android and iOS. +// This utility method returns the string boolean value of the current platform. + String _webviewBool(bool value) { + if (defaultTargetPlatform == TargetPlatform.iOS) { + return value ? '1' : '0'; + } + return value ? 'true' : 'false'; + } + testWidgets('Auto media playback', (WidgetTester tester) async { Completer controllerCompleter = Completer(); @@ -515,1152 +152,6 @@ Future main() async { isPaused = await controller.runJavascriptReturningResult('isPaused();'); expect(isPaused, _webviewBool(true)); - }); - - testWidgets('Changes to initialMediaPlaybackPolicy are ignored', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - Completer pageLoaded = Completer(); - - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: key, - initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - await pageLoaded.future; - - String isPaused = - await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(false)); - - pageLoaded = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: key, - initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - - await controller.reload(); - - await pageLoaded.future; - - isPaused = await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(false)); - }); - - testWidgets('Video plays inline when allowsInlineMediaPlayback is true', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final Completer pageLoaded = Completer(); - final Completer videoPlaying = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - javascriptChannels: { - JavascriptChannel( - name: 'VideoTestTime', - onMessageReceived: (JavascriptMessage message) { - final double currentTime = double.parse(message.message); - // Let it play for at least 1 second to make sure the related video's properties are set. - if (currentTime > 1 && !videoPlaying.isCompleted) { - videoPlaying.complete(null); - } - }, - ), - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - allowsInlineMediaPlayback: true, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - await pageLoaded.future; - - // Pump once to trigger the video play. - await tester.pump(); - - // Makes sure we get the correct event that indicates the video is actually playing. - await videoPlaying.future; - - final String fullScreen = - await controller.runJavascriptReturningResult('isFullScreen();'); - expect(fullScreen, _webviewBool(false)); - }); + }, timeout: const Timeout(Duration(minutes: 60))); }); - - group('Audio playback policy', () { - late String audioTestBase64; - setUpAll(() async { - final ByteData audioData = - await rootBundle.load('assets/sample_audio.ogg'); - final String base64AudioData = - base64Encode(Uint8List.view(audioData.buffer)); - final String audioTest = ''' - - Audio auto play - - - - - - - '''; - audioTestBase64 = base64Encode(const Utf8Encoder().convert(audioTest)); - }); - - testWidgets('Auto media playback', (WidgetTester tester) async { - Completer controllerCompleter = - Completer(); - Completer pageStarted = Completer(); - Completer pageLoaded = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageStarted: (String url) { - pageStarted.complete(null); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - ), - ), - ); - WebViewController controller = await controllerCompleter.future; - await pageStarted.future; - await pageLoaded.future; - - String isPaused = - await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(false)); - - controllerCompleter = Completer(); - pageStarted = Completer(); - pageLoaded = Completer(); - - // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageStarted: (String url) { - pageStarted.complete(null); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - - controller = await controllerCompleter.future; - await pageStarted.future; - await pageLoaded.future; - - isPaused = await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(true)); - }); - - testWidgets('Changes to initialMediaPlaybackPolicy are ignored', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - Completer pageStarted = Completer(); - Completer pageLoaded = Completer(); - - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: key, - initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageStarted: (String url) { - pageStarted.complete(null); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - await pageStarted.future; - await pageLoaded.future; - - String isPaused = - await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(false)); - - pageStarted = Completer(); - pageLoaded = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: key, - initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageStarted: (String url) { - pageStarted.complete(null); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - - await controller.reload(); - - await pageStarted.future; - await pageLoaded.future; - - isPaused = await controller.runJavascriptReturningResult('isPaused();'); - expect(isPaused, _webviewBool(false)); - }); - }); - - testWidgets('getTitle', (WidgetTester tester) async { - const String getTitleTest = ''' - - Some title - - - - - '''; - final String getTitleTestBase64 = - base64Encode(const Utf8Encoder().convert(getTitleTest)); - final Completer pageStarted = Completer(); - final Completer pageLoaded = Completer(); - final Completer controllerCompleter = - Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - initialUrl: 'data:text/html;charset=utf-8;base64,$getTitleTestBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - onPageStarted: (String url) { - pageStarted.complete(null); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - - final WebViewController controller = await controllerCompleter.future; - await pageStarted.future; - await pageLoaded.future; - - final String? title = await controller.getTitle(); - expect(title, 'Some title'); - }); - - group('Programmatic Scroll', () { - testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { - const String scrollTestPage = ''' - - - - - - -
- - - '''; - - final String scrollTestPageBase64 = - base64Encode(const Utf8Encoder().convert(scrollTestPage)); - - final Completer pageLoaded = Completer(); - final Completer controllerCompleter = - Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - initialUrl: - 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - - final WebViewController controller = await controllerCompleter.future; - await pageLoaded.future; - - await tester.pumpAndSettle(const Duration(seconds: 3)); - - int scrollPosX = await controller.getScrollX(); - int scrollPosY = await controller.getScrollY(); - - // Check scrollTo() - const int X_SCROLL = 123; - const int Y_SCROLL = 321; - // Get the initial position; this ensures that scrollTo is actually - // changing something, but also gives the native view's scroll position - // time to settle. - expect(scrollPosX, isNot(X_SCROLL)); - expect(scrollPosX, isNot(Y_SCROLL)); - - await controller.scrollTo(X_SCROLL, Y_SCROLL); - scrollPosX = await controller.getScrollX(); - scrollPosY = await controller.getScrollY(); - expect(scrollPosX, X_SCROLL); - expect(scrollPosY, Y_SCROLL); - - // Check scrollBy() (on top of scrollTo()) - await controller.scrollBy(X_SCROLL, Y_SCROLL); - scrollPosX = await controller.getScrollX(); - scrollPosY = await controller.getScrollY(); - expect(scrollPosX, X_SCROLL * 2); - expect(scrollPosY, Y_SCROLL * 2); - }); - }); - - group('SurfaceAndroidWebView', () { - setUpAll(() { - WebView.platform = SurfaceAndroidWebView(); - }); - - tearDownAll(() { - WebView.platform = AndroidWebView(); - }); - - testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { - const String scrollTestPage = ''' - - - - - - -
- - - '''; - - final String scrollTestPageBase64 = - base64Encode(const Utf8Encoder().convert(scrollTestPage)); - - final Completer pageLoaded = Completer(); - final Completer controllerCompleter = - Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - initialUrl: - 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - - final WebViewController controller = await controllerCompleter.future; - await pageLoaded.future; - - await tester.pumpAndSettle(const Duration(seconds: 3)); - - // Check scrollTo() - const int X_SCROLL = 123; - const int Y_SCROLL = 321; - - await controller.scrollTo(X_SCROLL, Y_SCROLL); - int scrollPosX = await controller.getScrollX(); - int scrollPosY = await controller.getScrollY(); - expect(X_SCROLL, scrollPosX); - expect(Y_SCROLL, scrollPosY); - - // Check scrollBy() (on top of scrollTo()) - await controller.scrollBy(X_SCROLL, Y_SCROLL); - scrollPosX = await controller.getScrollX(); - scrollPosY = await controller.getScrollY(); - expect(X_SCROLL * 2, scrollPosX); - expect(Y_SCROLL * 2, scrollPosY); - }); - - testWidgets('inputs are scrolled into view when focused', - (WidgetTester tester) async { - const String scrollTestPage = ''' - - - - - - -
- - - - '''; - - final String scrollTestPageBase64 = - base64Encode(const Utf8Encoder().convert(scrollTestPage)); - - final Completer pageLoaded = Completer(); - final Completer controllerCompleter = - Completer(); - - await tester.runAsync(() async { - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: SizedBox( - width: 200, - height: 200, - child: WebView( - initialUrl: - 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - javascriptMode: JavascriptMode.unrestricted, - ), - ), - ), - ); - await Future.delayed(const Duration(milliseconds: 20)); - await tester.pump(); - }); - - final WebViewController controller = await controllerCompleter.future; - await pageLoaded.future; - final String viewportRectJSON = await _runJavaScriptReturningResult( - controller, 'JSON.stringify(viewport.getBoundingClientRect())'); - final Map viewportRectRelativeToViewport = - jsonDecode(viewportRectJSON) as Map; - - num getDomRectComponent( - Map rectAsJson, String component) { - return rectAsJson[component]! as num; - } - - // Check that the input is originally outside of the viewport. - - final String initialInputClientRectJSON = - await _runJavaScriptReturningResult( - controller, 'JSON.stringify(inputEl.getBoundingClientRect())'); - final Map initialInputClientRectRelativeToViewport = - jsonDecode(initialInputClientRectJSON) as Map; - - expect( - getDomRectComponent( - initialInputClientRectRelativeToViewport, 'bottom') <= - getDomRectComponent(viewportRectRelativeToViewport, 'bottom'), - isFalse); - - await controller.runJavascript('inputEl.focus()'); - - // Check that focusing the input brought it into view. - - final String lastInputClientRectJSON = - await _runJavaScriptReturningResult( - controller, 'JSON.stringify(inputEl.getBoundingClientRect())'); - final Map lastInputClientRectRelativeToViewport = - jsonDecode(lastInputClientRectJSON) as Map; - - expect( - getDomRectComponent(lastInputClientRectRelativeToViewport, 'top') >= - getDomRectComponent(viewportRectRelativeToViewport, 'top'), - isTrue); - expect( - getDomRectComponent( - lastInputClientRectRelativeToViewport, 'bottom') <= - getDomRectComponent(viewportRectRelativeToViewport, 'bottom'), - isTrue); - - expect( - getDomRectComponent(lastInputClientRectRelativeToViewport, 'left') >= - getDomRectComponent(viewportRectRelativeToViewport, 'left'), - isTrue); - expect( - getDomRectComponent(lastInputClientRectRelativeToViewport, 'right') <= - getDomRectComponent(viewportRectRelativeToViewport, 'right'), - isTrue); - }); - }); - - group('NavigationDelegate', () { - const String blankPage = ''; - final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' - '${base64Encode(const Utf8Encoder().convert(blankPage))}'; - - testWidgets('can allow requests', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final StreamController pageLoads = - StreamController.broadcast(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: blankPageEncoded, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - navigationDelegate: (NavigationRequest request) { - return (request.url.contains('youtube.com')) - ? NavigationDecision.prevent - : NavigationDecision.navigate; - }, - onPageFinished: (String url) => pageLoads.add(url), - ), - ), - ); - - await pageLoads.stream.first; // Wait for initial page load. - final WebViewController controller = await controllerCompleter.future; - await controller.runJavascript('location.href = "$secondaryUrl"'); - - await pageLoads.stream.first; // Wait for the next page load. - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, secondaryUrl); - }); - - testWidgets('onWebResourceError', (WidgetTester tester) async { - final Completer errorCompleter = - Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: 'https://www.notawebsite..com', - onWebResourceError: (WebResourceError error) { - errorCompleter.complete(error); - }, - ), - ), - ); - - final WebResourceError error = await errorCompleter.future; - expect(error, isNotNull); - - expect(error.errorType, isNotNull); - expect( - error.failingUrl?.startsWith('https://www.notawebsite..com'), isTrue); - }); - - testWidgets('onWebResourceError is not called with valid url', - (WidgetTester tester) async { - final Completer errorCompleter = - Completer(); - final Completer pageFinishCompleter = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: - 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+', - onWebResourceError: (WebResourceError error) { - errorCompleter.complete(error); - }, - onPageFinished: (_) => pageFinishCompleter.complete(), - ), - ), - ); - - expect(errorCompleter.future, doesNotComplete); - await pageFinishCompleter.future; - }); - - testWidgets( - 'onWebResourceError only called for main frame', - (WidgetTester tester) async { - const String iframeTest = ''' - - - - WebResourceError test - - - - - - '''; - final String iframeTestBase64 = - base64Encode(const Utf8Encoder().convert(iframeTest)); - - final Completer errorCompleter = - Completer(); - final Completer pageFinishCompleter = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: - 'data:text/html;charset=utf-8;base64,$iframeTestBase64', - onWebResourceError: (WebResourceError error) { - errorCompleter.complete(error); - }, - onPageFinished: (_) => pageFinishCompleter.complete(), - ), - ), - ); - - expect(errorCompleter.future, doesNotComplete); - await pageFinishCompleter.future; - }, - ); - - testWidgets('can block requests', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final StreamController pageLoads = - StreamController.broadcast(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: blankPageEncoded, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - navigationDelegate: (NavigationRequest request) { - return (request.url.contains('youtube.com')) - ? NavigationDecision.prevent - : NavigationDecision.navigate; - }, - onPageFinished: (String url) => pageLoads.add(url), - ), - ), - ); - - await pageLoads.stream.first; // Wait for initial page load. - final WebViewController controller = await controllerCompleter.future; - await controller - .runJavascript('location.href = "https://www.youtube.com/"'); - - // There should never be any second page load, since our new URL is - // blocked. Still wait for a potential page change for some time in order - // to give the test a chance to fail. - await pageLoads.stream.first - .timeout(const Duration(milliseconds: 500), onTimeout: () => ''); - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, isNot(contains('youtube.com'))); - }); - - testWidgets('supports asynchronous decisions', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final StreamController pageLoads = - StreamController.broadcast(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: blankPageEncoded, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - navigationDelegate: (NavigationRequest request) async { - NavigationDecision decision = NavigationDecision.prevent; - decision = await Future.delayed( - const Duration(milliseconds: 10), - () => NavigationDecision.navigate); - return decision; - }, - onPageFinished: (String url) => pageLoads.add(url), - ), - ), - ); - - await pageLoads.stream.first; // Wait for initial page load. - final WebViewController controller = await controllerCompleter.future; - await controller.runJavascript('location.href = "$secondaryUrl"'); - - await pageLoads.stream.first; // Wait for second page to load. - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, secondaryUrl); - }); - }); - - testWidgets('launches with gestureNavigationEnabled on iOS', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: SizedBox( - width: 400, - height: 300, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - gestureNavigationEnabled: true, - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - ), - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, primaryUrl); - }); - - testWidgets('target _blank opens in same window', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - final Completer pageLoaded = Completer(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - await controller.runJavascript('window.open("$primaryUrl", "_blank")'); - await pageLoaded.future; - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, primaryUrl); - }); - - testWidgets( - 'can open new window and go back', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - Completer pageLoaded = Completer(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (String url) { - pageLoaded.complete(); - }, - initialUrl: primaryUrl, - ), - ), - ); - final WebViewController controller = await controllerCompleter.future; - expect(controller.currentUrl(), completion(primaryUrl)); - await pageLoaded.future; - pageLoaded = Completer(); - - await controller.runJavascript('window.open("$secondaryUrl")'); - await pageLoaded.future; - pageLoaded = Completer(); - expect(controller.currentUrl(), completion(secondaryUrl)); - - expect(controller.canGoBack(), completion(true)); - await controller.goBack(); - await pageLoaded.future; - await expectLater(controller.currentUrl(), completion(primaryUrl)); - }, - ); - - testWidgets( - 'JavaScript does not run in parent window', - (WidgetTester tester) async { - const String iframe = ''' - - - '''; - final String iframeTestBase64 = - base64Encode(const Utf8Encoder().convert(iframe)); - - final String openWindowTest = ''' - - - - XSS test - - - - - - '''; - final String openWindowTestBase64 = - base64Encode(const Utf8Encoder().convert(openWindowTest)); - final Completer controllerCompleter = - Completer(); - final Completer pageLoadCompleter = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - initialUrl: - 'data:text/html;charset=utf-8;base64,$openWindowTestBase64', - onPageFinished: (String url) { - pageLoadCompleter.complete(); - }, - ), - ), - ); - - final WebViewController controller = await controllerCompleter.future; - await pageLoadCompleter.future; - - final String iframeLoaded = - await controller.runJavascriptReturningResult('iframeLoaded'); - expect(iframeLoaded, 'true'); - - final String elementText = await controller.runJavascriptReturningResult( - 'document.querySelector("p") && document.querySelector("p").textContent', - ); - expect(elementText, 'null'); - }, - ); - - testWidgets( - 'clearCache should clear local storage', - (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); - - Completer pageLoadCompleter = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - initialUrl: primaryUrl, - javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (_) => pageLoadCompleter.complete(), - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - ), - ), - ); - - await pageLoadCompleter.future; - pageLoadCompleter = Completer(); - - final WebViewController controller = await controllerCompleter.future; - await controller.runJavascript('localStorage.setItem("myCat", "Tom");'); - final String myCatItem = await controller.runJavascriptReturningResult( - 'localStorage.getItem("myCat");', - ); - expect(myCatItem, '"Tom"'); - - await controller.clearCache(); - await pageLoadCompleter.future; - - final String nullItem = await controller.runJavascriptReturningResult( - 'localStorage.getItem("myCat");', - ); - expect(nullItem, 'null'); - }, - ); -} - -// JavaScript booleans evaluate to different string values on Android and iOS. -// This utility method returns the string boolean value of the current platform. -String _webviewBool(bool value) { - if (defaultTargetPlatform == TargetPlatform.iOS) { - return value ? '1' : '0'; - } - return value ? 'true' : 'false'; -} - -/// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests. -Future _getUserAgent(WebViewController controller) async { - return _runJavaScriptReturningResult(controller, 'navigator.userAgent;'); -} - -Future _runJavaScriptReturningResult( - WebViewController controller, - String js, -) async { - return jsonDecode(await controller.runJavascriptReturningResult(js)) - as String; -} - -class ResizableWebView extends StatefulWidget { - const ResizableWebView({ - super.key, - required this.onResize, - required this.onPageFinished, - }); - - final JavascriptMessageHandler onResize; - final VoidCallback onPageFinished; - - @override - State createState() => ResizableWebViewState(); -} - -class ResizableWebViewState extends State { - double webViewWidth = 200; - double webViewHeight = 200; - - static const String resizePage = ''' - - Resize test - - - - - - '''; - - @override - Widget build(BuildContext context) { - final String resizeTestBase64 = - base64Encode(const Utf8Encoder().convert(resizePage)); - return Directionality( - textDirection: TextDirection.ltr, - child: Column( - children: [ - SizedBox( - width: webViewWidth, - height: webViewHeight, - child: WebView( - initialUrl: - 'data:text/html;charset=utf-8;base64,$resizeTestBase64', - javascriptChannels: { - JavascriptChannel( - name: 'Resize', - onMessageReceived: widget.onResize, - ), - }, - onPageFinished: (_) => widget.onPageFinished(), - javascriptMode: JavascriptMode.unrestricted, - ), - ), - TextButton( - key: const Key('resizeButton'), - onPressed: () { - setState(() { - webViewWidth += 100.0; - webViewHeight += 100.0; - }); - }, - child: const Text('ResizeButton'), - ), - ], - ), - ); - } -} - -class CopyableObjectWithCallback with Copyable { - CopyableObjectWithCallback(this.callback); - - final VoidCallback callback; - - @override - CopyableObjectWithCallback copy() { - return CopyableObjectWithCallback(callback); - } -} - -class ClassWithCallbackClass { - ClassWithCallbackClass() { - callbackClass = CopyableObjectWithCallback( - withWeakReferenceTo( - this, - (WeakReference weakReference) { - return () { - // Weak reference to `this` in callback. - // ignore: unnecessary_statements - weakReference; - }; - }, - ), - ); - } - - late final CopyableObjectWithCallback callbackClass; -} - -class TestPlatformCallbacksHandler implements WebViewPlatformCallbacksHandler { - @override - FutureOr onNavigationRequest({ - required String url, - required bool isForMainFrame, - }) async { - return true; - } - - @override - void onPageStarted(String url) {} - - @override - void onPageFinished(String url) {} - - @override - void onProgress(int progress) {} - - @override - void onWebResourceError(WebResourceError error) {} } diff --git a/script/configs/still_requires_api_33_avd.yaml b/script/configs/still_requires_api_33_avd.yaml index 7368d3d16e0c..c0924a7c6d84 100644 --- a/script/configs/still_requires_api_33_avd.yaml +++ b/script/configs/still_requires_api_33_avd.yaml @@ -1,2 +1,3 @@ # Running the somes tests from these packages on an AVD with Android 34 causes failures. -- webview_flutter +# TODO(camsim99): Remove shard as it no packages require running on AVDs with Android 33. +[]