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

Commit

Permalink
[webview_flutter_wkwebview] Implement Objc side of Flutter Apis (#5934)
Browse files Browse the repository at this point in the history
  • Loading branch information
bparrishMines authored Jun 8, 2022
1 parent d50cab7 commit f894b98
Show file tree
Hide file tree
Showing 19 changed files with 877 additions and 38 deletions.
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
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

0 comments on commit f894b98

Please sign in to comment.