Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add an endpoint for keyboard input #797

Merged
merged 2 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions WebDriverAgentLib/Commands/FBCustomCommands.m
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ + (NSArray *)routes
[[FBRoute GET:@"/wda/device/location"] respondWithTarget:self action:@selector(handleGetLocation:)],
[[FBRoute GET:@"/wda/device/location"].withoutSession respondWithTarget:self action:@selector(handleGetLocation:)],
#if !TARGET_OS_TV // tvOS does not provide relevant APIs
[[FBRoute POST:@"/wda/element/:uuid/keyboardInput"] respondWithTarget:self action:@selector(handleKeyboardInput:)],
[[FBRoute GET:@"/wda/simulatedLocation"] respondWithTarget:self action:@selector(handleGetSimulatedLocation:)],
[[FBRoute GET:@"/wda/simulatedLocation"].withoutSession respondWithTarget:self action:@selector(handleGetSimulatedLocation:)],
[[FBRoute POST:@"/wda/simulatedLocation"] respondWithTarget:self action:@selector(handleSetSimulatedLocation:)],
Expand Down Expand Up @@ -543,6 +544,46 @@ + (NSString *)timeZone
}
return FBResponseWithOK();
}

+ (id<FBResponsePayload>)handleKeyboardInput:(FBRouteRequest *)request
{
FBElementCache *elementCache = request.session.elementCache;
BOOL hasElement = nil != request.parameters[@"uuid"];
XCUIElement *destination = hasElement
? [elementCache elementForUUID:(NSString *)request.parameters[@"uuid"]]
: request.session.activeApplication;
id keys = request.arguments[@"keys"];
if (![keys isKindOfClass:NSArray.class]) {
NSString *message = @"The 'keys' argument must be an array";
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:message
traceback:nil]);
}
for (id item in (NSArray *)keys) {
if ([item isKindOfClass:NSString.class]) {
NSString *keyValue = [FBKeyboard keyValueForName:item] ?: item;
[destination typeKey:keyValue modifierFlags:XCUIKeyModifierNone];
} else if ([item isKindOfClass:NSDictionary.class]) {
id key = [(NSDictionary *)item objectForKey:@"key"];
if (![key isKindOfClass:NSString.class]) {
NSString *message = [NSString stringWithFormat:@"All dictionaries of 'keys' array must have the 'key' item of type string. Got '%@' instead in the item %@", key, item];
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:message
traceback:nil]);
}
id modifiers = [(NSDictionary *)item objectForKey:@"modifierFlags"];
NSUInteger modifierFlags = XCUIKeyModifierNone;
if ([modifiers isKindOfClass:NSNumber.class]) {
modifierFlags = [(NSNumber *)modifiers unsignedIntValue];
}
NSString *keyValue = [FBKeyboard keyValueForName:item] ?: key;
[destination typeKey:keyValue modifierFlags:modifierFlags];
} else {
NSString *message = @"All items of the 'keys' array must be either dictionaries or strings";
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:message
traceback:nil]);
}
}
return FBResponseWithOK();
}
#endif

+ (id<FBResponsePayload>)handlePerformAccessibilityAudit:(FBRouteRequest *)request
Expand Down
10 changes: 10 additions & 0 deletions WebDriverAgentLib/Utilities/FBKeyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ NS_ASSUME_NONNULL_BEGIN

@interface FBKeyboard : NSObject

#if !TARGET_OS_TV
/**
Transforms key name to its string representation, which could be used with XCTest

@param name one of available keyboard key names defined in https://developer.apple.com/documentation/xctest/xcuikeyboardkey?language=objc
@return Either the key value or nil if no matches have been found
*/
+ (nullable NSString *)keyValueForName:(NSString *)name;
#endif

