Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

[webview_flutter_wkwebview] Implement Objc side of Flutter Apis #5934

Merged
merged 16 commits into from
Jun 8, 2022
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
<string>11.0</string>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# platform :ios, '11.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -664,7 +664,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,7 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
@import XCTest;
@import webview_flutter_wkwebview;

#import <OCMock/OCMock.h>

@interface FWFDataConvertersTests : XCTestCase
@end

Expand Down Expand Up @@ -43,4 +45,63 @@ - (void)testFWFWKUserScriptFromScriptData {
XCTAssertEqual(userScript.injectionTime, WKUserScriptInjectionTimeAtDocumentStart);
XCTAssertEqual(userScript.isForMainFrameOnly, NO);
}

- (void)testFWFWKNavigationActionDataFromNavigationAction {
WKNavigationAction *mockNavigationAction = OCMClassMock([WKNavigationAction class]);

NSURLRequest *request =
[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.flutter.dev/"]];
OCMStub([mockNavigationAction request]).andReturn(request);

WKFrameInfo *mockFrameInfo = OCMClassMock([WKFrameInfo class]);
OCMStub([mockFrameInfo isMainFrame]).andReturn(YES);
OCMStub([mockNavigationAction targetFrame]).andReturn(mockFrameInfo);

FWFWKNavigationActionData *data =
FWFWKNavigationActionDataFromNavigationAction(mockNavigationAction);
XCTAssertNotNil(data);
}

- (void)testFWFNSUrlRequestDataFromNSURLRequest {
NSMutableURLRequest *request =
[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://www.flutter.dev/"]];
request.HTTPMethod = @"POST";
request.HTTPBody = [@"aString" dataUsingEncoding:NSUTF8StringEncoding];
request.allHTTPHeaderFields = @{@"a" : @"field"};

FWFNSUrlRequestData *data = FWFNSUrlRequestDataFromNSURLRequest(request);
XCTAssertEqualObjects(data.url, @"https://www.flutter.dev/");
XCTAssertEqualObjects(data.httpMethod, @"POST");
XCTAssertEqualObjects(data.httpBody.data, [@"aString" dataUsingEncoding:NSUTF8StringEncoding]);
XCTAssertEqualObjects(data.allHttpHeaderFields, @{@"a" : @"field"});
}

- (void)testFWFWKFrameInfoDataFromWKFrameInfo {
WKFrameInfo *mockFrameInfo = OCMClassMock([WKFrameInfo class]);
OCMStub([mockFrameInfo isMainFrame]).andReturn(YES);

FWFWKFrameInfoData *targetFrameData = FWFWKFrameInfoDataFromWKFrameInfo(mockFrameInfo);
XCTAssertEqualObjects(targetFrameData.isMainFrame, @YES);
}

- (void)testFWFNSErrorDataFromNSError {
NSError *error = [NSError errorWithDomain:@"domain"
code:23
userInfo:@{NSLocalizedDescriptionKey : @"description"}];

FWFNSErrorData *data = FWFNSErrorDataFromNSError(error);
XCTAssertEqualObjects(data.code, @23);
XCTAssertEqualObjects(data.domain, @"domain");
XCTAssertEqualObjects(data.localizedDescription, @"description");
}

- (void)testFWFWKScriptMessageDataFromWKScriptMessage {
WKScriptMessage *mockScriptMessage = OCMClassMock([WKScriptMessage class]);
OCMStub([mockScriptMessage name]).andReturn(@"name");
OCMStub([mockScriptMessage body]).andReturn(@"message");

FWFWKScriptMessageData *data = FWFWKScriptMessageDataFromWKScriptMessage(mockScriptMessage);
XCTAssertEqualObjects(data.name, @"name");
XCTAssertEqualObjects(data.body, @"message");
}
@end
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,38 @@ @interface FWFNavigationDelegateHostApiTests : XCTestCase
@end

@implementation FWFNavigationDelegateHostApiTests
/**
* Creates a partially mocked FWFNavigationDelegate and adds it to instanceManager.
*
* @param instanceManager Instance manager to add the delegate to.
* @param identifier Identifier for the delegate added to the instanceManager.
*
* @return A mock FWFNavigationDelegate.
*/
- (id)mockNavigationDelegateWithManager:(FWFInstanceManager *)instanceManager
bparrishMines marked this conversation as resolved.
Show resolved Hide resolved
identifier:(long)identifier {
FWFNavigationDelegate *navigationDelegate = [[FWFNavigationDelegate alloc]
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
instanceManager:instanceManager];

[instanceManager addDartCreatedInstance:navigationDelegate withIdentifier:0];
return OCMPartialMock(navigationDelegate);
}

/**
* Creates a mock FWFNavigationDelegateFlutterApiImpl with instanceManager.
*
* @param instanceManager Instance manager passed to the Flutter API.
*
* @return A mock FWFNavigationDelegateFlutterApiImpl.
*/
- (id)mockFlutterApiWithManager:(FWFInstanceManager *)instanceManager {
FWFNavigationDelegateFlutterApiImpl *flutterAPI = [[FWFNavigationDelegateFlutterApiImpl alloc]
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
instanceManager:instanceManager];
return OCMPartialMock(flutterAPI);
}

- (void)testCreateWithIdentifier {
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
FWFNavigationDelegateHostApiImpl *hostAPI = [[FWFNavigationDelegateHostApiImpl alloc]
Expand All @@ -29,31 +61,156 @@ - (void)testCreateWithIdentifier {

- (void)testDidFinishNavigation {
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
FWFNavigationDelegateHostApiImpl *hostAPI = [[FWFNavigationDelegateHostApiImpl alloc]
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
instanceManager:instanceManager];

FlutterError *error;
[hostAPI createWithIdentifier:@0 error:&error];
FWFNavigationDelegate *navigationDelegate =
(FWFNavigationDelegate *)[instanceManager instanceForIdentifier:0];
id mockDelegate = OCMPartialMock(navigationDelegate);
FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
identifier:0];
FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
[self mockFlutterApiWithManager:instanceManager];

FWFNavigationDelegateFlutterApiImpl *flutterAPI = [[FWFNavigationDelegateFlutterApiImpl alloc]
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
instanceManager:instanceManager];
id mockFlutterApi = OCMPartialMock(flutterAPI);

OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterApi);
OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);

WKWebView *mockWebView = OCMClassMock([WKWebView class]);
OCMStub([mockWebView URL]).andReturn([NSURL URLWithString:@"https://flutter.dev/"]);
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];

[mockDelegate webView:mockWebView didFinishNavigation:OCMClassMock([WKNavigation class])];
OCMVerify([mockFlutterApi didFinishNavigationForDelegateWithIdentifier:@0
OCMVerify([mockFlutterAPI didFinishNavigationForDelegateWithIdentifier:@0
webViewIdentifier:@1
URL:@"https://flutter.dev/"
completion:OCMOCK_ANY]);
}

- (void)testDidStartProvisionalNavigation {
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];

FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
identifier:0];
FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
[self mockFlutterApiWithManager:instanceManager];

OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);

WKWebView *mockWebView = OCMClassMock([WKWebView class]);
OCMStub([mockWebView URL]).andReturn([NSURL URLWithString:@"https://flutter.dev/"]);
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];

