|
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 |
| - NSDictionary<UIApplicationOpenURLOptionsKey, id>* options = @{}; |
36 |
| - BOOL result = [appDelegate application:[UIApplication sharedApplication] |
37 |
| - openURL:url |
38 |
| - options:options |
39 |
| - infoPlistGetter:^NSDictionary*() { |
40 |
| - return @{@"FlutterDeepLinkingEnabled" : @(YES)}; |
41 |
| - }]; |
| 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:@{}]; |
42 | 69 | XCTAssertTrue(result);
|
43 |
| - 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]); |
44 | 96 | }
|
45 | 97 |
|
46 | 98 | - (void)testLaunchUrlWithQueryParameterAndFragment {
|
47 |
| - FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init]; |
48 |
| - FlutterViewController* viewController = OCMClassMock([FlutterViewController class]); |
49 |
| - FlutterEngine* engine = OCMClassMock([FlutterEngine class]); |
50 |
| - FlutterMethodChannel* navigationChannel = OCMClassMock([FlutterMethodChannel class]); |
51 |
| - OCMStub([engine navigationChannel]).andReturn(navigationChannel); |
52 |
| - OCMStub([viewController engine]).andReturn(engine); |
53 |
| - // Set blockNoInvoker to a strong local to retain to end of scope. |
54 |
| - id blockNoInvoker = [OCMArg invokeBlockWithArgs:@NO, nil]; |
55 |
| - OCMStub([engine waitForFirstFrame:3.0 callback:blockNoInvoker]); |
56 |
| - appDelegate.rootFlutterViewControllerGetter = ^{ |
57 |
| - return viewController; |
58 |
| - }; |
59 |
| - NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route?query=test#fragment"]; |
60 |
| - NSDictionary<UIApplicationOpenURLOptionsKey, id>* options = @{}; |
61 |
| - BOOL result = [appDelegate application:[UIApplication sharedApplication] |
62 |
| - openURL:url |
63 |
| - options:options |
64 |
| - infoPlistGetter:^NSDictionary*() { |
65 |
| - return @{@"FlutterDeepLinkingEnabled" : @(YES)}; |
66 |
| - }]; |
| 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:@{}]; |
67 | 106 | XCTAssertTrue(result);
|
68 |
| - OCMVerify([navigationChannel invokeMethod:@"pushRoute" |
69 |
| - arguments:@"/custom/route?query=test#fragment"]); |
| 107 | + OCMVerify([self.mockNavigationChannel invokeMethod:@"pushRoute" |
| 108 | + arguments:@"/custom/route?query=test#fragment"]); |
70 | 109 | }
|
71 | 110 |
|
72 | 111 | - (void)testLaunchUrlWithFragmentNoQueryParameter {
|
73 |
| - FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init]; |
74 |
| - FlutterViewController* viewController = OCMClassMock([FlutterViewController class]); |
75 |
| - FlutterEngine* engine = OCMClassMock([FlutterEngine class]); |
76 |
| - FlutterMethodChannel* navigationChannel = OCMClassMock([FlutterMethodChannel class]); |
77 |
| - OCMStub([engine navigationChannel]).andReturn(navigationChannel); |
78 |
| - OCMStub([viewController engine]).andReturn(engine); |
79 |
| - // Set blockNoInvoker to a strong local to retain to end of scope. |
80 |
| - id blockNoInvoker = [OCMArg invokeBlockWithArgs:@NO, nil]; |
81 |
| - OCMStub([engine waitForFirstFrame:3.0 callback:blockNoInvoker]); |
82 |
| - appDelegate.rootFlutterViewControllerGetter = ^{ |
83 |
| - return viewController; |
84 |
| - }; |
85 |
| - NSURL* url = [NSURL URLWithString:@"http://myApp/custom/route#fragment"]; |
86 |
| - NSDictionary<UIApplicationOpenURLOptionsKey, id>* options = @{}; |
87 |
| - BOOL result = [appDelegate application:[UIApplication sharedApplication] |
88 |
| - openURL:url |
89 |
| - options:options |
90 |
| - infoPlistGetter:^NSDictionary*() { |
91 |
| - return @{@"FlutterDeepLinkingEnabled" : @(YES)}; |
92 |
| - }]; |
| 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:@{}]; |
| 119 | + XCTAssertTrue(result); |
| 120 | + OCMVerify([self.mockNavigationChannel invokeMethod:@"pushRoute" |
| 121 | + arguments:@"/custom/route#fragment"]); |
| 122 | +} |
| 123 | + |
| 124 | +#pragma mark - Deep linking |
| 125 | + |
| 126 | +- (void)testUniversalLinkPushRoute { |
| 127 | + OCMStub([self.mockMainBundle objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]) |
| 128 | + .andReturn(@YES); |
| 129 | + |
| 130 | + NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:@"com.example.test"]; |
| 131 | + userActivity.webpageURL = [NSURL URLWithString:@"http://myApp/custom/route?query=test"]; |
| 132 | + BOOL result = [self.appDelegate |
| 133 | + application:[UIApplication sharedApplication] |
| 134 | + continueUserActivity:userActivity |
| 135 | + restorationHandler:^(NSArray<id<UIUserActivityRestoring>>* __nullable restorableObjects){ |
| 136 | + }]; |
93 | 137 | XCTAssertTrue(result);
|
94 |
| - OCMVerify([navigationChannel invokeMethod:@"pushRoute" arguments:@"/custom/route#fragment"]); |
| 138 | + OCMVerify([self.mockNavigationChannel invokeMethod:@"pushRoute" |
| 139 | + arguments:@"/custom/route?query=test"]); |
95 | 140 | }
|
96 | 141 |
|
97 | 142 | @end
|
0 commit comments