Selenide test not able to load correct element on a newly opened tab/window - groovy

I am using Selenide to open a sample input form on w3schools.com (found here).
The test inputs a name into the form and clicks submit, which then opens a new page in a separate tab/window which displays the request params in a div.
The following is my test code:
import com.codeborne.selenide.Configuration
import org.openqa.grid.internal.utils.configuration.StandaloneConfiguration
import org.openqa.selenium.remote.server.SeleniumServer
import spock.lang.Specification
import static com.codeborne.selenide.Condition.*
import static com.codeborne.selenide.Selectors.byName
import static com.codeborne.selenide.Selectors.byTitle
import static com.codeborne.selenide.Selenide.*
class DummyTest extends Specification {
def "Test w3schools form submit"() {
given: "Server setup configurations"
Configuration.browser = 'chrome'
StandaloneConfiguration configuration = new StandaloneConfiguration()
SeleniumServer server = new SeleniumServer(configuration)
server.boot()
when: "Name info is submitted into the form"
open("http://www.w3schools.com/html/html_forms.asp")
$(byName("firstname")).setValue("Clark")
$(byName("lastname")).setValue("Kent")
$x('//*[#id="main"]/div[3]/div/form/input[3]').click()
// Maybe this will fix the erroneous element problem?
$(byTitle('HTML Forms')).waitUntil(disappears, 5000)
then: "New page should display the passed-in request params"
$x("/html/body/div[1]").shouldHave(text("firstname=Clark&lastname=Kent"))
}
}
...where webdriver.chrome.driver is a passed-in environment variable pointing to the correct location of chromedriver.exe.
The test does open the initial form page and type in the first/last name correctly. When it clicks the submit button, it waits for the second page to load and display the request params.
As a note, the error happens regardless of whether or not my waitUntil() method call is present or not. Many of the other StackOverflow posts suggest doing something like that to fix the problem, though the Selenide docs say that the shouldHave() method call in my then clause should automatically do a fluent wait.
This is the output of my test case:
Condition failed with Exception:
$x("/html/body/div[1]").shouldHave(text("firstname=Clark&lastname=Kent"))
| | |
| | text 'firstname=Clark&lastname=Kent'
| Element should have text 'firstname=Clark&lastname=Kent' {By.xpath: /html/body/div[1]}
| Element: '<div class="w3-container top">w3schools.com
| THE WORLD'S LARGEST WEB DEVELOPER SITE</div>'
| Screenshot: file:/C:/Workspace/IntelliJ/dummy-selenide-project/build/reports/tests/1498836183270.0.png
| Timeout: 4 s.
NoSuchElementException: no such element: Unable to locate element: {"method":"xpath","selector":"/html/body/div[1]"}
So, it's clear that, as per the test output, it's grabbing the xPath of the initial page, not the second page.
Is there something that I'm missing or doing wrong? Do I need to instantiate a new object or something for every new page?

It was difficult to find the answer in the docs, but I found out in a question here that you do need to call a method to switch to another window.
You must call the static method Selenide#switchTo(), which returns a SelenideTargetLocator. On that object, you can call the method window(), which either takes a 0-based index (int) or a name/handle/title (String).
So, after adding the switchTo() method in my and clause for my Spock test, the working result looks like this:
def "Test w3schools form submit"() {
given: "Server setup configurations"
Configuration.browser = 'chrome'
StandaloneConfiguration configuration = new StandaloneConfiguration()
SeleniumServer server = new SeleniumServer(configuration)
server.boot()
when: "Name info is submitted into the form"
open("http://www.w3schools.com/html/html_forms.asp")
$(byName("firstname")).setValue("Clark")
$(byName("lastname")).setValue("Kent")
$x('//*[#id="main"]/div[3]/div/form/input[3]').click()
and: "Switch to newly opened window"
switchTo().window(1)
then: "New page should display the passed-in request params"
$x("/html/body/div[1]").shouldHave(text("firstname=Clark&lastname=Kent"))
}
Note: For sake of my test code, since the test only opens one new tab, calling switchTo(1) works fine here for Chrome, but note that the window/tab order may be different in other browsers.

Related

Why do I fail to submit data to textarea with python requests.post()

I want to use the requests.post tool to automatically query domain name attribution on this websitea website,But the return value is always empty, I guess it is because the post method failed to transfer the data to the textarea
url = 'http://icp.chinaz.com/searchs'
data = {
'hosts':'qq.com',
'btn_search':'%E6%9F%A5%E8%AF%A2', '__RequestVerificationToken=':'CfDJ8KmGV0zny1FLtrcRZkGCczG2owvCRigVmimqp33mw5t861kI7zU2VfQBri65hIwu_4VXbmtqImMmTeZzqxE7NwwvAtwWcZSMnk6UDuzP3Ymzh9YrHPJkj_cy8vSLXvUYXrFO0HnCb0pSweHIL9pkReY',
}
requests.post(url=url,data=data,headers=headers).content.decode('utf-8')
I'd be very grateful if you could point out where I'm going wrong
I have tried to replace headers and so on.

