How to pass conditional varible in using Selenium Java? - java

private static final String Accept = "Accept & continue";
public void acceptWorkspaceCreation() {
//Wait for Set up a Work Profile Screen for Android 9 Pixel
waitUtilByText(180, Accept);
assertTrue("Couldn't click on Accept & continue.",
findElementByIdAndClick("com.android.managedprovisioning", "next_button"));
}
public boolean waitUtilByText(int seconds, String text){
String textStr = "//*[#text='" + text + "//*[#id='";
return !new WebDriverWait(this.driver, seconds).until(ExpectedConditions.presenceOfAllElementsLocatedBy(By
.xpath(textStr))).isEmpty();
}
For some cases Accept value comes in caps 'ACCEPT & CONTINUE', How to validate both the strings in selenium.

The condition you are waiting for only checks for one thing:
ExpectedConditions.presenceOfAllElementsLocatedBy(By.xpath(textStr))
So, presumably (as I am not familiar with Selenium), you can use the ExpectedConditions.or method to check a number of conditions, and wait for just one to be true:
ExpectedConditions.or(
ExpectedConditions.presenceOfAllElementsLocatedBy(By.xpath(textStr)),
ExpectedConditions.presenceOfAllElementsLocatedBy(By.xpath(textStr2)))
where textStr2 is the alternative text you are looking for.

I'd suggest using native Android UiSelector's textMatches method where You'd write matcher for both upper and lower case texts. If you want to stick to the Xpath anyways - it also has a text matcher. But first of all, investigate why the text changes - that's not a good practice.

Related

Find locator in selenium by name with a parameter

