is it possible to get a css selector of an WebElement?
eg
var optionSelectors = mutableListOf<String>()
val options = selectWebElement?.findElements(By.cssSelector("option")).orEmpty()
for(option in options){
var optionSelector = option.getSelector()
optionSelectors.add(optionSelector)
}
return toJson(optionSelectors)
Thank you in advance
You can always use Reflection to get foundBy property value like:
Field field = element.getClass().getDeclaredField("foundBy");
field.setAccessible(true);
String foundBy = field.get(element).toString();
However the nature of your question is a little bit weird, given you found the element already you should know its selector, shouldn't you? If you want to interact with the Select option values you can go for the relevant Select class which has getOptions() function.
Also consider going for Page Object Model design pattern, it is one of best practices to keep your test logic separate from UI layer
Related
How to populate the value of this variable:
private val _urlList = MutableLiveData<List<Url>>()
of type Url:
data class Url(
val imgSrcUrl: String
)
with the incoming list of url strings from a firebase call?
Here is where the magic happens:
private fun getData(){
viewModelScope.launch {
try {
getImagesUrl {
"Here where I need to set the value of the variable to a listOf(it) with it being strings
of urls retrieved from firebase storage"
}
}catch (e: Exception){
"Handling the error"
}
}
}
Edit
The map function #dominicoder provided solved my problem, answer accepted.
Thank you all for your help
Your question is unclear because you're showing a live data of a single Url object but asking to stuff it with a list of strings. So first, your live data object needs to change to a list of Urls:
private val _urlList = MutableLiveData<List<Url>>()
Then, assuming getImagesUrl yields a list of strings, if I understood you correctly, then you would map that to a list of Urls:
getImagesUrl { listOfImageUrlStrings ->
_urlList.value = listOfImageUrlStrings.map { imageUrlString -> Url(imageUrlString) }
}
If that does not answer your question, you really need to review it and clarify.
You can set values on the MutableLiveDataObject in two ways (depends on what you're doing).
Setting the value as normal from the UI thread can be done with:
myLiveData.value = myobject
If you're setting it from a background thread like you might in a coroutine with a suspended function or async task etc then use:
myLiveData.postValue(myObject)
It's not clear from your question whether the LiveData is meant to hold a list as you mention both lists and single values. But your LiveData holds a set the values as a collection like a list, set or map. It's can be treated as a whole object so adding a value later needs to have the whole collection set again like:
myLiveData.value = mutableListOf<Url>()
//Response received and object created
myLiveData.value = myLiveData.value.apply {
add(myObject)
}
Or if the value is mutable updating the existing value (preferred as it's cleaner):
myLiveData.value.add(myObject)
The problem with that approach is you're exposing the map as a mutable/writeable object. Allowing accessors to change the values which you might not want.
So I am using a dynamic object and I can receive an API that lists all of the object's properties such as ID in one page, but that ID is the ID of a frame layout that is not interactable and only contains child such as textbox or error message.
what I'm trying to achieve is to know the properties of that parent object so that I can try to interact with the child, for example, the textbox EditText inside. I am able to interact with the child by using the XPath but haven't found a way to determine the parent by the determined ID.
I am trying to make automatic testing that only uses the API as a reference point to check what is the object to test so that in the future I don't have to change the test script in case of any changes in the configuration.
I can check the API for existing object properties like this:
public static checkIfExistForm1(String code){
def response = WS.sendRequest(findTestObject("Object Repository/WebService"))
def json = new JsonSlurper().parseText(response.getResponseBodyContent())
for(int i = 0;i < json.page.form.fieldset.size(); i++){
if(code.contentEquals(json.page.form.fieldset[i].control[0].id)){
return true;
break
}
}
return false;
}
and then I can make some kind of method to get the child of that object and try to interact with it
for example with this method I can select an object by XPath
public static getObjectID(String code){
//considering i know that this is the child, or creating another validation
//the child(EditText) also has ID "content-desc" that i can use
String dynamicIdPath = '//*[#content-desc="%s"]'
String xpath = String.format(dynamicIdPath,code)
TestObject to = new TestObject()
to.addProperty("xpath", ConditionType.EQUALS, xpath)
return to
}
any thoughts? thanks!
I am new to Android/Kotlin/Anko and I have a question regarding the way to access color (and probably other) resources from within Anko.
I know that there are helpers like textResource where you simply pass the R.string.my_color to simplify the process of setting resource strings but how about accessing colors using the Resources instance from the View class ?
Let’s say you have a subclass of Button and want to change the text color. If you use the textResource it will change the text string not the color, and if you use textColor then you must specify the real resource ID by using resources.getColor(R.color.my_color, null) which wouldn't be so annoying if you didn't have to pass the optional theme parameter (null here)
Is creating an extension on Resources useful here ?
fun Int.fromResources(resources: Resources): Int {
return resources.getColor(this, null)
}
What is the recommended way ?
EDIT
I changed the textColor value extension to do just that, which I found the cleanest thing to do except I have no idea if this is really Android friendly
var android.widget.TextView.textColor: Int
get() = throw AnkoException("'android.widget.TextView.textColor' property does not have a getter")
set(v) = setTextColor(resources.getColor(v, null))
I think you can use a property extension like this one instead of the one you suggested:
var TextView.textColorRes: Int
get() = throw PropertyWithoutGetterException("textColorRes")
set(#ColorRes v) = setTextColor(resources.getColor(v, null))
Or use ContextCompat as suggested by Damian Petla:
var TextView.textColorRes: Int
get() = throw PropertyWithoutGetterException("textColorRes")
set(#ColorRes v) = setTextColor(ContextCompat.getColor(context, v))
You should keep Anko's textColor:
Allows you to set a color directly without taking it from XML, if needed at some point
Prevents you from importing the wrong textColor (Anko's one or yours), same property names with different behaviour is not a good idea.
I have 2 ElementsCollections namely oddTableRowItems and evenTableRowItems:
private static ElementsCollection oddTableRowItems() {
return $$(By.className("odd"));
}
private static ElementsCollection evenTableRowItems() {
return $$(By.className("even"));
}
I want to combine the 2 in order to only do a for loop once. It is row items, and only there classnames differ for styling purposes and I can only identify them through classnames.
This is how I tried to combine it - but it does not work:
ElementsCollection rowElements = evenTableRowItems();
rowElements.addAll(oddTableRowItems());
I get an:
java.lang.UnsupportedOperationException
does anybody how can I combine the 2 ElementsCollections?
The API could probably be a little bit more friendly here. But this way you can combine two ElementsCollection instances. The key here is WebElementsCollectionWrapper class.
ElementsCollection evenElements = $$(By.className("even"));
ElementsCollection oddElements = $$(By.className("odd"));
List<SelenideElement> elementsCombined = new ArrayList<>(evenElement);
elementsCombined.addAll(oddElements);
WebElementsCollectionWrapper wrapper = new WebElementsCollectionWrapper(elementsCombined);
ElementsCollection selenideCollectionCombined = new ElementsCollection(wrapper);
All add* methods throw UnsupportedOperationException by design. It's because ElementsCollections represents a collection of existing web elements on a web page; and page elements cannot be modified by test. That's why you cannot add or remove elements on the page.
The easiest way is to select all matching elements at once:
$$(".odd,.even").shouldHave(size(10));
A little bit longer way is to compose a new list containing both collections:
List<String> newList = new ArrayList<String>();
newList.addAll($$(".odd"));
newList.addAll($$(".even"));
but your goal seems to be doubtful for me. You will get the list with invalid order. Why can it be useful? Why would one need to iterate all elements? I cannot imagine a use case for that.
According to the API:
Note that this implementation throws an UnsupportedOperationException unless add(int, E) is overridden.
You can try this code. This is work fine!
ArrayList<SelenideElement> newList = new ArrayList<SelenideElement>();
newList.addAll(Selenide.$$(By.className("odd"));
newList.addAll(Selenide.$$(By.className("even"));
I'm trying to use a single controller to list multiple similar collections so I can call different templates with the same controller. In fact, right now I have 6 controllers for listing and another 6 for forms but they're all duplicates.
I've made a non-functional plunker just to show how I intend it to work. I've avoided declaring routeProviders because knowing it wouldn't work I tried to make it as straight to the point as I could.
http://plnkr.co/edit/d06PcrJS5newhrmNy6EJ?p=preview
I've seen on stackoverflow how to declare a class with a dynamic name:
var str = "MyClass";
var obj = new window[str];
But as I have not been able to find where it's stored I'm not able to retrieve it.
Does anyone have a hint on how to do this?
You can use Angular's injector to return the service instance you want. For example:
app.controller('NodeListCtrl', function($scope, $location, $injector) {
var modelName = $location.path().split("/")[1];
$scope.modelName = modelName.charAt(0).toUpperCase() + modelName.slice(1);
$scope.nodes = $injector.get($scope.modelName).query();
});
Note: Don't forget to add the $injector to the controller's function signature.
jsfiddle: http://jsfiddle.net/bmleite/Mvk2y/