Issue
I am using selenium junit testing. If i try with selenium ide, the test goes well, but with junit it gives me this error:
org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"#firstName"}
I think that the span doesn't redirect to the page it should (but in normal schedule it does).
@Test
public void adminCreateDoc() {
driver.get("http://localhost:8080/login");
driver.manage().window().setSize(new Dimension(550, 706));
driver.findElement(By.linkText("Accesso amministratori")).click();
driver.findElement(By.id("username")).sendKeys("8245");
driver.findElement(By.id("password")).click();
driver.findElement(By.id("password")).sendKeys("prova1");
driver.findElement(By.cssSelector(".btn")).click();
driver.findElement(By.id("btn_createDoc")).click();
driver.findElement(By.id("firstName")).click();
driver.findElement(By.id("firstName")).sendKeys("Marco");
driver.findElement(By.id("lastName")).click();
driver.findElement(By.id("lastName")).sendKeys("Battiato");
driver.findElement(By.id("doc_type")).click();
driver.findElement(By.id("doc_type")).sendKeys("Cardiologo");
driver.findElement(By.id("id")).click();
driver.findElement(By.id("id")).sendKeys("855555");
driver.findElement(By.id("password")).click();
driver.findElement(By.id("password")).sendKeys("prova1");
driver.findElement(By.cssSelector(".glyphicon-send")).click();
}
I've tried with xpath, but without success...
HTML page with span redirection:
<body>
<div class="container text-center">
<div align="center">
<h2>Gestione dottori</h2>
<table class="table table-striped table-responsive-md">
<tr>
<th>ID</th>
<th>Nome</th>
<th>Specialità</th>
</tr>
<tr th:each="doc: ${listDoc}">
<td th:text="${doc.getId()}"></td>
<td>Dr. <span th:text="${doc.getFirstName()}"></span> <span th:text="${doc.getLastName()}"></span></td>
<td th:text="${doc.getDoc_type()}"></td>
</tr>
</table>
<a th:href = "@{/admin_createdoc}"><span id="btn_createDoc" class="plus bg-dark" >+</span></a>
<hr>
<div class="col col-lg-2 align-self-center">
<div class="p-1">
<form th:action="@{/admin_logout}" method=post>
<button name="btn_logout_profile" id="btn_logout_profile"
type="submit" class="btn btn-primary">Log Out</button>
</form>
</div>
</div>
</div>
</div>
</body>
Which redirect to this page:
<body>
<div class="container">
<div class="d-flex align-items-center justify-content-start">
<form th:action="@{/admin_homepage}" method=get>
<button name="btn_back_profile" id="btn_back_profile" type="submit"
class="btn btn-info">
<span class="fas fa-chevron-left"></span>
</button>
</form>
</div>
<br>
<div style='text-align: center'>
<form class="well form-horizontal" action="#"
th:action="@{/saveDoctor}" th:object="${doc}" method="POST"
id="contact_form">
<fieldset>
<!-- Form Name -->
<legend>
<center>
<h2>
<b>Doctor Creation Form</b>
</h2>
</center>
</legend>
<br>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label">First Name</label>
<div class="col-md-4 inputGroupContainer">
<div class="input-group">
<span class="input-group-addon"><i
class="glyphicon glyphicon-user"></i></span> <input name="first_name"
th:field="*{firstName}" placeholder="Doc First Name"
class="form-control" type="text" required>
</div>
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label">Last Name</label>
<div class="col-md-4 inputGroupContainer">
<div class="input-group">
<span class="input-group-addon"><i
class="glyphicon glyphicon-user"></i></span> <input name="last_name"
th:field="*{lastName}" placeholder="Doc Last Name"
class="form-control" type="text" required>
</div>
</div>
</div>
<div class="form-group">
<label class="col-md-4 control-label">Type</label>
<div class="col-md-4 inputGroupContainer">
<div class="input-group">
<span class="input-group-addon"><i
class="glyphicon glyphicon-list"></i></span> <input name="doc_type"
th:field="*{doc_type}" placeholder="Doc Type"
class="form-control" type="text" required>
</div>
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label">Id</label>
<div class="col-md-4 inputGroupContainer">
<div class="input-group">
<span class="input-group-addon"><i
class="glyphicon glyphicon-user"></i></span> <input name="id"
th:field="*{id}" placeholder="Doc ID" class="form-control"
type="number" required>
</div>
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label">Password</label>
<div class="col-md-4 inputGroupContainer">
<div class="input-group">
<span class="input-group-addon"><i
class="glyphicon glyphicon-user"></i></span> <input
th:field="*{password}" name="doc_password"
placeholder="Doc Password" class="form-control" type="password"
required>
</div>
</div>
</div>
<!-- Button -->
<div class="form-group" align="center">
<label class="col-md-4 control-label"></label>
<div class="col-md-4">
<br>
<button type="submit" class="btn btn-warning">
SUBMIT <span class="glyphicon glyphicon-send"></span>
</button>
</div>
</div>
</fieldset>
</form>
<div>
<form th:action="@{/admin_logout}" method=post>
<button name="btn_logout_profile" id="btn_logout_profile"
type="submit" class="btn btn-primary">Log Out</button>
</form>
</div>
</div>
</div>
</body>
Thank you for your time!
Solution
The failure is happening on this line
driver.findElement(By.id("firstName")).click();
ID as a CSS selector is #firstName
. Since this is right after a page transition, my guess is that the page isn't fully loaded before the next line of code is run. This can sometimes happen on a modern site where the page is loaded but there's still stuff being loaded asynchronously in the background. The fix is to add a wait, specifically a WebDriverWait
, to the following line. That is one option...
Instead of adding a wait piecemeal when it's found that you need it, I prefer to write helper methods that take care of common actions like click()
, sendKeys()
, etc. and then let those methods take care of the specific waits for me.
public void click(By locator) {
new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(locator)).click();
}
public void sendKeys(By locator, String text) {
findElement(locator).sendKeys(text);
}
public WebElement findElement(By locator) {
return new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(locator));
}
Then you can change your script to
@Test
public void adminCreateDoc() {
driver.get("http://localhost:8080/login");
driver.manage().window().setSize(new Dimension(550, 706));
click(By.linkText("Accesso amministratori"));
sendKeys(By.id("username"), "8245");
sendKeys(By.id("password"), "prova1");
click(By.cssSelector(".btn"));
click(By.id("btn_createDoc"));
sendKeys(By.id("firstName"), "Marco");
sendKeys(By.id("lastName"), "Battiato");
sendKeys(By.id("doc_type"), "Cardiologo");
sendKeys(By.id("id"), "855555");
sendKeys(By.id("password"), "prova1");
click(By.cssSelector(".glyphicon-send"));
}
which I think makes it more readable, adds waits to everything in case it's needed with no extra code, and so on...
The next level is using the Page Object Model to contain the locators and methods for each page. Then your code gets cleaned up significantly, makes it near-human readable, and MUCH easier to manage.
Answered By - JeffC
Answer Checked By - Senaida (JavaFixing Volunteer)