Skip to content
This repository has been archived by the owner on Aug 14, 2024. It is now read-only.

Commit

Permalink
[MSE-202] Create tests for WebView in the Ada chat and CareSDK (#18)
Browse files Browse the repository at this point in the history
* test: Add WebView mocks and tests

* test: Add WebView mocks and tests

* refactor: Small refactor

* test: Add some tests

* ci: Upgrade version. Update CHANGELOG.md
  • Loading branch information
slava-r-epam authored Jul 12, 2024
1 parent 064e489 commit 29a20b6
Show file tree
Hide file tree
Showing 9 changed files with 659 additions and 31 deletions.
4 changes: 4 additions & 0 deletions packages/ada_chat_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.1.1

- Refactor. Add more tests.

## 1.1.0

- Block Ada chat button on selected pages.
Expand Down
37 changes: 13 additions & 24 deletions packages/ada_chat_flutter/lib/src/ada_web_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ class AdaWebView extends StatefulWidget {
final void Function(String request, String response)? onLoadingError;

@override
State<AdaWebView> createState() => _AdaWebViewState();
State<AdaWebView> createState() => AdaWebViewState();

// coverage:ignore-start
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
Expand Down Expand Up @@ -135,9 +136,11 @@ class AdaWebView extends StatefulWidget {
properties.add(DoubleProperty('rolloutOverride', rolloutOverride));
properties.add(DiagnosticsProperty<bool>('testMode', testMode));
}
// coverage:ignore-end
}

class _AdaWebViewState extends State<AdaWebView> {
@visibleForTesting
class AdaWebViewState extends State<AdaWebView> {
late final WebViewController _controller;

@override
Expand Down Expand Up @@ -175,10 +178,10 @@ class _AdaWebViewState extends State<AdaWebView> {
onUrlChange: _onUrlChange,
onProgress: _onProgress,
onPageStarted: _onPageStarted,
onPageFinished: _onPageFinished,
onPageFinished: onPageFinished,
onHttpError: _onHttpError,
onWebResourceError: _onWebResourceError,
onNavigationRequest: _onNavigationRequest,
onNavigationRequest: onNavigationRequest,
),
);

Expand All @@ -188,12 +191,13 @@ class _AdaWebViewState extends State<AdaWebView> {
void _onConsoleMessage(JavaScriptConsoleMessage message) =>
widget.onConsoleMessage?.call(message.level.toString(), message.message);

FutureOr<NavigationDecision> _onNavigationRequest(NavigationRequest request) {
@visibleForTesting
FutureOr<NavigationDecision> onNavigationRequest(NavigationRequest request) {
final uri = Uri.parse(request.url);
log('AdaWebView:onNavigationRequest: '
'url=${uri.toString()}, isMainFrame=${request.isMainFrame}');

if (_isAdaChatLink(uri) || _isAdaSupportLink(uri) || _isBlankPage(uri)) {
if (isInternalAdaUrl(uri, widget.embedUri, widget.handle)) {
return NavigationDecision.navigate;
}

Expand All @@ -212,25 +216,18 @@ class _AdaWebViewState extends State<AdaWebView> {
return NavigationDecision.prevent;
}

bool _isBlankPage(Uri uri) => uri.toString() == 'about:blank';

bool _isAdaSupportLink(Uri uri) => uri.host == '${widget.handle}.ada.support';

bool _isAdaChatLink(Uri uri) => uri == widget.embedUri;

void _onWebResourceError(WebResourceError error) =>
log('AdaWebView:onWebResourceError: '
'errorCode=${error.errorCode}, '
'description=${error.description}');

void _onHttpError(HttpResponseError error) => widget.onLoadingError?.call(
error.request?.uri.toString() ?? '',
'uri=${error.response?.uri}, '
'statusCode=${error.response?.statusCode}, '
'headers=${error.response?.headers}, ',
'statusCode=${error.response?.statusCode}',
);

void _onPageFinished(String url) {
@visibleForTesting
void onPageFinished(String url) {
log('AdaWebView:onPageFinished: url=$url');

Future.delayed(Duration.zero, () async {
Expand Down Expand Up @@ -375,12 +372,4 @@ console.log("adaSettings: " + JSON.stringify(window.adaSettings));
},
);
}

dynamic jsonStrToMap(String message) {
if (message.isEmpty) {
return <String, dynamic>{};
}

return jsonDecode(message);
}
}
15 changes: 9 additions & 6 deletions packages/ada_chat_flutter/lib/src/customized_web_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ class CustomizedWebView extends StatefulWidget {
final BrowserSettings? browserSettings;

@override
State<CustomizedWebView> createState() => _CustomizedWebViewState();
State<CustomizedWebView> createState() => CustomizedWebViewState();
}

class _CustomizedWebViewState extends State<CustomizedWebView> {
@visibleForTesting
class CustomizedWebViewState extends State<CustomizedWebView> {
late final WebViewController _webViewController = WebViewController();
late final AdaButtonHide _adaButtonHide = AdaButtonHide(
webViewController: _webViewController,
Expand All @@ -37,9 +38,9 @@ class _CustomizedWebViewState extends State<CustomizedWebView> {
..setNavigationDelegate(
NavigationDelegate(
onUrlChange: _onUrlChange,
onProgress: _onProgress,
onProgress: onProgress,
onPageStarted: _onPageStarted,
onPageFinished: _onPageFinished,
onPageFinished: onPageFinished,
onNavigationRequest: _onNavigationRequest,
),
)
Expand All @@ -56,7 +57,8 @@ class _CustomizedWebViewState extends State<CustomizedWebView> {
void _onUrlChange(change) =>
log('CustomizedWebView:onUrlChange: url=${change.url}');

Future<void> _onPageFinished(String url) async {
@visibleForTesting
Future<void> onPageFinished(String url) async {
log('CustomizedWebView:onPageFinished: url=$url');

final pageController = widget.browserSettings?.control;
Expand Down Expand Up @@ -85,7 +87,8 @@ class _CustomizedWebViewState extends State<CustomizedWebView> {
await _adaButtonHide.maybeHideButton(url);
}

void _onProgress(int progress) {
@visibleForTesting
void onProgress(int progress) {
log('CustomizedWebView:onProgress: progress=$progress');

final pageController = widget.browserSettings?.control;
Expand Down
22 changes: 22 additions & 0 deletions packages/ada_chat_flutter/lib/src/utils.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:convert';

import 'package:flutter/foundation.dart';

String get getOsName {
Expand All @@ -9,3 +11,23 @@ String get getOsName {
return 'N/A';
}
}

bool isInternalAdaUrl(Uri uri, Uri embedUri, String handle) =>
isAdaChatLink(uri, embedUri) ||
isAdaSupportLink(uri, handle) ||
isBlankPage(uri);

bool isBlankPage(Uri uri) => uri.toString() == 'about:blank';

bool isAdaSupportLink(Uri uri, String handle) =>
uri.host == '$handle.ada.support';

bool isAdaChatLink(Uri uri, Uri embedUri) => uri == embedUri;

dynamic jsonStrToMap(String message) {
if (message.isEmpty) {
return <String, dynamic>{};
}

return json.decode(message);
}
3 changes: 2 additions & 1 deletion packages/ada_chat_flutter/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: ada_chat_flutter
description: Flutter implementation of Ada chat
version: 1.1.0
version: 1.1.1

environment:
sdk: '>=3.4.3 <4.0.0'
Expand All @@ -17,6 +17,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
mocktail: ^1.0.3
webview_flutter_platform_interface: ^2.10.0

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
Expand Down
150 changes: 150 additions & 0 deletions packages/ada_chat_flutter/test/src/ada_web_view_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import 'package:ada_chat_flutter/src/ada_controller.dart';
import 'package:ada_chat_flutter/src/ada_web_view.dart';
import 'package:ada_chat_flutter/src/browser_settings.dart';
import 'package:ada_chat_flutter/src/customized_web_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:webview_flutter/webview_flutter.dart';

import '../webview_mocks.dart';

class _MockAdaController extends Mock implements AdaController {}

const _title = '_title';
const _regexp = '_regexp';
const _handle = '_handle';
const _embedUri = 'https://example.com/embed.html';

final _browserSettings = BrowserSettings(
pageBuilder: (
context,
browser,
controller,
) =>
Column(
children: [
Text(_title),
browser,
],
),
adaHideUrls: [
RegExp(_regexp),
],
);

void main() {
late _MockAdaController mockAdaController;

setUp(() {
WebViewPlatform.instance = FakeWebViewPlatform();

mockAdaController = _MockAdaController();

when(() => mockAdaController.start()).thenAnswer((_) async {});
});

group('AdaWebView tests - ', () {
testWidgets(
'WHEN AdaWebView is pumped '
'THEN should show correct widgets',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: _buildAdaWebView(mockAdaController),
),
);

expect(
webViewCalls,
equals(['loadRequest: uri=$_embedUri']),
);
},
);

testWidgets(
'GIVEN the widget is pumped '
'WHEN onPageFinished is called '
'THEN should init and start Ada with correct params',
(WidgetTester tester) async {
await tester.runAsync(() async {
await tester.pumpWidget(
MaterialApp(
home: _buildAdaWebView(mockAdaController),
),
);

final state = tester.state<AdaWebViewState>(find.byType(AdaWebView));

state.onPageFinished(_embedUri);
await Future.delayed(Duration.zero);

verify(() => mockAdaController.start()).called(1);
expect(
webViewCalls,
contains('loadRequest: uri=https://example.com/embed.html'),
);
expect(
webViewCalls,
contains('addJavaScriptChannel: name=onLoaded'),
);
expect(
webViewCalls,
anyElement(contains('"metaFields":{"key":"value"')),
);
expect(
webViewCalls,
anyElement(
contains('{"handle":"$_handle","language":"en","cluster":'
'null,"domain":null,"hideMask":false'),
),
);
});
},
);

testWidgets(
'GIVEN the widget is pumped '
'WHEN onNavigationRequest is called for external page '
'THEN should show customized webview',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: _buildAdaWebView(mockAdaController),
),
);

final state = tester.state<AdaWebViewState>(find.byType(AdaWebView));

state.onNavigationRequest(
NavigationRequest(
url: 'https://external-page.com/index.html',
isMainFrame: true,
),
);
await tester.pumpAndSettle();

expect(find.text(_title), findsOneWidget);
expect(
find.byWidgetPredicate(
(w) =>
w is CustomizedWebView && w.browserSettings == _browserSettings,
),
findsOneWidget,
);
},
);
});
}

Widget _buildAdaWebView(_MockAdaController mockAdaController) {
return AdaWebView(
embedUri: Uri.parse(_embedUri),
handle: _handle,
controller: mockAdaController,
rolloutOverride: 1,
language: 'en',
metaFields: const {'key': 'value'},
browserSettings: _browserSettings,
);
}
Loading

0 comments on commit 29a20b6

Please sign in to comment.