From 7b4b291d40464971c26f764627647eb3aa7f7250 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach <mokhnach@gmail.com> Date: Wed, 12 Mar 2025 23:47:25 +0100 Subject: [PATCH 1/4] feat: Add 'limitXpathContextScope' setting --- .../Commands/FBSessionCommands.m | 4 + WebDriverAgentLib/Utilities/FBConfiguration.h | 8 ++ WebDriverAgentLib/Utilities/FBConfiguration.m | 12 ++ WebDriverAgentLib/Utilities/FBSettings.h | 1 + WebDriverAgentLib/Utilities/FBSettings.m | 1 + WebDriverAgentLib/Utilities/FBXPath-Private.h | 7 +- WebDriverAgentLib/Utilities/FBXPath.m | 119 ++++++++++++++---- .../FBXPathIntegrationTests.m | 23 ++++ WebDriverAgentTests/UnitTests/FBXPathTests.m | 16 +-- 9 files changed, 154 insertions(+), 37 deletions(-) diff --git a/WebDriverAgentLib/Commands/FBSessionCommands.m b/WebDriverAgentLib/Commands/FBSessionCommands.m index 8f84d7afe..67b6b81e9 100644 --- a/WebDriverAgentLib/Commands/FBSessionCommands.m +++ b/WebDriverAgentLib/Commands/FBSessionCommands.m @@ -351,6 +351,7 @@ + (NSArray *)routes FB_SETTING_MAX_TYPING_FREQUENCY: @([FBConfiguration maxTypingFrequency]), FB_SETTING_RESPECT_SYSTEM_ALERTS: @([FBConfiguration shouldRespectSystemAlerts]), FB_SETTING_USE_CLEAR_TEXT_SHORTCUT: @([FBConfiguration useClearTextShortcut]), + FB_SETTING_LIMIT_XPATH_CONTEXT_SCOPE: @([FBConfiguration limitXpathContextScope]), #if !TARGET_OS_TV FB_SETTING_SCREENSHOT_ORIENTATION: [FBConfiguration humanReadableScreenshotOrientation], #endif @@ -445,6 +446,9 @@ + (NSArray *)routes 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_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]) { diff --git a/WebDriverAgentLib/Utilities/FBConfiguration.h b/WebDriverAgentLib/Utilities/FBConfiguration.h index ad9687b19..cb0a67dcf 100644 --- a/WebDriverAgentLib/Utilities/FBConfiguration.h +++ b/WebDriverAgentLib/Utilities/FBConfiguration.h @@ -103,6 +103,14 @@ extern NSString *const FBSnapshotMaxDepthKey; + (NSUInteger)mjpegServerFramerate; + (void)setMjpegServerFramerate:(NSUInteger)framerate; +/** + Whether to limit the XPath scope to descendant items only while performing a lookup + in an element context. Enabled by default. Being disabled, allows to use XPath locators + like ".." in order to match parent items of the current context root. + */ ++ (BOOL)limitXpathContextScope; ++ (void)setLimitXpathContextScope:(BOOL)enabled; + /** The quality of display screenshots. The higher quality you set is the bigger screenshot size is. The highest quality value is 0 (lossless PNG) or 3 (lossless HEIC). The lowest quality is 2 (highly compressed JPEG). diff --git a/WebDriverAgentLib/Utilities/FBConfiguration.m b/WebDriverAgentLib/Utilities/FBConfiguration.m index 1065b892e..f3cf8e42f 100644 --- a/WebDriverAgentLib/Utilities/FBConfiguration.m +++ b/WebDriverAgentLib/Utilities/FBConfiguration.m @@ -56,6 +56,7 @@ static BOOL FBShouldUseCompactResponses; static NSString *FBElementResponseAttributes; static BOOL FBUseClearTextShortcut; +static BOOL FBLimitXpathContextScope = YES; #if !TARGET_OS_TV static UIInterfaceOrientation FBScreenshotOrientation; #endif @@ -438,6 +439,16 @@ + (BOOL)useClearTextShortcut return FBUseClearTextShortcut; } ++ (BOOL)limitXpathContextScope +{ + return FBLimitXpathContextScope; +} + ++ (void)setLimitXpathContextScope:(BOOL)enabled +{ + FBLimitXpathContextScope = enabled; +} + #if !TARGET_OS_TV + (BOOL)setScreenshotOrientation:(NSString *)orientation error:(NSError **)error { @@ -503,6 +514,7 @@ + (void)resetSessionSettings // 50 should be enough for the majority of the cases. The performance is acceptable for values up to 100. FBSetCustomParameterForElementSnapshot(FBSnapshotMaxDepthKey, @50); FBUseClearTextShortcut = YES; + FBLimitXpathContextScope = YES; #if !TARGET_OS_TV FBScreenshotOrientation = UIInterfaceOrientationUnknown; #endif diff --git a/WebDriverAgentLib/Utilities/FBSettings.h b/WebDriverAgentLib/Utilities/FBSettings.h index 1a551c63f..7f6d57970 100644 --- a/WebDriverAgentLib/Utilities/FBSettings.h +++ b/WebDriverAgentLib/Utilities/FBSettings.h @@ -38,6 +38,7 @@ extern NSString* const FB_SETTING_ANIMATION_COOL_OFF_TIMEOUT; 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; NS_ASSUME_NONNULL_END diff --git a/WebDriverAgentLib/Utilities/FBSettings.m b/WebDriverAgentLib/Utilities/FBSettings.m index f5a61a8a8..c19889db3 100644 --- a/WebDriverAgentLib/Utilities/FBSettings.m +++ b/WebDriverAgentLib/Utilities/FBSettings.m @@ -34,3 +34,4 @@ NSString* const FB_SETTING_MAX_TYPING_FREQUENCY = @"maxTypingFrequency"; 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"; diff --git a/WebDriverAgentLib/Utilities/FBXPath-Private.h b/WebDriverAgentLib/Utilities/FBXPath-Private.h index 31bb403bf..7346b2c3f 100644 --- a/WebDriverAgentLib/Utilities/FBXPath-Private.h +++ b/WebDriverAgentLib/Utilities/FBXPath-Private.h @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN If `query` argument is assigned then `excludedAttributes` argument is effectively ignored. @return zero if the method has completed successfully */ -+ (int)xmlRepresentationWithRootElement:(id<FBElement>)root ++ (int)xmlRepresentationWithRootElement:(id<FBXCElementSnapshot>)root writer:(xmlTextWriterPtr)writer elementStore:(nullable NSMutableDictionary *)elementStore query:(nullable NSString*)query @@ -45,9 +45,12 @@ NS_ASSUME_NONNULL_BEGIN @param xpathQuery actual query. Should be valid XPath 1.0-compatible expression @param document libxml2-compatible document pointer + @param contextNode Optonal context node instance @return pointer to a libxml2-compatible structure with set of matched nodes or NULL in case of failure */ -+ (xmlXPathObjectPtr)evaluate:(NSString *)xpathQuery document:(xmlDocPtr)doc; ++ (xmlXPathObjectPtr)evaluate:(NSString *)xpathQuery + document:(xmlDocPtr)doc + contextNode:(nullable xmlNodePtr)contextNode; @end diff --git a/WebDriverAgentLib/Utilities/FBXPath.m b/WebDriverAgentLib/Utilities/FBXPath.m index d36291fdd..18793041f 100644 --- a/WebDriverAgentLib/Utilities/FBXPath.m +++ b/WebDriverAgentLib/Utilities/FBXPath.m @@ -11,6 +11,7 @@ #import "FBConfiguration.h" #import "FBExceptions.h" +#import "FBElementUtils.h" #import "FBLogger.h" #import "FBMacros.h" #import "FBXMLGenerationOptions.h" @@ -145,7 +146,7 @@ + (nullable NSString *)xmlStringWithRootElement:(id<FBElement>)root } if (rc >= 0) { - rc = [self xmlRepresentationWithRootElement:root + rc = [self xmlRepresentationWithRootElement:[self snapshotWithRoot:root] writer:writer elementStore:nil query:nil @@ -193,10 +194,29 @@ + (nullable NSString *)xmlStringWithRootElement:(id<FBElement>)root } NSMutableDictionary *elementStore = [NSMutableDictionary dictionary]; int rc = xmlTextWriterStartDocument(writer, NULL, _UTF8Encoding, NULL); + id<FBXCElementSnapshot> lookupScopeSnapshot = nil; + id<FBXCElementSnapshot> contextRootSnapshot = nil; if (rc < 0) { [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterStartDocument. Error code: %d", rc]; } else { - rc = [self xmlRepresentationWithRootElement:root + if (FBConfiguration.limitXpathContextScope) { + lookupScopeSnapshot = [self snapshotWithRoot:root]; + } else { + if ([root isKindOfClass:XCUIElement.class]) { + lookupScopeSnapshot = [self snapshotWithRoot:[(XCUIElement *)root application]]; + contextRootSnapshot = [root isKindOfClass:XCUIApplication.class] + ? nil + : [(XCUIElement *)root fb_takeSnapshot:YES]; + } else { + lookupScopeSnapshot = (id<FBXCElementSnapshot>)root; + contextRootSnapshot = nil == lookupScopeSnapshot.parent ? nil : (id<FBXCElementSnapshot>)root; + while (nil != lookupScopeSnapshot.parent) { + lookupScopeSnapshot = lookupScopeSnapshot.parent; + } + } + } + + rc = [self xmlRepresentationWithRootElement:lookupScopeSnapshot writer:writer elementStore:elementStore query:xpathQuery @@ -214,7 +234,22 @@ + (nullable NSString *)xmlStringWithRootElement:(id<FBElement>)root return [self throwException:FBXPathQueryEvaluationException forQuery:xpathQuery]; } - xmlXPathObjectPtr queryResult = [self evaluate:xpathQuery document:doc]; + xmlXPathObjectPtr contextNodeQueryResult = [self matchNodeInDocument:doc + elementStore:elementStore.copy + forSnapshot:contextRootSnapshot]; + xmlNodePtr contextNode = NULL; + if (NULL != contextNodeQueryResult) { + xmlNodeSetPtr nodeSet = contextNodeQueryResult->nodesetval; + if (!xmlXPathNodeSetIsEmpty(nodeSet)) { + contextNode = nodeSet->nodeTab[0]; + } + } + xmlXPathObjectPtr queryResult = [self evaluate:xpathQuery + document:doc + contextNode:contextNode]; + if (NULL != contextNodeQueryResult) { + xmlXPathFreeObject(contextNodeQueryResult); + } if (NULL == queryResult) { xmlFreeTextWriter(writer); xmlFreeDoc(doc); @@ -256,6 +291,36 @@ + (NSArray *)collectMatchingSnapshots:(xmlNodeSetPtr)nodeSet return matchingSnapshots.copy; } ++ (nullable xmlXPathObjectPtr)matchNodeInDocument:(xmlDocPtr)doc + elementStore:(NSDictionary<NSString *, id<FBXCElementSnapshot>> *)elementStore + forSnapshot:(nullable id<FBXCElementSnapshot>)snapshot +{ + if (nil == snapshot) { + return NULL; + } + + NSString *contextRootUid = [FBElementUtils uidWithAccessibilityElement:[(id)snapshot accessibilityElement]]; + if (nil == contextRootUid) { + return NULL; + } + + for (NSString *key in elementStore) { + id<FBXCElementSnapshot> value = [elementStore objectForKey:key]; + NSString *snapshotUid = [FBElementUtils uidWithAccessibilityElement:[value accessibilityElement]]; + if (nil == snapshotUid || ![snapshotUid isEqualToString:contextRootUid]) { + continue; + } + NSString *indexQuery = [NSString stringWithFormat:@"//*[@%@=\"%@\"]", kXMLIndexPathKey, key]; + xmlXPathObjectPtr queryResult = [self evaluate:indexQuery + document:doc + contextNode:NULL]; + if (NULL != queryResult) { + return queryResult; + } + } + return NULL; +} + + (NSSet<Class> *)elementAttributesWithXPathQuery:(NSString *)query { if ([query rangeOfString:@"[^\\w@]@\\*[^\\w]" options:NSRegularExpressionSearch].location != NSNotFound) { @@ -271,7 +336,7 @@ + (NSArray *)collectMatchingSnapshots:(xmlNodeSetPtr)nodeSet return result.copy; } -+ (int)xmlRepresentationWithRootElement:(id<FBElement>)root ++ (int)xmlRepresentationWithRootElement:(id<FBXCElementSnapshot>)root writer:(xmlTextWriterPtr)writer elementStore:(nullable NSMutableDictionary *)elementStore query:(nullable NSString*)query @@ -312,14 +377,16 @@ + (int)xmlRepresentationWithRootElement:(id<FBElement>)root return 0; } -+ (xmlXPathObjectPtr)evaluate:(NSString *)xpathQuery document:(xmlDocPtr)doc ++ (xmlXPathObjectPtr)evaluate:(NSString *)xpathQuery + document:(xmlDocPtr)doc + contextNode:(nullable xmlNodePtr)contextNode { xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc); if (NULL == xpathCtx) { [FBLogger logFmt:@"Failed to invoke libxml2>xmlXPathNewContext for XPath query \"%@\"", xpathQuery]; return NULL; } - xpathCtx->node = doc->children; + xpathCtx->node = NULL == contextNode ? doc->children : contextNode; xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((const xmlChar *)[xpathQuery UTF8String], xpathCtx); if (NULL == xpathObj) { @@ -360,7 +427,7 @@ + (int)recordElementAttributes:(xmlTextWriterPtr)writer return 0; } -+ (int)writeXmlWithRootElement:(id<FBElement>)root ++ (int)writeXmlWithRootElement:(id<FBXCElementSnapshot>)root indexPath:(nullable NSString *)indexPath elementStore:(nullable NSMutableDictionary *)elementStore includedAttributes:(nullable NSSet<Class> *)includedAttributes @@ -368,29 +435,13 @@ + (int)writeXmlWithRootElement:(id<FBElement>)root { NSAssert((indexPath == nil && elementStore == nil) || (indexPath != nil && elementStore != nil), @"Either both or none of indexPath and elementStore arguments should be equal to nil", nil); - __block id<FBXCElementSnapshot> currentSnapshot; - NSArray<id<FBXCElementSnapshot>> *children; - if ([root isKindOfClass:XCUIElement.class]) { - XCUIElement *element = (XCUIElement *)root; - if (nil == includedAttributes || [includedAttributes containsObject:FBVisibleAttribute.class]) { - // If the app is not idle state while we retrieve the visiblity state - // then the snapshot retrieval operation might freeze and time out - [element.application fb_waitUntilStableWithTimeout:FBConfiguration.animationCoolOffTimeout]; - } - @autoreleasepool { - currentSnapshot = [element fb_takeSnapshot:YES]; - } - children = currentSnapshot.children; - } else { - currentSnapshot = (id<FBXCElementSnapshot>)root; - children = currentSnapshot.children; - } + NSArray<id<FBXCElementSnapshot>> *children = root.children; if (elementStore != nil && indexPath != nil && [indexPath isEqualToString:topNodeIndexPath]) { - [elementStore setObject:currentSnapshot forKey:topNodeIndexPath]; + [elementStore setObject:root forKey:topNodeIndexPath]; } - FBXCElementSnapshotWrapper *wrappedSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:currentSnapshot]; + FBXCElementSnapshotWrapper *wrappedSnapshot = [FBXCElementSnapshotWrapper ensureWrapped:root]; int rc = xmlTextWriterStartElement(writer, (xmlChar *)[wrappedSnapshot.wdType UTF8String]); if (rc < 0) { [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterStartElement for the tag value '%@'. Error code: %d", wrappedSnapshot.wdType, rc]; @@ -398,7 +449,7 @@ + (int)writeXmlWithRootElement:(id<FBElement>)root } rc = [self recordElementAttributes:writer - forElement:currentSnapshot + forElement:root indexPath:indexPath includedAttributes:includedAttributes]; if (rc < 0) { @@ -431,6 +482,20 @@ + (int)writeXmlWithRootElement:(id<FBElement>)root return 0; } ++ (id<FBXCElementSnapshot>)snapshotWithRoot:(id<FBElement>)root +{ + if (![root isKindOfClass:XCUIElement.class]) { + return (id<FBXCElementSnapshot>)root; + } + + // If the app is not idle state while we retrieve the visiblity state + // then the snapshot retrieval operation might freeze and time out + [[(XCUIElement *)root application] fb_waitUntilStableWithTimeout:FBConfiguration.animationCoolOffTimeout]; + @autoreleasepool { + return [(XCUIElement *)root fb_takeSnapshot:YES]; + } +} + @end diff --git a/WebDriverAgentTests/IntegrationTests/FBXPathIntegrationTests.m b/WebDriverAgentTests/IntegrationTests/FBXPathIntegrationTests.m index adbadeb32..77b3c2bcf 100644 --- a/WebDriverAgentTests/IntegrationTests/FBXPathIntegrationTests.m +++ b/WebDriverAgentTests/IntegrationTests/FBXPathIntegrationTests.m @@ -96,6 +96,29 @@ - (void)testFindMatchesInElement } } +- (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 *currentSnapshots = [FBXPath matchesWithRootElement:button forQuery:@"."]; + XCTAssertEqual(currentSnapshots.count, 1); + XCTAssertEqualObjects( + [FBXCElementSnapshotWrapper ensureWrapped:[currentSnapshots objectAtIndex:0]].wdType, + @"XCUIElementTypeButton" + ); + } @finally { + FBConfiguration.limitXpathContextScope = previousValue; + } +} + - (void)testFindMatchesInElementWithDotNotation { NSArray<id<FBXCElementSnapshot>> *matchingSnapshots = [FBXPath matchesWithRootElement:self.testedApplication forQuery:@".//XCUIElementTypeButton"]; diff --git a/WebDriverAgentTests/UnitTests/FBXPathTests.m b/WebDriverAgentTests/UnitTests/FBXPathTests.m index ad0d1988f..3d549e0b2 100644 --- a/WebDriverAgentTests/UnitTests/FBXPathTests.m +++ b/WebDriverAgentTests/UnitTests/FBXPathTests.m @@ -21,7 +21,7 @@ @interface FBXPathTests : XCTestCase @implementation FBXPathTests -- (NSString *)xmlStringWithElement:(id<FBElement>)element +- (NSString *)xmlStringWithElement:(id<FBXCElementSnapshot>)snapshot xpathQuery:(nullable NSString *)query excludingAttributes:(nullable NSArray<NSString *> *)excludedAttributes { @@ -33,7 +33,7 @@ - (NSString *)xmlStringWithElement:(id<FBElement>)element xmlChar *xmlbuff = NULL; int rc = xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL); if (rc >= 0) { - rc = [FBXPath xmlRepresentationWithRootElement:element + rc = [FBXPath xmlRepresentationWithRootElement:snapshot writer:writer elementStore:elementStore query:query @@ -60,7 +60,7 @@ - (void)testDefaultXPathPresentation { XCElementSnapshotDouble *snapshot = [XCElementSnapshotDouble new]; id<FBElement> element = (id<FBElement>)[FBXCElementSnapshotWrapper ensureWrapped:(id)snapshot]; - NSString *resultXml = [self xmlStringWithElement:element + NSString *resultXml = [self xmlStringWithElement:(id<FBXCElementSnapshot>)element xpathQuery:nil excludingAttributes:nil]; 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\" placeholderValue=\"%@\" private_indexPath=\"top\"/>\n", @@ -72,7 +72,7 @@ - (void)testtXPathPresentationWithSomeAttributesExcluded { XCElementSnapshotDouble *snapshot = [XCElementSnapshotDouble new]; id<FBElement> element = (id<FBElement>)[FBXCElementSnapshotWrapper ensureWrapped:(id)snapshot]; - NSString *resultXml = [self xmlStringWithElement:element + NSString *resultXml = [self xmlStringWithElement:(id<FBXCElementSnapshot>)element xpathQuery:nil excludingAttributes:@[@"type", @"visible", @"value", @"index"]]; NSString *expectedXml = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<%@ name=\"%@\" label=\"%@\" enabled=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" placeholderValue=\"%@\" private_indexPath=\"top\"/>\n", @@ -86,7 +86,7 @@ - (void)testXPathPresentationBasedOnQueryMatchingAllAttributes snapshot.value = @"йоло<>&\""; snapshot.label = @"a\nb"; id<FBElement> element = (id<FBElement>)[FBXCElementSnapshotWrapper ensureWrapped:(id)snapshot]; - NSString *resultXml = [self xmlStringWithElement:element + 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=\"%@\" placeholderValue=\"%@\" private_indexPath=\"top\"/>\n", @@ -98,7 +98,7 @@ - (void)testXPathPresentationBasedOnQueryMatchingSomeAttributes { XCElementSnapshotDouble *snapshot = [XCElementSnapshotDouble new]; id<FBElement> element = (id<FBElement>)[FBXCElementSnapshotWrapper ensureWrapped:(id)snapshot]; - NSString *resultXml = [self xmlStringWithElement:element + 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", @@ -117,7 +117,7 @@ - (void)testSnapshotXPathResultsMatching NSString *query = [NSString stringWithFormat:@"//%@", root.wdType]; int rc = xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL); if (rc >= 0) { - rc = [FBXPath xmlRepresentationWithRootElement:root + rc = [FBXPath xmlRepresentationWithRootElement:(id<FBXCElementSnapshot>)root writer:writer elementStore:elementStore query:query @@ -132,7 +132,7 @@ - (void)testSnapshotXPathResultsMatching XCTFail(@"Unable to create the source XML document"); } - xmlXPathObjectPtr queryResult = [FBXPath evaluate:query document:doc]; + xmlXPathObjectPtr queryResult = [FBXPath evaluate:query document:doc contextNode:NULL]; if (NULL == queryResult) { xmlFreeTextWriter(writer); xmlFreeDoc(doc); From d5bca9b46f6a27b78980d504fb500fae6eac09a3 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach <mokhnach@gmail.com> Date: Wed, 12 Mar 2025 23:55:29 +0100 Subject: [PATCH 2/4] make linter happy --- lib/webdriveragent.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/webdriveragent.js b/lib/webdriveragent.js index a2a9dbb0d..f36afbd1f 100644 --- a/lib/webdriveragent.js +++ b/lib/webdriveragent.js @@ -579,8 +579,8 @@ export class WebDriverAgent { setupProxies (sessionId) { const proxyOpts = { log: this.log, - server: this.url.hostname, - port: this.url.port, + server: this.url.hostname ?? undefined, + port: parseInt(this.url.port ?? '', 10) ?? undefined, base: this.basePath, timeout: this.wdaConnectionTimeout, keepAlive: true, From 479a5d4be7a970fd61124bf59e831004165f4423 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach <mokhnach@gmail.com> Date: Wed, 12 Mar 2025 23:56:18 +0100 Subject: [PATCH 3/4] moar --- lib/webdriveragent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/webdriveragent.js b/lib/webdriveragent.js index f36afbd1f..73b056bd5 100644 --- a/lib/webdriveragent.js +++ b/lib/webdriveragent.js @@ -580,7 +580,7 @@ export class WebDriverAgent { const proxyOpts = { log: this.log, server: this.url.hostname ?? undefined, - port: parseInt(this.url.port ?? '', 10) ?? undefined, + port: parseInt(this.url.port ?? '', 10) || undefined, base: this.basePath, timeout: this.wdaConnectionTimeout, keepAlive: true, From 5463b04f263ec60e9551449b6431fc3c53e15586 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach <mokhnach@gmail.com> Date: Thu, 13 Mar 2025 07:57:50 +0100 Subject: [PATCH 4/4] Bump versions --- .github/workflows/functional-test.yml | 14 +++++--------- test/unit/webdriveragent-specs.js | 8 ++++---- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/workflows/functional-test.yml b/.github/workflows/functional-test.yml index b8c9855aa..524f337dc 100644 --- a/.github/workflows/functional-test.yml +++ b/.github/workflows/functional-test.yml @@ -13,17 +13,13 @@ jobs: matrix: test_targets: - HOST_OS: 'macos-15' - XCODE_VERSION: '16.1.0' - IOS_VERSION: '18.1' + XCODE_VERSION: '16.2' + IOS_VERSION: '18.2' IOS_MODEL: iPhone 16 Plus - - HOST_OS: 'macos-14' - XCODE_VERSION: '15.3' - IOS_VERSION: '17.4' + - HOST_OS: 'macos-15' + XCODE_VERSION: '15.4' + IOS_VERSION: '17.5' IOS_MODEL: iPhone 15 Plus - - HOST_OS: 'macos-13' - XCODE_VERSION: 14.3.1 - IOS_VERSION: '16.4' - IOS_MODEL: iPhone 14 Plus # https://github.com/actions/runner-images/blob/main/images/macos/macos-14-Readme.md runs-on: ${{matrix.test_targets.HOST_OS}} diff --git a/test/unit/webdriveragent-specs.js b/test/unit/webdriveragent-specs.js index 1b844e08a..487f71d91 100644 --- a/test/unit/webdriveragent-specs.js +++ b/test/unit/webdriveragent-specs.js @@ -71,10 +71,10 @@ describe('launch', function () { await agent.launch('sessionId').should.eventually.eql({build: 'data'}); agent.url.href.should.eql(override); agent.jwproxy.server.should.eql('mockurl'); - agent.jwproxy.port.should.eql('8100'); + agent.jwproxy.port.should.eql(8100); agent.jwproxy.base.should.eql(''); agent.noSessionProxy.server.should.eql('mockurl'); - agent.noSessionProxy.port.should.eql('8100'); + agent.noSessionProxy.port.should.eql(8100); agent.noSessionProxy.base.should.eql(''); wdaStub.reset(); }); @@ -97,10 +97,10 @@ describe('use wda proxy url', function () { agent.url.hostname.should.eql('127.0.0.1'); agent.url.path.should.eql('/aabbccdd'); agent.jwproxy.server.should.eql('127.0.0.1'); - agent.jwproxy.port.should.eql('8100'); + agent.jwproxy.port.should.eql(8100); agent.jwproxy.base.should.eql('/aabbccdd'); agent.noSessionProxy.server.should.eql('127.0.0.1'); - agent.noSessionProxy.port.should.eql('8100'); + agent.noSessionProxy.port.should.eql(8100); agent.noSessionProxy.base.should.eql('/aabbccdd'); }); });