初始化提交

This commit is contained in:
2026-02-03 16:52:44 +08:00
commit d2f9806384
512 changed files with 65167 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate,UNUserNotificationCenterDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
@end

View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>
@interface FBAlertViewController : UIViewController
@end

View File

@@ -0,0 +1,82 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "FBAlertViewController.h"
#import <Photos/Photos.h>
#import <CoreLocation/CoreLocation.h>
@interface FBAlertViewController ()
@property (nonatomic, strong) CLLocationManager *locationManager;
@end
@implementation FBAlertViewController
- (IBAction)createAppAlert:(UIButton *)sender
{
[self presentAlertController];
}
- (IBAction)createAppSheet:(UIButton *)sender
{
UIAlertController *alerController =
[UIAlertController alertControllerWithTitle:@"Magic Sheet"
message:@"Should read"
preferredStyle:UIAlertControllerStyleActionSheet];
UIPopoverPresentationController *popPresenter = [alerController popoverPresentationController];
popPresenter.sourceView = sender;
[self presentViewController:alerController animated:YES completion:nil];
}
- (IBAction)createNotificationAlert:(UIButton *)sender
{
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound|UNAuthorizationOptionAlert|UNAuthorizationOptionBadge)
completionHandler:^(BOOL granted, NSError * _Nullable error)
{
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] registerForRemoteNotifications];
});
}];
}
- (IBAction)createCameraRollAccessAlert:(UIButton *)sender
{
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
}];
}
- (IBAction)createGPSAccessAlert:(UIButton *)sender
{
self.locationManager = [CLLocationManager new];
[self.locationManager requestAlwaysAuthorization];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
for (UITouch *touch in touches) {
if (fabs(touch.maximumPossibleForce - touch.force) < 0.0001) {
[self presentAlertController];
return;
}
}
}
- (void)presentAlertController
{
UIAlertController *alerController =
[UIAlertController alertControllerWithTitle:@"Magic"
message:@"Should read"
preferredStyle:UIAlertControllerStyleAlert];
[alerController addAction:[UIAlertAction actionWithTitle:@"Will do" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alerController animated:YES completion:nil];
}
@end

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
@interface FBNavigationController : UINavigationController
@end

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "FBNavigationController.h"
@implementation FBNavigationController
#if !TARGET_OS_TV
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
#endif
- (BOOL)shouldAutorotate
{
return YES;
}
@end

View File

@@ -0,0 +1,13 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
@interface FBScrollViewController : UIViewController
@end

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "FBScrollViewController.h"
#import "FBTableDataSource.h"
static const CGFloat FBSubviewHeight = 40.0;
@interface FBScrollViewController ()
@property (nonatomic, weak) IBOutlet UIScrollView *scrollView;
@property (nonatomic, strong) IBOutlet FBTableDataSource *dataSource;
@end
@implementation FBScrollViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setupLabelViews];
self.scrollView.contentSize = CGSizeMake(CGRectGetWidth(self.view.frame), self.dataSource.count * FBSubviewHeight);
}
- (void)setupLabelViews
{
NSUInteger count = self.dataSource.count;
for (NSInteger i = 0 ; i < count ; i++) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, i * FBSubviewHeight, CGRectGetWidth(self.view.frame), FBSubviewHeight)];
label.text = [self.dataSource textForElementAtIndex:i];
label.textAlignment = NSTextAlignmentCenter;
[self.scrollView addSubview:label];
}
}
@end

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
@interface FBTableDataSource : NSObject <UITableViewDataSource>
- (NSUInteger)count;
- (NSString *)textForElementAtIndex:(NSInteger)index;
@end

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "FBTableDataSource.h"
@implementation FBTableDataSource
- (NSUInteger)count
{
return 100;
}
- (NSString *)textForElementAtIndex:(NSInteger)index
{
return [NSString stringWithFormat:@"%ld", (long)index];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
cell.textLabel.text = [self textForElementAtIndex:indexPath.row];
return cell;
}
@end

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TouchSpotView : UIView
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "TouchSpotView.h"
@implementation TouchSpotView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = UIColor.lightGrayColor;
}
return self;
}
- (void)setBounds:(CGRect)newBounds
{
super.bounds = newBounds;
self.layer.cornerRadius = newBounds.size.width / 2.0;
}
@end

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import "TouchableView.h"
NS_ASSUME_NONNULL_BEGIN
@interface TouchViewController : UIViewController
@property (weak, nonatomic) IBOutlet TouchableView *touchable;
@property (weak, nonatomic) IBOutlet UILabel *numberOfTapsLabel;
@property (weak, nonatomic) IBOutlet UILabel *numberOfTouchesLabel;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "TouchViewController.h"
@implementation TouchViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.touchable.delegate = self;
self.numberOfTouchesLabel.text = @"0";
self.numberOfTapsLabel.text = @"0";
}
- (void)shouldHandleTapsNumber:(int)numberOfTaps {
self.numberOfTapsLabel.text = [NSString stringWithFormat:@"%d", numberOfTaps];
}
- (void)shouldHandleTouchesNumber:(int)touchesCount {
self.numberOfTouchesLabel.text = [NSString stringWithFormat:@"%d", touchesCount];
}
@end

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import "TouchSpotView.h"
NS_ASSUME_NONNULL_BEGIN
@protocol TouchableViewDelegate <NSObject>
- (void)shouldHandleTouchesNumber:(int)touchesCount;
- (void)shouldHandleTapsNumber:(int)numberOfTaps;
@end
@interface TouchableView : UIView
@property (nonatomic) NSMutableDictionary<NSNumber*, TouchSpotView*> *touchViews;
@property (nonatomic) int numberOFTaps;
@property (nonatomic) id delegate;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,108 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "TouchableView.h"
@implementation TouchableView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.multipleTouchEnabled = YES;
self.numberOFTaps = 0;
self.touchViews = [[NSMutableDictionary alloc] init];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
self.multipleTouchEnabled = YES;
self.numberOFTaps = 0;
self.touchViews = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.numberOFTaps += 1;
[self.delegate shouldHandleTouchesNumber:(int)touches.count];
for (UITouch *touch in touches)
{
[self createViewForTouch:touch];
}
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
TouchSpotView *view = [self viewForTouch:touch];
CGPoint newLocation = [touch locationInView:self];
view.center = newLocation;
}
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
[self removeViewForTouch:touch];
}
[self.delegate shouldHandleTapsNumber:self.numberOFTaps];
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
[self removeViewForTouch:touch];
}
}
- (void)createViewForTouch:(UITouch *)touch
{
if (touch)
{
TouchSpotView *newView = [[TouchSpotView alloc] init];
newView.bounds = CGRectMake(0, 0, 1, 1);
newView.center = [touch locationInView:self];
[self addSubview:newView];
[UIView animateWithDuration:0.2 animations:^{
newView.bounds = CGRectMake(0, 0, 100, 100);
}];
self.touchViews[[self touchHash:touch]] = newView;
}
}
- (TouchSpotView *)viewForTouch:(UITouch *)touch
{
return self.touchViews[[self touchHash:touch]];
}
- (void)removeViewForTouch:(UITouch *)touch
{
NSNumber *touchHash = [self touchHash:touch];
UIView *view = self.touchViews[touchHash];
if (view)
{
[view removeFromSuperview];
[self.touchViews removeObjectForKey:touchHash];
}
}
- (NSNumber *)touchHash:(UITouch *)touch
{
return [NSNumber numberWithUnsignedInteger:touch.hash];
}
@end

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *orentationLabel;
@end
@implementation ViewController
- (IBAction)deadlockApp:(id)sender
{
dispatch_sync(dispatch_get_main_queue(), ^{
// This will never execute
});
}
- (IBAction)didTapButton:(UIButton *)button
{
button.selected = !button.selected;
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self updateOrentationLabel];
}
#if !TARGET_OS_TV
- (void)updateOrentationLabel
{
NSString *orientation = nil;
switch (UIDevice.currentDevice.orientation) {
case UIInterfaceOrientationPortrait:
orientation = @"Portrait";
break;
case UIInterfaceOrientationPortraitUpsideDown:
orientation = @"PortraitUpsideDown";
break;
case UIInterfaceOrientationLandscapeLeft:
orientation = @"LandscapeLeft";
break;
case UIInterfaceOrientationLandscapeRight:
orientation = @"LandscapeRight";
break;
case UIDeviceOrientationFaceUp:
orientation = @"FaceUp";
break;
case UIDeviceOrientationFaceDown:
orientation = @"FaceDown";
break;
case UIInterfaceOrientationUnknown:
orientation = @"Unknown";
break;
}
self.orentationLabel.text = orientation;
}
#endif
@end

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Yo Yo</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Yo Yo</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Yo Yo</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Yo Yo</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,617 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="nJd-IZ-bfU">
<device id="retina6_5" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nbp-zX-Om1">
<rect key="frame" x="186.66666666666666" y="117" width="41" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="GzF-4f-MaH"/>
</constraints>
<state key="normal" title="Alerts"/>
<connections>
<segue destination="gK5-IX-eJx" kind="show" animates="NO" id="8RT-ON-yXm"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="YgP-SF-TkS">
<rect key="frame" x="159.66666666666666" y="155" width="95" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="wTu-YB-p22"/>
</constraints>
<state key="normal" title="Deadlock app"/>
<connections>
<action selector="deadlockApp:" destination="BYZ-38-t0r" eventType="touchUpInside" id="53X-DJ-KNY"/>
<action selector="showAlert:" destination="BYZ-38-t0r" eventType="touchUpInside" id="FEN-VX-MMc"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="M2N-Yn-ytb">
<rect key="frame" x="173" y="193" width="68" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="Sf6-pE-ROp"/>
</constraints>
<state key="normal" title="Attributes"/>
<connections>
<segue destination="65L-2T-0zt" kind="show" animates="NO" id="7cJ-OE-YWn"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="S56-6U-3gG">
<rect key="frame" x="177" y="231" width="60" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="7sH-TN-BNa"/>
</constraints>
<state key="normal" title="Scrolling"/>
<connections>
<segue destination="YzO-du-v3Y" kind="show" animates="NO" id="Ds1-bA-25D"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Orientation" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uiD-R4-b34">
<rect key="frame" x="164.66666666666666" y="269" width="85" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1Ht-AF-MGW">
<rect key="frame" x="186" y="298" width="42" height="30"/>
<state key="normal" title="Touch"/>
<connections>
<segue destination="XaE-eF-eIt" kind="show" id="q3u-B9-6R6"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" label="MainView"/>
<constraints>
<constraint firstItem="YgP-SF-TkS" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="41Q-cm-l5K"/>
<constraint firstItem="S56-6U-3gG" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="8UW-Nz-F04"/>
<constraint firstItem="1Ht-AF-MGW" firstAttribute="top" secondItem="uiD-R4-b34" secondAttribute="bottom" constant="8" id="8f1-dz-ei9"/>
<constraint firstItem="nbp-zX-Om1" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="Ahr-dg-qwa"/>
<constraint firstItem="1Ht-AF-MGW" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="BDr-QP-nJQ"/>
<constraint firstItem="nbp-zX-Om1" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" constant="29" id="GcP-yp-Ze6"/>
<constraint firstItem="uiD-R4-b34" firstAttribute="top" secondItem="S56-6U-3gG" secondAttribute="bottom" constant="8" id="SPc-4k-0MA"/>
<constraint firstItem="M2N-Yn-ytb" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="Yr3-Lt-qdn"/>
<constraint firstItem="YgP-SF-TkS" firstAttribute="top" secondItem="nbp-zX-Om1" secondAttribute="bottom" constant="8" id="b56-i5-Lxo"/>
<constraint firstItem="uiD-R4-b34" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="nFx-Xr-rGC"/>
<constraint firstItem="S56-6U-3gG" firstAttribute="top" secondItem="M2N-Yn-ytb" secondAttribute="bottom" constant="8" id="rMW-LW-ejO"/>
<constraint firstItem="M2N-Yn-ytb" firstAttribute="top" secondItem="YgP-SF-TkS" secondAttribute="bottom" constant="8" id="tMN-gl-smg"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="dmu-Fe-aoT"/>
<connections>
<outlet property="orentationLabel" destination="uiD-R4-b34" id="ysn-P0-ADp"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="39.375" y="612.67605633802816"/>
</scene>
<!--Touch View Controller-->
<scene sceneID="Sg1-EQ-IAj">
<objects>
<viewController id="XaE-eF-eIt" customClass="TouchViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="gEY-Ha-hqz"/>
<viewControllerLayoutGuide type="bottom" id="eVV-dI-eru"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="aEY-AK-0k9">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="C6B-Wo-jWm" customClass="TouchableView">
<rect key="frame" x="16" y="96.000000000000028" width="382" height="356.66666666666674"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" identifier="touchableView"/>
<constraints>
<constraint firstAttribute="width" secondItem="C6B-Wo-jWm" secondAttribute="height" multiplier="288:269" id="LeW-u7-tWl"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Taps" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IV8-Ak-Z8e">
<rect key="frame" x="139" y="458.66666666666669" width="131" height="21"/>
<accessibility key="accessibilityConfiguration" identifier="numberOfTapsLabel"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Touches" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Q4X-EY-aSL">
<rect key="frame" x="125" y="487.33333333333331" width="159" height="21"/>
<accessibility key="accessibilityConfiguration" identifier="numberOfTouchesLabel"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="C6B-Wo-jWm" firstAttribute="top" secondItem="gEY-Ha-hqz" secondAttribute="bottom" constant="8" id="1PW-tn-oTF"/>
<constraint firstAttribute="trailing" secondItem="IV8-Ak-Z8e" secondAttribute="trailing" constant="144" id="5Tn-XG-5OD"/>
<constraint firstAttribute="trailing" secondItem="Q4X-EY-aSL" secondAttribute="trailing" constant="130" id="6Rr-L0-eoR"/>
<constraint firstItem="IV8-Ak-Z8e" firstAttribute="firstBaseline" secondItem="C6B-Wo-jWm" secondAttribute="baseline" constant="24.5" symbolType="layoutAnchor" id="77y-Kf-c0Y"/>
<constraint firstAttribute="trailing" secondItem="C6B-Wo-jWm" secondAttribute="trailing" constant="16" id="CaC-dk-dbo"/>
<constraint firstItem="Q4X-EY-aSL" firstAttribute="leading" secondItem="aEY-AK-0k9" secondAttribute="leading" constant="125" id="Ych-oZ-hmA"/>
<constraint firstItem="Q4X-EY-aSL" firstAttribute="top" secondItem="IV8-Ak-Z8e" secondAttribute="bottom" constant="7.5" id="ktv-kW-VWI"/>
<constraint firstItem="C6B-Wo-jWm" firstAttribute="leading" secondItem="aEY-AK-0k9" secondAttribute="leading" constant="16" id="pI7-Ds-hXG"/>
<constraint firstItem="IV8-Ak-Z8e" firstAttribute="leading" secondItem="aEY-AK-0k9" secondAttribute="leading" constant="139" id="vbu-wn-Qvc"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="fDl-EC-1lc"/>
<connections>
<outlet property="numberOfTapsLabel" destination="IV8-Ak-Z8e" id="Ymh-Kr-C7w"/>
<outlet property="numberOfTouchesLabel" destination="Q4X-EY-aSL" id="fZ9-sH-tVY"/>
<outlet property="touchable" destination="C6B-Wo-jWm" id="6kR-LD-GPA"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="3Oz-AA-uE1" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-15" y="1514.7887323943662"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="bWj-vr-GrF">
<objects>
<navigationController id="nJd-IZ-bfU" customClass="FBNavigationController" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" id="hZx-8U-FDw">
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="Xg3-pw-x32"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="lCH-qk-44t" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-669" y="613"/>
</scene>
<!--View Controller-->
<scene sceneID="0V0-cO-CI5">
<objects>
<viewController id="65L-2T-0zt" customClass="ViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="dDG-qA-kaN"/>
<viewControllerLayoutGuide type="bottom" id="i3S-l1-3Mf"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ucg-Ag-VpP">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Value" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="mxs-lj-ChP">
<rect key="frame" x="20" y="99" width="374" height="34"/>
<accessibility key="accessibilityConfiguration" identifier="" label=""/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no"/>
</textField>
<pageControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" numberOfPages="3" translatesAutoresizingMaskIntoConstraints="NO" id="Kpo-oC-QzU">
<rect key="frame" x="161" y="324" width="39" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</pageControl>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" horizontalCompressionResistancePriority="749" verticalCompressionResistancePriority="749" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="LZF-72-rfb">
<rect key="frame" x="20" y="499" width="374" height="327"/>
<mutableString key="text">1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901</mutableString>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<datePicker contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" datePickerMode="dateAndTime" minuteInterval="1" style="wheels" translatesAutoresizingMaskIntoConstraints="NO" id="dxw-sJ-Dkr">
<rect key="frame" x="20" y="365" width="374" height="126"/>
<constraints>
<constraint firstAttribute="height" constant="126" id="QRU-5e-MQt"/>
</constraints>
</datePicker>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Exq-MB-zeu">
<rect key="frame" x="103.66666666666667" y="201" width="46.000000000000014" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="Otl-5O-2Nq"/>
</constraints>
<state key="normal" title="Button"/>
<connections>
<action selector="didTapButton:" destination="65L-2T-0zt" eventType="touchUpInside" id="G9g-YC-Y4j"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NJh-8E-4En">
<rect key="frame" x="106.66666666666667" y="242" width="42.000000000000014" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="VBZ-ac-GvI"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="Wim-fT-xNl">
<rect key="frame" x="64" y="267" width="131" height="29"/>
<constraints>
<constraint firstAttribute="height" constant="28" id="IoK-dx-Y4W"/>
</constraints>
<segments>
<segment title="First"/>
<segment title="Second"/>
</segments>
</segmentedControl>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="U7D-NR-20N">
<rect key="frame" x="207" y="276" width="118" height="31"/>
<constraints>
<constraint firstAttribute="width" constant="114" id="vy6-tS-yHd"/>
</constraints>
</slider>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SjL-dx-Zsi">
<rect key="frame" x="235" y="314" width="51" height="31"/>
<constraints>
<constraint firstAttribute="width" constant="49" id="dRJ-Hb-qNW"/>
</constraints>
</switch>
<progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="w6h-ic-qpa">
<rect key="frame" x="134" y="353" width="150" height="4"/>
<constraints>
<constraint firstAttribute="width" constant="150" id="7Tf-hh-LKX"/>
</constraints>
</progressView>
<stepper opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" maximumValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="u8X-Wb-Od3">
<rect key="frame" x="69" y="311" width="94" height="29"/>
<constraints>
<constraint firstAttribute="width" constant="94" id="bNi-xp-G14"/>
<constraint firstAttribute="height" constant="29" id="f0s-Rr-QTt"/>
</constraints>
</stepper>
<label opaque="NO" userInteractionEnabled="NO" alpha="0.0" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="alpha_invisible" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ucg-0s-c50">
<rect key="frame" x="224.33333333333334" y="197" width="111.66666666666666" height="21"/>
<color key="backgroundColor" red="1" green="0.0" blue="0.041046944598614132" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="664-Z3-V7W"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="hidden_invisible" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vt6-b1-U0h">
<rect key="frame" x="221.66666666666666" y="226" width="122.66666666666666" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="51j-sl-XYd"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GsS-jO-scK">
<rect key="frame" x="174" y="248" width="103" height="30"/>
<accessibility key="accessibilityConfiguration">
<bool key="isElement" value="NO"/>
</accessibility>
<constraints>
<constraint firstAttribute="height" constant="30" id="e1t-Kp-sno"/>
</constraints>
<state key="normal" title="not_accessible"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="point" keyPath="accessibilityActivationPoint">
<point key="value" x="200" y="220"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="bQv-RY-wkj">
<rect key="frame" x="171" y="320" width="20" height="20"/>
</activityIndicatorView>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Value2" borderStyle="roundedRect" textAlignment="natural" clearsOnBeginEditing="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Rab-I8-2aU">
<rect key="frame" x="20" y="141" width="374" height="34"/>
<accessibility key="accessibilityConfiguration" identifier="aIdentifier" label="aLabel"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no"/>
</textField>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="GsS-jO-scK" firstAttribute="leading" secondItem="Ucg-Ag-VpP" secondAttribute="leadingMargin" constant="154" id="1RA-Fx-3Jh"/>
<constraint firstItem="SjL-dx-Zsi" firstAttribute="top" secondItem="U7D-NR-20N" secondAttribute="bottom" constant="8" id="3NW-Hc-tPr"/>
<constraint firstItem="mxs-lj-ChP" firstAttribute="top" secondItem="dDG-qA-kaN" secondAttribute="bottom" constant="11" id="4Fw-0B-UwC"/>
<constraint firstItem="GsS-jO-scK" firstAttribute="top" secondItem="vt6-b1-U0h" secondAttribute="bottom" constant="1" id="4cH-4W-5qo"/>
<constraint firstItem="dxw-sJ-Dkr" firstAttribute="trailing" secondItem="Ucg-Ag-VpP" secondAttribute="trailingMargin" id="7fI-ha-3sR"/>
<constraint firstItem="mxs-lj-ChP" firstAttribute="leading" secondItem="Ucg-Ag-VpP" secondAttribute="leadingMargin" id="8mv-f6-WQ6"/>
<constraint firstItem="Rab-I8-2aU" firstAttribute="leading" secondItem="Ucg-Ag-VpP" secondAttribute="leadingMargin" id="AcZ-6f-OjT"/>
<constraint firstItem="LZF-72-rfb" firstAttribute="trailing" secondItem="Ucg-Ag-VpP" secondAttribute="trailingMargin" id="BPl-7D-KX8"/>
<constraint firstItem="dxw-sJ-Dkr" firstAttribute="top" secondItem="w6h-ic-qpa" secondAttribute="bottom" constant="8" id="CdC-6B-Sak"/>
<constraint firstItem="LZF-72-rfb" firstAttribute="leading" secondItem="Ucg-Ag-VpP" secondAttribute="leadingMargin" id="FXH-2O-HhB"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="GsS-jO-scK" secondAttribute="trailingMargin" id="GOy-RG-tCC"/>
<constraint firstItem="w6h-ic-qpa" firstAttribute="centerX" secondItem="Ucg-Ag-VpP" secondAttribute="centerX" constant="2" id="JAL-PW-u0w"/>
<constraint firstItem="i3S-l1-3Mf" firstAttribute="top" secondItem="LZF-72-rfb" secondAttribute="bottom" constant="36" id="K1W-hh-eAI"/>
<constraint firstItem="Exq-MB-zeu" firstAttribute="centerX" secondItem="Ucg-Ag-VpP" secondAttribute="centerX" constant="-80.5" id="KBS-g2-6jH"/>
<constraint firstItem="vt6-b1-U0h" firstAttribute="top" secondItem="ucg-0s-c50" secondAttribute="bottom" constant="8" id="Kgr-uw-g1y"/>
<constraint firstItem="Rab-I8-2aU" firstAttribute="trailing" secondItem="Ucg-Ag-VpP" secondAttribute="trailingMargin" id="PhW-Vk-nlO"/>
<constraint firstItem="u8X-Wb-Od3" firstAttribute="top" secondItem="Wim-fT-xNl" secondAttribute="bottom" constant="16" id="RFW-ZE-Wxe"/>
<constraint firstItem="Wim-fT-xNl" firstAttribute="centerX" secondItem="Ucg-Ag-VpP" secondAttribute="centerX" constant="-77.5" id="Sd1-7h-RwK"/>
<constraint firstItem="Wim-fT-xNl" firstAttribute="top" secondItem="NJh-8E-4En" secondAttribute="bottom" constant="4" id="TXS-dN-R68"/>
<constraint firstItem="NJh-8E-4En" firstAttribute="centerX" secondItem="Ucg-Ag-VpP" secondAttribute="centerX" constant="-79.5" id="Xqj-5G-gWd"/>
<constraint firstItem="w6h-ic-qpa" firstAttribute="top" secondItem="SjL-dx-Zsi" secondAttribute="bottom" constant="8" id="YmL-M2-lGN"/>
<constraint firstItem="Exq-MB-zeu" firstAttribute="top" secondItem="Rab-I8-2aU" secondAttribute="bottom" constant="26" id="aLQ-Gz-cjx"/>
<constraint firstItem="dxw-sJ-Dkr" firstAttribute="leading" secondItem="Ucg-Ag-VpP" secondAttribute="leadingMargin" id="amt-aZ-NnD"/>
<constraint firstItem="mxs-lj-ChP" firstAttribute="trailing" secondItem="Ucg-Ag-VpP" secondAttribute="trailingMargin" id="b9B-Xj-ypz"/>
<constraint firstItem="SjL-dx-Zsi" firstAttribute="centerX" secondItem="Ucg-Ag-VpP" secondAttribute="centerX" constant="52.5" id="c5J-nU-AFi"/>
<constraint firstItem="bQv-RY-wkj" firstAttribute="top" secondItem="Wim-fT-xNl" secondAttribute="bottom" constant="25" id="dOt-dS-sb7"/>
<constraint firstItem="bQv-RY-wkj" firstAttribute="leading" secondItem="u8X-Wb-Od3" secondAttribute="trailing" constant="8" id="dq2-ZM-aAA"/>
<constraint firstItem="LZF-72-rfb" firstAttribute="top" secondItem="dxw-sJ-Dkr" secondAttribute="bottom" constant="8" id="hqc-Vg-E1h"/>
<constraint firstItem="U7D-NR-20N" firstAttribute="top" secondItem="vt6-b1-U0h" secondAttribute="bottom" constant="29" id="n1M-yh-Yr6"/>
<constraint firstItem="u8X-Wb-Od3" firstAttribute="centerX" secondItem="Ucg-Ag-VpP" secondAttribute="centerX" constant="-91" id="ocA-VF-afs"/>
<constraint firstItem="vt6-b1-U0h" firstAttribute="centerX" secondItem="Ucg-Ag-VpP" secondAttribute="centerX" constant="76" id="odm-dZ-IbR"/>
<constraint firstItem="ucg-0s-c50" firstAttribute="top" secondItem="Rab-I8-2aU" secondAttribute="bottom" constant="22" id="pc0-2N-b6J"/>
<constraint firstItem="NJh-8E-4En" firstAttribute="top" secondItem="Exq-MB-zeu" secondAttribute="bottom" constant="11" id="qTa-xu-PhV"/>
<constraint firstItem="Rab-I8-2aU" firstAttribute="top" secondItem="mxs-lj-ChP" secondAttribute="bottom" constant="8" id="sDU-d6-48t"/>
<constraint firstItem="U7D-NR-20N" firstAttribute="centerX" secondItem="Ucg-Ag-VpP" secondAttribute="centerX" constant="59" id="spY-Aq-rh9"/>
<constraint firstItem="ucg-0s-c50" firstAttribute="centerX" secondItem="Ucg-Ag-VpP" secondAttribute="centerX" constant="73" id="xoU-03-2dV"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="Hk7-AH-JlS"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="0f2-qL-RU6" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1188.75" y="1217.9577464788733"/>
</scene>
<!--Alert View Controller-->
<scene sceneID="sNK-H9-0Zl">
<objects>
<viewController id="gK5-IX-eJx" customClass="FBAlertViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ew1-ik-0G1"/>
<viewControllerLayoutGuide type="bottom" id="LIO-6E-0HO"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="JWE-cA-TW0">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="y1x-J5-Nkw">
<rect key="frame" x="101.66666666666669" y="113" width="211" height="30"/>
<accessibility key="accessibilityConfiguration" identifier="textField"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="B7B-wI-RFc"/>
<constraint firstAttribute="width" constant="211" id="zSX-10-aQg"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4VK-4B-FEF">
<rect key="frame" x="149.66666666666666" y="194" width="113.99999999999997" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="xsL-Fq-mly"/>
</constraints>
<state key="normal" title="Create App Alert"/>
<connections>
<action selector="createAppAlert:" destination="gK5-IX-eJx" eventType="touchUpInside" id="t44-BA-HSi"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OQt-5y-Tft">
<rect key="frame" x="124" y="232" width="166" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="Y5y-Th-63a"/>
</constraints>
<state key="normal" title="Create Notification Alert"/>
<connections>
<action selector="createNotificationAlert:" destination="gK5-IX-eJx" eventType="touchUpInside" id="haL-pV-hTV"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rG5-3k-LFe">
<rect key="frame" x="122" y="270" width="169" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="im5-w3-Pgi"/>
</constraints>
<state key="normal" title="Create Camera Roll Alert"/>
<connections>
<action selector="createCameraRollAccessAlert:" destination="gK5-IX-eJx" eventType="touchUpInside" id="T8R-HK-sEe"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ntr-45-Ycd">
<rect key="frame" x="123" y="308" width="168" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="4B7-LM-UwV"/>
</constraints>
<state key="normal" title="Create GPS access Alert"/>
<connections>
<action selector="createGPSAccessAlert:" destination="gK5-IX-eJx" eventType="touchUpInside" id="gcM-Ip-10b"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CZ1-7J-OGl">
<rect key="frame" x="66.666666666666671" y="176" width="46" height="30"/>
<accessibility key="accessibilityConfiguration" identifier="button"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="tJH-wr-O3J"/>
</constraints>
<state key="normal" title="Button"/>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="plO-2e-XmX">
<rect key="frame" x="304.66666666666669" y="180" width="42" height="21"/>
<accessibility key="accessibilityConfiguration" identifier="label"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="2qA-NT-ddc"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3Zk-wE-jwj">
<rect key="frame" x="144" y="346" width="126" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="Mk4-bk-Y0L"/>
</constraints>
<state key="normal" title="Create Sheet Alert"/>
<connections>
<action selector="createAppSheet:" destination="gK5-IX-eJx" eventType="touchUpInside" id="U6X-fw-xU2"/>
</connections>
</button>
<button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="D7g-22-s49">
<rect key="frame" x="116" y="384" width="182" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="TzV-CY-64y"/>
</constraints>
<state key="normal" title="Create Alert (Force Touch)"/>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="3Zk-wE-jwj" firstAttribute="centerX" secondItem="JWE-cA-TW0" secondAttribute="centerX" id="83u-1W-xLt"/>
<constraint firstItem="D7g-22-s49" firstAttribute="centerX" secondItem="JWE-cA-TW0" secondAttribute="centerX" id="9nT-k0-9gf"/>
<constraint firstItem="ntr-45-Ycd" firstAttribute="top" secondItem="rG5-3k-LFe" secondAttribute="bottom" constant="8" id="DYt-NB-TSO"/>
<constraint firstItem="3Zk-wE-jwj" firstAttribute="top" secondItem="ntr-45-Ycd" secondAttribute="bottom" constant="8" id="Edq-zL-Soc"/>
<constraint firstItem="y1x-J5-Nkw" firstAttribute="centerX" secondItem="CZ1-7J-OGl" secondAttribute="centerX" constant="117.5" id="Ell-4O-Iqa"/>
<constraint firstItem="4VK-4B-FEF" firstAttribute="top" secondItem="y1x-J5-Nkw" secondAttribute="bottom" constant="51" id="JnR-cD-vZE"/>
<constraint firstItem="OQt-5y-Tft" firstAttribute="centerX" secondItem="CZ1-7J-OGl" secondAttribute="centerX" constant="117.5" id="LQq-Br-Fda"/>
<constraint firstItem="y1x-J5-Nkw" firstAttribute="centerX" secondItem="JWE-cA-TW0" secondAttribute="centerX" id="M6w-gn-nXE"/>
<constraint firstItem="ntr-45-Ycd" firstAttribute="centerX" secondItem="CZ1-7J-OGl" secondAttribute="centerX" constant="117.5" id="Qj1-d9-KDa"/>
<constraint firstItem="CZ1-7J-OGl" firstAttribute="top" secondItem="y1x-J5-Nkw" secondAttribute="bottom" constant="33" id="UHk-wV-Ehk"/>
<constraint firstItem="D7g-22-s49" firstAttribute="top" secondItem="3Zk-wE-jwj" secondAttribute="bottom" constant="8" id="eML-6C-Von"/>
<constraint firstItem="plO-2e-XmX" firstAttribute="top" secondItem="y1x-J5-Nkw" secondAttribute="bottom" constant="37" id="f0Z-LP-5Dg"/>
<constraint firstItem="4VK-4B-FEF" firstAttribute="centerX" secondItem="CZ1-7J-OGl" secondAttribute="centerX" constant="117" id="fDG-Jl-VER"/>
<constraint firstItem="y1x-J5-Nkw" firstAttribute="top" secondItem="Ew1-ik-0G1" secondAttribute="bottom" constant="25" id="gaL-qu-PB6"/>
<constraint firstItem="plO-2e-XmX" firstAttribute="centerX" secondItem="CZ1-7J-OGl" secondAttribute="centerX" constant="236" id="h2I-eE-doE"/>
<constraint firstItem="OQt-5y-Tft" firstAttribute="top" secondItem="4VK-4B-FEF" secondAttribute="bottom" constant="8" id="q3k-Mr-OUS"/>
<constraint firstItem="rG5-3k-LFe" firstAttribute="top" secondItem="OQt-5y-Tft" secondAttribute="bottom" constant="8" id="tBP-U5-8Lx"/>
<constraint firstItem="rG5-3k-LFe" firstAttribute="centerX" secondItem="CZ1-7J-OGl" secondAttribute="centerX" constant="117" id="wCj-0t-gIY"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="nfx-Tv-5Z0"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="B30-xl-1JX" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1216" y="-319"/>
</scene>
<!--View Controller-->
<scene sceneID="9RM-JC-4C4">
<objects>
<viewController id="YzO-du-v3Y" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="yvD-8P-ibI"/>
<viewControllerLayoutGuide type="bottom" id="REj-tv-GYS"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="55R-aH-x4M">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vYb-QB-vwU">
<rect key="frame" x="172.66666666666666" y="108" width="69" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="ymh-D3-SCA"/>
</constraints>
<state key="normal" title="TableView"/>
<connections>
<segue destination="3ur-D9-8FT" kind="show" animates="NO" id="yJb-Fl-2fY"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FdB-fa-0bl">
<rect key="frame" x="171" y="146" width="72" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="Xas-AZ-Nyw"/>
</constraints>
<state key="normal" title="ScrollView"/>
<connections>
<segue destination="GI0-zX-z7l" kind="show" animates="NO" id="iVA-s3-kPd"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="vYb-QB-vwU" firstAttribute="centerX" secondItem="55R-aH-x4M" secondAttribute="centerX" id="0MT-Eq-8dG"/>
<constraint firstItem="vYb-QB-vwU" firstAttribute="top" secondItem="yvD-8P-ibI" secondAttribute="bottom" constant="20" id="c3y-G3-HCj"/>
<constraint firstItem="FdB-fa-0bl" firstAttribute="centerX" secondItem="55R-aH-x4M" secondAttribute="centerX" id="gE9-Mi-Jcm"/>
<constraint firstItem="FdB-fa-0bl" firstAttribute="top" secondItem="vYb-QB-vwU" secondAttribute="bottom" constant="8" id="zLx-S1-Tnz"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="6Qr-Yi-87y"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="CvZ-nE-CBk" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1216" y="422"/>
</scene>
<!--Table View Controller-->
<scene sceneID="ow4-nH-afF">
<objects>
<tableViewController id="3ur-D9-8FT" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="jo0-3M-nUd">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="2mC-E2-VxB" style="IBUITableViewCellStyleDefault" id="wCs-18-PFf">
<rect key="frame" x="0.0" y="50" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="wCs-18-PFf" id="0KF-km-gbT">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="2mC-E2-VxB">
<rect key="frame" x="20" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<sections/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="accessibilityIdentifier" value="scrollView"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="dataSource" destination="TxP-Aa-p9i" id="IDu-Y4-oQL"/>
<outlet property="delegate" destination="3ur-D9-8FT" id="saC-SY-CSo"/>
</connections>
</tableView>
<navigationItem key="navigationItem" id="szz-4K-52y"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="21y-mk-Cmx" userLabel="First Responder" sceneMemberID="firstResponder"/>
<customObject id="TxP-Aa-p9i" customClass="FBTableDataSource"/>
</objects>
<point key="canvasLocation" x="2302" y="97"/>
</scene>
<!--Scroll View Controller-->
<scene sceneID="cZ2-Pn-cVV">
<objects>
<viewController id="GI0-zX-z7l" customClass="FBScrollViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="o0k-ze-PY2"/>
<viewControllerLayoutGuide type="bottom" id="1qn-YR-FCf"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="KLL-RT-L40">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xEQ-MI-a2g">
<rect key="frame" x="0.0" y="0.0" width="414" height="862"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="accessibilityIdentifier" value="scrollView"/>
</userDefinedRuntimeAttributes>
</scrollView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="xEQ-MI-a2g" firstAttribute="top" secondItem="KLL-RT-L40" secondAttribute="top" id="Bep-no-nwg"/>
<constraint firstAttribute="trailing" secondItem="xEQ-MI-a2g" secondAttribute="trailing" id="akP-zz-jsv"/>
<constraint firstItem="xEQ-MI-a2g" firstAttribute="leading" secondItem="KLL-RT-L40" secondAttribute="leading" id="eVn-mL-sBX"/>
<constraint firstItem="1qn-YR-FCf" firstAttribute="top" secondItem="xEQ-MI-a2g" secondAttribute="bottom" id="kg2-Z3-oRO"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="QQ9-xy-IwS"/>
<connections>
<outlet property="dataSource" destination="Vxa-lD-ODn" id="ti0-a9-iaY"/>
<outlet property="scrollView" destination="xEQ-MI-a2g" id="ZBD-iW-x6Q"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="fpw-oW-WXe" userLabel="First Responder" sceneMemberID="firstResponder"/>
<customObject id="Vxa-lD-ODn" customClass="FBTableDataSource"/>
</objects>
<point key="canvasLocation" x="2302" y="746"/>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, @"AppDelegate");
}
}

