appium-webdriveragent 5.4.0 → 5.5.0

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [5.5.0](https://github.com/appium/WebDriverAgent/compare/v5.4.1...v5.5.0) (2023-06-12)
2
+
3
+
4
+ ### Features
5
+
6
+ * Add accessibility audit extension ([#727](https://github.com/appium/WebDriverAgent/issues/727)) ([78321dd](https://github.com/appium/WebDriverAgent/commit/78321dd3dafdb142eed136b48ec101f1daed50a4))
7
+
8
+ ## [5.4.1](https://github.com/appium/WebDriverAgent/compare/v5.4.0...v5.4.1) (2023-06-09)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * Return default testmanagerd version if the info is not available ([#728](https://github.com/appium/WebDriverAgent/issues/728)) ([e6e2dbd](https://github.com/appium/WebDriverAgent/commit/e6e2dbd86fc0c48ae146905f0e69a6223360e856))
14
+
1
15
  ## [5.4.0](https://github.com/appium/WebDriverAgent/compare/v5.3.3...v5.4.0) (2023-06-09)
2
16
 
3
17
 
@@ -103,6 +103,26 @@ NS_ASSUME_NONNULL_BEGIN
103
103
  - (BOOL)fb_dismissKeyboardWithKeyNames:(nullable NSArray<NSString *> *)keyNames
104
104
  error:(NSError **)error;
105
105
 
106
+ /**
107
+ A wrapper over https://developer.apple.com/documentation/xctest/xcuiapplication/4190847-performaccessibilityauditwithaud?language=objc
108
+
109
+ @param auditTypes Combination of https://developer.apple.com/documentation/xctest/xcuiaccessibilityaudittype?language=objc
110
+ @param error If there is an error, upon return contains an NSError object that describes the problem.
111
+ @return List of found issues or nil if there was a failure
112
+ */
113
+ - (nullable NSArray<NSDictionary<NSString *, NSString*> *> *)fb_performAccessibilityAuditWithAuditTypesSet:(NSSet<NSString *> *)auditTypes
114
+ error:(NSError **)error;
115
+
116
+ /**
117
+ A wrapper over https://developer.apple.com/documentation/xctest/xcuiapplication/4190847-performaccessibilityauditwithaud?language=objc
118
+
119
+ @param auditTypes Combination of https://developer.apple.com/documentation/xctest/xcuiaccessibilityaudittype?language=objc
120
+ @param error If there is an error, upon return contains an NSError object that describes the problem.
121
+ @return List of found issues or nil if there was a failure
122
+ */
123
+ - (nullable NSArray<NSDictionary<NSString *, NSString*> *> *)fb_performAccessibilityAuditWithAuditTypes:(uint64_t)auditTypes
124
+ error:(NSError **)error;
125
+
106
126
  @end
107
127
 
108
128
  NS_ASSUME_NONNULL_END
@@ -12,6 +12,7 @@
12
12
  #import "FBElementTypeTransformer.h"
13
13
  #import "FBKeyboard.h"
14
14
  #import "FBLogger.h"
15
+ #import "FBExceptions.h"
15
16
  #import "FBMacros.h"
16
17
  #import "FBMathUtils.h"
17
18
  #import "FBActiveAppDetectionPoint.h"
@@ -33,6 +34,54 @@
33
34
 
34
35
  static NSString* const FBUnknownBundleId = @"unknown";
35
36
 
37
+ _Nullable id extractIssueProperty(id issue, NSString *propertyName) {
38
+ SEL selector = NSSelectorFromString(propertyName);
39
+ NSMethodSignature *methodSignature = [issue methodSignatureForSelector:selector];
40
+ if (nil == methodSignature) {
41
+ return nil;
42
+ }
43
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
44
+ [invocation setSelector:selector];
45
+ [invocation invokeWithTarget:issue];
46
+ id __unsafe_unretained result;
47
+ [invocation getReturnValue:&result];
48
+ return result;
49
+ }
50
+
51
+ NSDictionary<NSString *, NSNumber *> *auditTypeNamesToValues(void) {
52
+ static dispatch_once_t onceToken;
53
+ static NSDictionary *result;
54
+ dispatch_once(&onceToken, ^{
55
+ // https://developer.apple.com/documentation/xctest/xcuiaccessibilityaudittype?language=objc
56
+ result = @{
57
+ @"XCUIAccessibilityAuditTypeAction": @(1UL << 32),
58
+ @"XCUIAccessibilityAuditTypeAll": @(~0UL),
59
+ @"XCUIAccessibilityAuditTypeContrast": @(1UL << 0),
60
+ @"XCUIAccessibilityAuditTypeDynamicType": @(1UL << 16),
61
+ @"XCUIAccessibilityAuditTypeElementDetection": @(1UL << 1),
62
+ @"XCUIAccessibilityAuditTypeHitRegion": @(1UL << 2),
63
+ @"XCUIAccessibilityAuditTypeParentChild": @(1UL << 33),
64
+ @"XCUIAccessibilityAuditTypeSufficientElementDescription": @(1UL << 3),
65
+ @"XCUIAccessibilityAuditTypeTextClipped": @(1UL << 17),
66
+ @"XCUIAccessibilityAuditTypeTrait": @(1UL << 18),
67
+ };
68
+ });
69
+ return result;
70
+ }
71
+
72
+ NSDictionary<NSNumber *, NSString *> *auditTypeValuesToNames(void) {
73
+ static dispatch_once_t onceToken;
74
+ static NSDictionary *result;
75
+ dispatch_once(&onceToken, ^{
76
+ NSMutableDictionary *inverted = [NSMutableDictionary new];
77
+ [auditTypeNamesToValues() enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSNumber *value, BOOL *stop) {
78
+ inverted[value] = key;
79
+ }];
80
+ result = inverted.copy;
81
+ });
82
+ return result;
83
+ }
84
+
36
85
 
37
86
  @implementation XCUIApplication (FBHelpers)
38
87
 
@@ -281,4 +330,59 @@ static NSString* const FBUnknownBundleId = @"unknown";
281
330
  error:error];
282
331
  }
