IOS Class Chain lookup with Page Factory in java-client 8.x.x

I know it’s probably a typo but 'label needs to be ‘label’.

Good article on location strategies (including iOSClassChain):

iOSClassChain Queries Construction Rules:

Thank for the answer, but I’ve tried to put quotes in different ways and it doesn’t work. I also tried using this locator via driver.findElement(AppiumBy.iOSClassChain()) and the click worked, so it looks like the locator is properly and it’s something else

Ok, if this works I’m not understanding why you can’t just use it?

In our project we’re using page factory model because of cross-platforming

try →

@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeStaticText[`name == \"Login\" OR label == \"feeLabel\" OR value == \"feeLabel\"`] ")

no luck → share page source

No luck, here’s page source:

<?xml version="1.0" encoding="UTF-8"?> < XCUIElementTypeApplication type="XCUIElementTypeApplication" name="BCC.KZ" label="BCC.KZ" enabled="true" visible="true" accessible="false" x="0" y="0" width="390" height="844" index="0" > < XCUIElementTypeWindow type="XCUIElementTypeWindow" enabled="true" visible="true" accessible="false" x="0" y="0" width="390" height="844" index="0" > < XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="0" width="390" height="844" index="0" > < XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="0" width="390" height="844" index="0" > < XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="0" width="390" height="844" index="0" > < XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="0" width="390" height="844" index="0" > < XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="0" width="390" height="844" index="0" > < XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="0" width="390" height="844" index="0" > < XCUIElementTypeImage type="XCUIElementTypeImage" name="bcc_logo_lowercase" enabled="true" visible="true" accessible="false" x="16" y="71" width="128" height="32" index="0"/ > < XCUIElementTypeButton type="XCUIElementTypeButton" name="more icon" label="more icon" enabled="true" visible="true" accessible="true" x="342" y="71" width="32" height="32" index="1"/ > < XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="311" width="390" height="303" index="2" > < XCUIElementTypeImage type="XCUIElementTypeImage" name="right_green_circle" enabled="true" visible="true" accessible="false" x="95" y="69" width="295" height="350" index="0"/ > < XCUIElementTypeImage type="XCUIElementTypeImage" name="left_green_circle" enabled="true" visible="true" accessible="false" x="0" y="311" width="180" height="251" index="1"/ > < XCUIElementTypeImage type="XCUIElementTypeImage" name="auth_page_visual" enabled="true" visible="true" accessible="false" x="55" y="311" width="280" height="151" index="2"/ > < XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="#картакарта is the best credit card of 2021 according to VISA" name="#картакарта is the best credit card of 2021 according to VISA" label="#картакарта is the best credit card of 2021 according to VISA" enabled="true" visible="true" accessible="true" x="32" y="510" width="326" height="48" index="3"/ > < XCUIElementTypeButton type="XCUIElementTypeButton" name="Open a card" label="Open a card" enabled="true" visible="true" accessible="true" x="133" y="582" width="124" height="32" index="4" > < XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="Open a card" name="Open a card" label="Open a card" enabled="true" visible="true" accessible="false" x="159" y="590" width="72" height="16" index="0"/ > < /XCUIElementTypeButton > < /XCUIElementTypeOther > < XCUIElementTypeButton type="XCUIElementTypeButton" name="Login" label="Login" enabled="true" visible="true" accessible="true" x="16" y="662" width="358" height="54" index="3" > < XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="Login" name="Login" label="Login" enabled="true" visible="true" accessible="false" x="172" y="679" width="46" height="20" index="0"/ > < /XCUIElementTypeButton > < XCUIElementTypeButton type="XCUIElementTypeButton" name="Registration" label="Registration" enabled="true" visible="true" accessible="true" x="16" y="724" width="358" height="54" index="4" > < XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="Registration" name="Registration" label="Registration" enabled="true" visible="true" accessible="false" x="150" y="741" width="90" height="20" index="0"/ > < /XCUIElementTypeButton > < /XCUIElementTypeOther > < /XCUIElementTypeOther > < /XCUIElementTypeOther > < /XCUIElementTypeOther > < /XCUIElementTypeOther > < /XCUIElementTypeOther > < /XCUIElementTypeWindow > < /XCUIElementTypeApplication > < /AppiumAUT >

I use page factory as well, this works for me:

    @iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeOther[`name == 'dialog'`]/XCUIElementTypeButton[2]")

note the difference between and `

As I mentioned earlier, I placed this locator in the .findElement method and it works, so I don’t think the locator is the issue. However, I have also tried using with and `. It didn’t work :frowning:

I also noticed that when tests fail, an error occurs that the element Proxy element for: DefaultElementLocator ‘by id or name “signButton”’ is not found, as if the locator is invisible for the test and it sees only the name of the WebElement, that is, signButton. So perhaps I somehow put the annotation wrong?

