| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- /**
- * 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. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
- #import "FBSession.h"
- #import "FBSession-Private.h"
- #import <objc/runtime.h>
- #import "FBXCAccessibilityElement.h"
- #import "FBAlertsMonitor.h"
- #import "FBConfiguration.h"
- #import "FBElementCache.h"
- #import "FBExceptions.h"
- #import "FBMacros.h"
- #import "FBScreenRecordingContainer.h"
- #import "FBScreenRecordingPromise.h"
- #import "FBScreenRecordingRequest.h"
- #import "FBXCodeCompatibility.h"
- #import "FBXCTestDaemonsProxy.h"
- #import "XCUIApplication+FBQuiescence.h"
- #import "XCUIElement.h"
- /*!
- The intial value for the default application property.
- Setting this value to `defaultActiveApplication` property forces WDA to use the internal
- automated algorithm to determine the active on-screen application
- */
- NSString *const FBDefaultApplicationAuto = @"auto";
- @interface FBSession ()
- @property (nullable, nonatomic) XCUIApplication *testedApplication;
- @property (nonatomic) BOOL isTestedApplicationExpectedToRun;
- @property (nonatomic) BOOL shouldAppsWaitForQuiescence;
- @property (nonatomic, nullable) FBAlertsMonitor *alertsMonitor;
- @property (nonatomic, readwrite) NSMutableDictionary<NSNumber *, NSMutableDictionary<NSString *, NSNumber *> *> *elementsVisibilityCache;
- @end
- @interface FBSession (FBAlertsMonitorDelegate)
- - (void)didDetectAlert:(FBAlert *)alert;
- @end
- @implementation FBSession (FBAlertsMonitorDelegate)
- - (void)didDetectAlert:(FBAlert *)alert
- {
- if (nil == self.defaultAlertAction || 0 == self.defaultAlertAction.length) {
- return;
- }
- NSError *error;
- if ([self.defaultAlertAction isEqualToString:@"accept"]) {
- if (![alert acceptWithError:&error]) {
- [FBLogger logFmt:@"Cannot accept the alert. Original error: %@", error.description];
- }
- } else if ([self.defaultAlertAction isEqualToString:@"dismiss"]) {
- if (![alert dismissWithError:&error]) {
- [FBLogger logFmt:@"Cannot dismiss the alert. Original error: %@", error.description];
- }
- } else {
- [FBLogger logFmt:@"'%@' default alert action is unsupported", self.defaultAlertAction];
- }
- }
- @end
- @implementation FBSession
- static FBSession *_activeSession = nil;
- + (instancetype)activeSession
- {
- return _activeSession;
- }
- + (void)markSessionActive:(FBSession *)session
- {
- if (_activeSession) {
- [_activeSession kill];
- }
- _activeSession = session;
- }
- + (instancetype)sessionWithIdentifier:(NSString *)identifier
- {
- if (!identifier) {
- return nil;
- }
- if (![identifier isEqualToString:_activeSession.identifier]) {
- return nil;
- }
- return _activeSession;
- }
- + (instancetype)initWithApplication:(XCUIApplication *)application
- {
- FBSession *session = [FBSession new];
- session.useNativeCachingStrategy = YES;
- session.alertsMonitor = nil;
- session.defaultAlertAction = nil;
- session.elementsVisibilityCache = [NSMutableDictionary dictionary];
- session.identifier = [[NSUUID UUID] UUIDString];
- session.defaultActiveApplication = FBDefaultApplicationAuto;
- session.testedApplication = nil;
- session.isTestedApplicationExpectedToRun = nil != application && application.running;
- if (application) {
- session.testedApplication = application;
- session.shouldAppsWaitForQuiescence = application.fb_shouldWaitForQuiescence;
- }
- session.elementCache = [FBElementCache new];
- [FBSession markSessionActive:session];
- return session;
- }
- + (instancetype)initWithApplication:(nullable XCUIApplication *)application
- defaultAlertAction:(NSString *)defaultAlertAction
- {
- FBSession *session = [self.class initWithApplication:application];
- session.alertsMonitor = [[FBAlertsMonitor alloc] init];
- session.alertsMonitor.delegate = (id<FBAlertsMonitorDelegate>)session;
- session.defaultAlertAction = [defaultAlertAction lowercaseString];
- [session.alertsMonitor enable];
- return session;
- }
- - (void)kill
- {
- if (nil == _activeSession) {
- return;
- }
- if (nil != self.alertsMonitor) {
- [self.alertsMonitor disable];
- self.alertsMonitor = nil;
- }
- FBScreenRecordingPromise *activeScreenRecording = FBScreenRecordingContainer.sharedInstance.screenRecordingPromise;
- if (nil != activeScreenRecording) {
- NSError *error;
- if (![FBXCTestDaemonsProxy stopScreenRecordingWithUUID:activeScreenRecording.identifier error:&error]) {
- [FBLogger logFmt:@"%@", error];
- }
- [FBScreenRecordingContainer.sharedInstance reset];
- }
- if (nil != self.testedApplication
- && FBConfiguration.shouldTerminateApp
- && self.testedApplication.running
- && ![self.testedApplication fb_isSameAppAs:XCUIApplication.fb_systemApplication]) {
- @try {
- [self.testedApplication terminate];
- } @catch (NSException *e) {
- [FBLogger logFmt:@"%@", e.description];
- }
- }
- _activeSession = nil;
- }
- - (XCUIApplication *)activeApplication
- {
- BOOL isAuto = [self.defaultActiveApplication isEqualToString:FBDefaultApplicationAuto];
- NSString *defaultBundleId = isAuto ? nil : self.defaultActiveApplication;
- if (nil != defaultBundleId && [self applicationStateWithBundleId:defaultBundleId] >= XCUIApplicationStateRunningForeground) {
- return [self makeApplicationWithBundleId:defaultBundleId];
- }
- if (nil != self.testedApplication) {
- XCUIApplicationState testedAppState = self.testedApplication.state;
- if (testedAppState >= XCUIApplicationStateRunningForeground) {
- return (XCUIApplication *)self.testedApplication;
- }
- if (self.isTestedApplicationExpectedToRun && testedAppState <= XCUIApplicationStateNotRunning) {
- NSString *description = [NSString stringWithFormat:@"The application under test with bundle id '%@' is not running, possibly crashed", self.testedApplication.bundleID];
- @throw [NSException exceptionWithName:FBApplicationCrashedException reason:description userInfo:nil];
- }
- }
- return [XCUIApplication fb_activeApplicationWithDefaultBundleId:defaultBundleId];
- }
- - (XCUIApplication *)launchApplicationWithBundleId:(NSString *)bundleIdentifier
- shouldWaitForQuiescence:(nullable NSNumber *)shouldWaitForQuiescence
- arguments:(nullable NSArray<NSString *> *)arguments
- environment:(nullable NSDictionary <NSString *, NSString *> *)environment
- {
- XCUIApplication *app = [self makeApplicationWithBundleId:bundleIdentifier];
- if (nil == shouldWaitForQuiescence) {
- // Iherit the quiescence check setting from the main app under test by default
- app.fb_shouldWaitForQuiescence = nil != self.testedApplication && self.shouldAppsWaitForQuiescence;
- } else {
- app.fb_shouldWaitForQuiescence = [shouldWaitForQuiescence boolValue];
- }
- if (!app.running) {
- app.launchArguments = arguments ?: @[];
- app.launchEnvironment = environment ?: @{};
- [app launch];
- } else {
- [app activate];
- }
- if ([app fb_isSameAppAs:self.testedApplication]) {
- self.isTestedApplicationExpectedToRun = YES;
- }
- return app;
- }
- - (XCUIApplication *)activateApplicationWithBundleId:(NSString *)bundleIdentifier
- {
- XCUIApplication *app = [self makeApplicationWithBundleId:bundleIdentifier];
- [app activate];
- return app;
- }
- - (BOOL)terminateApplicationWithBundleId:(NSString *)bundleIdentifier
- {
- XCUIApplication *app = [self makeApplicationWithBundleId:bundleIdentifier];
- if ([app fb_isSameAppAs:self.testedApplication]) {
- self.isTestedApplicationExpectedToRun = NO;
- }
- if (app.running) {
- [app terminate];
- return YES;
- }
- return NO;
- }
- - (NSUInteger)applicationStateWithBundleId:(NSString *)bundleIdentifier
- {
- return [self makeApplicationWithBundleId:bundleIdentifier].state;
- }
- - (XCUIApplication *)makeApplicationWithBundleId:(NSString *)bundleIdentifier
- {
- return nil != self.testedApplication && [bundleIdentifier isEqualToString:(NSString *)self.testedApplication.bundleID]
- ? self.testedApplication
- : [[XCUIApplication alloc] initWithBundleIdentifier:bundleIdentifier];
- }
- @end
|