appium6:appium查找和点击调用链
1、python文件里从查找元素到点击元素
1
2
3
welcomeStartBtn = WebDriverWait(driver, 2.5).until(
EC.element_to_be_clickable((By.XPATH, '//XCUIElementTypeButton[@label="马上开始"]'))
)
2、appium收到的请求
1
POST /session/c5cf401e-596f-44dc-87fe-49d1969d532a/element {"using":"xpath","value":"//XCUIElementTypeButton[@label=\"马上开始\"]"}
3、XCUITestDriver处理请求
1
2
3
4
5
6
[c5cf401e][XCUITestDriver@e9eb] Calling AppiumDriver.findElement() with args: ["xpath","//XCUIElementTypeButton[@label=\"马上开始\"]","c5cf401e-596f-44dc-87fe-49d1969d532a"]
[c5cf401e][XCUITestDriver@e9eb] Executing command 'findElement'
[c5cf401e][XCUITestDriver@e9eb] Valid locator strategies for this request: xpath, id, name, class name, -ios predicate string, -ios class chain, accessibility id, css selector
[c5cf401e][XCUITestDriver@e9eb] Waiting up to 0 ms for condition
[c5cf401e][XCUITestDriver@e9eb] Matched '/element' to command name 'findElement'
[c5cf401e][XCUITestDriver@e9eb] Proxying [POST /element] to [POST http://127.0.0.1:8100/session/4F15956B-A028-4B39-8BCE-C5D573EA023B/element] with body: {"using":"xpath","value":"//XCUIElementTypeButton[@label=\"马上开始\"]"}
4、wda
的FBFindElementCommands
收到请求处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@implementation FBFindElementCommands
+ (NSArray *)routes
{
return
@[
[[FBRoute POST:@"/element"] respondWithTarget:self action:@selector(handleFindElement:)],
[[FBRoute POST:@"/elements"] respondWithTarget:self action:@selector(handleFindElements:)],
[[FBRoute POST:@"/element/:uuid/element"] respondWithTarget:self action:@selector(handleFindSubElement:)],
[[FBRoute POST:@"/element/:uuid/elements"] respondWithTarget:self action:@selector(handleFindSubElements:)],
[[FBRoute GET:@"/wda/element/:uuid/getVisibleCells"] respondWithTarget:self action:@selector(handleFindVisibleCells:)],
#if TARGET_OS_TV
[[FBRoute GET:@"/element/active"] respondWithTarget:self action:@selector(handleGetFocusedElement:)],
#else
[[FBRoute GET:@"/element/active"] respondWithTarget:self action:@selector(handleGetActiveElement:)],
#endif
];
}
+ (id<FBResponsePayload>)handleFindElement:(FBRouteRequest *)request
{
// activeApplication是根节点
FBSession *session = request.session;
XCUIElement *element = [self.class elementUsing:request.arguments[@"using"]
withValue:request.arguments[@"value"]
under:session.activeApplication];
if (!element) {
return FBNoSuchElementErrorResponseForRequest(request);
}
return FBResponseWithCachedElement(element, request.session.elementCache, FBConfiguration.shouldUseCompactResponses);
}
- (XCUIApplication *)makeApplicationWithBundleId:(NSString *)bundleIdentifier
{
return nil != self.testedApplication && [bundleIdentifier isEqualToString:(NSString *)self.testedApplication.bundleID]
? self.testedApplication
: [[XCUIApplication alloc] initWithBundleIdentifier:bundleIdentifier];
}
[HTTP] –> GET /session/c5cf401e-596f-44dc-87fe-49d1969d532a/element/0E000000-0000-0000-C7FD-000000000000/displayed {}
Proxying [GET /session/c5cf401e-596f-44dc-87fe-49d1969d532a/element/0E000000-0000-0000-C7FD-000000000000/displayed] to [GET http://127.0.0.1:8100/session/4F15956B-A028-4B39-8BCE-C5D573EA023B/element/0E000000-0000-0000-C7FD-000000000000/displayed]
滑动操作
1
[HTTP] --> POST /session/c5cf401e-596f-44dc-87fe-49d1969d532a/actions {"actions":[{"type":"pointer","parameters":{"pointerType":"touch"},"id":"touch","actions":[{"type":"pointerMove","duration":250,"x":375,"y":406,"origin":"viewport"},{"type":"pointerDown","duration":0,"button":0},{"type":"pointerMove","duration":250,"x":0,"y":406,"origin":"viewport"},{"type":"pointerUp","duration":0,"button":0}]}]}
代理执行转发
1
[XCUITestDriver@e9eb] Proxying [POST /actions] to [POST http://127.0.0.1:8100/session/4F15956B-A028-4B39-8BCE-C5D573EA023B/actions] with body: {"actions":[{"type":"pointer","parameters":{"pointerType":"touch"},"id":"touch","actions":[{"type":"pointerMove","duration":250,"x":375,"y":406,"origin":"viewport"},{"type":"pointerDown","duration":0,"button":0},{"type":"pointerMove","duration":250,"x":0,"y":406,"origin":"viewport"},{"type":"pointerUp","duration":0,"button":0}]}]}
WDA
-> FBTouchActionCommands.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+ (NSArray *)routes
{
return
@[
[[FBRoute POST:@"/actions"] respondWithTarget:self action:@selector(handlePerformW3CTouchActions:)],
];
}
+ (id<FBResponsePayload>)handlePerformW3CTouchActions:(FBRouteRequest *)request
{
XCUIApplication *application = request.session.activeApplication;
NSArray *actions = (NSArray *)request.arguments[@"actions"];
NSError *error;
if (![application fb_performW3CActions:actions elementCache:request.session.elementCache error:&error]) {
return FBResponseWithUnknownError(error);
}
return FBResponseWithOK();
}
[XCUIDevice.sharedDevice eventSynthesizer] synthesizeEvent:record
click 点击转发:
1
Proxying [POST /session/c5cf401e-596f-44dc-87fe-49d1969d532a/element/0E000000-0000-0000-C7FD-000000000000/click] to [POST http://127.0.0.1:8100/session/4F15956B-A028-4B39-8BCE-C5D573EA023B/element/0E000000-0000-0000-C7FD-000000000000/click]
WDA->FBElementCommands.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[[FBRoute POST:@"/element/:uuid/click"] respondWithTarget:self action:@selector(handleClick:)],
+ (id<FBResponsePayload>)handleClick:(FBRouteRequest *)request
{
FBElementCache *elementCache = request.session.elementCache;
XCUIElement *element = [elementCache elementForUUID:(NSString *)request.parameters[@"uuid"] checkStaleness:YES];
#if TARGET_OS_IOS
[element tap];
#elif TARGET_OS_TV
NSError *error = nil;
if (![element fb_selectWithError:&error]) {
return FBResponseWithStatus([FBCommandStatus invalidElementStateErrorWithMessage:error.description traceback:nil]);
}
#endif
return FBResponseWithOK();
}
设置内容
1
emailInput.send_keys("a1@test.com")
appium 收到请求
1
[HTTP] --> POST /session/652863ad-9fd4-4b98-b658-e67061595f9f/element/4F000000-0000-0000-4903-010000000000/value {"text":"a1@test.com","value":["a","1","@","t","e","s","t",".","c","o","m"]}
XUITestDriver处理请求并转发
1
2
3
4
5
[652863ad][XCUITestDriver@7e47] Calling AppiumDriver.setValue() with args: [["a","1","@","t","e","s","t",".","c","o","m"],"4F000000-0000-0000-4903-010000000000","652863ad-9fd4-4b98-b658-e67061595f9f"]
[652863ad][XCUITestDriver@7e47] Executing command 'setValue'
[652863ad][XCUITestDriver@7e47] Matched '/element/4F000000-0000-0000-4903-010000000000/value' to command name 'setValue'
[652863ad][XCUITestDriver@7e47] Added 'text' property "a1@test.com" to 'setValue' request body
[652863ad][XCUITestDriver@7e47] Proxying [POST /element/4F000000-0000-0000-4903-010000000000/value] to [POST http://127.0.0.1:8100/session/E932FD60-FD2E-4BEF-B8BC-61A3DB079C2F/element/4F000000-0000-0000-4903-010000000000/value] with body: {"value":["a","1","@","t","e","s","t",".","c","o","m"],"text":"a1@test.com"}
WDA -> FBElementCommands.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
[[FBRoute POST:@"/element/:uuid/value"] respondWithTarget:self action:@selector(handleSetValue:)],
+ (id<FBResponsePayload>)handleSetValue:(FBRouteRequest *)request
{
FBElementCache *elementCache = request.session.elementCache;
XCUIElement *element = [elementCache elementForUUID:(NSString *)request.parameters[@"uuid"]
checkStaleness:YES];
id value = request.arguments[@"value"] ?: request.arguments[@"text"];
if (!value) {
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:@"Neither 'value' nor 'text' parameter is provided" traceback:nil]);
}
NSString *textToType = [value isKindOfClass:NSArray.class]
? [value componentsJoinedByString:@""]
: value;
XCUIElementType elementType = [element elementType];
#if !TARGET_OS_TV
if (elementType == XCUIElementTypePickerWheel) {
[element adjustToPickerWheelValue:textToType];
return FBResponseWithOK();
}
#endif
if (elementType == XCUIElementTypeSlider) {
CGFloat sliderValue = textToType.floatValue;
if (sliderValue < 0.0 || sliderValue > 1.0 ) {
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:@"Value of slider should be in 0..1 range" traceback:nil]);
}
[element adjustToNormalizedSliderPosition:sliderValue];
return FBResponseWithOK();
}
NSUInteger frequency = (NSUInteger)[request.arguments[@"frequency"] longLongValue] ?: [FBConfiguration maxTypingFrequency];
NSError *error = nil;
if (![element fb_typeText:textToType
shouldClear:NO
frequency:frequency
error:&error]) {
return FBResponseWithStatus([FBCommandStatus invalidElementStateErrorWithMessage:error.description traceback:nil]);
}
return FBResponseWithOK();
}
// 调用系统方法
BOOL FBTypeText(NSString *text, NSUInteger typingSpeed, NSError **error)
{
NSString *name = text.length <= MAX_TEXT_ABBR_LEN
? [NSString stringWithFormat:@"Type '%@'", text]
: [NSString stringWithFormat:@"Type '%@…'", [text substringToIndex:MAX_TEXT_ABBR_LEN]];
XCSynthesizedEventRecord *eventRecord = [[XCSynthesizedEventRecord alloc] initWithName:name];
XCPointerEventPath *ep = [[XCPointerEventPath alloc] initForTextInput];
[ep typeText:text
atOffset:0.0
typingSpeed:typingSpeed
shouldRedact:NO];
[eventRecord addPointerEventPath:ep];
return [FBXCTestDaemonsProxy synthesizeEventWithRecord:eventRecord error:error];
}
调用卸载、安装app的流程
模拟器 https://github.com/appium/appium/tree/master https://github.com/appium/appium-xcuitest-driver https://github.com/appium/appium-ios-simulator https://github.com/appium/node-simctl
真机 https://github.com/appium/appium/tree/master https://github.com/appium/appium-xcuitest-driver https://github.com/appium/appium-ios-device 调用各个服务 real-device.js services.startInstallationProxyService AfcService com.apple.crashreportcopymobile
调用真机或模拟器的device初始化 determineDevice() const device = await getSimulator(this.opts.udid) {getSimulator} from ‘appium-ios-simulator’; const device = new RealDevice(this.opts.udid, this.log); appium-ios-device
https://github.com/appium/appium-xcuitest-driver/blob/master/lib/simulator-management.js -> https://github.com/appium/appium-ios-device -> https://github.com/appium/appium-ios-simulator/blob/master/lib/extensions/applications.js -> https://github.com/appium/node-simctl/blob/master/lib/simctl.js -> https://github.com/appium/node-simctl/blob/master/lib/subcommands/uninstall.js -> this.exec(‘uninstall’,…) -> https://github.com/appium/node-simctl/blob/master/lib/simctl.js -> ‘simctl xxxx’ -> xcrun simctl xxxxx
xcrun simctl uninstall booted com.example.myapp
xcrun simctl uninstall ABCD-1234-5678-90EF com.example.myapp
安装同理 -> https://github.com/appium/node-simctl/blob/master/lib/subcommands/install.js -> this.exec(‘install’,…) -> https://github.com/appium/node-simctl/blob/master/lib/simctl.js -> ‘simctl xxxx’ xcrun simctl install ABCD-1234-5678-90EF /xx/xx.ipa|app