import 'dart:async'; import 'dart:collection'; import 'dart:convert'; // import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:path_provider/path_provider.dart'; import 'in_app_webview/main.dart' as in_app_webview_test; import '.env.dart'; /// Returns a matcher that matches the isNullOrEmpty property. const Matcher isNullOrEmpty = _NullOrEmpty(); class _NullOrEmpty extends Matcher { const _NullOrEmpty(); @override bool matches(Object? item, Map matchState) => item == null || (item as dynamic).isEmpty; @override Description describe(Description description) => description.add('null or empty'); } class Foo { String? bar; String? baz; Foo({this.bar, this.baz}); Map toJson() { return {'bar': this.bar, 'baz': this.baz}; } } class MyInAppBrowser extends InAppBrowser { final Completer browserCreated = Completer(); final Completer firstPageLoaded = Completer(); MyInAppBrowser( {int? windowId, UnmodifiableListView? initialUserScripts}) : super(windowId: windowId, initialUserScripts: initialUserScripts); @override Future onBrowserCreated() async { browserCreated.complete(); } @override void onLoadStop(Uri? url) { super.onLoadStop(url); if (!firstPageLoaded.isCompleted) { firstPageLoaded.complete(); } } } class MyChromeSafariBrowser extends ChromeSafariBrowser { final Completer browserCreated = Completer(); final Completer firstPageLoaded = Completer(); final Completer browserClosed = Completer(); @override void onOpened() { browserCreated.complete(); } @override void onCompletedInitialLoad() { firstPageLoaded.complete(); } @override void onClosed() { browserClosed.complete(); } } void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); if (defaultTargetPlatform == TargetPlatform.android) { InAppWebViewController.setWebContentsDebuggingEnabled(true); } in_app_webview_test.main(); group('OLD InAppWebView', () { group('javascript code evaluation', () { testWidgets('evaluateJavascript', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; var result = await controller.evaluateJavascript(source: """ [1, true, ["bar", 5], {"foo": "baz"}]; """); expect(result, isNotNull); expect(result[0], 1); expect(result[1], true); expect(listEquals(result[2] as List?, ["bar", 5]), true); expect( mapEquals(result[3]?.cast(), {"foo": "baz"}), true); }); testWidgets('evaluateJavascript with content world', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.evaluateJavascript( source: "var foo = 49;", contentWorld: ContentWorld.world(name: "custom-world")); var result = await controller.evaluateJavascript(source: "foo"); expect(result, isNull); result = await controller.evaluateJavascript( source: "foo", contentWorld: ContentWorld.world(name: "custom-world")); expect(result, 49); }); testWidgets('callAsyncJavaScript', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; final String functionBody = """ var p = new Promise(function (resolve, reject) { window.setTimeout(function() { if (x >= 0) { resolve(x); } else { reject(y); } }, 1000); }); await p; return p; """; var result = await controller.callAsyncJavaScript( functionBody: functionBody, arguments: {'x': 49, 'y': 'error message'}); expect(result, isNotNull); expect(result!.error, isNull); expect(result.value, 49); result = await controller.callAsyncJavaScript( functionBody: functionBody, arguments: {'x': -49, 'y': 'error message'}); expect(result, isNotNull); expect(result!.value, isNull); expect(result.error, 'error message'); }); testWidgets('callAsyncJavaScript with content world', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.callAsyncJavaScript( functionBody: "window.foo = 49;", contentWorld: ContentWorld.world(name: "custom-world")); var result = await controller.callAsyncJavaScript( functionBody: "return window.foo;"); expect(result, isNotNull); expect(result!.error, isNull); expect(result.value, isNull); result = await controller.callAsyncJavaScript( functionBody: "return window.foo;", contentWorld: ContentWorld.world(name: "custom-world")); expect(result, isNotNull); expect(result!.error, isNull); expect(result.value, 49); }); }); testWidgets('loadUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; var url = await pageLoads.stream.first; expect(url, 'https://github.com/flutter'); await controller.loadUrl( urlRequest: URLRequest(url: Uri.parse('https://www.google.com/'))); url = await pageLoads.stream.first; expect(url, 'https://www.google.com/'); pageLoads.close(); }); testWidgets('loadUrl with headers', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageStarts = StreamController.broadcast(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions(javaScriptEnabled: true)), onLoadStart: (controller, url) { pageStarts.add(url!.toString()); }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; final Map headers = { 'test_header': 'flutter_test_header' }; await controller.loadUrl( urlRequest: URLRequest( url: Uri.parse('https://flutter-header-echo.herokuapp.com/'), headers: headers)); final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'https://flutter-header-echo.herokuapp.com/'); await pageStarts.stream.firstWhere((String url) => url == currentUrl); await pageLoads.stream.firstWhere((String url) => url == currentUrl); final String? content = await controller.evaluateJavascript( source: 'document.documentElement.innerText'); expect(content!.contains('flutter_test_header'), isTrue); pageStarts.close(); pageLoads.close(); }); // group("iOS loadFileURL", () { // late Directory appSupportDir; // late File fileHtml; // late File fileJs; // // setUpAll(() async { // appSupportDir = (await getApplicationSupportDirectory()); // // final Directory htmlFolder = Directory('${appSupportDir.path}/html/'); // if (!await htmlFolder.exists()) { // await htmlFolder.create(recursive: true); // } // // final Directory jsFolder = Directory('${appSupportDir.path}/js/'); // if (!await jsFolder.exists()) { // await jsFolder.create(recursive: true); // } // // var html = """ // // // file scheme // // // // // // """; // fileHtml = File(htmlFolder.path + "index.html"); // fileHtml.writeAsStringSync(html); // // var js = """ // console.log('message'); // """; // fileJs = File(jsFolder.path + "main.js"); // fileJs.writeAsStringSync(js); // }); // // testWidgets('initialUrl with file:// scheme and allowingReadAccessTo', // (WidgetTester tester) async { // final Completer consoleMessageShouldNotComplete = // Completer(); // await tester.pumpWidget( // Directionality( // textDirection: TextDirection.ltr, // child: InAppWebView( // key: GlobalKey(), // initialUrlRequest: // URLRequest(url: Uri.parse('file://${fileHtml.path}')), // onConsoleMessage: (controller, consoleMessage) { // consoleMessageShouldNotComplete.complete(consoleMessage); // }, // ), // ), // ); // var result = await consoleMessageShouldNotComplete.future // .timeout(const Duration(seconds: 2), onTimeout: () => null); // expect(result, null); // // final Completer consoleMessageCompleter = // Completer(); // await tester.pumpWidget( // Directionality( // textDirection: TextDirection.ltr, // child: InAppWebView( // key: GlobalKey(), // initialUrlRequest: // URLRequest(url: Uri.parse('file://${fileHtml.path}')), // initialOptions: InAppWebViewGroupOptions( // ios: IOSInAppWebViewOptions( // allowingReadAccessTo: // Uri.parse('file://${appSupportDir.path}/'))), // onConsoleMessage: (controller, consoleMessage) { // consoleMessageCompleter.complete(consoleMessage); // }, // ), // ), // ); // final ConsoleMessage consoleMessage = // await consoleMessageCompleter.future; // expect(consoleMessage.messageLevel, ConsoleMessageLevel.LOG); // expect(consoleMessage.message, 'message'); // }, skip: defaultTargetPlatform != TargetPlatform.iOS); // // testWidgets( // 'loadUrl with file:// scheme and allowingReadAccessTo argument', // (WidgetTester tester) async { // final Completer consoleMessageShouldNotComplete = // Completer(); // await tester.pumpWidget( // Directionality( // textDirection: TextDirection.ltr, // child: InAppWebView( // key: GlobalKey(), // onWebViewCreated: (controller) { // controller.loadUrl( // urlRequest: // URLRequest(url: Uri.parse('file://${fileHtml.path}'))); // }, // onConsoleMessage: (controller, consoleMessage) { // consoleMessageShouldNotComplete.complete(consoleMessage); // }, // ), // ), // ); // var result = await consoleMessageShouldNotComplete.future // .timeout(const Duration(seconds: 2), onTimeout: () => null); // expect(result, null); // // final Completer consoleMessageCompleter = // Completer(); // await tester.pumpWidget( // Directionality( // textDirection: TextDirection.ltr, // child: InAppWebView( // key: GlobalKey(), // onWebViewCreated: (controller) { // controller.loadUrl( // urlRequest: // URLRequest(url: Uri.parse('file://${fileHtml.path}')), // allowingReadAccessTo: // Uri.parse('file://${appSupportDir.path}/')); // }, // onConsoleMessage: (controller, consoleMessage) { // consoleMessageCompleter.complete(consoleMessage); // }, // ), // ), // ); // final ConsoleMessage consoleMessage = // await consoleMessageCompleter.future; // expect(consoleMessage.messageLevel, ConsoleMessageLevel.LOG); // expect(consoleMessage.message, 'message'); // }, skip: defaultTargetPlatform != TargetPlatform.iOS); // }, skip: defaultTargetPlatform != TargetPlatform.iOS); testWidgets('JavaScript Handler', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageStarted = Completer(); final Completer pageLoaded = Completer(); final Completer handlerFoo = Completer(); final Completer handlerFooWithArgs = Completer(); final List messagesReceived = []; await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialFile: "test_assets/in_app_webview_javascript_handler_test.html", onWebViewCreated: (controller) { controllerCompleter.complete(controller); controller.addJavaScriptHandler( handlerName: 'handlerFoo', callback: (args) { handlerFoo.complete(); return Foo(bar: 'bar_value', baz: 'baz_value'); }); controller.addJavaScriptHandler( handlerName: 'handlerFooWithArgs', callback: (args) { messagesReceived.add(args[0] as int); messagesReceived.add(args[1] as bool); messagesReceived.add(args[2] as List?); messagesReceived.add(args[3]?.cast() as Map?); messagesReceived.add(args[4]?.cast() as Map?); handlerFooWithArgs.complete(); }); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions(javaScriptEnabled: true)), onLoadStart: (controller, url) { pageStarted.complete(); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); await pageStarted.future; await pageLoaded.future; await handlerFoo.future; await handlerFooWithArgs.future; expect(messagesReceived[0], 1); expect(messagesReceived[1], true); expect( listEquals(messagesReceived[2] as List?, ["bar", 5]), true); expect(mapEquals(messagesReceived[3], {"foo": "baz"}), true); expect( mapEquals( messagesReceived[4], {"bar": "bar_value", "baz": "baz_value"}), true); }); testWidgets('resize webview', (WidgetTester tester) async { final String resizeTest = ''' Resize test '''; final String resizeTestBase64 = base64Encode(const Utf8Encoder().convert(resizeTest)); final Completer resizeCompleter = Completer(); final Completer pageStarted = Completer(); final Completer pageLoaded = Completer(); final Completer controllerCompleter = Completer(); final GlobalKey key = GlobalKey(); final InAppWebView webView = InAppWebView( key: key, initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,$resizeTestBase64')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); controller.addJavaScriptHandler( handlerName: 'resize', callback: (args) { resizeCompleter.complete(true); }); }, onLoadStart: (controller, url) { pageStarted.complete(); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions(javaScriptEnabled: true)), ); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: Column( children: [ SizedBox( width: 200, height: 200, child: webView, ), ], ), ), ); await controllerCompleter.future; await pageStarted.future; await pageLoaded.future; expect(resizeCompleter.isCompleted, false); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: Column( children: [ SizedBox( width: 400, height: 400, child: webView, ), ], ), ), ); await resizeCompleter.future; }); testWidgets('set custom userAgent', (WidgetTester tester) async { final Completer controllerCompleter1 = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, userAgent: 'Custom_User_Agent1', )), onWebViewCreated: (controller) { controllerCompleter1.complete(controller); }, ), ), ); InAppWebViewController controller1 = await controllerCompleter1.future; final String customUserAgent1 = await controller1.evaluateJavascript(source: 'navigator.userAgent;'); expect(customUserAgent1, 'Custom_User_Agent1'); await controller1.setOptions( options: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( userAgent: 'Custom_User_Agent2', ))); final String customUserAgent2 = await controller1.evaluateJavascript(source: 'navigator.userAgent;'); expect(customUserAgent2, 'Custom_User_Agent2'); }); group('Video playback policy', () { String videoTestBase64 = ""; setUpAll(() async { final ByteData videoData = await rootBundle.load('test_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: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,$videoTestBase64')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, mediaPlaybackRequiresUserGesture: false)), onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; bool isPaused = await controller.evaluateJavascript(source: 'isPaused();'); expect(isPaused, false); controllerCompleter = Completer(); pageLoaded = Completer(); // We change the key to re-create a new webview as we change the mediaPlaybackRequiresUserGesture await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,$videoTestBase64')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, mediaPlaybackRequiresUserGesture: true)), onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); controller = await controllerCompleter.future; await pageLoaded.future; isPaused = await controller.evaluateJavascript(source: 'isPaused();'); expect(isPaused, true); }); testWidgets('Video plays inline when allowsInlineMediaPlayback is true', (WidgetTester tester) async { Completer controllerCompleter = Completer(); Completer pageLoaded = Completer(); Completer onEnterFullscreenCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,$videoTestBase64')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, mediaPlaybackRequiresUserGesture: false, ), ios: IOSInAppWebViewOptions(allowsInlineMediaPlayback: true)), onLoadStop: (controller, url) { pageLoaded.complete(); }, onEnterFullscreen: (controller) { onEnterFullscreenCompleter.complete(); }, ), ), ); await pageLoaded.future; expect(onEnterFullscreenCompleter.future, doesNotComplete); }); testWidgets( 'Video plays fullscreen when allowsInlineMediaPlayback is false', (WidgetTester tester) async { Completer controllerCompleter = Completer(); Completer pageLoaded = Completer(); Completer onEnterFullscreenCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,$videoTestBase64')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, mediaPlaybackRequiresUserGesture: false, ), ios: IOSInAppWebViewOptions(allowsInlineMediaPlayback: false)), onLoadStop: (controller, url) { pageLoaded.complete(); }, onEnterFullscreen: (controller) { onEnterFullscreenCompleter.complete(); }, ), ), ); await pageLoaded.future; await expectLater(onEnterFullscreenCompleter.future, completes); }, skip: true); testWidgets('exit fullscreen event', (WidgetTester tester) async { Completer controllerCompleter = Completer(); Completer pageLoaded = Completer(); Completer onExitFullscreenCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,$videoTestBase64')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, mediaPlaybackRequiresUserGesture: false, ), ios: IOSInAppWebViewOptions(allowsInlineMediaPlayback: false)), onLoadStop: (controller, url) { pageLoaded.complete(); }, onExitFullscreen: (controller) { onExitFullscreenCompleter.complete(); }, ), ), ); InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await Future.delayed(Duration(seconds: 2)); await controller.evaluateJavascript(source: "exitFullscreen();"); await expectLater(onExitFullscreenCompleter.future, completes); }, skip: true /*defaultTargetPlatform != TargetPlatform.android*/); }); group('Audio playback policy', () { String audioTestBase64 = ""; setUpAll(() async { final ByteData audioData = await rootBundle.load('test_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: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,$audioTestBase64')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, mediaPlaybackRequiresUserGesture: false)), onLoadStart: (controller, url) { pageStarted.complete(); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); InAppWebViewController controller = await controllerCompleter.future; await pageStarted.future; await pageLoaded.future; bool isPaused = await controller.evaluateJavascript(source: 'isPaused();'); expect(isPaused, false); controllerCompleter = Completer(); pageStarted = Completer(); pageLoaded = Completer(); // We change the key to re-create a new webview as we change the mediaPlaybackRequiresUserGesture await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,$audioTestBase64')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, mediaPlaybackRequiresUserGesture: true), ), onLoadStart: (controller, url) { pageStarted.complete(); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); controller = await controllerCompleter.future; await pageStarted.future; await pageLoaded.future; isPaused = await controller.evaluateJavascript(source: 'isPaused();'); expect(isPaused, true); }); }); testWidgets('getTitle', (WidgetTester tester) async { final 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: InAppWebView( initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,$getTitleTestBase64')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStart: (controller, url) { pageStarted.complete(); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController 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 { final 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: InAppWebView( initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.scrollTo(x: 0, y: 0); await tester.pumpAndSettle(Duration(seconds: 3)); // Check scrollTo() const int X_SCROLL = 123; const int Y_SCROLL = 321; await controller.scrollTo(x: X_SCROLL, y: Y_SCROLL); await tester.pumpAndSettle(Duration(seconds: 2)); int? scrollPosX = await controller.getScrollX(); int? scrollPosY = await controller.getScrollY(); expect(scrollPosX, X_SCROLL); expect(scrollPosY, Y_SCROLL); // Check scrollBy() (on top of scrollTo()) await controller.scrollBy(x: X_SCROLL, y: Y_SCROLL); await tester.pumpAndSettle(Duration(seconds: 2)); scrollPosX = await controller.getScrollX(); scrollPosY = await controller.getScrollY(); expect(scrollPosX, X_SCROLL * 2); expect(scrollPosY, Y_SCROLL * 2); }); }); group('Android Hybrid Composition', () { testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { final 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: InAppWebView( initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( android: AndroidInAppWebViewOptions(useHybridComposition: true)), onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.scrollTo(x: 0, y: 0); await tester.pumpAndSettle(Duration(seconds: 3)); // Check scrollTo() const int X_SCROLL = 123; const int Y_SCROLL = 321; await controller.scrollTo(x: X_SCROLL, y: Y_SCROLL); await tester.pumpAndSettle(Duration(seconds: 2)); int? scrollPosX = await controller.getScrollX(); int? scrollPosY = await controller.getScrollY(); expect(scrollPosX, X_SCROLL); expect(scrollPosY, Y_SCROLL); // Check scrollBy() (on top of scrollTo()) await controller.scrollBy(x: X_SCROLL, y: Y_SCROLL); await tester.pumpAndSettle(Duration(seconds: 2)); scrollPosX = await controller.getScrollX(); scrollPosY = await controller.getScrollY(); expect(scrollPosX, X_SCROLL * 2); expect(scrollPosY, Y_SCROLL * 2); }, skip: defaultTargetPlatform != TargetPlatform.android); }, skip: defaultTargetPlatform != TargetPlatform.android); group('shouldOverrideUrlLoading', () { final String page = '''flutter_inappwebview'''; final String pageEncoded = 'data:text/html;charset=utf-8;base64,' + base64Encode(const Utf8Encoder().convert(page)); testWidgets('can allow requests', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse(pageEncoded)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, useShouldOverrideUrlLoading: true), ), shouldOverrideUrlLoading: (controller, navigationAction) async { return (navigationAction.request.url!.host .contains('youtube.com')) ? NavigationActionPolicy.CANCEL : NavigationActionPolicy.ALLOW; }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); await pageLoads.stream.first; // Wait for initial page load. final InAppWebViewController controller = await controllerCompleter.future; await controller.evaluateJavascript( source: 'location.href = "https://www.google.com/"'); await pageLoads.stream.first; // Wait for the next page load. final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'https://www.google.com/'); pageLoads.close(); }); testWidgets( 'allow requests on iOS only if iosWKNavigationType == IOSWKNavigationType.LINK_ACTIVATED', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse(pageEncoded)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, useShouldOverrideUrlLoading: true), ), shouldOverrideUrlLoading: (controller, navigationAction) async { var isFirstLoad = navigationAction.request.url!.scheme == "data"; return (isFirstLoad || navigationAction.iosWKNavigationType == IOSWKNavigationType.LINK_ACTIVATED) ? NavigationActionPolicy.ALLOW : NavigationActionPolicy.CANCEL; }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); await pageLoads.stream.first; // Wait for initial page load. final InAppWebViewController controller = await controllerCompleter.future; await controller.evaluateJavascript( source: 'location.href = "https://www.google.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 .map((event) => event as String?) .first .timeout(const Duration(milliseconds: 500), onTimeout: () => null); String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, isNot('https://www.google.com/')); await controller.evaluateJavascript( source: 'document.querySelector("#link").click();'); await pageLoads.stream.first; // Wait for the next page load. currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'https://github.com/pichillilorenzo/flutter_inappwebview'); pageLoads.close(); }, skip: defaultTargetPlatform != TargetPlatform.iOS); testWidgets('can block requests', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse(pageEncoded)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, useShouldOverrideUrlLoading: true), ), shouldOverrideUrlLoading: (controller, navigationAction) async { return (navigationAction.request.url!.host .contains('youtube.com')) ? NavigationActionPolicy.CANCEL : NavigationActionPolicy.ALLOW; }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); await pageLoads.stream.first; // Wait for initial page load. final InAppWebViewController controller = await controllerCompleter.future; await controller.evaluateJavascript( source: '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 .map((event) => event as String?) .first .timeout(const Duration(milliseconds: 500), onTimeout: () => null); final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, isNot(contains('youtube.com'))); pageLoads.close(); }); testWidgets('supports asynchronous decisions', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse(pageEncoded)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, useShouldOverrideUrlLoading: true), ), shouldOverrideUrlLoading: (controller, navigationAction) async { var action = NavigationActionPolicy.CANCEL; action = await Future.delayed( const Duration(milliseconds: 10), () => NavigationActionPolicy.ALLOW); return action; }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); await pageLoads.stream.first; // Wait for initial page load. final InAppWebViewController controller = await controllerCompleter.future; await controller.evaluateJavascript( source: 'location.href = "https://www.google.com"'); await pageLoads.stream.first; // Wait for second page to load. final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'https://www.google.com/'); pageLoads.close(); }); }); testWidgets('onLoadError', (WidgetTester tester) async { final Completer errorUrlCompleter = Completer(); final Completer errorCodeCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://www.notawebsite..com')), onLoadError: (controller, url, code, message) { errorUrlCompleter.complete(url.toString()); errorCodeCompleter.complete(code); }, ), ), ); final String url = await errorUrlCompleter.future; final int code = await errorCodeCompleter.future; if (defaultTargetPlatform == TargetPlatform.android) { expect(code, -2); } else if (defaultTargetPlatform == TargetPlatform.iOS) { expect(code, -1003); } expect(url, 'https://www.notawebsite..com/'); }); testWidgets('onLoadError is not called with valid url', (WidgetTester tester) async { final Completer errorUrlCompleter = Completer(); final Completer errorCodeCompleter = Completer(); final Completer errorMessageCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+')), onLoadError: (controller, url, code, message) { errorUrlCompleter.complete(url.toString()); errorCodeCompleter.complete(code); errorMessageCompleter.complete(message); }, ), ), ); expect(errorUrlCompleter.future, doesNotComplete); expect(errorCodeCompleter.future, doesNotComplete); expect(errorMessageCompleter.future, doesNotComplete); }); testWidgets('launches with allowsBackForwardNavigationGestures true on iOS', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: SizedBox( width: 400, height: 300, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), initialOptions: InAppWebViewGroupOptions( ios: IOSInAppWebViewOptions( allowsBackForwardNavigationGestures: true)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, ), ), ), ); final InAppWebViewController controller = await controllerCompleter.future; final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'https://github.com/flutter'); }, skip: defaultTargetPlatform != TargetPlatform.iOS); testWidgets('target _blank opens in same window', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialUrlRequest: URLRequest(url: Uri.parse("about:blank")), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, javaScriptCanOpenWindowsAutomatically: true), ), onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); await pageLoads.stream.first; final InAppWebViewController controller = await controllerCompleter.future; await controller.evaluateJavascript( source: 'window.open("about:blank", "_blank");'); await pageLoads.stream.first; final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'about:blank'); pageLoads.close(); }); testWidgets( 'can open new window and go back', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, javaScriptCanOpenWindowsAutomatically: true), ), onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); await pageLoads.stream.first; final InAppWebViewController controller = await controllerCompleter.future; await controller.evaluateJavascript( source: 'window.open("https://github.com/flutter");'); await pageLoads.stream.first; expect((await controller.getUrl())?.toString(), contains('github.com/flutter')); await controller.goBack(); await pageLoads.stream.first; expect( (await controller.getUrl())?.toString(), contains('flutter.dev')); pageLoads.close(); }, skip: true /* defaultTargetPlatform != TargetPlatform.android */, ); testWidgets( 'javascript does not run in parent window', (WidgetTester tester) async { final 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: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,$openWindowTestBase64')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, javaScriptCanOpenWindowsAutomatically: true), ), onLoadStop: (controller, url) { pageLoadCompleter.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoadCompleter.future; expect(controller.evaluateJavascript(source: 'iframeLoaded'), completion(true)); expect( controller.evaluateJavascript( source: 'document.querySelector("p") && document.querySelector("p").textContent'), completion(null), ); }, skip: defaultTargetPlatform != TargetPlatform.android, ); group('intercept ajax request', () { testWidgets('send string data', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer shouldInterceptAjaxPostRequestCompleter = Completer(); final Completer> onAjaxReadyStateChangeCompleter = Completer>(); final Completer> onAjaxProgressCompleter = Completer>(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ InAppWebViewAjaxTest

