0% found this document useful (0 votes)
457 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
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
457 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
We take content rights seriously. If you suspect this is your content, claim it here.
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