View File

@@ -0,0 +1,188 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import <WebDriverAgentLib/FBAlert.h>
#import "FBConfiguration.h"
#import "FBIntegrationTestCase.h"
#import "FBTestMacros.h"
#import "FBMacros.h"
@interface FBAlertTests : FBIntegrationTestCase
@end
@implementation FBAlertTests
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
[self goToAlertsPage];
[FBConfiguration disableApplicationUIInterruptionsHandling];
});
[self clearAlert];
}
- (void)tearDown
{
[self clearAlert];
[super tearDown];
}
- (void)showApplicationAlert
{
[self.testedApplication.buttons[FBShowAlertButtonName] tap];
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count != 0);
}
- (void)showApplicationSheet
{
[self.testedApplication.buttons[FBShowSheetAlertButtonName] tap];
FBAssertWaitTillBecomesTrue(self.testedApplication.sheets.count != 0);
}
- (void)testAlertPresence
{
FBAlert *alert = [FBAlert alertWithApplication:self.testedApplication];
XCTAssertFalse(alert.isPresent);
[self showApplicationAlert];
XCTAssertTrue(alert.isPresent);
}
- (void)testAlertText
{
FBAlert *alert = [FBAlert alertWithApplication:self.testedApplication];
XCTAssertNil(alert.text);
[self showApplicationAlert];
XCTAssertTrue([alert.text containsString:@"Magic"]);
XCTAssertTrue([alert.text containsString:@"Should read"]);
}
- (void)testAlertLabels
{
FBAlert* alert = [FBAlert alertWithApplication:self.testedApplication];
XCTAssertNil(alert.buttonLabels);
[self showApplicationAlert];
XCTAssertNotNil(alert.buttonLabels);
XCTAssertEqual(1, alert.buttonLabels.count);
XCTAssertEqualObjects(@"Will do", alert.buttonLabels[0]);
}
- (void)testClickAlertButton
{
FBAlert* alert = [FBAlert alertWithApplication:self.testedApplication];
XCTAssertFalse([alert clickAlertButton:@"Invalid" error:nil]);
[self showApplicationAlert];
XCTAssertFalse([alert clickAlertButton:@"Invalid" error:nil]);
FBAssertWaitTillBecomesTrue(alert.isPresent);
XCTAssertTrue([alert clickAlertButton:@"Will do" error:nil]);
FBAssertWaitTillBecomesTrue(!alert.isPresent);
}
- (void)testAcceptingAlert
{
NSError *error;
[self showApplicationAlert];
XCTAssertTrue([[FBAlert alertWithApplication:self.testedApplication] acceptWithError:&error]);
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count == 0);
XCTAssertNil(error);
}
- (void)testAcceptingAlertWithCustomLocator
{
NSError *error;
[self showApplicationAlert];
[FBConfiguration setAcceptAlertButtonSelector:@"**/XCUIElementTypeButton[-1]"];
@try {
XCTAssertTrue([[FBAlert alertWithApplication:self.testedApplication] acceptWithError:&error]);
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count == 0);
XCTAssertNil(error);
} @finally {
[FBConfiguration setAcceptAlertButtonSelector:@""];
}
}
- (void)testDismissingAlert
{
NSError *error;
[self showApplicationAlert];
XCTAssertTrue([[FBAlert alertWithApplication:self.testedApplication] dismissWithError:&error]);
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count == 0);
XCTAssertNil(error);
}
- (void)testDismissingAlertWithCustomLocator
{
NSError *error;
[self showApplicationAlert];
[FBConfiguration setDismissAlertButtonSelector:@"**/XCUIElementTypeButton[-1]"];
@try {
XCTAssertTrue([[FBAlert alertWithApplication:self.testedApplication] dismissWithError:&error]);
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count == 0);
XCTAssertNil(error);
} @finally {
[FBConfiguration setDismissAlertButtonSelector:@""];
}
}
- (void)testAlertElement
{
[self showApplicationAlert];
XCUIElement *alertElement = [FBAlert alertWithApplication:self.testedApplication].alertElement;
XCTAssertTrue(alertElement.exists);
XCTAssertTrue(alertElement.elementType == XCUIElementTypeAlert);
}
- (void)testNotificationAlert
{
FBAlert *alert = [FBAlert alertWithApplication:self.testedApplication];
XCTAssertNil(alert.text);
[self.testedApplication.buttons[@"Create Notification Alert"] tap];
FBAssertWaitTillBecomesTrue(alert.isPresent);
XCTAssertTrue([alert.text containsString:@"Would Like to Send You Notifications"]);
XCTAssertTrue([alert.text containsString:@"Notifications may include"]);
}
// This test case depends on the local app permission state.
- (void)testCameraRollAlert
{
FBAlert *alert = [FBAlert alertWithApplication:self.testedApplication];
XCTAssertNil(alert.text);
[self.testedApplication.buttons[@"Create Camera Roll Alert"] tap];
FBAssertWaitTillBecomesTrue(alert.isPresent);
// "Would Like to Access Your Photos" or "Would Like to Access Your Photo Library" displayes on the alert button.
XCTAssertTrue([alert.text containsString:@"Would Like to Access Your Photo"]);
// iOS 15 has different UI flow
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"15.0")) {
[[FBAlert alertWithApplication:self.testedApplication] dismissWithError:nil];
// CI env could take longer time to show up the button, thus it needs to wait a bit.
XCTAssertTrue([self.testedApplication.buttons[@"Cancel"] waitForExistenceWithTimeout:30.0]);
[self.testedApplication.buttons[@"Cancel"] tap];
}
}
- (void)testGPSAccessAlert
{
FBAlert *alert = [FBAlert alertWithApplication:self.testedApplication];
XCTAssertNil(alert.text);
[self.testedApplication.buttons[@"Create GPS access Alert"] tap];
FBAssertWaitTillBecomesTrue(alert.isPresent);
XCTAssertTrue([alert.text containsString:@"location"]);
XCTAssertTrue([alert.text containsString:@"Yo Yo"]);
}
@end

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBMacros.h"
#import "FBSession.h"
#import "FBXCodeCompatibility.h"
#import "FBTestMacros.h"
#import "XCUIElement+FBUtilities.h"
@interface FBAutoAlertsHandlerTests : FBIntegrationTestCase
@property (nonatomic) FBSession *session;
@end
@implementation FBAutoAlertsHandlerTests
- (void)setUp
{
[super setUp];
[self launchApplication];
[self goToAlertsPage];
[self clearAlert];
}
- (void)tearDown
{
[self clearAlert];
if (self.session) {
[self.session kill];
}
[super tearDown];
}
// The test is flaky on slow Travis CI
- (void)disabled_testAutoAcceptingOfAlerts
{
self.session = [FBSession
initWithApplication:XCUIApplication.fb_activeApplication
defaultAlertAction:@"accept"];
for (int i = 0; i < 2; i++) {
[self.testedApplication.buttons[FBShowAlertButtonName] tap];
[self.testedApplication fb_waitUntilStable];
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count == 0);
}
}
// The test is flaky on slow Travis CI
- (void)disabled_testAutoDismissingOfAlerts
{
self.session = [FBSession
initWithApplication:XCUIApplication.fb_activeApplication
defaultAlertAction:@"dismiss"];
for (int i = 0; i < 2; i++) {
[self.testedApplication.buttons[FBShowAlertButtonName] tap];
[self.testedApplication fb_waitUntilStable];
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count == 0);
}
}
@end

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBConfiguration.h"
#import "FBRuntimeUtils.h"
@interface FBConfigurationTests : FBIntegrationTestCase
@end
@implementation FBConfigurationTests
- (void)setUp
{
[super setUp];
[self launchApplication];
}
- (void)testReduceMotion
{
BOOL defaultReduceMotionEnabled = [FBConfiguration reduceMotionEnabled];
[FBConfiguration setReduceMotionEnabled:YES];
XCTAssertTrue([FBConfiguration reduceMotionEnabled]);
[FBConfiguration setReduceMotionEnabled:defaultReduceMotionEnabled];
XCTAssertEqual([FBConfiguration reduceMotionEnabled], defaultReduceMotionEnabled);
}
@end

View File

@@ -0,0 +1,250 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBFindElementCommands.h"
#import "FBTestMacros.h"
#import "FBXCodeCompatibility.h"
#import "XCUIElement+FBAccessibility.h"
#import "XCUIElement+FBIsVisible.h"
#import "XCUIElement+FBWebDriverAttributes.h"
@interface FBElementAttributeTests : FBIntegrationTestCase
@end
@implementation FBElementAttributeTests
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
[self goToAttributesPage];
});
}
- (void)testElementAccessibilityAttributes
{
// "Button" is accessibility element, and therefore isn't accessibility container
XCUIElement *buttonElement = self.testedApplication.buttons[@"Button"];
XCTAssertTrue(buttonElement.exists);
XCTAssertTrue(buttonElement.fb_isAccessibilityElement);
XCTAssertFalse(buttonElement.isWDAccessibilityContainer);
}
- (void)testContainerAccessibilityAttributes
{
// "not_accessible" isn't accessibility element, but contains accessibility elements, so it is accessibility container
XCUIElement *inaccessibleButtonElement = self.testedApplication.buttons[@"not_accessible"];
XCTAssertTrue(inaccessibleButtonElement.exists);
XCTAssertFalse(inaccessibleButtonElement.fb_isAccessibilityElement);
// FIXME: Xcode 11 environment returns false even if iOS 12
// We must fix here to XCTAssertTrue if Xcode version will return the value properly
XCTAssertFalse(inaccessibleButtonElement.isWDAccessibilityContainer);
}
- (void)testIgnoredAccessibilityAttributes
{
// Images are neither accessibility elements nor contain them, so both checks should fail
XCUIElement *imageElement = self.testedApplication.images.allElementsBoundByIndex.firstObject;
XCTAssertTrue(imageElement.exists);
XCTAssertFalse(imageElement.fb_isAccessibilityElement);
XCTAssertFalse(imageElement.isWDAccessibilityContainer);
}
- (void)testButtonAttributes
{
XCUIElement *element = self.testedApplication.buttons[@"Button"];
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypeButton");
XCTAssertEqualObjects(element.wdName, @"Button");
XCTAssertEqualObjects(element.wdLabel, @"Button");
XCTAssertNil(element.wdValue);
XCTAssertFalse(element.wdSelected);
XCTAssertTrue(element.fb_isVisible);
[element tap];
XCTAssertTrue(element.wdValue.boolValue);
XCTAssertTrue(element.wdSelected);
}
- (void)testLabelAttributes
{
XCUIElement *element = self.testedApplication.staticTexts[@"Label"];
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypeStaticText");
XCTAssertEqualObjects(element.wdName, @"Label");
XCTAssertEqualObjects(element.wdLabel, @"Label");
XCTAssertEqualObjects(element.wdValue, @"Label");
}
- (void)testIndexAttributes
{
XCUIElement *element = self.testedApplication.buttons[@"Button"];
XCTAssertTrue(element.exists);
XCTAssertEqual(element.wdIndex, 2);
XCUIElement *element2 = self.testedApplication;
XCTAssertTrue(element2.exists);
XCTAssertEqual(element2.wdIndex, 0);
}
- (void)testAccessibilityTraits
{
XCUIElement *button = self.testedApplication.buttons.firstMatch;
XCTAssertTrue(button.exists);
NSArray *buttonTraits = [button.wdTraits componentsSeparatedByString:@", "];
NSArray *expectedButtonTraits = @[@"Button"];
XCTAssertEqual(buttonTraits.count, expectedButtonTraits.count, @"Button should have exactly 1 trait");
XCTAssertEqualObjects(buttonTraits, expectedButtonTraits);
XCTAssertEqualObjects(button.wdType, @"XCUIElementTypeButton");
XCUIElement *toggle = self.testedApplication.switches.firstMatch;
XCTAssertTrue(toggle.exists);
// iOS 17.0 specific traits if available
NSArray *toggleTraits = [toggle.wdTraits componentsSeparatedByString:@", "];
NSArray *expectedToggleTraits;
#if __clang_major__ >= 16
if (@available(iOS 17.0, *)) {
expectedToggleTraits = @[@"ToggleButton", @"Button"];
XCTAssertEqual(toggleTraits.count, 2, @"Toggle should have exactly 2 traits on iOS 17+");
}
#else
expectedToggleTraits = @[@"Button"];
XCTAssertEqual(toggleTraits.count, 1, @"Toggle should have exactly 1 trait on iOS < 17");
#endif
XCTAssertEqualObjects(toggleTraits, expectedToggleTraits);
XCTAssertEqualObjects(toggle.wdType, @"XCUIElementTypeSwitch");
XCUIElement *slider = self.testedApplication.sliders.firstMatch;
XCTAssertTrue(slider.exists);
NSArray *sliderTraits = [slider.wdTraits componentsSeparatedByString:@", "];
NSArray *expectedSliderTraits = @[@"Adjustable"];
XCTAssertEqual(sliderTraits.count, expectedSliderTraits.count, @"Slider should have exactly 1 trait");
XCTAssertEqualObjects(sliderTraits, expectedSliderTraits);
XCTAssertEqualObjects(slider.wdType, @"XCUIElementTypeSlider");
XCUIElement *picker = self.testedApplication.pickerWheels.firstMatch;
XCTAssertTrue(picker.exists);
NSArray *pickerTraits = [picker.wdTraits componentsSeparatedByString:@", "];
NSArray *expectedPickerTraits = @[@"Adjustable"];
XCTAssertEqual(pickerTraits.count, expectedPickerTraits.count, @"Picker should have exactly 1 trait");
XCTAssertEqualObjects(pickerTraits, expectedPickerTraits);
XCTAssertEqualObjects(picker.wdType, @"XCUIElementTypePickerWheel");
}
- (void)testTextFieldAttributes
{
XCUIElement *element = self.testedApplication.textFields[@"Value"];
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypeTextField");
XCTAssertNil(element.wdName);
XCTAssertEqualObjects(element.wdLabel, @"");
XCTAssertEqualObjects(element.wdValue, @"Value");
}
- (void)testTextFieldWithAccessibilityIdentifiersAttributes
{
XCUIElement *element = self.testedApplication.textFields[@"aIdentifier"];
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypeTextField");
XCTAssertEqualObjects(element.wdName, @"aIdentifier");
XCTAssertEqualObjects(element.wdLabel, @"aLabel");
XCTAssertEqualObjects(element.wdValue, @"Value2");
}
- (void)testSegmentedControlAttributes
{
XCUIElement *element = self.testedApplication.segmentedControls.element;
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypeSegmentedControl");
XCTAssertNil(element.wdName);
XCTAssertNil(element.wdLabel);
XCTAssertNil(element.wdValue);
}
- (void)testSliderAttributes
{
XCUIElement *element = self.testedApplication.sliders.element;
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypeSlider");
XCTAssertNil(element.wdName);
XCTAssertNil(element.wdLabel);
XCTAssertTrue([element.wdValue containsString:@"50"]);
NSNumber *minValue = element.wdMinValue;
NSNumber *maxValue = element.wdMaxValue;
XCTAssertNotNil(minValue, @"Slider minValue should not be nil");
XCTAssertNotNil(maxValue, @"Slider maxValue should not be nil");
XCTAssertEqualObjects(minValue, @0);
XCTAssertEqualObjects(maxValue, @1);
}
- (void)testActivityIndicatorAttributes
{
XCUIElement *element = self.testedApplication.activityIndicators.element;
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypeActivityIndicator");
XCTAssertEqualObjects(element.wdName, @"Progress halted");
XCTAssertEqualObjects(element.wdLabel, @"Progress halted");
XCTAssertEqualObjects(element.wdValue, @"0");
}
- (void)testSwitchAttributes
{
XCUIElement *element = self.testedApplication.switches.element;
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypeSwitch");
XCTAssertNil(element.wdName);
XCTAssertNil(element.wdLabel);
XCTAssertNil(element.wdPlaceholderValue);
XCTAssertEqualObjects(element.wdValue, @"1");
XCTAssertFalse(element.wdSelected);
XCTAssertEqual(element.wdHittable, element.hittable);
[element tap];
XCTAssertEqualObjects(element.wdValue, @"0");
XCTAssertFalse(element.wdSelected);
}
- (void)testPickerWheelAttributes
{
XCUIElement *element = self.testedApplication.pickerWheels[@"Today"];
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypePickerWheel");
XCTAssertNil(element.wdName);
XCTAssertNil(element.wdLabel);
XCTAssertEqualObjects(element.wdValue, @"Today");
}
- (void)testPageIndicatorAttributes
{
XCUIElement *element = self.testedApplication.pageIndicators.element;
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypePageIndicator");
XCTAssertNil(element.wdName);
XCTAssertNil(element.wdLabel);
XCTAssertEqualObjects(element.wdValue, @"page 1 of 3");
}
- (void)testTextViewAttributes
{
XCUIElement *element = self.testedApplication.textViews.element;
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypeTextView");
XCTAssertNil(element.wdName);
XCTAssertNil(element.wdLabel);
XCTAssertEqualObjects(element.wdValue, @"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901");
}
@end

View File

@@ -0,0 +1,127 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBTestMacros.h"
#import "XCUIElement+FBWebDriverAttributes.h"
#import "FBXCodeCompatibility.h"
#import "XCUIElement+FBSwiping.h"
@interface FBElementSwipingTests : FBIntegrationTestCase
@property (nonatomic, strong) XCUIElement *scrollView;
- (void)openScrollView;
@end
@implementation FBElementSwipingTests
- (void)openScrollView
{
[self launchApplication];
[self goToScrollPageWithCells:YES];
self.scrollView = [[self.testedApplication.query descendantsMatchingType:XCUIElementTypeAny] matchingIdentifier:@"scrollView"].element;
}
- (void)setUp
{
[super setUp];
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"15.0")) {
[self openScrollView];
} else {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self openScrollView];
});
}
}
- (void)tearDown
{
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"15.0")) {
// Move to top page once to reset the scroll place
// since iOS 15 seems cannot handle cell visibility well when the view keps the view
[self.testedApplication terminate];
}
}
- (void)testSwipeUp
{
[self.scrollView fb_swipeWithDirection:@"up" velocity:nil];
FBAssertInvisibleCell(@"0");
}
- (void)testSwipeDown
{
[self.scrollView fb_swipeWithDirection:@"up" velocity:nil];
FBAssertInvisibleCell(@"0");
[self.scrollView fb_swipeWithDirection:@"down" velocity:nil];
FBAssertVisibleCell(@"0");
}
- (void)testSwipeDownWithVelocity
{
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
XCTSkip(@"Failed on Azure Pipeline. Local run succeeded.");
}
[self.scrollView fb_swipeWithDirection:@"up" velocity:@2500];
FBAssertInvisibleCell(@"0");
[self.scrollView fb_swipeWithDirection:@"down" velocity:@3000];
FBAssertVisibleCell(@"0");
}
@end
@interface FBElementSwipingApplicationTests : FBIntegrationTestCase
@property (nonatomic, strong) XCUIElement *scrollView;
- (void)openScrollView;
@end
@implementation FBElementSwipingApplicationTests
- (void)openScrollView
{
[self launchApplication];
[self goToScrollPageWithCells:YES];
}
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self openScrollView];
});
}
- (void)testSwipeUp
{
[self.testedApplication fb_swipeWithDirection:@"up" velocity:nil];
FBAssertInvisibleCell(@"0");
}
- (void)testSwipeDown
{
[self.testedApplication fb_swipeWithDirection:@"up" velocity:nil];
FBAssertInvisibleCell(@"0");
[self.testedApplication fb_swipeWithDirection:@"down" velocity:nil];
FBAssertVisibleCell(@"0");
}
- (void)testSwipeDownWithVelocity
{
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
XCTSkip(@"Failed on Azure Pipeline. Local run succeeded.");
}
[self.testedApplication fb_swipeWithDirection:@"up" velocity:@2500];
FBAssertInvisibleCell(@"0");
[self.testedApplication fb_swipeWithDirection:@"down" velocity:@2500];
FBAssertVisibleCell(@"0");
}
@end

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBMacros.h"
#import "FBTestMacros.h"
#import "FBXCodeCompatibility.h"
#import "XCUIElement+FBIsVisible.h"
@interface FBElementVisibilityTests : FBIntegrationTestCase
@end
@implementation FBElementVisibilityTests
- (void)testSpringBoardIcons
{
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
return;
}
[self launchApplication];
[self goToSpringBoardFirstPage];
// Check Icons on first screen
// Note: Calender app exits 2 (an app icon + a widget) exist on the home screen
// on iOS 15+. The firstMatch is for it.
XCTAssertTrue(self.springboard.icons[@"Calendar"].firstMatch.fb_isVisible);
XCTAssertTrue(self.springboard.icons[@"Reminders"].fb_isVisible);
// Check Icons on second screen screen
XCTAssertFalse(self.springboard.icons[@"IntegrationApp"].firstMatch.fb_isVisible);
}
- (void)testSpringBoardSubfolder
{
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad
|| SYSTEM_VERSION_GREATER_THAN(@"12.0")) {
return;
}
[self launchApplication];
[self goToSpringBoardExtras];
XCTAssertFalse(self.springboard.icons[@"Extras"].otherElements[@"Contacts"].fb_isVisible);
}
- (void)disabled_testIconsFromSearchDashboard
{
// This test causes:
// Failure fetching attributes for element <XCAccessibilityElement: 0x60800044dd10> Device element: Error Domain=XCTDaemonErrorDomain Code=13 "Value for attribute 5017 is an error." UserInfo={NSLocalizedDescription=Value for attribute 5017 is an error.}
[self launchApplication];
[self goToSpringBoardDashboard];
XCTAssertFalse(self.springboard.icons[@"Reminders"].fb_isVisible);
XCTAssertFalse([[[self.springboard descendantsMatchingType:XCUIElementTypeIcon]
matchingIdentifier:@"IntegrationApp"]
firstMatch].fb_isVisible);
}
- (void)testTableViewCells
{
[self launchApplication];
[self goToScrollPageWithCells:YES];
for (int i = 0 ; i < 10 ; i++) {
FBAssertWaitTillBecomesTrue(self.testedApplication.cells.allElementsBoundByIndex[i].fb_isVisible);
FBAssertWaitTillBecomesTrue(self.testedApplication.staticTexts.allElementsBoundByIndex[i].fb_isVisible);
}
}
@end

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBFailureProofTestCase.h"
#import "FBExceptionHandler.h"
@interface FBFailureProofTestCaseTests : FBFailureProofTestCase
@end
@implementation FBFailureProofTestCaseTests
- (void)setUp
{
[super setUp];
[[XCUIApplication new] launch];
}
- (void)testPreventElementSearchFailure
{
[[XCUIApplication new].buttons[@"kaboom"] tap];
}
- (void)testInactiveAppSearch
{
[[XCUIDevice sharedDevice] pressButton:XCUIDeviceButtonHome];
[[XCUIApplication new].buttons[@"kaboom"] tap];
}
- (void)testPreventAssertFailure
{
XCTAssertNotNil(nil);
}
@end

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBMacros.h"
#import "FBElementCache.h"
#import "FBTestMacros.h"
#import "XCUIDevice.h"
#import "XCUIDevice+FBRotation.h"
#import "XCUIElement+FBForceTouch.h"
#import "XCUIElement+FBIsVisible.h"
@interface FBForceTouchTests : FBIntegrationTestCase
@end
// It is recommnded to verify these tests with different iOS versions
@implementation FBForceTouchTests
- (void)verifyForceTapWithOrientation:(UIDeviceOrientation)orientation
{
[[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:orientation];
NSError *error;
XCTAssertTrue(self.testedApplication.alerts.count == 0);
[self.testedApplication.buttons[FBShowAlertForceTouchButtonName] fb_forceTouchCoordinate:nil
pressure:nil
duration:nil
error:&error];
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count > 0);
}
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
[self goToAlertsPage];
});
[self clearAlert];
}
- (void)tearDown
{
[self clearAlert];
[self resetOrientation];
[super tearDown];
}
- (void)testForceTap
{
if (![XCUIDevice sharedDevice].supportsPressureInteraction) {
return;
}
[self verifyForceTapWithOrientation:UIDeviceOrientationPortrait];
}
- (void)testForceTapInLandscapeLeft
{
if (![XCUIDevice sharedDevice].supportsPressureInteraction) {
return;
}
[self verifyForceTapWithOrientation:UIDeviceOrientationLandscapeLeft];
}
- (void)testForceTapInLandscapeRight
{
if (![XCUIDevice sharedDevice].supportsPressureInteraction) {
return;
}
[self verifyForceTapWithOrientation:UIDeviceOrientationLandscapeRight];
}
@end

View File

@@ -0,0 +1,82 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBImageProcessor.h"
#import "FBIntegrationTestCase.h"
@interface FBImageProcessorTests : FBIntegrationTestCase
@property (nonatomic) NSData *originalImage;
@property (nonatomic) CGSize originalSize;
@end
@implementation FBImageProcessorTests
- (void)setUp {
XCUIApplication *app = [[XCUIApplication alloc] init];
[app launch];
XCUIScreenshot *screenshot = app.screenshot;
self.originalImage = UIImageJPEGRepresentation(screenshot.image, 1.0);
self.originalSize = [FBImageProcessorTests scaledSizeFromImage:screenshot.image];
}
- (void)testScaling {
CGFloat halfScale = 0.5;
CGSize expectedHalfScaleSize = [FBImageProcessorTests sizeFromSize:self.originalSize scalingFactor:0.5];
[self scaleImageWithFactor:halfScale
expectedSize:expectedHalfScaleSize];
// 0 is the smalles scaling factor we accept
CGFloat minScale = 0.0;
CGSize expectedMinScaleSize = [FBImageProcessorTests sizeFromSize:self.originalSize scalingFactor:0.01];
[self scaleImageWithFactor:minScale
expectedSize:expectedMinScaleSize];
// For scaling factors above 100 we don't perform any scaling and just return the unmodified image
[self scaleImageWithFactor:1.0
expectedSize:self.originalSize];
[self scaleImageWithFactor:2.0
expectedSize:self.originalSize];
}
- (void)scaleImageWithFactor:(CGFloat)scalingFactor expectedSize:(CGSize)excpectedSize {
FBImageProcessor *scaler = [[FBImageProcessor alloc] init];
id expScaled = [self expectationWithDescription:@"Receive scaled image"];
[scaler submitImageData:self.originalImage
scalingFactor:scalingFactor
completionHandler:^(NSData *scaled) {
UIImage *scaledImage = [UIImage imageWithData:scaled];
CGSize scaledSize = [FBImageProcessorTests scaledSizeFromImage:scaledImage];
XCTAssertEqualWithAccuracy(scaledSize.width, excpectedSize.width, 1.0);
XCTAssertEqualWithAccuracy(scaledSize.height, excpectedSize.height, 1.0);
[expScaled fulfill];
}];
[self waitForExpectations:@[expScaled]
timeout:0.5];
}
+ (CGSize)scaledSizeFromImage:(UIImage *)image {
return CGSizeMake(image.size.width * image.scale, image.size.height * image.scale);
}
+ (CGSize)sizeFromSize:(CGSize)size scalingFactor:(CGFloat)scalingFactor {
return CGSizeMake(round(size.width * scalingFactor), round(size.height * scalingFactor));
}
@end

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
extern NSString *const FBShowAlertButtonName;
extern NSString *const FBShowSheetAlertButtonName;
extern NSString *const FBShowAlertForceTouchButtonName;
extern NSString *const FBTouchesCountLabelIdentifier;
extern NSString *const FBTapsCountLabelIdentifier;
/**
XCTestCase helper class used for integration tests
*/
@interface FBIntegrationTestCase : XCTestCase
@property (nonatomic, strong, readonly) XCUIApplication *testedApplication;
@property (nonatomic, strong, readonly) XCUIApplication *springboard;
/**
Launches application and resets side effects of testing like orientation etc.
*/
- (void)launchApplication;
/**
Navigates integration app to attributes page
*/
- (void)goToAttributesPage;
/**
Navigates integration app to alerts page
*/
- (void)goToAlertsPage;
/**
Navigates integration app to touch page
*/
- (void)goToTouchPage;
/**
Navigates to SpringBoard first page
*/
- (void)goToSpringBoardFirstPage;
/**
Navigates to SpringBoard path with Extras folder
*/
- (void)goToSpringBoardExtras;
/**
Navigates to SpringBoard's dashboard
*/
- (void)goToSpringBoardDashboard;
/**
Navigates integration app to scrolling page
@param showCells whether should navigate to view with cell or plain scrollview
*/
- (void)goToScrollPageWithCells:(BOOL)showCells;
/**
Verifies no alerts are present on the page.
If an alert exists then it is going to be dismissed.
*/
- (void)clearAlert;
/**
Resets device orientation to portrait mode
*/
- (void)resetOrientation;
@end

View File

@@ -0,0 +1,140 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBAlert.h"
#import "FBTestMacros.h"
#import "FBIntegrationTestCase.h"
#import "FBConfiguration.h"
#import "FBMacros.h"
#import "FBRunLoopSpinner.h"
#import "XCUIApplication+FBHelpers.h"
#import "XCUIDevice+FBRotation.h"
#import "XCUIElement.h"
#import "XCUIElement+FBIsVisible.h"
#import "XCUIElement+FBUtilities.h"
#import "XCTestConfiguration.h"
NSString *const FBShowAlertButtonName = @"Create App Alert";
NSString *const FBShowSheetAlertButtonName = @"Create Sheet Alert";
NSString *const FBShowAlertForceTouchButtonName = @"Create Alert (Force Touch)";
NSString *const FBTouchesCountLabelIdentifier = @"numberOfTouchesLabel";
NSString *const FBTapsCountLabelIdentifier = @"numberOfTapsLabel";
@interface FBIntegrationTestCase ()
@property (nonatomic, strong) XCUIApplication *testedApplication;
@property (nonatomic, strong) XCUIApplication *springboard;
@end
@implementation FBIntegrationTestCase
- (void)setUp
{
// Enable it to get extended XCTest logs printed into the console
// [FBConfiguration enableXcTestDebugLogs];
[super setUp];
[FBConfiguration disableRemoteQueryEvaluation];
[FBConfiguration disableAttributeKeyPathAnalysis];
[FBConfiguration configureDefaultKeyboardPreferences];
[FBConfiguration disableApplicationUIInterruptionsHandling];
[FBConfiguration disableScreenshots];
self.continueAfterFailure = NO;
self.springboard = XCUIApplication.fb_systemApplication;
self.testedApplication = [XCUIApplication new];
}
- (void)resetOrientation
{
if ([XCUIDevice sharedDevice].orientation != UIDeviceOrientationPortrait) {
[[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:UIDeviceOrientationPortrait];
}
}
- (void)launchApplication
{
[self.testedApplication launch];
[self.testedApplication fb_waitUntilStable];
FBAssertWaitTillBecomesTrue(self.testedApplication.buttons[@"Alerts"].fb_isVisible);
}
- (void)goToAttributesPage
{
[self.testedApplication.buttons[@"Attributes"] tap];
[self.testedApplication fb_waitUntilStable];
FBAssertWaitTillBecomesTrue(self.testedApplication.buttons[@"Button"].fb_isVisible);
}
- (void)goToAlertsPage
{
[self.testedApplication.buttons[@"Alerts"] tap];
[self.testedApplication fb_waitUntilStable];
FBAssertWaitTillBecomesTrue(self.testedApplication.buttons[FBShowAlertButtonName].fb_isVisible);
FBAssertWaitTillBecomesTrue(self.testedApplication.buttons[FBShowSheetAlertButtonName].fb_isVisible);
}
- (void)goToTouchPage
{
[self.testedApplication.buttons[@"Touch"] tap];
[self.testedApplication fb_waitUntilStable];
FBAssertWaitTillBecomesTrue(self.testedApplication.staticTexts[FBTouchesCountLabelIdentifier].fb_isVisible);
FBAssertWaitTillBecomesTrue(self.testedApplication.staticTexts[FBTapsCountLabelIdentifier].fb_isVisible);
}
- (void)goToSpringBoardFirstPage
{
[[XCUIDevice sharedDevice] pressButton:XCUIDeviceButtonHome];
[self.testedApplication fb_waitUntilStable];
FBAssertWaitTillBecomesTrue(XCUIApplication.fb_systemApplication.icons[@"Safari"].exists);
[[XCUIDevice sharedDevice] pressButton:XCUIDeviceButtonHome];
[self.testedApplication fb_waitUntilStable];
FBAssertWaitTillBecomesTrue(XCUIApplication.fb_systemApplication.icons[@"Calendar"].firstMatch.fb_isVisible);
}
- (void)goToSpringBoardExtras
{
[self goToSpringBoardFirstPage];
[self.springboard swipeLeft];
[self.testedApplication fb_waitUntilStable];
FBAssertWaitTillBecomesTrue(self.springboard.icons[@"Extras"].fb_isVisible);
}
- (void)goToSpringBoardDashboard
{
[self goToSpringBoardFirstPage];
[self.springboard swipeRight];
[self.testedApplication fb_waitUntilStable];
NSPredicate *predicate =
[NSPredicate predicateWithFormat:
@"%K IN %@",
FBStringify(XCUIElement, identifier),
@[@"SBSearchEtceteraIsolatedView", @"SpotlightSearchField"]
];
FBAssertWaitTillBecomesTrue([[self.springboard descendantsMatchingType:XCUIElementTypeAny] elementMatchingPredicate:predicate].fb_isVisible);
FBAssertWaitTillBecomesTrue(!self.springboard.icons[@"Calendar"].fb_isVisible);
}
- (void)goToScrollPageWithCells:(BOOL)showCells
{
[self.testedApplication.buttons[@"Scrolling"] tap];
[self.testedApplication fb_waitUntilStable];
FBAssertWaitTillBecomesTrue(self.testedApplication.buttons[@"TableView"].fb_isVisible);
[self.testedApplication.buttons[showCells ? @"TableView": @"ScrollView"] tap];
[self.testedApplication fb_waitUntilStable];
FBAssertWaitTillBecomesTrue(self.testedApplication.staticTexts[@"3"].fb_isVisible);
}
- (void)clearAlert
{
[self.testedApplication fb_waitUntilStable];
[[FBAlert alertWithApplication:self.testedApplication] dismissWithError:nil];
[self.testedApplication fb_waitUntilStable];
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count == 0);
}
@end

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBMacros.h"
#import "FBIntegrationTestCase.h"
#import "FBKeyboard.h"
#import "FBRunLoopSpinner.h"
#import "XCUIApplication+FBHelpers.h"
@interface FBKeyboardTests : FBIntegrationTestCase
@end
@implementation FBKeyboardTests
- (void)setUp
{
[super setUp];
[self launchApplication];
[self goToAttributesPage];
}
- (void)testKeyboardDismissal
{
XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];
[textField tap];
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"15.0")) {
// A workaround until find out to clear tutorial on iOS 15
XCUIElement *textField = self.testedApplication.staticTexts[@"Continue"];
if (textField.hittable) {
[textField tap];
}
}
NSError *error;
XCTAssertTrue([FBKeyboard waitUntilVisibleForApplication:self.testedApplication timeout:1 error:&error]);
XCTAssertNil(error);
if ([UIDevice.currentDevice userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
XCTAssertTrue([self.testedApplication fb_dismissKeyboardWithKeyNames:nil error:&error]);
XCTAssertNil(error);
} else {
XCTAssertFalse([self.testedApplication fb_dismissKeyboardWithKeyNames:@[@"return"] error:&error]);
XCTAssertNotNil(error);
}
}
- (void)testKeyboardPresenceVerification
{
NSError *error;
XCTAssertFalse([FBKeyboard waitUntilVisibleForApplication:self.testedApplication timeout:1 error:&error]);
XCTAssertNotNil(error);
}
@end

View File

@@ -0,0 +1,91 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "XCUIElement+FBTyping.h"
#import "FBPasteboard.h"
#import "FBTestMacros.h"
#import "FBXCodeCompatibility.h"
@interface FBPasteboardTests : FBIntegrationTestCase
@end
@implementation FBPasteboardTests
- (void)setUp
{
[super setUp];
[self launchApplication];
[self goToAttributesPage];
}
- (void)testSetPasteboard
{
NSString *text = @"Happy pasting";
XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];
NSError *error;
BOOL result = [FBPasteboard setData:(NSData *)[text dataUsingEncoding:NSUTF8StringEncoding]
forType:@"plaintext"
error:&error];
XCTAssertTrue(result);
XCTAssertNil(error);
[textField tap];
XCTAssertTrue([textField fb_clearTextWithError:&error]);
[textField pressForDuration:2.0];
XCUIElementQuery *pastItemsQuery = [[self.testedApplication descendantsMatchingType:XCUIElementTypeAny] matchingIdentifier:@"Paste"];
if (![pastItemsQuery.firstMatch waitForExistenceWithTimeout:2.0]) {
XCTFail(@"No matched element named 'Paste'");
}
XCUIElement *pasteItem = pastItemsQuery.firstMatch;
XCTAssertNotNil(pasteItem);
[pasteItem tap];
FBAssertWaitTillBecomesTrue([textField.value isEqualToString:text]);
}
- (void)testGetPasteboard
{
NSString *text = @"Happy copying";
XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];
NSError *error;
XCTAssertTrue([textField fb_typeText:text shouldClear:NO error:&error]);
[textField pressForDuration:2.0];
XCUIElement *selectAllItem = [[self.testedApplication descendantsMatchingType:XCUIElementTypeAny]
matchingIdentifier:@"Select All"].firstMatch;
XCTAssertTrue([selectAllItem waitForExistenceWithTimeout:5]);
[selectAllItem tap];
if (SYSTEM_VERSION_LESS_THAN(@"16.0")) {
[textField pressForDuration:2.0];
}
XCUIElement *copyItem = [[self.testedApplication descendantsMatchingType:XCUIElementTypeAny]
matchingIdentifier:@"Copy"].firstMatch;
XCTAssertTrue([copyItem waitForExistenceWithTimeout:5]);
[copyItem tap];
FBWaitExact(1.0);
NSData *result = [FBPasteboard dataForType:@"plaintext" error:&error];
XCTAssertNil(error);
XCTAssertEqualObjects(textField.value, [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]);
}
- (void)testUrlCopyPaste
{
NSString *urlString = @"http://appium.io?some=value";
NSError *error;
XCTAssertTrue([FBPasteboard setData:(NSData *)[urlString dataUsingEncoding:NSUTF8StringEncoding]
forType:@"url"
error:&error]);
XCTAssertNil(error);
NSData *result = [FBPasteboard dataForType:@"url" error:&error];
XCTAssertNil(error);
XCTAssertEqualObjects(urlString, [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]);
}
@end

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "XCUIElement+FBPickerWheel.h"
#import "XCUIElement+FBWebDriverAttributes.h"
#import "FBXCodeCompatibility.h"
@interface FBPickerWheelSelectTests : FBIntegrationTestCase
@end
@implementation FBPickerWheelSelectTests
static const CGFloat DEFAULT_OFFSET = (CGFloat)0.2;
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
[self goToAttributesPage];
});
}
- (void)testSelectNextPickerValue
{
XCUIElement *element = self.testedApplication.pickerWheels.allElementsBoundByIndex.firstObject;
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypePickerWheel");
NSError *error;
NSString *previousValue = element.wdValue;
XCTAssertTrue([element fb_selectNextOptionWithOffset:DEFAULT_OFFSET error:&error]);
XCTAssertNotEqualObjects(previousValue, element.wdValue);
}
- (void)testSelectPreviousPickerValue
{
XCUIElement *element = [self.testedApplication.pickerWheels elementBoundByIndex:1];
XCTAssertTrue(element.exists);
XCTAssertEqualObjects(element.wdType, @"XCUIElementTypePickerWheel");
NSError *error;
NSString *previousValue = element.wdValue;
XCTAssertTrue([element fb_selectPreviousOptionWithOffset:DEFAULT_OFFSET error:&error]);
XCTAssertNotEqualObjects(previousValue, element.wdValue);
}
@end

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBTestMacros.h"
#import "FBMacros.h"
#import "FBSession.h"
#import "FBXCodeCompatibility.h"
#import "XCUIElement+FBTyping.h"
#import "FBAlert.h"
#import "XCUIApplication+FBAlert.h"
@interface FBSafariAlertIntegrationTests : FBIntegrationTestCase
@property (nonatomic) FBSession *session;
@property (nonatomic) XCUIApplication *safariApp;
@end
@implementation FBSafariAlertIntegrationTests
- (void)setUp
{
[super setUp];
self.session = [FBSession initWithApplication:XCUIApplication.fb_activeApplication];
[self.session launchApplicationWithBundleId:FB_SAFARI_BUNDLE_ID
shouldWaitForQuiescence:nil
arguments:nil
environment:nil];
self.safariApp = self.session.activeApplication;
}
- (void)tearDown
{
[self.session terminateApplicationWithBundleId:FB_SAFARI_BUNDLE_ID];
}
- (void)disabled_testCanHandleSafariInputPrompt
{
XCUIElement *urlInput = [[self.safariApp
descendantsMatchingType:XCUIElementTypeTextField]
matchingPredicate:[
NSPredicate predicateWithFormat:@"label == 'Address' or label == 'URL'"
]].firstMatch;
if (!urlInput.exists) {
[[[self.safariApp descendantsMatchingType:XCUIElementTypeButton] matchingPredicate:[
NSPredicate predicateWithFormat:@"label == 'Address' or label == 'URL'"]].firstMatch tap];
}
XCTAssertTrue([urlInput fb_typeText:@"https://www.w3schools.com/js/tryit.asp?filename=tryjs_alert"
shouldClear:YES
error:nil]);
[[[self.safariApp descendantsMatchingType:XCUIElementTypeButton] matchingIdentifier:@"Go"].firstMatch tap];
XCUIElement *clickMeButton = [[self.safariApp descendantsMatchingType:XCUIElementTypeButton]
matchingPredicate:[NSPredicate predicateWithFormat:@"label == 'Try it'"]].firstMatch;
XCTAssertTrue([clickMeButton waitForExistenceWithTimeout:15.0]);
[clickMeButton tap];
FBAlert *alert = [FBAlert alertWithApplication:self.safariApp];
FBAssertWaitTillBecomesTrue([alert.text isEqualToString:@"I am an alert box!"]);
NSArray *buttonLabels = alert.buttonLabels;
XCTAssertEqualObjects(buttonLabels.firstObject, @"Close");
XCTAssertNotNil([self.safariApp fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeButton[@label='Close']"
shouldReturnAfterFirstMatch:YES].firstObject);
XCTAssertTrue([alert acceptWithError:nil]);
}
@end

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBScreen.h"
@interface FBScreenTests : FBIntegrationTestCase
@end
@implementation FBScreenTests
- (void)setUp
{
[super setUp];
[self launchApplication];
}
- (void)testScreenScale
{
XCTAssertTrue([FBScreen scale] >= 2);
}
@end

View File

@@ -0,0 +1,126 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBTestMacros.h"
#import "FBMacros.h"
#import "XCUIElement+FBIsVisible.h"
#import "XCUIElement+FBScrolling.h"
#import "XCUIElement+FBClassChain.h"
#import "FBXCodeCompatibility.h"
@interface FBScrollingTests : FBIntegrationTestCase
@property (nonatomic, strong) XCUIElement *scrollView;
@end
@implementation FBScrollingTests
+ (BOOL)shouldShowCells
{
return YES;
}
- (void)setUp
{
[super setUp];
[self launchApplication];
[self goToScrollPageWithCells:YES];
self.scrollView = [[self.testedApplication.query descendantsMatchingType:XCUIElementTypeAny] matchingIdentifier:@"scrollView"].element;
}
- (void)testCellVisibility
{
FBAssertVisibleCell(@"0");
FBAssertVisibleCell(@"10");
XCUIElement *cell10 = FBCellElementWithLabel(@"10");
XCTAssertEqual([cell10 isWDHittable], [cell10 isHittable]);
FBAssertInvisibleCell(@"30");
FBAssertInvisibleCell(@"50");
XCUIElement *cell50 = FBCellElementWithLabel(@"50");
XCTAssertEqual([cell50 isWDHittable], [cell50 isHittable]);
}
- (void)testSimpleScroll
{
if (SYSTEM_VERSION_LESS_THAN(@"16.0")) {
// This test is unstable in CI env
return;
}
FBAssertVisibleCell(@"0");
FBAssertVisibleCell(@"10");
[self.scrollView fb_scrollDownByNormalizedDistance:1.0];
FBAssertInvisibleCell(@"0");
FBAssertInvisibleCell(@"10");
XCTAssertTrue(self.testedApplication.staticTexts.count > 0);
[self.scrollView fb_scrollUpByNormalizedDistance:1.0];
FBAssertVisibleCell(@"0");
FBAssertVisibleCell(@"10");
}
- (void)testScrollToVisible
{
NSString *cellName = @"30";
FBAssertInvisibleCell(cellName);
NSError *error;
XCTAssertTrue([FBCellElementWithLabel(cellName) fb_scrollToVisibleWithError:&error]);
XCTAssertNil(error);
FBAssertVisibleCell(cellName);
}
- (void)testFarScrollToVisible
{
NSString *cellName = @"80";
NSError *error;
FBAssertInvisibleCell(cellName);
XCTAssertTrue([FBCellElementWithLabel(cellName) fb_scrollToVisibleWithError:&error]);
XCTAssertNil(error);
FBAssertVisibleCell(cellName);
}
- (void)testNativeFarScrollToVisible
{
if (SYSTEM_VERSION_LESS_THAN(@"16.0")) {
// This test is unstable in CI env
return;
}
NSString *cellName = @"80";
NSError *error;
FBAssertInvisibleCell(cellName);
XCTAssertTrue([FBCellElementWithLabel(cellName) fb_nativeScrollToVisibleWithError:&error]);
XCTAssertNil(error);
FBAssertVisibleCell(cellName);
}
- (void)testAttributeWithNullScrollToVisible
{
NSError *error;
NSArray<XCUIElement *> *queryMatches = [self.testedApplication fb_descendantsMatchingClassChain:@"**/XCUIElementTypeTable/XCUIElementTypeCell[60]" shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(queryMatches.count, 1);
XCUIElement *element = queryMatches.firstObject;
XCTAssertFalse(element.fb_isVisible);
[element fb_scrollToVisibleWithError:&error];
XCTAssertNil(error);
XCTAssertTrue(element.fb_isVisible);
if (SYSTEM_VERSION_LESS_THAN(@"16.0")) {
// This test is unstable in CI env
return;
}
[element tap];
XCTAssertTrue(element.wdSelected);
}
@end

View File

@@ -0,0 +1,133 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBExceptions.h"
#import "FBMacros.h"
#import "FBSession.h"
#import "FBXCodeCompatibility.h"
#import "FBTestMacros.h"
#import "FBUnattachedAppLauncher.h"
#import "XCUIApplication+FBHelpers.h"
#import "XCUIApplication.h"
@interface FBSession (Tests)
@end
@interface FBSessionIntegrationTests : FBIntegrationTestCase
@property (nonatomic) FBSession *session;
@end
static NSString *const SETTINGS_BUNDLE_ID = @"com.apple.Preferences";
@implementation FBSessionIntegrationTests
- (void)setUp
{
[super setUp];
[self launchApplication];
XCUIApplication *app = [[XCUIApplication alloc] initWithBundleIdentifier:self.testedApplication.bundleID];
self.session = [FBSession initWithApplication:app];
}
- (void)tearDown
{
[self.session kill];
[super tearDown];
}
- (void)testSettingsAppCanBeOpenedInScopeOfTheCurrentSession
{
XCUIApplication *testedApp = XCUIApplication.fb_activeApplication;
[self.session launchApplicationWithBundleId:SETTINGS_BUNDLE_ID
shouldWaitForQuiescence:nil
arguments:nil
environment:nil];
FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:SETTINGS_BUNDLE_ID]);
XCTAssertEqual([self.session applicationStateWithBundleId:SETTINGS_BUNDLE_ID], 4);
[self.session activateApplicationWithBundleId:testedApp.bundleID];
FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString: testedApp.bundleID]);
XCTAssertEqual([self.session applicationStateWithBundleId:testedApp.bundleID], 4);
}
- (void)testSettingsAppCanBeReopenedInScopeOfTheCurrentSession
{
XCUIApplication *systemApp = self.springboard;
[self.session launchApplicationWithBundleId:SETTINGS_BUNDLE_ID
shouldWaitForQuiescence:nil
arguments:nil
environment:nil];
FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:SETTINGS_BUNDLE_ID]);
XCTAssertTrue([self.session terminateApplicationWithBundleId:SETTINGS_BUNDLE_ID]);
FBAssertWaitTillBecomesTrue([systemApp.bundleID isEqualToString:self.session.activeApplication.bundleID]);
[self.session launchApplicationWithBundleId:SETTINGS_BUNDLE_ID
shouldWaitForQuiescence:nil
arguments:nil
environment:nil];
FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:SETTINGS_BUNDLE_ID]);
}
- (void)testMainAppCanBeReactivatedInScopeOfTheCurrentSession
{
XCUIApplication *testedApp = XCUIApplication.fb_activeApplication;
[self.session launchApplicationWithBundleId:SETTINGS_BUNDLE_ID
shouldWaitForQuiescence:nil
arguments:nil
environment:nil];
FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:SETTINGS_BUNDLE_ID]);
[self.session activateApplicationWithBundleId:testedApp.bundleID];
FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:testedApp.bundleID]);
}
- (void)testMainAppCanBeRestartedInScopeOfTheCurrentSession
{
XCUIApplication *systemApp = self.springboard;
XCUIApplication *testedApp = [[XCUIApplication alloc] initWithBundleIdentifier:self.testedApplication.bundleID];
[self.session terminateApplicationWithBundleId:testedApp.bundleID];
FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:systemApp.bundleID]);
[self.session launchApplicationWithBundleId:testedApp.bundleID
shouldWaitForQuiescence:nil
arguments:nil
environment:nil];
FBAssertWaitTillBecomesTrue([self.session.activeApplication.bundleID isEqualToString:testedApp.bundleID]);
}
- (void)testLaunchUnattachedApp
{
[FBUnattachedAppLauncher launchAppWithBundleId:SETTINGS_BUNDLE_ID];
[self.session kill];
XCTAssertEqualObjects(SETTINGS_BUNDLE_ID, XCUIApplication.fb_activeApplication.bundleID);
}
- (void)testAppWithInvalidBundleIDCannotBeStarted
{
XCUIApplication *testedApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"yolo"];
@try {
[testedApp launch];
XCTFail(@"An exception is expected to be thrown");
} @catch (NSException *exception) {
XCTAssertEqualObjects(FBApplicationMissingException, exception.name);
}
}
- (void)testAppWithInvalidBundleIDCannotBeActivated
{
XCUIApplication *testedApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"yolo"];
@try {
[testedApp activate];
XCTFail(@"An exception is expected to be thrown");
} @catch (NSException *exception) {
XCTAssertEqualObjects(FBApplicationMissingException, exception.name);
}
}
@end

View File

@@ -0,0 +1,103 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBElementCache.h"
#import "FBTestMacros.h"
#import "XCUIDevice+FBRotation.h"
#import "XCUIElement+FBIsVisible.h"
@interface FBTapTest : FBIntegrationTestCase
@end
// It is recommnded to verify these tests with different iOS versions
@implementation FBTapTest
- (void)verifyTapWithOrientation:(UIDeviceOrientation)orientation
{
[[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:orientation];
[self.testedApplication.buttons[FBShowAlertButtonName] tap];
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count > 0);
}
- (void)setUp
{
// Launch the app everytime to ensure the orientation for each test.
[super setUp];
[self launchApplication];
[self goToAlertsPage];
[self clearAlert];
}
- (void)tearDown
{
[self clearAlert];
[self resetOrientation];
[super tearDown];
}
- (void)testTap
{
[self verifyTapWithOrientation:UIDeviceOrientationPortrait];
}
- (void)testTapInLandscapeLeft
{
[self verifyTapWithOrientation:UIDeviceOrientationLandscapeLeft];
}
- (void)testTapInLandscapeRight
{
[self verifyTapWithOrientation:UIDeviceOrientationLandscapeRight];
}
- (void)testTapInPortraitUpsideDown
{
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
XCTSkip(@"Failed on Azure Pipeline. Local run succeeded.");
}
[self verifyTapWithOrientation:UIDeviceOrientationPortraitUpsideDown];
}
- (void)verifyTapByCoordinatesWithOrientation:(UIDeviceOrientation)orientation
{
[[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:orientation];
XCUIElement *dstButton = self.testedApplication.buttons[FBShowAlertButtonName];
[[dstButton coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.5)] tap];
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count > 0);
}
- (void)testTapCoordinates
{
[self verifyTapByCoordinatesWithOrientation:UIDeviceOrientationPortrait];
}
- (void)testTapCoordinatesInLandscapeLeft
{
[self verifyTapByCoordinatesWithOrientation:UIDeviceOrientationLandscapeLeft];
}
- (void)testTapCoordinatesInLandscapeRight
{
[self verifyTapByCoordinatesWithOrientation:UIDeviceOrientationLandscapeRight];
}
- (void)testTapCoordinatesInPortraitUpsideDown
{
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
XCTSkip(@"Failed on Azure Pipeline. Local run succeeded.");
}
[self verifyTapByCoordinatesWithOrientation:UIDeviceOrientationPortraitUpsideDown];
}
@end

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <WebDriverAgentLib/FBRunLoopSpinner.h>
/**
Macro used to wait till certain condition is true.
If condition will not become true within default timeout (1m) it will fail running test
*/
#define FBAssertWaitTillBecomesTrue(condition) \
({ \
NSError *__error; \
XCTAssertTrue([[[FBRunLoopSpinner new] \
interval:1.0] \
spinUntilTrue:^BOOL{ \
return (condition); \
}]); \
XCTAssertNil(__error); \
})
#define FBWaitExact(timeoutSeconds) \
({ \
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:(timeoutSeconds)]]; \
})
#define FBCellElementWithLabel(label) ([self.testedApplication descendantsMatchingType:XCUIElementTypeAny][label])
#define FBAssertVisibleCell(label) FBAssertWaitTillBecomesTrue(FBCellElementWithLabel(label).fb_isVisible)
#define FBAssertInvisibleCell(label) FBAssertWaitTillBecomesTrue(!FBCellElementWithLabel(label).fb_isVisible)

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "XCUIElement.h"
#import "XCUIElement+FBTyping.h"
@interface FBTypingTest : FBIntegrationTestCase
@end
@implementation FBTypingTest
- (void)setUp
{
[super setUp];
[self launchApplication];
[self goToAttributesPage];
}
- (void)testTextTyping
{
NSString *text = @"Happy typing";
XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];
NSError *error;
XCTAssertTrue([textField fb_typeText:text shouldClear:NO error:&error]);
XCTAssertNil(error);
XCTAssertEqualObjects(textField.value, text);
}
- (void)testTextTypingOnFocusedElement
{
NSString *text = @"Happy typing";
XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];
[textField tap];
XCTAssertTrue(textField.hasKeyboardFocus);
NSError *error;
XCTAssertTrue([textField fb_typeText:text shouldClear:NO error:&error]);
XCTAssertNil(error);
XCTAssertTrue([textField fb_typeText:text shouldClear:NO error:&error]);
XCTAssertNil(error);
NSString *expectedText = [NSString stringWithFormat:@"%@%@", text, text];
XCTAssertEqualObjects(textField.value, expectedText);
}
- (void)testTextClearing
{
XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];
[textField tap];
[textField typeText:@"Happy typing"];
XCTAssertTrue([textField.value length] > 0);
NSError *error;
XCTAssertTrue([textField fb_clearTextWithError:&error]);
XCTAssertNil(error);
XCTAssertEqualObjects(textField.value, @"");
XCTAssertTrue([textField fb_typeText:@"Happy typing" shouldClear:YES error:&error]);
XCTAssertTrue([textField fb_typeText:@"Happy typing 2" shouldClear:YES error:&error]);
XCTAssertEqualObjects(textField.value, @"Happy typing 2");
XCTAssertNil(error);
}
@end

View File