I'm struggling to figure out how to find an element by its name, but not by multiplying elements locators but to create one for its kind and passing a parameter. E.g there's a page with 10 buttons 'Add to Cart' for different items ('Laptop A', 'Laptop b','Laptop c', etc), so instead of creating 3 different elements I want to have one, something like >>
webElement elementByName(String itemName) = driver.findElement(By.xpath("//div[#button='add to cart' and #title = '" + itemName+ "']"))
Does anything like this exist in selenium? I'm new to selenium and could not find anything similar. thanks
Yes, it is possible. Before I share the 'answer', I would like to point out the following:
Some things to note
The xpath that you need to provide is nothing special to Selenium. It is a W3C standard.
https://www.w3.org/TR/2017/REC-xpath-datamodel-31-20170321/
If you're using Chrome, before even attempting to use it in Selenium, you can first test out your xpath by going to Developer Tools (F12) and pressing 'CTRL+F', and typing in the xpath in the search box that appears.
If your xpath matches an element, it will be highlighted in yellow. If that xpath works, it should also work if you pass that same xpath into Selenium, unless there are other reasons such as framework being used, etc. For instance, Shadow DOM xpaths are not especially friendly to use.
Searching by Visible Text
Going by your example of Laptop A; Laptop B; Laptop C, if you want to find the element based on visible text, then you can use the text() function (that xpath provides).
driver.findElement(By.xpath("//div[text()='Laptop A']");
driver.findElement(By.xpath("//div[text()='Laptop B']");
With the above in mind, you can thus put the strings A and B into an array and loop through them if you want:
for(int i=0; i<arr.size; i++){
driver.findElement(By.xpath("//div[text()='Laptop " + arr[i] + "']");
}
Additionally, you can also use the contains function that sort of acts like a regex:
driver.findElement(By.xpath("//div[contains(.,'Laptop')]");
This will match anything that has a visible text of Laptop, i.e. Laptop A, Laptop b, This is a Laptop, etc.
EDIT: based on comments below
public WebElement selectButtonByName(String name){
return driver.findElement(By.xpath("//div[#button='add to cart' and #title = '" + name+ "']"));
}
And then by using it:
WebElement tvElement = selectButtonByName("TV").click();
If i understood correctly, you need to wrap your the findElement method and create a custom method for it, best practice is always wrap and have custom methods for all driver related actions, which will help you on the long run and will be scalable. here is an oversimplified example:
first step is to wrap your driver:
public class MyDriver {
//initialise your webdriver, the way you see fit and lets assume you name it
myDriver;
// create your custom find element method
public WebElement findElement(final By by){
return myDriver.findElement(by);
}
// create a method which uses the findElemet method and passes the webelement to next
// method
public WebElement click(final By bys){
return clickElement(findElement(bys);
}
// this is the click method, which you can use directly for webelements
public WebElement clickElement(final WebElement element){
Actions action = new Action(myDriver);
action.moveToElement(element).click(element).build().perform();
return element;
}
}
// Now that we have our basic wrapper, lets use it for your needs:
public class YourTestCase {
MyDriver driver = new MyDriver(); //this is not best way to do it, just an example
// now you can use whatever parameter you like and add it with custom xpath logic
private WebElement myElementFinder(final String parameter){
return driver.findElement(By.xpath("//div[#button='add to cart'][#title = '" +
parameter + "']"));
}
// here you click the button, by passing the passing only
public void clickButton(final String buttonText){
driver.click(myElementFinder(buttonText))
}
}
By taking this logic you can build a solid framework, hope it helps, comment if need more explanation, cheers.

Selenium made a sendKeys for two fields instead of one for some reason

I made a pretty simple selenium test, where I want to open web page, clear field value, start entering text for this field, select first value from the hint drop down.
Web site is aviasales.com (I just found some site with a lot of controls, this is not an advertisement)
I did
DriverFactory.getDriver().findElement(By.id("flights-origin-prepop-whitelabel_en")).clear();
and it was working perfectly, I also checked via console that this is the only one object on a page like:
document.getElementById('flights-origin-prepop-whitelabel_en')
So, in next line I'm sending value:
DriverFactory.getDriver().findElement(By.id("flights-origin-prepop-whitelabel_en")).sendKeys("LAX");
but it send LAX value for both "flights-origin-prepop-whitelabel_en" and "flights-destination-prepop-whitelabel_en" for some reason, then i tried
DriverFactory.getDriver().findElement(By.id("//input[#id='flights-destination-prepop-whitelabel_en'][#placeholder='Destination']")).sendKeys(destinationAirport);
but I got the same result:
What could be a reason and how to fix this?
Thank you!
Yep... there's some weird behavior going on there. The site is copying whatever is entered into the first field into the second for reason I don't understand. I gave up trying to understand it and found a way around it.
Whenever I write code that I know I'm going to reuse, I put them into functions. Here's the script code
driver.navigate().to(url);
setOrigin("LAX");
setDestination("DFW");
...and since you are likely to use these repeatedly, the support functions.
public static void setOrigin(String origin)
{
WebElement e = driver.findElement(By.id("flights-origin-prepop-whitelabel_en"));
e.click();
e.clear();
e.sendKeys(origin);
e.sendKeys(Keys.TAB);
}
public static void setDestination(String dest)
{
WebElement e = driver.findElement(By.id("flights-destination-prepop-whitelabel_en"));
e.click();
e.clear();
e.sendKeys(dest);
e.sendKeys(Keys.TAB);
}
You can see the functions but basically I click in the field, clear the text (because usually there's something already in there), send the text, and then press to move out of the field and choose the default (first choice).
The reason of your issue is the ORIGIN and DESTINATION inputbox binded keyboard event which used to supply an autocomplete list according to your typed characters.
The binded keyborad event breaks the normal sendKeys() functionality. I met similar case in my projects and questions on StackOverFlow.
I tried input 'GSO' into DESTINATION by sendKeys('GSO'), but I get 'GGSSOO' on page after the sendKeys() complete.
To resolve your problem, we can't use sendKeys(), we have to use executeScript() to set the value by javascript in backgroud. But executeScript() won't fire keyborad event so you won't get the autocomplete list. So we need find out a way to fire keyborady event after set value by javascript.
Below code snippet worked on chrome when i tested on aviasales.com:
private void inputAirport(WebElement targetEle, String city) {
String script = "arguments[0].value = arguments[1]";
// set value by javascript in background
((JavascriptExecutor) driver).executeScript(script, targetEle, city + "6");
// wait 1s
Thread.sleep(1000);
// press backspace key to delete the last character to fire keyborad event
targetEle.sendKeys(Keys.BACK_SPACE);
// wait 2s to wait autocomplete list pop-up
Thread.sleep(2000);
// choose the first item of autocomplete list
driver.findElement(By.cssSelector("ul.mewtwo-autocomplete-list > li:nth-child(1)")).click();
}
public void inputOrigin(String city) {
WebElement target = driver.findElement(By.id("flights-origin-prepop-whitelabel_en"));
return inputAirport(target, city);
}
public void inputDestination(String city) {
WebElement target = driver.findElement(By.id("flights-origin-prepopflights-destination-prepop-whitelabel_en"));
return inputAirport(target, city);
}

Sendkeys dropping letters inconsistently

I'm using (new Actions(driver).sendKeys(String).perform() to send input to webdriver. However, I keep getting inconsistent results. Sometimes String is sent in perfectly, sometimes missing one or the other letter, even when the string itself is just a few characters in length.
I even tried sending the string one character at a time (using a for loop) and I'm still getting the same inconsistent behaviour - it's dropping keys less than before, but still not 100% accurate.
What could be possible reason for this, and how to solve this issue?
I'm using Chromedriver, Java/JUnit and the keyboard layout is set to US.
I'm still unclear exactly what was causing this - I see inconsistent failure to send all keys to some extjs components. I'm not sure if this is extjs doing something wierd or selenium being a bit flaky. We weren't doing anything odd in making the string or in calling send keys (except the additional wait to check the element is enabled and displayed) though so it doesn't feel like its something in our code.
My solution was to repeatedly send the keys until they visibly stick. I've wrapped the selenium api for a number of reasons so all calls to WebElements are routed through a class called WrappedElement which has the send keys method:
public void SendKeys(string text)
{
Func<string> errorMessage = () => string.Format("couldn't send keys {0} entered either {1} or {2}", text, Text, Value);
Action actionToTry = () =>
{
_element.Clear();
RawSendKeys(text);
};
Wait.RepeatedlyTry(actionToTry, () => Text == text || Value == text, errorMessage);
}
public void RawSendKeys(string text)
{
_element.SendKeys(text);
}
A simplified version of the wait method looks like this:
public void RepeatedlyTry(Action actionToTry, Func<bool> completed, Func<string> errorMessage)
{
Wait.Until(d =>
{
actionToTry();
return completed();
});
}
Where Until just calls Until on a configured DefaultWait

Prevent typing in the same style in JTextPane

forgive me for a possible misleading title, but the problem is a bit hard to describe.
I'm currently trying to create a basic texteditor using a JTextPane in Java and I've run into an issue.
As you know in most texteditors you can put your caret/cursor behind a piece of styled text (for example text which is bold) and then you can continue typing in that same style (Eg. append more bold characters).
Luckely this is present by default in the JTextPane, but I want to disable it for a certain style. Mainly the URL-style I coded (basicly this one just sets the HTML.Attribute.HREF attribute in the style to an URL).
So if I would put my caret behind a word (or piece of text) which is an URL, I want to ensure that the next characters which will be added, will not be in the URL-style.
Eg. I think tinymce has this behaviour:
You select text
Click on the Insert URL button
Insert the URL
Place the cursor right after the URL and start typing again in normal style
Is there a way to enforce this behaviour in a JTextPane?
I was thinking about something like this:
Adding a listener for content changes in the document
Check if the added characters were placed right behind a piece of text with the URLstyle
If that was the case => remove the "href" attribute from the style of those characters
The code i use for setting the URL-style to the selected text can be found below. "dot" and "mark" are retrieved from the caret.
SimpleAttributeSet attr = new SimpleAttributeSet(doc.getCharacterElement(dot).getAttributes());
StyleConstants.setUnderline(attr, true);
StyleConstants.setForeground(attr, Color.BLUE);
attr.addAttribute(HTML.Attribute.HREF, url);
doc.setCharacterAttributes((dot < mark) ? dot : mark, length, attr, true);
(Note: To be able to tell the difference between normal "blue underlined" text and an URL, the HREF attribute is used for an URL.)
PS: This is my first question here, so hopefully I gave enough information. ;)
Language: Java, JDK 1.7
Thanks in advance.
Add a CaretListener to detect move and check whether current caret position needs the style reset. If it's detected use
StyledEditorKit's method
public MutableAttributeSet getInputAttributes()
Here just remove the attributes you don't need (URL, blue, underline).
I thought I'd share my solution to the problem (found with the help of StanislavL's answer - thanks again for putting me on the right track).
The following method is called from within a caretlistener, passing the attributes found via the "getInputAttributes"-function and the dot and mark of the caret.
private void blockURLTyping(MutableAttributeSet inputAttr, int dot, int mark)
{
StyledDocument doc = getStyledDocument();
int begin = (dot < mark) ? dot - 1 : mark - 1;
if(begin >= 0)
{
Element dotEl = doc.getCharacterElement(begin);
Element markEl = doc.getCharacterElement((dot < mark) ? mark : dot);
AttributeSet dotAttr = dotEl.getAttributes();
AttributeSet markAttr = markEl.getAttributes();
if(dotAttr.isDefined(HTML.Attribute.HREF)) // Ensure atleast one of them isn't null
{
if(dotAttr.getAttribute(HTML.Attribute.HREF) == markAttr.getAttribute(HTML.Attribute.HREF))
{
inputAttr.addAttribute(HTML.Attribute.HREF, dotAttr.getAttribute(HTML.Attribute.HREF));
inputAttr.addAttribute(StyleConstants.Foreground, Color.BLUE);
inputAttr.addAttribute(StyleConstants.Underline, true);
return;
}
}
}
if(inputAttr.isDefined(HTML.Attribute.HREF)) // In all other cases => remove
{
inputAttr.removeAttribute(HTML.Attribute.HREF);
inputAttr.removeAttribute(StyleConstants.Foreground);
inputAttr.removeAttribute(StyleConstants.Underline);
}
}
Important note; The inputAttributes do not update when the caretposition changes but stays within the same element.
So: when the caret is positioned at the end of the URL, behind the last character => you remove the three attributes you can see in the code above => However when the caret is moved to another position within the URL, the attribute stays removed because the set does not update.
So in practice this means that when you remove attributes from the attributeset, they will stay removed untill the StyledEditorKit updates the inputattributes.
To work around this problem I decided to add the attributes again if the caret is in the middle of an URL, allowing you to insert characters in the middle of the URL - but not append or prepend characters (like I wanted).
The code can probably be optimized a bit more because in most cases dot==mark, but I wanted to share this solution.
PS: The comparison of the HREF-attributes is to deal with the situation where two different URLs are positioned next to eachother in a text. It basicly should check if they are both different instances of a certain object even if the URL itself might be the same.
Code that calls this function:
#Override
protected void fireCaretUpdate(CaretEvent e)
{
super.fireCaretUpdate(e);
MutableAttributeSet attr = getStyledEditorKit().getInputAttributes();
int dot = e.getDot();
int mark = e.getMark();
blockURLTyping(attr, dot, mark);
...
}

IPhone password field in AWT/SWT?

I want to create a special Password Dialog for my eclipse product, which is used with an on screen keyboard.
It would be very nice, if i could use a component like the IPhone Password field. In this field, the added character is shown for a second and after the second it is converted into the '*' character for hiding the complete password.
Did a jar/library exists, this is implemented in AWT or SWT?
Edit:
I could trying to implement it from scratch (SWT), but for these i would have to create a very special and complicated KeyListener for the password Text component. I would have to catch the keyReleased event and set the characters manually into the field.
So far i was not able to find any libraries in the web. Suggestion how this can be implemented are welcome too.
This is not really a full answer, rather than a discussion starter and I don't know of any out-of-the-box widgets which can do that.
My first idea was to inheriting the swt Text widget and overriding setEchoChar et al., but after looking at the code this doesn't really seem feasible, because this method is merely a wrapper around:
OS.SendMessage (handle, OS.EM_SETPASSWORDCHAR, echo, 0);
If anyone would know the OS specific low-level implementation, that might be helpful.
Anyway, on to a different approach. I would avoid the KeyListener and use a ModifyListener on the Text-Widget.
void addModifyListener(ModifyListener listener)
You could then build a wrapper which catches the entered text using this listener, appends it to a locally held string/stringbuffer (or e.g. the Eclipse Preferencestore) and send a modified full text to the Text widget using setText(String s), replacing all characters except the last by an echo character (e.g. *).
myText.setText((s.substring(0, s.length()-1)).replaceAll("[\\s\\S]","*")+s.charAt(s.length()-1));
This is a bit of a kludge, but it should work.
The not so straightforward bit is the 1 second timing, without stalling the whole view...
Depending on what Jules said the following code is some kind of working.
The code is quick and fast and i would like to have a more thread safe solution.
originalString = new StringBuffer();
passwordField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
synchronized (passwordField) {
String s = passwordField.getText();
String newS = s.replaceAll("[\\s\\S]", "*");
if (newS.equals(s)) {
while (originalString.length() > s.length()) {
originalString = originalString.deleteCharAt(originalString.length() - 1);
}
usernameField.setText(originalString.toString());
return;
}
if (originalString.length() < s.length()) {
originalString.append(s.charAt(s.length() - 1));
}
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
}
passwordField.setText(newS);
}
passwordField.redraw();
passwordField.setSelection(passwordField.getText().length());
}
});
Key Events are cached, so you can add more characters, also when the Thread is waiting.
Another Problem is the Cursor handling. the Cursor always moves to the first position, when you set the Text.
I think when this is working it is very near to the iphone solution.

Categories