diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index b23ecbee6..6ff82176d 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -7,9 +7,9 @@ jobs: prepare_matrix: runs-on: ubuntu-latest outputs: - versions: ${{ steps.generate-matrix.outputs.active }} + versions: ${{ steps.generate-matrix.outputs.lts }} steps: - - name: Select all active LTS versions of Node.js + - name: Select all current LTS versions of Node.js id: generate-matrix uses: msimerson/node-lts-versions@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cb85ea72..b3848005f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [9.5.0](https://github.com/appium/WebDriverAgent/compare/v9.4.1...v9.5.0) (2025-04-10) + +### Features + +* Add support for the autoClickAlertSelector setting ([#1002](https://github.com/appium/WebDriverAgent/issues/1002)) ([fd31b95](https://github.com/appium/WebDriverAgent/commit/fd31b9589199d0a7bc76919f6aa7c7c74c498b90)) + ## [9.4.1](https://github.com/appium/WebDriverAgent/compare/v9.4.0...v9.4.1) (2025-04-05) ### Miscellaneous Chores diff --git a/WebDriverAgentLib/Categories/XCUIElement+FBClassChain.h b/WebDriverAgentLib/Categories/XCUIElement+FBClassChain.h index 1cd1e8b93..b6e9ab924 100644 --- a/WebDriverAgentLib/Categories/XCUIElement+FBClassChain.h +++ b/WebDriverAgentLib/Categories/XCUIElement+FBClassChain.h @@ -50,7 +50,8 @@ NS_ASSUME_NONNULL_BEGIN @return an array of descendants matching given class chain @throws FBUnknownAttributeException if any of predicates in the chain contains unknown attribute(s) */ -- (NSArray *)fb_descendantsMatchingClassChain:(NSString *)classChainQuery shouldReturnAfterFirstMatch:(BOOL)shouldReturnAfterFirstMatch; +- (NSArray *)fb_descendantsMatchingClassChain:(NSString *)classChainQuery + shouldReturnAfterFirstMatch:(BOOL)shouldReturnAfterFirstMatch; @end diff --git a/WebDriverAgentLib/Commands/FBSessionCommands.m b/WebDriverAgentLib/Commands/FBSessionCommands.m index 67b6b81e9..01409ab02 100644 --- a/WebDriverAgentLib/Commands/FBSessionCommands.m +++ b/WebDriverAgentLib/Commands/FBSessionCommands.m @@ -10,6 +10,7 @@ #import "FBSessionCommands.h" #import "FBCapabilities.h" +#import "FBClassChainQueryParser.h" #import "FBConfiguration.h" #import "FBExceptions.h" #import "FBLogger.h" @@ -347,6 +348,7 @@ + (NSArray *)routes 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]), @@ -431,6 +433,13 @@ + (NSArray *)routes 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]]; } @@ -467,6 +476,26 @@ + (NSArray *)routes #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:@"%@ %@", diff --git a/WebDriverAgentLib/Info.plist b/WebDriverAgentLib/Info.plist index 9c4dcda35..5cc707d9a 100644 --- a/WebDriverAgentLib/Info.plist +++ b/WebDriverAgentLib/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 9.4.1 + 9.5.0 CFBundleSignature ???? CFBundleVersion - 9.4.1 + 9.5.0 NSPrincipalClass diff --git a/WebDriverAgentLib/Routing/FBCommandStatus.h b/WebDriverAgentLib/Routing/FBCommandStatus.h index 82bee1f60..7d61f68aa 100644 --- a/WebDriverAgentLib/Routing/FBCommandStatus.h +++ b/WebDriverAgentLib/Routing/FBCommandStatus.h @@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable, readonly) NSString* message; @property (nonatomic, nullable, readonly) NSString* traceback; @property (nonatomic, readonly) HTTPStatusCode statusCode; - +@property (nonatomic, readonly) BOOL hasError; + (instancetype)ok; diff --git a/WebDriverAgentLib/Routing/FBCommandStatus.m b/WebDriverAgentLib/Routing/FBCommandStatus.m index 453fb6192..4d1d9c589 100644 --- a/WebDriverAgentLib/Routing/FBCommandStatus.m +++ b/WebDriverAgentLib/Routing/FBCommandStatus.m @@ -109,6 +109,11 @@ - (instancetype)initWithError:(NSString *)error return self; } +- (BOOL)hasError +{ + return self.statusCode != kHTTPStatusCodeOK; +} + + (instancetype)ok { return [[FBCommandStatus alloc] initWithValue:nil]; diff --git a/WebDriverAgentLib/Routing/FBSession.h b/WebDriverAgentLib/Routing/FBSession.h index b617ae096..ca5be963d 100644 --- a/WebDriverAgentLib/Routing/FBSession.h +++ b/WebDriverAgentLib/Routing/FBSession.h @@ -121,6 +121,22 @@ extern NSString* const FB_SAFARI_BUNDLE_ID; */ - (NSUInteger)applicationStateWithBundleId:(NSString *)bundleIdentifier; +/** + Allows to enable automated session alerts monitoring. + Repeated calls are ignored if alerts monitoring has been already enabled. + + @returns YES if the actual alerts monitoring state has been changed + */ +- (BOOL)enableAlertsMonitor; + +/** + Allows to disable automated alerts monitoring + Repeated calls are ignored if alerts monitoring has been already disabled. + + @returns YES if the actual alerts monitoring state has been changed + */ +- (BOOL)disableAlertsMonitor; + @end NS_ASSUME_NONNULL_END diff --git a/WebDriverAgentLib/Routing/FBSession.m b/WebDriverAgentLib/Routing/FBSession.m index e8fb95a18..06afa5157 100644 --- a/WebDriverAgentLib/Routing/FBSession.m +++ b/WebDriverAgentLib/Routing/FBSession.m @@ -25,6 +25,7 @@ #import "FBXCTestDaemonsProxy.h" #import "XCUIApplication+FBQuiescence.h" #import "XCUIElement.h" +#import "XCUIElement+FBClassChain.h" /*! The intial value for the default application property. @@ -53,6 +54,22 @@ @implementation FBSession (FBAlertsMonitorDelegate) - (void)didDetectAlert:(FBAlert *)alert { + NSString *autoClickAlertSelector = FBConfiguration.autoClickAlertSelector; + if ([autoClickAlertSelector length] > 0) { + @try { + NSArray *matches = [alert.alertElement fb_descendantsMatchingClassChain:autoClickAlertSelector + shouldReturnAfterFirstMatch:YES]; + if (matches.count > 0) { + [[matches objectAtIndex:0] tap]; + } + } @catch (NSException *e) { + [FBLogger logFmt:@"Could not click at the alert element '%@'. Original error: %@", + autoClickAlertSelector, e.description]; + } + // This setting has priority over other settings if enabled + return; + } + if (nil == self.defaultAlertAction || 0 == self.defaultAlertAction.length) { return; } @@ -125,23 +142,41 @@ + (instancetype)initWithApplication:(nullable XCUIApplication *)application defaultAlertAction:(NSString *)defaultAlertAction { FBSession *session = [self.class initWithApplication:application]; - session.alertsMonitor = [[FBAlertsMonitor alloc] init]; - session.alertsMonitor.delegate = (id)session; session.defaultAlertAction = [defaultAlertAction lowercaseString]; - [session.alertsMonitor enable]; + [session enableAlertsMonitor]; return session; } +- (BOOL)enableAlertsMonitor +{ + if (nil != self.alertsMonitor) { + return NO; + } + + self.alertsMonitor = [[FBAlertsMonitor alloc] init]; + self.alertsMonitor.delegate = (id)self; + [self.alertsMonitor enable]; + return YES; +} + +- (BOOL)disableAlertsMonitor +{ + if (nil == self.alertsMonitor) { + return NO; + } + + [self.alertsMonitor disable]; + self.alertsMonitor = nil; + return YES; +} + - (void)kill { if (nil == _activeSession) { return; } - if (nil != self.alertsMonitor) { - [self.alertsMonitor disable]; - self.alertsMonitor = nil; - } + [self disableAlertsMonitor]; FBScreenRecordingPromise *activeScreenRecording = FBScreenRecordingContainer.sharedInstance.screenRecordingPromise; if (nil != activeScreenRecording) { diff --git a/WebDriverAgentLib/Utilities/FBConfiguration.h b/WebDriverAgentLib/Utilities/FBConfiguration.h index cb0a67dcf..f2da06736 100644 --- a/WebDriverAgentLib/Utilities/FBConfiguration.h +++ b/WebDriverAgentLib/Utilities/FBConfiguration.h @@ -283,6 +283,12 @@ typedef NS_ENUM(NSInteger, FBConfigurationKeyboardPreference) { + (void)setDismissAlertButtonSelector:(NSString *)classChainSelector; + (NSString *)dismissAlertButtonSelector; +/** + Sets class chain selector to apply for an automated alert click + */ ++ (void)setAutoClickAlertSelector:(NSString *)classChainSelector; ++ (NSString *)autoClickAlertSelector; + /** * Whether to use HIDEvent for text clear. * By default this is enabled and HIDEvent is used for text clear. diff --git a/WebDriverAgentLib/Utilities/FBConfiguration.m b/WebDriverAgentLib/Utilities/FBConfiguration.m index f3cf8e42f..7b75bcd9a 100644 --- a/WebDriverAgentLib/Utilities/FBConfiguration.m +++ b/WebDriverAgentLib/Utilities/FBConfiguration.m @@ -51,6 +51,7 @@ static BOOL FBIncludeNonModalElements; static NSString *FBAcceptAlertButtonSelector; static NSString *FBDismissAlertButtonSelector; +static NSString *FBAutoClickAlertSelector; static NSTimeInterval FBWaitForIdleTimeout; static NSTimeInterval FBAnimationCoolOffTimeout; static BOOL FBShouldUseCompactResponses; @@ -429,6 +430,16 @@ + (NSString *)dismissAlertButtonSelector return FBDismissAlertButtonSelector; } ++ (void)setAutoClickAlertSelector:(NSString *)classChainSelector +{ + FBAutoClickAlertSelector = classChainSelector; +} + ++ (NSString *)autoClickAlertSelector +{ + return FBAutoClickAlertSelector; +} + + (void)setUseClearTextShortcut:(BOOL)enabled { FBUseClearTextShortcut = enabled; @@ -509,6 +520,7 @@ + (void)resetSessionSettings FBIncludeNonModalElements = NO; FBAcceptAlertButtonSelector = @""; FBDismissAlertButtonSelector = @""; + FBAutoClickAlertSelector = @""; FBWaitForIdleTimeout = 10.; FBAnimationCoolOffTimeout = 2.; // 50 should be enough for the majority of the cases. The performance is acceptable for values up to 100. diff --git a/WebDriverAgentLib/Utilities/FBSettings.h b/WebDriverAgentLib/Utilities/FBSettings.h index 7f6d57970..cc25fe50c 100644 --- a/WebDriverAgentLib/Utilities/FBSettings.h +++ b/WebDriverAgentLib/Utilities/FBSettings.h @@ -39,6 +39,7 @@ extern NSString* const FB_SETTING_MAX_TYPING_FREQUENCY; extern NSString* const FB_SETTING_RESPECT_SYSTEM_ALERTS; extern NSString* const FB_SETTING_USE_CLEAR_TEXT_SHORTCUT; extern NSString* const FB_SETTING_LIMIT_XPATH_CONTEXT_SCOPE; +extern NSString* const FB_SETTING_AUTO_CLICK_ALERT_SELECTOR; NS_ASSUME_NONNULL_END diff --git a/WebDriverAgentLib/Utilities/FBSettings.m b/WebDriverAgentLib/Utilities/FBSettings.m index d91e343ed..09badf744 100644 --- a/WebDriverAgentLib/Utilities/FBSettings.m +++ b/WebDriverAgentLib/Utilities/FBSettings.m @@ -35,3 +35,4 @@ NSString* const FB_SETTING_RESPECT_SYSTEM_ALERTS = @"respectSystemAlerts"; NSString* const FB_SETTING_USE_CLEAR_TEXT_SHORTCUT = @"useClearTextShortcut"; NSString* const FB_SETTING_LIMIT_XPATH_CONTEXT_SCOPE = @"limitXPathContextScope"; +NSString* const FB_SETTING_AUTO_CLICK_ALERT_SELECTOR = @"autoClickAlertSelector"; diff --git a/package.json b/package.json index 8eb150709..5ba6ed9b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "appium-webdriveragent", - "version": "9.4.1", + "version": "9.5.0", "description": "Package bundling WebDriverAgent", "main": "./build/index.js", "types": "./build/index.d.ts",