Skip to content

Conversation

hanneshauer
Copy link

@hanneshauer hanneshauer commented May 23, 2020

Problem

Appium-clients can't use the iOS Class Chain and iOS Predicate String queries because the strings WDA expects don't match the strings the clients send.

Expected behaviour

When the client (I'm using the Python-client, but behaviour in other clients should be the same) requests to find elements using a class chain or predicate string WebElements matching the query should be returned if any are found.

>>> s.wd.find_element_by_ios_class_chain('XCUIElementTypeAny')
# Sent request: URL: http://10.10.10.120:8100/session/B2C73E2E-0FDE-44FF-8321-A193F36BE13E/element, data: {"using": "-ios class chain", "value": "XCUIElementTypeAny"}
<appium.webdriver.webelement.WebElement (session="B2C73E2E-0FDE-44FF-8321-A193F36BE13E", element="04000000-0000-0000-813C-000000000000")>
>>> s.wd.find_element_by_ios_predicate('visible == 1')
# Sent request: URL: http://10.10.10.120:8100/session/B2C73E2E-0FDE-44FF-8321-A193F36BE13E/element, data: {"using": "-ios predicate string", "value": "visible == 1"}
<appium.webdriver.webelement.WebElement (session="B2C73E2E-0FDE-44FF-8321-A193F36BE13E", element="01000000-0000-0000-813C-000000000000")>

Actual behaviour

Requesting to find elements results in an error because the WDA doesn't know how to handle the strings sent by the client.

>>> s.wd.find_element_by_ios_class_chain('XCUIElementTypeAny')
# Sent request: URL: http://10.10.10.120:8100/session/C953E926-BEC0-4A35-9D6A-ABEAECB6C18E/element, data: {"using": "-ios class chain", "value": "XCUIElementTypeAny"}
response value: {
  "value" : {
    "error" : "invalid argument",
    "message" : "Invalid locator requested: -ios class chain",
    "traceback" : "(\n\t0   CoreFoundation                      0x00000001899969a4 <redacted> + 252\n\t1   libobjc.A.dylib                     0x0000000188b6f9f8 objc_exception_throw + 56\n\t2   CoreFoundation                      0x00000001898b3e10 <redacted> + 0\n\t3   WebDriverAgentLib                   0x0000000102b85a50 +[FBFindElementCommands elementsUsing:withValue:under:shouldReturnAfterFirstMatch:] + 1412\n\t4   WebDriverAgentLib                   0x0000000102b85468 +[FBFindElementCommands elementUsing:withValue:under:] + 148\n\t5   WebDriverAgentLib                   0x0000000102b8419c +[FBFindElementCommands handleFindElement:] + 380\n\t6   WebDriverAgentLib                   0x0000000102b6304c -[FBRoute_TargetAction mountRequest:intoResponse:] + 212\n\t7   WebDriverAgentLib                   0x0000000102b4cc44 __37-[FBWebServer registerRouteHandlers:]_block_invoke + 492\n\t8   RoutingHTTPServer                   0x0000000102c6e490 -[RoutingHTTPServer handleRoute:withRequest:response:] + 144\n\t9   RoutingHTTPServer                   0x0000000102c6ec50 __72-[RoutingHTTPServer routeMethod:withPath:parameters:request:connection:]_block_invoke + 44\n\t10  libdispatch.dylib                   0x00000001893d57d4 <redacted> + 16\n\t11  libdispatch.dylib                   0x0000000189383cec <redacted> + 92\n\t12  libdispatch.dylib                   0x00000001893d57d4 <redacted> + 16\n\t13  libdispatch.dylib                   0x0000000189383008 <redacted> + 1068\n\t14  CoreFoundation                      0x000000018992832c <redacted> + 12\n\t15  CoreFoundation                      0x0000000189923264 <redacted> + 1924\n\t16  CoreFoundation                      0x00000001899227c0 CFRunLoopRunSpecific + 436\n\t17  Foundation                          0x000000018a2f0eac <redacted> + 300\n\t18  WebDriverAgentLib                   0x0000000102b4b6fc -[FBWebServer startServing] + 440\n\t19  WebDriverAgentRunner                0x00000001028e7bac -[UITestingUITests testRunner] + 116\n\t20  CoreFoundation                      0x000000018999e010 <redacted> + 144\n\t21  CoreFoundation                      0x000000018987fbe0 <redacted> + 292\n\t22  XCTest                              0x00000001010c55f4 __24-[XCTestCase invokeTest]_block_invoke_3 + 52\n\t23  XCTest                              0x00000001010c55a4 __24-[XCTestCase invokeTest]_block_invoke_2 + 312\n\t24  XCTest                              0x00000001011353c0 -[XCTMemoryChecker _assertInvalidObjectsDeallocatedAfterScope:] + 68\n\t25  XCTest                              0x00000001010cffe0 -[XCTestCase assertInvalidObjectsDeallocatedAfterScope:] + 112\n\t26  XCTest                              0x00000001010c543c __24-[XCTestCase invokeTest]_block_invoke.206 + 200\n\t27  XCTest                              0x000000010111c0d8 +[XCTestCase(Failures) performFailableBlock:testCase:testCaseRun:shouldInterruptTest:] + 84\n\t28  XCTest                              0x000000010111bfdc -[XCTestCase(Failures) _performTurningExceptionsIntoFailuresInterruptAfterHandling:block:] + 132\n\t29  XCTest                              0x00000001010c4ef0 -[XCTestCase invokeTest] + 1120\n\t30  XCTest                              0x00000001010c68b8 __26-[XCTestCase performTest:]_block_invoke_2 + 44\n\t31  XCTest                              0x000000010111c0d8 +[XCTestCase(Failures) performFailableBlock:testCase:testCaseRun:shouldInterruptTest:] + 84\n\t32  XCTest                              0x000000010111bfdc -[XCTestCase(Failures) _performTurningExceptionsIntoFailuresInterruptAfterHandling:block:] + 132\n\t33  XCTest                              0x00000001010c67f0 __26-[XCTestCase performTest:]_block_invoke.366 + 96\n\t34  XCTest                              0x000000010112e48c +[XCTContext runInContextForTestCase:block:] + 212\n\t35  XCTest                              0x00000001010c60dc -[XCTestCase performTest:] + 540\n\t36  XCTest                              0x000000010110986c -[XCTest runTest] + 60\n\t37  XCTest                              0x00000001010c05c8 __27-[XCTestSuite performTest:]_block_invoke + 268\n\t38  XCTest                              0x00000001010bfe58 __59-[XCTestSuite _performProtectedSectionForTest:testSection:]_block_invoke + 40\n\t39  XCTest                              0x000000010112e48c +[XCTContext runInContextForTestCase:block:] + 212\n\t40  XCTest                              0x00000001010bfe00 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 156\n\t41  XCTest                              0x00000001010c0150 -[XCTestSuite performTest:] + 320\n\t42  XCTest                              0x000000010110986c -[XCTest runTest] + 60\n\t43  XCTest                              0x00000001010c05c8 __27-[XCTestSuite performTest:]_block_invoke + 268\n\t44  XCTest                              0x00000001010bfe58 __59-[XCTestSuite _performProtectedSectionForTest:testSection:]_block_invoke + 40\n\t45  XCTest                              0x000000010112e48c +[XCTContext runInContextForTestCase:block:] + 212\n\t46  XCTest                              0x00000001010bfe00 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 156\n\t47  XCTest                              0x00000001010c0150 -[XCTestSuite performTest:] + 320\n\t48  XCTest                              0x000000010110986c -[XCTest runTest] + 60\n\t49  XCTest                              0x00000001010c05c8 __27-[XCTestSuite performTest:]_block_invoke + 268\n\t50  XCTest                              0x00000001010bfe58 __59-[XCTestSuite _performProtectedSectionForTest:testSection:]_block_invoke + 40\n\t51  XCTest                              0x000000010112e48c +[XCTContext runInContextForTestCase:block:] + 212\n\t52  XCTest                              0x00000001010bfe00 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 156\n\t53  XCTest                              0x00000001010c0150 -[XCTestSuite performTest:] + 320\n\t54  XCTest                              0x000000010110986c -[XCTest runTest] + 60\n\t55  XCTest                              0x00000001011405f0 __44-[XCTTestRunSession runTestsAndReturnError:]_block_invoke + 176\n\t56  XCTest                              0x00000001011406f0 __44-[XCTTestRunSession runTestsAndReturnError:]_block_invoke.100 + 108\n\t57  XCTest                              0x00000001010da354 -[XCTestObservationCenter _observeTestExecutionForBlock:] + 588\n\t58  XCTest                              0x0000000101140388 -[XCTTestRunSession runTestsAndReturnError:] + 592\n\t59  XCTest                              0x00000001010a4bbc -[XCTestDriver runTestsAndReturnError:] + 428\n\t60  XCTest                              0x000000010112a424 _XCTestMain + 2396\n\t61  WebDriverAgentRunner-Runner         0x0000000100bdb818 -[_XCTRunnerAppDelegate applicationWillResignActive:] + 0\n\t62  WebDriverAgentRunner-Runner         0x0000000100bdb720 _XCTRunnerRunTests + 0\n\t63  CoreFoundation                      0x0000000189928578 <redacted> + 20\n\t64  CoreFoundation                      0x0000000189927e7c <redacted> + 272\n\t65  CoreFoundation                      0x0000000189922ee0 <redacted> + 1024\n\t66  CoreFoundation                      0x00000001899227c0 CFRunLoopRunSpecific + 436\n\t67  GraphicsServices                    0x000000018bb2379c GSEventRunModal + 104\n\t68  UIKitCore                           0x00000001b62a1c38 UIApplicationMain + 212\n\t69  WebDriverAgentRunner-Runner         0x0000000100bdb9e4 main + 192\n\t70  libdyld.dylib                       0x00000001893e68e0 <redacted> + 4\n)"
  },
  "sessionId" : "C953E926-BEC0-4A35-9D6A-ABEAECB6C18E"
}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "xxx/env/lib/python3.7/site-packages/appium/webdriver/extensions/search_context/ios.py", line 104, in find_element_by_ios_class_chain
    return self.find_element(by=MobileBy.IOS_CLASS_CHAIN, value=class_chain_string)
  File "xxx/env/lib/python3.7/site-packages/appium/webdriver/webdriver.py", line 284, in find_element
    'value': value})['value']
  File "xxx/env/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "xxx/env/lib/python3.7/site-packages/appium/webdriver/errorhandler.py", line 24, in check_response
    super(MobileErrorHandler, self).check_response(response)
  File "xxx/env/lib/python3.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 131, in check_response
    status = value["status"]
