|
14 | 14 | FLUTTER_ASSERT_ARC |
15 | 15 |
|
16 | 16 | @interface FlutterAppDelegateTest : XCTestCase |
| 17 | +@property(strong) FlutterAppDelegate* appDelegate; |
| 18 | + |
| 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; |
17 | 25 | @end |
18 | 26 |
|
19 | 27 | @implementation FlutterAppDelegateTest |
20 | 28 |
|
21 | | -- (void)testLaunchUrl { |
| 29 | +- (void)setUp { |
| 30 | + [super setUp]; |
| 31 | + |
| 32 | + id mockMainBundle = OCMClassMock([NSBundle class]); |
| 33 | + OCMStub([mockMainBundle mainBundle]).andReturn(mockMainBundle); |
| 34 | + self.mockMainBundle = mockMainBundle; |
| 35 | + |
22 | 36 | FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init]; |
| 37 | + self.appDelegate = appDelegate; |
| 38 | + |
23 | 39 | FlutterViewController* viewController = OCMClassMock([FlutterViewController class]); |
24 | | - FlutterEngine* engine = OCMClassMock([FlutterEngine class]); |
25 | 40 | FlutterMethodChannel* navigationChannel = OCMClassMock([FlutterMethodChannel class]); |
| 41 | + self.mockNavigationChannel = navigationChannel; |
| 42 | + |
| 43 | + FlutterEngine* engine = OCMClassMock([FlutterEngine class]); |
26 | 44 | OCMStub([engine navigationChannel]).andReturn(navigationChannel); |
27 | 45 | OCMStub([viewController engine]).andReturn(engine); |
28 | | - // Set blockNoInvoker to a strong local to retain to end of scope. |
29 | | - id blockNoInvoker = [OCMArg invokeBlockWithArgs:@NO, nil]; |
30 | | - OCMStub([engine waitForFirstFrame:3.0 callback:blockNoInvoker]); |
| 46 | + |
| 47 | + id mockEngineFirstFrameCallback = [OCMArg invokeBlockWithArgs:@NO, nil]; |
| 48 | + self.mockEngineFirstFrameCallback = mockEngineFirstFrameCallback; |
| 49 | + OCMStub([engine waitForFirstFrame:3.0 callback:mockEngineFirstFrameCallback]); |
31 | 50 | appDelegate.rootFlutterViewControllerGetter = ^{ |
32 | 51 | return viewController; |
33 | 52 | }; |
34 | | - NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; |
35 | | - BOOL result = [appDelegate openURL:url |
36 | | - infoPlistGetter:^NSDictionary*() { |
37 | | - return @{@"FlutterDeepLinkingEnabled" : @YES}; |
38 | | - }]; |
| 53 | +} |
| 54 | + |
| 55 | +- (void)tearDown { |
| 56 | + // Explicitly stop mocking the NSBundle class property. |
| 57 | + [self.mockMainBundle stopMocking]; |
| 58 | + [super tearDown]; |
| 59 | +} |
| 60 | + |
| 61 | +- (void)testLaunchUrl { |
| 62 | + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) |
| 63 | + .andReturn(@YES); |
| 64 | + |
| 65 | + BOOL result = |
| 66 | + [self.appDelegate application:[UIApplication sharedApplication] |
| 67 | + openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test"] |
| 68 | + options:@{}]; |
39 | 69 | XCTAssertTrue(result); |
40 | | - OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route?query=test"]); |
| 70 | + OCMVerify([self.mockNavigationChannel invokeMethod:@"pushRoute" |
| 71 | + arguments:@"/custom/route?query=test"]); |
| 72 | +} |
| 73 | + |
| 74 | +- (void)testLaunchUrlWithDeepLinkingNotSet { |
| 75 | + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) |
| 76 | + .andReturn(nil); |
| 77 | + |
| 78 | + BOOL result = |
| 79 | + [self.appDelegate application:[UIApplication sharedApplication] |
| 80 | + openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test"] |
| 81 | + options:@{}]; |
| 82 | + XCTAssertFalse(result); |
| 83 | + OCMReject([self.mockNavigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); |
| 84 | +} |
| 85 | + |
| 86 | +- (void)testLaunchUrlWithDeepLinkingDisabled { |
| 87 | + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) |
| 88 | + .andReturn(@NO); |
| 89 | + |
| 90 | + BOOL result = |
| 91 | + [self.appDelegate application:[UIApplication sharedApplication] |
| 92 | + openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test"] |
| 93 | + options:@{}]; |
| 94 | + XCTAssertFalse(result); |
| 95 | + OCMReject([self.mockNavigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); |
41 | 96 | } |
42 | 97 |
|
43 | 98 | - (void)testLaunchUrlWithQueryParameterAndFragment { |
44 | | - FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init]; |
45 | | - FlutterViewController* viewController = OCMClassMock([FlutterViewController class]); |
46 | | - FlutterEngine* engine = OCMClassMock([FlutterEngine class]); |
47 | | - FlutterMethodChannel* navigationChannel = OCMClassMock([FlutterMethodChannel class]); |
48 | | - OCMStub([engine navigationChannel]).andReturn(navigationChannel); |
49 | | - OCMStub([viewController engine]).andReturn(engine); |
50 | | - // Set blockNoInvoker to a strong local to retain to end of scope. |
51 | | - id blockNoInvoker = [OCMArg invokeBlockWithArgs:@NO, nil]; |
52 | | - OCMStub([engine waitForFirstFrame:3.0 callback:blockNoInvoker]); |
53 | | - appDelegate.rootFlutterViewControllerGetter = ^{ |
54 | | - return viewController; |
55 | | - }; |
56 | | - NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route?query=test#fragment"]; |
57 | | - BOOL result = [appDelegate openURL:url |
58 | | - infoPlistGetter:^NSDictionary*() { |
59 | | - return @{@"FlutterDeepLinkingEnabled" : @YES}; |
60 | | - }]; |
| 99 | + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) |
| 100 | + .andReturn(@YES); |
| 101 | + |
| 102 | + BOOL result = [self.appDelegate |
| 103 | + application:[UIApplication sharedApplication] |
| 104 | + openURL:[NSURL URLWithString:@"http://myApp/custom/route?query=test#fragment"] |
| 105 | + options:@{}]; |
61 | 106 | XCTAssertTrue(result); |
62 | | - OCMVerify([navigationChannel invokeMethod:@"pushRoute" |
63 | | - arguments:@"/custom/route?query=test#fragment"]); |
| 107 | + OCMVerify([self.mockNavigationChannel invokeMethod:@"pushRoute" |
| 108 | + arguments:@"/custom/route?query=test#fragment"]); |
64 | 109 | } |
65 | 110 |
|
66 | 111 | - (void)testLaunchUrlWithFragmentNoQueryParameter { |
67 | | - FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init]; |
68 | | - FlutterViewController* viewController = OCMClassMock([FlutterViewController class]); |
69 | | - FlutterEngine* engine = OCMClassMock([FlutterEngine class]); |
70 | | - FlutterMethodChannel* navigationChannel = OCMClassMock([FlutterMethodChannel class]); |
71 | | - OCMStub([engine navigationChannel]).andReturn(navigationChannel); |
72 | | - OCMStub([viewController engine]).andReturn(engine); |
73 | | - // Set blockNoInvoker to a strong local to retain to end of scope. |
74 | | - id blockNoInvoker = [OCMArg invokeBlockWithArgs:@NO, nil]; |
75 | | - OCMStub([engine waitForFirstFrame:3.0 callback:blockNoInvoker]); |
76 | | - appDelegate.rootFlutterViewControllerGetter = ^{ |
77 | | - return viewController; |
78 | | - }; |
79 | | - NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route#fragment"]; |
80 | | - BOOL result = [appDelegate openURL:url |
81 | | - infoPlistGetter:^NSDictionary*() { |
82 | | - return @{@"FlutterDeepLinkingEnabled" : @YES}; |
83 | | - }]; |
| 112 | + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) |
| 113 | + .andReturn(@YES); |
| 114 | + |
| 115 | + BOOL result = |
| 116 | + [self.appDelegate application:[UIApplication sharedApplication] |
| 117 | + openURL:[NSURL URLWithString:@"http://myApp/custom/route#fragment"] |
| 118 | + options:@{}]; |
84 | 119 | XCTAssertTrue(result); |
85 | | - OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route#fragment"]); |
| 120 | + OCMVerify([self.mockNavigationChannel invokeMethod:@"pushRoute" |
| 121 | + arguments:@"/custom/route#fragment"]); |
86 | 122 | } |
87 | 123 |
|
88 | 124 | #pragma mark - Deep linking |
89 | 125 |
|
90 | 126 | - (void)testUniversalLinkWebBrowserUrl { |
91 | | - FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init]; |
92 | | - FlutterViewController* viewController = OCMClassMock([FlutterViewController class]); |
93 | | - FlutterEngine* engine = OCMClassMock([FlutterEngine class]); |
94 | | - FlutterMethodChannel* navigationChannel = OCMClassMock([FlutterMethodChannel class]); |
95 | | - OCMStub([engine navigationChannel]).andReturn(navigationChannel); |
96 | | - OCMStub([viewController engine]).andReturn(engine); |
97 | | - // Set blockArg to a strong local to retain to end of scope. |
98 | | - id blockArg = [OCMArg invokeBlockWithArgs:@NO, nil]; |
99 | | - OCMStub([engine waitForFirstFrame:3.0 callback:blockArg]); |
100 | | - appDelegate.rootFlutterViewControllerGetter = ^{ |
101 | | - return viewController; |
102 | | - }; |
| 127 | + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) |
| 128 | + .andReturn(@YES); |
| 129 | + |
103 | 130 | NSUserActivity* userActivity = |
104 | 131 | [[NSUserActivity alloc] initWithActivityType:NSUserActivityTypeBrowsingWeb]; |
105 | 132 | userActivity.webpageURL = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; |
106 | | - BOOL result = [appDelegate |
| 133 | + BOOL result = [self.appDelegate |
107 | 134 | application:[UIApplication sharedApplication] |
108 | 135 | continueUserActivity:userActivity |
109 | 136 | restorationHandler:^(NSArray<id<UIUserActivityRestoring>>* __nullable restorableObjects){ |
110 | 137 | }]; |
111 | 138 | XCTAssertFalse(result); |
112 | | - OCMReject([navigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); |
| 139 | + OCMReject([self.mockNavigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); |
113 | 140 | } |
114 | 141 |
|
115 | 142 | - (void)testUniversalLinkPushRoute { |
116 | | - id mockBundle = OCMClassMock([NSBundle class]); |
117 | | - OCMStub([mockBundle mainBundle]).andReturn(mockBundle); |
118 | | - OCMStub([mockBundle infoDictionary]).andReturn(@{@"FlutterDeepLinkingEnabled" : @YES}); |
| 143 | + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) |
| 144 | + .andReturn(@YES); |
119 | 145 |
|
120 | | - FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init]; |
121 | | - FlutterViewController* viewController = OCMClassMock([FlutterViewController class]); |
122 | | - FlutterEngine* engine = OCMClassMock([FlutterEngine class]); |
123 | | - FlutterMethodChannel* navigationChannel = OCMClassMock([FlutterMethodChannel class]); |
124 | | - OCMStub([engine navigationChannel]).andReturn(navigationChannel); |
125 | | - OCMStub([viewController engine]).andReturn(engine); |
126 | | - // Set blockArg to a strong local to retain to end of scope. |
127 | | - id blockArg = [OCMArg invokeBlockWithArgs:@NO, nil]; |
128 | | - OCMStub([engine waitForFirstFrame:3.0 callback:blockArg]); |
129 | | - appDelegate.rootFlutterViewControllerGetter = ^{ |
130 | | - return viewController; |
131 | | - }; |
132 | | - NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; |
133 | 146 | NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:@"com.example.test"]; |
134 | | - userActivity.webpageURL = url; |
135 | | - BOOL result = [appDelegate |
| 147 | + userActivity.webpageURL = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; |
| 148 | + BOOL result = [self.appDelegate |
136 | 149 | application:[UIApplication sharedApplication] |
137 | 150 | continueUserActivity:userActivity |
138 | 151 | restorationHandler:^(NSArray<id<UIUserActivityRestoring>>* __nullable restorableObjects){ |
139 | 152 | }]; |
140 | 153 | XCTAssertTrue(result); |
141 | | - OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route?query=test"]); |
142 | | - [mockBundle stopMocking]; |
| 154 | + OCMVerify([self.mockNavigationChannel invokeMethod:@"pushRoute" |
| 155 | + arguments:@"/custom/route?query=test"]); |
143 | 156 | } |
144 | 157 |
|
145 | 158 | @end |
0 commit comments