@@ -0,0 +1,64 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBConfiguration.h"
#import "FBMacros.h"
#import "FBScreenRecordingPromise.h"
#import "FBScreenRecordingRequest.h"
#import "FBScreenRecordingContainer.h"
#import "FBXCTestDaemonsProxy.h"
@interface FBVideoRecordingTests : FBIntegrationTestCase
@end
@implementation FBVideoRecordingTests
- (void)setUp
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"DisableDiagnosticScreenRecordings"];
[super setUp];
}
- (void)testStartingAndStoppingVideoRecording
{
XCTSkip(@"Failed on Azure Pipeline. Local run succeeded.");
// Video recording is only available since iOS 17
if (SYSTEM_VERSION_LESS_THAN(@"17.0")) {
return;
}
FBScreenRecordingRequest *recordingRequest = [[FBScreenRecordingRequest alloc] initWithFps:24
codec:0];
NSError *error;
FBScreenRecordingPromise *promise = [FBXCTestDaemonsProxy startScreenRecordingWithRequest:recordingRequest
error:&error];
XCTAssertNotNil(promise);
XCTAssertNotNil(promise.identifier);
XCTAssertNil(error);
[FBScreenRecordingContainer.sharedInstance storeScreenRecordingPromise:promise
fps:24
codec:0];
XCTAssertEqual(FBScreenRecordingContainer.sharedInstance.screenRecordingPromise, promise);
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.]];
BOOL isSuccessfull = [FBXCTestDaemonsProxy stopScreenRecordingWithUUID:promise.identifier error:&error];
XCTAssertTrue(isSuccessfull);
XCTAssertNil(error);
[FBScreenRecordingContainer.sharedInstance reset];
XCTAssertNil(FBScreenRecordingContainer.sharedInstance.screenRecordingPromise);
}
@end

View File

@@ -0,0 +1,124 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "XCUIElement.h"
#import "XCUIApplication+FBTouchAction.h"
#import "FBTestMacros.h"
#import "XCUIDevice+FBRotation.h"
#import "FBRunLoopSpinner.h"
@interface FBW3CMultiTouchActionsIntegrationTests : FBIntegrationTestCase
@end
@implementation FBW3CMultiTouchActionsIntegrationTests
- (void)verifyGesture:(NSArray<NSDictionary<NSString *, id> *> *)gesture orientation:(UIDeviceOrientation)orientation
{
[[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:orientation];
NSError *error;
XCTAssertTrue([self.testedApplication fb_performW3CActions:gesture elementCache:nil error:&error]);
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count > 0);
}
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
[self goToAlertsPage];
});
[self clearAlert];
}
- (void)tearDown
{
[self clearAlert];
[self resetOrientation];
[super tearDown];
}
- (void)testErroneousGestures
{
NSArray<NSArray<NSDictionary<NSString *, id> *> *> *invalidGestures =
@[
// One of the chains has duplicated id
@[
@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @1, @"y": @1},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerUp"},
],
},
@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @1, @"y": @1},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerUp"},
],
},
],
];
for (NSArray<NSDictionary<NSString *, id> *> *invalidGesture in invalidGestures) {
NSError *error;
XCTAssertFalse([self.testedApplication fb_performW3CActions:invalidGesture elementCache:nil error:&error]);
XCTAssertNotNil(error);
}
}
- (void)testSymmetricTwoFingersTap
{
XCUIElement *element = self.testedApplication.buttons[FBShowAlertButtonName];
NSArray<NSDictionary<NSString *, id> *> *gesture =
@[
@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"origin": element, @"x": @0, @"y": @0},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerUp"},
],
},
@{
@"type": @"pointer",
@"id": @"finger2",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"origin": element, @"x": @0, @"y": @0},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerUp"},
],
},
];
[self verifyGesture:gesture orientation:UIDeviceOrientationPortrait];
}
@end

View File

@@ -0,0 +1,491 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "XCUIElement.h"
#import "XCUIDevice.h"
#import "XCUIApplication+FBTouchAction.h"
#import "FBTestMacros.h"
#import "XCUIDevice+FBRotation.h"
#import "FBRunLoopSpinner.h"
#import "FBXCodeCompatibility.h"
@interface FBW3CTouchActionsIntegrationTestsPart1 : FBIntegrationTestCase
@end
@interface FBW3CTouchActionsIntegrationTestsPart2 : FBIntegrationTestCase
@property (nonatomic) XCUIElement *pickerWheel;
@end
@implementation FBW3CTouchActionsIntegrationTestsPart1
- (void)verifyGesture:(NSArray<NSDictionary<NSString *, id> *> *)gesture orientation:(UIDeviceOrientation)orientation
{
[[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:orientation];
NSError *error;
XCTAssertTrue([self.testedApplication fb_performW3CActions:gesture elementCache:nil error:&error]);
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count > 0);
}
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
[self goToAlertsPage];
});
[self clearAlert];
}
- (void)tearDown
{
[self clearAlert];
[self resetOrientation];
[super tearDown];
}
- (void)testErroneousGestures
{
NSArray<NSArray<NSDictionary<NSString *, id> *> *> *invalidGestures =
@[
// Empty chain
@[],
// Chain element without 'actions' key
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
},
],
// Chain element without type
@[@{
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @100, @"y": @100},
],
},
],
// Chain element without id
@[@{
@"type": @"pointer",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @100, @"y": @100},
],
},
],
// Chain element with empty id
@[@{
@"type": @"pointer",
@"id": @"",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @100, @"y": @100},
],
},
],
// Chain element with unsupported type
@[@{
@"type": @"key",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @100, @"y": @100},
],
},
],
// Chain element with unsupported pointerType (default)
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @100, @"y": @100},
],
},
],
// Chain element with unsupported pointerType (non-default)
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"pen"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @100, @"y": @100},
],
},
],
// Chain element without action item type
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"duration": @0, @"x": @1, @"y": @1},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerUp"},
],
},
],
// Chain element with singe up action
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerUp"},
],
},
],
// Chain element containing action item without y coordinate
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @1},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerUp"},
],
},
],
// Chain element containing action item with an unknown type
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMoved", @"duration": @0, @"x": @1, @"y": @1},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerUp"},
],
},
],
// Chain element where action items start with an incorrect item
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerMove", @"duration": @0, @"x": @1, @"y": @1},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerUp"},
],
},
],
// Chain element where pointerMove action item does not contain coordinates
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerUp"},
],
},
],
// Chain element where pointerMove action item cannot use coordinates of the previous item
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"origin": @"pointer"},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerUp"},
],
},
],
// Chain element where action items contains negative duration
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @1, @"y": @1},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @-100},
@{@"type": @"pointerUp"},
],
},
],
// Chain element where action items start with an incorrect one, because the correct one is canceled
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @1, @"y": @1},
@{@"type": @"pointerCancel"},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @-100},
@{@"type": @"pointerUp"},
],
},
],
];
for (NSArray<NSDictionary<NSString *, id> *> *invalidGesture in invalidGestures) {
NSError *error;
XCTAssertFalse([self.testedApplication fb_performW3CActions:invalidGesture elementCache:nil error:&error]);
XCTAssertNotNil(error);
}
}
- (void)testNothingDoesWithoutError
{
NSArray<NSDictionary<NSString *, id> *> *gesture =
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[],
},
];
NSError *error;
XCTAssertTrue([self.testedApplication fb_performW3CActions:gesture elementCache:nil error:&error]);
XCTAssertNil(error);
}
- (void)testTap
{
NSArray<NSDictionary<NSString *, id> *> *gesture =
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"origin": self.testedApplication.buttons[FBShowAlertButtonName], @"x": @0, @"y": @0},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerUp"},
],
},
];
[self verifyGesture:gesture orientation:UIDeviceOrientationPortrait];
}
- (void)testDoubleTap
{
NSArray<NSDictionary<NSString *, id> *> *gesture =
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"origin": self.testedApplication.buttons[FBShowAlertButtonName]},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @50},
@{@"type": @"pointerUp"},
@{@"type": @"pause", @"duration": @200},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @50},
@{@"type": @"pointerUp"},
],
},
];
[self verifyGesture:gesture orientation:UIDeviceOrientationLandscapeLeft];
}
- (void)testLongPressWithCombinedPause
{
NSArray<NSDictionary<NSString *, id> *> *gesture =
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"origin": self.testedApplication.buttons[FBShowAlertButtonName], @"x": @5, @"y": @5},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @200},
@{@"type": @"pause", @"duration": @200},
@{@"type": @"pause", @"duration": @100},
@{@"type": @"pointerUp"},
],
},
];
[self verifyGesture:gesture orientation:UIDeviceOrientationLandscapeRight];
}
- (void)testLongPress
{
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
XCTSkip(@"Failed on Azure Pipeline. Local run succeeded.");
}
UIDeviceOrientation orientation = UIDeviceOrientationLandscapeLeft;
[[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:orientation];
CGRect elementFrame = self.testedApplication.buttons[FBShowAlertButtonName].frame;
NSArray<NSDictionary<NSString *, id> *> *gesture =
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @(elementFrame.origin.x + 1), @"y": @(elementFrame.origin.y + 1)},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @500},
@{@"type": @"pointerUp"},
],
},
];
[self verifyGesture:gesture orientation:orientation];
}
- (void)testForceTap
{
if (![XCUIDevice.sharedDevice supportsPressureInteraction]) {
return;
}
NSArray<NSDictionary<NSString *, id> *> *gesture =
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"origin": self.testedApplication.buttons[FBShowAlertButtonName]},
@{@"type": @"pointerDown"},
@{@"type": @"pause", @"duration": @500},
@{@"type": @"pointerDown", @"pressure": @1.0, @"width":@10, @"height":@10},
@{@"type": @"pause", @"duration": @50},
@{@"type": @"pointerDown", @"pressure": @1.0, @"width":@10, @"height":@10},
@{@"type": @"pause", @"duration": @50},
@{@"type": @"pointerUp"},
],
},
];
[self verifyGesture:gesture orientation:UIDeviceOrientationLandscapeLeft];
}
@end
@implementation FBW3CTouchActionsIntegrationTestsPart2
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
[self goToAttributesPage];
});
self.pickerWheel = self.testedApplication.pickerWheels.allElementsBoundByIndex.firstObject;
}
- (void)tearDown
{
[self resetOrientation];
[super tearDown];
}
- (void)verifyPickerWheelPositionChangeWithGesture:(NSArray<NSDictionary<NSString *, id> *> *)gesture
{
NSString *previousValue = self.pickerWheel.value;
NSError *error;
XCTAssertTrue([self.testedApplication fb_performW3CActions:gesture elementCache:nil error:&error]);
XCTAssertNil(error);
XCTAssertTrue([[[[FBRunLoopSpinner new]
timeout:2.0]
timeoutErrorMessage:@"Picker wheel value has not been changed after 2 seconds timeout"]
spinUntilTrue:^BOOL{
return ![[self.pickerWheel fb_standardSnapshot].value isEqualToString:previousValue];
}
error:&error]);
XCTAssertNil(error);
}
- (void)testSwipePickerWheelWithElementCoordinates
{
CGRect pickerFrame = self.pickerWheel.frame;
NSArray<NSDictionary<NSString *, id> *> *gesture =
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"origin": self.pickerWheel, @"x": @0, @"y":@0},
@{@"type": @"pointerDown"},
@{@"type": @"pointerMove", @"duration": @100,@"width":@10, @"height":@10, @"pressure":@0.5, @"origin": self.pickerWheel, @"x": @0, @"y": @(pickerFrame.size.height / 2)},
@{@"type": @"pointerUp"},
],
},
];
[self verifyPickerWheelPositionChangeWithGesture:gesture];
}
- (void)testSwipePickerWheelWithRelativeCoordinates
{
CGRect pickerFrame = self.pickerWheel.frame;
NSArray<NSDictionary<NSString *, id> *> *gesture =
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @100, @"origin": self.pickerWheel, @"x": @0, @"y": @0},
@{@"type": @"pointerDown"},
@{@"type": @"pointerMove", @"duration": @100, @"width":@10, @"height":@10, @"pressure":@0.5, @"origin": @"pointer", @"x": @0, @"y": @(-pickerFrame.size.height / 2)},
@{@"type": @"pointerUp"},
],
},
];
[self verifyPickerWheelPositionChangeWithGesture:gesture];
}
- (void)testSwipePickerWheelWithAbsoluteCoordinates
{
CGRect pickerFrame = self.pickerWheel.frame;
NSArray<NSDictionary<NSString *, id> *> *gesture =
@[@{
@"type": @"pointer",
@"id": @"finger1",
@"parameters": @{@"pointerType": @"touch"},
@"actions": @[
@{@"type": @"pointerMove", @"duration": @0, @"x": @(pickerFrame.origin.x + pickerFrame.size.width / 2), @"y": @(pickerFrame.origin.y + pickerFrame.size.height / 2)},
@{@"type": @"pointerDown"},
@{@"type": @"pointerMove", @"duration": @100,@"width":@10, @"height":@10, @"pressure":@0.5, @"origin": @"pointer", @"x": @0, @"y": @(pickerFrame.size.height / 2)},
@{@"type": @"pointerUp"},
],
},
];
[self verifyPickerWheelPositionChangeWithGesture:gesture];
}
@end

View File

@@ -0,0 +1,184 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "XCUIElement.h"
#import "XCUIElement+FBTyping.h"
#import "XCUIApplication+FBTouchAction.h"
#import "XCUIElement+FBWebDriverAttributes.h"
#import "FBRuntimeUtils.h"
#import "FBXCodeCompatibility.h"
@interface FBW3CTypeActionsTests : FBIntegrationTestCase
@end
@implementation FBW3CTypeActionsTests
- (void)setUp
{
[super setUp];
[self launchApplication];
[self goToAttributesPage];
}
- (void)testErroneousGestures
{
if (![XCPointerEvent.class fb_areKeyEventsSupported]) {
return;
}
NSArray<NSArray<NSDictionary<NSString *, id> *> *> *invalidGestures =
@[
// missing balance 1
@[@{
@"type": @"key",
@"id": @"keyboard",
@"actions": @[
@{@"type": @"keyDown", @"value": @"h"},
@{@"type": @"keyUp", @"value": @"k"},
],
},
],
// missing balance 2
@[@{
@"type": @"key",
@"id": @"keyboard",
@"actions": @[
@{@"type": @"keyDown", @"value": @"h"},
],
},
],
// missing balance 3
@[@{
@"type": @"key",
@"id": @"keyboard",
@"actions": @[
@{@"type": @"keyUp", @"value": @"h"},
],
},
],
// missing key value
@[@{
@"type": @"key",
@"id": @"keyboard",
@"actions": @[
@{@"type": @"keyUp"},
],
},
],
// wrong key value
@[@{
@"type": @"key",
@"id": @"keyboard",
@"actions": @[
@{@"type": @"keyUp", @"value": @500},
],
},
],
// missing duration value
@[@{
@"type": @"key",
@"id": @"keyboard",
@"actions": @[
@{@"type": @"pause"},
],
},
],
// wrong duration value
@[@{
@"type": @"key",
@"id": @"keyboard",
@"actions": @[
@{@"type": @"duration", @"duration": @"bla"},
],
},
],
];
for (NSArray<NSDictionary<NSString *, id> *> *invalidGesture in invalidGestures) {
NSError *error;
XCTAssertFalse([self.testedApplication fb_performW3CActions:invalidGesture elementCache:nil error:&error]);
XCTAssertNotNil(error);
}
}
- (void)testTextTyping
{
if (![XCPointerEvent.class fb_areKeyEventsSupported]) {
return;
}
XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];
[textField tap];
NSArray<NSDictionary<NSString *, id> *> *typeAction =
@[
@{
@"type": @"key",
@"id": @"keyboard2",
@"actions": @[
@{@"type": @"pause", @"duration": @500},
@{@"type": @"keyDown", @"value": @"🏀"},
@{@"type": @"keyUp", @"value": @"🏀"},
@{@"type": @"keyDown", @"value": @"N"},
@{@"type": @"keyUp", @"value": @"N"},
@{@"type": @"keyDown", @"value": @"B"},
@{@"type": @"keyUp", @"value": @"B"},
@{@"type": @"keyDown", @"value": @"A"},
@{@"type": @"keyUp", @"value": @"A"},
@{@"type": @"keyDown", @"value": @"a"},
@{@"type": @"keyUp", @"value": @"a"},
@{@"type": @"keyDown", @"value": [NSString stringWithFormat:@"%C", 0xE003]},
@{@"type": @"keyUp", @"value": [NSString stringWithFormat:@"%C", 0xE003]},
@{@"type": @"pause", @"duration": @500},
],
},
];
NSError *error;
XCTAssertTrue([self.testedApplication fb_performW3CActions:typeAction
elementCache:nil
error:&error]);
XCTAssertNil(error);
XCTAssertEqualObjects(textField.wdValue, @"🏀NBA");
}
- (void)testTextTypingWithEmptyActions
{
if (![XCPointerEvent.class fb_areKeyEventsSupported]) {
return;
}
XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];
[textField tap];
NSArray<NSDictionary<NSString *, id> *> *typeAction =
@[
@{
@"type": @"pointer",
@"id": @"touch",
@"actions": @[],
},
];
NSError *error;
XCTAssertTrue([self.testedApplication fb_performW3CActions:typeAction
elementCache:nil
error:&error]);
XCTAssertNil(error);
XCTAssertEqualObjects(textField.value, @"");
}
@end

View File

@@ -0,0 +1,157 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBMacros.h"
#import "FBTestMacros.h"
#import "FBXPath.h"
#import "FBXCAccessibilityElement.h"
#import "FBXCodeCompatibility.h"
#import "FBXCElementSnapshotWrapper+Helpers.h"
#import "FBXMLGenerationOptions.h"
#import "XCUIApplication.h"
#import "XCUIElement.h"
#import "XCUIElement+FBFind.h"
#import "XCUIElement+FBUtilities.h"
#import "XCUIElement+FBWebDriverAttributes.h"
@interface FBXPathIntegrationTests : FBIntegrationTestCase
@property (nonatomic, strong) XCUIElement *testedView;
@end
@implementation FBXPathIntegrationTests
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
});
self.testedView = self.testedApplication.otherElements[@"MainView"];
XCTAssertTrue(self.testedView.exists);
FBAssertWaitTillBecomesTrue(self.testedView.buttons.count > 0);
}
- (id<FBXCElementSnapshot>)destinationSnapshot
{
XCUIElement *matchingElement = self.testedView.buttons.allElementsBoundByIndex.firstObject;
id<FBXCElementSnapshot> snapshot = [matchingElement fb_customSnapshot];
// Over iOS13, snapshot returns a child.
// The purpose of here is return a single element to replace children with an empty array for testing.
snapshot.children = @[];
return snapshot;
}
- (void)testApplicationNodeXMLRepresentation
{
id<FBXCElementSnapshot> snapshot = [self.testedApplication fb_customSnapshot];
snapshot.children = @[];
FBXCElementSnapshotWrapper *wrappedSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:snapshot];
NSString *xmlStr = [FBXPath xmlStringWithRootElement:wrappedSnapshot
options:nil];
int pid = [snapshot.accessibilityElement processIdentifier];
XCTAssertNotNil(xmlStr);
NSString *expectedXml = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<%@ type=\"%@\" name=\"%@\" label=\"%@\" enabled=\"%@\" visible=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" index=\"%lu\" traits=\"%@\" processId=\"%d\" bundleId=\"%@\"/>\n", wrappedSnapshot.wdType, wrappedSnapshot.wdType, wrappedSnapshot.wdName, wrappedSnapshot.wdLabel, FBBoolToString(wrappedSnapshot.wdEnabled), FBBoolToString(wrappedSnapshot.wdVisible), FBBoolToString(wrappedSnapshot.wdAccessible), [wrappedSnapshot.wdRect[@"x"] stringValue], [wrappedSnapshot.wdRect[@"y"] stringValue], [wrappedSnapshot.wdRect[@"width"] stringValue], [wrappedSnapshot.wdRect[@"height"] stringValue], wrappedSnapshot.wdIndex, wrappedSnapshot.wdTraits, pid, [self.testedApplication bundleID]];
XCTAssertEqualObjects(xmlStr, expectedXml);
}
- (void)testSingleDescendantXMLRepresentation
{
id<FBXCElementSnapshot> snapshot = self.destinationSnapshot;
FBXCElementSnapshotWrapper *wrappedSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:snapshot];
NSString *xmlStr = [FBXPath xmlStringWithRootElement:wrappedSnapshot
options:nil];
XCTAssertNotNil(xmlStr);
NSString *expectedXml = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<%@ type=\"%@\" name=\"%@\" label=\"%@\" enabled=\"%@\" visible=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" index=\"%lu\" traits=\"%@\"/>\n", wrappedSnapshot.wdType, wrappedSnapshot.wdType, wrappedSnapshot.wdName, wrappedSnapshot.wdLabel, FBBoolToString(wrappedSnapshot.wdEnabled), FBBoolToString(wrappedSnapshot.wdVisible), FBBoolToString(wrappedSnapshot.wdAccessible), [wrappedSnapshot.wdRect[@"x"] stringValue], [wrappedSnapshot.wdRect[@"y"] stringValue], [wrappedSnapshot.wdRect[@"width"] stringValue], [wrappedSnapshot.wdRect[@"height"] stringValue], wrappedSnapshot.wdIndex, wrappedSnapshot.wdTraits];
XCTAssertEqualObjects(xmlStr, expectedXml);
}
- (void)testSingleDescendantXMLRepresentationWithScope
{
id<FBXCElementSnapshot> snapshot = self.destinationSnapshot;
NSString *scope = @"AppiumAUT";
FBXMLGenerationOptions *options = [[FBXMLGenerationOptions new] withScope:scope];
FBXCElementSnapshotWrapper *wrappedSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:snapshot];
NSString *xmlStr = [FBXPath xmlStringWithRootElement:wrappedSnapshot
options:options];
XCTAssertNotNil(xmlStr);
NSString *expectedXml = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<%@>\n <%@ type=\"%@\" name=\"%@\" label=\"%@\" enabled=\"%@\" visible=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" index=\"%lu\" traits=\"%@\"/>\n</%@>\n", scope, wrappedSnapshot.wdType, wrappedSnapshot.wdType, wrappedSnapshot.wdName, wrappedSnapshot.wdLabel, FBBoolToString(wrappedSnapshot.wdEnabled), FBBoolToString(wrappedSnapshot.wdVisible), FBBoolToString(wrappedSnapshot.wdAccessible), [wrappedSnapshot.wdRect[@"x"] stringValue], [wrappedSnapshot.wdRect[@"y"] stringValue], [wrappedSnapshot.wdRect[@"width"] stringValue], [wrappedSnapshot.wdRect[@"height"] stringValue], wrappedSnapshot.wdIndex, wrappedSnapshot.wdTraits, scope];
XCTAssertEqualObjects(xmlStr, expectedXml);
}
- (void)testSingleDescendantXMLRepresentationWithoutAttributes
{
id<FBXCElementSnapshot> snapshot = self.destinationSnapshot;
FBXMLGenerationOptions *options = [[FBXMLGenerationOptions new]
withExcludedAttributes:@[@"visible", @"enabled", @"index", @"blabla"]];
FBXCElementSnapshotWrapper *wrappedSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:snapshot];
NSString *xmlStr = [FBXPath xmlStringWithRootElement:wrappedSnapshot
options:options];
XCTAssertNotNil(xmlStr);
NSString *expectedXml = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<%@ type=\"%@\" name=\"%@\" label=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" traits=\"%@\"/>\n", wrappedSnapshot.wdType, wrappedSnapshot.wdType, wrappedSnapshot.wdName, wrappedSnapshot.wdLabel, FBBoolToString(wrappedSnapshot.wdAccessible), [wrappedSnapshot.wdRect[@"x"] stringValue], [wrappedSnapshot.wdRect[@"y"] stringValue], [wrappedSnapshot.wdRect[@"width"] stringValue], [wrappedSnapshot.wdRect[@"height"] stringValue], wrappedSnapshot.wdTraits];
XCTAssertEqualObjects(xmlStr, expectedXml);
}
- (void)testFindMatchesInElement
{
NSArray<id<FBXCElementSnapshot>> *matchingSnapshots = [FBXPath matchesWithRootElement:self.testedApplication forQuery:@"//XCUIElementTypeButton"];
XCTAssertEqual([matchingSnapshots count], 5);
for (id<FBXCElementSnapshot> element in matchingSnapshots) {
XCTAssertTrue([[FBXCElementSnapshotWrapper ensureWrapped:element].wdType isEqualToString:@"XCUIElementTypeButton"]);
}
}
- (void)testFindMatchesWithoutContextScopeLimit
{
XCUIElement *button = self.testedApplication.buttons.firstMatch;
BOOL previousValue = FBConfiguration.limitXpathContextScope;
FBConfiguration.limitXpathContextScope = NO;
@try {
NSArray *parentSnapshots = [FBXPath matchesWithRootElement:button forQuery:@".."];
XCTAssertEqual(parentSnapshots.count, 1);
XCTAssertEqualObjects(
[FBXCElementSnapshotWrapper ensureWrapped:[parentSnapshots objectAtIndex:0]].wdLabel,
@"MainView"
);
NSArray *elements = [button.application fb_filterDescendantsWithSnapshots:parentSnapshots onlyChildren:NO];
XCTAssertEqual(elements.count, 1);
XCTAssertEqualObjects(
[[elements objectAtIndex:0] wdLabel],
@"MainView"
);
NSArray *currentSnapshots = [FBXPath matchesWithRootElement:button forQuery:@"."];
XCTAssertEqual(currentSnapshots.count, 1);
XCTAssertEqualObjects(
[FBXCElementSnapshotWrapper ensureWrapped:[currentSnapshots objectAtIndex:0]].wdType,
@"XCUIElementTypeButton"
);
NSArray *currentElements = [button.application fb_filterDescendantsWithSnapshots:currentSnapshots onlyChildren:NO];
XCTAssertEqual(currentElements.count, 1);
XCTAssertEqualObjects(
[[currentElements objectAtIndex:0] wdType],
@"XCUIElementTypeButton"
);
} @finally {
FBConfiguration.limitXpathContextScope = previousValue;
}
}
- (void)testFindMatchesInElementWithDotNotation
{
NSArray<id<FBXCElementSnapshot>> *matchingSnapshots = [FBXPath matchesWithRootElement:self.testedApplication forQuery:@".//XCUIElementTypeButton"];
XCTAssertEqual([matchingSnapshots count], 5);
for (id<FBXCElementSnapshot> element in matchingSnapshots) {
XCTAssertTrue([[FBXCElementSnapshotWrapper ensureWrapped:element].wdType isEqualToString:@"XCUIElementTypeButton"]);
}
}
@end

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.facebook.wda.integrationTests</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -0,0 +1,209 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBTestMacros.h"
#import "XCUIElement.h"
#import "XCUIElement+FBIsVisible.h"
#import "XCUIElement+FBUtilities.h"
#import "XCUIElement+FBWebDriverAttributes.h"
#import "FBXCElementSnapshotWrapper+Helpers.h"
#import "FBXCodeCompatibility.h"
@interface XCElementSnapshotHelperTests : FBIntegrationTestCase
@property (nonatomic, strong) XCUIElement *testedView;
@end
@implementation XCElementSnapshotHelperTests
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
});
self.testedView = self.testedApplication.otherElements[@"MainView"];
XCTAssertTrue(self.testedView.exists);
}
- (void)testDescendantsMatchingType
{
NSSet<NSString *> *expectedLabels = [NSSet setWithArray:@[
@"Alerts",
@"Attributes",
@"Scrolling",
@"Deadlock app",
@"Touch",
]];
NSArray<id<FBXCElementSnapshot>> *matchingSnapshots = [[FBXCElementSnapshotWrapper ensureWrapped:
[self.testedView fb_customSnapshot]]
fb_descendantsMatchingType:XCUIElementTypeButton];
XCTAssertEqual(matchingSnapshots.count, expectedLabels.count);
NSArray<NSString *> *labels = [matchingSnapshots valueForKeyPath:@"@distinctUnionOfObjects.label"];
XCTAssertEqualObjects([NSSet setWithArray:labels], expectedLabels);
NSArray<NSNumber *> *types = [matchingSnapshots valueForKeyPath:@"@distinctUnionOfObjects.elementType"];
XCTAssertEqual(types.count, 1, @"matchingSnapshots should contain only one type");
XCTAssertEqualObjects(types.lastObject, @(XCUIElementTypeButton), @"matchingSnapshots should contain only one type");
}
- (void)testParentMatchingType
{
XCUIElement *button = self.testedApplication.buttons[@"Alerts"];
FBAssertWaitTillBecomesTrue(button.exists);
id<FBXCElementSnapshot> windowSnapshot = [[FBXCElementSnapshotWrapper ensureWrapped:
[self.testedView fb_customSnapshot]]
fb_parentMatchingType:XCUIElementTypeWindow];
XCTAssertNotNil(windowSnapshot);
XCTAssertEqual(windowSnapshot.elementType, XCUIElementTypeWindow);
}
@end
@interface XCElementSnapshotHelperTests_AttributePage : FBIntegrationTestCase
@end
@implementation XCElementSnapshotHelperTests_AttributePage
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
[self goToAttributesPage];
});
}
- (void)testParentMatchingOneOfTypes
{
XCUIElement *todayPickerWheel = self.testedApplication.pickerWheels[@"Today"];
FBAssertWaitTillBecomesTrue(todayPickerWheel.exists);
id<FBXCElementSnapshot> datePicker = [[FBXCElementSnapshotWrapper ensureWrapped:
[todayPickerWheel fb_customSnapshot]]
fb_parentMatchingOneOfTypes:@[@(XCUIElementTypeDatePicker), @(XCUIElementTypeWindow)]];
XCTAssertNotNil(datePicker);
XCTAssertEqual(datePicker.elementType, XCUIElementTypeDatePicker);
}
- (void)testParentMatchingOneOfTypesWithXCUIElementTypeAny
{
XCUIElement *todayPickerWheel = self.testedApplication.pickerWheels[@"Today"];
FBAssertWaitTillBecomesTrue(todayPickerWheel.exists);
id<FBXCElementSnapshot> otherSnapshot =[[FBXCElementSnapshotWrapper ensureWrapped:
[todayPickerWheel fb_customSnapshot]]
fb_parentMatchingOneOfTypes:@[@(XCUIElementTypeAny)]];
XCTAssertNotNil(otherSnapshot);
}
- (void)testParentMatchingOneOfTypesWithAbsentParents
{
XCUIElement *todayPickerWheel = self.testedApplication.pickerWheels[@"Today"];
FBAssertWaitTillBecomesTrue(todayPickerWheel.exists);
id<FBXCElementSnapshot> otherSnapshot = [[FBXCElementSnapshotWrapper ensureWrapped:
[todayPickerWheel fb_customSnapshot]]
fb_parentMatchingOneOfTypes:@[@(XCUIElementTypeTab), @(XCUIElementTypeLink)]];
XCTAssertNil(otherSnapshot);
}
@end
@interface XCElementSnapshotHelperTests_ScrollView : FBIntegrationTestCase
@end
@implementation XCElementSnapshotHelperTests_ScrollView
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
[self goToScrollPageWithCells:false];
});
}
- (void)testParentMatchingOneOfTypesWithFilter
{
XCUIElement *threeStaticText = self.testedApplication.staticTexts[@"3"];
FBAssertWaitTillBecomesTrue(threeStaticText.exists);
NSArray *acceptedParents = @[
@(XCUIElementTypeScrollView),
@(XCUIElementTypeCollectionView),
@(XCUIElementTypeTable),
];
id<FBXCElementSnapshot> scrollView = [[FBXCElementSnapshotWrapper ensureWrapped:
[threeStaticText fb_customSnapshot]]
fb_parentMatchingOneOfTypes:acceptedParents
filter:^BOOL(id<FBXCElementSnapshot> snapshot) {
return [[FBXCElementSnapshotWrapper ensureWrapped:snapshot] isWDVisible];
}];
XCTAssertEqualObjects(scrollView.identifier, @"scrollView");
}
- (void)testParentMatchingOneOfTypesWithFilterRetruningNo
{
XCUIElement *threeStaticText = self.testedApplication.staticTexts[@"3"];
FBAssertWaitTillBecomesTrue(threeStaticText.exists);
NSArray *acceptedParents = @[
@(XCUIElementTypeScrollView),
@(XCUIElementTypeCollectionView),
@(XCUIElementTypeTable),
];
id<FBXCElementSnapshot> scrollView = [[FBXCElementSnapshotWrapper ensureWrapped:
[threeStaticText fb_customSnapshot]]
fb_parentMatchingOneOfTypes:acceptedParents
filter:^BOOL(id<FBXCElementSnapshot> snapshot) {
return NO;
}];
XCTAssertNil(scrollView);
}
- (void)testDescendantsCellSnapshots
{
XCUIElement *scrollView = self.testedApplication.scrollViews[@"scrollView"];
FBAssertWaitTillBecomesTrue(self.testedApplication.staticTexts[@"3"].fb_isVisible);
NSArray *cells = [[FBXCElementSnapshotWrapper ensureWrapped:
[scrollView fb_customSnapshot]]
fb_descendantsCellSnapshots];
XCTAssertGreaterThanOrEqual(cells.count, 10);
id<FBXCElementSnapshot> element = cells.firstObject;
XCTAssertEqualObjects(element.label, @"0");
}
@end
@interface XCElementSnapshotHelperTests_ScrollViewCells : FBIntegrationTestCase
@end
@implementation XCElementSnapshotHelperTests_ScrollViewCells
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
[self goToScrollPageWithCells:true];
});
}
- (void)testParentCellSnapshot
{
FBAssertWaitTillBecomesTrue(self.testedApplication.staticTexts[@"3"].fb_isVisible);
XCUIElement *threeStaticText = self.testedApplication.staticTexts[@"3"];
id<FBXCElementSnapshot> xcuiElementCell = [[FBXCElementSnapshotWrapper ensureWrapped:
[threeStaticText fb_customSnapshot]]
fb_parentCellSnapshot];
XCTAssertEqual(xcuiElementCell.elementType, 75);
}
@end

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "FBIntegrationTestCase.h"
#import "FBTestMacros.h"
#import "FBXCElementSnapshotWrapper+Helpers.h"
#import "XCUIElement.h"
#import "XCUIElement+FBUtilities.h"
@interface XCElementSnapshotHitPoint : FBIntegrationTestCase
@end
@implementation XCElementSnapshotHitPoint
- (void)testAccessibilityActivationPoint
{
[self launchApplication];
[self goToAttributesPage];
XCUIElement *dstBtn = self.testedApplication.buttons[@"not_accessible"];
CGPoint hitPoint = [FBXCElementSnapshotWrapper
ensureWrapped:[dstBtn fb_standardSnapshot]].fb_hitPoint.CGPointValue;
XCTAssertTrue(hitPoint.x > 0 && hitPoint.y > 0);
}
@end

