Webview Flutter Test - Dart
Webview Flutter Test - Dart
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';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
setUpAll(() {
SystemChannels.platform_views.setMockMethodCallHandler(
fakePlatformViewsController.fakePlatformViewsMethodHandler);
SystemChannels.platform
.setMockMethodCallHandler(_fakeCookieManager.onMethodCall);
});
setUp(() {
fakePlatformViewsController.reset();
_fakeCookieManager.reset();
});
expect(platformWebView.javascriptMode, JavascriptMode.unrestricted);
expect(controller, isNotNull);
await controller.loadUrl('https://flutter.io');
expect(controller, isNotNull);
// Missing schema.
expect(() => controller.loadUrl('flutter.io'), throwsA(anything));
expect(await controller.currentUrl(), isNull);
});
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'));
});
expect(controller, isNotNull);
expect(canGoBackNoPageLoaded, false);
});
expect(controller, isNotNull);
expect(fakePlatformViewsController.lastCreatedView.hasCache, true);
await controller.clearCache();
expect(fakePlatformViewsController.lastCreatedView.hasCache, false);
});
expect(controller, isNotNull);
final bool canGoBackFirstPageLoaded = await controller.canGoBack();
expect(canGoBackFirstPageLoaded, false);
});
expect(controller, isNotNull);
await controller.loadUrl('https://www.google.com');
final bool canGoBackSecondPageLoaded = await controller.canGoBack();
expect(canGoBackSecondPageLoaded, true);
});
expect(controller, isNotNull);
expect(canGoForwardNoPageLoaded, false);
});
expect(controller, isNotNull);
final bool canGoForwardFirstPageLoaded = await controller.canGoForward();
expect(canGoForwardFirstPageLoaded, false);
});
expect(controller, isNotNull);
await controller.loadUrl('https://youtube.com');
await controller.goBack();
final bool canGoForwardFirstPageBacked = await controller.canGoForward();
expect(canGoForwardFirstPageBacked, true);
});
expect(controller, isNotNull);
await controller.loadUrl('https://flutter.io');
await controller.goBack();
expect(controller, isNotNull);
await controller.loadUrl('https://flutter.io');
await controller.goBack();
expect(await controller.currentUrl(), 'https://youtube.com');
await controller.goForward();
expect(controller, isNotNull);
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');
});
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);
});
expect(platformWebView.javascriptChannelNames,
unorderedEquals(<String>['Tts', 'Alarm']));
});
expect(createChannel('1Alarm'), throwsAssertionError);
expect(createChannel('foo.bar'), throwsAssertionError);
expect(createChannel(''), throwsAssertionError);
});
await tester.pumpWidget(
WebView(
initialUrl: 'https://youtube.com',
// TODO(iskakaushik): Remove this when collection literals makes it to s
table.
// 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(),
),
);
expect(platformWebView.javascriptChannelNames,
unorderedEquals(<String>['Tts', 'Alarm2', 'Alarm3']));
});
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 s
table.
// ignore: prefer_collection_literals
javascriptChannels: <JavascriptChannel>[
JavascriptChannel(
name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
].toSet(),
),
);
expect(platformWebView.javascriptChannelNames,
unorderedEquals(<String>['Tts']));
});
expect(ttsMessagesReceived, isEmpty);
expect(alarmMessagesReceived, isEmpty);
platformWebView.fakeJavascriptPostMessage('Tts', 'Hello');
platformWebView.fakeJavascriptPostMessage('Tts', '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;
},
));
platformWebView.fakeOnPageStartedCallback();
expect(platformWebView.currentUrl, returnedUrl);
});
// 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();
});
await tester.pumpWidget(WebView(
initialUrl: 'https://youtube.com',
onPageStarted: (String url) {},
));
await tester.pumpWidget(WebView(
initialUrl: 'https://youtube.com',
onPageStarted: (String url) {
returnedUrl = url;
},
));
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;
},
));
platformWebView.fakeOnPageFinishedCallback();
expect(platformWebView.currentUrl, returnedUrl);
});
// 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();
});
await tester.pumpWidget(WebView(
initialUrl: 'https://youtube.com',
onPageFinished: (String url) {},
));
await tester.pumpWidget(WebView(
initialUrl: 'https://youtube.com',
onPageFinished: (String url) {
returnedUrl = url;
},
));
platformWebView.fakeOnPageFinishedCallback();
expect(platformWebView.currentUrl, returnedUrl);
});
});
group('navigationDelegate', () {
testWidgets('hasNavigationDelegate', (WidgetTester tester) async {
await tester.pumpWidget(const WebView(
initialUrl: 'https://youtube.com',
));
expect(platformWebView.hasNavigationDelegate, false);
await tester.pumpWidget(WebView(
initialUrl: 'https://youtube.com',
navigationDelegate: (NavigationRequest r) => null,
));
expect(platformWebView.hasNavigationDelegate, true);
});
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;
}));
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,
));
expect(platformWebView.debuggingEnabled, true);
});
expect(platformWebView.debuggingEnabled, false);
});
expect(platformWebView.debuggingEnabled, true);
await tester.pumpWidget(WebView(
key: key,
debuggingEnabled: false,
));
expect(platformWebView.debuggingEnabled, false);
});
});
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>(),
)));
});
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,
));
expect(platformWebView.userAgent, isNull);
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;
bool hasNavigationDelegate;
bool debuggingEnabled;
String userAgent;
// 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();
ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
channel.name,
data,
(ByteData data) {},
);
}
void fakeOnPageFinishedCallback() {
final StandardMethodCodec codec = const StandardMethodCodec();
ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
channel.name,
data,
(ByteData data) {},
);
}
class _FakePlatformViewsController {
FakePlatformWebView lastCreatedView;
void reset() {
lastCreatedView = null;
}
}
class _FakeCookieManager {
_FakeCookieManager() {
final MethodChannel channel = const MethodChannel(
'plugins.flutter.io/cookie_manager',
StandardMethodCodec(),
);
channel.setMockMethodCallHandler(onMethodCall);
}
void reset() {
hasCookies = true;
}
}
@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);
}
}
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;
}
}
@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;
}
}
@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);
}
}