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 
13 
15 
16 @interface FlutterAppDelegateTest : XCTestCase
17 @property(strong) FlutterAppDelegate* appDelegate;
19 @property(strong) id mockMainBundle;
20 @property(strong) id mockNavigationChannel;
21 
22 // Retain callback until the tests are done.
23 // https://github.com/flutter/flutter/issues/74267
24 @property(strong) id mockEngineFirstFrameCallback;
25 @end
26 
27 @implementation FlutterAppDelegateTest
28 
29 - (void)setUp {
30  [super setUp];
31 
32  id mockMainBundle = OCMClassMock([NSBundle class]);
33  OCMStub([mockMainBundle mainBundle]).andReturn(mockMainBundle);
34  self.mockMainBundle = mockMainBundle;
35 
37  self.appDelegate = appDelegate;
38 
40  self.viewController = viewController;
41 
42  FlutterMethodChannel* navigationChannel = OCMClassMock([FlutterMethodChannel class]);
43  self.mockNavigationChannel = navigationChannel;
44 
45  FlutterEngine* engine = OCMClassMock([FlutterEngine class]);
46  OCMStub([engine navigationChannel]).andReturn(navigationChannel);
47  OCMStub([viewController engine]).andReturn(engine);
48 
49  id mockEngineFirstFrameCallback = [OCMArg invokeBlockWithArgs:@NO, nil];
50  self.mockEngineFirstFrameCallback = mockEngineFirstFrameCallback;
51  OCMStub([engine waitForFirstFrame:3.0 callback:mockEngineFirstFrameCallback]);
53  return viewController;
54  };
55 }
56 
57 - (void)tearDown {
58  // Explicitly stop mocking the NSBundle class property.
59  [self.mockMainBundle stopMocking];
60  [super tearDown];
61 }
62 
63 - (void)testLaunchUrl {
64  OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
65  .andReturn(@YES);
66 
67  OCMStub([self.mockNavigationChannel
68  invokeMethod:@"pushRouteInformation"
69  arguments:@{@"location" : @"http://myApp/custom/route?query=test"}])
70  .andReturn(@YES);
71 
72  BOOL result =
73  [self.appDelegate application:[UIApplication sharedApplication]
74  openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test"]
75  options:@{}];
76 
77  XCTAssertTrue(result);
78  OCMVerifyAll(self.mockNavigationChannel);
79 }
80 
81 - (void)testLaunchUrlWithDeepLinkingNotSet {
82  OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
83  .andReturn(nil);
84 
85  OCMStub([self.mockNavigationChannel
86  invokeMethod:@"pushRouteInformation"
87  arguments:@{@"location" : @"http://myApp/custom/route?query=test"}])
88  .andReturn(@YES);
89 
90  BOOL result =
91  [self.appDelegate application:[UIApplication sharedApplication]
92  openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test"]
93  options:@{}];
94 
95  XCTAssertTrue(result);
96  OCMVerifyAll(self.mockNavigationChannel);
97 }
98 
99 - (void)testLaunchUrlWithDeepLinkingDisabled {
100  OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
101  .andReturn(@NO);
102 
103  BOOL result =
104  [self.appDelegate application:[UIApplication sharedApplication]
105  openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test"]
106  options:@{}];
107  XCTAssertFalse(result);
108  OCMReject([self.mockNavigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]);
109 }
110 
111 - (void)testLaunchUrlWithQueryParameterAndFragment {
112  OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
113  .andReturn(@YES);
114  OCMStub([self.mockNavigationChannel
115  invokeMethod:@"pushRouteInformation"
116  arguments:@{@"location" : @"http://myApp/custom/route?query=test#fragment"}])
117  .andReturn(@YES);
118  BOOL result = [self.appDelegate
119  application:[UIApplication sharedApplication]
120  openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test#fragment"]
121  options:@{}];
122  XCTAssertTrue(result);
123  OCMVerifyAll(self.mockNavigationChannel);
124 }
125 
126 - (void)testLaunchUrlWithFragmentNoQueryParameter {
127  OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
128  .andReturn(@YES);
129  OCMStub([self.mockNavigationChannel
130  invokeMethod:@"pushRouteInformation"
131  arguments:@{@"location" : @"http://myApp/custom/route#fragment"}])
132  .andReturn(@YES);
133  BOOL result =
134  [self.appDelegate application:[UIApplication sharedApplication]
135  openURL:[NSURL URLWithString:@"http://myApp/custom/route#fragment"]
136  options:@{}];
137  XCTAssertTrue(result);
138  OCMVerifyAll(self.mockNavigationChannel);
139 }
140 
141 - (void)testReleasesWindowOnDealloc {
142  __weak UIWindow* weakWindow;
143  @autoreleasepool {
144  id mockWindow = OCMClassMock([UIWindow class]);
146  appDelegate.window = mockWindow;
147  weakWindow = mockWindow;
148  XCTAssertNotNil(weakWindow);
149  [mockWindow stopMocking];
150  mockWindow = nil;
151  appDelegate = nil;
152  }
153  // App delegate has released the window.
154  XCTAssertNil(weakWindow);
155 }
156 
157 #pragma mark - Deep linking
158 
159 - (void)testUniversalLinkPushRouteInformation {
160  OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
161  .andReturn(@YES);
162  OCMStub([self.mockNavigationChannel
163  invokeMethod:@"pushRouteInformation"
164  arguments:@{@"location" : @"http://myApp/custom/route?query=test"}])
165  .andReturn(@YES);
166  NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:@"com.example.test"];
167  userActivity.webpageURL = [NSURL URLWithString:@"http://myApp/custom/route?query=test"];
168  BOOL result = [self.appDelegate
169  application:[UIApplication sharedApplication]
170  continueUserActivity:userActivity
171  restorationHandler:^(NSArray<id<UIUserActivityRestoring>>* __nullable restorableObjects){
172  }];
173  XCTAssertTrue(result);
174  OCMVerifyAll(self.mockNavigationChannel);
175 }
176 
177 - (void)testUseNonDeprecatedOpenURLAPI {
178  OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"])
179  .andReturn(@YES);
180  NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:@"com.example.test"];
181  userActivity.webpageURL = [NSURL URLWithString:@"http://myApp/custom/route?query=nonexist"];
182  OCMStub([self.viewController sendDeepLinkToFramework:[OCMArg any] completionHandler:[OCMArg any]])
183  .andDo(^(NSInvocation* invocation) {
184  void (^handler)(BOOL success);
185  [invocation getArgument:&handler atIndex:3];
186  handler(NO);
187  });
188  id mockApplication = OCMClassMock([UIApplication class]);
189  OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
190  BOOL result = [self.appDelegate
191  application:[UIApplication sharedApplication]
192  continueUserActivity:userActivity
193  restorationHandler:^(NSArray<id<UIUserActivityRestoring>>* __nullable restorableObjects){
194  }];
195  XCTAssertTrue(result);
196  OCMVerify([mockApplication openURL:[OCMArg any]
197  options:[OCMArg any]
198  completionHandler:[OCMArg any]]);
199 }
200 
201 @end
FlutterAppLifeCycleProvider UIWindow * window
FlutterViewController *(^ rootFlutterViewControllerGetter)(void)
FlutterAppDelegate * appDelegate
FlutterViewController * viewController