587 lines
26 KiB
Objective-C
587 lines
26 KiB
Objective-C
/**
|
|
* 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 "FBSessionCommands.h"
|
|
|
|
#import "FBCapabilities.h"
|
|
#import "FBClassChainQueryParser.h"
|
|
#import "FBConfiguration.h"
|
|
#import "FBExceptions.h"
|
|
#import "FBLogger.h"
|
|
#import "FBProtocolHelpers.h"
|
|
#import "FBRouteRequest.h"
|
|
#import "FBSession.h"
|
|
#import "FBSettings.h"
|
|
#import "FBRuntimeUtils.h"
|
|
#import "FBActiveAppDetectionPoint.h"
|
|
#import "FBXCodeCompatibility.h"
|
|
#import "XCUIApplication+FBHelpers.h"
|
|
#import "XCUIApplication+FBQuiescence.h"
|
|
#import "XCUIDevice.h"
|
|
#import "XCUIDevice+FBHealthCheck.h"
|
|
#import "XCUIDevice+FBHelpers.h"
|
|
#import "XCUIApplicationProcessDelay.h"
|
|
|
|
|
|
@implementation FBSessionCommands
|
|
|
|
#pragma mark - <FBCommandHandler>
|
|
|
|
+ (NSArray *)routes
|
|
{
|
|
return
|
|
@[
|
|
[[FBRoute POST:@"/url"] respondWithTarget:self action:@selector(handleOpenURL:)],
|
|
[[FBRoute POST:@"/session"].withoutSession respondWithTarget:self action:@selector(handleCreateSession:)],
|
|
[[FBRoute POST:@"/wda/apps/launch"] respondWithTarget:self action:@selector(handleSessionAppLaunch:)],
|
|
[[FBRoute POST:@"/wda/apps/activate"] respondWithTarget:self action:@selector(handleSessionAppActivate:)],
|
|
[[FBRoute POST:@"/wda/apps/terminate"] respondWithTarget:self action:@selector(handleSessionAppTerminate:)],
|
|
[[FBRoute POST:@"/wda/apps/state"] respondWithTarget:self action:@selector(handleSessionAppState:)],
|
|
[[FBRoute GET:@"/wda/apps/list"] respondWithTarget:self action:@selector(handleGetActiveAppsList:)],
|
|
[[FBRoute GET:@""] respondWithTarget:self action:@selector(handleGetActiveSession:)],
|
|
[[FBRoute DELETE:@""] respondWithTarget:self action:@selector(handleDeleteSession:)],
|
|
[[FBRoute GET:@"/status"].withoutSession respondWithTarget:self action:@selector(handleGetStatus:)],
|
|
|
|
// Health check might modify simulator state so it should only be called in-between testing sessions
|
|
[[FBRoute GET:@"/wda/healthcheck"].withoutSession respondWithTarget:self action:@selector(handleGetHealthCheck:)],
|
|
|
|
// Settings endpoints
|
|
[[FBRoute GET:@"/appium/settings"] respondWithTarget:self action:@selector(handleGetSettings:)],
|
|
[[FBRoute POST:@"/appium/settings"] respondWithTarget:self action:@selector(handleSetSettings:)],
|
|
];
|
|
}
|
|
|
|
|
|
#pragma mark - Commands
|
|
|
|
+ (id<FBResponsePayload>)handleOpenURL:(FBRouteRequest *)request
|
|
{
|
|
NSString *urlString = request.arguments[@"url"];
|
|
if (!urlString) {
|
|
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:@"URL is required" traceback:nil]);
|
|
}
|
|
NSString* bundleId = request.arguments[@"bundleId"];
|
|
NSNumber* idleTimeoutMs = request.arguments[@"idleTimeoutMs"];
|
|
NSError *error;
|
|
if (nil == bundleId) {
|
|
if (![XCUIDevice.sharedDevice fb_openUrl:urlString error:&error]) {
|
|
return FBResponseWithUnknownError(error);
|
|
}
|
|
} else {
|
|
if (![XCUIDevice.sharedDevice fb_openUrl:urlString withApplication:bundleId error:&error]) {
|
|
return FBResponseWithUnknownError(error);
|
|
}
|
|
if (idleTimeoutMs.doubleValue > 0) {
|
|
XCUIApplication *app = [[XCUIApplication alloc] initWithBundleIdentifier:bundleId];
|
|
[app fb_waitUntilStableWithTimeout:FBMillisToSeconds(idleTimeoutMs.doubleValue)];
|
|
}
|
|
}
|
|
return FBResponseWithOK();
|
|
}
|
|
|
|
+ (id<FBResponsePayload>)handleCreateSession:(FBRouteRequest *)request
|
|
{
|
|
if (nil != FBSession.activeSession) {
|
|
[FBSession.activeSession kill];
|
|
}
|
|
|
|
NSDictionary<NSString *, id> *capabilities;
|
|
NSError *error;
|
|
if (![request.arguments[@"capabilities"] isKindOfClass:NSDictionary.class]) {
|
|
return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:@"'capabilities' is mandatory to create a new session"
|
|
traceback:nil]);
|
|
}
|
|
if (nil == (capabilities = FBParseCapabilities((NSDictionary *)request.arguments[@"capabilities"], &error))) {
|
|
return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:error.localizedDescription traceback:nil]);
|
|
}
|
|
|
|
[FBConfiguration resetSessionSettings];
|
|
[FBConfiguration setShouldUseTestManagerForVisibilityDetection:[capabilities[FB_CAP_USE_TEST_MANAGER_FOR_VISIBLITY_DETECTION] boolValue]];
|
|
if (capabilities[FB_SETTING_USE_COMPACT_RESPONSES]) {
|
|
[FBConfiguration setShouldUseCompactResponses:[capabilities[FB_SETTING_USE_COMPACT_RESPONSES] boolValue]];
|
|
}
|
|
NSString *elementResponseAttributes = capabilities[FB_SETTING_ELEMENT_RESPONSE_ATTRIBUTES];
|
|
if (elementResponseAttributes) {
|
|
[FBConfiguration setElementResponseAttributes:elementResponseAttributes];
|
|
}
|
|
if (capabilities[FB_CAP_MAX_TYPING_FREQUENCY]) {
|
|
[FBConfiguration setMaxTypingFrequency:[capabilities[FB_CAP_MAX_TYPING_FREQUENCY] unsignedIntegerValue]];
|
|
}
|
|
if (capabilities[FB_CAP_USE_SINGLETON_TEST_MANAGER]) {
|
|
[FBConfiguration setShouldUseSingletonTestManager:[capabilities[FB_CAP_USE_SINGLETON_TEST_MANAGER] boolValue]];
|
|
}
|
|
if (capabilities[FB_CAP_DISABLE_AUTOMATIC_SCREENSHOTS]) {
|
|
if ([capabilities[FB_CAP_DISABLE_AUTOMATIC_SCREENSHOTS] boolValue]) {
|
|
[FBConfiguration disableScreenshots];
|
|
} else {
|
|
[FBConfiguration enableScreenshots];
|
|
}
|
|
}
|
|
if (capabilities[FB_CAP_SHOULD_TERMINATE_APP]) {
|
|
[FBConfiguration setShouldTerminateApp:[capabilities[FB_CAP_SHOULD_TERMINATE_APP] boolValue]];
|
|
}
|
|
NSNumber *delay = capabilities[FB_CAP_EVENT_LOOP_IDLE_DELAY_SEC];
|
|
if ([delay doubleValue] > 0.0) {
|
|
[XCUIApplicationProcessDelay setEventLoopHasIdledDelay:[delay doubleValue]];
|
|
} else {
|
|
[XCUIApplicationProcessDelay disableEventLoopDelay];
|
|
}
|
|
|
|
if (nil != capabilities[FB_SETTING_WAIT_FOR_IDLE_TIMEOUT]) {
|
|
FBConfiguration.waitForIdleTimeout = [capabilities[FB_SETTING_WAIT_FOR_IDLE_TIMEOUT] doubleValue];
|
|
}
|
|
|
|
if (nil == capabilities[FB_CAP_FORCE_SIMULATOR_SOFTWARE_KEYBOARD_PRESENCE] ||
|
|
[capabilities[FB_CAP_FORCE_SIMULATOR_SOFTWARE_KEYBOARD_PRESENCE] boolValue]) {
|
|
[FBConfiguration forceSimulatorSoftwareKeyboardPresence];
|
|
}
|
|
|
|
NSString *bundleID = capabilities[FB_CAP_BUNDLE_ID];
|
|
NSString *initialUrl = capabilities[FB_CAP_INITIAL_URL];
|
|
XCUIApplication *app = nil;
|
|
if (bundleID != nil) {
|
|
app = [[XCUIApplication alloc] initWithBundleIdentifier:bundleID];
|
|
BOOL forceAppLaunch = YES;
|
|
if (nil != capabilities[FB_CAP_FORCE_APP_LAUNCH]) {
|
|
forceAppLaunch = [capabilities[FB_CAP_FORCE_APP_LAUNCH] boolValue];
|
|
}
|
|
XCUIApplicationState appState = app.state;
|
|
BOOL isAppRunning = appState >= XCUIApplicationStateRunningBackground;
|
|
if (!isAppRunning || (isAppRunning && forceAppLaunch)) {
|
|
app.fb_shouldWaitForQuiescence = nil == capabilities[FB_CAP_SHOULD_WAIT_FOR_QUIESCENCE]
|
|
|| [capabilities[FB_CAP_SHOULD_WAIT_FOR_QUIESCENCE] boolValue];
|
|
app.launchArguments = (NSArray<NSString *> *)capabilities[FB_CAP_ARGUMENTS] ?: @[];
|
|
app.launchEnvironment = (NSDictionary <NSString *, NSString *> *)capabilities[FB_CAP_ENVIRNOMENT] ?: @{};
|
|
if (nil != initialUrl) {
|
|
if (app.running) {
|
|
[app terminate];
|
|
}
|
|
id<FBResponsePayload> errorResponse = [self openDeepLink:initialUrl
|
|
withApplication:bundleID
|
|
timeout:capabilities[FB_CAP_APP_LAUNCH_STATE_TIMEOUT_SEC]];
|
|
if (nil != errorResponse) {
|
|
return errorResponse;
|
|
}
|
|
} else {
|
|
NSTimeInterval defaultTimeout = _XCTApplicationStateTimeout();
|
|
if (nil != capabilities[FB_CAP_APP_LAUNCH_STATE_TIMEOUT_SEC]) {
|
|
_XCTSetApplicationStateTimeout([capabilities[FB_CAP_APP_LAUNCH_STATE_TIMEOUT_SEC] doubleValue]);
|
|
}
|
|
@try {
|
|
[app launch];
|
|
} @catch (NSException *e) {
|
|
return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:e.reason traceback:nil]);
|
|
} @finally {
|
|
if (nil != capabilities[FB_CAP_APP_LAUNCH_STATE_TIMEOUT_SEC]) {
|
|
_XCTSetApplicationStateTimeout(defaultTimeout);
|
|
}
|
|
}
|
|
}
|
|
if (!app.running) {
|
|
NSString *errorMsg = [NSString stringWithFormat:@"Cannot launch %@ application. Make sure the correct bundle identifier has been provided in capabilities and check the device log for possible crash report occurrences", bundleID];
|
|
return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:errorMsg
|
|
traceback:nil]);
|
|
}
|
|
} else if (appState == XCUIApplicationStateRunningBackground && !forceAppLaunch) {
|
|
if (nil != initialUrl) {
|
|
id<FBResponsePayload> errorResponse = [self openDeepLink:initialUrl
|
|
withApplication:bundleID
|
|
timeout:nil];
|
|
if (nil != errorResponse) {
|
|
return errorResponse;
|
|
}
|
|
} else {
|
|
[app activate];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nil != initialUrl && nil == bundleID) {
|
|
id<FBResponsePayload> errorResponse = [self openDeepLink:initialUrl
|
|
withApplication:nil
|
|
timeout:capabilities[FB_CAP_APP_LAUNCH_STATE_TIMEOUT_SEC]];
|
|
if (nil != errorResponse) {
|
|
return errorResponse;
|
|
}
|
|
}
|
|
|
|
if (capabilities[FB_SETTING_DEFAULT_ALERT_ACTION]) {
|
|
[FBSession initWithApplication:app
|
|
defaultAlertAction:(id)capabilities[FB_SETTING_DEFAULT_ALERT_ACTION]];
|
|
} else {
|
|
[FBSession initWithApplication:app];
|
|
}
|
|
|
|
if (nil != capabilities[FB_CAP_USE_NATIVE_CACHING_STRATEGY]) {
|
|
FBSession.activeSession.useNativeCachingStrategy = [capabilities[FB_CAP_USE_NATIVE_CACHING_STRATEGY] boolValue];
|
|
}
|
|
|
|
return FBResponseWithObject(FBSessionCommands.sessionInformation);
|
|
}
|
|
|
|
+ (id<FBResponsePayload>)handleSessionAppLaunch:(FBRouteRequest *)request
|
|
{
|
|
[request.session launchApplicationWithBundleId:(id)request.arguments[@"bundleId"]
|
|
shouldWaitForQuiescence:request.arguments[@"shouldWaitForQuiescence"]
|
|
arguments:request.arguments[@"arguments"]
|
|
environment:request.arguments[@"environment"]];
|
|
return FBResponseWithOK();
|
|
}
|
|
|
|
+ (id<FBResponsePayload>)handleSessionAppActivate:(FBRouteRequest *)request
|
|
{
|
|
[request.session activateApplicationWithBundleId:(id)request.arguments[@"bundleId"]];
|
|
return FBResponseWithOK();
|
|
}
|
|
|
|
+ (id<FBResponsePayload>)handleSessionAppTerminate:(FBRouteRequest *)request
|
|
{
|
|
BOOL result = [request.session terminateApplicationWithBundleId:(id)request.arguments[@"bundleId"]];
|
|
return FBResponseWithObject(@(result));
|
|
}
|
|
|
|
+ (id<FBResponsePayload>)handleSessionAppState:(FBRouteRequest *)request
|
|
{
|
|
NSUInteger state = [request.session applicationStateWithBundleId:(id)request.arguments[@"bundleId"]];
|
|
return FBResponseWithObject(@(state));
|
|
}
|
|
|
|
+ (id<FBResponsePayload>)handleGetActiveAppsList:(FBRouteRequest *)request
|
|
{
|
|
return FBResponseWithObject([XCUIApplication fb_activeAppsInfo]);
|
|
}
|
|
|
|
+ (id<FBResponsePayload>)handleGetActiveSession:(FBRouteRequest *)request
|
|
{
|
|
return FBResponseWithObject(FBSessionCommands.sessionInformation);
|
|
}
|
|
|
|
+ (id<FBResponsePayload>)handleDeleteSession:(FBRouteRequest *)request
|
|
{
|
|
[request.session kill];
|
|
return FBResponseWithOK();
|
|
}
|
|
|
|
+ (id<FBResponsePayload>)handleGetStatus:(FBRouteRequest *)request
|
|
{
|
|
// For updatedWDABundleId capability by Appium
|
|
NSString *productBundleIdentifier = @"com.facebook.WebDriverAgentRunner";
|
|
NSString *envproductBundleIdentifier = NSProcessInfo.processInfo.environment[@"WDA_PRODUCT_BUNDLE_IDENTIFIER"];
|
|
if (envproductBundleIdentifier && [envproductBundleIdentifier length] != 0) {
|
|
productBundleIdentifier = NSProcessInfo.processInfo.environment[@"WDA_PRODUCT_BUNDLE_IDENTIFIER"];
|
|
}
|
|
|
|
NSMutableDictionary *buildInfo = [NSMutableDictionary dictionaryWithDictionary:@{
|
|
@"time" : [self.class buildTimestamp],
|
|
@"productBundleIdentifier" : productBundleIdentifier,
|
|
}];
|
|
NSString *upgradeTimestamp = NSProcessInfo.processInfo.environment[@"UPGRADE_TIMESTAMP"];
|
|
if (nil != upgradeTimestamp && upgradeTimestamp.length > 0) {
|
|
[buildInfo setObject:upgradeTimestamp forKey:@"upgradedAt"];
|
|
}
|
|
NSDictionary *infoDict = [[NSBundle bundleForClass:self.class] infoDictionary];
|
|
NSString *version = [infoDict objectForKey:@"CFBundleShortVersionString"];
|
|
if (nil != version) {
|
|
[buildInfo setObject:version forKey:@"version"];
|
|
}
|
|
|
|
return FBResponseWithObject(
|
|
@{
|
|
@"ready" : @YES,
|
|
@"message" : @"WebDriverAgent is ready to accept commands",
|
|
@"state" : @"success",
|
|
@"os" :
|
|
@{
|
|
@"name" : [[UIDevice currentDevice] systemName],
|
|
@"version" : [[UIDevice currentDevice] systemVersion],
|
|
@"sdkVersion": FBSDKVersion() ?: @"unknown",
|
|
@"testmanagerdVersion": @(FBTestmanagerdVersion()),
|
|
},
|
|
@"ios" :
|
|
@{
|
|
#if TARGET_OS_SIMULATOR
|
|
@"simulatorVersion" : [[UIDevice currentDevice] systemVersion],
|
|
#endif
|
|
@"ip" : [XCUIDevice sharedDevice].fb_wifiIPAddress ?: [NSNull null]
|
|
},
|
|
@"build" : buildInfo.copy,
|
|
@"device": [self.class deviceNameByUserInterfaceIdiom:[UIDevice currentDevice].userInterfaceIdiom]
|
|
}
|
|
);
|
|
}
|
|
|
|
+ (id<FBResponsePayload>)handleGetHealthCheck:(FBRouteRequest *)request
|
|
{
|
|
if (![[XCUIDevice sharedDevice] fb_healthCheckWithApplication:[XCUIApplication fb_activeApplication]]) {
|
|
return FBResponseWithUnknownErrorFormat(@"Health check failed");
|
|
}
|
|
return FBResponseWithOK();
|
|
}
|
|
|
|
+ (id<FBResponsePayload>)handleGetSettings:(FBRouteRequest *)request
|
|
{
|
|
return FBResponseWithObject(
|
|
@{
|
|
FB_SETTING_USE_COMPACT_RESPONSES: @([FBConfiguration shouldUseCompactResponses]),
|
|
FB_SETTING_ELEMENT_RESPONSE_ATTRIBUTES: [FBConfiguration elementResponseAttributes],
|
|
FB_SETTING_MJPEG_SERVER_SCREENSHOT_QUALITY: @([FBConfiguration mjpegServerScreenshotQuality]),
|
|
FB_SETTING_MJPEG_SERVER_FRAMERATE: @([FBConfiguration mjpegServerFramerate]),
|
|
FB_SETTING_MJPEG_SCALING_FACTOR: @([FBConfiguration mjpegScalingFactor]),
|
|
FB_SETTING_MJPEG_FIX_ORIENTATION: @([FBConfiguration mjpegShouldFixOrientation]),
|
|
FB_SETTING_SCREENSHOT_QUALITY: @([FBConfiguration screenshotQuality]),
|
|
FB_SETTING_KEYBOARD_AUTOCORRECTION: @([FBConfiguration keyboardAutocorrection]),
|
|
FB_SETTING_KEYBOARD_PREDICTION: @([FBConfiguration keyboardPrediction]),
|
|
FB_SETTING_SNAPSHOT_MAX_DEPTH: @([FBConfiguration snapshotMaxDepth]),
|
|
FB_SETTING_USE_FIRST_MATCH: @([FBConfiguration useFirstMatch]),
|
|
FB_SETTING_WAIT_FOR_IDLE_TIMEOUT: @([FBConfiguration waitForIdleTimeout]),
|
|
FB_SETTING_ANIMATION_COOL_OFF_TIMEOUT: @([FBConfiguration animationCoolOffTimeout]),
|
|
FB_SETTING_BOUND_ELEMENTS_BY_INDEX: @([FBConfiguration boundElementsByIndex]),
|
|
FB_SETTING_REDUCE_MOTION: @([FBConfiguration reduceMotionEnabled]),
|
|
FB_SETTING_DEFAULT_ACTIVE_APPLICATION: request.session.defaultActiveApplication,
|
|
FB_SETTING_ACTIVE_APP_DETECTION_POINT: FBActiveAppDetectionPoint.sharedInstance.stringCoordinates,
|
|
FB_SETTING_INCLUDE_NON_MODAL_ELEMENTS: @([FBConfiguration includeNonModalElements]),
|
|
FB_SETTING_ACCEPT_ALERT_BUTTON_SELECTOR: FBConfiguration.acceptAlertButtonSelector,
|
|
FB_SETTING_DISMISS_ALERT_BUTTON_SELECTOR: FBConfiguration.dismissAlertButtonSelector,
|
|
FB_SETTING_AUTO_CLICK_ALERT_SELECTOR: FBConfiguration.autoClickAlertSelector,
|
|
FB_SETTING_DEFAULT_ALERT_ACTION: request.session.defaultAlertAction ?: @"",
|
|
FB_SETTING_MAX_TYPING_FREQUENCY: @([FBConfiguration maxTypingFrequency]),
|
|
FB_SETTING_RESPECT_SYSTEM_ALERTS: @([FBConfiguration shouldRespectSystemAlerts]),
|
|
FB_SETTING_USE_CLEAR_TEXT_SHORTCUT: @([FBConfiguration useClearTextShortcut]),
|
|
FB_SETTING_INCLUDE_HITTABLE_IN_PAGE_SOURCE: @([FBConfiguration includeHittableInPageSource]),
|
|
FB_SETTING_INCLUDE_NATIVE_FRAME_IN_PAGE_SOURCE: @([FBConfiguration includeNativeFrameInPageSource]),
|
|
FB_SETTING_INCLUDE_MIN_MAX_VALUE_IN_PAGE_SOURCE: @([FBConfiguration includeMinMaxValueInPageSource]),
|
|
FB_SETTING_LIMIT_XPATH_CONTEXT_SCOPE: @([FBConfiguration limitXpathContextScope]),
|
|
#if !TARGET_OS_TV
|
|
FB_SETTING_SCREENSHOT_ORIENTATION: [FBConfiguration humanReadableScreenshotOrientation],
|
|
#endif
|
|
}
|
|
);
|
|
}
|
|
|
|
// TODO if we get lots more settings, handling them with a series of if-statements will be unwieldy
|
|
// and this should be refactored
|
|
+ (id<FBResponsePayload>)handleSetSettings:(FBRouteRequest *)request
|
|
{
|
|
NSDictionary* settings = request.arguments[@"settings"];
|
|
|
|
if (nil != [settings objectForKey:FB_SETTING_USE_COMPACT_RESPONSES]) {
|
|
[FBConfiguration setShouldUseCompactResponses:[[settings objectForKey:FB_SETTING_USE_COMPACT_RESPONSES] boolValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_ELEMENT_RESPONSE_ATTRIBUTES]) {
|
|
[FBConfiguration setElementResponseAttributes:(NSString *)[settings objectForKey:FB_SETTING_ELEMENT_RESPONSE_ATTRIBUTES]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_MJPEG_SERVER_SCREENSHOT_QUALITY]) {
|
|
[FBConfiguration setMjpegServerScreenshotQuality:[[settings objectForKey:FB_SETTING_MJPEG_SERVER_SCREENSHOT_QUALITY] unsignedIntegerValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_MJPEG_SERVER_FRAMERATE]) {
|
|
[FBConfiguration setMjpegServerFramerate:[[settings objectForKey:FB_SETTING_MJPEG_SERVER_FRAMERATE] unsignedIntegerValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_SCREENSHOT_QUALITY]) {
|
|
[FBConfiguration setScreenshotQuality:[[settings objectForKey:FB_SETTING_SCREENSHOT_QUALITY] unsignedIntegerValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_MJPEG_SCALING_FACTOR]) {
|
|
[FBConfiguration setMjpegScalingFactor:[[settings objectForKey:FB_SETTING_MJPEG_SCALING_FACTOR] floatValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_MJPEG_FIX_ORIENTATION]) {
|
|
[FBConfiguration setMjpegShouldFixOrientation:[[settings objectForKey:FB_SETTING_MJPEG_FIX_ORIENTATION] boolValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_KEYBOARD_AUTOCORRECTION]) {
|
|
[FBConfiguration setKeyboardAutocorrection:[[settings objectForKey:FB_SETTING_KEYBOARD_AUTOCORRECTION] boolValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_KEYBOARD_PREDICTION]) {
|
|
[FBConfiguration setKeyboardPrediction:[[settings objectForKey:FB_SETTING_KEYBOARD_PREDICTION] boolValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_RESPECT_SYSTEM_ALERTS]) {
|
|
[FBConfiguration setShouldRespectSystemAlerts:[[settings objectForKey:FB_SETTING_RESPECT_SYSTEM_ALERTS] boolValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_SNAPSHOT_MAX_DEPTH]) {
|
|
[FBConfiguration setSnapshotMaxDepth:[[settings objectForKey:FB_SETTING_SNAPSHOT_MAX_DEPTH] intValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_USE_FIRST_MATCH]) {
|
|
[FBConfiguration setUseFirstMatch:[[settings objectForKey:FB_SETTING_USE_FIRST_MATCH] boolValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_BOUND_ELEMENTS_BY_INDEX]) {
|
|
[FBConfiguration setBoundElementsByIndex:[[settings objectForKey:FB_SETTING_BOUND_ELEMENTS_BY_INDEX] boolValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_REDUCE_MOTION]) {
|
|
[FBConfiguration setReduceMotionEnabled:[[settings objectForKey:FB_SETTING_REDUCE_MOTION] boolValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_DEFAULT_ACTIVE_APPLICATION]) {
|
|
request.session.defaultActiveApplication = (NSString *)[settings objectForKey:FB_SETTING_DEFAULT_ACTIVE_APPLICATION];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_ACTIVE_APP_DETECTION_POINT]) {
|
|
NSError *error;
|
|
if (![FBActiveAppDetectionPoint.sharedInstance setCoordinatesWithString:(NSString *)[settings objectForKey:FB_SETTING_ACTIVE_APP_DETECTION_POINT]
|
|
error:&error]) {
|
|
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:error.localizedDescription
|
|
traceback:nil]);
|
|
}
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_INCLUDE_NON_MODAL_ELEMENTS]) {
|
|
if ([XCUIElement fb_supportsNonModalElementsInclusion]) {
|
|
[FBConfiguration setIncludeNonModalElements:[[settings objectForKey:FB_SETTING_INCLUDE_NON_MODAL_ELEMENTS] boolValue]];
|
|
} else {
|
|
[FBLogger logFmt:@"'%@' settings value cannot be assigned, because non modal elements inclusion is not supported by the current iOS SDK", FB_SETTING_INCLUDE_NON_MODAL_ELEMENTS];
|
|
}
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_ACCEPT_ALERT_BUTTON_SELECTOR]) {
|
|
[FBConfiguration setAcceptAlertButtonSelector:(NSString *)[settings objectForKey:FB_SETTING_ACCEPT_ALERT_BUTTON_SELECTOR]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_DISMISS_ALERT_BUTTON_SELECTOR]) {
|
|
[FBConfiguration setDismissAlertButtonSelector:(NSString *)[settings objectForKey:FB_SETTING_DISMISS_ALERT_BUTTON_SELECTOR]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_AUTO_CLICK_ALERT_SELECTOR]) {
|
|
FBCommandStatus *status = [self.class configureAutoClickAlertWithSelector:settings[FB_SETTING_AUTO_CLICK_ALERT_SELECTOR]
|
|
forSession:request.session];
|
|
if (status.hasError) {
|
|
return FBResponseWithStatus(status);
|
|
}
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_WAIT_FOR_IDLE_TIMEOUT]) {
|
|
[FBConfiguration setWaitForIdleTimeout:[[settings objectForKey:FB_SETTING_WAIT_FOR_IDLE_TIMEOUT] doubleValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_ANIMATION_COOL_OFF_TIMEOUT]) {
|
|
[FBConfiguration setAnimationCoolOffTimeout:[[settings objectForKey:FB_SETTING_ANIMATION_COOL_OFF_TIMEOUT] doubleValue]];
|
|
}
|
|
if ([[settings objectForKey:FB_SETTING_DEFAULT_ALERT_ACTION] isKindOfClass:NSString.class]) {
|
|
request.session.defaultAlertAction = [settings[FB_SETTING_DEFAULT_ALERT_ACTION] lowercaseString];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_MAX_TYPING_FREQUENCY]) {
|
|
[FBConfiguration setMaxTypingFrequency:[[settings objectForKey:FB_SETTING_MAX_TYPING_FREQUENCY] unsignedIntegerValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_USE_CLEAR_TEXT_SHORTCUT]) {
|
|
[FBConfiguration setUseClearTextShortcut:[[settings objectForKey:FB_SETTING_USE_CLEAR_TEXT_SHORTCUT] boolValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_INCLUDE_HITTABLE_IN_PAGE_SOURCE]) {
|
|
[FBConfiguration setIncludeHittableInPageSource:[[settings objectForKey:FB_SETTING_INCLUDE_HITTABLE_IN_PAGE_SOURCE] boolValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_INCLUDE_NATIVE_FRAME_IN_PAGE_SOURCE]) {
|
|
[FBConfiguration setIncludeNativeFrameInPageSource:[[settings objectForKey:FB_SETTING_INCLUDE_NATIVE_FRAME_IN_PAGE_SOURCE] boolValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_INCLUDE_MIN_MAX_VALUE_IN_PAGE_SOURCE]) {
|
|
[FBConfiguration setIncludeMinMaxValueInPageSource:[[settings objectForKey:FB_SETTING_INCLUDE_MIN_MAX_VALUE_IN_PAGE_SOURCE] boolValue]];
|
|
}
|
|
if (nil != [settings objectForKey:FB_SETTING_LIMIT_XPATH_CONTEXT_SCOPE]) {
|
|
[FBConfiguration setLimitXpathContextScope:[[settings objectForKey:FB_SETTING_LIMIT_XPATH_CONTEXT_SCOPE] boolValue]];
|
|
}
|
|
|
|
#if !TARGET_OS_TV
|
|
if (nil != [settings objectForKey:FB_SETTING_SCREENSHOT_ORIENTATION]) {
|
|
NSError *error;
|
|
if (![FBConfiguration setScreenshotOrientation:(NSString *)[settings objectForKey:FB_SETTING_SCREENSHOT_ORIENTATION]
|
|
error:&error]) {
|
|
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:error.localizedDescription
|
|
traceback:nil]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return [self handleGetSettings:request];
|
|
}
|
|
|
|
|
|
#pragma mark - Helpers
|
|
|
|
+ (FBCommandStatus *)configureAutoClickAlertWithSelector:(NSString *)selector
|
|
forSession:(FBSession *)session
|
|
{
|
|
if (0 == [selector length]) {
|
|
[FBConfiguration setAutoClickAlertSelector:selector];
|
|
[session disableAlertsMonitor];
|
|
return [FBCommandStatus ok];
|
|
}
|
|
|
|
NSError *error;
|
|
FBClassChain *parsedChain = [FBClassChainQueryParser parseQuery:selector error:&error];
|
|
if (nil == parsedChain) {
|
|
return [FBCommandStatus invalidSelectorErrorWithMessage:error.localizedDescription
|
|
traceback:nil];
|
|
}
|
|
[FBConfiguration setAutoClickAlertSelector:selector];
|
|
[session enableAlertsMonitor];
|
|
return [FBCommandStatus ok];
|
|
}
|
|
|
|
+ (NSString *)buildTimestamp
|
|
{
|
|
return [NSString stringWithFormat:@"%@ %@",
|
|
[NSString stringWithUTF8String:__DATE__],
|
|
[NSString stringWithUTF8String:__TIME__]
|
|
];
|
|
}
|
|
|
|
/**
|
|
Return current session information.
|
|
This response does not have any active application information.
|
|
*/
|
|
+ (NSDictionary *)sessionInformation
|
|
{
|
|
return
|
|
@{
|
|
@"sessionId" : [FBSession activeSession].identifier ?: NSNull.null,
|
|
@"capabilities" : FBSessionCommands.currentCapabilities
|
|
};
|
|
}
|
|
|
|
/*
|
|
Return the device kind as lower case
|
|
*/
|
|
+ (NSString *)deviceNameByUserInterfaceIdiom:(UIUserInterfaceIdiom) userInterfaceIdiom
|
|
{
|
|
if (userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
|
return @"ipad";
|
|
} else if (userInterfaceIdiom == UIUserInterfaceIdiomTV) {
|
|
return @"apple tv";
|
|
} else if (userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
|
|
return @"iphone";
|
|
}
|
|
// CarPlay, Mac, Vision UI or unknown are possible
|
|
return @"Unknown";
|
|
|
|
}
|
|
|
|
+ (NSDictionary *)currentCapabilities
|
|
{
|
|
return
|
|
@{
|
|
@"device": [self.class deviceNameByUserInterfaceIdiom:[UIDevice currentDevice].userInterfaceIdiom],
|
|
@"sdkVersion": [[UIDevice currentDevice] systemVersion]
|
|
};
|
|
}
|
|
|
|
+(nullable id<FBResponsePayload>)openDeepLink:(NSString *)initialUrl
|
|
withApplication:(nullable NSString *)bundleID
|
|
timeout:(nullable NSNumber *)timeout
|
|
{
|
|
NSError *openError;
|
|
NSTimeInterval defaultTimeout = _XCTApplicationStateTimeout();
|
|
if (nil != timeout) {
|
|
_XCTSetApplicationStateTimeout([timeout doubleValue]);
|
|
}
|
|
@try {
|
|
BOOL result = nil == bundleID
|
|
? [XCUIDevice.sharedDevice fb_openUrl:initialUrl
|
|
error:&openError]
|
|
: [XCUIDevice.sharedDevice fb_openUrl:initialUrl
|
|
withApplication:(id)bundleID
|
|
error:&openError];
|
|
if (result) {
|
|
return nil;
|
|
}
|
|
NSString *errorMsg = [NSString stringWithFormat:@"Cannot open the URL %@ with the %@ application. Original error: %@",
|
|
initialUrl, bundleID ?: @"default", openError.localizedDescription];
|
|
return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:errorMsg traceback:nil]);
|
|
} @finally {
|
|
if (nil != timeout) {
|
|
_XCTSetApplicationStateTimeout(defaultTimeout);
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|