View File

@@ -0,0 +1,163 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import <mach/mach_time.h>
#import "FBIntegrationTestCase.h"
#import "FBElement.h"
#import "FBMacros.h"
#import "FBTestMacros.h"
#import "XCUIApplication.h"
#import "XCUIApplication+FBHelpers.h"
#import "XCUIElement+FBIsVisible.h"
#import "FBXCodeCompatibility.h"
void calculateMaxTreeDepth(NSDictionary *tree, NSNumber *currentDepth, NSNumber** maxDepth) {
if (nil == maxDepth) {
return;
}
NSArray *children = tree[@"children"];
if (nil == children || 0 == children.count) {
return;
}
for (NSDictionary *child in children) {
if (currentDepth.integerValue > [*maxDepth integerValue]) {
*maxDepth = currentDepth;
}
calculateMaxTreeDepth(child, @(currentDepth.integerValue + 1), maxDepth);
}
}
@interface XCUIApplicationHelperTests : FBIntegrationTestCase
@end
@implementation XCUIApplicationHelperTests
- (void)setUp
{
[super setUp];
[self launchApplication];
}
- (void)testQueringSpringboard
{
[self goToSpringBoardFirstPage];
XCTAssertTrue(XCUIApplication.fb_systemApplication.icons[@"Safari"].exists);
XCTAssertTrue(XCUIApplication.fb_systemApplication.icons[@"Calendar"].firstMatch.exists);
}
- (void)testApplicationTree
{
NSDictionary *tree = self.testedApplication.fb_tree;
XCTAssertNotNil(tree);
NSNumber *maxDepth;
calculateMaxTreeDepth(tree, @0, &maxDepth);
XCTAssertGreaterThan(maxDepth.integerValue, 3);
XCTAssertNotNil(self.testedApplication.fb_accessibilityTree);
}
- (void)testApplicationTreeAttributesFiltering
{
NSDictionary *applicationTree = [self.testedApplication fb_tree:[NSSet setWithArray:@[@"visible"]]];
XCTAssertNotNil(applicationTree);
XCTAssertNil([applicationTree objectForKey:@"isVisible"], @"'isVisible' key should not be present in the application tree");
}
- (void)testDeactivateApplication
{
NSError *error;
uint64_t timeStarted = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW);
NSTimeInterval backgroundDuration = 5.0;
XCTAssertTrue([self.testedApplication fb_deactivateWithDuration:backgroundDuration error:&error]);
NSTimeInterval timeElapsed = (clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW) - timeStarted) / NSEC_PER_SEC;
XCTAssertNil(error);
XCTAssertEqualWithAccuracy(timeElapsed, backgroundDuration, 3.0);
XCTAssertTrue(self.testedApplication.buttons[@"Alerts"].exists);
}
- (void)testActiveApplication
{
XCUIApplication *systemApp = XCUIApplication.fb_systemApplication;
XCTAssertTrue([XCUIApplication fb_activeApplication].buttons[@"Alerts"].fb_isVisible);
[self goToSpringBoardFirstPage];
XCTAssertEqualObjects([XCUIApplication fb_activeApplication].bundleID, systemApp.bundleID);
XCTAssertTrue(systemApp.icons[@"Safari"].fb_isVisible);
}
- (void)testActiveElement
{
[self goToAttributesPage];
XCTAssertNil(self.testedApplication.fb_activeElement);
XCUIElement *textField = self.testedApplication.textFields[@"aIdentifier"];
[textField tap];
FBAssertWaitTillBecomesTrue(nil != self.testedApplication.fb_activeElement);
XCTAssertEqualObjects(((id<FBElement>)self.testedApplication.fb_activeElement).wdUID,
((id<FBElement>)textField).wdUID);
}
- (void)testActiveApplicationsInfo
{
NSArray *appsInfo = [XCUIApplication fb_activeAppsInfo];
XCTAssertTrue(appsInfo.count > 0);
BOOL isAppActive = NO;
for (NSDictionary *appInfo in appsInfo) {
if ([appInfo[@"bundleId"] isEqualToString:self.testedApplication.bundleID]) {
isAppActive = YES;
break;
}
}
XCTAssertTrue(isAppActive);
}
- (void)testTestmanagerdVersion
{
XCTAssertGreaterThan(FBTestmanagerdVersion(), 0);
}
- (void)testAccessbilityAudit
{
if (SYSTEM_VERSION_LESS_THAN(@"17.0")) {
return;
}
NSError *error;
NSArray *auditIssues1 = [XCUIApplication.fb_activeApplication fb_performAccessibilityAuditWithAuditTypes:~0UL
error:&error];
XCTAssertNotNil(auditIssues1);
XCTAssertNil(error);
NSMutableSet *set = [NSMutableSet new];
[set addObject:@"XCUIAccessibilityAuditTypeAll"];
NSArray *auditIssues2 = [XCUIApplication.fb_activeApplication fb_performAccessibilityAuditWithAuditTypesSet:set.copy
error:&error];
// 'elementDescription' is not in this list because it could have
// different object id's debug description in XCTest.
NSArray *checkKeys = @[
@"auditType",
@"compactDescription",
@"detailedDescription",
@"element",
@"elementAttributes"
];
XCTAssertEqual([auditIssues1 count], [auditIssues2 count]);
for (int i = 1; i < [auditIssues1 count]; i++) {
for (NSString *k in checkKeys) {
XCTAssertEqualObjects(
[auditIssues1[i] objectForKey:k],
[auditIssues2[i] objectForKey:k]
);
}
}
XCTAssertNil(error);
}
@end

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "XCUIDevice+FBHealthCheck.h"
#import "XCUIElement.h"
@interface XCUIDeviceHealthCheckTests : FBIntegrationTestCase
@end
@implementation XCUIDeviceHealthCheckTests
- (void)testHealthCheck
{
[self launchApplication];
XCTAssertTrue(self.testedApplication.exists);
XCTAssertTrue([[XCUIDevice sharedDevice] fb_healthCheckWithApplication:self.testedApplication]);
}
@end

View File

@@ -0,0 +1,220 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBImageUtils.h"
#import "FBMacros.h"
#import "FBTestMacros.h"
#import "XCUIApplication.h"
#import "XCUIApplication+FBHelpers.h"
#import "XCUIDevice+FBHelpers.h"
#import "XCUIDevice+FBRotation.h"
#import "XCUIScreen.h"
@interface XCUIDeviceHelperTests : FBIntegrationTestCase
@end
@implementation XCUIDeviceHelperTests
- (void)restorePortraitOrientation
{
if ([XCUIDevice sharedDevice].orientation != UIDeviceOrientationPortrait) {
[[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:UIDeviceOrientationPortrait];
}
}
- (void)setUp
{
[super setUp];
[self launchApplication];
[self restorePortraitOrientation];
}
- (void)tearDown
{
[self restorePortraitOrientation];
[super tearDown];
}
- (void)testScreenshot
{
NSError *error = nil;
NSData *screenshotData = [[XCUIDevice sharedDevice] fb_screenshotWithError:&error];
XCTAssertNotNil(screenshotData);
XCTAssertNil(error);
XCTAssertTrue(FBIsPngImage(screenshotData));
UIImage *screenshot = [UIImage imageWithData:screenshotData];
XCTAssertNotNil(screenshot);
XCUIScreen *mainScreen = XCUIScreen.mainScreen;
UIImage *screenshotExact = ((XCUIScreenshot *)mainScreen.screenshot).image;
XCTAssertEqualWithAccuracy(screenshotExact.size.height * mainScreen.scale,
screenshot.size.height,
FLT_EPSILON);
XCTAssertEqualWithAccuracy(screenshotExact.size.width * mainScreen.scale,
screenshot.size.width,
FLT_EPSILON);
}
- (void)testLandscapeScreenshot
{
XCTAssertTrue([[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:UIDeviceOrientationLandscapeLeft]);
NSError *error = nil;
NSData *screenshotData = [[XCUIDevice sharedDevice] fb_screenshotWithError:&error];
XCTAssertNotNil(screenshotData);
XCTAssertTrue(FBIsPngImage(screenshotData));
XCTAssertNil(error);
UIImage *screenshot = [UIImage imageWithData:screenshotData];
XCTAssertNotNil(screenshot);
XCTAssertTrue(screenshot.size.width > screenshot.size.height);
XCUIScreen *mainScreen = XCUIScreen.mainScreen;
UIImage *screenshotExact = ((XCUIScreenshot *)mainScreen.screenshot).image;
CGSize realMainScreenSize = screenshotExact.size.height > screenshot.size.width
? CGSizeMake(screenshotExact.size.height * mainScreen.scale, screenshotExact.size.width * mainScreen.scale)
: CGSizeMake(screenshotExact.size.width * mainScreen.scale, screenshotExact.size.height * mainScreen.scale);
XCTAssertEqualWithAccuracy(realMainScreenSize.height, screenshot.size.height, FLT_EPSILON);
XCTAssertEqualWithAccuracy(realMainScreenSize.width, screenshot.size.width, FLT_EPSILON);
}
- (void)testWifiAddress
{
NSString *adderss = [XCUIDevice sharedDevice].fb_wifiIPAddress;
if (!adderss) {
return;
}
NSRange range = [adderss rangeOfString:@"^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})" options:NSRegularExpressionSearch];
XCTAssertTrue(range.location != NSNotFound);
}
- (void)testGoToHomeScreen
{
NSError *error;
XCTAssertTrue([[XCUIDevice sharedDevice] fb_goToHomescreenWithError:&error]);
XCTAssertNil(error);
FBAssertWaitTillBecomesTrue([XCUIApplication fb_activeApplication].icons[@"Safari"].exists);
}
- (void)testLockUnlockScreen
{
XCTAssertFalse([[XCUIDevice sharedDevice] fb_isScreenLocked]);
NSError *error;
XCTAssertTrue([[XCUIDevice sharedDevice] fb_lockScreen:&error]);
XCTAssertTrue([[XCUIDevice sharedDevice] fb_isScreenLocked]);
XCTAssertNil(error);
XCTAssertTrue([[XCUIDevice sharedDevice] fb_unlockScreen:&error]);
XCTAssertFalse([[XCUIDevice sharedDevice] fb_isScreenLocked]);
XCTAssertNil(error);
}
- (void)testUrlSchemeActivation
{
if (SYSTEM_VERSION_LESS_THAN(@"16.4")) {
return;
}
NSError *error;
XCTAssertTrue([XCUIDevice.sharedDevice fb_openUrl:@"https://apple.com" error:&error]);
FBAssertWaitTillBecomesTrue([XCUIApplication.fb_activeApplication.bundleID isEqualToString:@"com.apple.mobilesafari"]);
XCTAssertNil(error);
}
- (void)testUrlSchemeActivationWithApp
{
if (SYSTEM_VERSION_LESS_THAN(@"16.4")) {
return;
}
NSError *error;
XCTAssertTrue([XCUIDevice.sharedDevice fb_openUrl:@"https://apple.com"
withApplication:@"com.apple.mobilesafari"
error:&error]);
FBAssertWaitTillBecomesTrue([XCUIApplication.fb_activeApplication.bundleID isEqualToString:@"com.apple.mobilesafari"]);
XCTAssertNil(error);
}
#if !TARGET_OS_TV
- (void)testSimulatedLocationSetup
{
if (SYSTEM_VERSION_LESS_THAN(@"16.4")) {
return;
}
CLLocation *simulatedLocation = [[CLLocation alloc] initWithLatitude:50 longitude:50];
NSError *error;
XCTAssertTrue([XCUIDevice.sharedDevice fb_setSimulatedLocation:simulatedLocation error:&error]);
XCTAssertNil(error);
CLLocation *currentLocation = [XCUIDevice.sharedDevice fb_getSimulatedLocation:&error];
XCTAssertNil(error);
XCTAssertNotNil(currentLocation);
XCTAssertEqualWithAccuracy(simulatedLocation.coordinate.latitude, currentLocation.coordinate.latitude, 0.1);
XCTAssertEqualWithAccuracy(simulatedLocation.coordinate.longitude, currentLocation.coordinate.longitude, 0.1);
XCTAssertTrue([XCUIDevice.sharedDevice fb_clearSimulatedLocation:&error]);
XCTAssertNil(error);
currentLocation = [XCUIDevice.sharedDevice fb_getSimulatedLocation:&error];
XCTAssertNil(error);
XCTAssertNotEqualWithAccuracy(simulatedLocation.coordinate.latitude, currentLocation.coordinate.latitude, 0.1);
XCTAssertNotEqualWithAccuracy(simulatedLocation.coordinate.longitude, currentLocation.coordinate.longitude, 0.1);
}
#endif
- (void)testPressingUnsupportedButton
{
NSError *error;
NSNumber *duration = nil;
XCTAssertFalse([XCUIDevice.sharedDevice fb_pressButton:@"volumeUpp"
forDuration:duration
error:&error]);
XCTAssertNotNil(error);
}
- (void)testPressingSupportedButton
{
NSError *error;
XCTAssertTrue([XCUIDevice.sharedDevice fb_pressButton:@"home"
forDuration:nil
error:&error]);
XCTAssertNil(error);
}
- (void)testPressingSupportedButtonNumber
{
NSError *error;
XCTAssertTrue([XCUIDevice.sharedDevice fb_pressButton:@"home"
forDuration:[NSNumber numberWithDouble:1.0]
error:&error]);
XCTAssertNil(error);
}
- (void)testLongPressHomeButton
{
NSError *error;
// kHIDPage_Consumer = 0x0C
// kHIDUsage_Csmr_Menu = 0x40
XCTAssertTrue([XCUIDevice.sharedDevice fb_performIOHIDEventWithPage:0x0C
usage:0x40
duration:1.0
error:&error]);
XCTAssertNil(error);
}
- (void)testAppearance
{
if (SYSTEM_VERSION_LESS_THAN(@"15.0")) {
return;
}
NSError *error;
XCTAssertTrue([XCUIDevice.sharedDevice fb_setAppearance:FBUIInterfaceAppearanceDark error:&error]);
XCTAssertNil(error);
}
@end

View File

@@ -0,0 +1,84 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import <UIKit/UIKit.h>
#import "FBIntegrationTestCase.h"
#import "XCUIDevice+FBRotation.h"
@interface XCUIDeviceRotationTests : FBIntegrationTestCase
@end
@implementation XCUIDeviceRotationTests
- (void)setUp
{
[super setUp];
[self launchApplication];
}
- (void)tearDown
{
[self resetOrientation];
[super tearDown];
}
- (void)testLandscapeRightOrientation
{
BOOL success = [[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:UIDeviceOrientationLandscapeRight];
XCTAssertTrue(success, @"Device should support LandscapeRight");
// Device rotation gives opposite interface rotation
XCTAssertTrue(self.testedApplication.staticTexts[@"LandscapeLeft"].exists);
}
- (void)testLandscapeLeftOrientation
{
BOOL success = [[XCUIDevice sharedDevice] fb_setDeviceInterfaceOrientation:UIDeviceOrientationLandscapeLeft];
XCTAssertTrue(success, @"Device should support LandscapeLeft");
// Device rotation gives opposite interface rotation
XCTAssertTrue(self.testedApplication.staticTexts[@"LandscapeRight"].exists);
}
- (void)testLandscapeRightRotation
{
BOOL success = [[XCUIDevice sharedDevice] fb_setDeviceRotation:@{
@"x" : @(0),
@"y" : @(0),
@"z" : @(90)
}];
XCTAssertTrue(success, @"Device should support LandscapeRight");
// Device rotation gives opposite interface rotation
XCTAssertTrue(self.testedApplication.staticTexts[@"LandscapeLeft"].exists);
}
- (void)testLandscapeLeftRotation
{
BOOL success = [[XCUIDevice sharedDevice] fb_setDeviceRotation:@{
@"x" : @(0),
@"y" : @(0),
@"z" : @(270)
}];
XCTAssertTrue(success, @"Device should support LandscapeLeft");
// Device rotation gives opposite interface rotation
XCTAssertTrue(self.testedApplication.staticTexts[@"LandscapeRight"].exists);
}
- (void)testRotationTiltRotation
{
UIDeviceOrientation currentRotation = [XCUIDevice sharedDevice].orientation;
BOOL success = [[XCUIDevice sharedDevice] fb_setDeviceRotation:@{
@"x" : @(15),
@"y" : @(0),
@"z" : @(0)}
];
XCTAssertFalse(success, @"Device should not support tilt");
XCTAssertEqual(currentRotation, [XCUIDevice sharedDevice].orientation, @"Device doesnt support tilt, should be at previous orientation");
}
@end

View File

@@ -0,0 +1,248 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBTestMacros.h"
#import "XCUIElement+FBWebDriverAttributes.h"
#import "XCUIElement+FBFind.h"
#import "FBElementUtils.h"
#import "FBConfiguration.h"
#import "FBResponsePayload.h"
#import "FBXCodeCompatibility.h"
@interface XCUIElementAttributesTests : FBIntegrationTestCase
@property (nonatomic, strong) XCUIElement *matchingElement;
@end
@implementation XCUIElementAttributesTests
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
});
XCUIElement *testedView = self.testedApplication.otherElements[@"MainView"];
FBAssertWaitTillBecomesTrue(testedView.exists);
self.matchingElement = [[testedView fb_descendantsMatchingIdentifier:@"Alerts" shouldReturnAfterFirstMatch:YES] firstObject];
XCTAssertNotNil(self.matchingElement);
}
- (void)verifyGettingAttributeWithShortcut:(NSString *)shortcutName expectedValue:(id)expectedValue
{
NSString *fullAttributeName = [NSString stringWithFormat:@"wd%@", [NSString stringWithFormat:@"%@%@", [[shortcutName substringToIndex:1] uppercaseString], [shortcutName substringFromIndex:1]]];
id actualValue = [self.matchingElement fb_valueForWDAttributeName:fullAttributeName];
id actualShortcutValue = [self.matchingElement fb_valueForWDAttributeName:shortcutName];
if (nil == expectedValue) {
XCTAssertNil(actualValue);
XCTAssertNil(actualShortcutValue);
return;
}
if ([actualValue isKindOfClass:NSString.class]) {
XCTAssertTrue([actualValue isEqualToString:expectedValue]);
XCTAssertTrue([actualShortcutValue isEqualToString:expectedValue]);
} else if ([actualValue isKindOfClass:NSNumber.class]) {
XCTAssertTrue([actualValue isEqualToNumber:expectedValue]);
XCTAssertTrue([actualShortcutValue isEqualToNumber:expectedValue]);
} else {
XCTAssertEqual(actualValue, expectedValue);
XCTAssertEqual(actualShortcutValue, expectedValue);
}
}
- (void)testGetNameAttribute
{
[self verifyGettingAttributeWithShortcut:@"name" expectedValue:self.matchingElement.wdName];
}
- (void)testGetValueAttribute
{
[self verifyGettingAttributeWithShortcut:@"value" expectedValue:self.matchingElement.wdValue];
}
- (void)testGetLabelAttribute
{
[self verifyGettingAttributeWithShortcut:@"label" expectedValue:self.matchingElement.wdLabel];
}
- (void)testGetTypeAttribute
{
[self verifyGettingAttributeWithShortcut:@"type" expectedValue:self.matchingElement.wdType];
}
- (void)testGetRectAttribute
{
NSString *shortcutName = @"rect";
for (NSString *key in @[@"x", @"y", @"width", @"height"]) {
NSNumber *actualValue = [self.matchingElement fb_valueForWDAttributeName:[FBElementUtils wdAttributeNameForAttributeName:shortcutName]][key];
NSNumber *actualShortcutValue = [self.matchingElement fb_valueForWDAttributeName:shortcutName][key];
NSNumber *expectedValue = self.matchingElement.wdRect[key];
XCTAssertTrue([actualValue isEqualToNumber:expectedValue]);
XCTAssertTrue([actualShortcutValue isEqualToNumber:expectedValue]);
}
}
- (void)testGetEnabledAttribute
{
[self verifyGettingAttributeWithShortcut:@"enabled" expectedValue:[NSNumber numberWithBool:self.matchingElement.wdEnabled]];
}
- (void)testGetAccessibleAttribute
{
[self verifyGettingAttributeWithShortcut:@"accessible" expectedValue:[NSNumber numberWithBool:self.matchingElement.wdAccessible]];
}
- (void)testGetUidAttribute
{
[self verifyGettingAttributeWithShortcut:@"UID" expectedValue:self.matchingElement.wdUID];
}
- (void)testGetVisibleAttribute
{
[self verifyGettingAttributeWithShortcut:@"visible" expectedValue:[NSNumber numberWithBool:self.matchingElement.wdVisible]];
}
- (void)testGetAccessibilityContainerAttribute
{
[self verifyGettingAttributeWithShortcut:@"accessibilityContainer" expectedValue:[NSNumber numberWithBool:self.matchingElement.wdAccessibilityContainer]];
}
- (void)testGetInvalidAttribute
{
XCTAssertThrowsSpecificNamed([self verifyGettingAttributeWithShortcut:@"invalid" expectedValue:@"blabla"], NSException, FBUnknownAttributeException);
}
@end
@interface XCUIElementFBFindTests_CompactResponses : FBIntegrationTestCase
@end
@implementation XCUIElementFBFindTests_CompactResponses
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
});
}
- (void)testCompactResponseYes
{
XCUIElement *alertsButton = self.testedApplication.buttons[@"Alerts"];
NSDictionary *fields = FBDictionaryResponseWithElement(alertsButton, YES);
XCTAssertNotNil(fields[@"ELEMENT"]);
XCTAssertNotNil(fields[@"element-6066-11e4-a52e-4f735466cecf"]);
XCTAssertEqual(fields.count, 2);
}
- (void)testCompactResponseNo
{
XCUIElement *alertsButton = self.testedApplication.buttons[@"Alerts"];
NSDictionary *fields = FBDictionaryResponseWithElement(alertsButton, NO);
XCTAssertNotNil(fields[@"ELEMENT"]);
XCTAssertNotNil(fields[@"element-6066-11e4-a52e-4f735466cecf"]);
XCTAssertEqualObjects(fields[@"type"], @"XCUIElementTypeButton");
XCTAssertEqualObjects(fields[@"label"], @"Alerts");
XCTAssertEqual(fields.count, 4);
}
@end
@interface XCUIElementFBFindTests_ResponseFields : FBIntegrationTestCase
@end
@implementation XCUIElementFBFindTests_ResponseFields
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
});
}
- (void)testCompactResponseYesWithResponseAttributesSet
{
[FBConfiguration setElementResponseAttributes:@"name,text,enabled"];
XCUIElement *alertsButton = self.testedApplication.buttons[@"Alerts"];
NSDictionary *fields = FBDictionaryResponseWithElement(alertsButton, YES);
XCTAssertNotNil(fields[@"ELEMENT"]);
XCTAssertNotNil(fields[@"element-6066-11e4-a52e-4f735466cecf"]);
XCTAssertEqual(fields.count, 2);
}
- (void)testCompactResponseNoWithResponseAttributesSet
{
[FBConfiguration setElementResponseAttributes:@"name,text,enabled"];
XCUIElement *alertsButton = self.testedApplication.buttons[@"Alerts"];
NSDictionary *fields = FBDictionaryResponseWithElement(alertsButton, NO);
XCTAssertNotNil(fields[@"ELEMENT"]);
XCTAssertNotNil(fields[@"element-6066-11e4-a52e-4f735466cecf"]);
XCTAssertEqualObjects(fields[@"name"], @"XCUIElementTypeButton");
XCTAssertEqualObjects(fields[@"text"], @"Alerts");
XCTAssertEqualObjects(fields[@"enabled"], @(YES));
XCTAssertEqual(fields.count, 5);
}
- (void)testInvalidAttribute
{
[FBConfiguration setElementResponseAttributes:@"invalid_field,name"];
XCUIElement *alertsButton = self.testedApplication.buttons[@"Alerts"];
NSDictionary *fields = FBDictionaryResponseWithElement(alertsButton, NO);
XCTAssertNotNil(fields[@"ELEMENT"]);
XCTAssertNotNil(fields[@"element-6066-11e4-a52e-4f735466cecf"]);
XCTAssertEqualObjects(fields[@"name"], @"XCUIElementTypeButton");
XCTAssertEqual(fields.count, 3);
}
- (void)testKnownAttributes
{
[FBConfiguration setElementResponseAttributes:@"name,type,label,text,rect,enabled,displayed,selected"];
XCUIElement *alertsButton = self.testedApplication.buttons[@"Alerts"];
NSDictionary *fields = FBDictionaryResponseWithElement(alertsButton, NO);
XCTAssertNotNil(fields[@"ELEMENT"]);
XCTAssertNotNil(fields[@"element-6066-11e4-a52e-4f735466cecf"]);
XCTAssertEqualObjects(fields[@"name"], @"XCUIElementTypeButton");
XCTAssertEqualObjects(fields[@"type"], @"XCUIElementTypeButton");
XCTAssertEqualObjects(fields[@"label"], @"Alerts");
XCTAssertEqualObjects(fields[@"text"], @"Alerts");
XCTAssertTrue(matchesRegex([fields[@"rect"] description], @"\\{\\s*height = [0-9]+;\\s*width = [0-9]+;\\s*x = [0-9]+;\\s*y = [0-9]+;\\s*\\}"));
XCTAssertEqualObjects(fields[@"enabled"], @(YES));
XCTAssertEqualObjects(fields[@"displayed"], @(YES));
XCTAssertEqualObjects(fields[@"selected"], @(NO));
XCTAssertEqual(fields.count, 10);
}
- (void)testArbitraryAttributes
{
[FBConfiguration setElementResponseAttributes:@"attribute/name,attribute/value"];
XCUIElement *alertsButton = self.testedApplication.buttons[@"Alerts"];
NSDictionary *fields = FBDictionaryResponseWithElement(alertsButton, NO);
XCTAssertNotNil(fields[@"ELEMENT"]);
XCTAssertNotNil(fields[@"element-6066-11e4-a52e-4f735466cecf"]);
XCTAssertEqualObjects(fields[@"attribute/name"], @"Alerts");
XCTAssertEqualObjects(fields[@"attribute/value"], [NSNull null]);
XCTAssertEqual(fields.count, 4);
}
static BOOL matchesRegex(NSString *target, NSString *pattern) {
if (!target)
return NO;
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:NULL];
return [regex numberOfMatchesInString:target options:0 range:NSMakeRange(0, target.length)] == 1;
}
@end

View File

