Skip to content

Commit

Permalink
[webview_flutter_wkwebview] Fixes bug where an NSError not an `NSEr…
Browse files Browse the repository at this point in the history
…rorData` was returned (flutter#5973)
  • Loading branch information
bparrishMines authored and yutaaraki-toydium committed Aug 12, 2022
1 parent a1af363 commit c673a9b
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -351,4 +351,41 @@ - (void)testEvaluateJavaScript {
XCTAssertEqualObjects(returnValue, @"result");
XCTAssertNil(returnError);
}

- (void)testEvaluateJavaScriptReturnsNSErrorData {
FWFWebView *mockWebView = OCMClassMock([FWFWebView class]);

OCMStub([mockWebView
evaluateJavaScript:@"runJavaScript"
completionHandler:([OCMArg invokeBlockWithArgs:[NSNull null],
[NSError errorWithDomain:@"errorDomain"
code:0
userInfo:@{
NSLocalizedDescriptionKey :
@"description"
}],
nil])]);

FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:0];

FWFWebViewHostApiImpl *hostAPI =
[[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager];

NSString __block *returnValue;
FlutterError __block *returnError;
[hostAPI evaluateJavaScriptForWebViewWithIdentifier:@0
javaScriptString:@"runJavaScript"
completion:^(id result, FlutterError *error) {
returnValue = result;
returnError = error;
}];

XCTAssertNil(returnValue);
FWFNSErrorData *errorData = returnError.details;
XCTAssertTrue([errorData isKindOfClass:[FWFNSErrorData class]]);
XCTAssertEqualObjects(errorData.code, @0);
XCTAssertEqualObjects(errorData.domain, @"errorDomain");
XCTAssertEqualObjects(errorData.localizedDescription, @"description");
}
@end
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ - (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)identifie
if (!result || [result isKindOfClass:[NSString class]] ||
[result isKindOfClass:[NSNumber class]]) {
returnValue = result;
} else {
} else if (![result isKindOfClass:[NSNull class]]) {
NSString *className = NSStringFromClass([result class]);
NSLog(@"Return type of evaluateJavaScript is not directly supported: %@. Returned "
@"description of value.",
Expand All @@ -151,7 +151,7 @@ - (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)identifie
} else {
flutterError = [FlutterError errorWithCode:@"FWFEvaluateJavaScriptError"
message:@"Failed evaluating JavaScript."
details:error];
details:FWFNSErrorDataFromNSError(error)];
}

completion(returnValue, flutterError);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -908,11 +908,25 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi {
Future<Object?> evaluateJavaScriptForInstances(
WKWebView instance,
String javaScriptString,
) {
return evaluateJavaScript(
instanceManager.getIdentifier(instance)!,
javaScriptString,
);
) async {
try {
final Object? result = await evaluateJavaScript(
instanceManager.getIdentifier(instance)!,
javaScriptString,
);
return result;
} on PlatformException catch (exception) {
if (exception.details is! NSErrorData) {
rethrow;
}

throw PlatformException(
code: exception.code,
message: exception.message,
stacktrace: exception.stacktrace,
details: (exception.details as NSErrorData).toNSError(),
);
}
}

/// Calls [setNavigationDelegate] with the ids of the provided object instances.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,10 @@ class WebKitWebViewPlatformController extends WebViewPlatformController {
return '"<null>"';
}
return '(null)';
} else if (value is bool) {
return value ? '1' : '0';
} else if (value is double && value.truncate() == value) {
return value.truncate().toString();
} else if (value is List) {
final List<String> stringValues = <String>[];
for (final Object? listValue in value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
Expand Down Expand Up @@ -815,6 +816,30 @@ void main() {
.thenAnswer((_) => Future<String>.value('stopstop'));
expect(webView.evaluateJavaScript('gogo'), completion('stopstop'));
});

test('evaluateJavaScript returns NSError', () {
when(mockPlatformHostApi.evaluateJavaScript(webViewInstanceId, 'gogo'))
.thenThrow(
PlatformException(
code: '',
details: NSErrorData(
code: 0,
domain: 'domain',
localizedDescription: 'desc',
),
),
);
expect(
webView.evaluateJavaScript('gogo'),
throwsA(
isA<PlatformException>().having(
(PlatformException exception) => exception.details,
'details',
isA<NSError>(),
),
),
);
});
});

group('WKUIDelegate', () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,42 @@ void main() {
);
});

testWidgets('evaluateJavascript with bool return value',
(WidgetTester tester) async {
await buildWidget(tester);

when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer(
(_) => Future<Object?>.value(true),
);
// The legacy implementation of webview_flutter_wkwebview would convert
// objects to strings before returning them to Dart. This verifies bool
// is represented the way it is in Objective-C.
// `NSNumber.description` converts bool values to a 1 or 0.
expect(
testController.evaluateJavascript('runJavaScript'),
completion('1'),
);
});

testWidgets('evaluateJavascript with double return value',
(WidgetTester tester) async {
await buildWidget(tester);

when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer(
(_) => Future<Object?>.value(1.0),
);
// The legacy implementation of webview_flutter_wkwebview would convert
// objects to strings before returning them to Dart. This verifies
// double is represented the way it is in Objective-C. If a double
// doesn't contain any decimal values, it gets truncated to an int.
// This should be happenning because NSNumber convertes float values
// with no decimals to an int when using `NSNumber.description`.
expect(
testController.evaluateJavascript('runJavaScript'),
completion('1'),
);
});

testWidgets('evaluateJavascript with list return value',
(WidgetTester tester) async {
await buildWidget(tester);
Expand Down

0 comments on commit c673a9b

Please sign in to comment.