blob: 358df898403aea29af2b747a99e9b1a812d06724 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart';
import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart';
import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart';
import 'web_kit_webview_widget_test.mocks.dart';
@GenerateMocks(<Type>[
WKNavigationDelegate,
WKScriptMessageHandler,
WKWebView,
WKWebViewConfiguration,
WKWebsiteDataStore,
WKUIDelegate,
WKUserContentController,
JavascriptChannelRegistry,
WebViewPlatformCallbacksHandler,
WebViewWidgetProxy,
])
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('$WebKitWebViewWidget', () {
late MockWKWebView mockWebView;
late MockWebViewWidgetProxy mockWebViewWidgetProxy;
late MockWKUserContentController mockUserContentController;
late MockWKWebViewConfiguration mockWebViewConfiguration;
late MockWKUIDelegate mockUIDelegate;
late MockWKWebsiteDataStore mockWebsiteDataStore;
late MockWKNavigationDelegate mockNavigationDelegate;
late MockWebViewPlatformCallbacksHandler mockCallbacksHandler;
late MockJavascriptChannelRegistry mockJavascriptChannelRegistry;
late WebKitWebViewPlatformController testController;
setUp(() {
mockWebView = MockWKWebView();
mockWebViewConfiguration = MockWKWebViewConfiguration();
mockUserContentController = MockWKUserContentController();
mockUIDelegate = MockWKUIDelegate();
mockWebsiteDataStore = MockWKWebsiteDataStore();
mockNavigationDelegate = MockWKNavigationDelegate();
mockWebViewWidgetProxy = MockWebViewWidgetProxy();
when(mockWebViewWidgetProxy.createWebView(any)).thenReturn(mockWebView);
when(mockWebViewWidgetProxy.createUIDelgate()).thenReturn(mockUIDelegate);
when(mockWebViewWidgetProxy.createNavigationDelegate())
.thenReturn(mockNavigationDelegate);
when(mockWebView.configuration).thenReturn(mockWebViewConfiguration);
when(mockWebViewConfiguration.userContentController).thenReturn(
mockUserContentController,
);
when(mockWebViewConfiguration.webSiteDataStore).thenReturn(
mockWebsiteDataStore,
);
mockCallbacksHandler = MockWebViewPlatformCallbacksHandler();
mockJavascriptChannelRegistry = MockJavascriptChannelRegistry();
});
// Builds a WebViewCupertinoWidget with default parameters.
Future<void> buildWidget(
WidgetTester tester, {
CreationParams? creationParams,
bool hasNavigationDelegate = false,
bool hasProgressTracking = false,
}) async {
await tester.pumpWidget(WebKitWebViewWidget(
creationParams: creationParams ??
CreationParams(
webSettings: WebSettings(
userAgent: const WebSetting<String?>.absent(),
hasNavigationDelegate: hasNavigationDelegate,
hasProgressTracking: hasProgressTracking,
)),
callbacksHandler: mockCallbacksHandler,
javascriptChannelRegistry: mockJavascriptChannelRegistry,
webViewProxy: mockWebViewWidgetProxy,
configuration: mockWebViewConfiguration,
onBuildWidget: (WebKitWebViewPlatformController controller) {
testController = controller;
return Container();
},
));
await tester.pumpAndSettle();
}
testWidgets('build $WebKitWebViewWidget', (WidgetTester tester) async {
await buildWidget(tester);
});
testWidgets('Requests to open a new window loads request in same window',
(WidgetTester tester) async {
await buildWidget(tester);
final dynamic onCreateWebView =
verify(mockUIDelegate.onCreateWebView = captureAny).captured.single
as void Function(WKWebViewConfiguration, WKNavigationAction);
const NSUrlRequest request = NSUrlRequest(url: 'https://google.com');
onCreateWebView(
mockWebViewConfiguration,
const WKNavigationAction(
request: request,
targetFrame: WKFrameInfo(isMainFrame: false),
),
);
verify(mockWebView.loadRequest(request));
});
group('$CreationParams', () {
testWidgets('autoMediaPlaybackPolicy true', (WidgetTester tester) async {
await buildWidget(
tester,
creationParams: CreationParams(
autoMediaPlaybackPolicy:
AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
webSettings: WebSettings(
userAgent: const WebSetting<String?>.absent(),
hasNavigationDelegate: false,
),
),
);
verify(
mockWebViewConfiguration.mediaTypesRequiringUserActionForPlayback =
<WKAudiovisualMediaType>{
WKAudiovisualMediaType.all,
});
});
testWidgets('autoMediaPlaybackPolicy false', (WidgetTester tester) async {
await buildWidget(
tester,
creationParams: CreationParams(
autoMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
webSettings: WebSettings(
userAgent: const WebSetting<String?>.absent(),
hasNavigationDelegate: false,
),
),
);
verify(
mockWebViewConfiguration.mediaTypesRequiringUserActionForPlayback =
<WKAudiovisualMediaType>{
WKAudiovisualMediaType.none,
});
});
testWidgets('javascriptChannelNames', (WidgetTester tester) async {
when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn(
MockWKScriptMessageHandler(),
);
await buildWidget(
tester,
creationParams: CreationParams(
javascriptChannelNames: <String>{'a', 'b'},
webSettings: WebSettings(
userAgent: const WebSetting<String?>.absent(),
hasNavigationDelegate: false,
),
),
);
final List<dynamic> javaScriptChannels = verify(
mockUserContentController.addScriptMessageHandler(
captureAny,
captureAny,
),
).captured;
expect(
javaScriptChannels[0],
isA<WKScriptMessageHandler>(),
);
expect(javaScriptChannels[1], 'a');
expect(
javaScriptChannels[2],
isA<WKScriptMessageHandler>(),
);
expect(javaScriptChannels[3], 'b');
});
group('$WebSettings', () {
testWidgets('allowsInlineMediaPlayback', (WidgetTester tester) async {
await buildWidget(
tester,
creationParams: CreationParams(
webSettings: WebSettings(
userAgent: const WebSetting<String?>.absent(),
allowsInlineMediaPlayback: true,
),
),
);
verify(mockWebViewConfiguration.allowsInlineMediaPlayback = true);
});
});
});
group('$WebKitWebViewPlatformController', () {
testWidgets('clearCache', (WidgetTester tester) async {
await buildWidget(tester);
await testController.clearCache();
verify(mockWebsiteDataStore.removeDataOfTypes(
<WKWebsiteDataTypes>{
WKWebsiteDataTypes.memoryCache,
WKWebsiteDataTypes.diskCache,
WKWebsiteDataTypes.offlineWebApplicationCache,
WKWebsiteDataTypes.localStroage,
},
DateTime.fromMillisecondsSinceEpoch(0),
));
});
testWidgets('addJavascriptChannels', (WidgetTester tester) async {
when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn(
MockWKScriptMessageHandler(),
);
await buildWidget(tester);
await testController.addJavascriptChannels(<String>{'c', 'd'});
final List<dynamic> javaScriptChannels = verify(
mockUserContentController.addScriptMessageHandler(
captureAny, captureAny),
).captured;
expect(
javaScriptChannels[0],
isA<WKScriptMessageHandler>(),
);
expect(javaScriptChannels[1], 'c');
expect(
javaScriptChannels[2],
isA<WKScriptMessageHandler>(),
);
expect(javaScriptChannels[3], 'd');
final List<WKUserScript> userScripts =
verify(mockUserContentController.addUserScript(captureAny))
.captured
.cast<WKUserScript>();
expect(userScripts[0].source, 'window.c = webkit.messageHandlers.c;');
expect(
userScripts[0].injectionTime,
WKUserScriptInjectionTime.atDocumentStart,
);
expect(userScripts[0].isMainFrameOnly, false);
expect(userScripts[1].source, 'window.d = webkit.messageHandlers.d;');
expect(
userScripts[1].injectionTime,
WKUserScriptInjectionTime.atDocumentStart,
);
expect(userScripts[0].isMainFrameOnly, false);
});
testWidgets('removeJavascriptChannels', (WidgetTester tester) async {
when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn(
MockWKScriptMessageHandler(),
);
await buildWidget(tester);
await testController.addJavascriptChannels(<String>{'c', 'd'});
reset(mockUserContentController);
await testController.removeJavascriptChannels(<String>{'c'});
verify(mockUserContentController.removeAllScriptMessageHandlers());
verify(mockUserContentController.removeAllUserScripts());
final List<dynamic> javaScriptChannels = verify(
mockUserContentController.addScriptMessageHandler(
captureAny, captureAny),
).captured;
expect(
javaScriptChannels[0],
isA<WKScriptMessageHandler>(),
);
expect(javaScriptChannels[1], 'd');
final List<WKUserScript> userScripts =
verify(mockUserContentController.addUserScript(captureAny))
.captured
.cast<WKUserScript>();
expect(userScripts[0].source, 'window.d = webkit.messageHandlers.d;');
expect(
userScripts[0].injectionTime,
WKUserScriptInjectionTime.atDocumentStart,
);
expect(userScripts[0].isMainFrameOnly, false);
});
});
group('$WebViewPlatformCallbacksHandler', () {
testWidgets('onPageStarted', (WidgetTester tester) async {
await buildWidget(tester);
final dynamic didStartProvisionalNavigation = verify(
mockNavigationDelegate.didStartProvisionalNavigation =
captureAny)
.captured
.single as void Function(WKWebView, String);
didStartProvisionalNavigation(mockWebView, 'https://google.com');
verify(mockCallbacksHandler.onPageStarted('https://google.com'));
});
testWidgets('onPageFinished', (WidgetTester tester) async {
await buildWidget(tester);
final dynamic didFinishNavigation =
verify(mockNavigationDelegate.didFinishNavigation = captureAny)
.captured
.single as void Function(WKWebView, String);
didFinishNavigation(mockWebView, 'https://google.com');
verify(mockCallbacksHandler.onPageFinished('https://google.com'));
});
testWidgets('onWebResourceError from didFailNavigation',
(WidgetTester tester) async {
await buildWidget(tester);
final dynamic didFailNavigation =
verify(mockNavigationDelegate.didFailNavigation = captureAny)
.captured
.single as void Function(WKWebView, NSError);
didFailNavigation(
mockWebView,
const NSError(
code: WKErrorCode.webViewInvalidated,
domain: 'domain',
localizedDescription: 'my desc',
),
);
final WebResourceError error =
verify(mockCallbacksHandler.onWebResourceError(captureAny))
.captured
.single as WebResourceError;
expect(error.description, 'my desc');
expect(error.errorCode, WKErrorCode.webViewInvalidated);
expect(error.domain, 'domain');
expect(error.errorType, WebResourceErrorType.webViewInvalidated);
});
testWidgets('onWebResourceError from didFailProvisionalNavigation',
(WidgetTester tester) async {
await buildWidget(tester);
final dynamic didFailProvisionalNavigation = verify(
mockNavigationDelegate.didFailProvisionalNavigation =
captureAny)
.captured
.single as void Function(WKWebView, NSError);
didFailProvisionalNavigation(
mockWebView,
const NSError(
code: WKErrorCode.webContentProcessTerminated,
domain: 'domain',
localizedDescription: 'my desc',
),
);
final WebResourceError error =
verify(mockCallbacksHandler.onWebResourceError(captureAny))
.captured
.single as WebResourceError;
expect(error.description, 'my desc');
expect(error.errorCode, WKErrorCode.webContentProcessTerminated);
expect(error.domain, 'domain');
expect(
error.errorType,
WebResourceErrorType.webContentProcessTerminated,
);
});
testWidgets(
'onWebResourceError from webViewWebContentProcessDidTerminate',
(WidgetTester tester) async {
await buildWidget(tester);
final dynamic webViewWebContentProcessDidTerminate = verify(
mockNavigationDelegate.webViewWebContentProcessDidTerminate =
captureAny)
.captured
.single as void Function(WKWebView);
webViewWebContentProcessDidTerminate(mockWebView);
final WebResourceError error =
verify(mockCallbacksHandler.onWebResourceError(captureAny))
.captured
.single as WebResourceError;
expect(error.description, '');
expect(error.errorCode, WKErrorCode.webContentProcessTerminated);
expect(error.domain, 'WKErrorDomain');
expect(
error.errorType,
WebResourceErrorType.webContentProcessTerminated,
);
});
testWidgets('onNavigationRequest from decidePolicyForNavigationAction',
(WidgetTester tester) async {
await buildWidget(tester, hasNavigationDelegate: true);
final dynamic decidePolicyForNavigationAction = verify(
mockNavigationDelegate.decidePolicyForNavigationAction =
captureAny)
.captured
.single
as Future<WKNavigationActionPolicy> Function(
WKWebView, WKNavigationAction);
when(mockCallbacksHandler.onNavigationRequest(
isForMainFrame: argThat(isFalse, named: 'isForMainFrame'),
url: 'https://google.com',
)).thenReturn(true);
expect(
decidePolicyForNavigationAction(
mockWebView,
const WKNavigationAction(
request: NSUrlRequest(url: 'https://google.com'),
targetFrame: WKFrameInfo(isMainFrame: false),
),
),
completion(WKNavigationActionPolicy.allow),
);
verify(mockCallbacksHandler.onNavigationRequest(
url: 'https://google.com',
isForMainFrame: false,
));
});
});
group('$JavascriptChannelRegistry', () {
testWidgets('onJavascriptChannelMessage', (WidgetTester tester) async {
when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn(
MockWKScriptMessageHandler(),
);
await buildWidget(tester);
await testController.addJavascriptChannels(<String>{'hello'});
final MockWKScriptMessageHandler messageHandler = verify(
mockUserContentController.addScriptMessageHandler(
captureAny, 'hello'))
.captured
.single as MockWKScriptMessageHandler;
final dynamic didReceiveScriptMessage =
verify(messageHandler.didReceiveScriptMessage = captureAny)
.captured
.single as void Function(
WKUserContentController userContentController,
WKScriptMessage message,
);
didReceiveScriptMessage(
mockUserContentController,
const WKScriptMessage(name: 'hello', body: 'A message.'),
);
verify(mockJavascriptChannelRegistry.onJavascriptChannelMessage(
'hello',
'A message.',
));
});
});
});
}