@@ -0,0 +1,472 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBIntegrationTestCase.h"
#import "FBElementUtils.h"
#import "FBExceptions.h"
#import "FBTestMacros.h"
#import "XCUIElement.h"
#import "XCUIElement+FBFind.h"
#import "XCUIElement+FBUID.h"
#import "FBXCElementSnapshotWrapper+Helpers.h"
#import "XCUIElement+FBIsVisible.h"
#import "XCUIElement+FBClassChain.h"
#import "XCUIElement+FBResolve.h"
#import "FBXPath.h"
#import "FBXCodeCompatibility.h"
@interface XCUIElementFBFindTests : FBIntegrationTestCase
@property (nonatomic, strong) XCUIElement *testedView;
@end
@implementation XCUIElementFBFindTests
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
});
self.testedView = self.testedApplication.otherElements[@"MainView"];
FBAssertWaitTillBecomesTrue(self.testedView.exists);
}
- (void)testDescendantsWithClassName
{
NSSet<NSString *> *expectedLabels = [NSSet setWithArray:@[
@"Alerts",
@"Attributes",
@"Scrolling",
@"Deadlock app",
@"Touch",
]];
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingClassName:@"XCUIElementTypeButton"
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(matchingSnapshots.count, expectedLabels.count);
NSArray<NSString *> *labels = [matchingSnapshots valueForKeyPath:@"@distinctUnionOfObjects.label"];
XCTAssertEqualObjects([NSSet setWithArray:labels], expectedLabels);
NSArray<NSNumber *> *types = [matchingSnapshots valueForKeyPath:@"@distinctUnionOfObjects.elementType"];
XCTAssertEqual(types.count, 1, @"matchingSnapshots should contain only one type");
XCTAssertEqualObjects(types.lastObject, @(XCUIElementTypeButton), @"matchingSnapshots should contain only one type");
}
- (void)testSingleDescendantWithClassName
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingClassName:@"XCUIElementTypeButton"
shouldReturnAfterFirstMatch:YES];
XCTAssertEqual(matchingSnapshots.count, 1);
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton);
}
- (void)testDescendantsWithIdentifier
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingIdentifier:@"Alerts"
shouldReturnAfterFirstMatch:NO];
int snapshotsCount = 2;
XCTAssertEqual(matchingSnapshots.count, snapshotsCount);
XCTAssertEqual(matchingSnapshots.firstObject.elementType, XCUIElementTypeButton);
XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts");
NSArray<XCUIElement *> *selfElementsById = [matchingSnapshots.lastObject fb_descendantsMatchingIdentifier:@"Alerts" shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(selfElementsById.count, 1);
}
- (void)testSingleDescendantWithIdentifier
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingIdentifier:@"Alerts"
shouldReturnAfterFirstMatch:YES];
XCTAssertEqual(matchingSnapshots.count, 1);
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton);
XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts");
}
- (void)testStableInstance
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingIdentifier:@"Alerts"
shouldReturnAfterFirstMatch:YES];
XCTAssertEqual(matchingSnapshots.count, 1);
for (XCUIElement *el in @[
matchingSnapshots.lastObject,
[matchingSnapshots.lastObject fb_stableInstanceWithUid:[matchingSnapshots.lastObject fb_uid]]
]) {
XCTAssertEqual(el.elementType, XCUIElementTypeButton);
XCTAssertEqualObjects(el.label, @"Alerts");
}
}
- (void)testSingleDescendantWithMissingIdentifier
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingIdentifier:@"blabla" shouldReturnAfterFirstMatch:YES];
XCTAssertEqual(matchingSnapshots.count, 0);
}
- (void)testDescendantsWithXPathQuery
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeButton[@label='Alerts']"
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(matchingSnapshots.count, 1);
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton);
XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts");
}
- (void)testSelfWithXPathQuery
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedApplication fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeApplication"
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(matchingSnapshots.count, 1);
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeApplication);
}
- (void)testSingleDescendantWithXPathQuery
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedApplication fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeButton[@hittable='true']"
shouldReturnAfterFirstMatch:YES];
XCTAssertEqual(matchingSnapshots.count, 1);
XCUIElement *matchingSnapshot = [matchingSnapshots firstObject];
XCTAssertNotNil(matchingSnapshot);
XCTAssertEqual(matchingSnapshot.elementType, XCUIElementTypeButton);
XCTAssertEqualObjects(matchingSnapshot.label, @"Alerts");
}
- (void)testSingleDescendantWithXPathQueryNoMatches
{
XCUIElement *matchingSnapshot = [[self.testedView fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeButtonnn"
shouldReturnAfterFirstMatch:YES] firstObject];
XCTAssertNil(matchingSnapshot);
}
- (void)testSingleLastDescendantWithXPathQuery
{
XCUIElement *matchingSnapshot = [[self.testedView fb_descendantsMatchingXPathQuery:@"(//XCUIElementTypeButton)[last()]"
shouldReturnAfterFirstMatch:YES] firstObject];
XCTAssertNotNil(matchingSnapshot);
XCTAssertEqual(matchingSnapshot.elementType, XCUIElementTypeButton);
}
- (void)testDescendantsWithXPathQueryNoMatches
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeButton[@label='Alerts1']"
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(matchingSnapshots.count, 0);
}
- (void)testDescendantsWithComplexXPathQuery
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingXPathQuery:@"//*[@label='Scrolling']/preceding::*[boolean(string(@label))]"
shouldReturnAfterFirstMatch:NO];
int snapshotsCount = 6;
XCTAssertEqual(matchingSnapshots.count, snapshotsCount);
}
- (void)testDescendantsWithWrongXPathQuery
{
XCTAssertThrowsSpecificNamed([self.testedView fb_descendantsMatchingXPathQuery:@"//*[blabla(@label, Scrolling')]"
shouldReturnAfterFirstMatch:NO],
NSException, FBInvalidXPathException);
}
- (void)testFirstDescendantWithWrongXPathQuery
{
XCTAssertThrowsSpecificNamed([self.testedView fb_descendantsMatchingXPathQuery:@"//*[blabla(@label, Scrolling')]"
shouldReturnAfterFirstMatch:YES],
NSException, FBInvalidXPathException);
}
- (void)testVisibleDescendantWithXPathQuery
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeButton[@name='Alerts' and @enabled='true' and @visible='true']" shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(matchingSnapshots.count, 1);
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton);
XCTAssertTrue(matchingSnapshots.lastObject.isEnabled);
XCTAssertTrue(matchingSnapshots.lastObject.fb_isVisible);
XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts");
}
- (void)testDescendantsWithPredicateString
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"label = 'Alerts'"];
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingPredicate:predicate
shouldReturnAfterFirstMatch:NO];
int snapshotsCount = 2;
XCTAssertEqual(matchingSnapshots.count, snapshotsCount);
XCTAssertEqual(matchingSnapshots.firstObject.elementType, XCUIElementTypeButton);
XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts");
NSPredicate *selfPredicate = [NSPredicate predicateWithFormat:@"label == 'Alerts'"];
NSArray<XCUIElement *> *selfElementsByPredicate = [matchingSnapshots.lastObject fb_descendantsMatchingPredicate:selfPredicate
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(selfElementsByPredicate.count, 1);
}
- (void)testSelfWithPredicateString
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"type == 'XCUIElementTypeApplication'"];
NSArray<XCUIElement *> *matchingSnapshots = [self.testedApplication fb_descendantsMatchingPredicate:predicate
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(matchingSnapshots.count, 1);
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeApplication);
}
- (void)testSingleDescendantWithPredicateString
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"type = 'XCUIElementTypeButton'"];
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingPredicate:predicate shouldReturnAfterFirstMatch:YES];
XCTAssertEqual(matchingSnapshots.count, 1);
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton);
}
- (void)testSingleDescendantWithPredicateStringByIndex
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"type == 'XCUIElementTypeButton' AND index == 2"];
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingPredicate:predicate shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(matchingSnapshots.count, 1);
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton);
}
- (void)testDescendantsWithPropertyStrict
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingProperty:@"label"
value:@"Alert"
partialSearch:NO];
XCTAssertEqual(matchingSnapshots.count, 0);
matchingSnapshots = [self.testedView fb_descendantsMatchingProperty:@"label" value:@"Alerts" partialSearch:NO];
int snapshotsCount = 2;
XCTAssertEqual(matchingSnapshots.count, snapshotsCount);
XCTAssertEqual(matchingSnapshots.firstObject.elementType, XCUIElementTypeButton);
XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts");
}
- (void)testGlobalWithPropertyStrict
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedApplication fb_descendantsMatchingProperty:@"label"
value:@"Alert"
partialSearch:NO];
XCTAssertEqual(matchingSnapshots.count, 0);
matchingSnapshots = [self.testedApplication fb_descendantsMatchingProperty:@"label" value:@"Alerts" partialSearch:NO];
int snapshotsCount = 2;
XCTAssertEqual(matchingSnapshots.count, snapshotsCount);
XCTAssertEqual(matchingSnapshots.firstObject.elementType, XCUIElementTypeButton);
XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts");
}
- (void)testDescendantsWithPropertyPartial
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingProperty:@"label"
value:@"Alerts"
partialSearch:NO];
int snapshotsCount = 2;
XCTAssertEqual(matchingSnapshots.count, snapshotsCount);
XCTAssertEqual(matchingSnapshots.firstObject.elementType, XCUIElementTypeButton);
XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts");
}
- (void)testDescendantsWithClassChain
{
NSArray<XCUIElement *> *matchingSnapshots;
NSString *queryString =@"XCUIElementTypeWindow/XCUIElementTypeOther/**/XCUIElementTypeButton";
matchingSnapshots = [self.testedApplication fb_descendantsMatchingClassChain:queryString
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(matchingSnapshots.count, 5); // /XCUIElementTypeButton
for (XCUIElement *matchingSnapshot in matchingSnapshots) {
XCTAssertEqual(matchingSnapshot.elementType, XCUIElementTypeButton);
}
}
- (void)testDescendantsWithClassChainWithIndex
{
NSArray<XCUIElement *> *matchingSnapshots;
// iPhone
NSString *queryString = @"XCUIElementTypeWindow/*/*/*/*[2]/*/*/XCUIElementTypeButton";
matchingSnapshots = [self.testedApplication fb_descendantsMatchingClassChain:queryString
shouldReturnAfterFirstMatch:NO];
if (matchingSnapshots.count == 0) {
// iPad
queryString = @"XCUIElementTypeWindow/*/*/*/*/*[2]/*/*/XCUIElementTypeButton";
matchingSnapshots = [self.testedApplication fb_descendantsMatchingClassChain:queryString
shouldReturnAfterFirstMatch:NO];
}
XCTAssertEqual(matchingSnapshots.count, 5); // /XCUIElementTypeButton
for (XCUIElement *matchingSnapshot in matchingSnapshots) {
XCTAssertEqual(matchingSnapshot.elementType, XCUIElementTypeButton);
}
}
- (void)testDescendantsWithClassChainAndPredicates
{
NSArray<XCUIElement *> *matchingSnapshots;
NSString *queryString = @"XCUIElementTypeWindow/**/XCUIElementTypeButton[`label BEGINSWITH 'A'`]";
matchingSnapshots = [self.testedApplication fb_descendantsMatchingClassChain:queryString
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(matchingSnapshots.count, 2);
XCTAssertEqualObjects([matchingSnapshots firstObject].label, @"Alerts");
XCTAssertEqualObjects([matchingSnapshots lastObject].label, @"Attributes");
}
- (void)testDescendantsWithIndirectClassChainAndPredicates
{
NSString *queryString = @"XCUIElementTypeWindow/**/XCUIElementTypeButton[`label BEGINSWITH 'A'`]";
NSArray<XCUIElement *> *simpleQueryMatches = [self.testedApplication fb_descendantsMatchingClassChain:queryString
shouldReturnAfterFirstMatch:NO];
NSArray<XCUIElement *> *deepQueryMatches = [self.testedApplication fb_descendantsMatchingClassChain:@"XCUIElementTypeWindow/**/XCUIElementTypeButton[`label BEGINSWITH 'A'`]"
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(simpleQueryMatches.count, deepQueryMatches.count);
XCTAssertEqualObjects([simpleQueryMatches firstObject].label, [deepQueryMatches firstObject].label);
XCTAssertEqualObjects([simpleQueryMatches lastObject].label, [deepQueryMatches lastObject].label);
}
- (void)testClassChainWithDescendantPredicate
{
NSArray<XCUIElement *> *simpleQueryMatches = [self.testedApplication fb_descendantsMatchingClassChain:@"XCUIElementTypeWindow/*/*[1]"
shouldReturnAfterFirstMatch:NO];
NSArray<XCUIElement *> *predicateQueryMatches = [self.testedApplication fb_descendantsMatchingClassChain:@"XCUIElementTypeWindow/*/*[$type == 'XCUIElementTypeButton' AND label BEGINSWITH 'A'$]"
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(simpleQueryMatches.count, predicateQueryMatches.count);
XCTAssertEqual([simpleQueryMatches firstObject].elementType, [predicateQueryMatches firstObject].elementType);
XCTAssertEqual([simpleQueryMatches lastObject].elementType, [predicateQueryMatches lastObject].elementType);
}
- (void)testSingleDescendantWithComplexIndirectClassChain
{
NSArray<XCUIElement *> *queryMatches = [self.testedApplication fb_descendantsMatchingClassChain:@"**/*/XCUIElementTypeButton[2]"
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(queryMatches.count, 1);
XCTAssertEqual(queryMatches.lastObject.elementType, XCUIElementTypeButton);
XCTAssertEqualObjects(queryMatches.lastObject.label, @"Deadlock app");
}
- (void)testSingleDescendantWithComplexIndirectClassChainAndZeroMatches
{
NSArray<XCUIElement *> *queryMatches = [self.testedApplication fb_descendantsMatchingClassChain:@"**/*/XCUIElementTypeWindow"
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(queryMatches.count, 0);
}
- (void)testDescendantsWithClassChainAndPredicatesAndIndexes
{
NSArray<XCUIElement *> *matchingSnapshots;
NSString *queryString = @"XCUIElementTypeWindow[`name != 'bla'`]/**/XCUIElementTypeButton[`label BEGINSWITH \"A\"`][1]";
matchingSnapshots = [self.testedApplication fb_descendantsMatchingClassChain:queryString
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(matchingSnapshots.count, 1);
XCTAssertEqualObjects([matchingSnapshots firstObject].label, @"Alerts");
}
- (void)testSingleDescendantWithClassChain
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingClassChain:@"XCUIElementTypeButton"
shouldReturnAfterFirstMatch:YES];
XCTAssertEqual(matchingSnapshots.count, 1);
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton);
XCTAssertTrue([matchingSnapshots.lastObject.label isEqualToString:@"Alerts"]);
}
- (void)testSingleDescendantWithClassChainAndNegativeIndex
{
NSArray<XCUIElement *> *matchingSnapshots;
matchingSnapshots = [self.testedView fb_descendantsMatchingClassChain:@"XCUIElementTypeButton[-1]"
shouldReturnAfterFirstMatch:YES];
XCTAssertEqual(matchingSnapshots.count, 1);
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton);
XCTAssertTrue([matchingSnapshots.lastObject.label isEqualToString:@"Touch"]);
matchingSnapshots = [self.testedView fb_descendantsMatchingClassChain:@"XCUIElementTypeButton[-10]"
shouldReturnAfterFirstMatch:YES];
XCTAssertEqual(matchingSnapshots.count, 0);
}
- (void)testInvalidQueryWithClassChain
{
XCTAssertThrowsSpecificNamed([self.testedView fb_descendantsMatchingClassChain:@"NoXCUIElementTypePrefix"
shouldReturnAfterFirstMatch:YES],
NSException, FBClassChainQueryParseException);
}
- (void)testHandleInvalidQueryWithClassChainAsNoElementWithoutError
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView
fb_descendantsMatchingClassChain:@"XCUIElementTypeBlabla"
shouldReturnAfterFirstMatch:YES];
XCTAssertEqual(matchingSnapshots.count, 0);
}
- (void)testClassChainWithInvalidPredicate
{
XCTAssertThrowsSpecificNamed([self.testedApplication fb_descendantsMatchingClassChain:@"XCUIElementTypeWindow[`bla != 'bla'`]"
shouldReturnAfterFirstMatch:NO],
NSException, FBUnknownAttributeException);;
}
@end
@interface XCUIElementFBFindTests_AttributesPage : FBIntegrationTestCase
@end
@implementation XCUIElementFBFindTests_AttributesPage
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
[self goToAttributesPage];
});
}
- (void)testNestedQueryWithClassChain
{
NSString *queryString = @"XCUIElementTypePicker";
FBAssertWaitTillBecomesTrue(self.testedApplication.buttons[@"Button"].fb_isVisible);
XCUIElement *datePicker = [self.testedApplication
descendantsMatchingType:XCUIElementTypeDatePicker].allElementsBoundByIndex.firstObject;
NSArray<XCUIElement *> *matches = [datePicker fb_descendantsMatchingClassChain:queryString
shouldReturnAfterFirstMatch:NO];
XCTAssertEqual(matches.count, 1);
XCUIElementType expectedType = XCUIElementTypePicker;
XCTAssertEqual([matches firstObject].elementType, expectedType);
}
@end
@interface XCUIElementFBFindTests_ScrollPage : FBIntegrationTestCase
@end
@implementation XCUIElementFBFindTests_ScrollPage
- (void)setUp
{
[super setUp];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self launchApplication];
[self goToScrollPageWithCells:YES];
});
}
- (void)testInvisibleDescendantWithXPathQuery
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedApplication fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeStaticText[@visible='false']"
shouldReturnAfterFirstMatch:NO];
XCTAssertGreaterThan(matchingSnapshots.count, 1);
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeStaticText);
XCTAssertFalse(matchingSnapshots.lastObject.fb_isVisible);
}
- (void)testNonHittableDescendantWithXPathQuery
{
NSArray<XCUIElement *> *matchingSnapshots = [self.testedApplication fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeStaticText[@hittable='false']"
shouldReturnAfterFirstMatch:NO];
XCTAssertGreaterThan(matchingSnapshots.count, 1);
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeStaticText);
XCTAssertFalse(matchingSnapshots.lastObject.fb_isVisible);
}
@end

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "XCTest/XCUIElementTypes.h"
#import "FBIntegrationTestCase.h"
#import "FBTestMacros.h"
#import "FBElement.h"
#import "FBElementUtils.h"
#import "FBXCElementSnapshot.h"
#import "XCUIElement+FBUtilities.h"
@interface XCUIElementHelperIntegrationTests : FBIntegrationTestCase
@end
@implementation XCUIElementHelperIntegrationTests
- (void)setUp
{
[super setUp];
[self launchApplication];
[self goToAlertsPage];
}
- (void)testDescendantsFiltering
{
NSArray<XCUIElement *> *buttons = self.testedApplication.buttons.allElementsBoundByIndex;
XCTAssertTrue(buttons.count > 0);
NSArray<XCUIElement *> *windows = self.testedApplication.windows.allElementsBoundByIndex;
XCTAssertTrue(windows.count > 0);
NSMutableArray<XCUIElement *> *allElements = [NSMutableArray array];
[allElements addObjectsFromArray:buttons];
[allElements addObjectsFromArray:windows];
NSMutableArray<id<FBXCElementSnapshot>> *buttonSnapshots = [NSMutableArray array];
[buttonSnapshots addObject:[buttons.firstObject fb_customSnapshot]];
NSArray<XCUIElement *> *result = [self.testedApplication fb_filterDescendantsWithSnapshots:buttonSnapshots
onlyChildren:NO];
XCTAssertEqual(1, result.count);
XCTAssertEqual([result.firstObject elementType], XCUIElementTypeButton);
}
@end

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
@interface XCElementSnapshotDouble : NSObject<XCUIElementAttributes>
@property (readwrite, nullable) id value;
@property (readwrite, nullable, copy) NSString *label;
@property (nonatomic, assign) UIAccessibilityTraits traits;
@end

View File

@@ -0,0 +1,114 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "XCElementSnapshotDouble.h"
#import "FBXCAccessibilityElement.h"
#import "FBXCElementSnapshot.h"
#import "XCUIHitPointResult.h"
@implementation XCElementSnapshotDouble
- (id)init
{
self = [super init];
self->_value = @"magicValue";
self->_label = @"testLabel";
return self;
}
- (NSString *)identifier
{
return @"testName";
}
- (CGRect)frame
{
return CGRectZero;
}
- (NSString *)title
{
return @"testTitle";
}
- (XCUIElementType)elementType
{
return XCUIElementTypeOther;
}
- (BOOL)isEnabled
{
return YES;
}
- (XCUIUserInterfaceSizeClass)horizontalSizeClass
{
return XCUIUserInterfaceSizeClassUnspecified;
}
- (XCUIUserInterfaceSizeClass)verticalSizeClass
{
return XCUIUserInterfaceSizeClassUnspecified;
}
- (NSString *)placeholderValue
{
return @"testPlaceholderValue";
}
- (BOOL)isSelected
{
return YES;
}
- (BOOL)hasFocus
{
return YES;
}
- (NSDictionary *)additionalAttributes
{
return @{};
}
- (id<FBXCAccessibilityElement>)accessibilityElement
{
return nil;
}
- (id<FBXCElementSnapshot>)parent
{
return nil;
}
- (XCUIHitPointResult *)hitPoint:(NSError **)error
{
return [[XCUIHitPointResult alloc] initWithHitPoint:CGPointZero hittable:YES];
}
- (NSArray *)children
{
return @[];
}
- (NSArray *)_allDescendants
{
return @[];
}
- (CGRect)visibleFrame
{
return CGRectZero;
}
- (UIAccessibilityTraits)traits
{
return UIAccessibilityTraitButton;
}
@end

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
@interface XCUIApplicationDouble : NSObject
@property (nonatomic, assign, readonly) BOOL didTerminate;
@property (nonatomic, strong) NSString* bundleID;
@property (nonatomic) BOOL fb_shouldWaitForQuiescence;
- (BOOL)running;
@end

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "XCUIApplicationDouble.h"
@interface XCUIApplicationDouble ()
@property (nonatomic, assign, readwrite) BOOL didTerminate;
@end
@implementation XCUIApplicationDouble
- (instancetype)init
{
self = [super init];
if (self) {
_bundleID = @"some.bundle.identifier";
}
return self;
}
- (void)terminate
{
self.didTerminate = YES;
}
- (NSUInteger)processID
{
return 0;
}
- (NSString *)bundleID
{
return @"com.facebook.awesome";
}
- (void)fb_nativeResolve
{
}
- (id)query
{
return nil;
}
- (BOOL)fb_shouldWaitForQuiescence
{
return NO;
}
-(void)setFb_shouldWaitForQuiescence:(BOOL)value
{
}
- (BOOL)running
{
return NO;
}
@end

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import <WebDriverAgentLib/FBElement.h>
#import <XCTest/XCUIElementTypes.h>
@class XCUIApplication;
@interface XCUIElementDouble : NSObject<FBElement>
@property (nonatomic, strong, nonnull) XCUIApplication *application;
@property (nonatomic, readwrite, assign) CGRect frame;
@property (nonatomic, readwrite, nullable) id lastSnapshot;
@property (nonatomic, assign) BOOL fb_isObstructedByAlert;
@property (nonatomic, readonly, nonnull) NSString *fb_cacheId;
@property (nonatomic, readwrite, copy, nonnull) NSDictionary *wdRect;
@property (nonatomic, readwrite, assign) CGRect wdFrame;
@property (nonatomic, readwrite, copy, nonnull) NSString *wdUID;
@property (nonatomic, copy, readwrite, nullable) NSString *wdName;
@property (nonatomic, copy, readwrite, nullable) NSString *wdLabel;
@property (nonatomic, copy, readwrite, nonnull) NSString *wdType;
@property (nonatomic, strong, readwrite, nullable) NSString *wdValue;
@property (nonatomic, readwrite, getter=isWDEnabled) BOOL wdEnabled;
@property (nonatomic, readwrite, getter=isWDSelected) BOOL wdSelected;
@property (nonatomic, readwrite, assign) CGRect wdNativeFrame;
@property (nonatomic, readwrite) NSUInteger wdIndex;
@property (nonatomic, readwrite, getter=isWDVisible) BOOL wdVisible;
@property (nonatomic, readwrite, getter=isWDAccessible) BOOL wdAccessible;
@property (nonatomic, readwrite, getter = isWDFocused) BOOL wdFocused;
@property (nonatomic, readwrite, getter = isWDHittable) BOOL wdHittable;
@property (nonatomic, copy, readwrite, nullable) NSString *wdPlaceholderValue;
@property (copy, nonnull) NSArray *children;
@property (nonatomic, readwrite, assign) XCUIElementType elementType;
@property (nonatomic, readwrite, getter=isWDAccessibilityContainer) BOOL wdAccessibilityContainer;
@property (nonatomic, copy, readwrite, nullable) NSString *wdTraits;
- (void)resolve;
- (id _Nonnull)fb_standardSnapshot;
- (id _Nonnull)fb_customSnapshot;
- (nullable id)query;
// Checks
@property (nonatomic, assign, readonly) BOOL didResolve;
@end

View File

@@ -0,0 +1,103 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "XCUIElementDouble.h"
@interface XCUIElementDouble ()
@property (nonatomic, assign, readwrite) BOOL didResolve;
@end
@implementation XCUIElementDouble
- (id)init
{
self = [super init];
if (self) {
self.wdFrame = CGRectZero;
self.wdNativeFrame = CGRectZero;
self.wdName = @"testName";
self.wdLabel = @"testLabel";
self.wdValue = @"magicValue";
self.wdPlaceholderValue = @"testPlaceholderValue";
self.wdTraits = @"testTraits";
self.wdVisible = YES;
self.wdAccessible = YES;
self.wdEnabled = YES;
self.wdSelected = YES;
self.wdFocused = YES;
self.wdHittable = YES;
self.wdIndex = 0;
#if TARGET_OS_TV
self.wdFocused = YES;
#endif
self.children = @[];
self.wdRect = @{@"x": @0,
@"y": @0,
@"width": @0,
@"height": @0,
};
self.wdAccessibilityContainer = NO;
self.elementType = XCUIElementTypeOther;
self.wdType = @"XCUIElementTypeOther";
self.wdUID = @"0";
self.lastSnapshot = nil;
}
return self;
}
- (id)fb_valueForWDAttributeName:(NSString *)name
{
return @"test";
}
- (id)query
{
return nil;
}
- (void)resolve
{
self.didResolve = YES;
}
- (void)fb_nativeResolve
{
self.didResolve = YES;
}
- (id _Nonnull)fb_standardSnapshot;
{
return [self lastSnapshot];
}
- (id _Nonnull)fb_customSnapshot;
{
return [self lastSnapshot];
}
- (NSString *)fb_cacheId
{
return self.wdUID;
}
- (id)lastSnapshot
{
return self;
}
- (id)fb_uid
{
return self.wdUID;
}
- (NSString *)wdTraits
{
return self.wdTraits;
}
@end

View File

@@ -0,0 +1,313 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "XCUIElementDouble.h"
#import "FBClassChainQueryParser.h"
@interface FBClassChainTests : XCTestCase
@end
@implementation FBClassChainTests
- (void)testValidChain
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"XCUIElementTypeWindow/XCUIElementTypeButton" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 2);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeWindow);
XCTAssertNil(firstElement.position);
XCTAssertFalse(firstElement.isDescendant);
FBClassChainItem *secondElement = [result.elements objectAtIndex:1];
XCTAssertEqual(secondElement.type, XCUIElementTypeButton);
XCTAssertNil(secondElement.position);
XCTAssertFalse(secondElement.isDescendant);
}
- (void)testValidChainWithStar
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"XCUIElementTypeWindow/XCUIElementTypeButton[3]/*[4]/*[5]/XCUIElementTypeAlert" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 5);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeWindow);
XCTAssertNil(firstElement.position);
XCTAssertFalse(firstElement.isDescendant);
FBClassChainItem *secondElement = [result.elements objectAtIndex:1];
XCTAssertEqual(secondElement.type, XCUIElementTypeButton);
XCTAssertEqual(secondElement.position.integerValue, 3);
XCTAssertFalse(secondElement.isDescendant);
FBClassChainItem *thirdElement = [result.elements objectAtIndex:2];
XCTAssertEqual(thirdElement.type, XCUIElementTypeAny);
XCTAssertEqual(thirdElement.position.integerValue, 4);
XCTAssertFalse(thirdElement.isDescendant);
FBClassChainItem *fourthElement = [result.elements objectAtIndex:3];
XCTAssertEqual(fourthElement.type, XCUIElementTypeAny);
XCTAssertEqual(fourthElement.position.integerValue, 5);
XCTAssertFalse(fourthElement.isDescendant);
FBClassChainItem *fifthsElement = [result.elements objectAtIndex:4];
XCTAssertEqual(fifthsElement.type, XCUIElementTypeAlert);
XCTAssertNil(fifthsElement.position);
XCTAssertFalse(fifthsElement.isDescendant);
}
- (void)testValidSingleStarChain
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"*" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 1);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeAny);
XCTAssertNil(firstElement.position);
XCTAssertFalse(firstElement.isDescendant);
}
- (void)testValidSingleStarIndirectChain
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"**/*/*/XCUIElementTypeButton" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 3);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeAny);
XCTAssertNil(firstElement.position);
XCTAssertTrue(firstElement.isDescendant);
FBClassChainItem *secondElement = [result.elements objectAtIndex:1];
XCTAssertEqual(secondElement.type, XCUIElementTypeAny);
XCTAssertNil(secondElement.position);
XCTAssertFalse(secondElement.isDescendant);
FBClassChainItem *thirdElement = [result.elements objectAtIndex:2];
XCTAssertEqual(thirdElement.type, XCUIElementTypeButton);
XCTAssertNil(thirdElement.position);
XCTAssertFalse(thirdElement.isDescendant);
}
- (void)testValidDoubleIndirectChainAndStar
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"**/XCUIElementTypeButton/**/*" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 2);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeButton);
XCTAssertNil(firstElement.position);
XCTAssertTrue(firstElement.isDescendant);
FBClassChainItem *secondElement = [result.elements objectAtIndex:1];
XCTAssertEqual(secondElement.type, XCUIElementTypeAny);
XCTAssertNil(secondElement.position);
XCTAssertTrue(secondElement.isDescendant);
}
- (void)testValidDoubleIndirectChainAndClassName
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"**/XCUIElementTypeButton/**/XCUIElementTypeImage" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 2);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeButton);
XCTAssertNil(firstElement.position);
XCTAssertTrue(firstElement.isDescendant);
FBClassChainItem *secondElement = [result.elements objectAtIndex:1];
XCTAssertEqual(secondElement.type, XCUIElementTypeImage);
XCTAssertNil(secondElement.position);
XCTAssertTrue(secondElement.isDescendant);
}
- (void)testValidChainWithNegativeIndex
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"XCUIElementTypeWindow/XCUIElementTypeButton[-1]" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 2);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeWindow);
XCTAssertNil(firstElement.position);
XCTAssertEqual(firstElement.predicates.count, 0);
XCTAssertFalse(firstElement.isDescendant);
FBClassChainItem *secondElement = [result.elements objectAtIndex:1];
XCTAssertEqual(secondElement.type, XCUIElementTypeButton);
XCTAssertEqual(secondElement.position.integerValue, -1);
XCTAssertEqual(secondElement.predicates.count, 0);
XCTAssertFalse(secondElement.isDescendant);
}
- (void)testValidChainWithSinglePredicate
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"XCUIElementTypeWindow[`name == 'blabla'`]/XCUIElementTypeButton" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 2);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeWindow);
XCTAssertNil(firstElement.position);
XCTAssertEqual(firstElement.predicates.count, 1);
XCTAssertFalse(firstElement.isDescendant);
FBClassChainItem *secondElement = [result.elements objectAtIndex:1];
XCTAssertEqual(secondElement.type, XCUIElementTypeButton);
XCTAssertNil(secondElement.position);
XCTAssertEqual(secondElement.predicates.count, 0);
XCTAssertFalse(secondElement.isDescendant);
}
- (void)testValidChainWithMultiplePredicates
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"XCUIElementTypeWindow[`name == 'blabla'`]/XCUIElementTypeButton[`value == 'blabla'`]" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 2);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeWindow);
XCTAssertNil(firstElement.position);
XCTAssertEqual(firstElement.predicates.count, 1);
XCTAssertFalse(firstElement.isDescendant);
FBClassChainItem *secondElement = [result.elements objectAtIndex:1];
XCTAssertEqual(secondElement.type, XCUIElementTypeButton);
XCTAssertNil(secondElement.position);
XCTAssertEqual(secondElement.predicates.count, 1);
XCTAssertFalse(secondElement.isDescendant);
}
- (void)testValidChainWithIndirectSearchAndPredicates
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"**/XCUIElementTypeTable[`name == 'blabla'`][10]/**/XCUIElementTypeButton[`value == 'blabla'`]" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 2);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeTable);
XCTAssertEqual(firstElement.position.integerValue, 10);
XCTAssertEqual(firstElement.predicates.count, 1);
XCTAssertTrue(firstElement.isDescendant);
FBClassChainItem *secondElement = [result.elements objectAtIndex:1];
XCTAssertEqual(secondElement.type, XCUIElementTypeButton);
XCTAssertNil(secondElement.position);
XCTAssertEqual(secondElement.predicates.count, 1);
XCTAssertTrue(secondElement.isDescendant);
}
- (void)testValidChainWithMultiplePredicatesAndPositions
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"*[`name == \"к``ири````'лиця\"`][3]/XCUIElementTypeButton[`value == \"blabla\"`][-1]" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 2);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeAny);
XCTAssertEqual(firstElement.position.integerValue, 3);
XCTAssertEqual(firstElement.predicates.count, 1);
XCTAssertFalse(firstElement.isDescendant);
FBClassChainItem *secondElement = [result.elements objectAtIndex:1];
XCTAssertEqual(secondElement.type, XCUIElementTypeButton);
XCTAssertEqual(secondElement.position.integerValue, -1);
XCTAssertEqual(secondElement.predicates.count, 1);
XCTAssertFalse(secondElement.isDescendant);
}
- (void)testValidChainWithDescendantPredicate
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"**/XCUIElementTypeTable[$type == 'XCUIElementTypeImage' AND name == 'olala'$][`name == 'blabla'`][10]" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 1);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeTable);
XCTAssertEqual(firstElement.position.integerValue, 10);
XCTAssertEqual(firstElement.predicates.count, 2);
XCTAssertTrue(firstElement.isDescendant);
}
- (void)testValidChainWithMultipleDescendantPredicates
{
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:@"**/XCUIElementTypeTable[$type == 'XCUIElementTypeImage' AND name == 'olala'$][`value == 'peace'`][$value == 'yolo'$][`name == 'blabla'`][10]" error:&error];
XCTAssertNotNil(result);
XCTAssertEqual(result.elements.count, 1);
FBClassChainItem *firstElement = [result.elements firstObject];
XCTAssertEqual(firstElement.type, XCUIElementTypeTable);
XCTAssertEqual(firstElement.position.integerValue, 10);
XCTAssertEqual(firstElement.predicates.count, 4);
XCTAssertTrue(firstElement.isDescendant);
}
- (void)testInvalidChains
{
NSArray *invalidQueries = @[
@"/XCUIElementTypeWindow"
,@"XCUIElementTypeWindow/"
,@"XCUIElementTypeWindow//*"
,@"XCUIElementTypeWindow*/*"
,@"**"
,@"***"
,@"**/*/**"
,@"/**"
,@"XCUIElementTypeWindow/**"
,@"**[1]/XCUIElementTypeWindow"
,@"**[`name == '1'`]/XCUIElementTypeWindow"
,@"XCUIElementTypeWindow[0]"
,@"XCUIElementTypeWindow[1][1]"
,@"blabla"
,@"XCUIElementTypeWindow/blabla"
,@" XCUIElementTypeWindow"
,@"XCUIElementTypeWindow[ 2 ]"
,@"XCUIElementTypeWindow[[2]"
,@"XCUIElementTypeWindow[2]]"
,@"XCUIElementType[Window[2]]"
,@"XCUIElementTypeWindow[visible = 1]"
,@"XCUIElementTypeWindow[1][`visible = 1`]"
,@"XCUIElementTypeWindow[1] [`visible = 1`]"
,@"XCUIElementTypeWindow[ `visible = 1`]"
,@"XCUIElementTypeWindow[`visible = 1][`name = \"bla\"`]"
,@"XCUIElementTypeWindow[`visible = 1]"
,@"XCUIElementTypeWindow[$visible = 1]"
,@"XCUIElementTypeWindow[``]"
,@"XCUIElementTypeWindow[$$]"
,@"XCUIElementTypeWindow[`name = \"bla```bla\"`]"
,@"XCUIElementTypeWindow[$name = \"bla$$$bla\"$]"
];
for (NSString *invalidQuery in invalidQueries) {
NSError *error;
FBClassChain *result = [FBClassChainQueryParser parseQuery:invalidQuery error:&error];
XCTAssertNil(result);
XCTAssertNotNil(error);
}
}
@end

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBConfiguration.h"
@interface FBConfigurationTests : XCTestCase
@end
@implementation FBConfigurationTests
- (void)setUp
{
[super setUp];
unsetenv("USE_PORT");
unsetenv("VERBOSE_LOGGING");
}
- (void)testBindingPortDefault
{
XCTAssertTrue(NSEqualRanges([FBConfiguration bindingPortRange], NSMakeRange(8100, 100)));
}
- (void)testBindingPortEnvironmentOverwrite
{
setenv("USE_PORT", "1000", 1);
XCTAssertTrue(NSEqualRanges([FBConfiguration bindingPortRange], NSMakeRange(1000, 1)));
}
- (void)testVerboseLoggingDefault
{
XCTAssertFalse([FBConfiguration verboseLoggingEnabled]);
}
- (void)testVerboseLoggingEnvironmentOverwrite
{
setenv("VERBOSE_LOGGING", "YES", 1);
XCTAssertTrue([FBConfiguration verboseLoggingEnabled]);
}
@end