Appium logs:
13:55:47.273 [main] ERROR base.TestBase - Can’t click element: Proxy element for: DefaultElementLocator ‘by id or name “signButton”’
[HTTP] → POST /session/f8bc1d5f-2cbe-4075-85db-d9be232f6836/element
[HTTP] {“using”:“css selector”,“value”:“#signButton”}

you have

 <XCUIElementTypeButton type="XCUIElementTypeButton" name="Login" label="Login" enabled="true" visible="true" accessible="true" x="16" y="662" width="358" height="54" index="3" >
    < XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="Login" name="Login" label="Login" enabled="true" visible="true" accessible="false" x="172" y="679" width="46" height="20" index="0"/ >
</XCUIElementTypeButton > 

Your initial search is correct.

even simple

@iOSXCUITFindBy(id = "Login")
protected WebElement signButton;

should work. somewhere else problem is … add Appium server DEBUG logs

[HTTP] → POST /session/8a4f8a8f-ec0a-44e4-bbf5-38cfc1c9e7f0/element
[HTTP] {“using”:“css selector”,“value”:“#signButton”}
[AppiumDriver@a9ea] Plugins which can handle cmd ‘findElement’: images
[AppiumDriver@a9ea] Plugin images is now handling cmd ‘findElement’
[AppiumDriver@a9ea] Executing default handling behavior for command ‘findElement’
[XCUITestDriver@d6ab (8a4f8a8f)] Got response with status 404: {“value”:{“error”:“no such element”,“message”:“unable to find an element using ‘class chain’, value ‘**/*[name == \"signButton\"]’”,“traceback”:"(\n\t0 WebDriverAgentLib 0x00000001310914b8 FBNoSuchElementErrorResponseForRequest + 252\n\t1 WebDriverAgentLib 0x00000001310912e8 +[FBFindElementCommands handleFindElement:] + 312\n\t2 WebDriverAgentLib 0x00000001310563bc -[FBRoute_TargetAction mountRequest:intoResponse:] + 168\n\t3 WebDriverAgentLib 0x00000001310423cc __37-[FBWebServer registerRouteHandlers:]_block_invoke + 404\n\t4 WebDriverAgentLib 0x0000000131078114 -[RoutingHTTPServer handleRoute:withRequest:response:] + 168\n\t5 WebDriverAgentLib 0x0000000131078c04 __72-[RoutingHTTPServer routeMethod:withPath:parameters:request:connection:]_block_invoke + 64\n\t6 libdispatch.dylib 0x000000010bd63708 _dispatch_client_callout + 16\n\t7 libdispatch.dylib …
[HTTP] ← POST /session/8a4f8a8f-ec0a-44e4-bbf5-38cfc1c9e7f0/element 404 201 ms - 1025

what is it? why css? how you init page?
PS let me compare with mine logs …

so mine are for code:

    @iOSXCUITFindBy(id = "I28LogInView")
    private WebElement mainContainer;

------>

2023-04-17 10:23:35:947 - [debug] [XCUITestDriver@e5fb (862b8c74)] Calling AppiumDriver.getCurrentContext() with args: ["862b8c74-c07a-4986-a3f2-74db891d7cce"]
2023-04-17 10:23:35:948 - [debug] [XCUITestDriver@e5fb (862b8c74)] Executing command 'getCurrentContext'
2023-04-17 10:23:35:948 - [debug] [XCUITestDriver@e5fb (862b8c74)] Responding to client with driver.getCurrentContext() result: "NATIVE_APP"
2023-04-17 10:23:35:948 - [HTTP] <-- GET /session/862b8c74-c07a-4986-a3f2-74db891d7cce/context 200 1 ms - 22
2023-04-17 10:23:35:949 - [HTTP] 
2023-04-17 10:23:35:954 - [HTTP] --> POST /session/862b8c74-c07a-4986-a3f2-74db891d7cce/element
2023-04-17 10:23:35:954 - [HTTP] {"using":"id","value":"I28LogInView"}
2023-04-17 10:23:35:954 - [debug] [XCUITestDriver@e5fb (862b8c74)] Calling AppiumDriver.findElement() with args: ["id","I28LogInView","862b8c74-c07a-4986-a3f2-74db891d7cce"]
2023-04-17 10:23:35:954 - [debug] [XCUITestDriver@e5fb (862b8c74)] Executing command 'findElement'
2023-04-17 10:23:35:954 - [debug] [XCUITestDriver@e5fb (862b8c74)] Valid locator strategies for this request: xpath, id, name, class name, -ios predicate string, -ios class chain, accessibility id, css selector
2023-04-17 10:23:35:955 - [debug] [XCUITestDriver@e5fb (862b8c74)] Waiting up to 0 ms for condition
2023-04-17 10:23:35:955 - [debug] [XCUITestDriver@e5fb (862b8c74)] Matched '/element' to command name 'findElement'
2023-04-17 10:23:35:956 - [debug] [XCUITestDriver@e5fb (862b8c74)] Proxying [POST /element] to [POST http://127.0.0.1:6109/session/223EC553-DC1C-4DD7-89DC-6AC3E41C172D/element] with body: {"using":"id","value":"I28LogInView"}

public PageBase(AppiumDriver appiumDriver) {
PageFactory.initElements(driver, this);
driver = appiumDriver;
}

But if I add AppiumFieldDecorator I will catch ExceptionInInitializerError because my Java version is 19.0.1. Can this be fixed somehow without downgrading to java 11?

not correct! this calls SELENIUM instead of Appium decorators. correct is:

PageFactory.initElements(new AppiumFieldDecorator(driver, Duration.ofSeconds(DEFAULT_WAIT_TIME)), this);

As I said, because of AppiumFieldDecorator I catch ExceptionInInitializerError

[8.0.0 Beta 2] PageFactory still not working with Java 17 · Issue #1619 · appium/java-client · GitHub

try mentioned solution → Solving the JDK 17 Compatibility Issue with Appium: A Step-by-Step Guide

Still no luck… Also I have tried java 11 and the error is still showing on startup

What in Appium logs?

[AppiumDriver@cc73] New XCUITestDriver session created successfully, session cca027a4-e946-4374-89e2-88e37938fe88 added to master session list
[AppiumDriver@cc73] Promoting 1 sessionless plugins to be attached to session ID cca027a4-e946-4374-89e2-88e37938fe88
[HTTP] <-- POST /session 200 45554 ms - 956
[HTTP]
[HTTP] --> POST /session/cca027a4-e946-4374-89e2-88e37938fe88/execute/sync
[HTTP] {“script”:“mobile: configureLocalization”,“args”:[{“keyboard”:{“layout”:“QWERTY”,“name”:“en_US”,“hardware”:“Automated”}}]}
[AppiumDriver@cc73] Plugins which can handle cmd ‘execute’: images
[AppiumDriver@cc73] Plugin images is now handling cmd ‘execute’
[AppiumDriver@cc73] Executing default handling behavior for command ‘execute’
[HTTP] <-- POST /session/cca027a4-e946-4374-89e2-88e37938fe88/execute/sync 200 899 ms - 14
[HTTP]

java.lang.ExceptionInInitializerError
at io.appium.java_client.pagefactory.utils.ProxyFactory.getEnhancedProxy(ProxyFactory.java:53)
at io.appium.java_client.pagefactory.utils.ProxyFactory.getEnhancedProxy(ProxyFactory.java:33)
at io.appium.java_client.pagefactory.AppiumFieldDecorator.proxyForAnElement(AppiumFieldDecorator.java:208)
at io.appium.java_client.pagefactory.AppiumFieldDecorator.access$000(AppiumFieldDecorator.java:60)
at io.appium.java_client.pagefactory.AppiumFieldDecorator$1.proxyForLocator(AppiumFieldDecorator.java:99)
at org.openqa.selenium.support.pagefactory.DefaultFieldDecorator.decorate(DefaultFieldDecorator.java:63)
at io.appium.java_client.pagefactory.AppiumFieldDecorator.decorate(AppiumFieldDecorator.java:146)
at org.openqa.selenium.support.PageFactory.proxyFields(PageFactory.java:111)
at org.openqa.selenium.support.PageFactory.initElements(PageFactory.java:103)
at base.PageBase.(PageBase.java:52)
at steps.AuthSteps.(AuthSteps.java:13)
at tests.A03001.WithdrawAccountTenge.withdrawAccountTenge(WithdrawAccountTenge.java:17)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:139)
at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:677)
at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:221)
at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:50)
at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:962)
at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:194)
at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:148)
at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:128)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.testng.TestRunner.privateRun(TestRunner.java:806)
at org.testng.TestRunner.run(TestRunner.java:601)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:433)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:427)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:387)
at org.testng.SuiteRunner.run(SuiteRunner.java:330)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1256)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1176)
at org.testng.TestNG.runSuites(TestNG.java:1099)
at org.testng.TestNG.run(TestNG.java:1067)
at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:105)
Caused by: java.lang.IllegalArgumentException
at org.objectweb.asm.ClassVisitor.(ClassVisitor.java:75)
at net.sf.cglib.core.DebuggingClassWriter.(DebuggingClassWriter.java:49)
at net.sf.cglib.core.DefaultGeneratorStrategy.getClassVisitor(DefaultGeneratorStrategy.java:30)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:24)
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:332)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)
at net.sf.cglib.core.KeyFactory$Generator.create(KeyFactory.java:221)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:174)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:153)
at net.sf.cglib.proxy.Enhancer.(Enhancer.java:73)
… 39 more

= Isssue is with pagefactory init elements

Good news! In java-client 8.5.1 the bug with page factory initialization was fixed, now all annotations work correctly (cglib replaced with with bytebuddy)
java-client 8.5.1 changelog

1 Like