In Java, Selenium, you can wait until a text is present in a webelement (with a WebDriverWait):
wait.until(ExpectedConditions.textToBePresentInElement(webelement, expectedMessage));
However, what do you do when you don't want just expectedMessage to be present in the element (= expectedMessage being a substring of webelement.getText()), but to be the exact text of the webelement (=expectedMessage being the same string as webelement.getText())?
Selenium does provide the function:
wait.until(ExpectedConditions.textToBe(locator, expectedMessage));
but when you have gathered webelements by the locators with #FindBy in your page class, it's awkward to make the locators again directly accessible to test classes.
How can this be solved?
You can create your own ExpectedCondition:
public static ExpectedCondition<Boolean> waitForTextInElementEquals(WebElement elm, String text) {
return new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
try {
String elementText = elm.getText();
return elementText.equals(text);
} catch (StaleElementReferenceException var3) {
return null;
}
}
public String toString() {
return String.format("text ('%s') to be present in element %s", text, elm);
}
};
}
Which you can use just like the ExpectedConditions already in WebDriverWait:
WebDriverWait wait = new WebDriverWait(WebDriver, 30, 1000);
wait.until(waitForTextInElementEquals(foo, bar));
There's another simpler solution:
WebDriverWait wait = new WebDriverWait(webdriver, waitForElementTimeout).until(ExpectedConditions.attributeToBe(webelement, "text", expected));
Tested with selenium 3.8.1.
Related
I have created a Page Object in Java with Appium and Selenium that is currently working for an Android app as shown below:
public class MattVerifyPage extends PageObject{
private AppiumDriver driver = FrameworkInitialize.driver;
By verifyTitle = By.xpath("/hierarchy/android.widget.TextView");
public void verifyTitle(String expectedTitle){
String actualTitle = driver.findElement(verifyTitle).getText();
However, I need it to work an the Android app and the iOS app, the xpath selector is different for both apps. I think I need to do something like this:
#AndroidFindBy(xpath = “androidxpath”)
#iOSFindBy(xpath = “iOSxpath”)
public MobileElement verifyTitle ;
This would mean regardless of whether I am using Android or iOS I would still just use the one variable called 'verifyTitle'.
However, when I do this, the driver.findElement line (String actualTitle = driver.findElement(verifyTitle).getText() shows the following error:
findElement
(org.openqa.selenium.By)
in DefaultGenericMobileDriver cannot be applied
to
(io.appium.java_client.MobileElement)
I think I am comparing AppiumElements with SeleniumElements but I’m not sure how to resolve it.
Any help would be greatly appreciated.
Thanks
Matt
Yes, lots of mixing of object types in your original example. You're on the right track with the #OSFindBy annotations. Once you have those defined you already have the element so no need to find it again. The following would be all you'd need:
verifyTitle.getText()
See this blog post for more information on the Page Object Model (POM).
Summary:
import all the good stuff including PageFactory;
public class YourPage {
private WebDriver driver;
public YourPage(AppiumDriver<MobileElement> driver) {
this.driver = driver;
PageFactory.initElements(new AppiumFieldDecorator(driver), this);
}
#AndroidFindBy(id = "android_button")
#iOSFindBy(id = "ios_button")
private MobileElement that_button;
public void pushTheButton() {
that_button.click()
}
}
Note: above code is untested / written off the top of my head / I don't write Java for a living. Prone to error, but should give you the idea.
This Working with Me, My Project Selenium, TestNG and Appium use PageFactory.initElements
public class Login extends Setup {
#Test
public void loginAlert() throws InterruptedException {
Button button = new Button(driver);
PageFactory.initElements(driver, button);
Input input = new Input(driver);
PageFactory.initElements(driver, input);
Input input1 = new Input(driver);
System.out.println("Test Alert Login");
button.ById("navigation_more");
button.ById("btnLogin");
input.ById("et_email_or_phone_number", "dikakoko.com");
input1.ById("tet_password", "dikakoko");
}
}
Below this is the function I called above.
public class Input {
AppiumDriver<MobileElement> driver;
Root root = new Root();
public Input(AppiumDriver<MobileElement> driver) {
this.driver = driver;
}
public void ById(String selector, String textValue) {
MobileElement element = driver.findElement(By.id(root.element() + ":id/" + selector));
waitForVisible(driver, element);
Actions actions = new Actions(driver);
actions.moveToElement(element);
actions.click();
actions.sendKeys(textValue);
actions.build().perform();
System.out.println("Input: " + textValue);
}
private void waitForVisible(AppiumDriver<MobileElement> driver, MobileElement element) {
try {
Thread.sleep(5000);
System.out.println("Waiting for element visibility");
WebDriverWait wait = new WebDriverWait(driver, 20);
wait.until(ExpectedConditions.visibilityOf(element));
} catch (Exception e) {
e.printStackTrace();
}
}
}
and This
public class Button {
AppiumDriver<MobileElement> driver;
Root root = new Root();
public Button(AppiumDriver<MobileElement> driver) {
this.driver = driver;
}
public void ById(String selector) {
MobileElement element = driver.findElement(By.id(root.element() + ":id/" + selector));
Actions actions = new Actions(driver);
actions.moveToElement(element);
actions.click();
actions.build().perform();
System.out.println("Button is Clicked!");
}
}
I Use This
Button button = new Button(driver);
PageFactory.initElements(driver, button);
Input input = new Input(driver);
PageFactory.initElements(driver, input);
My References : From www.seleniumeasy.com
I have a problem with Selenium tests for Java when using standalone-webdriver-firefox docker image v.2.53.0. The problem is when I want insert text to input with placehoder. And this placehoder sometimes appear or not. I tried clean input before insert text but the result was the same. I increased wait time but that not help. When I using APIwebdriver this problem not appear.
These are my methods
#FindBy(how = How.XPATH, using = "//input[contains(#id, 'redirect-uri')]")
private List<WebElement> inputTextRedirectUriElements;
public EdEnvironmentPage enterRedirectUri(String redirectUri) {
WebElement webElement = inputTextRedirectUriElements.get(inputTextRedirectUriElements.size() -1);
webElement.sendKeys(redirectUri);
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("arguments[0].blur(); return true", webElement);
WaitUtil.waitUntilPageIsLoaded(driver);
return this;
}
public EdEnvironmentPage enterRedirectUriWithoutHttps(String redirectUri) {
WebElement webElement = inputTextRedirectUriElements.get(inputTextRedirectUriElements.size() -1);
webElement.clear();
webElement.sendKeys(redirectUri);
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("arguments[0].blur(); return true", webElement);
WaitUtil.waitUntilPageIsLoaded(driver);
return this;
}
WaitUtil
public static void waitUntilPageIsLoaded(WebDriver driver) {
new WebDriverWait(driver, 30).until(ExpectedConditions.and(
ExpectedConditions.presenceOfElementLocated(By.id("loading-completed")),
ExpectedConditions.invisibilityOfElementLocated(By.className("loader"))
));
}
Problem resolve change selenium version to 2.53.1
I am using this code to check for invisibility:
WebDriverWait wait = new WebDriverWait(driver,40);
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath(<some xpath>)));
This works perfectly if there is only one element corresponding to the xpath in the webpage.
I have three in the webpage which I am trying to write a script for, and I need selenium to wait for all three.
Note: I am not using absolute xpath.
ExpectedConditions.invisibilityOfElementLocated check for the first element. In your case you could write your own implementation of ExpectedCondition where you have to check if the object is displayed for each of the element which is found.
For Example (not tested) :
private static void waitTillAllVisible(WebDriverWait wait, By locator) {
wait.until(new ExpectedCondition<Boolean>() {
#Override
public Boolean apply(WebDriver driver) {
Iterator<WebElement> eleIterator = driver.findElements(locator).iterator();
while (eleIterator.hasNext()) {
boolean displayed = false;
try {
displayed = eleIterator.next().isDisplayed();
}
catch (NoSuchElementException | StaleElementReferenceException e) {
// 'No such element' or 'Stale' means element is not available on the page
displayed = false;
}
if (displayed) {
// return false even if one of them is displayed.
return false;
}
}
// this means all are not displayed/invisible
return true;
}
});
}
I'm unable to click on an element within a list, once i have clicked on the dropdown list?
Method I have created which Doesn't work.
public static void waitForTextToAppearAndClick(WebDriver driver, WebElement element, String textToAppear) throws InterruptedException{
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.visibilityOf(element));
WebElement locator = element;
locator.click();
WebElement textToClick = driver.findElement(By.linkText(textToAppear));
wait.until(ExpectedConditions.elementToBeClickable(textToClick));
textToClick.click();
}
Using thread.sleep seems to works but I don't want to use this method, can anybody recommend a way to wait and click on a specific text element once I have clicked on the primary button?
public static void waitForTextToAppearAndClick(WebDriver driver, WebElement element, String textToAppear) throws InterruptedException{
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.visibilityOf(element));
WebElement locator = element;
locator.click();
Thread.sleep(3000);
driver.findElement(By.linkText(textToAppear)).click();;
}
Please note I need to click on BBQ Sauce, the thread.sleep() is successful when needing to click on BBQ Sauce
Thanks your for help
Use your own implementation of FluentWait in order to wait until text is present after your click:
Wait wait = new FluentWait<>(this.driver)
.withTimeout(driverTimeoutSeconds, TimeUnit.SECONDS)
.pollingEvery(500, TimeUnit.MILLISECONDS)
.ignoring(StaleElementReferenceException.class)
.ignoring(NoSuchElementException.class)
.ignoring(ElementNotVisibleException.class);
WebElement foo = wait.until(new Function() {
public WebElement apply(WebDriver driver) {
return element.getText().length() > 0;
}
});
all thanks for your help the following method seems to of done the trick:
public static void waitForTextToAppearAndClick(WebDriver driver, WebElement element, String textToAppear) throws InterruptedException{
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.visibilityOf(element));
WebElement locator = element;
locator.click();
wait.until(ExpectedConditions.elementToBeClickable(By.linkText(textToAppear)));
driver.findElement(By.linkText(textToAppear)).click();
}
I am using WebDriver with Java binding. I am using generic methods for element waiting. One of them is called waitByPageTitle.
Here is my definition for this method:
public void waitByPageTitle(WebDriver driver, String pageTitle) {
WebDriverWait wait = new WebDriverWait(driver, DEFAULT_IMPLICIT_WAIT);
try {
wait.until(ExpectedConditions.titleContains(pageTitle));
} catch (TimeoutException e) {
...
}
}
In my page objects, when a method needs to wait by a page title, I pass arguments to this method. But there are scenarios where the page title can be different based on different events.
How do I change the generic waitByPageTitle method so it accepts multiple arguments, and can wait by any one of them which ever it sees the first?
Thanks.
You can use FluentWait and Java varargs
// This method will accept any number of titles
public void waitUntilTextChanges(WebDriver driver, String... titles) {
new FluentWait<WebDriver>(driver)
.withTimeout(60, TimeUnit.SECONDS)
.pollingEvery(10, TimeUnit.MILLISECONDS)
.until(new Predicate<WebDriver>() {
public boolean apply(WebDriver d) {
boolean titleMatched = false;
// Get current window title
String windowTitle = driver.getTitle();
for(String title : titles){
// Iterate through all input titles and compare with window title
titleMatched = windowTitle.equalsIgnoreCase(title);
// If match found, exit
if(titleMatched){
break;
}
}
return titleMatched;
}
});
}