Skip to content
This repository was 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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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