0% found this document useful (0 votes)
411 views

Webview Flutter Test - Dart

The document contains tests for the WebView widget in Flutter. It tests functionality like: - Creating a WebView - Loading initial and different URLs - Handling invalid URLs - Controlling the WebView using a WebViewController to go back, forward, reload etc - Checking the current URL and navigation state
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
411 views

Webview Flutter Test - Dart

The document contains tests for the WebView widget in Flutter. It tests functionality like: - Creating a WebView - Loading initial and different URLs - Handling invalid URLs - Controlling the WebView using a WebViewController to go back, forward, reload etc - Checking the current URL and navigation state
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

// 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 s
table.
// 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 s
table.
// 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 s
table.
// 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 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(),
),
);

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 fai
ls as the
// widget's cache of current channel wasn't properly updated when updating j
avascriptChannels to
// null.
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(),
),
);

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(),
),
);

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 s
table.
// 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);
}
}

You might also like