View File

@@ -0,0 +1,124 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBElementCache.h"
#import "XCUIElementDouble.h"
#import "XCUIElement+FBCaching.h"
#import "XCUIElement+FBUtilities.h"
@interface FBElementCacheTests : XCTestCase
@property (nonatomic, strong) FBElementCache *cache;
@end
@implementation FBElementCacheTests
- (void)setUp
{
[super setUp];
self.cache = [FBElementCache new];
}
- (void)testStoringElement
{
XCUIElementDouble *el1 = XCUIElementDouble.new;
el1.wdUID = @"1";
XCUIElementDouble *el2 = XCUIElementDouble.new;
el2.wdUID = @"2";
NSString *firstUUID = [self.cache storeElement:(XCUIElement *)el1];
NSString *secondUUID = [self.cache storeElement:(XCUIElement *)el2];
XCTAssertEqualObjects(firstUUID, el1.wdUID);
XCTAssertEqualObjects(secondUUID, el2.wdUID);
}
- (void)testFetchingElement
{
XCUIElement *element = (XCUIElement *)XCUIElementDouble.new;
NSString *uuid = [self.cache storeElement:element];
XCTAssertNotNil(uuid, @"Stored index should be higher than 0");
XCUIElement *cachedElement = [self.cache elementForUUID:uuid];
XCTAssertEqual(element, cachedElement);
}
- (void)testFetchingBadIndex
{
XCTAssertThrows([self.cache elementForUUID:@"random"]);
}
- (void)testLinearCacheExpulsion
{
const int ELEMENT_COUNT = 1050;
NSMutableArray *elements = [NSMutableArray arrayWithCapacity:ELEMENT_COUNT];
NSMutableArray *elementIds = [NSMutableArray arrayWithCapacity:ELEMENT_COUNT];
for(int i = 0; i < ELEMENT_COUNT; i++) {
XCUIElementDouble *el = XCUIElementDouble.new;
el.wdUID = [NSString stringWithFormat:@"%@", @(i)];
[elements addObject:(XCUIElement *)el];
}
// The capacity of the cache is limited to 1024 elements. Add 1050
// elements and make sure:
// - The first 26 elements are no longer present in the cache
// - The remaining 1024 elements are present in the cache
for(int i = 0; i < ELEMENT_COUNT; i++) {
[elementIds addObject:[self.cache storeElement:elements[i]]];
}
for(int i = 0; i < ELEMENT_COUNT - ELEMENT_CACHE_SIZE; i++) {
XCTAssertThrows([self.cache elementForUUID:elementIds[i]]);
}
for(int i = ELEMENT_COUNT - ELEMENT_CACHE_SIZE; i < ELEMENT_COUNT; i++) {
XCTAssertEqual(elements[i], [self.cache elementForUUID:elementIds[i]]);
}
}
- (void)testMRUCacheExpulsion
{
const int ELEMENT_COUNT = 1050;
const int ACCESSED_ELEMENT_COUNT = 24;
NSMutableArray *elements = [NSMutableArray arrayWithCapacity:ELEMENT_COUNT];
NSMutableArray *elementIds = [NSMutableArray arrayWithCapacity:ELEMENT_COUNT];
for(int i = 0; i < ELEMENT_COUNT; i++) {
XCUIElementDouble *el = XCUIElementDouble.new;
el.wdUID = [NSString stringWithFormat:@"%@", @(i)];
[elements addObject:(XCUIElement *)el];
}
// The capacity of the cache is limited to 1024 elements. Add 1050
// elements, but with a twist: access the first 24 elements before
// adding the last 50 elements. Then, make sure:
// - The first 24 elements are present in the cache
// - The next 26 elements are not present in the cache
// - The remaining 1000 elements are present in the cache
for(int i = 0; i < ELEMENT_CACHE_SIZE; i++) {
[elementIds addObject:[self.cache storeElement:elements[i]]];
}
for(int i = 0; i < ACCESSED_ELEMENT_COUNT; i++) {
[self.cache elementForUUID:elementIds[i]];
}
for(int i = ELEMENT_CACHE_SIZE; i < ELEMENT_COUNT; i++) {
[elementIds addObject:[self.cache storeElement:elements[i]]];
}
for(int i = 0; i < ACCESSED_ELEMENT_COUNT; i++) {
XCTAssertEqual(elements[i], [self.cache elementForUUID:elementIds[i]]);
}
for(int i = ACCESSED_ELEMENT_COUNT; i < ELEMENT_COUNT - ELEMENT_CACHE_SIZE + ACCESSED_ELEMENT_COUNT; i++) {
XCTAssertThrows([self.cache elementForUUID:elementIds[i]]);
}
for(int i = ELEMENT_COUNT - ELEMENT_CACHE_SIZE + ACCESSED_ELEMENT_COUNT; i < ELEMENT_COUNT; i++) {
XCTAssertEqual(elements[i], [self.cache elementForUUID:elementIds[i]]);
}
}
@end

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBElementTypeTransformer.h"
@interface FBElementTypeTransformerTests : XCTestCase
@end
@implementation FBElementTypeTransformerTests
- (void)testStringWithElementType
{
XCTAssertEqualObjects(@"XCUIElementTypeAny", [FBElementTypeTransformer stringWithElementType:XCUIElementTypeAny]);
XCTAssertEqualObjects(@"XCUIElementTypeIcon", [FBElementTypeTransformer stringWithElementType:XCUIElementTypeIcon]);
XCTAssertEqualObjects(@"XCUIElementTypeTab", [FBElementTypeTransformer stringWithElementType:XCUIElementTypeTab]);
XCTAssertEqualObjects(@"XCUIElementTypeOther", [FBElementTypeTransformer stringWithElementType:XCUIElementTypeOther]);
}
- (void)testShortStringWithElementType
{
XCTAssertEqualObjects(@"Any", [FBElementTypeTransformer shortStringWithElementType:XCUIElementTypeAny]);
XCTAssertEqualObjects(@"Icon", [FBElementTypeTransformer shortStringWithElementType:XCUIElementTypeIcon]);
XCTAssertEqualObjects(@"Tab", [FBElementTypeTransformer shortStringWithElementType:XCUIElementTypeTab]);
XCTAssertEqualObjects(@"Other", [FBElementTypeTransformer shortStringWithElementType:XCUIElementTypeOther]);
}
- (void)testElementTypeWithElementTypeName
{
XCTAssertEqual(XCUIElementTypeAny, [FBElementTypeTransformer elementTypeWithTypeName:@"XCUIElementTypeAny"]);
XCTAssertEqual(XCUIElementTypeIcon, [FBElementTypeTransformer elementTypeWithTypeName:@"XCUIElementTypeIcon"]);
XCTAssertEqual(XCUIElementTypeTab, [FBElementTypeTransformer elementTypeWithTypeName:@"XCUIElementTypeTab"]);
XCTAssertEqual(XCUIElementTypeOther, [FBElementTypeTransformer elementTypeWithTypeName:@"XCUIElementTypeOther"]);
XCTAssertThrows([FBElementTypeTransformer elementTypeWithTypeName:@"Whatever"]);
XCTAssertThrows([FBElementTypeTransformer elementTypeWithTypeName:nil]);
XCTAssertEqual(XCUIElementTypeOther, [FBElementTypeTransformer elementTypeWithTypeName:@"XCUIElementTypeNewType"]);
}
@end

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBElement.h"
#import "XCUIElementDouble.h"
#import "FBElementUtils.h"
@interface FBElementUtilitiesTests : XCTestCase
@end
@implementation FBElementUtilitiesTests
- (void)testTypesFiltering {
NSMutableArray *elements = [NSMutableArray new];
XCUIElementDouble *el1 = [XCUIElementDouble new];
[elements addObject:el1];
XCUIElementDouble *el2 = [XCUIElementDouble new];
el2.elementType = XCUIElementTypeAlert;
el2.wdType = @"XCUIElementTypeAlert";
[elements addObject:el2];
XCUIElementDouble *el3 = [XCUIElementDouble new];
[elements addObject:el3];
NSSet *result = [FBElementUtils uniqueElementTypesWithElements:elements];
XCTAssertEqual([result count], 2);
}
@end

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBErrorBuilder.h"
@interface FBErrorBuilderTests : XCTestCase
@end
@implementation FBErrorBuilderTests
- (void)testErrorWithDescription
{
NSString *expectedDescription = @"Magic description";
NSError *error =
[[[FBErrorBuilder builder]
withDescription:expectedDescription]
build];
XCTAssertNotNil(error);
XCTAssertEqualObjects([error localizedDescription], expectedDescription);
}
- (void)testErrorWithDescriptionFormat
{
NSError *error =
[[[FBErrorBuilder builder]
withDescriptionFormat:@"Magic %@", @"bob"]
build];
XCTAssertEqualObjects([error localizedDescription], @"Magic bob");
}
- (void)testInnerError
{
NSError *innerError = [NSError errorWithDomain:@"Domain" code:1 userInfo:@{}];
NSError *error =
[[[FBErrorBuilder builder]
withInnerError:innerError]
build];
XCTAssertEqual(error.userInfo[NSUnderlyingErrorKey], innerError);
}
- (void)testBuildWithError
{
NSString *expectedDescription = @"Magic description";
NSError *error;
BOOL result =
[[[FBErrorBuilder builder]
withDescription:expectedDescription]
buildError:&error];
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.localizedDescription, expectedDescription);
XCTAssertFalse(result);
}
@end

View File

@@ -0,0 +1,59 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBAlert.h"
#import "FBExceptionHandler.h"
#import "FBExceptions.h"
@interface RouteResponseDouble : NSObject
- (void)setHeader:(NSString *)field value:(NSString *)value;
- (void)setStatusCode:(NSUInteger)code;
- (void)respondWithData:(NSData *)data;
@end
@implementation RouteResponseDouble
- (void)setHeader:(NSString *)field value:(NSString *)value {}
- (void)setStatusCode:(NSUInteger)code {}
- (void)respondWithData:(NSData *)data {}
@end
@interface FBExceptionHandlerTests : XCTestCase
@property (nonatomic) FBExceptionHandler *exceptionHandler;
@end
@implementation FBExceptionHandlerTests
- (void)setUp
{
self.exceptionHandler = [FBExceptionHandler new];
}
- (void)testMatchingErrorHandling
{
NSException *exception = [NSException exceptionWithName:FBElementNotVisibleException
reason:@"reason"
userInfo:@{}];
[self.exceptionHandler handleException:exception
forResponse:(RouteResponse *)[RouteResponseDouble new]];
}
- (void)testNonMatchingErrorHandling
{
NSException *exception = [NSException exceptionWithName:@"something"
reason:@"reason"
userInfo:@{}];
[self.exceptionHandler handleException:exception
forResponse:(RouteResponse *)[RouteResponseDouble new]];
}
@end

View File

@@ -0,0 +1,127 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "LRUCache.h"
@interface FBLRUCacheTests : XCTestCase
@end
@implementation FBLRUCacheTests
- (void)assertArray:(NSArray *)array1 equalsTo:(NSArray *)array2
{
XCTAssertEqualObjects(array1, array2);
}
- (void)testRecentlyInsertedObjectReplacesTheOldestOne
{
LRUCache *cache = [[LRUCache alloc] initWithCapacity:1];
[cache setObject:@"foo" forKey:@"bar"];
[cache setObject:@"foo2" forKey:@"bar2"];
[cache setObject:@"foo3" forKey:@"bar3"];
XCTAssertEqualObjects(@[@"foo3"], cache.allObjects);
}
- (void)testRecentObjectReplacementAndBump
{
LRUCache *cache = [[LRUCache alloc] initWithCapacity:2];
[cache setObject:@"foo" forKey:@"bar"];
[cache setObject:@"foo2" forKey:@"bar2"];
[self assertArray:@[@"foo2", @"foo"] equalsTo:cache.allObjects];
XCTAssertNotNil([cache objectForKey:@"bar"]);
[self assertArray:@[@"foo", @"foo2"] equalsTo:cache.allObjects];
[cache setObject:@"foo3" forKey:@"bar3"];
[self assertArray:@[@"foo3", @"foo"] equalsTo:cache.allObjects];
[cache setObject:@"foo0" forKey:@"bar"];
[self assertArray:@[@"foo0", @"foo3"] equalsTo:cache.allObjects];
[cache setObject:@"foo4" forKey:@"bar4"];
[self assertArray:@[@"foo4", @"foo0"] equalsTo:cache.allObjects];
}
- (void)testBumpFromHead
{
LRUCache *cache = [[LRUCache alloc] initWithCapacity:3];
[cache setObject:@"foo" forKey:@"bar"];
[cache setObject:@"foo2" forKey:@"bar2"];
[cache setObject:@"foo3" forKey:@"bar3"];
XCTAssertNotNil([cache objectForKey:@"bar3"]);
[self assertArray:@[@"foo3", @"foo2", @"foo"] equalsTo:cache.allObjects];
[cache setObject:@"foo4" forKey:@"bar4"];
[cache setObject:@"foo5" forKey:@"bar5"];
[self assertArray:@[@"foo5", @"foo4", @"foo3"] equalsTo:cache.allObjects];
}
- (void)testBumpFromMiddle
{
LRUCache *cache = [[LRUCache alloc] initWithCapacity:3];
[cache setObject:@"foo" forKey:@"bar"];
[cache setObject:@"foo2" forKey:@"bar2"];
[cache setObject:@"foo3" forKey:@"bar3"];
XCTAssertNotNil([cache objectForKey:@"bar2"]);
[self assertArray:@[@"foo2", @"foo3", @"foo"] equalsTo:cache.allObjects];
[cache setObject:@"foo4" forKey:@"bar4"];
[cache setObject:@"foo5" forKey:@"bar5"];
[self assertArray:@[@"foo5", @"foo4", @"foo2"] equalsTo:cache.allObjects];
}
- (void)testBumpFromTail
{
LRUCache *cache = [[LRUCache alloc] initWithCapacity:3];
[cache setObject:@"foo" forKey:@"bar"];
[cache setObject:@"foo2" forKey:@"bar2"];
[cache setObject:@"foo3" forKey:@"bar3"];
XCTAssertNotNil([cache objectForKey:@"bar3"]);
[self assertArray:@[@"foo3", @"foo2", @"foo"] equalsTo:cache.allObjects];
[cache setObject:@"foo4" forKey:@"bar4"];
[cache setObject:@"foo5" forKey:@"bar5"];
[self assertArray:@[@"foo5", @"foo4", @"foo3"] equalsTo:cache.allObjects];
}
- (void)testInsertionLoop
{
LRUCache *cache = [[LRUCache alloc] initWithCapacity:1];
NSUInteger count = 100;
for (NSUInteger i = 0; i <= count; ++i) {
[cache setObject:@(i) forKey:@(i)];
XCTAssertNotNil([cache objectForKey:@(i)]);
}
XCTAssertEqualObjects(@[@(count)], cache.allObjects);
}
- (void)testRemoveExistingObjectForKey {
LRUCache *cache = [[LRUCache alloc] initWithCapacity:3];
[cache setObject:@"foo" forKey:@"bar"];
[cache setObject:@"foo2" forKey:@"bar2"];
[cache setObject:@"foo3" forKey:@"bar3"];
[self assertArray:@[@"foo3", @"foo2", @"foo"] equalsTo:cache.allObjects];
[cache removeObjectForKey:@"bar2"];
XCTAssertNil([cache objectForKey:@"bar2"]);
[self assertArray:@[@"foo3", @"foo"] equalsTo:cache.allObjects];
}
- (void)testRemoveNonExistingObjectForKey {
LRUCache *cache = [[LRUCache alloc] initWithCapacity:2];
[cache setObject:@"foo" forKey:@"bar"];
[cache removeObjectForKey:@"nonExisting"];
XCTAssertNotNil([cache objectForKey:@"bar"]);
[self assertArray:@[@"foo"] equalsTo:cache.allObjects];
}
- (void)testRemoveAndInsertFlow {
LRUCache *cache = [[LRUCache alloc] initWithCapacity:2];
[cache setObject:@"foo" forKey:@"bar"];
[cache setObject:@"foo2" forKey:@"bar2"];
[cache removeObjectForKey:@"bar"];
XCTAssertNil([cache objectForKey:@"bar"]);
[cache setObject:@"foo3" forKey:@"bar3"];
[self assertArray:@[@"foo3", @"foo2"] equalsTo:cache.allObjects];
}
@end

View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBMathUtils.h"
@interface FBMathUtilsTests : XCTestCase
@end
@implementation FBMathUtilsTests
- (void)testGetCenter
{
XCTAssertTrue(CGPointEqualToPoint(FBRectGetCenter(CGRectMake(0, 0, 4, 4)), CGPointMake(2, 2)));
XCTAssertTrue(CGPointEqualToPoint(FBRectGetCenter(CGRectMake(1, 1, 4, 4)), CGPointMake(3, 3)));
XCTAssertTrue(CGPointEqualToPoint(FBRectGetCenter(CGRectMake(1, 3, 6, 14)), CGPointMake(4, 10)));
}
- (void)testFuzzyEqualFloats
{
XCTAssertTrue(FBFloatFuzzyEqualToFloat(0, 0, 0));
XCTAssertTrue(FBFloatFuzzyEqualToFloat(0.5, 0.6, 0.2));
XCTAssertTrue(FBFloatFuzzyEqualToFloat(0.6, 0.5, 0.2));
XCTAssertTrue(FBFloatFuzzyEqualToFloat(0.5, 0.6, 0.10001));
}
- (void)testFuzzyNotEqualFloats
{
XCTAssertFalse(FBFloatFuzzyEqualToFloat(0, 1, 0));
XCTAssertFalse(FBFloatFuzzyEqualToFloat(1, 0, 0));
XCTAssertFalse(FBFloatFuzzyEqualToFloat(0.5, 0.6, 0.05));
XCTAssertFalse(FBFloatFuzzyEqualToFloat(0.6, 0.5, 0.05));
}
- (void)testFuzzyEqualPoints
{
CGPoint referencePoint = CGPointMake(3, 3);
XCTAssertTrue(FBPointFuzzyEqualToPoint(referencePoint, CGPointMake(3, 3), 2));
XCTAssertTrue(FBPointFuzzyEqualToPoint(referencePoint, CGPointMake(3, 4), 2));
XCTAssertTrue(FBPointFuzzyEqualToPoint(referencePoint, CGPointMake(4, 3), 2));
}
- (void)testFuzzyNotEqualPoints
{
CGPoint referencePoint = CGPointMake(3, 3);
XCTAssertFalse(FBPointFuzzyEqualToPoint(referencePoint, CGPointMake(5, 5), 1));
XCTAssertFalse(FBPointFuzzyEqualToPoint(referencePoint, CGPointMake(3, 5), 1));
XCTAssertFalse(FBPointFuzzyEqualToPoint(referencePoint, CGPointMake(5, 3), 1));
}
- (void)testFuzzyEqualSizes
{
CGSize referenceSize = CGSizeMake(3, 3);
XCTAssertTrue(FBSizeFuzzyEqualToSize(referenceSize, CGSizeMake(3, 3), 2));
XCTAssertTrue(FBSizeFuzzyEqualToSize(referenceSize, CGSizeMake(3, 4), 2));
XCTAssertTrue(FBSizeFuzzyEqualToSize(referenceSize, CGSizeMake(4, 3), 2));
}
- (void)testFuzzyNotEqualSizes
{
CGSize referenceSize = CGSizeMake(3, 3);
XCTAssertFalse(FBSizeFuzzyEqualToSize(referenceSize, CGSizeMake(5, 5), 1));
XCTAssertFalse(FBSizeFuzzyEqualToSize(referenceSize, CGSizeMake(3, 5), 1));
XCTAssertFalse(FBSizeFuzzyEqualToSize(referenceSize, CGSizeMake(5, 3), 1));
}
- (void)testFuzzyEqualRects
{
CGRect referenceRect = CGRectMake(3, 3, 3, 3);
XCTAssertTrue(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(3, 3, 3, 3), 2));
XCTAssertTrue(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(3, 4, 3, 3), 2));
XCTAssertTrue(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(4, 3, 3, 3), 2));
XCTAssertTrue(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(3, 3, 3, 4), 2));
XCTAssertTrue(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(3, 3, 4, 3), 2));
}
- (void)testFuzzyNotEqualRects
{
CGRect referenceRect = CGRectMake(3, 3, 3, 3);
XCTAssertFalse(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(5, 5, 5, 5), 1));
XCTAssertFalse(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(3, 5, 3, 3), 1));
XCTAssertFalse(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(5, 3, 3, 3), 1));
XCTAssertFalse(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(3, 3, 3, 5), 1));
XCTAssertFalse(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(3, 3, 5, 3), 1));
}
- (void)testFuzzyEqualRectsSymmetry
{
CGRect referenceRect = CGRectMake(0, 0, 2, 2);
XCTAssertFalse(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(1, 1, 3, 3), 1));
XCTAssertFalse(FBRectFuzzyEqualToRect(referenceRect, CGRectMake(-1, -1, 1, 1), 1));
}
- (void)testSizeInversion
{
const CGSize screenSizePortrait = CGSizeMake(10, 15);
const CGSize screenSizeLandscape = CGSizeMake(15, 10);
const CGFloat t = FBDefaultFrameFuzzyThreshold;
XCTAssertTrue(FBSizeFuzzyEqualToSize(screenSizePortrait, FBAdjustDimensionsForApplication(screenSizePortrait, UIInterfaceOrientationPortrait), t));
XCTAssertTrue(FBSizeFuzzyEqualToSize(screenSizePortrait, FBAdjustDimensionsForApplication(screenSizePortrait, UIInterfaceOrientationPortraitUpsideDown), t));
XCTAssertTrue(FBSizeFuzzyEqualToSize(screenSizeLandscape, FBAdjustDimensionsForApplication(screenSizePortrait, UIInterfaceOrientationLandscapeLeft), t));
XCTAssertTrue(FBSizeFuzzyEqualToSize(screenSizeLandscape, FBAdjustDimensionsForApplication(screenSizePortrait, UIInterfaceOrientationLandscapeRight), t));
XCTAssertTrue(FBSizeFuzzyEqualToSize(screenSizeLandscape, FBAdjustDimensionsForApplication(screenSizeLandscape, UIInterfaceOrientationLandscapeLeft), t));
XCTAssertTrue(FBSizeFuzzyEqualToSize(screenSizeLandscape, FBAdjustDimensionsForApplication(screenSizeLandscape, UIInterfaceOrientationLandscapeRight), t));
}
@end

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBProtocolHelpers.h"
@interface FBProtocolHelpersTests : XCTestCase
@end
@implementation FBProtocolHelpersTests
- (void)testValidPrefixedCapsParsing
{
NSError *error = nil;
NSDictionary<NSString *, id> *parsedCaps = FBParseCapabilities(@{
@"firstMatch": @[@{
@"appium:bundleId": @"com.example.id"
}]
}, &error);
XCTAssertNil(error);
XCTAssertEqualObjects(parsedCaps[@"bundleId"], @"com.example.id");
}
- (void)testValidPrefixedCapsMerging
{
NSError *error = nil;
NSDictionary<NSString *, id> *parsedCaps = FBParseCapabilities(@{
@"firstMatch": @[@{
@"bundleId": @"com.example.id"
}],
@"alwaysMatch": @{
@"google:cap": @"super"
}
}, &error);
XCTAssertNil(error);
XCTAssertEqualObjects(parsedCaps[@"bundleId"], @"com.example.id");
XCTAssertEqualObjects(parsedCaps[@"google:cap"], @"super");
}
- (void)testEmptyCaps
{
NSError *error = nil;
NSDictionary<NSString *, id> *parsedCaps = FBParseCapabilities(@{}, &error);
XCTAssertNil(error);
XCTAssertEqual(parsedCaps.count, 0);
}
- (void)testCapsMergingFailure
{
NSError *error = nil;
NSDictionary<NSString *, id> *parsedCaps = FBParseCapabilities(@{
@"firstMatch": @[@{
@"appium:bundleId": @"com.example.id"
}],
@"alwaysMatch": @{
@"bundleId": @"other"
}
}, &error);
XCTAssertNil(parsedCaps);
XCTAssertNotNil(error);
}
- (void)testPrefixingStandardCapability
{
NSError *error = nil;
NSDictionary<NSString *, id> *parsedCaps = FBParseCapabilities(@{
@"firstMatch": @[@{
@"appium:platformName": @"com.example.id"
}]
}, &error);
XCTAssertNil(parsedCaps);
XCTAssertNotNil(error);
}
@end

View File

@@ -0,0 +1,129 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBRoute.h"
@class RouteResponse;
@interface FBHandlerMock : NSObject
@property (nonatomic, assign) BOOL didCallSomeSelector;
@end
@implementation FBHandlerMock
- (id)someSelector:(id)arg
{
self.didCallSomeSelector = YES;
return nil;
};
@end
@interface FBRouteTests : XCTestCase
@end
@implementation FBRouteTests
- (void)testGetRoute
{
FBRoute *route = [FBRoute GET:@"/"];
XCTAssertEqualObjects(route.verb, @"GET");
}
- (void)testPostRoute
{
FBRoute *route = [FBRoute POST:@"/"];
XCTAssertEqualObjects(route.verb, @"POST");
}
- (void)testPutRoute
{
FBRoute *route = [FBRoute PUT:@"/"];
XCTAssertEqualObjects(route.verb, @"PUT");
}
- (void)testDeleteRoute
{
FBRoute *route = [FBRoute DELETE:@"/"];
XCTAssertEqualObjects(route.verb, @"DELETE");
}
- (void)testTargetAction
{
FBHandlerMock *mock = [FBHandlerMock new];
FBRoute *route = [[FBRoute new] respondWithTarget:mock action:@selector(someSelector:)];
[route mountRequest:(id)NSObject.new intoResponse:(id)NSObject.new];
XCTAssertTrue(mock.didCallSomeSelector);
}
- (void)testRespond
{
XCTestExpectation *expectation = [self expectationWithDescription:@"Calling respond block works!"];
FBRoute *route = [[FBRoute new] respondWithBlock:^id<FBResponsePayload>(FBRouteRequest *request) {
[expectation fulfill];
return nil;
}];
[route mountRequest:(id)NSObject.new intoResponse:(id)NSObject.new];
[self waitForExpectationsWithTimeout:0.0 handler:nil];
}
- (void)testRouteWithSessionWithSlash
{
FBRoute *route = [[FBRoute POST:@"/deactivateApp"] respondWithTarget:self action:@selector(dummyHandler:)];
XCTAssertEqualObjects(route.path, @"/session/:sessionID/deactivateApp");
}
- (void)testRouteWithSession
{
FBRoute *route = [[FBRoute POST:@"deactivateApp"] respondWithTarget:self action:@selector(dummyHandler:)];
XCTAssertEqualObjects(route.path, @"/session/:sessionID/deactivateApp");
}
- (void)testRouteWithoutSessionWithSlash
{
FBRoute *route = [[FBRoute POST:@"/deactivateApp"].withoutSession respondWithTarget:self action:@selector(dummyHandler:)];
XCTAssertEqualObjects(route.path, @"/deactivateApp");
}
- (void)testRouteWithoutSession
{
FBRoute *route = [[FBRoute POST:@"deactivateApp"].withoutSession respondWithTarget:self action:@selector(dummyHandler:)];
XCTAssertEqualObjects(route.path, @"/deactivateApp");
}
- (void)testEmptyRouteWithSession
{
FBRoute *route = [[FBRoute POST:@""] respondWithTarget:self action:@selector(dummyHandler:)];
XCTAssertEqualObjects(route.path, @"/session/:sessionID");
}
- (void)testEmptyRouteWithoutSession
{
FBRoute *route = [[FBRoute POST:@""].withoutSession respondWithTarget:self action:@selector(dummyHandler:)];
XCTAssertEqualObjects(route.path, @"/");
}
- (void)testEmptyRouteWithSessionWithSlash
{
FBRoute *route = [[FBRoute POST:@"/"] respondWithTarget:self action:@selector(dummyHandler:)];
XCTAssertEqualObjects(route.path, @"/session/:sessionID");
}
- (void)testEmptyRouteWithoutSessionWithSlash
{
FBRoute *route = [[FBRoute POST:@"/"].withoutSession respondWithTarget:self action:@selector(dummyHandler:)];
XCTAssertEqualObjects(route.path, @"/");
}
+ (id<FBResponsePayload>)dummyHandler:(FBRouteRequest *)request
{
return nil;
}
@end

