diff --git a/WebDriverAgent.xcodeproj/project.pbxproj b/WebDriverAgent.xcodeproj/project.pbxproj index 59d25de16..c52bdd5cd 100644 --- a/WebDriverAgent.xcodeproj/project.pbxproj +++ b/WebDriverAgent.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 0E0413382DF1E15100AF007C /* XCUIElement+FBMinMax.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E0413372DF1E15100AF007C /* XCUIElement+FBMinMax.m */; }; + 0E0413392DF1E15100AF007C /* XCUIElement+FBMinMax.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E0413372DF1E15100AF007C /* XCUIElement+FBMinMax.m */; }; + 0E04133B2DF1E15900AF007C /* XCUIElement+FBMinMax.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E04133A2DF1E15900AF007C /* XCUIElement+FBMinMax.h */; }; + 0E04133C2DF1E15900AF007C /* XCUIElement+FBMinMax.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E04133A2DF1E15900AF007C /* XCUIElement+FBMinMax.h */; }; 1357E296233D05240054BDB8 /* XCUIHitPointResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 1357E295233D05240054BDB8 /* XCUIHitPointResult.h */; }; 1357E297233D05240054BDB8 /* XCUIHitPointResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 1357E295233D05240054BDB8 /* XCUIHitPointResult.h */; }; 13815F6F2328D20400CDAB61 /* FBActiveAppDetectionPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 13815F6D2328D20400CDAB61 /* FBActiveAppDetectionPoint.h */; }; @@ -918,6 +922,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0E0413372DF1E15100AF007C /* XCUIElement+FBMinMax.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "XCUIElement+FBMinMax.m"; sourceTree = ""; }; + 0E04133A2DF1E15900AF007C /* XCUIElement+FBMinMax.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCUIElement+FBMinMax.h"; sourceTree = ""; }; 1357E295233D05240054BDB8 /* XCUIHitPointResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCUIHitPointResult.h; sourceTree = ""; }; 13815F6D2328D20400CDAB61 /* FBActiveAppDetectionPoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBActiveAppDetectionPoint.h; sourceTree = ""; }; 13815F6E2328D20400CDAB61 /* FBActiveAppDetectionPoint.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBActiveAppDetectionPoint.m; sourceTree = ""; }; @@ -1785,6 +1791,8 @@ EE8DDD7C20C5733B004D4925 /* XCUIElement+FBForceTouch.m */, EE9AB7471CAEDF0C008C271F /* XCUIElement+FBIsVisible.h */, EE9AB7481CAEDF0C008C271F /* XCUIElement+FBIsVisible.m */, + 0E04133A2DF1E15900AF007C /* XCUIElement+FBMinMax.h */, + 0E0413372DF1E15100AF007C /* XCUIElement+FBMinMax.m */, 7136A4771E8918E60024FC3D /* XCUIElement+FBPickerWheel.h */, 7136A4781E8918E60024FC3D /* XCUIElement+FBPickerWheel.m */, 71D3B3D3267FC7260076473D /* XCUIElement+FBResolve.h */, @@ -2377,6 +2385,7 @@ 641EE6782240C5CA00173FCB /* XCTNSNotificationExpectation.h in Headers */, 641EE6792240C5CA00173FCB /* XCUIRecorderNodeFinder.h in Headers */, 641EE67A2240C5CA00173FCB /* XCUIElement+FBAccessibility.h in Headers */, + 0E04133C2DF1E15900AF007C /* XCUIElement+FBMinMax.h in Headers */, 641EE67B2240C5CA00173FCB /* XCUIRecorderUtilities.h in Headers */, 6496A5DA230D6EB30087F8CB /* AXSettings.h in Headers */, 641EE67C2240C5CA00173FCB /* XCTestCaseRun.h in Headers */, @@ -2701,6 +2710,7 @@ EEE9B4721CD02B88009D2030 /* FBRunLoopSpinner.h in Headers */, EE3A18621CDE618F00DE4205 /* FBErrorBuilder.h in Headers */, EE35AD261E3B77D600A02D78 /* XCApplicationMonitor_iOS.h in Headers */, + 0E04133B2DF1E15900AF007C /* XCUIElement+FBMinMax.h in Headers */, EE3A18661CDE734B00DE4205 /* FBKeyboard.h in Headers */, AD6C269C1CF2494200F8B5FF /* XCUIApplication+FBHelpers.h in Headers */, 714D88CC2733FB970074A925 /* FBXMLGenerationOptions.h in Headers */, @@ -3202,6 +3212,7 @@ 71BB58F92B96531900CB9BFE /* FBScreenRecordingContainer.m in Sources */, 641EE6152240C5CA00173FCB /* XCUIElement+FBScrolling.m in Sources */, 641EE6162240C5CA00173FCB /* FBSessionCommands.m in Sources */, + 0E0413392DF1E15100AF007C /* XCUIElement+FBMinMax.m in Sources */, 641EE6192240C5CA00173FCB /* FBConfiguration.m in Sources */, 641EE61A2240C5CA00173FCB /* FBElementCache.m in Sources */, 71F5BE26252E576C00EE9EBA /* XCUIElement+FBSwiping.m in Sources */, @@ -3249,6 +3260,7 @@ 13DE7A45287C2A8D003243C6 /* FBXCAccessibilityElement.m in Sources */, 641EE70E2240CE4800173FCB /* FBTVNavigationTracker.m in Sources */, 71BD20741F86116100B36EC2 /* XCUIApplication+FBTouchAction.m in Sources */, + 0E0413382DF1E15100AF007C /* XCUIElement+FBMinMax.m in Sources */, EE158AE71CBD456F00A3E3F0 /* FBWebServer.m in Sources */, 715557D4211DBCE700613B26 /* FBTCPSocket.m in Sources */, EE3A18631CDE618F00DE4205 /* FBErrorBuilder.m in Sources */, diff --git a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m index 0dc19ef07..a2875a165 100644 --- a/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +++ b/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m @@ -49,6 +49,8 @@ static NSString* const FBExclusionAttributePlaceholderValue = @"placeholderValue"; static NSString* const FBExclusionAttributeNativeFrame = @"nativeFrame"; static NSString* const FBExclusionAttributeTraits = @"traits"; +static NSString* const FBExclusionAttributeMinValue = @"minValue"; +static NSString* const FBExclusionAttributeMaxValue = @"maxValue"; _Nullable id extractIssueProperty(id issue, NSString *propertyName) { SEL selector = NSSelectorFromString(propertyName); @@ -211,6 +213,8 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot FBExclusionAttributePlaceholderValue, FBExclusionAttributeNativeFrame, FBExclusionAttributeTraits, + FBExclusionAttributeMinValue, + FBExclusionAttributeMaxValue, nil]; for (NSString *key in attributeBlocks) { @@ -248,7 +252,7 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot { // Base attributes common to every element - NSMutableDictionary *blocks = + NSMutableDictionary *blocks = [@{ FBExclusionAttributeFrame: ^{ return NSStringFromCGRect(wrappedSnapshot.wdFrame); @@ -282,6 +286,16 @@ + (NSDictionary *)dictionaryForElement:(id)snapshot }; } + // Only for elements that support min/max value + if (FBDoesElementSupportMinMaxValue(elementType)) { + blocks[FBExclusionAttributeMinValue] = ^{ + return wrappedSnapshot.wdMinValue; + }; + blocks[FBExclusionAttributeMaxValue] = ^{ + return wrappedSnapshot.wdMaxValue; + }; + } + return [blocks copy]; } diff --git a/WebDriverAgentLib/Categories/XCUIElement+FBMinMax.h b/WebDriverAgentLib/Categories/XCUIElement+FBMinMax.h new file mode 100644 index 000000000..76857aea5 --- /dev/null +++ b/WebDriverAgentLib/Categories/XCUIElement+FBMinMax.h @@ -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. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface XCUIElement (FBMinMax) + +/*! Minimum value (minValue) – may be nil if the element does not have this attribute */ +@property (nonatomic, readonly, nullable) NSNumber *fb_minValue; + +/*! Maximum value (maxValue) - may be nil if the element does not have this attribute */ +@property (nonatomic, readonly, nullable) NSNumber *fb_maxValue; + +@end + +@interface FBXCElementSnapshotWrapper (FBMinMax) + +/*! Minimum value (minValue) – may be nil if the element does not have this attribute */ +@property (nonatomic, readonly, nullable) NSNumber *fb_minValue; + +/*! Maximum value (maxValue) - may be nil if the element does not have this attribute */ +@property (nonatomic, readonly, nullable) NSNumber *fb_maxValue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/WebDriverAgentLib/Categories/XCUIElement+FBMinMax.m b/WebDriverAgentLib/Categories/XCUIElement+FBMinMax.m new file mode 100644 index 000000000..78ff474b2 --- /dev/null +++ b/WebDriverAgentLib/Categories/XCUIElement+FBMinMax.m @@ -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. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "FBLogger.h" +#import "XCUIElement+FBMinMax.h" +#import "FBXCElementSnapshotWrapper+Helpers.h" +#import "XCUIElement+FBUtilities.h" +#import "XCTestPrivateSymbols.h" + +@interface FBXCElementSnapshotWrapper (FBMinMaxInternal) + +- (NSNumber *)fb_numericAttribute:(NSString *)attributeName symbol:(NSNumber *)symbol; + +@end + +@implementation XCUIElement (FBMinMax) + +- (NSNumber *)fb_minValue +{ + @autoreleasepool { + id snapshot = [self fb_standardSnapshot]; + return [[FBXCElementSnapshotWrapper ensureWrapped:snapshot] fb_minValue]; + } +} + +- (NSNumber *)fb_maxValue +{ + @autoreleasepool { + id snapshot = [self fb_standardSnapshot]; + return [[FBXCElementSnapshotWrapper ensureWrapped:snapshot] fb_maxValue]; + } +} + +@end + +@implementation FBXCElementSnapshotWrapper (FBMinMax) + +- (NSNumber *)fb_minValue +{ + return [self fb_numericAttribute:FB_XCAXACustomMinValueAttributeName + symbol:FB_XCAXACustomMinValueAttribute]; +} + +- (NSNumber *)fb_maxValue +{ + return [self fb_numericAttribute:FB_XCAXACustomMaxValueAttributeName + symbol:FB_XCAXACustomMaxValueAttribute]; +} + +- (NSNumber *)fb_numericAttribute:(NSString *)attributeName symbol:(NSNumber *)symbol +{ + NSNumber *cached = (self.snapshot.additionalAttributes ?: @{})[symbol]; + if (cached) { + return cached; + } + + NSError *error = nil; + NSNumber *raw = [self fb_attributeValue:attributeName error:&error]; + if (nil != raw) { + NSMutableDictionary *updated = [NSMutableDictionary dictionaryWithDictionary:self.additionalAttributes ?: @{}]; + updated[symbol] = raw; + self.snapshot.additionalAttributes = updated.copy; + return raw; + } + + [FBLogger logFmt:@"[FBMinMax] Cannot determine %@ for %@: %@", attributeName, self.fb_description, error.localizedDescription]; + return nil; +} + +@end diff --git a/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.m b/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.m index e82327c9a..cd0d05964 100644 --- a/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.m +++ b/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.m @@ -23,6 +23,7 @@ #import "XCTestPrivateSymbols.h" #import "XCUIHitPointResult.h" #import "FBAccessibilityTraits.h" +#import "XCUIElement+FBMinMax.h" #define BROKEN_RECT CGRectMake(-1, -1, 0, 0) @@ -76,6 +77,16 @@ - (id)fb_valueForWDAttributeName:(NSString *)name return [self valueForKey:[FBElementUtils wdAttributeNameForAttributeName:name]]; } +- (NSNumber *)wdMinValue +{ + return self.fb_minValue; +} + +- (NSNumber *)wdMaxValue +{ + return self.fb_maxValue; +} + - (NSString *)wdValue { id value = self.value; diff --git a/WebDriverAgentLib/Commands/FBSessionCommands.m b/WebDriverAgentLib/Commands/FBSessionCommands.m index f1d2e418f..8071a6e0c 100644 --- a/WebDriverAgentLib/Commands/FBSessionCommands.m +++ b/WebDriverAgentLib/Commands/FBSessionCommands.m @@ -355,6 +355,7 @@ + (NSArray *)routes 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], @@ -463,6 +464,9 @@ + (NSArray *)routes 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]]; } diff --git a/WebDriverAgentLib/Routing/FBElement.h b/WebDriverAgentLib/Routing/FBElement.h index 45499c847..1c10e5e51 100644 --- a/WebDriverAgentLib/Routing/FBElement.h +++ b/WebDriverAgentLib/Routing/FBElement.h @@ -71,6 +71,12 @@ NS_ASSUME_NONNULL_BEGIN /*! Element's placeholder value */ @property (nonatomic, readonly, copy, nullable) NSString *wdPlaceholderValue; +/*! Element's minimum value */ +@property (nonatomic, readonly, strong, nullable) NSNumber *wdMinValue; + +/*! Element's maximum value */ +@property (nonatomic, readonly, strong, nullable) NSNumber *wdMaxValue; + /** Returns value of given property specified in WebDriver Spec Check the FBElement protocol to get list of supported attributes. diff --git a/WebDriverAgentLib/Utilities/FBConfiguration.h b/WebDriverAgentLib/Utilities/FBConfiguration.h index cfadf4cbb..cabb04f1d 100644 --- a/WebDriverAgentLib/Utilities/FBConfiguration.h +++ b/WebDriverAgentLib/Utilities/FBConfiguration.h @@ -359,6 +359,18 @@ typedef NS_ENUM(NSInteger, FBConfigurationKeyboardPreference) { + (void)setIncludeNativeFrameInPageSource:(BOOL)enabled; + (BOOL)includeNativeFrameInPageSource; +/** + * Whether to include `minValue`/`maxValue` attributes in the page source. + * These attributes are retrieved from native element snapshots and represent + * value boundaries for elements like sliders or progress indicators. + * This may affect performance if used on many elements. + * Disabled by default. + * + * @param enabled Either YES or NO + */ ++ (void)setIncludeMinMaxValueInPageSource:(BOOL)enabled; ++ (BOOL)includeMinMaxValueInPageSource; + @end NS_ASSUME_NONNULL_END diff --git a/WebDriverAgentLib/Utilities/FBConfiguration.m b/WebDriverAgentLib/Utilities/FBConfiguration.m index 56057c83d..fd42a5fff 100644 --- a/WebDriverAgentLib/Utilities/FBConfiguration.m +++ b/WebDriverAgentLib/Utilities/FBConfiguration.m @@ -63,6 +63,7 @@ #endif static BOOL FBShouldIncludeHittableInPageSource = NO; static BOOL FBShouldIncludeNativeFrameInPageSource = NO; +static BOOL FBShouldIncludeMinMaxValueInPageSource = NO; @implementation FBConfiguration @@ -664,4 +665,14 @@ + (BOOL)includeNativeFrameInPageSource return FBShouldIncludeNativeFrameInPageSource; } ++ (void)setIncludeMinMaxValueInPageSource:(BOOL)enabled +{ + FBShouldIncludeMinMaxValueInPageSource = enabled; +} + ++ (BOOL)includeMinMaxValueInPageSource +{ + return FBShouldIncludeMinMaxValueInPageSource; +} + @end diff --git a/WebDriverAgentLib/Utilities/FBElementHelpers.h b/WebDriverAgentLib/Utilities/FBElementHelpers.h index 41c449b58..8ab3a488f 100644 --- a/WebDriverAgentLib/Utilities/FBElementHelpers.h +++ b/WebDriverAgentLib/Utilities/FBElementHelpers.h @@ -15,8 +15,16 @@ NS_ASSUME_NONNULL_BEGIN Checks if the element is a text field @param elementType XCTest element type - @return YES if the elemnt is a text field + @return YES if the element is a text field */ BOOL FBDoesElementSupportInnerText(XCUIElementType elementType); +/** + Checks if the element supports min/max value attributes + + @param elementType XCTest element type + @return YES if the element type supports min/max value attributes + */ +BOOL FBDoesElementSupportMinMaxValue(XCUIElementType elementType); + NS_ASSUME_NONNULL_END diff --git a/WebDriverAgentLib/Utilities/FBElementHelpers.m b/WebDriverAgentLib/Utilities/FBElementHelpers.m index eaea413f0..c02a764db 100644 --- a/WebDriverAgentLib/Utilities/FBElementHelpers.m +++ b/WebDriverAgentLib/Utilities/FBElementHelpers.m @@ -15,3 +15,8 @@ BOOL FBDoesElementSupportInnerText(XCUIElementType elementType) { || elementType == XCUIElementTypeSearchField || elementType == XCUIElementTypeSecureTextField; } + +BOOL FBDoesElementSupportMinMaxValue(XCUIElementType elementType) { + return elementType == XCUIElementTypeSlider + || elementType == XCUIElementTypeStepper; +} diff --git a/WebDriverAgentLib/Utilities/FBSettings.h b/WebDriverAgentLib/Utilities/FBSettings.h index ab1a5cb9c..ead224c46 100644 --- a/WebDriverAgentLib/Utilities/FBSettings.h +++ b/WebDriverAgentLib/Utilities/FBSettings.h @@ -42,6 +42,6 @@ extern NSString* const FB_SETTING_LIMIT_XPATH_CONTEXT_SCOPE; extern NSString* const FB_SETTING_AUTO_CLICK_ALERT_SELECTOR; extern NSString *const FB_SETTING_INCLUDE_HITTABLE_IN_PAGE_SOURCE; extern NSString *const FB_SETTING_INCLUDE_NATIVE_FRAME_IN_PAGE_SOURCE; - +extern NSString *const FB_SETTING_INCLUDE_MIN_MAX_VALUE_IN_PAGE_SOURCE; NS_ASSUME_NONNULL_END diff --git a/WebDriverAgentLib/Utilities/FBSettings.m b/WebDriverAgentLib/Utilities/FBSettings.m index 441f2e5cd..c0796e82d 100644 --- a/WebDriverAgentLib/Utilities/FBSettings.m +++ b/WebDriverAgentLib/Utilities/FBSettings.m @@ -38,3 +38,4 @@ NSString* const FB_SETTING_AUTO_CLICK_ALERT_SELECTOR = @"autoClickAlertSelector"; NSString* const FB_SETTING_INCLUDE_HITTABLE_IN_PAGE_SOURCE = @"includeHittableInPageSource"; NSString* const FB_SETTING_INCLUDE_NATIVE_FRAME_IN_PAGE_SOURCE = @"includeNativeFrameInPageSource"; +NSString* const FB_SETTING_INCLUDE_MIN_MAX_VALUE_IN_PAGE_SOURCE = @"includeMinMaxValueInPageSource"; diff --git a/WebDriverAgentLib/Utilities/FBXPath.m b/WebDriverAgentLib/Utilities/FBXPath.m index f91d096c4..15e943c7c 100644 --- a/WebDriverAgentLib/Utilities/FBXPath.m +++ b/WebDriverAgentLib/Utilities/FBXPath.m @@ -22,6 +22,7 @@ #import "XCUIElement+FBUtilities.h" #import "XCUIElement+FBWebDriverAttributes.h" #import "XCTestPrivateSymbols.h" +#import "FBElementHelpers.h" @interface FBElementAttribute : NSObject @@ -113,6 +114,14 @@ @interface FBTraitsAttribute : FBElementAttribute @end +@interface FBMinValueAttribute : FBElementAttribute + +@end + +@interface FBMaxValueAttribute : FBElementAttribute + +@end + #if TARGET_OS_TV @interface FBFocusedAttribute : FBElementAttribute @@ -374,6 +383,11 @@ + (int)xmlRepresentationWithRootElement:(id)root // Include nativeFrame only when requested [includedAttributes removeObject:FBNativeFrameAttribute.class]; } + if (!FBConfiguration.includeMinMaxValueInPageSource) { + // minValue/maxValue are retrieved from private APIs and may be slow on deep trees + [includedAttributes removeObject:FBMinValueAttribute.class]; + [includedAttributes removeObject:FBMaxValueAttribute.class]; + } if (nil != excludedAttributes) { for (NSString *excludedAttributeName in excludedAttributes) { for (Class supportedAttribute in FBElementAttribute.supportedAttributes) { @@ -437,6 +451,16 @@ + (int)recordElementAttributes:(xmlTextWriterPtr)writer if (includedAttributes && ![includedAttributes containsObject:attributeCls]) { continue; } + // Text-input placeholder (only for elements that support inner text) + if ((attributeCls == FBPlaceholderValueAttribute.class) && + !FBDoesElementSupportInnerText(element.elementType)) { + continue; + } + // Only for elements that support min/max value + if ((attributeCls == FBMinValueAttribute.class || attributeCls == FBMaxValueAttribute.class) && + !FBDoesElementSupportMinMaxValue(element.elementType)) { + continue; + } int rc = [attributeCls recordWithWriter:writer forElement:[FBXCElementSnapshotWrapper ensureWrapped:element]]; if (rc < 0) { @@ -597,6 +621,8 @@ + (int)recordWithWriter:(xmlTextWriterPtr)writer forElement:(id)eleme FBPlaceholderValueAttribute.class, FBTraitsAttribute.class, FBNativeFrameAttribute.class, + FBMinValueAttribute.class, + FBMaxValueAttribute.class, ]; } @@ -860,3 +886,31 @@ + (NSString *)valueForElement:(id)element } @end + +@implementation FBMinValueAttribute + ++ (NSString *)name +{ + return @"minValue"; +} + ++ (NSString *)valueForElement:(id)element +{ + return [element.wdMinValue stringValue]; +} + +@end + +@implementation FBMaxValueAttribute + ++ (NSString *)name +{ + return @"maxValue"; +} + ++ (NSString *)valueForElement:(id)element +{ + return [element.wdMaxValue stringValue]; +} + +@end diff --git a/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.h b/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.h index da99cfd58..272045551 100644 --- a/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.h +++ b/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.h @@ -22,6 +22,14 @@ extern NSString *FB_XCAXAIsElementAttributeName; /*! Accessibility identifier for visible frame attribute */ extern NSString *FB_XCAXAVisibleFrameAttributeName; +/*! Accessibility identifier для минимума */ +extern NSNumber *FB_XCAXACustomMinValueAttribute; +extern NSString *FB_XCAXACustomMinValueAttributeName; + +/*! Accessibility identifier для максимума */ +extern NSNumber *FB_XCAXACustomMaxValueAttribute; +extern NSString *FB_XCAXACustomMaxValueAttributeName; + /*! Getter for XCTest logger */ extern id (*XCDebugLogger)(void); diff --git a/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.m b/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.m index 502a6f8b7..50d9fc940 100644 --- a/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.m +++ b/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.m @@ -19,6 +19,10 @@ NSNumber *FB_XCAXAIsElementAttribute; NSString *FB_XCAXAIsElementAttributeName = @"XC_kAXXCAttributeIsElement"; NSString *FB_XCAXAVisibleFrameAttributeName = @"XC_kAXXCAttributeVisibleFrame"; +NSNumber *FB_XCAXACustomMinValueAttribute; +NSString *FB_XCAXACustomMinValueAttributeName = @"XC_kAXXCAttributeMinValue"; +NSNumber *FB_XCAXACustomMaxValueAttribute; +NSString *FB_XCAXACustomMaxValueAttributeName = @"XC_kAXXCAttributeMaxValue"; void (*XCSetDebugLogger)(id ); id (*XCDebugLogger)(void); @@ -42,6 +46,16 @@ NSCAssert(FB_XCAXAIsVisibleAttribute != nil , @"Failed to retrieve FB_XCAXAIsVisibleAttribute", FB_XCAXAIsVisibleAttribute); NSCAssert(FB_XCAXAIsElementAttribute != nil , @"Failed to retrieve FB_XCAXAIsElementAttribute", FB_XCAXAIsElementAttribute); + + NSString *XC_kAXXCAttributeMinValue = *(NSString *__autoreleasing *)FBRetrieveXCTestSymbol([FB_XCAXACustomMinValueAttributeName UTF8String]); + NSString *XC_kAXXCAttributeMaxValue = *(NSString *__autoreleasing *)FBRetrieveXCTestSymbol([FB_XCAXACustomMaxValueAttributeName UTF8String]); + + NSArray *minMaxAttrs = XCAXAccessibilityAttributesForStringAttributes(@[XC_kAXXCAttributeMinValue, XC_kAXXCAttributeMaxValue]); + FB_XCAXACustomMinValueAttribute = minMaxAttrs[0]; + FB_XCAXACustomMaxValueAttribute = minMaxAttrs[1]; + + NSCAssert(FB_XCAXACustomMinValueAttribute != nil, @"Failed to retrieve FB_XCAXACustomMinValueAttribute", FB_XCAXACustomMinValueAttribute); + NSCAssert(FB_XCAXACustomMaxValueAttribute != nil, @"Failed to retrieve FB_XCAXACustomMaxValueAttribute", FB_XCAXACustomMaxValueAttribute); } void *FBRetrieveXCTestSymbol(const char *name) @@ -74,7 +88,9 @@ dispatch_once(&onceCustomAttributeNamesToken, ^{ customNames = @[ FB_XCAXAIsVisibleAttributeName, - FB_XCAXAIsElementAttributeName + FB_XCAXAIsElementAttributeName, + FB_XCAXACustomMinValueAttributeName, + FB_XCAXACustomMaxValueAttributeName ]; }); return customNames; diff --git a/WebDriverAgentTests/IntegrationTests/FBElementAttributeTests.m b/WebDriverAgentTests/IntegrationTests/FBElementAttributeTests.m index 0fd1f4cca..73f893ca1 100644 --- a/WebDriverAgentTests/IntegrationTests/FBElementAttributeTests.m +++ b/WebDriverAgentTests/IntegrationTests/FBElementAttributeTests.m @@ -180,8 +180,18 @@ - (void)testSliderAttributes 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; diff --git a/WebDriverAgentTests/UnitTests/FBXPathTests.m b/WebDriverAgentTests/UnitTests/FBXPathTests.m index 1f162d1d1..1b3444a9e 100644 --- a/WebDriverAgentTests/UnitTests/FBXPathTests.m +++ b/WebDriverAgentTests/UnitTests/FBXPathTests.m @@ -64,8 +64,8 @@ - (void)testDefaultXPathPresentation xpathQuery:nil excludingAttributes:nil]; NSLog(@"[DefaultXPath] Result XML:\n%@", resultXml); - NSString *expectedXml = [NSString stringWithFormat:@"\n<%@ type=\"%@\" value=\"%@\" name=\"%@\" label=\"%@\" enabled=\"%@\" visible=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" index=\"%lu\" placeholderValue=\"%@\" 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.wdPlaceholderValue, element.wdTraits]; + NSString *expectedXml = [NSString stringWithFormat:@"\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]); } @@ -76,8 +76,8 @@ - (void)testtXPathPresentationWithSomeAttributesExcluded NSString *resultXml = [self xmlStringWithElement:(id)element xpathQuery:nil excludingAttributes:@[@"type", @"visible", @"value", @"index", @"traits", @"nativeFrame"]]; - NSString *expectedXml = [NSString stringWithFormat:@"\n<%@ name=\"%@\" label=\"%@\" enabled=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" placeholderValue=\"%@\" 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"], element.wdPlaceholderValue]; + NSString *expectedXml = [NSString stringWithFormat:@"\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); } @@ -90,8 +90,8 @@ - (void)testXPathPresentationBasedOnQueryMatchingAllAttributes NSString *resultXml = [self xmlStringWithElement:(id)element xpathQuery:[NSString stringWithFormat:@"//%@[@*]", element.wdType] excludingAttributes:@[@"visible"]]; - NSString *expectedXml = [NSString stringWithFormat:@"\n<%@ type=\"%@\" value=\"%@\" name=\"%@\" label=\"%@\" enabled=\"%@\" visible=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" index=\"%lu\" hittable=\"%@\" placeholderValue=\"%@\" traits=\"%@\" nativeFrame=\"%@\" private_indexPath=\"top\"/>\n", - element.wdType, element.wdType, @"йоло<>&"", element.wdName, @"a 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.wdPlaceholderValue, element.wdTraits, NSStringFromCGRect(element.wdNativeFrame)]; + NSString *expectedXml = [NSString stringWithFormat:@"\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, @"йоло<>&"", element.wdName, @"a 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); }