[mockDelegate webView:mockWebView
didStartProvisionalNavigation:OCMClassMock([WKNavigation class])];
OCMVerify([mockFlutterAPI
didStartProvisionalNavigationForDelegateWithIdentifier:@0
webViewIdentifier:@1
URL:@"https://flutter.dev/"
completion:OCMOCK_ANY]);
}

- (void)testDecidePolicyForNavigationAction {
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];

FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
identifier:0];
FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
[self mockFlutterApiWithManager:instanceManager];

OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);

WKWebView *mockWebView = OCMClassMock([WKWebView class]);
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];

WKNavigationAction *mockNavigationAction = OCMClassMock([WKNavigationAction class]);
OCMStub([mockNavigationAction request])
.andReturn([NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.flutter.dev"]]);

WKFrameInfo *mockFrameInfo = OCMClassMock([WKFrameInfo class]);
OCMStub([mockFrameInfo isMainFrame]).andReturn(YES);
OCMStub([mockNavigationAction targetFrame]).andReturn(mockFrameInfo);

OCMStub([mockFlutterAPI
decidePolicyForNavigationActionForDelegateWithIdentifier:@0
webViewIdentifier:@1
navigationAction:
[OCMArg isKindOfClass:[FWFWKNavigationActionData
class]]
completion:
([OCMArg
invokeBlockWithArgs:
[FWFWKNavigationActionPolicyEnumData
makeWithValue:
FWFWKNavigationActionPolicyEnumCancel],
[NSNull null], nil])]);

WKNavigationActionPolicy __block callbackPolicy = -1;
[mockDelegate webView:mockWebView
decidePolicyForNavigationAction:mockNavigationAction
decisionHandler:^(WKNavigationActionPolicy policy) {
callbackPolicy = policy;
}];
XCTAssertEqual(callbackPolicy, WKNavigationActionPolicyCancel);
}

- (void)testDidFailNavigation {
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];

FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
identifier:0];
FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
[self mockFlutterApiWithManager:instanceManager];

OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);

WKWebView *mockWebView = OCMClassMock([WKWebView class]);
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];

[mockDelegate webView:mockWebView
didFailNavigation:OCMClassMock([WKNavigation class])
withError:[NSError errorWithDomain:@"domain" code:0 userInfo:nil]];
OCMVerify([mockFlutterAPI
didFailNavigationForDelegateWithIdentifier:@0
webViewIdentifier:@1
error:[OCMArg isKindOfClass:[FWFNSErrorData class]]
completion:OCMOCK_ANY]);
}

- (void)testDidFailProvisionalNavigation {
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];

FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
identifier:0];
FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
[self mockFlutterApiWithManager:instanceManager];

OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);

WKWebView *mockWebView = OCMClassMock([WKWebView class]);
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];

[mockDelegate webView:mockWebView
didFailProvisionalNavigation:OCMClassMock([WKNavigation class])
withError:[NSError errorWithDomain:@"domain" code:0 userInfo:nil]];
OCMVerify([mockFlutterAPI
didFailProvisionalNavigationForDelegateWithIdentifier:@0
webViewIdentifier:@1
error:[OCMArg isKindOfClass:[FWFNSErrorData
class]]
completion:OCMOCK_ANY]);
}

- (void)testWebViewWebContentProcessDidTerminate {
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];

FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
identifier:0];
FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
[self mockFlutterApiWithManager:instanceManager];

OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);

WKWebView *mockWebView = OCMClassMock([WKWebView class]);
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];

[mockDelegate webViewWebContentProcessDidTerminate:mockWebView];
OCMVerify([mockFlutterAPI
webViewWebContentProcessDidTerminateForDelegateWithIdentifier:@0
webViewIdentifier:@1
completion:OCMOCK_ANY]);
}
@end
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,37 @@ @interface FWFObjectHostApiTests : XCTestCase
@end

@implementation FWFObjectHostApiTests
/**
* Creates a partially mocked FWFObject and adds it to instanceManager.
*
* @param instanceManager Instance manager to add the delegate to.
* @param identifier Identifier for the delegate added to the instanceManager.
*
* @return A mock FWFObject.
*/
- (id)mockObjectWithManager:(FWFInstanceManager *)instanceManager identifier:(long)identifier {
FWFObject *object =
[[FWFObject alloc] initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
instanceManager:instanceManager];

[instanceManager addDartCreatedInstance:object withIdentifier:0];
return OCMPartialMock(object);
}

/**
* Creates a mock FWFObjectFlutterApiImpl with instanceManager.
*
* @param instanceManager Instance manager passed to the Flutter API.
*
* @return A mock FWFObjectFlutterApiImpl.
*/
- (id)mockFlutterApiWithManager:(FWFInstanceManager *)instanceManager {
FWFObjectFlutterApiImpl *flutterAPI = [[FWFObjectFlutterApiImpl alloc]
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
instanceManager:instanceManager];
return OCMPartialMock(flutterAPI);
}

- (void)testAddObserver {
NSObject *mockObject = OCMClassMock([NSObject class]);

Expand Down Expand Up @@ -82,4 +113,34 @@ - (void)testDispose {
XCTAssertFalse([instanceManager containsInstance:object]);
XCTAssertNil(error);
}

- (void)testObserveValueForKeyPath {
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];

FWFObject *mockObject = [self mockObjectWithManager:instanceManager identifier:0];
FWFObjectFlutterApiImpl *mockFlutterAPI = [self mockFlutterApiWithManager:instanceManager];

OCMStub([mockObject objectApi]).andReturn(mockFlutterAPI);

NSObject *object = [[NSObject alloc] init];
[instanceManager addDartCreatedInstance:object withIdentifier:1];

[mockObject observeValueForKeyPath:@"keyPath"
ofObject:object
change:@{NSKeyValueChangeOldKey : @"key"}
context:nil];
OCMVerify([mockFlutterAPI
observeValueForObjectWithIdentifier:@0
keyPath:@"keyPath"
objectIdentifier:@1
changeKeys:[OCMArg checkWithBlock:^BOOL(
NSArray<FWFNSKeyValueChangeKeyEnumData *>
*value) {
return value[0].value == FWFNSKeyValueChangeKeyEnumOldValue;
}]
changeValues:[OCMArg checkWithBlock:^BOOL(id value) {
return [@"key" isEqual:value[0]];
}]
completion:OCMOCK_ANY]);
}
@end
Loading