Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@clicman
Copy link
Contributor

@clicman clicman commented Dec 12, 2014

Since UiAutomator and Selendroid requires different locators I`ve added new annotations:

  • SelendroidFindBy
  • SelendroidFindBys
  • SelendroidFindAll

And added dependency of AUTOMATION_NAME capatibility.
So now is possible to create one page object which covers all andoid versions.

@TikhomirovSergey
Copy link
Contributor

As for me it is goog. :) @Jonahss What is your opinion?

But there some remarks:

  • @clicman Can you add some tests here?
  • Why these pairs are illegal?

checkDisallowedAnnotationPairs(androidBy, androidBys);
checkDisallowedAnnotationPairs(androidBy, selendroidBys);
checkDisallowedAnnotationPairs(androidBy, androidFindAll);
checkDisallowedAnnotationPairs(androidBy, selendroidFindAll);
checkDisallowedAnnotationPairs(androidBys, androidFindAll);
checkDisallowedAnnotationPairs(androidBys, selendroidFindAll);
...

@Jonahss
As I know the default AUTOMATION_NAME is "Appium". Am I right? So if "Selendroid" is set up at capabilities then @AndroidFindBy can be ignored.

So if I am right this checking should be fixed and it will look like this:

checkDisallowedAnnotationPairs(androidBy, androidBys);
checkDisallowedAnnotationPairs(androidBy, androidFindAll);
checkDisallowedAnnotationPairs(androidBys, androidFindAll);

checkDisallowedAnnotationPairs(iOSBy, iOSBys);
checkDisallowedAnnotationPairs(iOSBy, iOSFindAll);
checkDisallowedAnnotationPairs(iOSBys, iOSFindAll);

checkDisallowedAnnotationPairs(SelendroidFindBy, SelendroidFindBys);
checkDisallowedAnnotationPairs(SelendroidFindBys, SelendroidFindAll);
checkDisallowedAnnotationPairs(SelendroidFindBy, SelendroidFindAll);

@Jonahss
Copy link
Member

Jonahss commented Dec 12, 2014

If it's good by @TikhomirovSergey then it's good by me :)

Thanks for the Pr @clicman, can you address the comments above?

@TikhomirovSergey
Copy link
Contributor

There is one more remark.
@clicman are you sure that uiAutomator and accessibility are valid strategies for Selendroid? I am not. If actually they are invalid then this and this should be removed. So the valid strategy list will be:

String id() default "";
String name() default "";
String className() default "";
String tagName() default "";
String xpath() default ""

@clicman
Copy link
Contributor Author

clicman commented Dec 13, 2014

@TikhomirovSergey you right uiAutomator and accessibility needs to be removed (copy-paste issue).

@Jonahss about validations: Ive added additional validations to prevent symantical errors. selendroid and android annotations are depend each other. It should not to be possible add to one page objects field @AndroidFindBy and @SelendroidFindAll annotations. In this case strong typization is not possible for this field.

@clicman
Copy link
Contributor Author

clicman commented Dec 13, 2014

@TikhomirovSergey one more issue about selendroid mode. In this mode you still can use @AndroidFindBy annotations. It is trick if you using finding by name, it this case you may not use selendroid annotations and minimize code. But if you in addition annotate field with selendroid - android annotation will be not used.

@TikhomirovSergey
Copy link
Contributor

Ok! :) Let me show my point of view.

So, I think there are possible situations when we should run test against old Android (it means API level which is lower than 18) where UI Automator doesn't work. As I know Selendroid works bad against new API's. I can be wrong because actually I usually use automator strategies and I work with API level > = 18, if it is so please correct me.

Ok!

Here are two examples. Lets imagine that capabilities are received from parameters (test is parameterized).

So:

@Before
public void setUp() throws Exception {
    File app= new File("path to your apk");
    DesiredCapabilities capabilities = new DesiredCapabilities();
    capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator");
    capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
    capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "Selendroid");//<===!!!
    capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "4.1");//is not necessary
    driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);

   PageFactory.initElements(new AppiumFieldDecorator(driver, 5, TimeUnit.SECONDS), 
                        desiredPageObjectInstace);
}

and

@Before
public void setUp() throws Exception {
    File app= new File("path to your apk");
    DesiredCapabilities capabilities = new DesiredCapabilities();
    capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator");
    capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
    //there is no selendroid 
    driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);

   PageFactory.initElements(new AppiumFieldDecorator(driver, 5, TimeUnit.SECONDS), 
                        desiredPageObjectInstace);
}

The desired page/screen object looks like:

....
@SelendroidFindBy(relevant locator strategy for selendroid) //this will be used when Selendroid
// mode is set up at capabilities. @AndroidFindBy is ignored 
@AndroidFindBy(relevant locator strategy for UI automator) //this will be used when Selendroid
// mode is not(!!!) set up at capabilities. @SelendroidFindBy is ignored 
@iOSFindBy(relevant locator strategy for iOS automation) //lets imagine that we need to test it
//on iOS. It is applied when iOS platform is set up at capabilities. Android locators are ignored.
private WebElement yourElement;
....

I think it looks cool! Locator strategies are splitted for Selendroid and UI Automator modes, but all Android is covered.

@Jonahss and @clicman What are you think about this?

@TikhomirovSergey
Copy link
Contributor

So
Let read the code that is proposed by @clicman. I am commenting it step by step. Ok:

AppiumAnnotations(Field field, String platform, 
               String automation //automation has been got from capabilities
                ) {
        super(field);
        mobileField = field;
        this.platform = String.valueOf(platform).
                toUpperCase().trim();
        this.automation = String.valueOf(automation).
                                toUpperCase().trim();
    } 

....

    @Override
    public By buildBy() {
        assertValidAnnotations(); //firstly it validates the declaration
                //and here we go!!!

                SelendroidFindBy selendroidBy = mobileField
                .getAnnotation(SelendroidFindBy.class);
        if (selendroidBy != null && ANDROID.toUpperCase().equals(platform) &&
                        "Selendroid".toUpperCase().equals(automation)) { //<=! If there is Selendroid
                        //and field is annotated by @SelendroidFindBy 
            return getMobileBy(selendroidBy, //<== then it returns something relevant 
                                  getFilledValue(selendroidBy)); //for Selendroid
        }

               //the same is for complex locator strategies
                SelendroidFindBys selendroidBys = mobileField
                .getAnnotation(SelendroidFindBys.class);
        if (selendroidBys != null && ANDROID.toUpperCase().equals(platform) &&
                        "Selendroid".toUpperCase().equals(automation)) {
            return getMobileBy(selendroidBys, getFilledValue(selendroidBys)); //<=!
        }

                SelendroidFindAll selendroidAll = mobileField
                .getAnnotation(SelendroidFindAll.class);
        if (selendroidAll != null && ANDROID.toUpperCase().equals(platform) &&
                        "Selendroid".toUpperCase().equals(automation)) {
            return getMobileBy(selendroidAll, getFilledValue(selendroidAll)); //<=!
        }

///////As you can see if now it is working at Selendroid mode @AndroidFindBy is always ignored. !!!               

        AndroidFindBy androidBy = mobileField
                .getAnnotation(AndroidFindBy.class);
        if (androidBy != null && ANDROID.toUpperCase().equals(platform)) {
            return getMobileBy(androidBy, getFilledValue(androidBy));
        }

        AndroidFindBys androidBys = mobileField
                .getAnnotation(AndroidFindBys.class);
        if (androidBys != null && ANDROID.toUpperCase().equals(platform)) {
            return getComplexMobileBy(androidBys.value(), ByChained.class);
        }

        AndroidFindAll androidFindAll = mobileField.getAnnotation(AndroidFindAll.class);
        if (androidFindAll != null && ANDROID.toUpperCase().equals(platform)) {
            return getComplexMobileBy(androidFindAll.value(), ByAll.class);
        }

              //if now there is iOS  
        iOSFindBy iOSBy = mobileField.getAnnotation(iOSFindBy.class);
        if (iOSBy != null && IOS.toUpperCase().equals(platform)) {
            return getMobileBy(iOSBy, getFilledValue(iOSBy));
        }

        iOSFindBys iOSBys = mobileField.getAnnotation(iOSFindBys.class);
        if (iOSBys != null && IOS.toUpperCase().equals(platform)) {
            return getComplexMobileBy(iOSBys.value(), ByChained.class);
        }

        iOSFindAll iOSFindAll = mobileField.getAnnotation(iOSFindAll.class);
        if (iOSFindAll != null && IOS.toUpperCase().equals(platform)) {
            return getComplexMobileBy(iOSFindAll.value(), ByAll.class);
        }       

//If the field is not annotated Appium-specific annotations it attempts to get By-strategy using 
//Selenium @FindBy's
        return super.buildBy();
    }

So the the addition parameter (platform) helps to rout possible Android strategies.

@TikhomirovSergey
Copy link
Contributor

@Jonahss about validations: Ive added additional validations to prevent symantical errors. selendroid and android annotations are depend each other. It should not to be possible add to one page objects field @AndroidFindBy and @SelendroidFindAll annotations. In this case strong typization is not possible for this field.

I am looking at step-by-step code above and I can't find the problem :) If there were tests ( @clicman please don't forget to commit them) I think the situation would be clear.

@TikhomirovSergey one more issue about selendroid mode. In this mode you still can use @AndroidFindBy annotations. It is trick if you using finding by name, it this case you may not use selendroid annotations and minimize code. But if you in addition annotate field with selendroid - android annotation will be not used.
Ok. But id's and xpath's and something else are different. If used locators are the same on each target platform/mode it is enough to use Selenium FindBy's at all page objects in your test project.

Actually AndroidFindBy's, iOSFindBy's and SelendroidFindBy are cool when locators are different (as usually it is) on each target platform. This way we avoid the necessity to implement page objects for each mobile OS or its version.

@clicman
Copy link
Contributor Author

clicman commented Dec 13, 2014

@TikhomirovSergey

As I know Selendroid works bad against new API's.

I`m testing mobile apps about two weeks and has not collected enough experience to confirm or refute it. :)

I think it looks cool! Locator strategies are splitted for Selendroid and UI Automator modes, but all Android is covered.
@Jonahss and @clicman What are you think about this?

Yes its really cool. You described this pull requests behavour. Or I don`t see difference :)

I am seeing at step-by-step code above and I can't find the problem :) If there were tests ( @clicman please don't forget to commit them) I think the situation would be clear.

I`ll write tests today, but here is situation what I want to ve avoid:

@SelendroidFindBy(relevant locator strategy for selendroid) //Here we suppose one WebElement object
@AndroidFindAll(relevant locator strategy for UI automator)  //Here we suppose List of web elements
private WebElement element; // How we should typize this field in this case? Additional validations will strict such annotating.

Actually AndroidFindBy's, iOSFindBy's and SelendroidFindBy are cool when locators are different (as usually it happens) on each target platform.

Yes, but it covers this situation:

//Capatibilities are in Selendroid mode
@AndroidFindBy(name="OK") //This will be used in selendroid mode if no @SelendroidFindBy annotation
@IosFindBy(...)
private WebElement okButton;
``
Code less on one string. Just shugar, more freedom :)

@TikhomirovSergey
Copy link
Contributor

Ok! :) I think if I've described the expected behavior so why is it illegal:

@SelendroidFindBy(relevant locator strategy for selendroid) //Here we suppose one WebElement object
@AndroidFindAll(relevant locator strategy for UI automator)  //It is actually the SINGLE element
//which is found by few POSSIBLE locators. Here ByAll objects is used.
private WebElement element; 

?

Please look at ByAll sources.

If the list is supposed here then you will implement

@SelendroidFindBy(relevant locator strategy for selendroid)
@AndroidFindAll(relevant locator strategy for UI automator)  
private List<WebElement> element; // !!!

Nothing is complicated!!!

There is a problem with the naming. FindAll exists in Selenium. Appium-specific annotations were named the same way. As for me the more correct is FindAny

@TikhomirovSergey
Copy link
Contributor

Actually the illegal way is:

@SelendroidFindBy(relevant locator strategy for selendroid)
@SelendroidFindBys(relevant locator strategy for selendroid)  
@SelendroidFindAll(relevant locator strategy for selendroid)  
private WebElement element; // or List<WebElement>

because there is not clear what strategy we should use - By, ByChained or ByAll.

So, if all is clear now I advice you to change validation from:

            checkDisallowedAnnotationPairs(androidBy, androidBys);
            checkDisallowedAnnotationPairs(androidBy, selendroidBys);
        checkDisallowedAnnotationPairs(androidBy, androidFindAll);
        checkDisallowedAnnotationPairs(androidBy, selendroidFindAll);
        checkDisallowedAnnotationPairs(androidBys, androidFindAll);
        checkDisallowedAnnotationPairs(androidBys, selendroidFindAll);

        checkDisallowedAnnotationPairs(selendroidBy, androidBys);
                checkDisallowedAnnotationPairs(selendroidBy, selendroidBys);
        checkDisallowedAnnotationPairs(selendroidBy, androidFindAll);
        checkDisallowedAnnotationPairs(selendroidBy, selendroidFindAll);
        checkDisallowedAnnotationPairs(selendroidBys, androidFindAll);
        checkDisallowedAnnotationPairs(selendroidBys, selendroidFindAll);

        checkDisallowedAnnotationPairs(iOSBy, iOSBys);
        checkDisallowedAnnotationPairs(iOSBy, iOSFindAll);
        checkDisallowedAnnotationPairs(iOSBys, iOSFindAll);

to

checkDisallowedAnnotationPairs(androidBy, androidBys);
checkDisallowedAnnotationPairs(androidBy, androidFindAll);
checkDisallowedAnnotationPairs(androidBys, androidFindAll);

checkDisallowedAnnotationPairs(iOSBy, iOSBys);
checkDisallowedAnnotationPairs(iOSBy, iOSFindAll);
checkDisallowedAnnotationPairs(iOSBys, iOSFindAll);

checkDisallowedAnnotationPairs(SelendroidFindBy, SelendroidFindBys);
checkDisallowedAnnotationPairs(SelendroidFindBys, SelendroidFindAll);
checkDisallowedAnnotationPairs(SelendroidFindBy, SelendroidFindAll);

@clicman
Copy link
Contributor Author

clicman commented Dec 13, 2014

Wow! I didn`t know what ByAll is agnostic. You totally right. Thanks. I will change validations.

[*] Remove unnecessary validators
[+] Add selendroid tests
@clicman
Copy link
Contributor Author

clicman commented Dec 13, 2014


T E S T S

Running io.appium.java_client.pagefactory_tests.AndroidPageObjectTest
Tests run: 27, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 248.793 sec
Running io.appium.java_client.pagefactory_tests.SelendroidModeTest
Tests run: 12, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 223.364 sec

Results :

Tests run: 39, Failures: 0, Errors: 0, Skipped: 0

Seems feature is ready to be merged. If it so, could you guys release a new version after merge?

@TikhomirovSergey
Copy link
Contributor

Ok! Thank you very much! I think that @Jonahss will merge it soon.

Jonahss added a commit that referenced this pull request Dec 15, 2014
[+] Add support for selendroid locators in page object annotations
@Jonahss Jonahss merged commit 3432ef7 into appium:master Dec 15, 2014
@Jonahss
Copy link
Member

Jonahss commented Dec 15, 2014

Cool ^.^

I'll run the tests and should be able to release a new version today.

@TikhomirovSergey
Copy link
Contributor

@Jonahss please wait for one day. I'll propose one more pull request. It will be bug fix + new test.

@Jonahss
Copy link
Member

Jonahss commented Dec 15, 2014

Okay :)

On Monday, December 15, 2014, Sergey Tikhomirov [email protected]
wrote:

@Jonahss https://github.com/Jonahss please wait for one day. I'll
propose one more pull request. It will be bug fix + new test.


Reply to this email directly or view it on GitHub
#140 (comment).

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