/**
Types a string into active element. There must be element with keyboard focus; otherwise an
error is raised.
Expand Down
65 changes: 65 additions & 0 deletions WebDriverAgentLib/Utilities/FBKeyboard.m
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,69 @@ + (BOOL)waitUntilVisibleForApplication:(XCUIApplication *)app timeout:(NSTimeInt
error:error];
}

#if !TARGET_OS_TV

+ (NSString *)keyValueForName:(NSString *)name
{
static dispatch_once_t onceKeys;
static NSDictionary<NSString *, NSString *> *keysMapping;
dispatch_once(&onceKeys, ^{
keysMapping = @{
@"XCUIKeyboardKeyDelete": XCUIKeyboardKeyDelete,
@"XCUIKeyboardKeyReturn": XCUIKeyboardKeyReturn,
@"XCUIKeyboardKeyEnter": XCUIKeyboardKeyEnter,
@"XCUIKeyboardKeyTab": XCUIKeyboardKeyTab,
@"XCUIKeyboardKeySpace": XCUIKeyboardKeySpace,
@"XCUIKeyboardKeyEscape": XCUIKeyboardKeyEscape,

@"XCUIKeyboardKeyUpArrow": XCUIKeyboardKeyUpArrow,
@"XCUIKeyboardKeyDownArrow": XCUIKeyboardKeyDownArrow,
@"XCUIKeyboardKeyLeftArrow": XCUIKeyboardKeyLeftArrow,
@"XCUIKeyboardKeyRightArrow": XCUIKeyboardKeyRightArrow,

@"XCUIKeyboardKeyF1": XCUIKeyboardKeyF1,
@"XCUIKeyboardKeyF2": XCUIKeyboardKeyF2,
@"XCUIKeyboardKeyF3": XCUIKeyboardKeyF3,
@"XCUIKeyboardKeyF4": XCUIKeyboardKeyF4,
@"XCUIKeyboardKeyF5": XCUIKeyboardKeyF5,
@"XCUIKeyboardKeyF6": XCUIKeyboardKeyF6,
@"XCUIKeyboardKeyF7": XCUIKeyboardKeyF7,
@"XCUIKeyboardKeyF8": XCUIKeyboardKeyF8,
@"XCUIKeyboardKeyF9": XCUIKeyboardKeyF9,
@"XCUIKeyboardKeyF10": XCUIKeyboardKeyF10,
@"XCUIKeyboardKeyF11": XCUIKeyboardKeyF11,
@"XCUIKeyboardKeyF12": XCUIKeyboardKeyF12,
@"XCUIKeyboardKeyF13": XCUIKeyboardKeyF13,
@"XCUIKeyboardKeyF14": XCUIKeyboardKeyF14,
@"XCUIKeyboardKeyF15": XCUIKeyboardKeyF15,
@"XCUIKeyboardKeyF16": XCUIKeyboardKeyF16,
@"XCUIKeyboardKeyF17": XCUIKeyboardKeyF17,
@"XCUIKeyboardKeyF18": XCUIKeyboardKeyF18,
@"XCUIKeyboardKeyF19": XCUIKeyboardKeyF19,

@"XCUIKeyboardKeyForwardDelete": XCUIKeyboardKeyForwardDelete,
@"XCUIKeyboardKeyHome": XCUIKeyboardKeyHome,
@"XCUIKeyboardKeyEnd": XCUIKeyboardKeyEnd,
@"XCUIKeyboardKeyPageUp": XCUIKeyboardKeyPageUp,
@"XCUIKeyboardKeyPageDown": XCUIKeyboardKeyPageDown,
@"XCUIKeyboardKeyClear": XCUIKeyboardKeyClear,
@"XCUIKeyboardKeyHelp": XCUIKeyboardKeyHelp,

@"XCUIKeyboardKeyCapsLock": XCUIKeyboardKeyCapsLock,
@"XCUIKeyboardKeyShift": XCUIKeyboardKeyShift,
@"XCUIKeyboardKeyControl": XCUIKeyboardKeyControl,
@"XCUIKeyboardKeyOption": XCUIKeyboardKeyOption,
@"XCUIKeyboardKeyCommand": XCUIKeyboardKeyCommand,
@"XCUIKeyboardKeyRightShift": XCUIKeyboardKeyRightShift,
@"XCUIKeyboardKeyRightControl": XCUIKeyboardKeyRightControl,
@"XCUIKeyboardKeyRightOption": XCUIKeyboardKeyRightOption,
@"XCUIKeyboardKeyRightCommand": XCUIKeyboardKeyRightCommand,
@"XCUIKeyboardKeySecondaryFn": XCUIKeyboardKeySecondaryFn
};
});
return keysMapping[name];
}

#endif

@end
Loading