| // Copyright 2014 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #import <EarlGrey/EarlGrey.h> | |
| #import <XCTest/XCTest.h> | |
| #import "../ios_add2app/AppDelegate.h" | |
| #import "../ios_add2app/DualFlutterViewController.h" | |
| #import "../ios_add2app/FullScreenViewController.h" | |
| #import "../ios_add2app/MainViewController.h" | |
| #import "../ios_add2app/HybridViewController.h" | |
| @interface FlutterTests : XCTestCase | |
| @end | |
| @implementation FlutterTests { | |
| int _flutterWarmEngineTaps; | |
| } | |
| - (instancetype)init { | |
| self = [super init]; | |
| if (self) { | |
| _flutterWarmEngineTaps = 0; | |
| } | |
| return self; | |
| } | |
| - (void)expectSemanticsNotification:(FlutterViewController*)viewController { | |
| [self expectationForNotification:FlutterSemanticsUpdateNotification object:viewController handler:nil]; | |
| [viewController.engine ensureSemanticsEnabled]; | |
| [self waitForExpectationsWithTimeout:30.0 handler:nil]; | |
| } | |
| - (void)testFullScreenCanPop { | |
| [[EarlGrey selectElementWithMatcher:grey_keyWindow()] | |
| assertWithMatcher:grey_sufficientlyVisible()]; | |
| [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Full Screen (Cold)")] | |
| performAction:grey_tap()]; | |
| __weak FlutterViewController *weakViewController; | |
| @autoreleasepool { | |
| UINavigationController *navController = | |
| (UINavigationController *)((AppDelegate *) | |
| [[UIApplication sharedApplication] | |
| delegate]) | |
| .window.rootViewController; | |
| weakViewController = | |
| (FullScreenViewController *)navController.visibleViewController; | |
| [self expectSemanticsNotification:weakViewController]; | |
| GREYAssertNotNil(weakViewController, | |
| @"Expected non-nil FullScreenViewController."); | |
| } | |
| [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"POP")] | |
| performAction:grey_tap()]; | |
| // EarlGrey v1 isn't good at detecting this yet - 2.0 will be able to do it | |
| int tries = 10; | |
| double delay = 1.0; | |
| while (weakViewController != nil && tries != 0) { | |
| CFRunLoopRunInMode(kCFRunLoopDefaultMode, delay, false); | |
| tries--; | |
| } | |
| [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Native iOS View")] | |
| assertWithMatcher:grey_sufficientlyVisible()]; | |
| GREYAssertNil(weakViewController, | |
| @"Expected FullScreenViewController to be deallocated."); | |
| } | |
| - (void)testDualFlutterView { | |
| [[EarlGrey selectElementWithMatcher:grey_keyWindow()] | |
| assertWithMatcher:grey_sufficientlyVisible()]; | |
| [[EarlGrey | |
| selectElementWithMatcher:grey_buttonTitle(@"Dual Flutter View (Cold)")] | |
| performAction:grey_tap()]; | |
| @autoreleasepool { | |
| UINavigationController *navController = | |
| (UINavigationController *)((AppDelegate *) | |
| [[UIApplication sharedApplication] | |
| delegate]) | |
| .window.rootViewController; | |
| DualFlutterViewController *viewController = | |
| (DualFlutterViewController *)navController.visibleViewController; | |
| GREYAssertNotNil(viewController, | |
| @"Expected non-nil DualFlutterViewController."); | |
| [self expectSemanticsNotification:viewController.topFlutterViewController]; | |
| [self expectSemanticsNotification:viewController.bottomFlutterViewController]; | |
| } | |
| // Verify that there are two Flutter views with the expected marquee text. | |
| [[[EarlGrey | |
| selectElementWithMatcher:grey_accessibilityLabel(@"This is Marquee")] | |
| atIndex:0] assertWithMatcher:grey_notNil()]; | |
| [[[EarlGrey | |
| selectElementWithMatcher:grey_accessibilityLabel(@"This is Marquee")] | |
| atIndex:1] assertWithMatcher:grey_notNil()]; | |
| [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Back")] | |
| performAction:grey_tap()]; | |
| [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Native iOS View")] | |
| assertWithMatcher:grey_sufficientlyVisible()]; | |
| } | |
| - (void)testHybridView { | |
| [[EarlGrey selectElementWithMatcher:grey_keyWindow()] | |
| assertWithMatcher:grey_sufficientlyVisible()]; | |
| [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Hybrid View (Warm)")] | |
| performAction:grey_tap()]; | |
| @autoreleasepool { | |
| UINavigationController *navController = | |
| (UINavigationController *)((AppDelegate *) | |
| [[UIApplication sharedApplication] | |
| delegate]) | |
| .window.rootViewController; | |
| HybridViewController *viewController = | |
| (HybridViewController *)navController.visibleViewController; | |
| GREYAssertNotNil(viewController.flutterViewController, | |
| @"Expected non-nil FlutterViewController."); | |
| [self expectSemanticsNotification:viewController.flutterViewController]; | |
| } | |
| [self validateCountsFlutter:@"Platform" count:0]; | |
| [self validateCountsPlatform:@"Flutter" count:_flutterWarmEngineTaps]; | |
| static const int platformTapCount = 4; | |
| static const int flutterTapCount = 6; | |
| for (int i = _flutterWarmEngineTaps; i < flutterTapCount; | |
| i++, _flutterWarmEngineTaps++) { | |
| [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel( | |
| @"Increment via Flutter")] | |
| performAction:grey_tap()]; | |
| } | |
| [self validateCountsFlutter:@"Platform" count:0]; | |
| [self validateCountsPlatform:@"Flutter" count:_flutterWarmEngineTaps]; | |
| for (int i = 0; i < platformTapCount; i++) { | |
| [[EarlGrey | |
| selectElementWithMatcher:grey_accessibilityLabel(@"Increment via iOS")] | |
| performAction:grey_tap()]; | |
| } | |
| [self validateCountsFlutter:@"Platform" count:platformTapCount]; | |
| [self validateCountsPlatform:@"Flutter" count:_flutterWarmEngineTaps]; | |
| [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Back")] | |
| performAction:grey_tap()]; | |
| [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Native iOS View")] | |
| assertWithMatcher:grey_sufficientlyVisible()]; | |
| } | |
| /** Validates that the text labels showing the number of button taps match the | |
| * expected counts. */ | |
| - (void)validateCountsFlutter:(NSString *)labelPrefix count:(int)flutterCount { | |
| NSString *flutterCountStr = | |
| [NSString stringWithFormat:@"%@ button tapped %d times.", labelPrefix, | |
| flutterCount]; | |
| // TODO(https://github.com/flutter/flutter/issues/17988): Flutter doesn't | |
| // expose accessibility IDs, so the best we can do is to search for an element | |
| // with the text we expect. | |
| [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(flutterCountStr)] | |
| assertWithMatcher:grey_sufficientlyVisible()]; | |
| } | |
| - (void)validateCountsPlatform:(NSString *)labelPrefix | |
| count:(int)platformCount { | |
| NSString *platformCountStr = | |
| [NSString stringWithFormat:@"%@ button tapped %d times.", labelPrefix, | |
| platformCount]; | |
| [[[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"counter_on_iOS")] | |
| assertWithMatcher:grey_text(platformCountStr)] | |
| assertWithMatcher:grey_sufficientlyVisible()]; | |
| } | |
| @end |