InAppWebViewAjaxTest

"""), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, useShouldInterceptAjaxRequest: true, )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, shouldInterceptAjaxRequest: (controller, ajaxRequest) async { assert(ajaxRequest.data == "firstname=Foo&lastname=Bar"); ajaxRequest.responseType = 'json'; ajaxRequest.data = "firstname=Foo2&lastname=Bar2"; shouldInterceptAjaxPostRequestCompleter.complete(controller); return ajaxRequest; }, onAjaxReadyStateChange: (controller, ajaxRequest) async { if (ajaxRequest.readyState == AjaxRequestReadyState.DONE && ajaxRequest.status == 200) { Map res = ajaxRequest.response; onAjaxReadyStateChangeCompleter.complete(res); } return AjaxRequestAction.PROCEED; }, onAjaxProgress: (controller, ajaxRequest) async { if (ajaxRequest.event!.type == AjaxRequestEventType.LOAD) { Map res = ajaxRequest.response; onAjaxProgressCompleter.complete(res); } return AjaxRequestAction.PROCEED; }, ), ), ); await shouldInterceptAjaxPostRequestCompleter.future; final Map onAjaxReadyStateChangeValue = await onAjaxReadyStateChangeCompleter.future; final Map onAjaxProgressValue = await onAjaxProgressCompleter.future; expect( mapEquals(onAjaxReadyStateChangeValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}), true); expect( mapEquals( onAjaxProgressValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}), true); }); testWidgets('send json data', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer shouldInterceptAjaxPostRequestCompleter = Completer(); final Completer> onAjaxReadyStateChangeCompleter = Completer>(); final Completer> onAjaxProgressCompleter = Completer>(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ InAppWebViewAjaxTest

InAppWebViewAjaxTest

"""), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, useShouldInterceptAjaxRequest: true, )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, shouldInterceptAjaxRequest: (controller, ajaxRequest) async { String data = ajaxRequest.data; assert(data.contains('"firstname":"Foo"') && data.contains('"lastname":"Bar"')); ajaxRequest.responseType = 'json'; ajaxRequest.data = '{"firstname": "Foo2", "lastname": "Bar2"}'; shouldInterceptAjaxPostRequestCompleter.complete(controller); return ajaxRequest; }, onAjaxReadyStateChange: (controller, ajaxRequest) async { if (ajaxRequest.readyState == AjaxRequestReadyState.DONE && ajaxRequest.status == 200) { Map res = ajaxRequest.response; onAjaxReadyStateChangeCompleter.complete(res); } return AjaxRequestAction.PROCEED; }, onAjaxProgress: (controller, ajaxRequest) async { if (ajaxRequest.event!.type == AjaxRequestEventType.LOAD) { Map res = ajaxRequest.response; onAjaxProgressCompleter.complete(res); } return AjaxRequestAction.PROCEED; }, ), ), ); await shouldInterceptAjaxPostRequestCompleter.future; final Map onAjaxReadyStateChangeValue = await onAjaxReadyStateChangeCompleter.future; final Map onAjaxProgressValue = await onAjaxProgressCompleter.future; expect( mapEquals(onAjaxReadyStateChangeValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}), true); expect( mapEquals( onAjaxProgressValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}), true); }); testWidgets('send URLSearchParams data', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer shouldInterceptAjaxPostRequestCompleter = Completer(); final Completer> onAjaxReadyStateChangeCompleter = Completer>(); final Completer> onAjaxProgressCompleter = Completer>(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ InAppWebViewAjaxTest

InAppWebViewAjaxTest

"""), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, useShouldInterceptAjaxRequest: true, )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, shouldInterceptAjaxRequest: (controller, ajaxRequest) async { assert(ajaxRequest.data == "firstname=Foo&lastname=Bar"); ajaxRequest.responseType = 'json'; ajaxRequest.data = "firstname=Foo2&lastname=Bar2"; shouldInterceptAjaxPostRequestCompleter.complete(controller); return ajaxRequest; }, onAjaxReadyStateChange: (controller, ajaxRequest) async { if (ajaxRequest.readyState == AjaxRequestReadyState.DONE && ajaxRequest.status == 200) { Map res = ajaxRequest.response; onAjaxReadyStateChangeCompleter.complete(res); } return AjaxRequestAction.PROCEED; }, onAjaxProgress: (controller, ajaxRequest) async { if (ajaxRequest.event!.type == AjaxRequestEventType.LOAD) { Map res = ajaxRequest.response; onAjaxProgressCompleter.complete(res); } return AjaxRequestAction.PROCEED; }, ), ), ); await shouldInterceptAjaxPostRequestCompleter.future; final Map onAjaxReadyStateChangeValue = await onAjaxReadyStateChangeCompleter.future; final Map onAjaxProgressValue = await onAjaxProgressCompleter.future; expect( mapEquals(onAjaxReadyStateChangeValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}), true); expect( mapEquals( onAjaxProgressValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}), true); }); testWidgets('send FormData', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer shouldInterceptAjaxPostRequestCompleter = Completer(); final Completer> onAjaxReadyStateChangeCompleter = Completer>(); final Completer> onAjaxProgressCompleter = Completer>(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ InAppWebViewAjaxTest

InAppWebViewAjaxTest

"""), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, useShouldInterceptAjaxRequest: true, )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, shouldInterceptAjaxRequest: (controller, ajaxRequest) async { assert(ajaxRequest.data != null); var body = ajaxRequest.data.cast(); var bodyString = String.fromCharCodes(body); assert(bodyString.indexOf("WebKitFormBoundary") >= 0); ajaxRequest.data = utf8.encode(bodyString .replaceFirst("Foo", "Foo2") .replaceFirst("Bar", "Bar2")); ajaxRequest.responseType = 'json'; shouldInterceptAjaxPostRequestCompleter.complete(controller); return ajaxRequest; }, onAjaxReadyStateChange: (controller, ajaxRequest) async { if (ajaxRequest.readyState == AjaxRequestReadyState.DONE && ajaxRequest.status == 200) { Map res = ajaxRequest.response; onAjaxReadyStateChangeCompleter.complete(res); } return AjaxRequestAction.PROCEED; }, onAjaxProgress: (controller, ajaxRequest) async { if (ajaxRequest.event!.type == AjaxRequestEventType.LOAD) { Map res = ajaxRequest.response; onAjaxProgressCompleter.complete(res); } return AjaxRequestAction.PROCEED; }, ), ), ); await shouldInterceptAjaxPostRequestCompleter.future; final Map onAjaxReadyStateChangeValue = await onAjaxReadyStateChangeCompleter.future; final Map onAjaxProgressValue = await onAjaxProgressCompleter.future; expect( mapEquals(onAjaxReadyStateChangeValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}), true); expect( mapEquals( onAjaxProgressValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}), true); }); }); group('intercept fetch request', () { testWidgets('send string data', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer> fetchPostCompleter = Completer>(); final Completer shouldInterceptFetchPostRequestCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ InAppWebViewFetchTest

InAppWebViewFetchTest

"""), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, useShouldInterceptFetchRequest: true, )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); controller.addJavaScriptHandler( handlerName: "fetchPost", callback: (args) { fetchPostCompleter .complete(args[0] as Map); }); }, shouldInterceptFetchRequest: (controller, fetchRequest) async { assert(fetchRequest.body == "firstname=Foo&lastname=Bar"); fetchRequest.body = "firstname=Foo2&lastname=Bar2"; shouldInterceptFetchPostRequestCompleter.complete(); return fetchRequest; }, ), ), ); await shouldInterceptFetchPostRequestCompleter.future; var fetchPostCompleterValue = await fetchPostCompleter.future; expect( mapEquals(fetchPostCompleterValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}), true); }); testWidgets('send json data', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer> fetchPostCompleter = Completer>(); final Completer shouldInterceptFetchPostRequestCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ InAppWebViewFetchTest

InAppWebViewFetchTest

"""), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, useShouldInterceptFetchRequest: true, )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); controller.addJavaScriptHandler( handlerName: "fetchPost", callback: (args) { fetchPostCompleter .complete(args[0] as Map); }); }, shouldInterceptFetchRequest: (controller, fetchRequest) async { String body = fetchRequest.body; assert(body.contains('"firstname":"Foo"') && body.contains('"lastname":"Bar"')); fetchRequest.body = '{"firstname": "Foo2", "lastname": "Bar2"}'; shouldInterceptFetchPostRequestCompleter.complete(); return fetchRequest; }, ), ), ); await shouldInterceptFetchPostRequestCompleter.future; var fetchPostCompleterValue = await fetchPostCompleter.future; expect( mapEquals(fetchPostCompleterValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}), true); }); testWidgets('send URLSearchParams data', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer> fetchPostCompleter = Completer>(); final Completer shouldInterceptFetchPostRequestCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ InAppWebViewFetchTest

InAppWebViewFetchTest

"""), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, useShouldInterceptFetchRequest: true, )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); controller.addJavaScriptHandler( handlerName: "fetchPost", callback: (args) { fetchPostCompleter .complete(args[0] as Map); }); }, shouldInterceptFetchRequest: (controller, fetchRequest) async { assert(fetchRequest.body == "firstname=Foo&lastname=Bar"); fetchRequest.body = "firstname=Foo2&lastname=Bar2"; shouldInterceptFetchPostRequestCompleter.complete(); return fetchRequest; }, ), ), ); await shouldInterceptFetchPostRequestCompleter.future; var fetchPostCompleterValue = await fetchPostCompleter.future; expect( mapEquals(fetchPostCompleterValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}), true); }); testWidgets('send FormData', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer> fetchPostCompleter = Completer>(); final Completer shouldInterceptFetchPostRequestCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ InAppWebViewFetchTest

InAppWebViewFetchTest

"""), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, useShouldInterceptFetchRequest: true, )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); controller.addJavaScriptHandler( handlerName: "fetchPost", callback: (args) { fetchPostCompleter .complete(args[0] as Map); }); }, shouldInterceptFetchRequest: (controller, fetchRequest) async { assert(fetchRequest.body != null); var body = fetchRequest.body.cast(); var bodyString = String.fromCharCodes(body); assert(bodyString.indexOf("WebKitFormBoundary") >= 0); fetchRequest.body = utf8.encode(bodyString .replaceFirst("Foo", "Foo2") .replaceFirst("Bar", "Bar2")); shouldInterceptFetchPostRequestCompleter.complete(); return fetchRequest; }, ), ), ); await shouldInterceptFetchPostRequestCompleter.future; var fetchPostCompleterValue = await fetchPostCompleter.future; expect( mapEquals(fetchPostCompleterValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}), true); }); }); testWidgets('Content Blocker', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev/')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions(clearCache: true, contentBlockers: [ ContentBlocker( trigger: ContentBlockerTrigger(urlFilter: ".*", resourceType: [ ContentBlockerTriggerResourceType.IMAGE, ContentBlockerTriggerResourceType.STYLE_SHEET ], ifTopUrl: [ "https://flutter.dev/" ]), action: ContentBlockerAction( type: ContentBlockerActionType.BLOCK)) ])), onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); await expectLater(pageLoaded.future, completes); }); testWidgets('Http Auth Credential Database', (WidgetTester tester) async { HttpAuthCredentialDatabase httpAuthCredentialDatabase = HttpAuthCredentialDatabase.instance(); final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); httpAuthCredentialDatabase.setHttpAuthCredential( protectionSpace: URLProtectionSpace( host: environment["NODE_SERVER_IP"]!, protocol: "http", realm: "Node", port: 8081), credential: URLCredential(username: "USERNAME", password: "PASSWORD")); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse("http://${environment["NODE_SERVER_IP"]}:8081/")), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, )), onLoadStop: (controller, url) { pageLoaded.complete(); }, onReceivedHttpAuthRequest: (controller, challenge) async { return new HttpAuthResponse( action: HttpAuthResponseAction.USE_SAVED_HTTP_AUTH_CREDENTIALS); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; final String h1Content = await controller.evaluateJavascript( source: "document.body.querySelector('h1').textContent"); expect(h1Content, "Authorized"); var credentials = await httpAuthCredentialDatabase.getHttpAuthCredentials( protectionSpace: URLProtectionSpace( host: environment["NODE_SERVER_IP"]!, protocol: "http", realm: "Node", port: 8081)); expect(credentials.length, 1); await httpAuthCredentialDatabase.clearAllAuthCredentials(); credentials = await httpAuthCredentialDatabase.getHttpAuthCredentials( protectionSpace: URLProtectionSpace( host: environment["NODE_SERVER_IP"]!, protocol: "http", realm: "Node", port: 8081)); expect(credentials, isEmpty); }); testWidgets('onReceivedHttpAuthRequest', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse("http://${environment["NODE_SERVER_IP"]}:8081/")), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, )), onLoadStop: (controller, url) { pageLoaded.complete(); }, onReceivedHttpAuthRequest: (controller, challenge) async { return new HttpAuthResponse( username: "USERNAME", password: "PASSWORD", action: HttpAuthResponseAction.PROCEED, permanentPersistence: true); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; final String h1Content = await controller.evaluateJavascript( source: "document.body.querySelector('h1').textContent"); expect(h1Content, "Authorized"); }); testWidgets('onConsoleMessage', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer onConsoleMessageCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialFile: "test_assets/in_app_webview_on_console_message_test.html", onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onConsoleMessage: (controller, consoleMessage) { onConsoleMessageCompleter.complete(consoleMessage); }, ), ), ); final ConsoleMessage consoleMessage = await onConsoleMessageCompleter.future; expect(consoleMessage.message, 'message'); expect(consoleMessage.messageLevel, ConsoleMessageLevel.LOG); }); group("WebView Windows", () { testWidgets('onCreateWindow return false', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialFile: "test_assets/in_app_webview_on_create_window_test.html", initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, javaScriptCanOpenWindowsAutomatically: true, )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { if (url!.toString() == "https://flutter.dev/") { pageLoaded.complete(); } }, onCreateWindow: (controller, createNavigationAction) async { controller.loadUrl(urlRequest: createNavigationAction.request); return false; }, ), ), ); await expectLater(pageLoaded.future, completes); }); testWidgets('onCreateWindow return true', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer onCreateWindowCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialFile: "test_assets/in_app_webview_on_create_window_test.html", initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, javaScriptCanOpenWindowsAutomatically: true, ), android: AndroidInAppWebViewOptions(supportMultipleWindows: true)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onCreateWindow: (controller, createNavigationAction) async { onCreateWindowCompleter .complete(createNavigationAction.windowId); return true; }, ), ), ); var windowId = await onCreateWindowCompleter.future; final Completer windowControllerCompleter = Completer(); final Completer windowPageLoaded = Completer(); final Completer onCloseWindowCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), windowId: windowId, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, )), onWebViewCreated: (controller) { windowControllerCompleter.complete(controller); }, onLoadStop: (controller, url) async { if (url!.scheme != "about" && !windowPageLoaded.isCompleted) { windowPageLoaded.complete(url.toString()); await controller.evaluateJavascript( source: "window.close();"); } }, onCloseWindow: (controller) { onCloseWindowCompleter.complete(); }, ), ), ); final String windowUrlLoaded = await windowPageLoaded.future; expect(windowUrlLoaded, "https://flutter.dev/"); await expectLater(onCloseWindowCompleter.future, completes); }); }); testWidgets('onFindResultReceived', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer numberOfMatchesCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialFile: "test_assets/in_app_webview_initial_file_test.html", initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { controller.findAllAsync(find: "InAppWebViewInitialFileTest"); }, onFindResultReceived: (controller, int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting) async { if (isDoneCounting && !numberOfMatchesCompleter.isCompleted) { numberOfMatchesCompleter.complete(numberOfMatches); } }, ), ), ); final int numberOfMatches = await numberOfMatchesCompleter.future; expect(numberOfMatches, 2); }); testWidgets('onDownloadStartRequest', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer onDownloadStartCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ InAppWebViewOnDownloadStartTest

InAppWebViewOnDownloadStartTest

download file """), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, useOnDownloadStart: true)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onDownloadStartRequest: (controller, request) { onDownloadStartCompleter.complete(request.url.toString()); }, ), ), ); final String url = await onDownloadStartCompleter.future; expect(url, "http://${environment["NODE_SERVER_IP"]}:8082/test-download-file"); }); testWidgets('javascript dialogs', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final Completer alertCompleter = Completer(); final Completer confirmCompleter = Completer(); final Completer promptCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialFile: "test_assets/in_app_webview_on_js_dialog_test.html", initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); controller.addJavaScriptHandler( handlerName: 'confirm', callback: (args) { confirmCompleter.complete(args[0] as bool); }); controller.addJavaScriptHandler( handlerName: 'prompt', callback: (args) { promptCompleter.complete(args[0] as String); }); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, onJsAlert: (controller, jsAlertRequest) async { JsAlertResponseAction action = JsAlertResponseAction.CONFIRM; alertCompleter.complete(jsAlertRequest); return JsAlertResponse(handledByClient: true, action: action); }, onJsConfirm: (controller, jsConfirmRequest) async { JsConfirmResponseAction action = JsConfirmResponseAction.CONFIRM; return JsConfirmResponse(handledByClient: true, action: action); }, onJsPrompt: (controller, jsPromptRequest) async { JsPromptResponseAction action = JsPromptResponseAction.CONFIRM; return JsPromptResponse( handledByClient: true, action: action, value: 'new value'); }, ), ), ); await pageLoaded.future; final JsAlertRequest jsAlertRequest = await alertCompleter.future; expect(jsAlertRequest.message, 'alert message'); final bool onJsConfirmValue = await confirmCompleter.future; expect(onJsConfirmValue, true); final String onJsPromptValue = await promptCompleter.future; expect(onJsPromptValue, 'new value'); }); testWidgets('onLoadHttpError', (WidgetTester tester) async { final Completer errorUrlCompleter = Completer(); final Completer statusCodeCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://google.com/404')), onLoadHttpError: (controller, url, statusCode, description) async { errorUrlCompleter.complete(url.toString()); statusCodeCompleter.complete(statusCode); }, ), ), ); final String url = await errorUrlCompleter.future; final int code = await statusCodeCompleter.future; expect(url, 'https://google.com/404'); expect(code, 404); }); testWidgets('onLoadResourceCustomScheme', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer imageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialFile: "test_assets/in_app_webview_on_load_resource_custom_scheme_test.html", initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, resourceCustomSchemes: ["my-special-custom-scheme"])), onWebViewCreated: (controller) { controllerCompleter.complete(controller); controller.addJavaScriptHandler( handlerName: "imageLoaded", callback: (args) { imageLoaded.complete(); }); }, onLoadResourceCustomScheme: (controller, url) async { if (url.scheme == "my-special-custom-scheme") { var bytes = await rootBundle.load("test_assets/" + url .toString() .replaceFirst("my-special-custom-scheme://", "", 0)); var response = CustomSchemeResponse( data: bytes.buffer.asUint8List(), contentType: "image/svg+xml", contentEncoding: "utf-8"); return response; } return null; }, ), ), ); await expectLater(imageLoaded.future, completes); }); testWidgets('onLoadResource', (WidgetTester tester) async { List resourceList = [ "https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css", "https://code.jquery.com/jquery-3.3.1.min.js", "https://via.placeholder.com/100x50" ]; List resourceLoaded = []; final Completer loadedResourceCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialFile: "test_assets/in_app_webview_on_load_resource_test.html", initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, useOnLoadResource: true)), onLoadStop: (controller, url) { pageLoaded.complete(); }, onLoadResource: (controller, response) async { resourceLoaded.add(response.url!.toString()); if (resourceLoaded.length == resourceList.length) { loadedResourceCompleter.complete(); } }), ), ); await pageLoaded.future; await loadedResourceCompleter.future; expect(resourceLoaded, unorderedEquals(resourceList)); }); testWidgets('onUpdateVisitedHistory', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer firstPushCompleter = Completer(); final Completer secondPushCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse("https://flutter.dev/")), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions(clearCache: true)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, onUpdateVisitedHistory: (controller, url, androidIsReload) async { if (url!.toString().endsWith("second-push")) { secondPushCompleter.complete(); } else if (url.toString().endsWith("first-push")) { firstPushCompleter.complete(); } }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.evaluateJavascript(source: """ var state = {} var title = '' var url = 'first-push'; history.pushState(state, title, url); setTimeout(function() { var url = 'second-push'; history.pushState(state, title, url); }, 500); """); await firstPushCompleter.future; expect((await controller.getUrl())?.toString(), 'https://flutter.dev/first-push'); await secondPushCompleter.future; expect((await controller.getUrl())?.toString(), 'https://flutter.dev/second-push'); }); testWidgets('onProgressChanged', (WidgetTester tester) async { final Completer onProgressChangedCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, )), onProgressChanged: (controller, progress) { if (progress == 100) { onProgressChangedCompleter.complete(); } }, ), ), ); await expectLater(onProgressChangedCompleter.future, completes); }); testWidgets('androidOnSafeBrowsingHit', (WidgetTester tester) async { final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse('chrome://safe-browsing/match?type=malware')), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( // if I set javaScriptEnabled to true, it will crash! javaScriptEnabled: false, clearCache: true, ), android: AndroidInAppWebViewOptions( safeBrowsingEnabled: true, ), ), onWebViewCreated: (controller) { controller.android.startSafeBrowsing(); }, onLoadStop: (controller, url) { pageLoaded.complete(url!.toString()); }, androidOnSafeBrowsingHit: (controller, url, threatType) async { return SafeBrowsingResponse( report: true, action: SafeBrowsingResponseAction.PROCEED); }, ), ), ); final String url = await pageLoaded.future; expect(url, "chrome://safe-browsing/match?type=malware"); }, skip: defaultTargetPlatform != TargetPlatform.android); testWidgets('onScrollChanged', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final Completer onScrollChangedCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, onScrollChanged: (controller, x, y) { if (x == 0 && y == 500) { onScrollChangedCompleter.complete(); } }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; controller.scrollTo(x: 0, y: 500); await tester.pumpAndSettle(Duration(seconds: 1)); await expectLater(onScrollChangedCompleter.future, completes); }); testWidgets('SSL request', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse( "https://${environment["NODE_SERVER_IP"]}:4433/")), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, onReceivedServerTrustAuthRequest: (controller, challenge) async { return new ServerTrustAuthResponse( action: ServerTrustAuthResponseAction.PROCEED); }, onReceivedClientCertRequest: (controller, challenge) async { return new ClientCertResponse( certificatePath: "test_assets/certificate.pfx", certificatePassword: "", androidKeyStoreType: "PKCS12", action: ClientCertResponseAction.PROCEED); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; final String h1Content = await controller.evaluateJavascript( source: "document.body.querySelector('h1').textContent"); expect(h1Content, "Authorized"); }); testWidgets('onPrint', (WidgetTester tester) async { final Completer onPrintCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onLoadStop: (controller, url) async { await controller.evaluateJavascript(source: "window.print();"); }, onPrint: (controller, url) { onPrintCompleter.complete(url?.toString()); }, ), ), ); final String url = await onPrintCompleter.future; expect(url, 'https://github.com/flutter'); }, skip: true); testWidgets('onWindowFocus', (WidgetTester tester) async { final Completer onWindowFocusCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onLoadStop: (controller, url) async { await controller.evaluateJavascript( source: 'window.dispatchEvent(new Event("focus"));'); }, onWindowFocus: (controller) { onWindowFocusCompleter.complete(); }, ), ), ); await expectLater(onWindowFocusCompleter.future, completes); }); testWidgets('onWindowBlur', (WidgetTester tester) async { final Completer onWindowBlurCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onLoadStop: (controller, url) async { await controller.evaluateJavascript( source: 'window.dispatchEvent(new Event("blur"));'); }, onWindowBlur: (controller) { onWindowBlurCompleter.complete(); }, ), ), ); await expectLater(onWindowBlurCompleter.future, completes); }); testWidgets('onPageCommitVisible', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer onPageCommitVisibleCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onPageCommitVisible: (controller, url) { onPageCommitVisibleCompleter.complete(url?.toString()); }, ), ), ); final String? url = await onPageCommitVisibleCompleter.future; expect(url, 'https://github.com/flutter'); }); testWidgets('onTitleChanged', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final Completer onTitleChangedCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, onTitleChanged: (controller, title) { if (title == "title test") { onTitleChangedCompleter.complete(); } }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.evaluateJavascript( source: "document.title = 'title test';"); await expectLater(onTitleChangedCompleter.future, completes); }); testWidgets('onZoomScaleChanged', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final Completer onZoomScaleChangedCompleter = Completer(); var listenForScaleChange = false; await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, onZoomScaleChanged: (controller, oldScale, newScale) { if (listenForScaleChange) { onZoomScaleChangedCompleter.complete(); } }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; listenForScaleChange = true; await controller.zoomBy(zoomFactor: 2); await expectLater(onZoomScaleChangedCompleter.future, completes); }); testWidgets('androidOnPermissionRequest', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final Completer> onPermissionRequestCompleter = Completer>(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://permission.site/')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, androidOnPermissionRequest: (controller, origin, resources) async { onPermissionRequestCompleter.complete(resources); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.evaluateJavascript( source: "document.querySelector('#camera').click();"); final List resources = await onPermissionRequestCompleter.future; expect(listEquals(resources, ['android.webkit.resource.VIDEO_CAPTURE']), true); }, skip: defaultTargetPlatform != TargetPlatform.android); testWidgets('androidShouldInterceptRequest', (WidgetTester tester) async { List resourceList = [ "https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css", "https://code.jquery.com/jquery-3.3.1.min.js", "https://via.placeholder.com/100x50" ]; List resourceLoaded = []; final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final Completer loadedResourceCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ placeholder 100x50 """), initialOptions: InAppWebViewGroupOptions( android: AndroidInAppWebViewOptions( useShouldInterceptRequest: true)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, androidShouldInterceptRequest: (controller, request) async { resourceLoaded.add(request.url.toString()); if (resourceLoaded.length == resourceList.length) { loadedResourceCompleter.complete(); } return null; }, ), ), ); await pageLoaded.future; await loadedResourceCompleter.future; expect(resourceLoaded, containsAll(resourceList)); }, skip: defaultTargetPlatform != TargetPlatform.android); testWidgets('androidOnReceivedIcon', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final Completer onReceivedIconCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, androidOnReceivedIcon: (controller, icon) { onReceivedIconCompleter.complete(icon); }, ), ), ); await pageLoaded.future; final Uint8List icon = await onReceivedIconCompleter.future; expect(icon, isNotNull); }, skip: defaultTargetPlatform != TargetPlatform.android); testWidgets('androidOnReceivedTouchIconUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer onReceivedTouchIconUrlCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ """), initialOptions: InAppWebViewGroupOptions( android: AndroidInAppWebViewOptions( useShouldInterceptRequest: true)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, androidOnReceivedTouchIconUrl: (controller, url, precomposed) { onReceivedTouchIconUrlCompleter.complete(url.toString()); }, ), ), ); final String url = await onReceivedTouchIconUrlCompleter.future; expect(url, "https://placehold.it/72x72"); }, skip: defaultTargetPlatform != TargetPlatform.android); testWidgets('androidOnJsBeforeUnload', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final Completer onJsBeforeUnloadCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) async { await controller.evaluateJavascript(source: """ window.addEventListener('beforeunload', function (e) { e.preventDefault(); e.returnValue = ''; }); """); if (!pageLoaded.isCompleted) { pageLoaded.complete(); } }, androidOnJsBeforeUnload: (controller, jsBeforeUnloadRequest) async { onJsBeforeUnloadCompleter .complete(jsBeforeUnloadRequest.url.toString()); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.evaluateJavascript( source: "window.location.href = 'https://github.com/flutter';"); final String url = await onJsBeforeUnloadCompleter.future; expect(url, 'https://github.com/flutter'); }, skip: true /*defaultTargetPlatform != TargetPlatform.android*/); group("iosOnNavigationResponse", () { testWidgets('allow navigation', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final Completer onNavigationResponseCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), initialOptions: InAppWebViewGroupOptions( ios: IOSInAppWebViewOptions(useOnNavigationResponse: true)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, iosOnNavigationResponse: (controller, navigationResponse) async { onNavigationResponseCompleter .complete(navigationResponse.response!.url.toString()); return IOSNavigationResponseAction.ALLOW; }, ), ), ); await pageLoaded.future; final String url = await onNavigationResponseCompleter.future; expect(url, 'https://github.com/flutter'); }, skip: defaultTargetPlatform != TargetPlatform.iOS); testWidgets('cancel navigation', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final Completer onNavigationResponseCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), initialOptions: InAppWebViewGroupOptions( ios: IOSInAppWebViewOptions(useOnNavigationResponse: true)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, iosOnNavigationResponse: (controller, navigationResponse) async { onNavigationResponseCompleter .complete(navigationResponse.response!.url.toString()); return IOSNavigationResponseAction.CANCEL; }, ), ), ); final String url = await onNavigationResponseCompleter.future; expect(url, 'https://github.com/flutter'); expect(pageLoaded.future, doesNotComplete); }, skip: defaultTargetPlatform != TargetPlatform.iOS); }, skip: defaultTargetPlatform != TargetPlatform.iOS); testWidgets('initialUserScripts', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), initialUserScripts: UnmodifiableListView([ UserScript( source: "var foo = 49;", injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START), UserScript( source: "var foo2 = 19;", injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START, contentWorld: ContentWorld.PAGE), UserScript( source: "var bar = 2;", injectionTime: UserScriptInjectionTime.AT_DOCUMENT_END, contentWorld: ContentWorld.DEFAULT_CLIENT), UserScript( source: "var bar2 = 12;", injectionTime: UserScriptInjectionTime.AT_DOCUMENT_END, contentWorld: ContentWorld.world(name: "test")), ]), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) async { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; expect(await controller.evaluateJavascript(source: "foo;"), 49); expect(await controller.evaluateJavascript(source: "foo2;"), 19); expect( await controller.evaluateJavascript( source: "foo2;", contentWorld: ContentWorld.PAGE), 19); expect(await controller.evaluateJavascript(source: "bar;"), isNull); expect(await controller.evaluateJavascript(source: "bar2;"), isNull); expect( await controller.evaluateJavascript( source: "bar;", contentWorld: ContentWorld.DEFAULT_CLIENT), 2); expect( await controller.evaluateJavascript( source: "bar2;", contentWorld: ContentWorld.world(name: "test")), 12); }); group('POST requests', () { testWidgets('initialUrlRequest', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer postPageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse( "http://${environment["NODE_SERVER_IP"]}:8082/test-post"), method: 'POST', body: Uint8List.fromList(utf8.encode("name=FooBar")), headers: { 'Content-Type': 'application/x-www-form-urlencoded' }), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { postPageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await postPageLoaded.future; final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'http://${environment["NODE_SERVER_IP"]}:8082/test-post'); final String? pContent = await controller.evaluateJavascript( source: "document.querySelector('p').innerHTML;"); expect(pContent, "HELLO FooBar!"); }); testWidgets('loadUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer postPageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { if (url?.scheme != "about") { postPageLoaded.complete(); } }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; var postData = Uint8List.fromList(utf8.encode("name=FooBar")); await controller.loadUrl( urlRequest: URLRequest( url: Uri.parse( "http://${environment["NODE_SERVER_IP"]}:8082/test-post"), method: 'POST', body: postData, headers: { 'Content-Type': 'application/x-www-form-urlencoded' })); await postPageLoaded.future; final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'http://${environment["NODE_SERVER_IP"]}:8082/test-post'); final String? pContent = await controller.evaluateJavascript( source: "document.querySelector('p').innerHTML;"); expect(pContent, "HELLO FooBar!"); }); testWidgets('postUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer postPageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { if (url?.scheme != "about") { postPageLoaded.complete(); } }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; var postData = Uint8List.fromList(utf8.encode("name=FooBar")); await controller.postUrl( url: Uri.parse( "http://${environment["NODE_SERVER_IP"]}:8082/test-post"), postData: postData); await postPageLoaded.future; final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'http://${environment["NODE_SERVER_IP"]}:8082/test-post'); final String? pContent = await controller.evaluateJavascript( source: "document.querySelector('p').innerHTML;"); expect(pContent, "HELLO FooBar!"); }); }); testWidgets('loadData', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoads.stream.first; await controller.loadData( data: """ placeholder 100x50 """, encoding: 'utf-8', mimeType: 'text/html', historyUrl: Uri.parse("https://flutter.dev"), baseUrl: Uri.parse("https://flutter.dev")); await pageLoads.stream.first; final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'https://flutter.dev/'); pageLoads.close(); }); testWidgets('loadFile', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoads.stream.first; await controller.loadFile( assetFilePath: "test_assets/in_app_webview_initial_file_test.html"); await pageLoads.stream.first; final Uri? url = await controller.getUrl(); expect(url, isNotNull); expect(url!.scheme, 'file'); expect(url.path, endsWith("test_assets/in_app_webview_initial_file_test.html")); pageLoads.close(); }); testWidgets('reload', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; String? url = await pageLoads.stream.first; expect(url, 'https://github.com/flutter'); await controller.reload(); url = await pageLoads.stream.first; expect(url, 'https://github.com/flutter'); pageLoads.close(); }); testWidgets('web history - go back and forward', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev/')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; var url = await pageLoads.stream.first; var webHistory = await controller.getCopyBackForwardList(); expect(url, 'https://flutter.dev/'); expect(webHistory!.currentIndex, 0); expect(webHistory.list!.length, 1); expect(webHistory.list![0].url.toString(), 'https://flutter.dev/'); await controller.loadUrl( urlRequest: URLRequest(url: Uri.parse('https://github.com/flutter'))); url = await pageLoads.stream.first; webHistory = await controller.getCopyBackForwardList(); expect(url, 'https://github.com/flutter'); expect(await controller.canGoBack(), true); expect(await controller.canGoForward(), false); expect(await controller.canGoBackOrForward(steps: -1), true); expect(await controller.canGoBackOrForward(steps: 1), false); expect(webHistory!.currentIndex, 1); expect(webHistory.list!.length, 2); expect(webHistory.list![0].url.toString(), 'https://flutter.dev/'); expect(webHistory.list![1].url.toString(), 'https://github.com/flutter'); await Future.delayed(Duration(seconds: 1)); await controller.goBack(); url = await pageLoads.stream.first; webHistory = await controller.getCopyBackForwardList(); expect(url, 'https://flutter.dev/'); expect(await controller.canGoBack(), false); expect(await controller.canGoForward(), true); expect(await controller.canGoBackOrForward(steps: -1), false); expect(await controller.canGoBackOrForward(steps: 1), true); expect(webHistory!.currentIndex, 0); expect(webHistory.list!.length, 2); expect(webHistory.list![0].url.toString(), 'https://flutter.dev/'); expect(webHistory.list![1].url.toString(), 'https://github.com/flutter'); await Future.delayed(Duration(seconds: 1)); await controller.goForward(); url = await pageLoads.stream.first; webHistory = await controller.getCopyBackForwardList(); expect(url, 'https://github.com/flutter'); expect(await controller.canGoBack(), true); expect(await controller.canGoForward(), false); expect(await controller.canGoBackOrForward(steps: -1), true); expect(await controller.canGoBackOrForward(steps: 1), false); expect(webHistory!.currentIndex, 1); expect(webHistory.list!.length, 2); expect(webHistory.list![0].url.toString(), 'https://flutter.dev/'); expect(webHistory.list![1].url.toString(), 'https://github.com/flutter'); await Future.delayed(Duration(seconds: 1)); await controller.goTo(historyItem: webHistory.list![0]); url = await pageLoads.stream.first; webHistory = await controller.getCopyBackForwardList(); expect(url, 'https://flutter.dev/'); expect(await controller.canGoBack(), false); expect(await controller.canGoForward(), true); expect(await controller.canGoBackOrForward(steps: -1), false); expect(await controller.canGoBackOrForward(steps: 1), true); expect(webHistory!.currentIndex, 0); expect(webHistory.list!.length, 2); expect(webHistory.list![0].url.toString(), 'https://flutter.dev/'); expect(webHistory.list![1].url.toString(), 'https://github.com/flutter'); pageLoads.close(); }); testWidgets('getProgress', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; final int? progress = await controller.getProgress(); expect(progress, 100); }); testWidgets('getHtml', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; final String? html = await controller.getHtml(); expect(html, isNotNull); }); testWidgets('getFavicons', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; final List? favicons = await controller.getFavicons(); expect(favicons, isNotNull); expect(favicons, isNotEmpty); }); testWidgets('isLoading', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageStarted = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions(clearCache: true)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStart: (controller, url) { pageStarted.complete(); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageStarted.future; expect(await controller.isLoading(), true); await pageLoaded.future; expect(await controller.isLoading(), false); }); testWidgets('stopLoading', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions(clearCache: true)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStart: (controller, url) { controller.stopLoading(); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; if (defaultTargetPlatform == TargetPlatform.android) { await pageLoaded.future; expect(await controller.evaluateJavascript(source: "document.body"), isNullOrEmpty); } else if (defaultTargetPlatform == TargetPlatform.iOS) { expect(pageLoaded.future, doesNotComplete); } }); testWidgets('injectJavascriptFileFromUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final Completer jQueryLoaded = Completer(); final Completer jQueryLoadError = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.injectJavascriptFileFromUrl( urlFile: Uri.parse('https://www.notawebsite..com/jquery-3.3.1.min.js'), scriptHtmlTagAttributes: ScriptHtmlTagAttributes( id: 'jquery-error', onError: () { jQueryLoadError.complete(); }, )); await jQueryLoadError.future; expect( await controller.evaluateJavascript( source: "document.body.querySelector('#jquery-error') == null;"), false); expect( await controller.evaluateJavascript(source: "window.jQuery == null;"), true); await controller.injectJavascriptFileFromUrl( urlFile: Uri.parse('https://code.jquery.com/jquery-3.3.1.min.js'), scriptHtmlTagAttributes: ScriptHtmlTagAttributes( id: 'jquery', onLoad: () { jQueryLoaded.complete(); }, )); await jQueryLoaded.future; expect( await controller.evaluateJavascript( source: "document.body.querySelector('#jquery') == null;"), false); expect( await controller.evaluateJavascript(source: "window.jQuery == null;"), false); }); testWidgets('injectJavascriptFileFromAsset', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.injectJavascriptFileFromAsset( assetFilePath: 'test_assets/js/jquery-3.3.1.min.js'); expect( await controller.evaluateJavascript(source: "window.jQuery == null;"), false); }); testWidgets('injectCSSCode', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.injectCSSCode(source: """ body { background-color: rgb(0, 0, 255); } """); String? backgroundColor = await controller.evaluateJavascript(source: """ var element = document.body; var style = getComputedStyle(element); style.backgroundColor; """); expect(backgroundColor, 'rgb(0, 0, 255)'); }); testWidgets('injectCSSFileFromUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.injectCSSFileFromUrl( urlFile: Uri.parse( 'https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css'), cssLinkHtmlTagAttributes: CSSLinkHtmlTagAttributes(id: 'bootstrap')); await Future.delayed(Duration(seconds: 2)); expect( await controller.evaluateJavascript( source: "document.head.querySelector('#bootstrap') == null;"), false); }); testWidgets('injectCSSFileFromAsset', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.injectCSSFileFromAsset( assetFilePath: 'test_assets/css/blue-body.css'); String? backgroundColor = await controller.evaluateJavascript(source: """ var element = document.body; var style = getComputedStyle(element); style.backgroundColor; """); expect(backgroundColor, 'rgb(0, 0, 255)'); }); testWidgets('takeScreenshot', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; var screenshotConfiguration = ScreenshotConfiguration( compressFormat: CompressFormat.JPEG, quality: 20, rect: InAppWebViewRect(width: 100, height: 100, x: 50, y: 50)); var screenshot = await controller.takeScreenshot( screenshotConfiguration: screenshotConfiguration); expect(screenshot, isNotNull); }); testWidgets('clearCache', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await expectLater(controller.clearCache(), completes); }); testWidgets('T-Rex Runner game', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; var html = await controller.getTRexRunnerHtml(); var css = await controller.getTRexRunnerCss(); expect(html, isNotNull); expect(css, isNotNull); }); testWidgets('pause/resume timers', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('about:blank')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await controller.evaluateJavascript(source: """ var count = 0; setInterval(function() { count = count + 10; }, 20); """); await controller.pauseTimers(); await Future.delayed(Duration(seconds: 2)); await controller.resumeTimers(); expect( await controller.evaluateJavascript(source: "count;"), lessThan(50)); await Future.delayed(Duration(seconds: 4)); expect(await controller.evaluateJavascript(source: "count;"), greaterThan(50)); }); testWidgets('printCurrentPage', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await expectLater(controller.printCurrentPage(), completes); }, skip: true); testWidgets('getContentHeight', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; final contentHeight = await controller.getContentHeight(); expect(contentHeight, isNonZero); expect(contentHeight, isPositive); }); testWidgets('zoomBy', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await expectLater( controller.zoomBy(zoomFactor: 3.0, animated: true), completes); }); testWidgets('getZoomScale', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; final scale = await controller.getZoomScale(); expect(scale, isNonZero); expect(scale, isPositive); }); testWidgets('clearFocus', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await expectLater(controller.clearFocus(), completes); }); testWidgets('requestFocusNodeHref', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await expectLater(controller.requestFocusNodeHref(), completes); }); testWidgets('requestImageRef', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await expectLater(controller.requestImageRef(), completes); }); testWidgets('getMetaTags', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; List metaTags = await controller.getMetaTags(); expect(metaTags, isNotEmpty); }); testWidgets('getMetaThemeColor', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; expect(await controller.getMetaThemeColor(), isNotNull); }); testWidgets('getCertificate', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; var sslCertificate = await controller.getCertificate(); expect(sslCertificate, isNotNull); expect(sslCertificate!.x509Certificate, isNotNull); expect(sslCertificate.issuedBy, isNotNull); expect(sslCertificate.issuedTo, isNotNull); expect(sslCertificate.validNotAfterDate, isNotNull); expect(sslCertificate.validNotBeforeDate, isNotNull); }); testWidgets('add/remove user scripts', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoads.stream.first; var userScript1 = UserScript( source: "window.foo = 49;", injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START); var userScript2 = UserScript( source: "window.bar = 19;", injectionTime: UserScriptInjectionTime.AT_DOCUMENT_END); await controller.addUserScripts(userScripts: [userScript1, userScript2]); await controller.reload(); await pageLoads.stream.first; var value = await controller.evaluateJavascript(source: "window.foo;"); expect(value, 49); value = await controller.evaluateJavascript(source: "window.bar;"); expect(value, 19); await controller.removeUserScript(userScript: userScript1); await controller.reload(); await pageLoads.stream.first; value = await controller.evaluateJavascript(source: "window.foo;"); expect(value, isNull); value = await controller.evaluateJavascript(source: "window.bar;"); expect(value, 19); await controller.removeAllUserScripts(); await controller.reload(); await pageLoads.stream.first; value = await controller.evaluateJavascript(source: "window.foo;"); expect(value, isNull); value = await controller.evaluateJavascript(source: "window.bar;"); expect(value, isNull); pageLoads.close(); }); // testWidgets('saveWebArchive', (WidgetTester tester) async { // final Completer controllerCompleter = Completer(); // final Completer pageLoaded = Completer(); // // await tester.pumpWidget( // Directionality( // textDirection: TextDirection.ltr, // child: InAppWebView( // key: GlobalKey(), // initialUrlRequest: // URLRequest(url: Uri.parse('https://github.com/flutter')), // onWebViewCreated: (controller) { // controllerCompleter.complete(controller); // }, // onLoadStop: (controller, url) { // pageLoaded.complete(); // }, // ), // ), // ); // // final InAppWebViewController controller = // await controllerCompleter.future; // await pageLoaded.future; // // // wait a little bit after page load otherwise Android will not save the web archive // await Future.delayed(Duration(seconds: 1)); // // var supportDir = await getApplicationSupportDirectory(); // // var fileName = "flutter-website."; // if (defaultTargetPlatform == TargetPlatform.android) { // fileName = fileName + WebArchiveFormat.MHT.toValue(); // } else if (defaultTargetPlatform == TargetPlatform.iOS) { // fileName = fileName + WebArchiveFormat.WEBARCHIVE.toValue(); // } // // var fullPath = supportDir.path + Platform.pathSeparator + fileName; // var path = await controller.saveWebArchive(filePath: fullPath); // expect(path, isNotNull); // expect(path, endsWith(fileName)); // // path = await controller.saveWebArchive( // filePath: supportDir.path, autoname: true); // expect(path, isNotNull); // }); testWidgets('isSecureContext', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoads.stream.first; expect(await controller.isSecureContext(), true); await controller.loadUrl( urlRequest: URLRequest(url: Uri.parse('http://example.com/'))); await pageLoads.stream.first; expect(await controller.isSecureContext(), false); pageLoads.close(); }); test('getDefaultUserAgent', () async { expect(await InAppWebViewController.getDefaultUserAgent(), isNotNull); }); testWidgets('launches with pull-to-refresh feature', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final pullToRefreshController = PullToRefreshController( options: PullToRefreshOptions( color: Colors.blue, size: AndroidPullToRefreshSize.DEFAULT, backgroundColor: Colors.grey, enabled: true, slingshotDistance: 150, distanceToTriggerSync: 150, attributedTitle: IOSNSAttributedString(string: "test")), onRefresh: () {}, ); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), initialOptions: InAppWebViewGroupOptions( android: AndroidInAppWebViewOptions(useHybridComposition: true)), pullToRefreshController: pullToRefreshController, onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'https://github.com/flutter'); }); group('WebMessage', () { testWidgets('WebMessageChannel', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer webMessageCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ WebMessageChannel Test
"""), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onConsoleMessage: (controller, consoleMessage) { webMessageCompleter.complete(consoleMessage.message); }, onLoadStop: (controller, url) async { var webMessageChannel = await controller.createWebMessageChannel(); var port1 = webMessageChannel!.port1; var port2 = webMessageChannel.port2; await port1.setWebMessageCallback((message) async { await port1 .postMessage(WebMessage(data: message! + " and back")); }); await controller.postWebMessage( message: WebMessage(data: "capturePort", ports: [port2]), targetOrigin: Uri.parse("*")); await controller.evaluateJavascript( source: "document.getElementById('button').click();"); }, ), ), ); await controllerCompleter.future; final String message = await webMessageCompleter.future; expect(message, 'JavaScript To Native and back'); }); testWidgets('WebMessageListener', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final Completer webMessageCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), onWebViewCreated: (controller) async { await controller.addWebMessageListener(WebMessageListener( jsObjectName: "myTestObj", allowedOriginRules: Set.from(["https://*.example.com"]), onPostMessage: (message, sourceOrigin, isMainFrame, replyProxy) { assert( sourceOrigin.toString() == "https://www.example.com"); assert(isMainFrame); replyProxy.postMessage(message! + " and back"); }, )); controllerCompleter.complete(controller); }, onConsoleMessage: (controller, consoleMessage) { webMessageCompleter.complete(consoleMessage.message); }, onLoadStop: (controller, url) async { if (url.toString() == "https://www.example.com/") { pageLoaded.complete(); } }, ), ), ); final controller = await controllerCompleter.future; await controller.loadUrl( urlRequest: URLRequest(url: Uri.parse("https://www.example.com/"))); await pageLoaded.future; await controller.evaluateJavascript(source: """ myTestObj.addEventListener('message', function(event) { console.log(event.data); }); myTestObj.postMessage('JavaScript To Native'); """); final String message = await webMessageCompleter.future; expect(message, 'JavaScript To Native and back'); }); }); group('android methods', () { testWidgets('clearSslPreferences', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await expectLater(controller.android.clearSslPreferences(), completes); }, skip: defaultTargetPlatform != TargetPlatform.android); testWidgets('pause/resume', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await expectLater(controller.android.pause(), completes); await Future.delayed(Duration(seconds: 1)); await expectLater(controller.android.resume(), completes); }, skip: defaultTargetPlatform != TargetPlatform.android); testWidgets('getOriginalUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; var originUrl = (await controller.getOriginalUrl())?.toString(); expect(originUrl, 'https://github.com/flutter'); }); testWidgets('pageDown/pageUp', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; expect(await controller.android.pageDown(bottom: false), true); await Future.delayed(Duration(seconds: 1)); expect(await controller.android.pageUp(top: false), true); }, skip: defaultTargetPlatform != TargetPlatform.android); testWidgets('zoomIn/zoomOut', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; expect(await controller.android.zoomIn(), true); await Future.delayed(Duration(seconds: 1)); expect(await controller.android.zoomOut(), true); }, skip: defaultTargetPlatform != TargetPlatform.android); testWidgets('clearHistory', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController.broadcast(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoads.add(url!.toString()); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoads.stream.first; await controller.loadUrl( urlRequest: URLRequest(url: Uri.parse('https://github.com/flutter'))); await pageLoads.stream.first; var webHistory = await controller.getCopyBackForwardList(); expect(webHistory!.list!.length, 2); await controller.android.clearHistory(); webHistory = await controller.getCopyBackForwardList(); expect(webHistory!.list!.length, 1); pageLoads.close(); }, skip: defaultTargetPlatform != TargetPlatform.android); test('clearClientCertPreferences', () async { await expectLater( AndroidInAppWebViewController.clearClientCertPreferences(), completes); }, skip: defaultTargetPlatform != TargetPlatform.android); test('getSafeBrowsingPrivacyPolicyUrl', () async { expect( await AndroidInAppWebViewController .getSafeBrowsingPrivacyPolicyUrl(), isNotNull); }, skip: defaultTargetPlatform != TargetPlatform.android); test('setSafeBrowsingWhitelist', () async { expect( await AndroidInAppWebViewController.setSafeBrowsingWhitelist( hosts: ["flutter.dev", "github.com"]), true); }, skip: defaultTargetPlatform != TargetPlatform.android); test('getCurrentWebViewPackage', () async { expect(await AndroidInAppWebViewController.getCurrentWebViewPackage(), isNotNull); }, skip: defaultTargetPlatform != TargetPlatform.android); test('setWebContentsDebuggingEnabled', () async { expect( AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true), completes); }, skip: defaultTargetPlatform != TargetPlatform.android); }, skip: defaultTargetPlatform != TargetPlatform.android); group('ios methods', () { testWidgets('reloadFromOrigin', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; await expectLater(controller.ios.reloadFromOrigin(), completes); }, skip: defaultTargetPlatform != TargetPlatform.iOS); testWidgets('createPdf', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; var iosWKPdfConfiguration = IOSWKPDFConfiguration( rect: InAppWebViewRect(width: 100, height: 100, x: 50, y: 50)); var pdf = await controller.ios .createPdf(iosWKPdfConfiguration: iosWKPdfConfiguration); expect(pdf, isNotNull); }, skip: defaultTargetPlatform != TargetPlatform.iOS); testWidgets('createWebArchiveData', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) { pageLoaded.complete(); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; expect(await controller.ios.createWebArchiveData(), isNotNull); }, skip: defaultTargetPlatform != TargetPlatform.iOS); testWidgets('Apple Pay API enabled', (WidgetTester tester) async { final Completer pageLoaded = Completer(); final Completer alertMessageCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialData: InAppWebViewInitialData(data: """ Apple Pay API """), initialOptions: InAppWebViewGroupOptions( ios: IOSInAppWebViewOptions( applePayAPIEnabled: true, ), ), onLoadStop: (controller, url) { pageLoaded.complete(); }, onJsAlert: (controller, jsAlertRequest) async { alertMessageCompleter.complete(jsAlertRequest.message); }, ), ), ); await pageLoaded.future; final message = await alertMessageCompleter.future; expect(message, 'true'); }, skip: defaultTargetPlatform != TargetPlatform.iOS); test('handlesURLScheme', () async { expect(await IOSInAppWebViewController.handlesURLScheme("http"), true); expect(await IOSInAppWebViewController.handlesURLScheme("https"), true); }, skip: defaultTargetPlatform != TargetPlatform.iOS); }, skip: defaultTargetPlatform != TargetPlatform.iOS); }); group('Service Worker', () { testWidgets('shouldInterceptRequest', (WidgetTester tester) async { final Completer completer = Completer(); var swAvailable = await AndroidWebViewFeature.isFeatureSupported( AndroidWebViewFeature.SERVICE_WORKER_BASIC_USAGE); var swInterceptAvailable = await AndroidWebViewFeature.isFeatureSupported( AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST); if (swAvailable && swInterceptAvailable) { AndroidServiceWorkerController serviceWorkerController = AndroidServiceWorkerController.instance(); await serviceWorkerController .setServiceWorkerClient(AndroidServiceWorkerClient( shouldInterceptRequest: (request) async { if (!completer.isCompleted) { completer.complete(); } return null; }, )); } else { completer.complete(); } await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://mdn.github.io/sw-test/')), ), ), ); expect(completer.future, completes); }, skip: defaultTargetPlatform != TargetPlatform.android); testWidgets('setServiceWorkerClient to null', (WidgetTester tester) async { final Completer pageLoaded = Completer(); var swAvailable = await AndroidWebViewFeature.isFeatureSupported( AndroidWebViewFeature.SERVICE_WORKER_BASIC_USAGE); var swInterceptAvailable = await AndroidWebViewFeature.isFeatureSupported( AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST); if (swAvailable && swInterceptAvailable) { AndroidServiceWorkerController serviceWorkerController = AndroidServiceWorkerController.instance(); await serviceWorkerController.setServiceWorkerClient(null); } await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://mdn.github.io/sw-test/')), onLoadStop: (controller, url) { pageLoaded.complete(url!.toString()); }, ), ), ); final String url = await pageLoaded.future; expect(url, "https://mdn.github.io/sw-test/"); }, skip: defaultTargetPlatform != TargetPlatform.android); }); group('Cookie Manager', () { testWidgets('set, get, delete', (WidgetTester tester) async { CookieManager cookieManager = CookieManager.instance(); final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev/')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( clearCache: true, )), onLoadStop: (controller, url) { pageLoaded.complete(url!.toString()); }, ), ), ); final url = Uri.parse(await pageLoaded.future); await cookieManager.setCookie( url: url, name: "myCookie", value: "myValue"); List cookies = await cookieManager.getCookies(url: url); expect(cookies, isNotEmpty); Cookie? cookie = await cookieManager.getCookie(url: url, name: "myCookie"); expect(cookie?.value.toString(), "myValue"); await cookieManager.deleteCookie(url: url, name: "myCookie"); cookie = await cookieManager.getCookie(url: url, name: "myCookie"); expect(cookie, isNull); await cookieManager.deleteCookies(url: url); cookies = await cookieManager.getCookies(url: url); expect(cookies, isEmpty); }); }); group('HeadlessInAppWebView', () { test('run and dispose', () async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); var headlessWebView = new HeadlessInAppWebView( initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, ); headlessWebView.onLoadStop = (controller, url) async { pageLoaded.complete(); }; await headlessWebView.run(); expect(headlessWebView.isRunning(), true); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; final String? url = (await controller.getUrl())?.toString(); expect(url, 'https://github.com/flutter'); await headlessWebView.dispose(); expect(headlessWebView.isRunning(), false); }); test('take screenshot', () async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); var headlessWebView = new HeadlessInAppWebView( initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) async { pageLoaded.complete(); }); await headlessWebView.run(); expect(headlessWebView.isRunning(), true); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; final String? url = (await controller.getUrl())?.toString(); expect(url, 'https://github.com/flutter'); final Size? size = await headlessWebView.getSize(); expect(size, isNotNull); final Uint8List? screenshot = await controller.takeScreenshot(); expect(screenshot, isNotNull); await headlessWebView.dispose(); expect(headlessWebView.isRunning(), false); }); test('set and get custom size', () async { final Completer controllerCompleter = Completer(); var headlessWebView = new HeadlessInAppWebView( initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), initialSize: Size(600, 800), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, ); await headlessWebView.run(); expect(headlessWebView.isRunning(), true); final Size? size = await headlessWebView.getSize(); expect(size, isNotNull); expect(size, Size(600, 800)); await headlessWebView.setSize(Size(1080, 1920)); final Size? newSize = await headlessWebView.getSize(); expect(newSize, isNotNull); expect(newSize, Size(1080, 1920)); await headlessWebView.dispose(); expect(headlessWebView.isRunning(), false); }); test('set/get options', () async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); var headlessWebView = new HeadlessInAppWebView( initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions(javaScriptEnabled: false)), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, onLoadStop: (controller, url) async { pageLoaded.complete(); }, ); await headlessWebView.run(); final InAppWebViewController controller = await controllerCompleter.future; await pageLoaded.future; InAppWebViewGroupOptions? options = await controller.getOptions(); expect(options, isNotNull); expect(options!.crossPlatform.javaScriptEnabled, false); await controller.setOptions( options: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions(javaScriptEnabled: true))); options = await controller.getOptions(); expect(options, isNotNull); expect(options!.crossPlatform.javaScriptEnabled, true); }); }); group('InAppBrowser', () { test('openUrlRequest and close', () async { var inAppBrowser = new MyInAppBrowser(); expect(inAppBrowser.isOpened(), false); expect(() async { await inAppBrowser.show(); }, throwsA(isInstanceOf())); await inAppBrowser.openUrlRequest( urlRequest: URLRequest(url: Uri.parse("https://github.com/flutter"))); await inAppBrowser.browserCreated.future; expect(inAppBrowser.isOpened(), true); expect(() async { await inAppBrowser.openUrlRequest( urlRequest: URLRequest(url: Uri.parse("https://flutter.dev"))); }, throwsA(isInstanceOf())); await inAppBrowser.firstPageLoaded.future; var controller = inAppBrowser.webViewController; final String? url = (await controller.getUrl())?.toString(); expect(url, 'https://github.com/flutter'); await inAppBrowser.close(); expect(inAppBrowser.isOpened(), false); expect(() async => await inAppBrowser.webViewController.getUrl(), throwsA(isInstanceOf())); }); test('openFile and close', () async { var inAppBrowser = new MyInAppBrowser(); expect(inAppBrowser.isOpened(), false); expect(() async { await inAppBrowser.show(); }, throwsA(isInstanceOf())); await inAppBrowser.openFile( assetFilePath: "test_assets/in_app_webview_initial_file_test.html"); await inAppBrowser.browserCreated.future; expect(inAppBrowser.isOpened(), true); expect(() async { await inAppBrowser.openUrlRequest( urlRequest: URLRequest(url: Uri.parse("https://github.com/flutter"))); }, throwsA(isInstanceOf())); await inAppBrowser.firstPageLoaded.future; var controller = inAppBrowser.webViewController; final String? url = (await controller.getUrl())?.toString(); expect(url, endsWith("in_app_webview_initial_file_test.html")); await inAppBrowser.close(); expect(inAppBrowser.isOpened(), false); expect(() async => await inAppBrowser.webViewController.getUrl(), throwsA(isInstanceOf())); }); test('openData and close', () async { var inAppBrowser = new MyInAppBrowser(); expect(inAppBrowser.isOpened(), false); expect(() async { await inAppBrowser.show(); }, throwsA(isInstanceOf())); await inAppBrowser.openData( data: """ placeholder 100x50 """, encoding: 'utf-8', mimeType: 'text/html', androidHistoryUrl: Uri.parse("https://flutter.dev"), baseUrl: Uri.parse("https://flutter.dev")); await inAppBrowser.browserCreated.future; expect(inAppBrowser.isOpened(), true); expect(() async { await inAppBrowser.openUrlRequest( urlRequest: URLRequest(url: Uri.parse("https://github.com/flutter"))); }, throwsA(isInstanceOf())); await inAppBrowser.firstPageLoaded.future; var controller = inAppBrowser.webViewController; final String? url = (await controller.getUrl())?.toString(); expect(url, 'https://flutter.dev/'); await inAppBrowser.close(); expect(inAppBrowser.isOpened(), false); expect(() async => await inAppBrowser.webViewController.getUrl(), throwsA(isInstanceOf())); }); test('set/get options', () async { var inAppBrowser = new MyInAppBrowser(); await inAppBrowser.openUrlRequest( urlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), options: InAppBrowserClassOptions( crossPlatform: InAppBrowserOptions(hideToolbarTop: true))); await inAppBrowser.browserCreated.future; await inAppBrowser.firstPageLoaded.future; InAppBrowserClassOptions? options = await inAppBrowser.getOptions(); expect(options, isNotNull); expect(options!.crossPlatform.hideToolbarTop, true); await inAppBrowser.setOptions( options: InAppBrowserClassOptions( crossPlatform: InAppBrowserOptions(hideToolbarTop: false))); options = await inAppBrowser.getOptions(); expect(options, isNotNull); expect(options!.crossPlatform.hideToolbarTop, false); }); }); group('ChromeSafariBrowser', () { test('open and close', () async { var chromeSafariBrowser = new MyChromeSafariBrowser(); expect(chromeSafariBrowser.isOpened(), false); await chromeSafariBrowser.open( url: Uri.parse("https://github.com/flutter")); await chromeSafariBrowser.browserCreated.future; expect(chromeSafariBrowser.isOpened(), true); expect(() async { await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev")); }, throwsA(isInstanceOf())); await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes); await chromeSafariBrowser.close(); await chromeSafariBrowser.browserClosed.future; expect(chromeSafariBrowser.isOpened(), false); }); test('add custom menu item', () async { var chromeSafariBrowser = new MyChromeSafariBrowser(); chromeSafariBrowser.addMenuItem(ChromeSafariBrowserMenuItem( id: 2, label: 'Custom item menu 1', action: (url, title) { print('Custom item menu 1 clicked!'); })); expect(chromeSafariBrowser.isOpened(), false); await chromeSafariBrowser.open( url: Uri.parse("https://github.com/flutter")); await chromeSafariBrowser.browserCreated.future; expect(chromeSafariBrowser.isOpened(), true); expect(() async { await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev")); }, throwsA(isInstanceOf())); await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes); await chromeSafariBrowser.close(); await chromeSafariBrowser.browserClosed.future; expect(chromeSafariBrowser.isOpened(), false); }); group('Android Custom Tabs', () { test('add custom action button', () async { var chromeSafariBrowser = new MyChromeSafariBrowser(); var actionButtonIcon = await rootBundle.load('test_assets/images/flutter-logo.png'); chromeSafariBrowser.setActionButton(ChromeSafariBrowserActionButton( id: 1, description: 'Action Button description', icon: actionButtonIcon.buffer.asUint8List(), action: (url, title) { print('Action Button 1 clicked!'); })); expect(chromeSafariBrowser.isOpened(), false); await chromeSafariBrowser.open( url: Uri.parse("https://github.com/flutter")); await chromeSafariBrowser.browserCreated.future; expect(chromeSafariBrowser.isOpened(), true); expect(() async { await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev")); }, throwsA(isInstanceOf())); await expectLater( chromeSafariBrowser.firstPageLoaded.future, completes); await chromeSafariBrowser.close(); await chromeSafariBrowser.browserClosed.future; expect(chromeSafariBrowser.isOpened(), false); }); test('Custom Tabs single instance', () async { var chromeSafariBrowser = new MyChromeSafariBrowser(); expect(chromeSafariBrowser.isOpened(), false); await chromeSafariBrowser.open( url: Uri.parse("https://github.com/flutter"), options: ChromeSafariBrowserClassOptions( android: AndroidChromeCustomTabsOptions(isSingleInstance: true))); await chromeSafariBrowser.browserCreated.future; expect(chromeSafariBrowser.isOpened(), true); expect(() async { await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev")); }, throwsA(isInstanceOf())); await expectLater( chromeSafariBrowser.firstPageLoaded.future, completes); await chromeSafariBrowser.close(); await chromeSafariBrowser.browserClosed.future; expect(chromeSafariBrowser.isOpened(), false); }); test('Trusted Web Activity', () async { var chromeSafariBrowser = new MyChromeSafariBrowser(); expect(chromeSafariBrowser.isOpened(), false); await chromeSafariBrowser.open( url: Uri.parse("https://github.com/flutter"), options: ChromeSafariBrowserClassOptions( android: AndroidChromeCustomTabsOptions( isTrustedWebActivity: true))); await chromeSafariBrowser.browserCreated.future; expect(chromeSafariBrowser.isOpened(), true); expect(() async { await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev")); }, throwsA(isInstanceOf())); await expectLater( chromeSafariBrowser.firstPageLoaded.future, completes); await chromeSafariBrowser.close(); await chromeSafariBrowser.browserClosed.future; expect(chromeSafariBrowser.isOpened(), false); }); test('Trusted Web Activity single instance', () async { var chromeSafariBrowser = new MyChromeSafariBrowser(); expect(chromeSafariBrowser.isOpened(), false); await chromeSafariBrowser.open( url: Uri.parse("https://github.com/flutter"), options: ChromeSafariBrowserClassOptions( android: AndroidChromeCustomTabsOptions( isTrustedWebActivity: true, isSingleInstance: true))); await chromeSafariBrowser.browserCreated.future; expect(chromeSafariBrowser.isOpened(), true); expect(() async { await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev")); }, throwsA(isInstanceOf())); await expectLater( chromeSafariBrowser.firstPageLoaded.future, completes); await chromeSafariBrowser.close(); await chromeSafariBrowser.browserClosed.future; expect(chromeSafariBrowser.isOpened(), false); }); }, skip: defaultTargetPlatform != TargetPlatform.android); }); group('InAppLocalhostServer', () { final InAppLocalhostServer localhostServer = InAppLocalhostServer(); setUpAll(() async { await localhostServer.start(); }); testWidgets('load asset file', (WidgetTester tester) async { expect(localhostServer.isRunning(), true); final Completer controllerCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest( url: Uri.parse('http://localhost:8080/test_assets/index.html')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, ), ), ); final InAppWebViewController controller = await controllerCompleter.future; final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'http://localhost:8080/test_assets/index.html'); }); tearDownAll(() async { await localhostServer.close(); }); }); }