How can i use multiple FindBy annotations in C# for both iOS and Android in the same Page Object?

Hello,

I wanted to create the same page object for both Android and iOS for an app i`m testing and Appium only searches by the first FindBy not the rest.

I wanted to write something like this :

//Returns the intro page background so its scale can be checked by the checkBackgroundState() method.
[FindsBy(How = How.XPath, Using = “//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAImage[1]”)]
[FindsByIOSUIAutomation(IosUIAutomation = “applications()[0].windows()[0].scrollViews()[0]”)]
[FindsByAndroidUIAutomator(AndroidUIAutomator = “new UiSelector().resourceId(“android:id/content”)”)]
public IWebElement Introbackground;

But only the first Xpath annotation is called and when it fails the next one is not called.

Is it possible in C# to define the elements for both platforms in the same page object or do i have to create separate objects for both platforms ?

Appium Log:

/wd/hub/session/a987ca20-d6af-4109-922b-d5df33d25123/element {“using”:“xpath”,“value”:"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAImage[1]"}
info: [debug] Waiting up to 0ms for condition
info: [debug] Pushing command to appium work queue: [“find”,{“strategy”:“xpath”,“selector”:"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAImage[1]",“context”:"",“multiple”:false}]
info: [debug] [BOOTSTRAP] [debug] Got data from client: {“cmd”:“action”,“action”:“find”,“params”:{“strategy”:“xpath”,“selector”:"//UIAApplication[1]/UIAWindow[1
]/UIAScrollView[1]/UIAImage[1]",“context”:"",“multiple”:false}}
info: [debug] [BOOTSTRAP] [debug] Got command of type ACTION
info: [debug] [BOOTSTRAP] [debug] Got command action: find
info: [debug] [BOOTSTRAP] [debug] Finding //UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAImage[1] using XPATH with the contextId: multiple: false

info: [debug] [BOOTSTRAP] [debug] Returning result: {“value”:“Could not find an element using supplied strategy. “,“status”:7}
info: [debug] Condition unmet after 456ms. Timing out.
info: [debug] Responding to client with error: {“status”:7,“value”:{“message”:“An element could not be located on the page using the given search parameters.”,“origValue”:“Could not find an element using supplied strategy. “},“sessionId”:“a987ca20-d6af-4109-922b-d5df33d25123”}
info: <-- POST /wd/hub/session/a987ca20-d6af-4109-922b-d5df33d25123/element 500 459.686 ms - 230
info: --> GET /wd/hub/session/a987ca20-d6af-4109-922b-d5df33d25123/window/current/size {}
info: [debug] Pushing command to appium work queue: [“getDeviceSize”]
info: [debug] [BOOTSTRAP] [debug] Got data from client: {“cmd”:“action”,“action”:“getDeviceSize”,“params”:{}}
info: [debug] [BOOTSTRAP] [debug] Got command of type ACTION
info: [debug] [BOOTSTRAP] [debug] Got command action: getDeviceSize
info: [debug] [BOOTSTRAP] [debug] Returning result: {“value”:{“width”:800,“height”:1232},“status”:0}
info: [debug] Responding to client with success: {“status”:0,“value”:{“width”:800,“height”:1232},“sessionId”:“a987ca20-d6af-4109-922b-d5df33d25123”}
info: <-- GET /wd/hub/session/a987ca20-d6af-4109-922b-d5df33d25123/window/current/size 200 5.951 ms - 99 {“status”:0,“value”:{“width”:800,“height”:1232},“sessionId”:“a987ca20-d6af-4109-922b-d5df33d25123”}
info: --> POST /wd/hub/session/a987ca20-d6af-4109-922b-d5df33d25123/element {“using”:“xpath”,“value”:”//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAImage[1]”}
info: [debug] Waiting up to 0ms for condition
info: [debug] Pushing command to appium work queue: [“find”,{“strategy”:“xpath”,“selector”:”//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAImage[1]”,“context”:"",“multiple”:false}]
info: [debug] [BOOTSTRAP] [debug] Got data from client: {“cmd”:“action”,“action”:“find”,“params”:{“strategy”:“xpath”,“selector”:"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAImage[1]",“context”:"",“multiple”:false}}
info: [debug] [BOOTSTRAP] [debug] Got command of type ACTION
info: [debug] [BOOTSTRAP] [debug] Got command action: find
info: [debug] [BOOTSTRAP] [debug] Finding //UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAImage[1] using XPATH with the contextId: multiple: false
info: [debug] [BOOTSTRAP] [debug] Returning result: {“value”:"Could not find an element using supplied strategy. ",“status”:7}
info: [debug] Condition unmet after 32ms. Timing out.
info: [debug] Responding to client with error: {“status”:7,“value”:{“message”:“An element could not be located on the page using the given search parameters.”,“origValue”:"Could not find an element using supplied strategy. "},“sessionId”:“a987ca20-d6af-4109-922b-d5df33d25123”}
info: <-- POST /wd/hub/session/a987ca20-d6af-4109-922b-d5df33d25123/element 500 34.326 ms - 230
info: --> POST /wd/hub/session/a987ca20-d6af-4109-922b-d5df33d25123/appium/device/rotate {“LANDSCAPE”:1}
info: [debug] Responding to client that a method is not implemented
info: <-- POST /wd/hub/session/a987ca20-d6af-4109-922b-d5df33d25123/appium/device/rotate 501 3.389 ms - 158

info: --> POST /wd/hub/session/a987ca20-d6af-4109-922b-d5df33d25123/appium/app/close {}

It is possible to use multiple annotations the way you want.
There are samples which work fine:
PageObject samples
PageObject tests

Also there is WIKI with detailed description.

What are you trying to check? Is it web site loaded in mobile browser/content loaded in webview or native mobile app?
If there is web/HTML content then attributes FindsByIOSUIAutomation and FindsByAndroidUIAutomator won’t work because they are designed for native apps. But you can define few FindsBy at this case.

//Returns the intro page background so its scale can be checked by the checkBackgroundState() method.
[FindsBy(How = How.XPath, Using = “xpath expression 1”)]
[FindsBy(How = How.XPath, Using = “xpath expression 2”)]
[FindsBy(How = How.XPath, Using = “xpath expression 3”)]
public IWebElement Introbackground;

Otherwise it looks strange. It really looks strange because at your case works xpath for a native app
I have a question;

  • Do you use RemoteWebDriver instead of AndroidDriver/IOSDriver? If it is true then you should use AndroidDriver/IOSDriver instead of RemoteWebDriver. I think your tests should be parameterized. How?

http://nunit.org/index.php?p=parameterizedTests&r=2.5

If the answer is NO then please open an issue here: https://github.com/appium/appium-dotnet-driver/issues
Please attach to this ticket a sample of your C# code and full server output as two separated gists (https://help.github.com/articles/creating-gists/).

2 Likes

Tank you very much @SergeyTichomirov this helped a lot.

I found that my issue was due to reusing the PageObjectConstructors from the previous code made mostly for WEB and they lacked the new AppiumPageObjectMemberDecorator(new TimeOutDuration(TimeSpan.FromSeconds(xx)) parameter from the PageFactory.InitElements().

Now if i`m using the below code it works like a charm :
[FindsBySequence]
[MobileFindsByAll(Android = true, IOS = true)]
[FindsByIOSUIAutomation(Name = “Email Address”,Priority = 1)]
[FindsByAndroidUIAutomator(ID = “com.yardi.pshark:id/mat_edit_text_email”, Priority = 1)]
public IWebElement usernameField;

with this constructor:
public ObjectFactoryBase(AppiumDriver mDriver)
{
this.mDriver = mDriver;
PageFactory.InitElements(mDriver, this,
new AppiumPageObjectMemberDecorator(new TimeOutDuration(TimeSpan.FromSeconds(5))));
}

@SergeyTichomirov @Gery
What about testing hybrid app in both ios and android ?

In this case if we use all three findby to locate elements in a page.
while running tests if we try to use one element from webview and another from native view
Then, do we have to switch between native and webview or not ?

Yes. User has to switch between native context and webview like they do at the common case:

driver.FindElement(By) //and do further actions

I think it is better to implement two separated page objects. One for native context and another one for webview. And when it is necessary user’s code will perform the switching between contexts.