Selenium: Stale Element Reference Exception Error

I am trying to loop through all the pages of a website. but I am getting a stale element reference: element is not attached to the page document error. This happens when the script try to click the third page. The script got the error when it runs to page.click(). Any suggestions?
while driver.find_element_by_id('jsGrid_vgAllCases').find_elements_by_tag_name('a')[-1].text=='...':
links=driver.find_element_by_id('jsGrid_vgAllCases').find_elements_by_tag_name('a')
for link in links:
if ((link.text !='...') and (link.text !='ADD DOCUMENTS')):
print('Page Number: '+ link.text)
print('Page Position: '+str(links.index(link)))
position=links.index(link)
page=driver.find_element_by_id('jsGrid_vgAllCases').find_elements_by_tag_name('a')[position]
page.click()
time.sleep(5)
driver.find_element_by_id('jsGrid_vgAllCases').find_elements_by_tag_name('a')[-1].click()
You can locate the link element each time again according to the index, not to use elements found initially.
Something like this:
amount = len(driver.find_element_by_id('jsGrid_vgAllCases').find_elements_by_tag_name('a'))
for i in range(1,amount+1):
link = driver.find_element_by_xpath("(//*[#id='jsGrid_vgAllCases']//a)["+str(i) +"]")
from now you can continue within your for loop with this link like this:
amount = len(driver.find_element_by_id('jsGrid_vgAllCases').find_elements_by_tag_name('a'))
for i in range(1,amount+1):
link = driver.find_element_by_xpath("(//*[#id='jsGrid_vgAllCases']//a)["+str(i) +"]")
if ((link.text !='...') and (link.text !='ADD DOCUMENTS')):
print('Page Number: '+ link.text)
print('Page Position: '+str(links.index(link)))
position=links.index(link)
page=driver.find_element_by_id('jsGrid_vgAllCases').find_elements_by_tag_name('a')[position]
page.click()
time.sleep(5)
(I'm not sure about the correctness of all the rest your code, just copy-pasted it)
I'm running into an issue with the Stale Element Exception too. Interesting with Firefox no problem, Chrome && Edge both fail randomly. In general i have two generic find method with retry logic, these find methods would look like:
// Yes C# but should be relevant for any WebDriver...
public static IWebElement( this IWebDriver driver, By locator)
public static IWebElement( this IWebElement element, By locator)
The WebDriver variant seems to work fine for my othe fetches as the search is always "fresh"... But the WebElement search is the one causing grief. Unfortunately the app forces me to need the WebElement version. Why he page/html will be something like:
<node id='Best closest ID Possible'>
<span>
<div>text i want</div>
<div>meh ignore this </div>
<div>More text i want</div>
</span>
<span>
<!-- same pattern ... -->
So the code get the closest element possible by id and child spans i.e. "//*[#id='...']/span" will give all the nodes of interest. This is now where i run into issues, enumerating all element, will do two XPath select i.e. "./div[1]" and "./div[3]" for pulling out the text desired. It is only in fetching the text nodes under the elements where randomly a StaleElement will be thrown. Sometimes the very first XPath fails, sometimes i'll go through a few pages, as the pages being might have 10,000's or more pages, while the structure is the same i'll spot check random pages as they all the same format. At most i've gotten through 20 consecutive pages with Chrome (ver 92.0.4515.107) or Edge (ver 94.0.986), both seem to be the latest as of now.
One solution that should work, get all the the span elements first, i.e. '//*[#id='x']/span' get my list then query from the driver like:
var nodeList = driver.FindElements(By.XPath('//*[#id='x']/span' ));
for( int idx = 0 ; idx < nodeList.Count; idx++)
{
string str1 = driver.FindElements(By.XPath("//*[#id='x']/span[idx+1]/div[1]")).GetAttribute("innerText");
string str2 = driver.FindElements(By.XPath("//*[#id='x']/span[idx+1]/div[3]")).GetAttribute("innerText");
}
```
Think it would work but, YUK! This is kind of simplified and being able to do an XPath from the respective "ID" located node would be preferable..

TestFx - How to test validation dialogs with no ids

I have an application with grid of records and button insert. After clicking insert, there is a form, where you fill in data and click Ok for adding new record to the grid. After clicking Ok, there is validation which fires dialog with error informations, if any of the text fields do not match validation rules. Is there any posible way to test text on the dialog with textFx, if the dialog has no id?
This is an example for Alert based dialog:
In your test:
alert_dialog_has_header_and_content(
"Removing 'Almaty' location", "Are you sure to remove this record?");
In you helper test class:
public void alert_dialog_has_header_and_content(final String expectedHeader, final String expectedContent) {
final javafx.stage.Stage actualAlertDialog = getTopModalStage();
assertNotNull(actualAlertDialog);
final DialogPane dialogPane = (DialogPane) actualAlertDialog.getScene().getRoot();
assertEquals(expectedHeader, dialogPane.getHeaderText());
assertEquals(expectedContent, dialogPane.getContentText());
}
private javafx.stage.Stage getTopModalStage() {
// Get a list of windows but ordered from top[0] to bottom[n] ones.
// It is needed to get the first found modal window.
final List<Window> allWindows = new ArrayList<>(robot.robotContext().getWindowFinder().listWindows());
Collections.reverse(allWindows);
return (javafx.stage.Stage) allWindows
.stream()
.filter(window -> window instanceof javafx.stage.Stage)
.filter(window -> ((javafx.stage.Stage) window).getModality() == Modality.APPLICATION_MODAL)
.findFirst()
.orElse(null);
}
I know this issue is a little old and probably got fixed, but for documentation purpose in case someone else look for a fix for an issue alike, I see dialog.getDialogPane() in Dialog documentation, which would help lookup for specific controls inside the pane. So further on #plaidshirt query, we could retrieve buttons and input fields with:
dialog.getDialogPane().lookupAll()
Then narrow that down to buttons and input fields for example.

Using Spock Data Tables to Test Geb Page Objects

Full Disclosure: I'm very new to both Geb and Spock.
As part of a test suite I'm working on, we have to test run the same test on several page elements. I would love to be able to abstract this behavior using a Spock data-table. However, when I do this, Geb complains that it doesn't recognize the page property.
Here is a bare-bones example of what I'm talking about:
when:
textBox = value
submit()
then:"value is updated"
at SuccessPage
textBox == value
where:
textBox | value
box1 | val1
box2 | val2
box3 | val3
In this example, boxes 1-3 are defined in the content object of a Page.
These tests work when I do them independently, but not when I use a data table. Why isn't the Geb element getting substituted correctly?
Data tables are executed outside of the context of the test for which they are specified. They have to be executed that way to know how to actually construct multiple iterations of your test based on them. In that context box1 does not point to a page property as you're browser is not yet pointing at SuccessPage.
To get around it you will need to use content names (which will be instances of String) and resolve them as properties of the page when you are in the right context:
when:
page."$textBox" = value
submit()
then:"value is updated"
at SuccessPage
page."$textBox" == value
where:
textBox | value
'box1' | val1
'box2' | val2
'box3' | val3

Method call on page object does not return result

I am having problem to use page object method call to return an object on the page.
Here is my example codes in test_log_in.rb
...
class TestLogIn < Test::Unit::TestCase
.....
def test_failed_log_in
#log_in_page= LogIn.new(#browser)
#log_in_page.go_to_log_in
#log_in_page.log_in("174773476","test","aaa111as")
puts #log_in_page.error_message
puts #log_in_page.get_error_message
end
end
My log in class is defined below:
class LogIn
include PageObject
...
h3(:error_message, :class => 'no-margin white-text')
...
def log_in (access_number, user_id, password)
self.access_number = access_number
self.user_id = user_id
self.password = password
log_me_in
AccountSummary.new(#browser)
end
....
def get_error_message
self.error_message
end
....
end
Why will the following lines returns no output?
puts #log_in_page.error_message
puts #log_in_page.get_error_message
Can you please help me?
Thanks.
My guess is that there is a timing issue due to the error message being displayed through javascript. It is likely that the error message element is always in the html of the page, but when login fails, the error message element's style is changed to be something visible.
When a page object (or watir) element returns its text, it only includes the visible text. So if the error message is not yet visible, you get no text.
Try waiting to ensure the element is visible to the user:
def get_error_message
error_message_element.when_present.text
end
when_present will wait up to 5 seconds for the element to become visible to the user and then return the text.
Update - Multiple h3s:
It looks like the actual problem is that there are multiple h3 elements that have that class. Watir always returns the first match, which in this case is the cookie error heading. Given that it is not being displayed, you get the blank text.
The solution would be to reduce the search scope so that you the first h3 is the one you want. This can be done by only looking in the error message list. I would define the page object as follows. Note that you do not need to specify the class of the h3 element since it is the only h3 element in the list.
class LogIn
include PageObject
div(:error_message_list, :id => 'errorMessageList')
h3(:error_message){ error_message_list_element.h3_element }
end
This will then find the right h3 element:
browser = Watir::Browser.new
browser.goto 'https://www.bnz.co.nz/ib4b/app/login'
browser.button(:text => 'Login').click
page = LogIn.new(browser)
page.error_message
#=> "There were 3 errors on the page."

Resources