View File

@@ -0,0 +1,98 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBRunLoopSpinner.h"
@interface FBRunLoopSpinnerTests : XCTestCase
@property (nonatomic, strong) FBRunLoopSpinner *spinner;
@end
/**
Non of the test methods should block testing thread.
If they do, that means they are broken
*/
@implementation FBRunLoopSpinnerTests
- (void)setUp
{
[super setUp];
self.spinner = [[FBRunLoopSpinner new] timeout:0.1];
}
- (void)testSpinUntilCompletion
{
__block BOOL _didExecuteBlock = NO;
[FBRunLoopSpinner spinUntilCompletion:^(void (^completion)(void)) {
_didExecuteBlock = YES;
completion();
}];
XCTAssertTrue(_didExecuteBlock);
}
- (void)testSpinUntilTrue
{
__block BOOL _didExecuteBlock = NO;
BOOL didSucceed =
[self.spinner spinUntilTrue:^BOOL{
_didExecuteBlock = YES;
return YES;
}];
XCTAssertTrue(didSucceed);
XCTAssertTrue(_didExecuteBlock);
}
- (void)testSpinUntilTrueTimeout
{
NSError *error;
BOOL didSucceed =
[self.spinner spinUntilTrue:^BOOL{
return NO;
} error:&error];
XCTAssertFalse(didSucceed);
XCTAssertNotNil(error);
}
- (void)testSpinUntilTrueTimeoutMessage
{
NSString *expectedMessage = @"Magic message";
NSError *error;
BOOL didSucceed =
[[self.spinner timeoutErrorMessage:expectedMessage]
spinUntilTrue:^BOOL{
return NO;
} error:&error];
XCTAssertFalse(didSucceed);
XCTAssertEqual(error.localizedDescription, expectedMessage);
}
- (void)testSpinUntilNotNil
{
__block id expectedObject = NSObject.new;
NSError *error;
id returnedObject =
[self.spinner spinUntilNotNil:^id{
return expectedObject;
} error:&error];
XCTAssertNil(error);
XCTAssertEqual(returnedObject, expectedObject);
}
- (void)testSpinUntilNotNilTimeout
{
NSError *error;
id element =
[self.spinner spinUntilNotNil:^id{
return nil;
} error:&error];
XCTAssertNil(element);
XCTAssertNotNil(error);
}
@end

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBRuntimeUtils.h"
#import "XCTestPrivateSymbols.h"
@protocol FBMagicProtocol <NSObject>
@end
const NSString *FBRuntimeUtilsTestsConstString = @"FBRuntimeUtilsTestsConstString";
@interface FBRuntimeUtilsTests : XCTestCase <FBMagicProtocol>
@end
@implementation FBRuntimeUtilsTests
- (void)testClassesThatConformsToProtocol
{
XCTAssertEqualObjects(@[self.class], FBClassesThatConformsToProtocol(@protocol(FBMagicProtocol)));
}
- (void)testRetrievingFrameworkSymbols
{
NSString *binaryPath = [NSBundle bundleForClass:self.class].executablePath;
NSString *symbolPointer = *(NSString*__autoreleasing*)FBRetrieveSymbolFromBinary(binaryPath.UTF8String, "FBRuntimeUtilsTestsConstString");
XCTAssertNotNil(symbolPointer);
XCTAssertEqualObjects(symbolPointer, FBRuntimeUtilsTestsConstString);
}
- (void)testXCTestSymbols
{
XCTAssertTrue(XCDebugLogger != NULL);
XCTAssertTrue(XCSetDebugLogger != NULL);
XCTAssertNotNil(FB_XCAXAIsVisibleAttribute);
XCTAssertNotNil(FB_XCAXAIsElementAttribute);
}
@end

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBRuntimeUtils.h"
@interface FBSDKVersionTests : XCTestCase
@property (nonatomic, readonly) NSString *currentSDKVersion;
@property (nonatomic, readonly) NSString *lowerSDKVersion;
@property (nonatomic, readonly) NSString *higherSDKVersion;
@end
@implementation FBSDKVersionTests
- (void)setUp
{
[super setUp];
NSDictionary *bundleDict = [[NSBundle mainBundle] infoDictionary];
[bundleDict setValue:@"11.0" forKey:@"DTSDKName"];
_currentSDKVersion = FBSDKVersion();
_lowerSDKVersion = [NSString stringWithFormat:@"%@", @((int)[self.currentSDKVersion doubleValue] - 1)];
_higherSDKVersion = [NSString stringWithFormat:@"%@", @((int)[self.currentSDKVersion doubleValue] + 1)];
}
- (void)testIsSDKVersionLessThanOrEqualTo
{
XCTAssertTrue(isSDKVersionLessThanOrEqualTo(self.higherSDKVersion));
XCTAssertFalse(isSDKVersionLessThanOrEqualTo(self.lowerSDKVersion));
XCTAssertTrue(isSDKVersionLessThanOrEqualTo(self.currentSDKVersion));
}
- (void)testIsSDKVersionLessThan
{
XCTAssertTrue(isSDKVersionLessThan(self.higherSDKVersion));
XCTAssertFalse(isSDKVersionLessThan(self.lowerSDKVersion));
XCTAssertFalse(isSDKVersionLessThan(self.currentSDKVersion));
}
- (void)testIsSDKVersionEqualTo
{
XCTAssertFalse(isSDKVersionEqualTo(self.higherSDKVersion));
XCTAssertFalse(isSDKVersionEqualTo(self.lowerSDKVersion));
XCTAssertTrue(isSDKVersionEqualTo(self.currentSDKVersion));
}
- (void)testIsSDKVersionGreaterThanOrEqualTo
{
XCTAssertFalse(isSDKVersionGreaterThanOrEqualTo(self.higherSDKVersion));
XCTAssertTrue(isSDKVersionGreaterThanOrEqualTo(self.lowerSDKVersion));
XCTAssertTrue(isSDKVersionGreaterThanOrEqualTo(self.currentSDKVersion));
}
- (void)testIsSDKVersionGreaterThan
{
XCTAssertFalse(isSDKVersionGreaterThan(self.higherSDKVersion));
XCTAssertTrue(isSDKVersionGreaterThan(self.lowerSDKVersion));
XCTAssertFalse(isSDKVersionGreaterThan(self.currentSDKVersion));
}
@end

View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBSession.h"
#import "FBConfiguration.h"
#import "XCUIApplicationDouble.h"
@interface FBSessionTests : XCTestCase
@property (nonatomic, strong) FBSession *session;
@property (nonatomic, strong) XCUIApplication *testedApplication;
@property (nonatomic) BOOL shouldTerminateAppValue;
@end
@implementation FBSessionTests
- (void)setUp
{
[super setUp];
self.testedApplication = (id)XCUIApplicationDouble.new;
self.shouldTerminateAppValue = FBConfiguration.shouldTerminateApp;
[FBConfiguration setShouldTerminateApp:NO];
self.session = [FBSession initWithApplication:self.testedApplication];
}
- (void)tearDown
{
[self.session kill];
[FBConfiguration setShouldTerminateApp:self.shouldTerminateAppValue];
[super tearDown];
}
- (void)testSessionFetching
{
FBSession *fetchedSession = [FBSession sessionWithIdentifier:self.session.identifier];
XCTAssertEqual(self.session, fetchedSession);
}
- (void)testSessionFetchingBadIdentifier
{
XCTAssertNil([FBSession sessionWithIdentifier:@"FAKE_IDENTIFIER"]);
}
- (void)testSessionCreation
{
XCTAssertNotNil(self.session.identifier);
XCTAssertNotNil(self.session.elementCache);
}
- (void)testActiveSession
{
XCTAssertEqual(self.session, [FBSession activeSession]);
}
- (void)testActiveSessionIsNilAfterKilling
{
[self.session kill];
XCTAssertNil([FBSession activeSession]);
}
@end

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "NSString+FBXMLSafeString.h"
@interface FBXMLSafeStringTests : XCTestCase
@end
@implementation FBXMLSafeStringTests
- (void)testSafeXmlStringTransformationWithEmptyReplacement {
NSString *withInvalidChar = [NSString stringWithFormat:@"bla%@", @"\uFFFF"];
NSString *withoutInvalidChar = @"bla";
XCTAssertNotEqualObjects(withInvalidChar, withoutInvalidChar);
XCTAssertEqualObjects([withInvalidChar fb_xmlSafeStringWithReplacement:@""], withoutInvalidChar);
}
- (void)testSafeXmlStringTransformationWithNonEmptyReplacement {
NSString *withInvalidChar = [NSString stringWithFormat:@"bla%@", @"\uFFFF"];
XCTAssertEqualObjects([withInvalidChar fb_xmlSafeStringWithReplacement:@"1"], @"bla1");
}
- (void)testSafeXmlStringTransformationWithSmileys {
NSString *validString = @"Yo👿";
XCTAssertEqualObjects([validString fb_xmlSafeStringWithReplacement:@""], validString);
}
@end

View File

@@ -0,0 +1,152 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBMacros.h"
#import "FBXPath.h"
#import "FBXPath-Private.h"
#import "XCUIElementDouble.h"
#import "XCElementSnapshotDouble.h"
#import "FBXCElementSnapshotWrapper+Helpers.h"
@interface FBXPathTests : XCTestCase
@end
@implementation FBXPathTests
- (NSString *)xmlStringWithElement:(id<FBXCElementSnapshot>)snapshot
xpathQuery:(nullable NSString *)query
excludingAttributes:(nullable NSArray<NSString *> *)excludedAttributes
{
xmlDocPtr doc;
xmlTextWriterPtr writer = xmlNewTextWriterDoc(&doc, 0);
NSMutableDictionary *elementStore = [NSMutableDictionary dictionary];
int buffersize;
xmlChar *xmlbuff = NULL;
int rc = xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL);
if (rc >= 0) {
rc = [FBXPath xmlRepresentationWithRootElement:snapshot
writer:writer
elementStore:elementStore
query:query
excludingAttributes:excludedAttributes];
if (rc >= 0) {
rc = xmlTextWriterEndDocument(writer);
}
}
if (rc >= 0) {
xmlDocDumpFormatMemory(doc, &xmlbuff, &buffersize, 1);
}
xmlFreeTextWriter(writer);
xmlFreeDoc(doc);
XCTAssertTrue(rc >= 0);
XCTAssertEqual(1, [elementStore count]);
NSString *result = [NSString stringWithCString:(const char *)xmlbuff encoding:NSUTF8StringEncoding];
xmlFree(xmlbuff);
return result;
}
- (void)testDefaultXPathPresentation
{
XCElementSnapshotDouble *snapshot = [XCElementSnapshotDouble new];
id<FBElement> element = (id<FBElement>)[FBXCElementSnapshotWrapper ensureWrapped:(id)snapshot];
NSString *resultXml = [self xmlStringWithElement:(id<FBXCElementSnapshot>)element
xpathQuery:nil
excludingAttributes:nil];
NSLog(@"[DefaultXPath] Result XML:\n%@", resultXml);
NSString *expectedXml = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<%@ type=\"%@\" value=\"%@\" name=\"%@\" label=\"%@\" enabled=\"%@\" visible=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" index=\"%lu\" traits=\"%@\" private_indexPath=\"top\"/>\n",
element.wdType, element.wdType, element.wdValue, element.wdName, element.wdLabel, FBBoolToString(element.wdEnabled), FBBoolToString(element.wdVisible), FBBoolToString(element.wdAccessible), element.wdRect[@"x"], element.wdRect[@"y"], element.wdRect[@"width"], element.wdRect[@"height"], element.wdIndex, element.wdTraits];
XCTAssertTrue([resultXml isEqualToString: expectedXml]);
}
- (void)testtXPathPresentationWithSomeAttributesExcluded
{
XCElementSnapshotDouble *snapshot = [XCElementSnapshotDouble new];
id<FBElement> element = (id<FBElement>)[FBXCElementSnapshotWrapper ensureWrapped:(id)snapshot];
NSString *resultXml = [self xmlStringWithElement:(id<FBXCElementSnapshot>)element
xpathQuery:nil
excludingAttributes:@[@"type", @"visible", @"value", @"index", @"traits", @"nativeFrame"]];
NSString *expectedXml = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<%@ name=\"%@\" label=\"%@\" enabled=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" private_indexPath=\"top\"/>\n",
element.wdType, element.wdName, element.wdLabel, FBBoolToString(element.wdEnabled), FBBoolToString(element.wdAccessible), element.wdRect[@"x"], element.wdRect[@"y"], element.wdRect[@"width"], element.wdRect[@"height"]];
XCTAssertEqualObjects(resultXml, expectedXml);
}
- (void)testXPathPresentationBasedOnQueryMatchingAllAttributes
{
XCElementSnapshotDouble *snapshot = [XCElementSnapshotDouble new];
snapshot.value = @"йоло<>&\"";
snapshot.label = @"a\nb";
id<FBElement> element = (id<FBElement>)[FBXCElementSnapshotWrapper ensureWrapped:(id)snapshot];
NSString *resultXml = [self xmlStringWithElement:(id<FBXCElementSnapshot>)element
xpathQuery:[NSString stringWithFormat:@"//%@[@*]", element.wdType]
excludingAttributes:@[@"visible"]];
NSString *expectedXml = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<%@ type=\"%@\" value=\"%@\" name=\"%@\" label=\"%@\" enabled=\"%@\" visible=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" index=\"%lu\" hittable=\"%@\" traits=\"%@\" nativeFrame=\"%@\" private_indexPath=\"top\"/>\n",
element.wdType, element.wdType, @"йоло&lt;&gt;&amp;&quot;", element.wdName, @"a&#10;b", FBBoolToString(element.wdEnabled), FBBoolToString(element.wdVisible), FBBoolToString(element.wdAccessible), element.wdRect[@"x"], element.wdRect[@"y"], element.wdRect[@"width"], element.wdRect[@"height"], element.wdIndex, FBBoolToString(element.wdHittable), element.wdTraits, NSStringFromCGRect(element.wdNativeFrame)];
XCTAssertEqualObjects(expectedXml, resultXml);
}
- (void)testXPathPresentationBasedOnQueryMatchingSomeAttributes
{
XCElementSnapshotDouble *snapshot = [XCElementSnapshotDouble new];
id<FBElement> element = (id<FBElement>)[FBXCElementSnapshotWrapper ensureWrapped:(id)snapshot];
NSString *resultXml = [self xmlStringWithElement:(id<FBXCElementSnapshot>)element
xpathQuery:[NSString stringWithFormat:@"//%@[@%@ and contains(@%@, 'blabla')]", element.wdType, @"value", @"name"]
excludingAttributes:nil];
NSString *expectedXml = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<%@ value=\"%@\" name=\"%@\" private_indexPath=\"top\"/>\n",
element.wdType, element.wdValue, element.wdName];
XCTAssertTrue([resultXml isEqualToString: expectedXml]);
}
- (void)testSnapshotXPathResultsMatching
{
xmlDocPtr doc;
xmlTextWriterPtr writer = xmlNewTextWriterDoc(&doc, 0);
NSMutableDictionary *elementStore = [NSMutableDictionary dictionary];
XCElementSnapshotDouble *snapshot = [XCElementSnapshotDouble new];
id<FBElement> root = (id<FBElement>)[FBXCElementSnapshotWrapper ensureWrapped:(id)snapshot];
NSString *query = [NSString stringWithFormat:@"//%@", root.wdType];
int rc = xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL);
if (rc >= 0) {
rc = [FBXPath xmlRepresentationWithRootElement:(id<FBXCElementSnapshot>)root
writer:writer
elementStore:elementStore
query:query
excludingAttributes:nil];
if (rc >= 0) {
rc = xmlTextWriterEndDocument(writer);
}
}
if (rc < 0) {
xmlFreeTextWriter(writer);
xmlFreeDoc(doc);
XCTFail(@"Unable to create the source XML document");
}
xmlXPathObjectPtr queryResult = [FBXPath evaluate:query document:doc contextNode:NULL];
if (NULL == queryResult) {
xmlFreeTextWriter(writer);
xmlFreeDoc(doc);
XCTAssertNotEqual(NULL, queryResult);
}
NSArray *matchingSnapshots = [FBXPath collectMatchingSnapshots:queryResult->nodesetval
elementStore:elementStore];
xmlXPathFreeObject(queryResult);
xmlFreeTextWriter(writer);
xmlFreeDoc(doc);
XCTAssertNotNil(matchingSnapshots);
XCTAssertEqual(1, [matchingSnapshots count]);
}
@end

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "NSDictionary+FBUtf8SafeDictionary.h"
@interface NSDictionaryFBUtf8SafeTests : XCTestCase
@end
@implementation NSDictionaryFBUtf8SafeTests
- (void)testEmptySafeDictConversion
{
NSDictionary *d = @{};
XCTAssertEqualObjects(d, d.fb_utf8SafeDictionary);
}
- (void)testNonEmptySafeDictConversion
{
NSDictionary *d = @{
@"1": @[@3, @4],
@"5": @{@"6": @7, @"8": @9},
@"10": @"11"
};
XCTAssertEqualObjects(d, d.fb_utf8SafeDictionary);
}
@end

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "NSExpression+FBFormat.h"
#import "FBElementUtils.h"
@interface NSExpressionFBFormatTests : XCTestCase
@end
@implementation NSExpressionFBFormatTests
- (void)testFormattingForExistingProperty
{
NSExpression *expr = [NSExpression expressionWithFormat:@"wdName"];
NSExpression *prop = [NSExpression fb_wdExpressionWithExpression:expr];
XCTAssertEqualObjects([prop keyPath], @"wdName");
}
- (void)testFormattingForExistingPropertyShortcut
{
NSExpression *expr = [NSExpression expressionWithFormat:@"visible"];
NSExpression *prop = [NSExpression fb_wdExpressionWithExpression:expr];
XCTAssertEqualObjects([prop keyPath], @"isWDVisible");
}
- (void)testFormattingForValidExpressionWOKeys
{
NSExpression *expr = [NSExpression expressionWithFormat:@"1"];
NSExpression *prop = [NSExpression fb_wdExpressionWithExpression:expr];
XCTAssertEqualObjects([prop constantValue], [NSNumber numberWithInt:1]);
}
- (void)testFormattingForExistingComplexProperty
{
NSExpression *expr = [NSExpression expressionWithFormat:@"wdRect.x"];
NSExpression *prop = [NSExpression fb_wdExpressionWithExpression:expr];
XCTAssertEqualObjects([prop keyPath], @"wdRect.x");
}
- (void)testFormattingForExistingComplexPropertyWOPrefix
{
NSExpression *expr = [NSExpression expressionWithFormat:@"rect.x"];
NSExpression *prop = [NSExpression fb_wdExpressionWithExpression:expr];
XCTAssertEqualObjects([prop keyPath], @"wdRect.x");
}
- (void)testFormattingForPredicateWithUnknownKey
{
NSExpression *expr = [NSExpression expressionWithFormat:@"title"];
XCTAssertThrowsSpecificNamed([NSExpression fb_wdExpressionWithExpression:expr], NSException, FBUnknownAttributeException);
}
@end

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "NSPredicate+FBFormat.h"
@interface NSPredicateFBFormatTests : XCTestCase
@end
@implementation NSPredicateFBFormatTests
- (void)testFormattingForExistingProperty
{
NSPredicate *expr = [NSPredicate predicateWithFormat:@"wdName == 'blabla'"];
XCTAssertNotNil([NSPredicate fb_formatSearchPredicate:expr]);
}
- (void)testFormattingForExistingPropertyOnTheRightSide
{
NSPredicate *expr = [NSPredicate predicateWithFormat:@"0 == wdAccessible"];
XCTAssertNotNil([NSPredicate fb_formatSearchPredicate:expr]);
}
- (void)testFormattingForExistingPropertyShortcut
{
NSPredicate *expr = [NSPredicate predicateWithFormat:@"visible == 1"];
XCTAssertNotNil([NSPredicate fb_formatSearchPredicate:expr]);
}
- (void)testFormattingForComplexExpression
{
NSPredicate *expr = [NSPredicate predicateWithFormat:@"visible == 1 AND NOT (type == 'blabla' OR NOT (label IN {'3', '4'}))"];
XCTAssertNotNil([NSPredicate fb_formatSearchPredicate:expr]);
}
- (void)testFormattingForValidExpressionWOKeys
{
NSPredicate *expr = [NSPredicate predicateWithFormat:@"1 = 1"];
XCTAssertNotNil([NSPredicate fb_formatSearchPredicate:expr]);
}
- (void)testFormattingForExistingComplexProperty
{
NSPredicate *expr = [NSPredicate predicateWithFormat:@"wdRect.x == '0'"];
XCTAssertNotNil([NSPredicate fb_formatSearchPredicate:expr]);
}
- (void)testFormattingForExistingComplexPropertyWOPrefix
{
NSPredicate *expr = [NSPredicate predicateWithFormat:@"rect.x == '0'"];
XCTAssertNotNil([NSPredicate fb_formatSearchPredicate:expr]);
}
- (void)testFormattingForPredicateWithUnknownKey
{
NSPredicate *expr = [NSPredicate predicateWithFormat:@"title == 'blabla'"];
XCTAssertThrows([NSPredicate fb_formatSearchPredicate:expr]);
}
- (void)testBlockPredicateCreation
{
NSPredicate *expr = [NSPredicate predicateWithFormat:@"rect.x == '0'"];
XCTAssertNotNil([NSPredicate fb_snapshotBlockPredicateWithPredicate:expr]);
}
@end

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "FBElementUtils.h"
@interface XCUIElementHelpersTests : XCTestCase
@property (nonatomic) NSDictionary *namesMapping;
@end
@implementation XCUIElementHelpersTests
- (void)setUp
{
[super setUp];
self.namesMapping = [FBElementUtils wdAttributeNamesMapping];
}
- (void)testMappingContainsNamesAndAliases
{
XCTAssertTrue([self.namesMapping.allKeys containsObject:@"wdName"]);
XCTAssertTrue([self.namesMapping.allKeys containsObject:@"name"]);
}
- (void)testMappingContainsCorrectValueForAttrbutesWithoutGetters
{
XCTAssertTrue([[self.namesMapping objectForKey:@"label"] isEqualToString:@"wdLabel"]);
XCTAssertTrue([[self.namesMapping objectForKey:@"wdLabel"] isEqualToString:@"wdLabel"]);
}
- (void)testMappingContainsCorrectValueForAttrbutesWithGetters
{
XCTAssertTrue([[self.namesMapping objectForKey:@"visible"] isEqualToString:@"isWDVisible"]);
XCTAssertTrue([[self.namesMapping objectForKey:@"wdVisible"] isEqualToString:@"isWDVisible"]);
}
- (void)testEachPropertyHasAlias
{
NSArray *aliases = [self.namesMapping.allKeys filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"NOT(SELF beginsWith[c] 'wd')"]];
NSArray *names = [self.namesMapping.allKeys filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF beginsWith[c] 'wd'"]];
XCTAssertEqual(aliases.count, names.count);
}
@end

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2018-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import <WebDriverAgentLib/FBElement.h>
#import <XCTest/XCUIElementTypes.h>
@class XCUIApplication;
@interface XCUIElementDouble : NSObject<FBElement>
@property (nonatomic, strong, nonnull) XCUIApplication *application;
@property (nonatomic, readwrite, assign) CGRect frame;
@property (nonatomic, readwrite, nullable) id lastSnapshot;
@property (nonatomic, assign) BOOL fb_isObstructedByAlert;
@property (nonatomic, readwrite, copy, nonnull) NSDictionary *wdRect;
@property (nonatomic, readwrite, assign) CGRect wdFrame;
@property (nonatomic, readwrite, copy, nonnull) NSString *wdUID;
@property (nonatomic, copy, readwrite, nullable) NSString *wdName;
@property (nonatomic, copy, readwrite, nullable) NSString *wdLabel;
@property (nonatomic, copy, readwrite, nonnull) NSString *wdType;
@property (nonatomic, strong, readwrite, nullable) NSString *wdValue;
@property (nonatomic, readwrite, getter=isWDEnabled) BOOL wdEnabled;
@property (nonatomic, readwrite, getter=isWDSelected) BOOL wdSelected;
@property (nonatomic, readwrite) NSUInteger wdIndex;
@property (nonatomic, readwrite, getter=isWDVisible) BOOL wdVisible;
@property (nonatomic, readwrite, getter=isWDAccessible) BOOL wdAccessible;
@property (nonatomic, readwrite, getter=isWDFocused) BOOL wdFocused;
@property (nonatomic, readwrite, getter = isWDHittable) BOOL wdHittable;
@property (copy, nonnull) NSArray *children;
@property (nonatomic, readwrite, assign) XCUIElementType elementType;
@property (nonatomic, readwrite, getter=isWDAccessibilityContainer) BOOL wdAccessibilityContainer;
- (void)resolve;
- (id _Nonnull)fb_standardSnapshot;
- (id _Nonnull)fb_customSnapshot;
// Checks
@property (nonatomic, assign, readonly) BOOL didResolve;
@end

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) 2018-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "XCUIElementDouble.h"
@interface XCUIElementDouble ()
@property (nonatomic, assign, readwrite) BOOL didResolve;
@end
@implementation XCUIElementDouble
- (id)init
{
self = [super init];
if (self) {
self.wdFrame = CGRectMake(0, 0, 0, 0);
self.wdName = @"testName";
self.wdLabel = @"testLabel";
self.wdValue = @"magicValue";
self.wdVisible = YES;
self.wdAccessible = YES;
self.wdEnabled = YES;
self.wdSelected = YES;
self.wdHittable = YES;
self.wdIndex = 0;
#if TARGET_OS_TV
self.wdFocused = YES;
#endif
self.children = @[];
self.wdRect = @{@"x": @0,
@"y": @0,
@"width": @0,
@"height": @0,
};
self.wdAccessibilityContainer = NO;
self.elementType = XCUIElementTypeOther;
self.wdType = @"XCUIElementTypeOther";
self.wdUID = @"0";
self.lastSnapshot = nil;
}
return self;
}
- (id)fb_valueForWDAttributeName:(NSString *)name
{
return @"test";
}
- (id)fb_standardSnapshot
{
return [self lastSnapshot];
}
- (id)fb_customSnapshot
{
return [self lastSnapshot];
}
- (void)resolve
{
self.didResolve = YES;
}
- (id)lastSnapshot
{
return self;
}
- (id)fb_uid
{
return self.wdUID;
}
@end

View File

@@ -0,0 +1,86 @@
/**
* Copyright (c) 2018-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import "XCUIElementDouble.h"
#import "FBTVNavigationTracker.h"
#import "FBTVNavigationTracker-Private.h"
@interface FBTVNavigationTrackerTests : XCTestCase
@end
@implementation FBTVNavigationTrackerTests
- (void)testHorizontalDirectionWithItemShouldBeRight
{
XCUIElementDouble *el1 = XCUIElementDouble.new;
FBTVNavigationItem *item = [FBTVNavigationItem itemWithUid:@"123456789"];
FBTVNavigationTracker *tracker = [FBTVNavigationTracker trackerWithTargetElement:(XCUIElement *)el1];
FBTVDirection direction = [tracker horizontalDirectionWithItem:item andDelta:0.1];
XCTAssertEqual(FBTVDirectionRight, direction);
}
- (void)testHorizontalDirectionWithItemShouldBeLeft
{
XCUIElementDouble *el1 = XCUIElementDouble.new;
FBTVNavigationItem *item = [FBTVNavigationItem itemWithUid:@"123456789"];
FBTVNavigationTracker *tracker = [FBTVNavigationTracker trackerWithTargetElement:(XCUIElement *)el1];
FBTVDirection direction = [tracker horizontalDirectionWithItem:item andDelta:-0.1];
XCTAssertEqual(FBTVDirectionLeft, direction);
}
- (void)testHorizontalDirectionWithItemShouldBeNone
{
XCUIElementDouble *el1 = XCUIElementDouble.new;
FBTVNavigationItem *item = [FBTVNavigationItem itemWithUid:@"123456789"];
FBTVNavigationTracker *tracker = [FBTVNavigationTracker trackerWithTargetElement:(XCUIElement *)el1];
FBTVDirection direction = [tracker horizontalDirectionWithItem:item andDelta:DBL_EPSILON];
XCTAssertEqual(FBTVDirectionNone, direction);
}
- (void)testVerticalDirectionWithItemShouldBeDown
{
XCUIElementDouble *el1 = XCUIElementDouble.new;
FBTVNavigationItem *item = [FBTVNavigationItem itemWithUid:@"123456789"];
FBTVNavigationTracker *tracker = [FBTVNavigationTracker trackerWithTargetElement:(XCUIElement *)el1];
FBTVDirection direction = [tracker verticalDirectionWithItem:item andDelta:0.1];
XCTAssertEqual(FBTVDirectionDown, direction);
}
- (void)testVerticalDirectionWithItemShouldBeUp
{
XCUIElementDouble *el1 = XCUIElementDouble.new;
FBTVNavigationItem *item = [FBTVNavigationItem itemWithUid:@"123456789"];
FBTVNavigationTracker *tracker = [FBTVNavigationTracker trackerWithTargetElement:(XCUIElement *)el1];
FBTVDirection direction = [tracker verticalDirectionWithItem:item andDelta:-0.1];
XCTAssertEqual(FBTVDirectionUp, direction);
}
- (void)testVerticalDirectionWithItemShouldBeNone
{
XCUIElementDouble *el1 = XCUIElementDouble.new;
FBTVNavigationItem *item = [FBTVNavigationItem itemWithUid:@"123456789"];
FBTVNavigationTracker *tracker = [FBTVNavigationTracker trackerWithTargetElement:(XCUIElement *)el1];
FBTVDirection direction = [tracker verticalDirectionWithItem:item andDelta:DBL_EPSILON];
XCTAssertEqual(FBTVDirectionNone, direction);
}
@end

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.facebook.wda.unitTests</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>