@devicelab/maestro-runner-darwin-arm64 1.1.29 → 1.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +15 -4
  2. package/bin/maestro-runner +0 -0
  3. package/drivers/android/devicelab-android-driver-test.apk +0 -0
  4. package/drivers/android/devicelab-android-driver.apk +0 -0
  5. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/ATTRIBUTION.md +16 -0
  6. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Lifecycle.swift +33 -0
  7. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Snapshot.swift +195 -88
  8. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests.swift +3 -0
  9. package/drivers/ios/WebDriverAgent/CHANGELOG.md +202 -0
  10. package/drivers/ios/WebDriverAgent/README.md +18 -9
  11. package/drivers/ios/WebDriverAgent/Scripts/ci/build-real.sh +6 -10
  12. package/drivers/ios/WebDriverAgent/Scripts/fetch-prebuilt-wda.mjs +3 -2
  13. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/project.pbxproj +119 -0
  14. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCAXClient_iOS+FBSnapshotReqParams.m +8 -0
  15. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +3 -0
  16. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +30 -1
  17. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.h +53 -0
  18. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.m +124 -0
  19. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.h +6 -0
  20. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.m +100 -23
  21. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHinge.h +41 -0
  22. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHinge.m +215 -0
  23. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBRotation.m +8 -3
  24. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBFind.m +20 -7
  25. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.m +110 -50
  26. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTyping.h +7 -0
  27. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTyping.m +20 -0
  28. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.m +14 -11
  29. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBCustomCommands.m +86 -10
  30. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBElementCommands.m +57 -7
  31. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBSessionCommands.m +3 -2
  32. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Info.plist +2 -2
  33. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBHTTPServer.m +39 -16
  34. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBScreenRecordingRequest.m +7 -2
  35. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession.h +22 -0
  36. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession.m +78 -12
  37. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBTCPSocket.h +5 -1
  38. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBTCPSocket.m +10 -1
  39. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBWebServer.m +4 -1
  40. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.h +2 -2
  41. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.m +20 -0
  42. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBConfiguration.h +8 -0
  43. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBConfiguration.m +2 -0
  44. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBHIDOperations.h +41 -0
  45. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBHIDOperations.m +244 -0
  46. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBImageProcessor.m +3 -1
  47. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMathUtils.h +11 -0
  48. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMathUtils.m +38 -0
  49. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMjpegServer.m +5 -3
  50. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreen.h +17 -0
  51. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreen.m +76 -1
  52. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreenshot.h +2 -2
  53. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreenshot.m +6 -2
  54. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettings.h +2 -0
  55. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettings.m +2 -0
  56. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettingsHandler.m +48 -0
  57. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.m +121 -13
  58. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXPath.m +181 -128
  59. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCache.m +8 -5
  60. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCacheNode.h +1 -1
  61. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/WebDriverAgentLib.h +1 -0
  62. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/COORDINATE_PROBE.md +3 -1
  63. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBCoordinateProbeViewController.m +18 -0
  64. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/SceneDelegate.m +33 -0
  65. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/ViewController.m +26 -7
  66. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBConfigurationTests.m +52 -0
  67. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBDisplayGestureTests.m +541 -0
  68. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBElementSwipingTests.m +95 -0
  69. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBImageProcessorTests.m +34 -0
  70. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBScreenTests.m +392 -0
  71. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBSessionIntegrationTests.m +97 -0
  72. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBTypingTest.m +33 -0
  73. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBW3CTouchActionsIntegrationTests.m +126 -0
  74. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m +111 -0
  75. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceHelperTests.m +97 -0
  76. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceRotationTests.m +53 -0
  77. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIElementFBFindTests.m +113 -0
  78. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests_tvOS/FBTVFocusIntegrationTests.m +21 -0
  79. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests_watchOS/WDAAppLifecycleIntegrationTests.swift +12 -0
  80. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBCustomCommandsTests.m +61 -0
  81. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBElementUtilitiesTests.m +40 -0
  82. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBHTTPServerSessionTests.m +22 -4
  83. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBHTTPServerTests.m +199 -5
  84. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBLRUCacheTests.m +34 -0
  85. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBMathUtilsTests.m +45 -0
  86. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBSessionTests.m +201 -0
  87. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBXPathTests.m +101 -0
  88. package/drivers/ios/WebDriverAgent/docs/hinge-angle.md +54 -0
  89. package/drivers/ios/WebDriverAgent/docs/multiple-displays.md +168 -0
  90. package/drivers/ios/WebDriverAgent/lib/webdriveragent.ts +1 -2
  91. package/drivers/ios/WebDriverAgent/lib/xcodebuild.ts +58 -10
  92. package/drivers/ios/WebDriverAgent/package.json +8 -15
  93. package/drivers/ios/WebDriverAgent/test/functional/helpers/display-settings.ts +49 -0
  94. package/drivers/ios/WebDriverAgent/test/functional/helpers/simulator.ts +7 -7
  95. package/drivers/ios/WebDriverAgent/test/functional/webdriveragent-e2e.spec.ts +4 -0
  96. package/drivers/ios/WebDriverAgent/test/unit/xcodebuild.spec.ts +230 -0
  97. package/drivers/ios/WebDriverAgent/tsconfig.json +2 -1
  98. package/drivers/ios/devicelab-ios-agent/README.md +12 -0
  99. package/drivers/ios/devicelab-ios-agent/device/DevicelabIOSAgent.app/DevicelabIOSAgent +0 -0
  100. package/drivers/ios/devicelab-ios-agent/device/DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests +0 -0
  101. package/drivers/ios/devicelab-ios-agent/device/manifest.json +3 -3
  102. package/drivers/ios/devicelab-ios-agent/simulator/DevicelabIOSAgent.app/DevicelabIOSAgent +0 -0
  103. package/drivers/ios/devicelab-ios-agent/simulator/DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests +0 -0
  104. package/drivers/ios/devicelab-ios-agent/simulator/manifest.json +3 -3
  105. package/package.json +1 -1
  106. package/drivers/ios/WebDriverAgent/.editorconfig +0 -11
  107. package/drivers/ios/WebDriverAgent/.github/ISSUE_TEMPLATE.md +0 -4
  108. package/drivers/ios/WebDriverAgent/.github/actions/xcode-setup/action.yml +0 -32
  109. package/drivers/ios/WebDriverAgent/.github/dependabot.yml +0 -22
  110. package/drivers/ios/WebDriverAgent/.github/workflows/functional-test.yml +0 -103
  111. package/drivers/ios/WebDriverAgent/.github/workflows/pr-title.yml +0 -10
  112. package/drivers/ios/WebDriverAgent/.github/workflows/publish.js.yml +0 -112
  113. package/drivers/ios/WebDriverAgent/.github/workflows/unit-test.yml +0 -32
  114. package/drivers/ios/WebDriverAgent/.github/workflows/wda-package.yml +0 -114
  115. package/drivers/ios/WebDriverAgent/.github/workflows/wda-tests.yml +0 -246
  116. package/drivers/ios/WebDriverAgent/oxfmt.config.mjs +0 -6
  117. package/drivers/ios/WebDriverAgent/oxlint.config.mjs +0 -6
  118. package/drivers/ios/WebDriverAgent/release.config.mjs +0 -14
