• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 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 <EarlGrey/EarlGrey.h>
6#import <XCTest/XCTest.h>
7
8#import "../ios_add2app/AppDelegate.h"
9#import "../ios_add2app/DualFlutterViewController.h"
10#import "../ios_add2app/FullScreenViewController.h"
11#import "../ios_add2app/MainViewController.h"
12#import "../ios_add2app/HybridViewController.h"
13
14@interface FlutterTests : XCTestCase
15@end
16
17@implementation FlutterTests {
18  int _flutterWarmEngineTaps;
19}
20
21- (instancetype)init {
22  self = [super init];
23
24  if (self) {
25    _flutterWarmEngineTaps = 0;
26  }
27
28  return self;
29}
30
31- (void)expectSemanticsNotification:(FlutterViewController*)viewController {
32  [self expectationForNotification:FlutterSemanticsUpdateNotification object:viewController handler:nil];
33  [viewController.engine ensureSemanticsEnabled];
34  [self waitForExpectationsWithTimeout:30.0 handler:nil];
35}
36
37- (void)testFullScreenCanPop {
38  [[EarlGrey selectElementWithMatcher:grey_keyWindow()]
39      assertWithMatcher:grey_sufficientlyVisible()];
40
41  [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Full Screen (Cold)")]
42      performAction:grey_tap()];
43
44  __weak FlutterViewController *weakViewController;
45  @autoreleasepool {
46    UINavigationController *navController =
47        (UINavigationController *)((AppDelegate *)
48                                       [[UIApplication sharedApplication]
49                                           delegate])
50            .window.rootViewController;
51    weakViewController =
52        (FullScreenViewController *)navController.visibleViewController;
53    [self expectSemanticsNotification:weakViewController];
54    GREYAssertNotNil(weakViewController,
55                     @"Expected non-nil FullScreenViewController.");
56  }
57
58  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"POP")]
59      performAction:grey_tap()];
60  // EarlGrey v1 isn't good at detecting this yet - 2.0 will be able to do it
61  int tries = 10;
62  double delay = 1.0;
63  while (weakViewController != nil && tries != 0) {
64    CFRunLoopRunInMode(kCFRunLoopDefaultMode, delay, false);
65    tries--;
66  }
67  [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Native iOS View")]
68      assertWithMatcher:grey_sufficientlyVisible()];
69  GREYAssertNil(weakViewController,
70                @"Expected FullScreenViewController to be deallocated.");
71}
72
73- (void)testDualFlutterView {
74  [[EarlGrey selectElementWithMatcher:grey_keyWindow()]
75      assertWithMatcher:grey_sufficientlyVisible()];
76
77  [[EarlGrey
78      selectElementWithMatcher:grey_buttonTitle(@"Dual Flutter View (Cold)")]
79      performAction:grey_tap()];
80
81  @autoreleasepool {
82    UINavigationController *navController =
83        (UINavigationController *)((AppDelegate *)
84                                       [[UIApplication sharedApplication]
85                                           delegate])
86            .window.rootViewController;
87    DualFlutterViewController *viewController =
88        (DualFlutterViewController *)navController.visibleViewController;
89    GREYAssertNotNil(viewController,
90                     @"Expected non-nil DualFlutterViewController.");
91    [self expectSemanticsNotification:viewController.topFlutterViewController];
92    [self expectSemanticsNotification:viewController.bottomFlutterViewController];
93  }
94
95  // Verify that there are two Flutter views with the expected marquee text.
96  [[[EarlGrey
97      selectElementWithMatcher:grey_accessibilityLabel(@"This is Marquee")]
98      atIndex:0] assertWithMatcher:grey_notNil()];
99  [[[EarlGrey
100      selectElementWithMatcher:grey_accessibilityLabel(@"This is Marquee")]
101      atIndex:1] assertWithMatcher:grey_notNil()];
102
103  [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Back")]
104      performAction:grey_tap()];
105
106  [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Native iOS View")]
107      assertWithMatcher:grey_sufficientlyVisible()];
108}
109
110- (void)testHybridView {
111  [[EarlGrey selectElementWithMatcher:grey_keyWindow()]
112      assertWithMatcher:grey_sufficientlyVisible()];
113
114  [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Hybrid View (Warm)")]
115      performAction:grey_tap()];
116
117  @autoreleasepool {
118    UINavigationController *navController =
119        (UINavigationController *)((AppDelegate *)
120                                       [[UIApplication sharedApplication]
121                                           delegate])
122            .window.rootViewController;
123    HybridViewController *viewController =
124        (HybridViewController *)navController.visibleViewController;
125    GREYAssertNotNil(viewController.flutterViewController,
126                     @"Expected non-nil FlutterViewController.");
127    [self expectSemanticsNotification:viewController.flutterViewController];
128  }
129
130  [self validateCountsFlutter:@"Platform" count:0];
131  [self validateCountsPlatform:@"Flutter" count:_flutterWarmEngineTaps];
132
133  static const int platformTapCount = 4;
134  static const int flutterTapCount = 6;
135
136  for (int i = _flutterWarmEngineTaps; i < flutterTapCount;
137       i++, _flutterWarmEngineTaps++) {
138    [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(
139                                            @"Increment via Flutter")]
140        performAction:grey_tap()];
141  }
142
143  [self validateCountsFlutter:@"Platform" count:0];
144  [self validateCountsPlatform:@"Flutter" count:_flutterWarmEngineTaps];
145
146  for (int i = 0; i < platformTapCount; i++) {
147    [[EarlGrey
148        selectElementWithMatcher:grey_accessibilityLabel(@"Increment via iOS")]
149        performAction:grey_tap()];
150  }
151
152  [self validateCountsFlutter:@"Platform" count:platformTapCount];
153  [self validateCountsPlatform:@"Flutter" count:_flutterWarmEngineTaps];
154
155  [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Back")]
156      performAction:grey_tap()];
157  [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Native iOS View")]
158      assertWithMatcher:grey_sufficientlyVisible()];
159}
160
161/** Validates that the text labels showing the number of button taps match the
162 * expected counts. */
163- (void)validateCountsFlutter:(NSString *)labelPrefix count:(int)flutterCount {
164  NSString *flutterCountStr =
165      [NSString stringWithFormat:@"%@ button tapped %d times.", labelPrefix,
166                                 flutterCount];
167
168  // TODO(https://github.com/flutter/flutter/issues/17988): Flutter doesn't
169  // expose accessibility IDs, so the best we can do is to search for an element
170  // with the text we expect.
171  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(flutterCountStr)]
172      assertWithMatcher:grey_sufficientlyVisible()];
173}
174
175- (void)validateCountsPlatform:(NSString *)labelPrefix
176                         count:(int)platformCount {
177  NSString *platformCountStr =
178      [NSString stringWithFormat:@"%@ button tapped %d times.", labelPrefix,
179                                 platformCount];
180
181  [[[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"counter_on_iOS")]
182      assertWithMatcher:grey_text(platformCountStr)]
183      assertWithMatcher:grey_sufficientlyVisible()];
184}
185
186@end
187