| Flutter iOS Embedder |

FlutterAppDelegateTest.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import <OCMock/OCMock.h>
6 #import <XCTest/XCTest.h>
7
8 #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h"
9 #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h"
10 #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
11 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h"
12 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h"
13 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
14 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h"
15
16 FLUTTER_ASSERT_ARC
17
18 @interface FlutterAppDelegateTest : XCTestCase
19 @property(strong) FlutterAppDelegate* appDelegate;
20 @property(strong) FlutterViewController* viewController;
21 @property(strong) id mockMainBundle;
22 @property(strong) id mockNavigationChannel;
23 @property(strong) FlutterEngine* engine;
24
25 // Retain callback until the tests are done.
26 // https://github.com/flutter/flutter/issues/74267
27 @property(strong) id mockEngineFirstFrameCallback;
28 @end
29
30 @implementation FlutterAppDelegateTest
31
32 - (void)setUp {
33 [super setUp];
34
35 id mockMainBundle = OCMClassMock([NSBundle class]);
36 OCMStub([mockMainBundle mainBundle]).andReturn(mockMainBundle);
37 self.mockMainBundle = mockMainBundle;
38
39 FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init];
40 self.appDelegate = appDelegate;
41
42 FlutterViewController* viewController = OCMClassMock([FlutterViewController class]);
43 self.viewController = viewController;
44
45 FlutterMethodChannel* navigationChannel = OCMClassMock([FlutterMethodChannel class]);
46 self.mockNavigationChannel = navigationChannel;
47
48 FlutterEngine* engine = OCMClassMock([FlutterEngine class]);
49 OCMStub([engine navigationChannel]).andReturn(navigationChannel);
50 OCMStub([viewController engine]).andReturn(engine);
51 self.engine = engine;
52
53 id mockEngineFirstFrameCallback = [OCMArg invokeBlockWithArgs:@NO, nil];
54 self.mockEngineFirstFrameCallback = mockEngineFirstFrameCallback;
55 OCMStub([engine waitForFirstFrame:3.0 callback:mockEngineFirstFrameCallback]);
56 appDelegate.rootFlutterViewControllerGetter = ^{
57 return viewController;
58 };
59 }
60
61 - (void)tearDown {
62 // Explicitly stop mocking the NSBundle class property.
63 [self.mockMainBundle stopMocking];
64 [super tearDown];
65 }
66
67 - (void)testLaunchUrl {
68 OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
69 .andReturn(@YES);
70
71 OCMStub([self.mockNavigationChannel
72 invokeMethod:@"pushRouteInformation"
73 arguments:@{@"location" : @"http://myApp/custom/route?query=test"}])
74 .andReturn(@YES);
75
76 BOOL result =
77 [self.appDelegate application:[UIApplication sharedApplication]
78 openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test"]
79 options:@{}];
80
81 XCTAssertTrue(result);
82 OCMVerifyAll(self.mockNavigationChannel);
83 }
84
85 - (void)testLaunchUrlWithDeepLinkingNotSet {
86 OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
87 .andReturn(nil);
88
89 OCMStub([self.mockNavigationChannel
90 invokeMethod:@"pushRouteInformation"
91 arguments:@{@"location" : @"http://myApp/custom/route?query=test"}])
92 .andReturn(@YES);
93
94 BOOL result =
95 [self.appDelegate application:[UIApplication sharedApplication]
96 openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test"]
97 options:@{}];
98
99 XCTAssertTrue(result);
100 OCMVerifyAll(self.mockNavigationChannel);
101 }
102
103 - (void)testLaunchUrlWithDeepLinkingDisabled {
104 OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
105 .andReturn(@NO);
106
107 BOOL result =
108 [self.appDelegate application:[UIApplication sharedApplication]
109 openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test"]
110 options:@{}];
111 XCTAssertFalse(result);
112 OCMReject([self.mockNavigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]);
113 }
114
115 - (void)testLaunchUrlWithQueryParameterAndFragment {
116 OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
117 .andReturn(@YES);
118 OCMStub([self.mockNavigationChannel
119 invokeMethod:@"pushRouteInformation"
120 arguments:@{@"location" : @"http://myApp/custom/route?query=test#fragment"}])
121 .andReturn(@YES);
122 BOOL result = [self.appDelegate
123 application:[UIApplication sharedApplication]
124 openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test#fragment"]
125 options:@{}];
126 XCTAssertTrue(result);
127 OCMVerifyAll(self.mockNavigationChannel);
128 }
129
130 - (void)testLaunchUrlWithFragmentNoQueryParameter {
131 OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
132 .andReturn(@YES);
133 OCMStub([self.mockNavigationChannel
134 invokeMethod:@"pushRouteInformation"
135 arguments:@{@"location" : @"http://myApp/custom/route#fragment"}])
136 .andReturn(@YES);
137 BOOL result =
138 [self.appDelegate application:[UIApplication sharedApplication]
139 openURL:[NSURL URLWithString:@"http://myApp/custom/route#fragment"]
140 options:@{}];
141 XCTAssertTrue(result);
142 OCMVerifyAll(self.mockNavigationChannel);
143 }
144
145 - (void)testReleasesWindowOnDealloc {
146 __weak UIWindow* weakWindow;
147 @autoreleasepool {
148 id mockWindow = OCMClassMock([UIWindow class]);
149 FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init];
150 appDelegate.window = mockWindow;
151 weakWindow = mockWindow;
152 XCTAssertNotNil(weakWindow);
153 [mockWindow stopMocking];
154 mockWindow = nil;
155 appDelegate = nil;
156 }
157 // App delegate has released the window.
158 XCTAssertNil(weakWindow);
159 }
160
161 - (void)testGrabLaunchEngine {
162 // Clear out the mocking of the root view controller.
163 [self.mockMainBundle stopMocking];
164 self.appDelegate.rootFlutterViewControllerGetter = nil;
165 // Working with plugins forces the creation of an engine.
166 XCTAssertFalse([self.appDelegate hasPlugin:@"hello"]);
167 XCTAssertNotNil([self.appDelegate takeLaunchEngine]);
168 XCTAssertNil([self.appDelegate takeLaunchEngine]);
169 }
170
171 - (void)testGrabLaunchEngineWithoutPlugins {
172 XCTAssertNil([self.appDelegate takeLaunchEngine]);
173 }
174
175 #pragma mark - Deep linking
176
177 - (void)testUniversalLinkPushRouteInformation {
178 OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
179 .andReturn(@YES);
180 OCMStub([self.mockNavigationChannel
181 invokeMethod:@"pushRouteInformation"
182 arguments:@{@"location" : @"http://myApp/custom/route?query=test"}])
183 .andReturn(@YES);
184 NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:@"com.example.test"];
185 userActivity.webpageURL = [NSURL URLWithString:@"http://myApp/custom/route?query=test"];
186 BOOL result = [self.appDelegate
187 application:[UIApplication sharedApplication]
188 continueUserActivity:userActivity
189 restorationHandler:^(NSArray<id<UIUserActivityRestoring>>* __nullable restorableObjects){
190 }];
191 XCTAssertTrue(result);
192 OCMVerifyAll(self.mockNavigationChannel);
193 }
194
195 - (void)testUseNonDeprecatedOpenURLAPI {
196 OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
197 .andReturn(@YES);
198 NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:@"com.example.test"];
199 userActivity.webpageURL = [NSURL URLWithString:@"http://myApp/custom/route?query=nonexist"];
200 OCMStub([self.engine sendDeepLinkToFramework:[OCMArg any] completionHandler:[OCMArg any]])
201 .andDo(^(NSInvocation* invocation) {
202 void (^handler)(BOOL success);
203 [invocation getArgument:&handler atIndex:3];
204 handler(NO);
205 });
206 id mockApplication = OCMClassMock([UIApplication class]);
207 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
208 BOOL result = [self.appDelegate
209 application:[UIApplication sharedApplication]
210 continueUserActivity:userActivity
211 restorationHandler:^(NSArray<id<UIUserActivityRestoring>>* __nullable restorableObjects){
212 }];
213 XCTAssertTrue(result);
214 OCMVerify([mockApplication openURL:[OCMArg any]
215 options:[OCMArg any]
216 completionHandler:[OCMArg any]]);
217 }
218
219 - (void)testSetGetPluginRegistrant {
220 id mockRegistrant = OCMProtocolMock(@protocol(FlutterPluginRegistrant));
221 self.appDelegate.pluginRegistrant = mockRegistrant;
222 XCTAssertEqual(self.appDelegate.pluginRegistrant, mockRegistrant);
223 }
224
225 - (void)testSetGetPluginRegistrantSelf {
226 __weak FlutterAppDelegate* appDelegate = self.appDelegate;
227 @autoreleasepool {
228 appDelegate.pluginRegistrant = (id)appDelegate;
229 self.appDelegate = nil;
230 }
231 // A retain cycle would keep this alive.
232 XCTAssertNil(appDelegate);
233 }
234
235 @end
FlutterAppDelegate.h
FlutterAppDelegate_Internal.h
FlutterAppDelegate_Test.h
FlutterEngine.h
FlutterEngine_Internal.h
FlutterEngine_Test.h
FlutterViewController.h
FlutterAppDelegate
Definition: FlutterAppDelegate.h:28
FlutterAppDelegate::window
FlutterAppLifeCycleProvider UIWindow * window
Definition: FlutterAppDelegate.h:30
FlutterAppDelegate::pluginRegistrant
NSObject< FlutterPluginRegistrant > * pluginRegistrant
Definition: FlutterAppDelegate.h:44
FlutterAppDelegate::rootFlutterViewControllerGetter
FlutterViewController *(^ rootFlutterViewControllerGetter)(void)
FlutterAppDelegateTest
Definition: FlutterAppDelegateTest.mm:19
FlutterAppDelegateTest::mockEngineFirstFrameCallback
id mockEngineFirstFrameCallback
Definition: FlutterAppDelegateTest.mm:27
FlutterAppDelegateTest::engine
FlutterEngine * engine
Definition: FlutterAppDelegateTest.mm:23
FlutterAppDelegateTest::appDelegate
FlutterAppDelegate * appDelegate
Definition: FlutterAppDelegateTest.mm:19
FlutterAppDelegateTest::mockNavigationChannel
id mockNavigationChannel
Definition: FlutterAppDelegateTest.mm:22
FlutterAppDelegateTest::mockMainBundle
id mockMainBundle
Definition: FlutterAppDelegateTest.mm:21
FlutterAppDelegateTest::viewController
FlutterViewController * viewController
Definition: FlutterAppDelegateTest.mm:20
FlutterEngine
Definition: FlutterEngine.h:61
FlutterMethodChannel
Definition: FlutterChannels.h:221
FlutterViewController
Definition: FlutterViewController.h:57
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13
FlutterPluginRegistrant-p
Definition: FlutterPlugin.h:480