KeyError: 'status'
>>> s.wd.find_element_by_ios_predicate('visible == 1')
Sent request: URL: http://10.10.10.120:8100/session/C953E926-BEC0-4A35-9D6A-ABEAECB6C18E/element, data: {"using": "-ios predicate string", "value": "visible == 1"}
response value: {
  "value" : {
    "error" : "invalid argument",
    "message" : "Invalid locator requested: -ios predicate string",
    "traceback" : "(\n\t0   CoreFoundation                      0x00000001899969a4 <redacted> + 252\n\t1   libobjc.A.dylib                     0x0000000188b6f9f8 objc_exception_throw + 56\n\t2   CoreFoundation                      0x00000001898b3e10 <redacted> + 0\n\t3   WebDriverAgentLib                   0x0000000102b85a50 +[FBFindElementCommands elementsUsing:withValue:under:shouldReturnAfterFirstMatch:] + 1412\n\t4   WebDriverAgentLib                   0x0000000102b85468 +[FBFindElementCommands elementUsing:withValue:under:] + 148\n\t5   WebDriverAgentLib                   0x0000000102b8419c +[FBFindElementCommands handleFindElement:] + 380\n\t6   WebDriverAgentLib                   0x0000000102b6304c -[FBRoute_TargetAction mountRequest:intoResponse:] + 212\n\t7   WebDriverAgentLib                   0x0000000102b4cc44 __37-[FBWebServer registerRouteHandlers:]_block_invoke + 492\n\t8   RoutingHTTPServer                   0x0000000102c6e490 -[RoutingHTTPServer handleRoute:withRequest:response:] + 144\n\t9   RoutingHTTPServer                   0x0000000102c6ec50 __72-[RoutingHTTPServer routeMethod:withPath:parameters:request:connection:]_block_invoke + 44\n\t10  libdispatch.dylib                   0x00000001893d57d4 <redacted> + 16\n\t11  libdispatch.dylib                   0x0000000189383cec <redacted> + 92\n\t12  libdispatch.dylib                   0x00000001893d57d4 <redacted> + 16\n\t13  libdispatch.dylib                   0x0000000189383008 <redacted> + 1068\n\t14  CoreFoundation                      0x000000018992832c <redacted> + 12\n\t15  CoreFoundation                      0x0000000189923264 <redacted> + 1924\n\t16  CoreFoundation                      0x00000001899227c0 CFRunLoopRunSpecific + 436\n\t17  Foundation                          0x000000018a2f0eac <redacted> + 300\n\t18  WebDriverAgentLib                   0x0000000102b4b6fc -[FBWebServer startServing] + 440\n\t19  WebDriverAgentRunner                0x00000001028e7bac -[UITestingUITests testRunner] + 116\n\t20  CoreFoundation                      0x000000018999e010 <redacted> + 144\n\t21  CoreFoundation                      0x000000018987fbe0 <redacted> + 292\n\t22  XCTest                              0x00000001010c55f4 __24-[XCTestCase invokeTest]_block_invoke_3 + 52\n\t23  XCTest                              0x00000001010c55a4 __24-[XCTestCase invokeTest]_block_invoke_2 + 312\n\t24  XCTest                              0x00000001011353c0 -[XCTMemoryChecker _assertInvalidObjectsDeallocatedAfterScope:] + 68\n\t25  XCTest                              0x00000001010cffe0 -[XCTestCase assertInvalidObjectsDeallocatedAfterScope:] + 112\n\t26  XCTest                              0x00000001010c543c __24-[XCTestCase invokeTest]_block_invoke.206 + 200\n\t27  XCTest                              0x000000010111c0d8 +[XCTestCase(Failures) performFailableBlock:testCase:testCaseRun:shouldInterruptTest:] + 84\n\t28  XCTest                              0x000000010111bfdc -[XCTestCase(Failures) _performTurningExceptionsIntoFailuresInterruptAfterHandling:block:] + 132\n\t29  XCTest                              0x00000001010c4ef0 -[XCTestCase invokeTest] + 1120\n\t30  XCTest                              0x00000001010c68b8 __26-[XCTestCase performTest:]_block_invoke_2 + 44\n\t31  XCTest                              0x000000010111c0d8 +[XCTestCase(Failures) performFailableBlock:testCase:testCaseRun:shouldInterruptTest:] + 84\n\t32  XCTest                              0x000000010111bfdc -[XCTestCase(Failures) _performTurningExceptionsIntoFailuresInterruptAfterHandling:block:] + 132\n\t33  XCTest                              0x00000001010c67f0 __26-[XCTestCase performTest:]_block_invoke.366 + 96\n\t34  XCTest                              0x000000010112e48c +[XCTContext runInContextForTestCase:block:] + 212\n\t35  XCTest                              0x00000001010c60dc -[XCTestCase performTest:] + 540\n\t36  XCTest                              0x000000010110986c -[XCTest runTest] + 60\n\t37  XCTest                              0x00000001010c05c8 __27-[XCTestSuite performTest:]_block_invoke + 268\n\t38  XCTest                              0x00000001010bfe58 __59-[XCTestSuite _performProtectedSectionForTest:testSection:]_block_invoke + 40\n\t39  XCTest                              0x000000010112e48c +[XCTContext runInContextForTestCase:block:] + 212\n\t40  XCTest                              0x00000001010bfe00 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 156\n\t41  XCTest                              0x00000001010c0150 -[XCTestSuite performTest:] + 320\n\t42  XCTest                              0x000000010110986c -[XCTest runTest] + 60\n\t43  XCTest                              0x00000001010c05c8 __27-[XCTestSuite performTest:]_block_invoke + 268\n\t44  XCTest                              0x00000001010bfe58 __59-[XCTestSuite _performProtectedSectionForTest:testSection:]_block_invoke + 40\n\t45  XCTest                              0x000000010112e48c +[XCTContext runInContextForTestCase:block:] + 212\n\t46  XCTest                              0x00000001010bfe00 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 156\n\t47  XCTest                              0x00000001010c0150 -[XCTestSuite performTest:] + 320\n\t48  XCTest                              0x000000010110986c -[XCTest runTest] + 60\n\t49  XCTest                              0x00000001010c05c8 __27-[XCTestSuite performTest:]_block_invoke + 268\n\t50  XCTest                              0x00000001010bfe58 __59-[XCTestSuite _performProtectedSectionForTest:testSection:]_block_invoke + 40\n\t51  XCTest                              0x000000010112e48c +[XCTContext runInContextForTestCase:block:] + 212\n\t52  XCTest                              0x00000001010bfe00 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 156\n\t53  XCTest                              0x00000001010c0150 -[XCTestSuite performTest:] + 320\n\t54  XCTest                              0x000000010110986c -[XCTest runTest] + 60\n\t55  XCTest                              0x00000001011405f0 __44-[XCTTestRunSession runTestsAndReturnError:]_block_invoke + 176\n\t56  XCTest                              0x00000001011406f0 __44-[XCTTestRunSession runTestsAndReturnError:]_block_invoke.100 + 108\n\t57  XCTest                              0x00000001010da354 -[XCTestObservationCenter _observeTestExecutionForBlock:] + 588\n\t58  XCTest                              0x0000000101140388 -[XCTTestRunSession runTestsAndReturnError:] + 592\n\t59  XCTest                              0x00000001010a4bbc -[XCTestDriver runTestsAndReturnError:] + 428\n\t60  XCTest                              0x000000010112a424 _XCTestMain + 2396\n\t61  WebDriverAgentRunner-Runner         0x0000000100bdb818 -[_XCTRunnerAppDelegate applicationWillResignActive:] + 0\n\t62  WebDriverAgentRunner-Runner         0x0000000100bdb720 _XCTRunnerRunTests + 0\n\t63  CoreFoundation                      0x0000000189928578 <redacted> + 20\n\t64  CoreFoundation                      0x0000000189927e7c <redacted> + 272\n\t65  CoreFoundation                      0x0000000189922ee0 <redacted> + 1024\n\t66  CoreFoundation                      0x00000001899227c0 CFRunLoopRunSpecific + 436\n\t67  GraphicsServices                    0x000000018bb2379c GSEventRunModal + 104\n\t68  UIKitCore                           0x00000001b62a1c38 UIApplicationMain + 212\n\t69  WebDriverAgentRunner-Runner         0x0000000100bdb9e4 main + 192\n\t70  libdyld.dylib                       0x00000001893e68e0 <redacted> + 4\n)"
  },
  "sessionId" : "C953E926-BEC0-4A35-9D6A-ABEAECB6C18E"
}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "xxx/env/lib/python3.7/site-packages/appium/webdriver/extensions/search_context/ios.py", line 72, in find_element_by_ios_predicate
    return self.find_element(by=MobileBy.IOS_PREDICATE, value=predicate_string)
  File "xxx/env/lib/python3.7/site-packages/appium/webdriver/webdriver.py", line 284, in find_element
    'value': value})['value']
  File "xxx/env/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "xxx/env/lib/python3.7/site-packages/appium/webdriver/errorhandler.py", line 24, in check_response
    super(MobileErrorHandler, self).check_response(response)
  File "xxx/env/lib/python3.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 131, in check_response
    status = value["status"]