283
332
 
333
+ - (NSArray<NSDictionary<NSString *, NSString*> *> *)fb_performAccessibilityAuditWithAuditTypesSet:(NSSet<NSString *> *)auditTypes
334
+ error:(NSError **)error;
335
+ {
336
+ uint64_t numTypes = 0;
337
+ NSDictionary *namesMap = auditTypeNamesToValues();
338
+ for (NSString *value in auditTypes) {
339
+ NSNumber *typeValue = namesMap[value];
340
+ if (nil == typeValue) {
341
+ NSString *reason = [NSString stringWithFormat:@"Audit type value '%@' is not known. Only the following audit types are supported: %@", value, namesMap.allKeys];
342
+ @throw [NSException exceptionWithName:FBInvalidArgumentException reason:reason userInfo:@{}];
343
+ }
344
+ numTypes |= [typeValue unsignedLongLongValue];
345
+ }
346
+ return [self fb_performAccessibilityAuditWithAuditTypes:numTypes error:error];
347
+ }
348
+
349
+ - (NSArray<NSDictionary<NSString *, NSString*> *> *)fb_performAccessibilityAuditWithAuditTypes:(uint64_t)auditTypes
350
+ error:(NSError **)error;
351
+ {
352
+ SEL selector = NSSelectorFromString(@"performAccessibilityAuditWithAuditTypes:issueHandler:error:");
353
+ if (![self respondsToSelector:selector]) {
354
+ [[[FBErrorBuilder alloc]
355
+ withDescription:@"Accessibility audit is only supported since iOS 17/Xcode 15"]
356
+ buildError:error];
357
+ return nil;
358
+ }
359
+
360
+ NSMutableArray<NSDictionary *> *resultArray = [NSMutableArray array];
361
+ NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector];
362
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
363
+ [invocation setSelector:selector];
364
+ [invocation setArgument:&auditTypes atIndex:2];
365
+ BOOL (^issueHandler)(id) = ^BOOL(id issue) {
366
+ NSString *auditType = @"";
367
+ NSDictionary *valuesToNamesMap = auditTypeValuesToNames();
368
+ NSNumber *auditTypeValue = [issue valueForKey:@"auditType"];
369
+ if (nil != auditTypeValue) {
370
+ auditType = valuesToNamesMap[auditTypeValue] ?: [auditTypeValue stringValue];
371
+ }
372
+ [resultArray addObject:@{
373
+ @"detailedDescription": extractIssueProperty(issue, @"detailedDescription") ?: @"",
374
+ @"compactDescription": extractIssueProperty(issue, @"compactDescription") ?: @"",
375
+ @"auditType": auditType,
376
+ @"element": [extractIssueProperty(issue, @"element") description] ?: @"",
377
+ }];
378
+ return YES;
379
+ };
380
+ [invocation setArgument:&issueHandler atIndex:3];
381
+ [invocation setArgument:&error atIndex:4];
382
+ [invocation invokeWithTarget:self];
383
+ BOOL isSuccessful;
384
+ [invocation getReturnValue:&isSuccessful];
385
+ return isSuccessful ? resultArray.copy : nil;
386
+ }
387
+
284
388
  @end
@@ -58,6 +58,7 @@
58
58
  [[FBRoute GET:@"/wda/batteryInfo"] respondWithTarget:self action:@selector(handleGetBatteryInfo:)],
59
59
  #endif
60
60
  [[FBRoute POST:@"/wda/pressButton"] respondWithTarget:self action:@selector(handlePressButtonCommand:)],
61
+ [[FBRoute POST:@"/wda/performAccessibilityAudit"] respondWithTarget:self action:@selector(handlePerformAccessibilityAudit:)],
61
62
  [[FBRoute POST:@"/wda/performIoHidEvent"] respondWithTarget:self action:@selector(handlePeformIOHIDEvent:)],