@@ -175,154 +175,175 @@ static NSString *const topNodeIndexPath = @"top";
175
175
  + (nullable NSString *)xmlStringWithRootElement:(id<FBElement>)root
176
176
  options:(nullable FBXMLGenerationOptions *)options
177
177
  {
178
- xmlDocPtr doc;
179
- xmlTextWriterPtr writer = xmlNewTextWriterDoc(&doc, 0);
180
- int rc = xmlTextWriterStartDocument(writer, NULL, _UTF8Encoding, NULL);
181
- if (rc < 0) {
182
- [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterStartDocument. Error code: %d", rc];
183
- } else {
184
- BOOL hasScope = nil != options.scope && [options.scope length] > 0;
185
- if (hasScope) {
186
- rc = xmlTextWriterStartElement(writer,
187
- (xmlChar *)[[self safeXmlStringWithString:options.scope] UTF8String]);
188
- if (rc < 0) {
189
- [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterStartElement for the tag value '%@'. Error code: %d", options.scope, rc];
190
- }
178
+ xmlBufferPtr buffer = NULL;
179
+ xmlTextWriterPtr writer = NULL;
180
+ @try {
181
+ buffer = xmlBufferCreate();
182
+ if (NULL == buffer) {
183
+ [FBLogger log:@"Failed to invoke libxml2>xmlBufferCreate"];
184
+ return nil;
191
185
  }
192
-
193
- if (rc >= 0) {
194
- [self waitUntilStableWithElement:root];
195
- // If 'includeHittableInPageSource' setting is enabled, then use native snapshots
196
- // to calculate a more accurate value for the 'hittable' attribute.
197
- rc = [self xmlRepresentationWithRootElement:[self snapshotWithRoot:root
198
- useNative:FBConfiguration.sharedInstance.includeHittableInPageSource]
199
- writer:writer
200
- elementStore:nil
201
- query:nil
202
- excludingAttributes:options.excludedAttributes];
186
+ writer = xmlNewTextWriterMemory(buffer, 0);
187
+ if (NULL == writer) {
188
+ [FBLogger log:@"Failed to invoke libxml2>xmlNewTextWriterMemory"];
189
+ return nil;
203
190
  }
191
+ xmlTextWriterSetIndent(writer, 1);
192
+ xmlTextWriterSetIndentString(writer, BAD_CAST " ");
193
+ int rc = xmlTextWriterStartDocument(writer, NULL, _UTF8Encoding, NULL);
194
+ if (rc < 0) {
195
+ [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterStartDocument. Error code: %d", rc];
196
+ } else {
197
+ BOOL hasScope = nil != options.scope && [options.scope length] > 0;
198
+ if (hasScope) {
199
+ rc = xmlTextWriterStartElement(writer,
200
+ (xmlChar *)[[self safeXmlStringWithString:options.scope] UTF8String]);
201
+ if (rc < 0) {
202
+ [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterStartElement for the tag value '%@'. Error code: %d", options.scope, rc];
203
+ }
204
+ }
204
205
 
205
- if (rc >= 0 && hasScope) {
206
- rc = xmlTextWriterEndElement(writer);
207
- if (rc < 0) {
208
- [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterEndElement. Error code: %d", rc];
206
+ if (rc >= 0) {
207
+ [self waitUntilStableWithElement:root];
208
+ // If 'includeHittableInPageSource' setting is enabled, then use native snapshots
209
+ // to calculate a more accurate value for the 'hittable' attribute.
210
+ rc = [self xmlRepresentationWithRootElement:[self snapshotWithRoot:root
211
+ useNative:FBConfiguration.sharedInstance.includeHittableInPageSource]
212
+ writer:writer
213
+ elementStore:nil
214
+ query:nil
215
+ excludingAttributes:options.excludedAttributes];
209
216
  }
210
- }
211
217
 
212
- if (rc >= 0) {
213
- rc = xmlTextWriterEndDocument(writer);
214
- if (rc < 0) {
215
- [FBLogger logFmt:@"Failed to invoke libxml2>xmlXPathNewContext. Error code: %d", rc];
218
+ if (rc >= 0 && hasScope) {
219
+ rc = xmlTextWriterEndElement(writer);
220
+ if (rc < 0) {
221
+ [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterEndElement. Error code: %d", rc];
222
+ }
223
+ }
224
+
225
+ if (rc >= 0) {
226
+ rc = xmlTextWriterEndDocument(writer);
227
+ if (rc < 0) {
228
+ [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterEndDocument. Error code: %d", rc];
229
+ }
216
230
  }
217
231
  }
232
+ if (rc < 0) {
233
+ return nil;
234
+ }
235
+ return [[NSString alloc] initWithBytes:xmlBufferContent(buffer)
236
+ length:(NSUInteger)xmlBufferLength(buffer)
237
+ encoding:NSUTF8StringEncoding];
238
+ } @finally {
239
+ if (NULL != writer) {
240
+ xmlFreeTextWriter(writer);
241
+ }
242
+ if (NULL != buffer) {
243
+ xmlBufferFree(buffer);
244
+ }
218
245
  }
219
- if (rc < 0) {
220
- xmlFreeTextWriter(writer);
221
- xmlFreeDoc(doc);
222
- return nil;
223
- }
224
- int buffersize;
225
- xmlChar *xmlbuff;
226
- xmlDocDumpFormatMemory(doc, &xmlbuff, &buffersize, 1);
227
- xmlFreeTextWriter(writer);
228
- xmlFreeDoc(doc);
229
- NSString *result = [NSString stringWithCString:(const char *)xmlbuff encoding:NSUTF8StringEncoding];
230
- xmlFree(xmlbuff);
231
- return result;
232
246
  }
233
247
 
234
248
  + (NSArray<id<FBXCElementSnapshot>> *)matchesWithRootElement:(id<FBElement>)root
235
249
  forQuery:(NSString *)xpathQuery
236
250
  {
237
- xmlDocPtr doc;
238
-
239
- xmlTextWriterPtr writer = xmlNewTextWriterDoc(&doc, 0);
240
- if (NULL == writer) {
241
- [FBLogger logFmt:@"Failed to invoke libxml2>xmlNewTextWriterDoc for XPath query \"%@\"", xpathQuery];
242
- return [self throwException:FBXPathQueryEvaluationException forQuery:xpathQuery];
243
- }
244
- NSMutableDictionary *elementStore = [NSMutableDictionary dictionary];
245
- int rc = xmlTextWriterStartDocument(writer, NULL, _UTF8Encoding, NULL);
246
- id<FBXCElementSnapshot> lookupScopeSnapshot = nil;
247
- id<FBXCElementSnapshot> contextRootSnapshot = nil;
248
- BOOL useNativeSnapshot = nil == xpathQuery
249
- ? NO
250
- : [[self.class elementAttributesWithXPathQuery:xpathQuery] containsObject:FBHittableAttribute.class];
251
- if (rc < 0) {
252
- [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterStartDocument. Error code: %d", rc];
253
- } else {
254
- [self waitUntilStableWithElement:root];
255
- if (FBConfiguration.sharedInstance.limitXpathContextScope) {
256
- lookupScopeSnapshot = [self snapshotWithRoot:root useNative:useNativeSnapshot];
251
+ xmlDocPtr doc = NULL;
252
+ xmlTextWriterPtr writer = NULL;
253
+ xmlXPathObjectPtr contextNodeQueryResult = NULL;
254
+ xmlXPathObjectPtr queryResult = NULL;
255
+ @try {
256
+ writer = xmlNewTextWriterDoc(&doc, 0);
257
+ if (NULL == writer) {
258
+ [FBLogger logFmt:@"Failed to invoke libxml2>xmlNewTextWriterDoc for XPath query \"%@\"", xpathQuery];
259
+ return [self throwException:FBXPathQueryEvaluationException forQuery:xpathQuery];
260
+ }
261
+ NSMutableDictionary *elementStore = [NSMutableDictionary dictionary];
262
+ int rc = xmlTextWriterStartDocument(writer, NULL, _UTF8Encoding, NULL);
263
+ id<FBXCElementSnapshot> lookupScopeSnapshot = nil;
264
+ id<FBXCElementSnapshot> contextRootSnapshot = nil;
265
+ BOOL useNativeSnapshot = nil == xpathQuery
266
+ ? NO
267
+ : [[self.class elementAttributesWithXPathQuery:xpathQuery] containsObject:FBHittableAttribute.class];
268
+ if (rc < 0) {
269
+ [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterStartDocument. Error code: %d", rc];
257
270
  } else {
258
- if ([root isKindOfClass:XCUIElement.class]) {
259
- lookupScopeSnapshot = [self snapshotWithRoot:[(XCUIElement *)root application]
260
- useNative:useNativeSnapshot];
261
- // root.lastSnapshot may be stale leftover from an unrelated earlier command.
262
- contextRootSnapshot = [root isKindOfClass:XCUIApplication.class]
263
- ? nil
264
- : ([(XCUIElement *)root fb_cachedSnapshot] ?: [self snapshotWithRoot:(XCUIElement *)root
265
- useNative:useNativeSnapshot]);
271
+ [self waitUntilStableWithElement:root];
272
+ if (FBConfiguration.sharedInstance.limitXpathContextScope) {
273
+ lookupScopeSnapshot = [self snapshotWithRoot:root useNative:useNativeSnapshot];
266
274
  } else {
267
- lookupScopeSnapshot = (id<FBXCElementSnapshot>)root;
268
- contextRootSnapshot = nil == lookupScopeSnapshot.parent ? nil : (id<FBXCElementSnapshot>)root;
269
- while (nil != lookupScopeSnapshot.parent) {
270
- lookupScopeSnapshot = lookupScopeSnapshot.parent;
275
+ if ([root isKindOfClass:XCUIElement.class]) {
276
+ lookupScopeSnapshot = [self snapshotWithRoot:[(XCUIElement *)root application]
277
+ useNative:useNativeSnapshot];
278
+ // root.lastSnapshot may be stale leftover from an unrelated earlier command.
279
+ contextRootSnapshot = [root isKindOfClass:XCUIApplication.class]
280
+ ? nil
281
+ : ([(XCUIElement *)root fb_cachedSnapshot] ?: [self snapshotWithRoot:(XCUIElement *)root
282
+ useNative:useNativeSnapshot]);
283
+ } else {
284
+ lookupScopeSnapshot = (id<FBXCElementSnapshot>)root;
285
+ contextRootSnapshot = nil == lookupScopeSnapshot.parent ? nil : (id<FBXCElementSnapshot>)root;
286
+ while (nil != lookupScopeSnapshot.parent) {
287
+ lookupScopeSnapshot = lookupScopeSnapshot.parent;
288
+ }
271
289
  }
272
290
  }
273
- }
274
291
 
275
- rc = [self xmlRepresentationWithRootElement:lookupScopeSnapshot
276
- writer:writer
277
- elementStore:elementStore
278
- query:xpathQuery
279
- excludingAttributes:nil];
280
- if (rc >= 0) {
281
- rc = xmlTextWriterEndDocument(writer);
282
- if (rc < 0) {
283
- [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterEndDocument. Error code: %d", rc];
292
+ rc = [self xmlRepresentationWithRootElement:lookupScopeSnapshot
293
+ writer:writer
294
+ elementStore:elementStore
295
+ query:xpathQuery
296
+ excludingAttributes:nil];
297
+ if (rc >= 0) {
298
+ rc = xmlTextWriterEndDocument(writer);
299
+ if (rc < 0) {
300
+ [FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterEndDocument. Error code: %d", rc];
301
+ }
284
302
  }
285
303
  }
286
- }
287
- if (rc < 0) {
288
- xmlFreeTextWriter(writer);
289
- xmlFreeDoc(doc);
290
- return [self throwException:FBXPathQueryEvaluationException forQuery:xpathQuery];
291
- }
304
+ if (rc < 0) {
305
+ return [self throwException:FBXPathQueryEvaluationException forQuery:xpathQuery];
306
+ }
292
307
 
293
- xmlXPathObjectPtr contextNodeQueryResult = [self matchNodeInDocument:doc
294
- elementStore:elementStore.copy
295
- forSnapshot:contextRootSnapshot];
296
- xmlNodePtr contextNode = NULL;
297
- if (NULL != contextNodeQueryResult) {
298
- xmlNodeSetPtr nodeSet = contextNodeQueryResult->nodesetval;
299
- if (!xmlXPathNodeSetIsEmpty(nodeSet)) {
300
- contextNode = nodeSet->nodeTab[0];
308
+ contextNodeQueryResult = [self matchNodeInDocument:doc
309
+ elementStore:elementStore.copy
310
+ forSnapshot:contextRootSnapshot];
311
+ xmlNodePtr contextNode = NULL;
312
+ if (NULL != contextNodeQueryResult) {
313
+ xmlNodeSetPtr nodeSet = contextNodeQueryResult->nodesetval;
314
+ if (!xmlXPathNodeSetIsEmpty(nodeSet)) {
315
+ contextNode = nodeSet->nodeTab[0];
316
+ }
317
+ }
318
+ NSString *evaluationError = nil;
319
+ queryResult = [self evaluate:xpathQuery
320
+ document:doc
321
+ contextNode:contextNode
322
+ errorMessage:&evaluationError];
323
+ if (NULL == queryResult) {
324
+ return [self throwException:FBInvalidXPathException forQuery:xpathQuery detail:evaluationError];
301
325
  }
302
- }
303
- NSString *evaluationError = nil;
304
- xmlXPathObjectPtr queryResult = [self evaluate:xpathQuery
305
- document:doc
306
- contextNode:contextNode
307
- errorMessage:&evaluationError];
308
- if (NULL != contextNodeQueryResult) {
309
- xmlXPathFreeObject(contextNodeQueryResult);
310
- }
311
- if (NULL == queryResult) {
312
- xmlFreeTextWriter(writer);
313
- xmlFreeDoc(doc);
314
- return [self throwException:FBInvalidXPathException forQuery:xpathQuery detail:evaluationError];
315
- }
316
326
 
317
- NSArray *matchingSnapshots = [self collectMatchingSnapshots:queryResult->nodesetval
318
- elementStore:elementStore];
319
- xmlXPathFreeObject(queryResult);
320
- xmlFreeTextWriter(writer);
321
- xmlFreeDoc(doc);
322
- if (nil == matchingSnapshots) {
323
- return [self throwException:FBXPathQueryEvaluationException forQuery:xpathQuery];
327
+ NSArray *matchingSnapshots = [self collectMatchingSnapshots:queryResult->nodesetval
328
+ elementStore:elementStore];
329
+ if (nil == matchingSnapshots) {
330
+ return [self throwException:FBXPathQueryEvaluationException forQuery:xpathQuery];
331
+ }
332
+ return matchingSnapshots;
333
+ } @finally {
334
+ if (NULL != queryResult) {
335
+ xmlXPathFreeObject(queryResult);
336
+ }
337
+ if (NULL != contextNodeQueryResult) {
338
+ xmlXPathFreeObject(contextNodeQueryResult);
339
+ }
340
+ if (NULL != writer) {
341
+ xmlFreeTextWriter(writer);
342
+ }
343
+ if (NULL != doc) {
344
+ xmlFreeDoc(doc);
345
+ }
324
346
  }
325
- return matchingSnapshots;
326
347
  }
327
348
 
328
349
  + (NSArray *)collectMatchingSnapshots:(xmlNodeSetPtr)nodeSet
@@ -504,6 +525,18 @@ static NSString *const topNodeIndexPath = @"top";
504
525
  indexPath:(nullable NSString *)indexPath
505
526
  includedAttributes:(nullable NSSet<Class> *)includedAttributes
506
527
  {
528
+ return [self recordElementAttributes:writer forElement:element indexPath:indexPath
529
+ includedAttributes:includedAttributes siblingIndex:nil];
530
+ }
531
+
532
+ + (int)recordElementAttributes:(xmlTextWriterPtr)writer
533
+ forElement:(id<FBXCElementSnapshot>)element
534
+ indexPath:(nullable NSString *)indexPath
535
+ includedAttributes:(nullable NSSet<Class> *)includedAttributes
536
+ siblingIndex:(nullable NSNumber *)siblingIndex
537
+ {
538
+ FBXCElementSnapshotWrapper *wrapped = [FBXCElementSnapshotWrapper ensureWrapped:element];
539
+ NSDictionary *rect = nil;
507
540
  for (Class attributeCls in FBElementAttribute.supportedAttributes) {
508
541
  // include all supported attributes by default unless enumerated explicitly
509
542
  if (includedAttributes && ![includedAttributes containsObject:attributeCls]) {
@@ -519,8 +552,15 @@ static NSString *const topNodeIndexPath = @"top";
519
552
  !FBDoesElementSupportMinMaxValue(element.elementType)) {
520
553
  continue;
521
554
  }
522
- int rc = [attributeCls recordWithWriter:writer
523
- forElement:[FBXCElementSnapshotWrapper ensureWrapped:element]];
555
+ int rc;
556
+ if (attributeCls == FBIndexAttribute.class && nil != siblingIndex) {
557
+ rc = [attributeCls recordWithWriter:writer forValue:siblingIndex.stringValue];
558
+ } else if ([attributeCls isSubclassOfClass:FBDimensionAttribute.class]) {
559
+ rect = rect ?: wrapped.wdRect;
560
+ rc = [attributeCls recordWithWriter:writer forValue:[rect[[attributeCls name]] description]];
561
+ } else {
562
+ rc = [attributeCls recordWithWriter:writer forElement:wrapped];
563
+ }
524
564
  if (rc < 0) {
525
565
  return rc;
526
566
  }
@@ -559,6 +599,17 @@ static NSString *const topNodeIndexPath = @"top";
559
599
  elementStore:(nullable NSMutableDictionary *)elementStore
560
600
  includedAttributes:(nullable NSSet<Class> *)includedAttributes
561
601
  writer:(xmlTextWriterPtr)writer
602
+ {
603
+ return [self writeXmlWithRootElement:root indexPath:indexPath elementStore:elementStore
604
+ includedAttributes:includedAttributes writer:writer siblingIndex:nil];
605
+ }
606
+
607
+ + (int)writeXmlWithRootElement:(id<FBXCElementSnapshot>)root
608
+ indexPath:(nullable NSString *)indexPath
609
+ elementStore:(nullable NSMutableDictionary *)elementStore
610
+ includedAttributes:(nullable NSSet<Class> *)includedAttributes
611
+ writer:(xmlTextWriterPtr)writer
612
+ siblingIndex:(nullable NSNumber *)siblingIndex
562
613
  {
563
614
  NSAssert((indexPath == nil && elementStore == nil) || (indexPath != nil && elementStore != nil), @"Either both or none of indexPath and elementStore arguments should be equal to nil", nil);
564
615
 
@@ -578,7 +629,8 @@ static NSString *const topNodeIndexPath = @"top";
578
629
  rc = [self recordElementAttributes:writer
579
630
  forElement:root
580
631
  indexPath:indexPath
581
- includedAttributes:includedAttributes];
632
+ includedAttributes:includedAttributes
633
+ siblingIndex:siblingIndex];
582
634
  if (rc < 0) {
583
635
  return rc;
584
636
  }
@@ -594,7 +646,8 @@ static NSString *const topNodeIndexPath = @"top";
594
646
  indexPath:newIndexPath
595
647
  elementStore:elementStore
596
648
  includedAttributes:includedAttributes
597
- writer:writer];
649
+ writer:writer
650
+ siblingIndex:@(i)];
598
651
  if (rc < 0) {
599
652
  return rc;
600
653
  }
@@ -80,7 +80,7 @@
80
80
  }
81
81
  LRUCacheNode *nextNode = node.next;
82
82
  if (nil != nextNode) {
83
- nextNode.prev = node.prev;
83
+ nextNode.prev = previousNode;
84
84
  }
85
85
  if (node == self.tailNode) {
86
86
  self.tailNode = previousNode;
@@ -94,19 +94,22 @@
94
94
  return;
95
95
  }
96
96
 
97
+ LRUCacheNode *previousNode = node.prev;
97
98
  if (nil != node.next) {
98
- node.next.prev = node.prev;
99
+ node.next.prev = previousNode;
99
100
  }
100
101
  if (node == self.headNode) {
101
102
  self.headNode = node.next;
102
103
  }
103
- if (nil != node.prev) {
104
- node.prev.next = node.next;
104
+ if (nil != previousNode) {
105
+ previousNode.next = node.next;
105
106
  }
106
107
  if (node == self.tailNode) {
107
- self.tailNode = node.prev;
108
+ self.tailNode = previousNode;
108
109
  }
109
110
  [self.store removeObjectForKey:(id)node.key];
111
+ node.next = nil;
112
+ node.prev = nil;
110
113
  }
111
114
 
112
115
  - (nullable LRUCacheNode *)addNodeToHead:(nullable LRUCacheNode *)node
@@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
27
27
  /*! Pointer to the next node */
28
28
  @property (nonatomic, nullable) LRUCacheNode *next;
29
29
  /*! Pointer to the previous node */
30
- @property (nonatomic, nullable) LRUCacheNode *prev;
30
+ @property (nonatomic, weak, nullable) LRUCacheNode *prev;
31
31
 
32
32
  /**
33
33
  Factory method to create a new cache node with the given value and key
@@ -47,6 +47,7 @@ FOUNDATION_EXPORT const unsigned char WebDriverAgentLib_VersionString[];
47
47
  #import <WebDriverAgentLib/XCTIssue+FBPatcher.h>
48
48
  #import <WebDriverAgentLib/XCUIApplication+FBHelpers.h>
49
49
  #import <WebDriverAgentLib/XCUIDevice+FBHelpers.h>
50
+ #import <WebDriverAgentLib/XCUIDevice+FBHinge.h>
50
51
  #import <WebDriverAgentLib/XCUIDevice+FBRotation.h>
51
52
  #import <WebDriverAgentLib/XCUIDevice+FBVoiceOver.h>
52
53
  #import <WebDriverAgentLib/XCUIElement.h>
@@ -21,7 +21,9 @@ shows 80 numbered rows, each 44 points high.
21
21
 
22
22
  The `probe-status` label exposes `start`, `last`, `phase`, and `count` after a
23
23
  touch. `start` and `last` are **canvas-local app points**. It also exposes
24
- `canvasBounds`, `canvasWindowRect`, `windowSize`, `screenSize`, and `scrollY`.
24
+ `canvasBounds`, `canvasWindowRect`, `windowSize`, `screenSize`, `scrollY`, and
25
+ `fullyVisibleRows` (row indices whose full rectangles lie inside the table bounds).
26
+ `rotationInProgress` stays true until the fixture's rotation transition completes.
25
27
  Geometry is refreshed when the view lays out, including after rotation or window
26
28
  resizing. Touch-related fields are absent before the first touch.
27
29
 
@@ -106,6 +106,17 @@
106
106
  [self.view addSubview:self.table];
107
107
  }
108
108
 
109
+ - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
110
+ {
111
+ [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
112
+ self.measurement[@"rotationInProgress"] = @YES;
113
+ [self publishMeasurement];
114
+ [coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
115
+ self.measurement[@"rotationInProgress"] = @NO;
116
+ [self publishMeasurement];
117
+ }];
118
+ }
119
+
109
120
  - (void)viewDidLayoutSubviews
110
121
  {
111
122
  [super viewDidLayoutSubviews];
@@ -139,6 +150,13 @@
139
150
  self.measurement[@"windowSize"] = @[@(window.bounds.size.width), @(window.bounds.size.height)];
140
151
  self.measurement[@"screenSize"] = @[@(window.screen.bounds.size.width), @(window.screen.bounds.size.height)];
141
152
  self.measurement[@"scrollY"] = @(self.table.contentOffset.y);
153
+ NSMutableArray<NSNumber *> *fullyVisibleRows = [NSMutableArray new];
154
+ for (NSIndexPath *indexPath in self.table.indexPathsForVisibleRows) {
155
+ if (CGRectContainsRect(self.table.bounds, [self.table rectForRowAtIndexPath:indexPath])) {
156
+ [fullyVisibleRows addObject:@(indexPath.row)];
157
+ }
158
+ }
159
+ self.measurement[@"fullyVisibleRows"] = fullyVisibleRows;
142
160
  NSData *data = [NSJSONSerialization dataWithJSONObject:self.measurement
143
161
  options:NSJSONWritingSortedKeys
144
162
  error:nil];
@@ -10,7 +10,40 @@
10
10
 
11
11
  @implementation SceneDelegate
12
12
 
13
+ - (void)showIdentifierLookupFixture
14
+ {
15
+ UIViewController *controller = [UIViewController new];
16
+ controller.view.backgroundColor = UIColor.whiteColor;
17
+ controller.view.accessibilityIdentifier = @"IdentifierLookupFixture";
18
+ NSArray *identifiers = @[@"something", @"different", @"", NSNull.null, @"something", @"", NSNull.null, @"quote'\"é", NSNull.null];
19
+ NSArray<NSString *> *labels = @[@"other label", @"something", @"something", @"something", @"something", @"", @"", @"quoted", @"日本語"];
20
+ NSMutableArray<UILabel *> *labelViews = [NSMutableArray array];
21
+ for (NSUInteger i = 0; i < identifiers.count; i++) {
22
+ UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 90 + i * 40, 300, 35)];
23
+ label.text = [NSString stringWithFormat:@"Fixture %lu", (unsigned long)i];
24
+ label.accessibilityIdentifier = identifiers[i] == NSNull.null ? nil : identifiers[i];
25
+ label.accessibilityLabel = labels[i];
26
+ label.isAccessibilityElement = YES;
27
+ [labelViews addObject:label];
28
+ [controller.view addSubview:label];
29
+ }
30
+ UIButton *mutate = [UIButton buttonWithType:UIButtonTypeSystem];
31
+ mutate.frame = CGRectMake(20, 470, 220, 40);
32
+ [mutate setTitle:@"Change identifier" forState:UIControlStateNormal];
33
+ mutate.accessibilityIdentifier = @"mutateIdentifier";
34
+ [mutate addAction:[UIAction actionWithHandler:^(__kindof UIAction *action) {
35
+ labelViews[0].accessibilityIdentifier = @"changed";
36
+ }] forControlEvents:UIControlEventTouchUpInside];
37
+ [controller.view addSubview:mutate];
38
+ self.window.rootViewController = controller;
39
+ [self.window makeKeyAndVisible];
40
+ }
41
+
13
42
  - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
43
+ if ([NSProcessInfo.processInfo.arguments containsObject:@"--identifier-lookup-test"]) {
44
+ [self showIdentifierLookupFixture];
45
+ return;
46
+ }
14
47
  // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
15
48
  // If using a storyboard, the `window` property will automatically be set and attached to the scene.
16
49
  // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession:` instead).
@@ -9,6 +9,10 @@
9
9
  #import "ViewController.h"
10
10
  #import "FBCoordinateProbeViewController.h"
11
11
 
12
+ #if !TARGET_OS_TV
13
+ #import <WebKit/WebKit.h>
14
+ #endif
15
+
12
16
  @interface ViewController ()
13
17
  @property (weak, nonatomic) IBOutlet UILabel *orentationLabel;
14
18
  @property (weak, nonatomic) IBOutlet UIButton *button;
@@ -19,6 +23,17 @@
19
23
  - (void)viewDidLoad
20
24
  {
21
25
  [super viewDidLoad];
26
+
27
+ #if !TARGET_OS_TV
28
+ if ([NSProcessInfo.processInfo.arguments containsObject:@"--webview-fixture"]) {
29
+ WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
30
+ webView.autoresizingMask = UIViewAutoresizingFlexibleWidth
31
+ | UIViewAutoresizingFlexibleHeight;
32
+ [self.view addSubview:webView];
33
+ [webView loadHTMLString:@"<!doctype html><html><body>"
34
+ "<p>WebContent snapshot fixture</p></body></html>" baseURL:nil];
35
+ }
36
+ #endif
22
37
 
23
38
  UIAccessibilityCustomAction *action1 =
24
39
  [[UIAccessibilityCustomAction alloc] initWithName:@"Custom Action 1"
@@ -96,10 +111,20 @@
96
111
  }
97
112
 
98
113
  #if !TARGET_OS_TV
114
+ - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
115
+ {
116
+ [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
117
+ [coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
118
+ // A 180-degree rotation can leave bounds unchanged. Read the scene after
119
+ // the transition as well as during layout.
120
+ [self updateOrentationLabel];
121
+ }];
122
+ }
123
+
99
124
  - (void)updateOrentationLabel
100
125
  {
101
126
  NSString *orientation = nil;
102
- switch (UIDevice.currentDevice.orientation) {
127
+ switch (self.view.window.windowScene.interfaceOrientation) {
103
128
  case UIInterfaceOrientationPortrait:
104
129
  orientation = @"Portrait";
105
130
  break;
@@ -112,12 +137,6 @@
112
137
  case UIInterfaceOrientationLandscapeRight:
113
138
  orientation = @"LandscapeRight";
114
139
  break;
115
- case UIDeviceOrientationFaceUp:
116
- orientation = @"FaceUp";
117
- break;
118
- case UIDeviceOrientationFaceDown:
119
- orientation = @"FaceDown";
120
- break;
121
140
  case UIInterfaceOrientationUnknown:
122
141
  orientation = @"Unknown";
123
142
  break;
@@ -12,6 +12,9 @@
12
12
  #import "FBConfiguration.h"
13
13
  #import "FBRuntimeUtils.h"
14
14
  #import "FBTestMacros.h"
15
+ #import "FBXCAccessibilityElement.h"
16
+ #import "FBXCAXClientProxy.h"
17
+ #import "XCUIApplication.h"
15
18
  #import "XCUIElement.h"
16
19
  #import "XCUIElement+FBIsVisible.h"
17
20
 
@@ -68,4 +71,53 @@
68
71
  }
69
72
  }
70
73
 
74
+ - (void)testAccessibilityDeadlineAllowsResponsiveNativeAppSnapshots
75
+ {
76
+ [self launchApplication];
77
+ NSTimeInterval previousDeadline = FBConfiguration.sharedInstance.accessibilityDeadline;
78
+ @try {
79
+ FBConfiguration.sharedInstance.accessibilityDeadline = 3;
80
+ NSError *error = nil;
81
+ XCTAssertNotNil([self.testedApplication snapshotWithError:&error]);
82
+ XCTAssertNil(error);
83
+ } @finally {
84
+ FBConfiguration.sharedInstance.accessibilityDeadline = previousDeadline;
85
+ [self.testedApplication terminate];
86
+ }
87
+ }
88
+
89
+ - (void)testAccessibilityDeadlineAllowsWebContentSnapshots
90
+ {
91
+ NSTimeInterval previousDeadline = FBConfiguration.sharedInstance.accessibilityDeadline;
92
+ @try {
93
+ // Resolve the remote AX element before enabling the guard so the test
94
+ // exercises a WebContent-owned snapshot, not just the host app's tree.
95
+ FBConfiguration.sharedInstance.accessibilityDeadline = 0;
96
+ self.testedApplication.launchArguments = @[@"--webview-fixture"];
97
+ [self.testedApplication launch];
98
+ XCUIElement *text = self.testedApplication.webViews.staticTexts[
99
+ @"WebContent snapshot fixture"];
100
+ XCTAssertTrue([text waitForExistenceWithTimeout:15]);
101
+ NSError *error = nil;
102
+ id<FBXCElementSnapshot> initial = [text snapshotWithError:&error];
103
+ XCTAssertNotNil(initial);
104
+ XCTAssertNil(error);
105
+ id<FBXCAccessibilityElement> element = initial.accessibilityElement;
106
+ XCTAssertNotEqual(element.processIdentifier, self.testedApplication.processID);
107
+
108
+ FBConfiguration.sharedInstance.accessibilityDeadline = 3;
109
+ for (NSUInteger i = 0; i < 3; i++) {
110
+ error = nil;
111
+ id<FBXCElementSnapshot> snapshot = [FBXCAXClientProxy.sharedClient
112
+ snapshotForElement:element attributes:@[] inDepth:YES error:&error];
113
+ XCTAssertNotNil(snapshot);
114
+ XCTAssertNil(error);
115
+ }
116
+ XCTAssertTrue(text.exists);
117
+ } @finally {
118
+ FBConfiguration.sharedInstance.accessibilityDeadline = previousDeadline;
119
+ [self.testedApplication terminate];
120
+ }
121
+ }
122
+
71
123
  @end