KeyError: 'status'

Proposed fix

Change WDA to match the strings the clients are sending, i.e. prepend "-ios " to the "class chain" and "predicate string" usingTexts.

While it might seem backwards to change this in the server rather than adapt the clients, because multiple clients already use the same string and the "-ios "-prefix seems to have been established (in order to differentiate between lookup strategies that work exclusively on the iOS server) adapting the server seems like the more straight-forward fix.

Background

I've tried to find out when changes were introduced that made the server and client behaviour diverge but it turns out the respective strings have remained unchanged from when they were added around 3 years ago. I only tested the Python client because that's what I'm working with right now but searched through the other clients and all that have added support for class chain and predicate string lookups share the same strings, so the problem should exist for all of them. From what I can see none of those clients has been able to use these lookup strategies for years which seems odd but accurate from my research. I have found six clients linked from the official Appium documentation that all use the same "-ios..."-strings, so they seem to be an established pattern and fixing the server should make all of them work at the same time instead of having to fix all clients individually (it also seems like a reasonable idea to specially format strategies that work exclusively on iOS and are not supported by other Appium/Selenium servers).

Relevant GitHub code, issues and PRs:

Appium-clients have broadly agreed on using "-ios "-prefixes on strings used for iOS-exclusive element lookup strategies but the server can't provide those due to a mismatch
@hanneshauer hanneshauer changed the title Fix: Update "find elements using"-strings fix: Update "find elements using"-strings May 23, 2020
@electricbubble
Copy link

