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

Page Object design extension question #320

Closed
SergeyErmakovMercDev opened this issue Feb 17, 2016 · 6 comments
Closed

Page Object design extension question #320

SergeyErmakovMercDev opened this issue Feb 17, 2016 · 6 comments

Comments

@SergeyErmakovMercDev
Copy link

Hello,
I'm using new page object design with Widget class. And recently caught some unexpected behaviour.
Let me describe my classes:

public class Movies {
    @iOSFindBy(uiAutomator = ".tableViews()[0].cells()")
    public List<Movie> items;
}

public class Movie extends Widget {
    @iOSFindBy(uiAutomator = ".staticTexts()[0]")
    public IOSElement nameElement;

    public IOSElement getNameElement() {
       return nameElement;
    }
}

And these classes I use smth like this:

Movies movies = ...
List<Movie> items = movies.items; //items is not null
Movie item = items.get(0);

IOSElement fieldElement = item.nameElement; // fieldElement is null
IOSElement methodElement = item.getNameElement(); // methodElement is not null

Why does it work so?
Are there any ways to get element directly from field without creating getter?

@TikhomirovSergey
Copy link
Contributor

Hi @SergeyErmakovMercDev

It is not a bug. It works by design.

  • The first point.
    Are you familiar with Page Object design pattern? Please pay attention to requirenments. So the thing you want conflicts with the pattern spec most likely. I think it makes sense to implement a public method which describes some business-logic/logic of interaction with a widget instead.
  • The second point.
    Have you read this description? Please pay attention to the** "What if interaction with a "widget" has special details for each used platform, but the same at high-level"**. Thing that you want may become senseless when you will want to design something crossplatform.

If it is interesting I can describe how does it work.

IOSElement fieldElement = items.nameElement; // fieldElement is null

because items is not the real object. It is a proxy created via CGLIB. A real object with populated fields is instantiated once when any declared method is invoked. But it stays not available anyway.

I'm waiting for your response.

@SergeyErmakovMercDev
Copy link
Author

@TikhomirovSergey, thanks for responce.

I've just read the requirements and in most cases in project our team comply them. But in this situation I need to interact with sub-elements in widject directly. Like this:

public class Movies {
    @iOSFindBy(uiAutomator = ".tableViews()[0].cells()")
    public List<Movie> items;

    public void swapMovies(int indexA, int indexB) {
         Point centerA = items.get(indexA).getNameElement().getCenter();
         Point centerB = items.get(indexB).getNameElement().getCenter();

         getDriver().swipe(centerA.getX(), centerA.getY(), centerB.getX(), centerB.getY(), DEFAULT_DURATION);
         // and so on
    }
}

Of cource I understand that I can make simple getNameCenter() method at Movie and the problem will be closed. But we are using Cucumber + Appium in project, thus in step definitions we get elements via reflection. The reason why we do this is simple: decrease coding time.

So, everything is okey if we get fields via reflection from pages. And we supposed that widgets work familiar. But they don't.

Sergey, I'll be so grateful to you, if you'll explain how does this work. And why in one case I got null at other an object.

@SergeyErmakovMercDev
Copy link
Author

BTW. Don't afraid about cross-platform. We simply don't need it :)

@TikhomirovSergey
Copy link
Contributor

@SergeyErmakovMercDev
Ok. I got your problem. And it seem I have the solution...

If it is interesting I can explain how does it work.

A decorator instance scans a page object instance. If a field has a type of some WebElement/list of WebElement then the decorator works as usual:
https://github.com/appium/java-client/blob/master/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java#L155

Ok. It tries to create a widget otherwise:
https://github.com/appium/java-client/blob/master/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java#L160
https://github.com/appium/java-client/blob/master/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java#L200
https://github.com/appium/java-client/blob/master/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java#L205

It creates not real objects, it creates proxies:
https://github.com/appium/java-client/blob/master/src/main/java/io/appium/java_client/pagefactory/utils/ProxyFactory.java

Real objects which wrapped inside proxies are created when any declared method is invoked:
https://github.com/appium/java-client/blob/master/src/main/java/io/appium/java_client/pagefactory/WidgetInterceptor.java#L51
https://github.com/appium/java-client/blob/master/src/main/java/io/appium/java_client/pagefactory/WidgetListInterceptor.java

So the problem is that there is no ability to exract a real widget-object, What if there was something like that:

   @FindBy
   Widget widget;

   ...
   Object someValue = widget.
           getSelfReference(). //this method would be used in some extraordinary situations like yours
               publicField;

?

It could fix the known problem:
#267
Point: What if interaction with a "widget" has special details for each used platform, but the same at high-level
To find: ...for now it is not possible to

@TikhomirovSergey
Copy link
Contributor

You can propose a PR if you want :) Also don't forget to add a test if you want to try.

TikhomirovSergey added a commit that referenced this issue Mar 2, 2016
@SButterfly

You can:
- switch to your java_client fork using git

- > git checkout -b your_new_branch_name master

- > git pull https://github.com/User_Name/java-client.git target_branch

But now you can clone master
@TikhomirovSergey
Copy link
Contributor

This issue has been fixed and it is going to be published soon

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants