Uh oh!
There was an error while loading. Please reload this page.
[+] Add support for selendroid locators in page object annotations - #140
Conversation
TikhomirovSergey
commented
Dec 12, 2014
As for me it is goog. :) @Jonahss What is your opinion? But there some remarks:
@Jonahss So if I am right this checking should be fixed and it will look like this:
|
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
commented
Dec 12, 2014
There is one more remark.
|
clicman
commented
Dec 13, 2014
@TikhomirovSergey you right uiAutomator and accessibility needs to be removed (copy-paste issue). @Jonahss about validations: I |
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
commented
Dec 13, 2014
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: @BeforepublicvoidsetUp() throwsException {
Fileapp= newFile("path to your apk");
DesiredCapabilitiescapabilities = newDesiredCapabilities();
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 necessarydriver = newAndroidDriver(newURL("http://127.0.0.1:4723/wd/hub"), capabilities);
PageFactory.initElements(newAppiumFieldDecorator(driver, 5, TimeUnit.SECONDS), desiredPageObjectInstace);
}and @BeforepublicvoidsetUp() throwsException {
Fileapp= newFile("path to your apk");
DesiredCapabilitiescapabilities = newDesiredCapabilities();
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator");
capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
//there is no selendroid driver = newAndroidDriver(newURL("http://127.0.0.1:4723/wd/hub"), capabilities);
PageFactory.initElements(newAppiumFieldDecorator(driver, 5, TimeUnit.SECONDS), desiredPageObjectInstace);
}The desired page/screen object looks like: ....
@SelendroidFindBy(relevantlocatorstrategyforselendroid) //this will be used when Selendroid// mode is set up at capabilities. @AndroidFindBy is ignored @AndroidFindBy(relevantlocatorstrategyforUIautomator) //this will be used when Selendroid// mode is not(!!!) set up at capabilities. @SelendroidFindBy is ignored @iOSFindBy(relevantlocatorstrategyforiOSautomation) //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.privateWebElementyourElement;
....I think it looks cool! Locator strategies are splitted for Selendroid and UI Automator modes, but all Android is covered. |
TikhomirovSergey
commented
Dec 13, 2014
So AppiumAnnotations(Fieldfield, Stringplatform, Stringautomation//automation has been got from capabilities
) {
super(field);
mobileField = field;
this.platform = String.valueOf(platform).
toUpperCase().trim();
this.automation = String.valueOf(automation).
toUpperCase().trim();
} ....
@OverridepublicBybuildBy() {
assertValidAnnotations(); //firstly it validates the declaration//and here we go!!!SelendroidFindByselendroidBy = 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 returngetMobileBy(selendroidBy, //<== then it returns something relevant getFilledValue(selendroidBy)); //for Selendroid
}
//the same is for complex locator strategiesSelendroidFindBysselendroidBys = mobileField
.getAnnotation(SelendroidFindBys.class);
if (selendroidBys != null && ANDROID.toUpperCase().equals(platform) &&
"Selendroid".toUpperCase().equals(automation)) {
returngetMobileBy(selendroidBys, getFilledValue(selendroidBys)); //<=!
}
SelendroidFindAllselendroidAll = mobileField
.getAnnotation(SelendroidFindAll.class);
if (selendroidAll != null && ANDROID.toUpperCase().equals(platform) &&
"Selendroid".toUpperCase().equals(automation)) {
returngetMobileBy(selendroidAll, getFilledValue(selendroidAll)); //<=!
}
///////As you can see if now it is working at Selendroid mode @AndroidFindBy is always ignored. !!! AndroidFindByandroidBy = mobileField
.getAnnotation(AndroidFindBy.class);
if (androidBy != null && ANDROID.toUpperCase().equals(platform)) {
returngetMobileBy(androidBy, getFilledValue(androidBy));
}
AndroidFindBysandroidBys = mobileField
.getAnnotation(AndroidFindBys.class);
if (androidBys != null && ANDROID.toUpperCase().equals(platform)) {
returngetComplexMobileBy(androidBys.value(), ByChained.class);
}
AndroidFindAllandroidFindAll = mobileField.getAnnotation(AndroidFindAll.class);
if (androidFindAll != null && ANDROID.toUpperCase().equals(platform)) {
returngetComplexMobileBy(androidFindAll.value(), ByAll.class);
}
//if now there is iOS iOSFindByiOSBy = mobileField.getAnnotation(iOSFindBy.class);
if (iOSBy != null && IOS.toUpperCase().equals(platform)) {
returngetMobileBy(iOSBy, getFilledValue(iOSBy));
}
iOSFindBysiOSBys = mobileField.getAnnotation(iOSFindBys.class);
if (iOSBys != null && IOS.toUpperCase().equals(platform)) {
returngetComplexMobileBy(iOSBys.value(), ByChained.class);
}
iOSFindAlliOSFindAll = mobileField.getAnnotation(iOSFindAll.class);
if (iOSFindAll != null && IOS.toUpperCase().equals(platform)) {
returngetComplexMobileBy(iOSFindAll.value(), ByAll.class);
} //If the field is not annotated Appium-specific annotations it attempts to get By-strategy using //Selenium @FindBy'sreturnsuper.buildBy();
}So the the addition parameter (platform) helps to rout possible Android strategies. |
TikhomirovSergey
commented
Dec 13, 2014
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.
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
commented
Dec 13, 2014
I`m testing mobile apps about two weeks and has not collected enough experience to confirm or refute it. :)
Yes it
I`ll write tests today, but here is situation what I want to ve avoid: @SelendroidFindBy(relevantlocatorstrategyforselendroid) //Here we suppose one WebElement object@AndroidFindAll(relevantlocatorstrategyforUIautomator) //Here we suppose List of web elementsprivateWebElementelement; // How we should typize this field in this case? Additional validations will strict such annotating.
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(...)
privateWebElementokButton;
``
Codelessononestring. Justshugar, morefreedom :) |
TikhomirovSergey
commented
Dec 13, 2014
Ok! :) I think if I've described the expected behavior so why is it illegal: @SelendroidFindBy(relevantlocatorstrategyforselendroid) //Here we suppose one WebElement object@AndroidFindAll(relevantlocatorstrategyforUIautomator) //It is actually the SINGLE element//which is found by few POSSIBLE locators. Here ByAll objects is used.privateWebElementelement; ? Please look at ByAll sources. If the list is supposed here then you will implement @SelendroidFindBy(relevantlocatorstrategyforselendroid)
@AndroidFindAll(relevantlocatorstrategyforUIautomator) privateList<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
commented
Dec 13, 2014
Actually the illegal way is: @SelendroidFindBy(relevantlocatorstrategyforselendroid)
@SelendroidFindBys(relevantlocatorstrategyforselendroid) @SelendroidFindAll(relevantlocatorstrategyforselendroid) privateWebElementelement; // 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
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
commented
Dec 13, 2014
T E S T SRunning io.appium.java_client.pagefactory_tests.AndroidPageObjectTest 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
commented
Dec 13, 2014
Ok! Thank you very much! I think that @Jonahss will merge it soon. |
[+] Add support for selendroid locators in page object annotations
Jonahss
commented
Dec 15, 2014
Cool ^.^ I'll run the tests and should be able to release a new version today. |
TikhomirovSergey
commented
Dec 15, 2014
@Jonahss please wait for one day. I'll propose one more pull request. It will be bug fix + new test. |
Jonahss
commented
Dec 15, 2014
Okay :) On Monday, December 15, 2014, Sergey Tikhomirov notifications@github.com
|
Since UiAutomator and Selendroid requires different locators I`ve added new annotations:
And added dependency of AUTOMATION_NAME capatibility.
So now is possible to create one page object which covers all andoid versions.