|
@@ -0,0 +1,1211 @@
|
|
|
|
|
+// Copyright 2018 The Chromium Authors. All rights reserved.
|
|
|
|
|
+// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
|
+// found in the LICENSE file.
|
|
|
|
|
+
|
|
|
|
|
+import 'dart:math';
|
|
|
|
|
+import 'dart:typed_data';
|
|
|
|
|
+
|
|
|
|
|
+import 'package:flutter/services.dart';
|
|
|
|
|
+import 'package:flutter/src/foundation/basic_types.dart';
|
|
|
|
|
+import 'package:flutter/src/gestures/recognizer.dart';
|
|
|
|
|
+import 'package:flutter/widgets.dart';
|
|
|
|
|
+import 'package:flutter_test/flutter_test.dart';
|
|
|
|
|
+import 'package:webview_flutter/platform_interface.dart';
|
|
|
|
|
+import 'package:webview_flutter/webview_flutter.dart';
|
|
|
|
|
+
|
|
|
|
|
+typedef void VoidCallback();
|
|
|
|
|
+
|
|
|
|
|
+void main() {
|
|
|
|
|
+ TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
|
+
|
|
|
|
|
+ final _FakePlatformViewsController fakePlatformViewsController =
|
|
|
|
|
+ _FakePlatformViewsController();
|
|
|
|
|
+
|
|
|
|
|
+ final _FakeCookieManager _fakeCookieManager = _FakeCookieManager();
|
|
|
|
|
+
|
|
|
|
|
+ setUpAll(() {
|
|
|
|
|
+ SystemChannels.platform_views.setMockMethodCallHandler(
|
|
|
|
|
+ fakePlatformViewsController.fakePlatformViewsMethodHandler);
|
|
|
|
|
+ SystemChannels.platform
|
|
|
|
|
+ .setMockMethodCallHandler(_fakeCookieManager.onMethodCall);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ setUp(() {
|
|
|
|
|
+ fakePlatformViewsController.reset();
|
|
|
|
|
+ _fakeCookieManager.reset();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Create WebView', (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(const WebView());
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Initial url', (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(await controller.currentUrl(), 'https://youtube.com');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Javascript mode', (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(const WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ javascriptMode: JavascriptMode.unrestricted,
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.javascriptMode, JavascriptMode.unrestricted);
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(const WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ javascriptMode: JavascriptMode.disabled,
|
|
|
|
|
+ ));
|
|
|
|
|
+ expect(platformWebView.javascriptMode, JavascriptMode.disabled);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Load url', (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+
|
|
|
|
|
+ await controller.loadUrl('https://flutter.io');
|
|
|
|
|
+
|
|
|
|
|
+ expect(await controller.currentUrl(), 'https://flutter.io');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Invalid urls', (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+
|
|
|
|
|
+ expect(() => controller.loadUrl(null), throwsA(anything));
|
|
|
|
|
+ expect(await controller.currentUrl(), isNull);
|
|
|
|
|
+
|
|
|
|
|
+ expect(() => controller.loadUrl(''), throwsA(anything));
|
|
|
|
|
+ expect(await controller.currentUrl(), isNull);
|
|
|
|
|
+
|
|
|
|
|
+ // Missing schema.
|
|
|
|
|
+ expect(() => controller.loadUrl('flutter.io'), throwsA(anything));
|
|
|
|
|
+ expect(await controller.currentUrl(), isNull);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Headers in loadUrl', (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+
|
|
|
|
|
+ final Map<String, String> headers = <String, String>{
|
|
|
|
|
+ 'CACHE-CONTROL': 'ABC'
|
|
|
|
|
+ };
|
|
|
|
|
+ await controller.loadUrl('https://flutter.io', headers: headers);
|
|
|
|
|
+ expect(await controller.currentUrl(), equals('https://flutter.io'));
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets("Can't go back before loading a page",
|
|
|
|
|
+ (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+
|
|
|
|
|
+ final bool canGoBackNoPageLoaded = await controller.canGoBack();
|
|
|
|
|
+
|
|
|
|
|
+ expect(canGoBackNoPageLoaded, false);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets("Clear Cache", (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+ expect(fakePlatformViewsController.lastCreatedView.hasCache, true);
|
|
|
|
|
+
|
|
|
|
|
+ await controller.clearCache();
|
|
|
|
|
+
|
|
|
|
|
+ expect(fakePlatformViewsController.lastCreatedView.hasCache, false);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets("Can't go back with no history", (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://flutter.io',
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+ final bool canGoBackFirstPageLoaded = await controller.canGoBack();
|
|
|
|
|
+
|
|
|
|
|
+ expect(canGoBackFirstPageLoaded, false);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Can go back', (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://flutter.io',
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+
|
|
|
|
|
+ await controller.loadUrl('https://www.google.com');
|
|
|
|
|
+ final bool canGoBackSecondPageLoaded = await controller.canGoBack();
|
|
|
|
|
+
|
|
|
|
|
+ expect(canGoBackSecondPageLoaded, true);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets("Can't go forward before loading a page",
|
|
|
|
|
+ (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+
|
|
|
|
|
+ final bool canGoForwardNoPageLoaded = await controller.canGoForward();
|
|
|
|
|
+
|
|
|
|
|
+ expect(canGoForwardNoPageLoaded, false);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets("Can't go forward with no history", (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://flutter.io',
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+ final bool canGoForwardFirstPageLoaded = await controller.canGoForward();
|
|
|
|
|
+
|
|
|
|
|
+ expect(canGoForwardFirstPageLoaded, false);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Can go forward', (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://flutter.io',
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+
|
|
|
|
|
+ await controller.loadUrl('https://youtube.com');
|
|
|
|
|
+ await controller.goBack();
|
|
|
|
|
+ final bool canGoForwardFirstPageBacked = await controller.canGoForward();
|
|
|
|
|
+
|
|
|
|
|
+ expect(canGoForwardFirstPageBacked, true);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Go back', (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+
|
|
|
|
|
+ expect(await controller.currentUrl(), 'https://youtube.com');
|
|
|
|
|
+
|
|
|
|
|
+ await controller.loadUrl('https://flutter.io');
|
|
|
|
|
+
|
|
|
|
|
+ expect(await controller.currentUrl(), 'https://flutter.io');
|
|
|
|
|
+
|
|
|
|
|
+ await controller.goBack();
|
|
|
|
|
+
|
|
|
|
|
+ expect(await controller.currentUrl(), 'https://youtube.com');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Go forward', (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+
|
|
|
|
|
+ expect(await controller.currentUrl(), 'https://youtube.com');
|
|
|
|
|
+
|
|
|
|
|
+ await controller.loadUrl('https://flutter.io');
|
|
|
|
|
+
|
|
|
|
|
+ expect(await controller.currentUrl(), 'https://flutter.io');
|
|
|
|
|
+
|
|
|
|
|
+ await controller.goBack();
|
|
|
|
|
+
|
|
|
|
|
+ expect(await controller.currentUrl(), 'https://youtube.com');
|
|
|
|
|
+
|
|
|
|
|
+ await controller.goForward();
|
|
|
|
|
+
|
|
|
|
|
+ expect(await controller.currentUrl(), 'https://flutter.io');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Current URL', (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ expect(controller, isNotNull);
|
|
|
|
|
+
|
|
|
|
|
+ // Test a WebView without an explicitly set first URL.
|
|
|
|
|
+ expect(await controller.currentUrl(), isNull);
|
|
|
|
|
+
|
|
|
|
|
+ await controller.loadUrl('https://youtube.com');
|
|
|
|
|
+ expect(await controller.currentUrl(), 'https://youtube.com');
|
|
|
|
|
+
|
|
|
|
|
+ await controller.loadUrl('https://flutter.io');
|
|
|
|
|
+ expect(await controller.currentUrl(), 'https://flutter.io');
|
|
|
|
|
+
|
|
|
|
|
+ await controller.goBack();
|
|
|
|
|
+ expect(await controller.currentUrl(), 'https://youtube.com');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Reload url', (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://flutter.io',
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.currentUrl, 'https://flutter.io');
|
|
|
|
|
+ expect(platformWebView.amountOfReloadsOnCurrentUrl, 0);
|
|
|
|
|
+
|
|
|
|
|
+ await controller.reload();
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.currentUrl, 'https://flutter.io');
|
|
|
|
|
+ expect(platformWebView.amountOfReloadsOnCurrentUrl, 1);
|
|
|
|
|
+
|
|
|
|
|
+ await controller.loadUrl('https://youtube.com');
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.amountOfReloadsOnCurrentUrl, 0);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('evaluate Javascript', (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://flutter.io',
|
|
|
|
|
+ javascriptMode: JavascriptMode.unrestricted,
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(
|
|
|
|
|
+ await controller.evaluateJavascript("fake js string"), "fake js string",
|
|
|
|
|
+ reason: 'should get the argument');
|
|
|
|
|
+ expect(
|
|
|
|
|
+ () => controller.evaluateJavascript(null),
|
|
|
|
|
+ throwsA(anything),
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('evaluate Javascript with JavascriptMode disabled',
|
|
|
|
|
+ (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://flutter.io',
|
|
|
|
|
+ javascriptMode: JavascriptMode.disabled,
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(
|
|
|
|
|
+ () => controller.evaluateJavascript('fake js string'),
|
|
|
|
|
+ throwsA(anything),
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(
|
|
|
|
|
+ () => controller.evaluateJavascript(null),
|
|
|
|
|
+ throwsA(anything),
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Cookies can be cleared once', (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ const WebView(
|
|
|
|
|
+ initialUrl: 'https://flutter.io',
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+ final CookieManager cookieManager = CookieManager();
|
|
|
|
|
+ final bool hasCookies = await cookieManager.clearCookies();
|
|
|
|
|
+ expect(hasCookies, true);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Second cookie clear does not have cookies',
|
|
|
|
|
+ (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ const WebView(
|
|
|
|
|
+ initialUrl: 'https://flutter.io',
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+ final CookieManager cookieManager = CookieManager();
|
|
|
|
|
+ final bool hasCookies = await cookieManager.clearCookies();
|
|
|
|
|
+ expect(hasCookies, true);
|
|
|
|
|
+ final bool hasCookiesSecond = await cookieManager.clearCookies();
|
|
|
|
|
+ expect(hasCookiesSecond, false);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Initial JavaScript channels', (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ // TODO(iskakaushik): Remove this when collection literals makes it to stable.
|
|
|
|
|
+ // ignore: prefer_collection_literals
|
|
|
|
|
+ javascriptChannels: <JavascriptChannel>[
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Alarm', onMessageReceived: (JavascriptMessage msg) {}),
|
|
|
|
|
+ ].toSet(),
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.javascriptChannelNames,
|
|
|
|
|
+ unorderedEquals(<String>['Tts', 'Alarm']));
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ test('Only valid JavaScript channel names are allowed', () {
|
|
|
|
|
+ final JavascriptMessageHandler noOp = (JavascriptMessage msg) {};
|
|
|
|
|
+ JavascriptChannel(name: 'Tts1', onMessageReceived: noOp);
|
|
|
|
|
+ JavascriptChannel(name: '_Alarm', onMessageReceived: noOp);
|
|
|
|
|
+ JavascriptChannel(name: 'foo_bar_', onMessageReceived: noOp);
|
|
|
|
|
+
|
|
|
|
|
+ VoidCallback createChannel(String name) {
|
|
|
|
|
+ return () {
|
|
|
|
|
+ JavascriptChannel(name: name, onMessageReceived: noOp);
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ expect(createChannel('1Alarm'), throwsAssertionError);
|
|
|
|
|
+ expect(createChannel('foo.bar'), throwsAssertionError);
|
|
|
|
|
+ expect(createChannel(''), throwsAssertionError);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Unique JavaScript channel names are required',
|
|
|
|
|
+ (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ // TODO(iskakaushik): Remove this when collection literals makes it to stable.
|
|
|
|
|
+ // ignore: prefer_collection_literals
|
|
|
|
|
+ javascriptChannels: <JavascriptChannel>[
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Alarm', onMessageReceived: (JavascriptMessage msg) {}),
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Alarm', onMessageReceived: (JavascriptMessage msg) {}),
|
|
|
|
|
+ ].toSet(),
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(tester.takeException(), isNot(null));
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('JavaScript channels update', (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ // TODO(iskakaushik): Remove this when collection literals makes it to stable.
|
|
|
|
|
+ // ignore: prefer_collection_literals
|
|
|
|
|
+ javascriptChannels: <JavascriptChannel>[
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Alarm', onMessageReceived: (JavascriptMessage msg) {}),
|
|
|
|
|
+ ].toSet(),
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ // TODO(iskakaushik): Remove this when collection literals makes it to stable.
|
|
|
|
|
+ // ignore: prefer_collection_literals
|
|
|
|
|
+ javascriptChannels: <JavascriptChannel>[
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Alarm2', onMessageReceived: (JavascriptMessage msg) {}),
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Alarm3', onMessageReceived: (JavascriptMessage msg) {}),
|
|
|
|
|
+ ].toSet(),
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.javascriptChannelNames,
|
|
|
|
|
+ unorderedEquals(<String>['Tts', 'Alarm2', 'Alarm3']));
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Remove all JavaScript channels and then add',
|
|
|
|
|
+ (WidgetTester tester) async {
|
|
|
|
|
+ // This covers a specific bug we had where after updating javascriptChannels to null,
|
|
|
|
|
+ // updating it again with a subset of the previously registered channels fails as the
|
|
|
|
|
+ // widget's cache of current channel wasn't properly updated when updating javascriptChannels to
|
|
|
|
|
+ // null.
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ // TODO(iskakaushik): Remove this when collection literals makes it to stable.
|
|
|
|
|
+ // ignore: prefer_collection_literals
|
|
|
|
|
+ javascriptChannels: <JavascriptChannel>[
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
|
|
|
|
|
+ ].toSet(),
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ const WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ // TODO(iskakaushik): Remove this when collection literals makes it to stable.
|
|
|
|
|
+ // ignore: prefer_collection_literals
|
|
|
|
|
+ javascriptChannels: <JavascriptChannel>[
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
|
|
|
|
|
+ ].toSet(),
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.javascriptChannelNames,
|
|
|
|
|
+ unorderedEquals(<String>['Tts']));
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('JavaScript channel messages', (WidgetTester tester) async {
|
|
|
|
|
+ final List<String> ttsMessagesReceived = <String>[];
|
|
|
|
|
+ final List<String> alarmMessagesReceived = <String>[];
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ // TODO(iskakaushik): Remove this when collection literals makes it to stable.
|
|
|
|
|
+ // ignore: prefer_collection_literals
|
|
|
|
|
+ javascriptChannels: <JavascriptChannel>[
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Tts',
|
|
|
|
|
+ onMessageReceived: (JavascriptMessage msg) {
|
|
|
|
|
+ ttsMessagesReceived.add(msg.message);
|
|
|
|
|
+ }),
|
|
|
|
|
+ JavascriptChannel(
|
|
|
|
|
+ name: 'Alarm',
|
|
|
|
|
+ onMessageReceived: (JavascriptMessage msg) {
|
|
|
|
|
+ alarmMessagesReceived.add(msg.message);
|
|
|
|
|
+ }),
|
|
|
|
|
+ ].toSet(),
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ expect(ttsMessagesReceived, isEmpty);
|
|
|
|
|
+ expect(alarmMessagesReceived, isEmpty);
|
|
|
|
|
+
|
|
|
|
|
+ platformWebView.fakeJavascriptPostMessage('Tts', 'Hello');
|
|
|
|
|
+ platformWebView.fakeJavascriptPostMessage('Tts', 'World');
|
|
|
|
|
+
|
|
|
|
|
+ expect(ttsMessagesReceived, <String>['Hello', 'World']);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ group('$PageStartedCallback', () {
|
|
|
|
|
+ testWidgets('onPageStarted is not null', (WidgetTester tester) async {
|
|
|
|
|
+ String returnedUrl;
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ onPageStarted: (String url) {
|
|
|
|
|
+ returnedUrl = url;
|
|
|
|
|
+ },
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ platformWebView.fakeOnPageStartedCallback();
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.currentUrl, returnedUrl);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('onPageStarted is null', (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(const WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ onPageStarted: null,
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ // The platform side will always invoke a call for onPageStarted. This is
|
|
|
|
|
+ // to test that it does not crash on a null callback.
|
|
|
|
|
+ platformWebView.fakeOnPageStartedCallback();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('onPageStarted changed', (WidgetTester tester) async {
|
|
|
|
|
+ String returnedUrl;
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ onPageStarted: (String url) {},
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ onPageStarted: (String url) {
|
|
|
|
|
+ returnedUrl = url;
|
|
|
|
|
+ },
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ platformWebView.fakeOnPageStartedCallback();
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.currentUrl, returnedUrl);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ group('$PageFinishedCallback', () {
|
|
|
|
|
+ testWidgets('onPageFinished is not null', (WidgetTester tester) async {
|
|
|
|
|
+ String returnedUrl;
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ onPageFinished: (String url) {
|
|
|
|
|
+ returnedUrl = url;
|
|
|
|
|
+ },
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ platformWebView.fakeOnPageFinishedCallback();
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.currentUrl, returnedUrl);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('onPageFinished is null', (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(const WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ onPageFinished: null,
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ // The platform side will always invoke a call for onPageFinished. This is
|
|
|
|
|
+ // to test that it does not crash on a null callback.
|
|
|
|
|
+ platformWebView.fakeOnPageFinishedCallback();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('onPageFinished changed', (WidgetTester tester) async {
|
|
|
|
|
+ String returnedUrl;
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ onPageFinished: (String url) {},
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ onPageFinished: (String url) {
|
|
|
|
|
+ returnedUrl = url;
|
|
|
|
|
+ },
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ platformWebView.fakeOnPageFinishedCallback();
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.currentUrl, returnedUrl);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ group('navigationDelegate', () {
|
|
|
|
|
+ testWidgets('hasNavigationDelegate', (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(const WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.hasNavigationDelegate, false);
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ navigationDelegate: (NavigationRequest r) => null,
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.hasNavigationDelegate, true);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('Block navigation', (WidgetTester tester) async {
|
|
|
|
|
+ final List<NavigationRequest> navigationRequests = <NavigationRequest>[];
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ navigationDelegate: (NavigationRequest request) {
|
|
|
|
|
+ navigationRequests.add(request);
|
|
|
|
|
+ // Only allow navigating to https://flutter.dev
|
|
|
|
|
+ return request.url == 'https://flutter.dev'
|
|
|
|
|
+ ? NavigationDecision.navigate
|
|
|
|
|
+ : NavigationDecision.prevent;
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.hasNavigationDelegate, true);
|
|
|
|
|
+
|
|
|
|
|
+ platformWebView.fakeNavigate('https://www.google.com');
|
|
|
|
|
+ // The navigation delegate only allows navigation to https://flutter.dev
|
|
|
|
|
+ // so we should still be in https://youtube.com.
|
|
|
|
|
+ expect(platformWebView.currentUrl, 'https://youtube.com');
|
|
|
|
|
+ expect(navigationRequests.length, 1);
|
|
|
|
|
+ expect(navigationRequests[0].url, 'https://www.google.com');
|
|
|
|
|
+ expect(navigationRequests[0].isForMainFrame, true);
|
|
|
|
|
+
|
|
|
|
|
+ platformWebView.fakeNavigate('https://flutter.dev');
|
|
|
|
|
+ await tester.pump();
|
|
|
|
|
+ expect(platformWebView.currentUrl, 'https://flutter.dev');
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ group('debuggingEnabled', () {
|
|
|
|
|
+ testWidgets('enable debugging', (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(const WebView(
|
|
|
|
|
+ debuggingEnabled: true,
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.debuggingEnabled, true);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('defaults to false', (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(const WebView());
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.debuggingEnabled, false);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('can be changed', (WidgetTester tester) async {
|
|
|
|
|
+ final GlobalKey key = GlobalKey();
|
|
|
|
|
+ await tester.pumpWidget(WebView(key: key));
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(WebView(
|
|
|
|
|
+ key: key,
|
|
|
|
|
+ debuggingEnabled: true,
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.debuggingEnabled, true);
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(WebView(
|
|
|
|
|
+ key: key,
|
|
|
|
|
+ debuggingEnabled: false,
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.debuggingEnabled, false);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ group('Custom platform implementation', () {
|
|
|
|
|
+ setUpAll(() {
|
|
|
|
|
+ WebView.platform = MyWebViewPlatform();
|
|
|
|
|
+ });
|
|
|
|
|
+ tearDownAll(() {
|
|
|
|
|
+ WebView.platform = null;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('creation', (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ const WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ gestureNavigationEnabled: true,
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ final MyWebViewPlatform builder = WebView.platform;
|
|
|
|
|
+ final MyWebViewPlatformController platform = builder.lastPlatformBuilt;
|
|
|
|
|
+
|
|
|
|
|
+ expect(
|
|
|
|
|
+ platform.creationParams,
|
|
|
|
|
+ MatchesCreationParams(CreationParams(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ webSettings: WebSettings(
|
|
|
|
|
+ javascriptMode: JavascriptMode.disabled,
|
|
|
|
|
+ hasNavigationDelegate: false,
|
|
|
|
|
+ debuggingEnabled: false,
|
|
|
|
|
+ userAgent: WebSetting<String>.of(null),
|
|
|
|
|
+ gestureNavigationEnabled: true,
|
|
|
|
|
+ ),
|
|
|
|
|
+ // TODO(iskakaushik): Remove this when collection literals makes it to stable.
|
|
|
|
|
+ // ignore: prefer_collection_literals
|
|
|
|
|
+ javascriptChannelNames: Set<String>(),
|
|
|
|
|
+ )));
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ testWidgets('loadUrl', (WidgetTester tester) async {
|
|
|
|
|
+ WebViewController controller;
|
|
|
|
|
+ await tester.pumpWidget(
|
|
|
|
|
+ WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
|
+ controller = webViewController;
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ final MyWebViewPlatform builder = WebView.platform;
|
|
|
|
|
+ final MyWebViewPlatformController platform = builder.lastPlatformBuilt;
|
|
|
|
|
+
|
|
|
|
|
+ final Map<String, String> headers = <String, String>{
|
|
|
|
|
+ 'header': 'value',
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ await controller.loadUrl('https://google.com', headers: headers);
|
|
|
|
|
+
|
|
|
|
|
+ expect(platform.lastUrlLoaded, 'https://google.com');
|
|
|
|
|
+ expect(platform.lastRequestHeaders, headers);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ testWidgets('Set UserAgent', (WidgetTester tester) async {
|
|
|
|
|
+ await tester.pumpWidget(const WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ javascriptMode: JavascriptMode.unrestricted,
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ final FakePlatformWebView platformWebView =
|
|
|
|
|
+ fakePlatformViewsController.lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.userAgent, isNull);
|
|
|
|
|
+
|
|
|
|
|
+ await tester.pumpWidget(const WebView(
|
|
|
|
|
+ initialUrl: 'https://youtube.com',
|
|
|
|
|
+ javascriptMode: JavascriptMode.unrestricted,
|
|
|
|
|
+ userAgent: 'UA',
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ expect(platformWebView.userAgent, 'UA');
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class FakePlatformWebView {
|
|
|
|
|
+ FakePlatformWebView(int id, Map<dynamic, dynamic> params) {
|
|
|
|
|
+ if (params.containsKey('initialUrl')) {
|
|
|
|
|
+ final String initialUrl = params['initialUrl'];
|
|
|
|
|
+ if (initialUrl != null) {
|
|
|
|
|
+ history.add(initialUrl);
|
|
|
|
|
+ currentPosition++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (params.containsKey('javascriptChannelNames')) {
|
|
|
|
|
+ javascriptChannelNames =
|
|
|
|
|
+ List<String>.from(params['javascriptChannelNames']);
|
|
|
|
|
+ }
|
|
|
|
|
+ javascriptMode = JavascriptMode.values[params['settings']['jsMode']];
|
|
|
|
|
+ hasNavigationDelegate =
|
|
|
|
|
+ params['settings']['hasNavigationDelegate'] ?? false;
|
|
|
|
|
+ debuggingEnabled = params['settings']['debuggingEnabled'];
|
|
|
|
|
+ userAgent = params['settings']['userAgent'];
|
|
|
|
|
+ channel = MethodChannel(
|
|
|
|
|
+ 'plugins.flutter.io/webview_$id', const StandardMethodCodec());
|
|
|
|
|
+ channel.setMockMethodCallHandler(onMethodCall);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ MethodChannel channel;
|
|
|
|
|
+
|
|
|
|
|
+ List<String> history = <String>[];
|
|
|
|
|
+ int currentPosition = -1;
|
|
|
|
|
+ int amountOfReloadsOnCurrentUrl = 0;
|
|
|
|
|
+ bool hasCache = true;
|
|
|
|
|
+
|
|
|
|
|
+ String get currentUrl => history.isEmpty ? null : history[currentPosition];
|
|
|
|
|
+ JavascriptMode javascriptMode;
|
|
|
|
|
+ List<String> javascriptChannelNames;
|
|
|
|
|
+
|
|
|
|
|
+ bool hasNavigationDelegate;
|
|
|
|
|
+ bool debuggingEnabled;
|
|
|
|
|
+ String userAgent;
|
|
|
|
|
+
|
|
|
|
|
+ Future<dynamic> onMethodCall(MethodCall call) {
|
|
|
|
|
+ switch (call.method) {
|
|
|
|
|
+ case 'loadUrl':
|
|
|
|
|
+ final Map<dynamic, dynamic> request = call.arguments;
|
|
|
|
|
+ _loadUrl(request['url']);
|
|
|
|
|
+ return Future<void>.sync(() {});
|
|
|
|
|
+ case 'updateSettings':
|
|
|
|
|
+ if (call.arguments['jsMode'] != null) {
|
|
|
|
|
+ javascriptMode = JavascriptMode.values[call.arguments['jsMode']];
|
|
|
|
|
+ }
|
|
|
|
|
+ if (call.arguments['hasNavigationDelegate'] != null) {
|
|
|
|
|
+ hasNavigationDelegate = call.arguments['hasNavigationDelegate'];
|
|
|
|
|
+ }
|
|
|
|
|
+ if (call.arguments['debuggingEnabled'] != null) {
|
|
|
|
|
+ debuggingEnabled = call.arguments['debuggingEnabled'];
|
|
|
|
|
+ }
|
|
|
|
|
+ userAgent = call.arguments['userAgent'];
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'canGoBack':
|
|
|
|
|
+ return Future<bool>.sync(() => currentPosition > 0);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'canGoForward':
|
|
|
|
|
+ return Future<bool>.sync(() => currentPosition < history.length - 1);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'goBack':
|
|
|
|
|
+ currentPosition = max(-1, currentPosition - 1);
|
|
|
|
|
+ return Future<void>.sync(() {});
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'goForward':
|
|
|
|
|
+ currentPosition = min(history.length - 1, currentPosition + 1);
|
|
|
|
|
+ return Future<void>.sync(() {});
|
|
|
|
|
+ case 'reload':
|
|
|
|
|
+ amountOfReloadsOnCurrentUrl++;
|
|
|
|
|
+ return Future<void>.sync(() {});
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'currentUrl':
|
|
|
|
|
+ return Future<String>.value(currentUrl);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'evaluateJavascript':
|
|
|
|
|
+ return Future<dynamic>.value(call.arguments);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'addJavascriptChannels':
|
|
|
|
|
+ final List<String> channelNames = List<String>.from(call.arguments);
|
|
|
|
|
+ javascriptChannelNames.addAll(channelNames);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'removeJavascriptChannels':
|
|
|
|
|
+ final List<String> channelNames = List<String>.from(call.arguments);
|
|
|
|
|
+ javascriptChannelNames
|
|
|
|
|
+ .removeWhere((String channel) => channelNames.contains(channel));
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'clearCache':
|
|
|
|
|
+ hasCache = false;
|
|
|
|
|
+ return Future<void>.sync(() {});
|
|
|
|
|
+ }
|
|
|
|
|
+ return Future<void>.sync(() {});
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void fakeJavascriptPostMessage(String jsChannel, String message) {
|
|
|
|
|
+ final StandardMethodCodec codec = const StandardMethodCodec();
|
|
|
|
|
+ final Map<String, dynamic> arguments = <String, dynamic>{
|
|
|
|
|
+ 'channel': jsChannel,
|
|
|
|
|
+ 'message': message
|
|
|
|
|
+ };
|
|
|
|
|
+ final ByteData data = codec
|
|
|
|
|
+ .encodeMethodCall(MethodCall('javascriptChannelMessage', arguments));
|
|
|
|
|
+ ServicesBinding.instance.defaultBinaryMessenger
|
|
|
|
|
+ .handlePlatformMessage(channel.name, data, (ByteData data) {});
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Fakes a main frame navigation that was initiated by the webview, e.g when
|
|
|
|
|
+ // the user clicks a link in the currently loaded page.
|
|
|
|
|
+ void fakeNavigate(String url) {
|
|
|
|
|
+ if (!hasNavigationDelegate) {
|
|
|
|
|
+ print('no navigation delegate');
|
|
|
|
|
+ _loadUrl(url);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ final StandardMethodCodec codec = const StandardMethodCodec();
|
|
|
|
|
+ final Map<String, dynamic> arguments = <String, dynamic>{
|
|
|
|
|
+ 'url': url,
|
|
|
|
|
+ 'isForMainFrame': true
|
|
|
|
|
+ };
|
|
|
|
|
+ final ByteData data =
|
|
|
|
|
+ codec.encodeMethodCall(MethodCall('navigationRequest', arguments));
|
|
|
|
|
+ ServicesBinding.instance.defaultBinaryMessenger
|
|
|
|
|
+ .handlePlatformMessage(channel.name, data, (ByteData data) {
|
|
|
|
|
+ final bool allow = codec.decodeEnvelope(data);
|
|
|
|
|
+ if (allow) {
|
|
|
|
|
+ _loadUrl(url);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void fakeOnPageStartedCallback() {
|
|
|
|
|
+ final StandardMethodCodec codec = const StandardMethodCodec();
|
|
|
|
|
+
|
|
|
|
|
+ final ByteData data = codec.encodeMethodCall(MethodCall(
|
|
|
|
|
+ 'onPageStarted',
|
|
|
|
|
+ <dynamic, dynamic>{'url': currentUrl},
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
|
|
|
|
|
+ channel.name,
|
|
|
|
|
+ data,
|
|
|
|
|
+ (ByteData data) {},
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void fakeOnPageFinishedCallback() {
|
|
|
|
|
+ final StandardMethodCodec codec = const StandardMethodCodec();
|
|
|
|
|
+
|
|
|
|
|
+ final ByteData data = codec.encodeMethodCall(MethodCall(
|
|
|
|
|
+ 'onPageFinished',
|
|
|
|
|
+ <dynamic, dynamic>{'url': currentUrl},
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
|
|
|
|
|
+ channel.name,
|
|
|
|
|
+ data,
|
|
|
|
|
+ (ByteData data) {},
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void _loadUrl(String url) {
|
|
|
|
|
+ history = history.sublist(0, currentPosition + 1);
|
|
|
|
|
+ history.add(url);
|
|
|
|
|
+ currentPosition++;
|
|
|
|
|
+ amountOfReloadsOnCurrentUrl = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class _FakePlatformViewsController {
|
|
|
|
|
+ FakePlatformWebView lastCreatedView;
|
|
|
|
|
+
|
|
|
|
|
+ Future<dynamic> fakePlatformViewsMethodHandler(MethodCall call) {
|
|
|
|
|
+ switch (call.method) {
|
|
|
|
|
+ case 'create':
|
|
|
|
|
+ final Map<dynamic, dynamic> args = call.arguments;
|
|
|
|
|
+ final Map<dynamic, dynamic> params = _decodeParams(args['params']);
|
|
|
|
|
+ lastCreatedView = FakePlatformWebView(
|
|
|
|
|
+ args['id'],
|
|
|
|
|
+ params,
|
|
|
|
|
+ );
|
|
|
|
|
+ return Future<int>.sync(() => 1);
|
|
|
|
|
+ default:
|
|
|
|
|
+ return Future<void>.sync(() {});
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void reset() {
|
|
|
|
|
+ lastCreatedView = null;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+Map<dynamic, dynamic> _decodeParams(Uint8List paramsMessage) {
|
|
|
|
|
+ final ByteBuffer buffer = paramsMessage.buffer;
|
|
|
|
|
+ final ByteData messageBytes = buffer.asByteData(
|
|
|
|
|
+ paramsMessage.offsetInBytes,
|
|
|
|
|
+ paramsMessage.lengthInBytes,
|
|
|
|
|
+ );
|
|
|
|
|
+ return const StandardMessageCodec().decodeMessage(messageBytes);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class _FakeCookieManager {
|
|
|
|
|
+ _FakeCookieManager() {
|
|
|
|
|
+ final MethodChannel channel = const MethodChannel(
|
|
|
|
|
+ 'plugins.flutter.io/cookie_manager',
|
|
|
|
|
+ StandardMethodCodec(),
|
|
|
|
|
+ );
|
|
|
|
|
+ channel.setMockMethodCallHandler(onMethodCall);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool hasCookies = true;
|
|
|
|
|
+
|
|
|
|
|
+ Future<bool> onMethodCall(MethodCall call) {
|
|
|
|
|
+ switch (call.method) {
|
|
|
|
|
+ case 'clearCookies':
|
|
|
|
|
+ bool hadCookies = false;
|
|
|
|
|
+ if (hasCookies) {
|
|
|
|
|
+ hadCookies = true;
|
|
|
|
|
+ hasCookies = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ return Future<bool>.sync(() {
|
|
|
|
|
+ return hadCookies;
|
|
|
|
|
+ });
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ return Future<bool>.sync(() => null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void reset() {
|
|
|
|
|
+ hasCookies = true;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class MyWebViewPlatform implements WebViewPlatform {
|
|
|
|
|
+ MyWebViewPlatformController lastPlatformBuilt;
|
|
|
|
|
+
|
|
|
|
|
+ @override
|
|
|
|
|
+ Widget build({
|
|
|
|
|
+ BuildContext context,
|
|
|
|
|
+ CreationParams creationParams,
|
|
|
|
|
+ @required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
|
|
|
|
|
+ @required WebViewPlatformCreatedCallback onWebViewPlatformCreated,
|
|
|
|
|
+ Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
|
|
|
|
|
+ }) {
|
|
|
|
|
+ assert(onWebViewPlatformCreated != null);
|
|
|
|
|
+ lastPlatformBuilt = MyWebViewPlatformController(
|
|
|
|
|
+ creationParams, gestureRecognizers, webViewPlatformCallbacksHandler);
|
|
|
|
|
+ onWebViewPlatformCreated(lastPlatformBuilt);
|
|
|
|
|
+ return Container();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @override
|
|
|
|
|
+ Future<bool> clearCookies() {
|
|
|
|
|
+ return Future<bool>.sync(() => null);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class MyWebViewPlatformController extends WebViewPlatformController {
|
|
|
|
|
+ MyWebViewPlatformController(this.creationParams, this.gestureRecognizers,
|
|
|
|
|
+ WebViewPlatformCallbacksHandler platformHandler)
|
|
|
|
|
+ : super(platformHandler);
|
|
|
|
|
+
|
|
|
|
|
+ CreationParams creationParams;
|
|
|
|
|
+ Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers;
|
|
|
|
|
+
|
|
|
|
|
+ String lastUrlLoaded;
|
|
|
|
|
+ Map<String, String> lastRequestHeaders;
|
|
|
|
|
+
|
|
|
|
|
+ @override
|
|
|
|
|
+ Future<void> loadUrl(String url, Map<String, String> headers) {
|
|
|
|
|
+ equals(1, 1);
|
|
|
|
|
+ lastUrlLoaded = url;
|
|
|
|
|
+ lastRequestHeaders = headers;
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class MatchesWebSettings extends Matcher {
|
|
|
|
|
+ MatchesWebSettings(this._webSettings);
|
|
|
|
|
+
|
|
|
|
|
+ final WebSettings _webSettings;
|
|
|
|
|
+
|
|
|
|
|
+ @override
|
|
|
|
|
+ Description describe(Description description) =>
|
|
|
|
|
+ description.add('$_webSettings');
|
|
|
|
|
+
|
|
|
|
|
+ @override
|
|
|
|
|
+ bool matches(
|
|
|
|
|
+ covariant WebSettings webSettings, Map<dynamic, dynamic> matchState) {
|
|
|
|
|
+ return _webSettings.javascriptMode == webSettings.javascriptMode &&
|
|
|
|
|
+ _webSettings.hasNavigationDelegate ==
|
|
|
|
|
+ webSettings.hasNavigationDelegate &&
|
|
|
|
|
+ _webSettings.debuggingEnabled == webSettings.debuggingEnabled &&
|
|
|
|
|
+ _webSettings.gestureNavigationEnabled ==
|
|
|
|
|
+ webSettings.gestureNavigationEnabled &&
|
|
|
|
|
+ _webSettings.userAgent == webSettings.userAgent;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class MatchesCreationParams extends Matcher {
|
|
|
|
|
+ MatchesCreationParams(this._creationParams);
|
|
|
|
|
+
|
|
|
|
|
+ final CreationParams _creationParams;
|
|
|
|
|
+
|
|
|
|
|
+ @override
|
|
|
|
|
+ Description describe(Description description) =>
|
|
|
|
|
+ description.add('$_creationParams');
|
|
|
|
|
+
|
|
|
|
|
+ @override
|
|
|
|
|
+ bool matches(covariant CreationParams creationParams,
|
|
|
|
|
+ Map<dynamic, dynamic> matchState) {
|
|
|
|
|
+ return _creationParams.initialUrl == creationParams.initialUrl &&
|
|
|
|
|
+ MatchesWebSettings(_creationParams.webSettings)
|
|
|
|
|
+ .matches(creationParams.webSettings, matchState) &&
|
|
|
|
|
+ orderedEquals(_creationParams.javascriptChannelNames)
|
|
|
|
|
+ .matches(creationParams.javascriptChannelNames, matchState);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|