62
63
  [[FBRoute POST:@"/wda/expectNotification"] respondWithTarget:self action:@selector(handleExpectNotification:)],
63
64
  [[FBRoute POST:@"/wda/siri/activate"] respondWithTarget:self action:@selector(handleActivateSiri:)],
@@ -544,4 +545,23 @@
544
545
  }
545
546
  #endif
546
547
 
548
+ + (id<FBResponsePayload>)handlePerformAccessibilityAudit:(FBRouteRequest *)request
549
+ {
550
+ NSError *error;
551
+ NSArray *requestedTypes = request.arguments[@"auditTypes"];
552
+ NSMutableSet *typesSet = [NSMutableSet set];
553
+ if (nil == requestedTypes || 0 == [requestedTypes count]) {
554
+ [typesSet addObject:@"XCUIAccessibilityAuditTypeAll"];
555
+ } else {
556
+ [typesSet addObjectsFromArray:requestedTypes];
557
+ }
558
+ NSArray *result = [request.session.activeApplication fb_performAccessibilityAuditWithAuditTypesSet:typesSet.copy
559
+ error:&error];
560
+ if (nil == result) {
561
+ return FBResponseWithStatus([FBCommandStatus unknownErrorWithMessage:error.description
562
+ traceback:nil]);
563
+ }
564
+ return FBResponseWithObject(result);
565
+ }
566
+
547
567
  @end
@@ -11,6 +11,7 @@
11
11
 
12
12
  #import "FBXCAccessibilityElement.h"
13
13
  #import "FBLogger.h"
14
+ #import "FBExceptions.h"
14
15
  #import "FBRunLoopSpinner.h"
15
16
  #import "FBMacros.h"
16
17
  #import "FBActiveAppDetectionPoint.h"
@@ -123,12 +123,16 @@ NSInteger FBTestmanagerdVersion(void)
123
123
  static NSInteger testmanagerdVersion;
124
124
  dispatch_once(&getTestmanagerdVersion, ^{
125
125
  id<XCTestManager_ManagerInterface> proxy = [FBXCTestDaemonsProxy testRunnerProxy];
126
- dispatch_semaphore_t sem = dispatch_semaphore_create(0);
127
- [proxy _XCT_exchangeProtocolVersion:testmanagerdVersion reply:^(unsigned long long code) {
128
- testmanagerdVersion = (NSInteger) code;
129
- dispatch_semaphore_signal(sem);
130
- }];
131
- dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)));
126
+ if ([(NSObject *)proxy respondsToSelector:@selector(_XCT_exchangeProtocolVersion:reply:)]) {
127
+ [FBRunLoopSpinner spinUntilCompletion:^(void(^completion)(void)){
128
+ [proxy _XCT_exchangeProtocolVersion:testmanagerdVersion reply:^(unsigned long long code) {
129
+ testmanagerdVersion = (NSInteger) code;
130
+ completion();
131
+ }];
132
+ }];
133
+ } else {
134
+ testmanagerdVersion = 0xFFFF;
135
+ }
132
136
  });
133
137
  return testmanagerdVersion;
134
138
  }
@@ -14,6 +14,7 @@
14
14
  #import "FBApplication.h"
15
15
  #import "FBIntegrationTestCase.h"
16
16
  #import "FBElement.h"
17
+ #import "FBMacros.h"
17
18
  #import "FBTestMacros.h"
18
19
  #import "XCUIApplication+FBHelpers.h"
19
20
  #import "XCUIElement+FBIsVisible.h"
@@ -94,4 +95,24 @@
94
95
  XCTAssertGreaterThan(FBTestmanagerdVersion(), 0);
95
96
  }
96
97
 
98
+ - (void)testAccessbilityAudit
99
+ {
100
+ if (SYSTEM_VERSION_LESS_THAN(@"17.0")) {
101
+ return;
102
+ }
103
+
104
+ NSError *error;
105
+ NSArray *auditIssues1 = [FBApplication.fb_activeApplication fb_performAccessibilityAuditWithAuditTypes:~0UL
106
+ error:&error];
107
+ XCTAssertNotNil(auditIssues1);
108
+ XCTAssertNil(error);
109
+
110
+ NSMutableSet *set = [NSMutableSet new];
111
+ [set addObject:@"XCUIAccessibilityAuditTypeAll"];
112
+ NSArray *auditIssues2 = [FBApplication.fb_activeApplication fb_performAccessibilityAuditWithAuditTypesSet:set.copy
113
+ error:&error];
114
+ XCTAssertEqualObjects(auditIssues1, auditIssues2);
115
+ XCTAssertNil(error);
116
+ }
117
+
97
118
  @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-webdriveragent",
3
- "version": "5.4.0",
3
+ "version": "5.5.0",
4
4
  "description": "Package bundling WebDriverAgent",
5
5
  "main": "./build/index.js",
6
6
  "scripts": {