• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
8
9#include "FlutterBinaryMessenger.h"
10
11#if !__has_feature(objc_arc)
12#error ARC must be enabled!
13#endif
14
15@interface FlutterViewControllerTest : XCTestCase
16@end
17
18// The following conditional compilation defines an API 13 concept on earlier API targets so that
19// a compiler compiling against API 12 or below does not blow up due to non-existent members.
20#if __IPHONE_OS_VERSION_MAX_ALLOWED < 130000
21typedef enum UIAccessibilityContrast : NSInteger {
22  UIAccessibilityContrastUnspecified = 0,
23  UIAccessibilityContrastNormal = 1,
24  UIAccessibilityContrastHigh = 2
25} UIAccessibilityContrast;
26
27@interface UITraitCollection (MethodsFromNewerSDK)
28- (UIAccessibilityContrast)accessibilityContrast;
29@end
30#endif
31
32@implementation FlutterViewControllerTest
33
34- (void)testBinaryMessenger {
35  id engine = OCMClassMock([FlutterEngine class]);
36  FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
37                                                                    nibName:nil
38                                                                     bundle:nil];
39  XCTAssertNotNil(vc);
40  id messenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
41  OCMStub([engine binaryMessenger]).andReturn(messenger);
42  XCTAssertEqual(vc.binaryMessenger, messenger);
43  OCMVerify([engine binaryMessenger]);
44}
45
46#pragma mark - Platform Brightness
47
48- (void)testItReportsLightPlatformBrightnessByDefault {
49  // Setup test.
50  id engine = OCMClassMock([FlutterEngine class]);
51
52  id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
53  OCMStub([engine settingsChannel]).andReturn(settingsChannel);
54
55  FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
56                                                                    nibName:nil
57                                                                     bundle:nil];
58
59  // Exercise behavior under test.
60  [vc traitCollectionDidChange:nil];
61
62  // Verify behavior.
63  OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
64                               return [message[@"platformBrightness"] isEqualToString:@"light"];
65                             }]]);
66
67  // Clean up mocks
68  [engine stopMocking];
69  [settingsChannel stopMocking];
70}
71
72- (void)testItReportsPlatformBrightnessWhenViewWillAppear {
73  // Setup test.
74  id engine = OCMClassMock([FlutterEngine class]);
75
76  id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
77  OCMStub([engine settingsChannel]).andReturn(settingsChannel);
78
79  FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
80                                                                    nibName:nil
81                                                                     bundle:nil];
82
83  // Exercise behavior under test.
84  [vc viewWillAppear:false];
85
86  // Verify behavior.
87  OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
88                               return [message[@"platformBrightness"] isEqualToString:@"light"];
89                             }]]);
90
91  // Clean up mocks
92  [engine stopMocking];
93  [settingsChannel stopMocking];
94}
95
96- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt {
97  if (!@available(iOS 13, *)) {
98    return;
99  }
100
101  // Setup test.
102  id engine = OCMClassMock([FlutterEngine class]);
103
104  id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
105  OCMStub([engine settingsChannel]).andReturn(settingsChannel);
106
107  FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine
108                                                                        nibName:nil
109                                                                         bundle:nil];
110  id mockTraitCollection =
111      [self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
112
113  // We partially mock the real FlutterViewController to act as the OS and report
114  // the UITraitCollection of our choice. Mocking the object under test is not
115  // desirable, but given that the OS does not offer a DI approach to providing
116  // our own UITraitCollection, this seems to be the least bad option.
117  id partialMockVC = OCMPartialMock(realVC);
118  OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
119
120  // Exercise behavior under test.
121  [partialMockVC traitCollectionDidChange:nil];
122
123  // Verify behavior.
124  OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
125                               return [message[@"platformBrightness"] isEqualToString:@"dark"];
126                             }]]);
127
128  // Clean up mocks
129  [partialMockVC stopMocking];
130  [engine stopMocking];
131  [settingsChannel stopMocking];
132  [mockTraitCollection stopMocking];
133}
134
135// Creates a mocked UITraitCollection with nil values for everything except userInterfaceStyle,
136// which is set to the given "style".
137- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style {
138  id mockTraitCollection = OCMClassMock([UITraitCollection class]);
139  OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style);
140  return mockTraitCollection;
141}
142
143#pragma mark - Platform Contrast
144
145- (void)testItReportsNormalPlatformContrastByDefault {
146  if (!@available(iOS 13, *)) {
147    return;
148  }
149
150  // Setup test.
151  id engine = OCMClassMock([FlutterEngine class]);
152
153  id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
154  OCMStub([engine settingsChannel]).andReturn(settingsChannel);
155
156  FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
157                                                                    nibName:nil
158                                                                     bundle:nil];
159
160  // Exercise behavior under test.
161  [vc traitCollectionDidChange:nil];
162
163  // Verify behavior.
164  OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
165                               return [message[@"platformContrast"] isEqualToString:@"normal"];
166                             }]]);
167
168  // Clean up mocks
169  [engine stopMocking];
170  [settingsChannel stopMocking];
171}
172
173- (void)testItReportsPlatformContrastWhenViewWillAppear {
174  if (!@available(iOS 13, *)) {
175    return;
176  }
177
178  // Setup test.
179  id engine = OCMClassMock([FlutterEngine class]);
180
181  id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
182  OCMStub([engine settingsChannel]).andReturn(settingsChannel);
183
184  FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
185                                                                    nibName:nil
186                                                                     bundle:nil];
187
188  // Exercise behavior under test.
189  [vc viewWillAppear:false];
190
191  // Verify behavior.
192  OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
193                               return [message[@"platformContrast"] isEqualToString:@"normal"];
194                             }]]);
195
196  // Clean up mocks
197  [engine stopMocking];
198  [settingsChannel stopMocking];
199}
200
201- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt {
202  if (!@available(iOS 13, *)) {
203    return;
204  }
205
206  // Setup test.
207  id engine = OCMClassMock([FlutterEngine class]);
208
209  id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
210  OCMStub([engine settingsChannel]).andReturn(settingsChannel);
211
212  FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine
213                                                                        nibName:nil
214                                                                         bundle:nil];
215  id mockTraitCollection = [self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh];
216
217  // We partially mock the real FlutterViewController to act as the OS and report
218  // the UITraitCollection of our choice. Mocking the object under test is not
219  // desirable, but given that the OS does not offer a DI approach to providing
220  // our own UITraitCollection, this seems to be the least bad option.
221  id partialMockVC = OCMPartialMock(realVC);
222  OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
223
224  // Exercise behavior under test.
225  [partialMockVC traitCollectionDidChange:mockTraitCollection];
226
227  // Verify behavior.
228  OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
229                               return [message[@"platformContrast"] isEqualToString:@"high"];
230                             }]]);
231
232  // Clean up mocks
233  [partialMockVC stopMocking];
234  [engine stopMocking];
235  [settingsChannel stopMocking];
236  [mockTraitCollection stopMocking];
237}
238
239// Creates a mocked UITraitCollection with nil values for everything except accessibilityContrast,
240// which is set to the given "contrast".
241- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {
242  id mockTraitCollection = OCMClassMock([UITraitCollection class]);
243  OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast);
244  return mockTraitCollection;
245}
246
247@end
248