-
-
Notifications
You must be signed in to change notification settings - Fork 765
[+] Add support for selendroid locators in page object annotations #140
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
Conversation
[-] fix variable names
|
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:
|
|
If it's good by @TikhomirovSergey then it's good by me :) Thanks for the Pr @clicman, can you address the comments above? |
|
There is one more remark.
|
|
@TikhomirovSergey you right uiAutomator and accessibility needs to be removed (copy-paste issue). @Jonahss about validations: I |
|
@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! :) 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. |
|
So 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. |
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. |
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(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.
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 :) |
|
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 |
|
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); |
|
Wow! I didn`t know what ByAll is agnostic. You totally right. Thanks. I will change validations. |
[*] Remove unnecessary validators [+] Add selendroid tests
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? |
|
Ok! Thank you very much! I think that @Jonahss will merge it soon. |
[+] Add support for selendroid locators in page object annotations
|
Cool ^.^ I'll run the tests and should be able to release a new version today. |
|
@Jonahss please wait for one day. I'll propose one more pull request. It will be bug fix + new test. |
|
Okay :) On Monday, December 15, 2014, Sergey Tikhomirov [email protected]
|
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.