Why don't you modify find_element_by_ios_predicate ?

Although I haven't used appium/python-client

I personally think that such modification makes the code of WDA not tidy.
For example, class name and name and other usingText have no -ios prefix.

Copy link
Member

@KazuCocoa KazuCocoa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appium-clients can't use the iOS Class Chain and iOS Predicate String queries because the strings WDA expects don't match the strings the clients send.

Sorry, what do you mean? Do you mean when your client sends requests to WDA directly case?

This module is designed to get requests by appium-xcuitest-driver. Appium clients communicate with Appium server (not directly to appium/WDA)
https://github.com/appium/appium-xcuitest-driver/blob/08ac6564964f5e7c7fcb7d32beb6ce22eee57d75/lib/commands/find.js#L26 transfers the -ios class chain to class chain for WDA. For WDA, all contexts are for iOS, so such prefix does not so much make sense for WDA.

I'd also recommend you to modify client side.
You can call find_element('class chain', 'your target') directly if you wanted to send a custom find element.
https://github.com/appium/python-client/blob/8205776240b4c92e021064751e5b113d0c915d50/appium/webdriver/webelement.py#L80

@hanneshauer
Copy link
Author

This module is designed to get requests by appium-xcuitest-driver. Appium clients communicate with Appium server (not directly to appium/WDA)
https://github.com/appium/appium-xcuitest-driver/blob/08ac6564964f5e7c7fcb7d32beb6ce22eee57d75/lib/commands/find.js#L26 transfers the -ios class chain to class chain for WDA. For WDA, all contexts are for iOS, so such prefix

You are, of course, correct. My mistake was communicating with WDA directly instead of using appium-xcuitest-driver as a mediator which handles the string conversion and thus avoids the problems I've described. Sorry for the misunderstanding resulting from my setup, this obviously renders this PR moot.

@hanneshauer hanneshauer deleted the hanneshauer-fix-findelements-strings branch May 24, 2020 09:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants