Behat, PhantomJS - wait for page load after clicking link? - selenium-webdriver

I'm using Behat 3 and PhantomJS 2. Currently I have a scenario defined as such:
#javascript
Scenario: I visit the blog through the Blog & Events menu.
Given I am an anonymous user
And I am on the homepage
And I follow "Link Text"
Then I should be on "/path-to-page"
When I run this with Goutte it's fine. When I run this with vanilla Selenium, it's fine (it launches a browser I can see). However, when I configure Selenium to point the webdriver host to PhantomJS, it explodes on Then I should be on "/path-to-page" claiming it's still on /.
If I add the following wait step:
#javascript
Scenario: I visit the blog through the Blog & Events menu.
Given I am an anonymous user
And I am on the homepage
And I follow "Link Text"
And I wait 4 seconds
Then I should be on "/path-to-page"
Then my scenario passes in the green, all good.
Is there a way to get PhantomJS to wait for the page to load before checking the current path? I don't want to depend on arbitrary timeouts. I need a headless solution and PhantomJS seems to be pretty well supported, but if I can't do something as simple as clicking a link and verifying the page that was loaded without adding random waiting steps everywhere, I might need to re-evaluate my decision.

Try using this implicit wait in your feature context. In my experience it has helped.
/**
* #BeforeStep
*/
public function implicitlyWait($event)
{
// Set up implicit timeouts
$driver = $this->getSession()->getDriver()->getWebDriverSession();
$driver->timeouts()->implicit_wait(array("ms" => 10000));
}

I was having the same issue, and doing something like this fails because its using the state of the current url:
$this->getSession()->wait(10000, "document.readyState === 'complete'");
So my workaround for this was adding a variable to the page every time a step is done. When I link is clicked, the variable will no long exist, this will guarantee that am working with a different page.
/**
* #AfterStep
*/
public function setStepStatus()
{
$this->getSession()->evaluateScript('window.behatStepHasCompleted = true;');
}
/**
* #When /^(?:|I )wait for the page to be loaded$/
*/
public function waitForThePageToBeLoaded()
{
$this->getSession()->wait(10000, "!window.behatStepHasCompleted && document.readyState === 'complete'");
}

You can always make use of a closure function to encapsule your steps, just as mentioned in the docs. Through it, you can get your steps to run when they're ready. Let's implement a spinner function:
public function spinner($closure, $secs) {
for ($i = 0; $i <= $secs; $i++) {
try {
$closure();
return;
}
catch (Exception $e) {
if ($i == $secs) {
throw $e;
}
}
sleep(1);
}
}
What we're doing here is wait for a number of seconds for the closure function to run successfully. When the time's run out, throw an exception, for we want to know when something's not behaving correctly.
Now let's wrap your function to assert you're in the right page within the spinner:
public function iShouldBeOnPage($wantedUrl) {
$this->spinner(function() use($wantedUrl) {
$currentUrl = $this->getSession()->getCurrentUrl();
if ($currentUrl == $wantedUrl) {
return;
}
else {
throw new Exception("url is $currentUrl, not $wantedUrl");
}
}, 30);
What we're doing here is wait up to 30 seconds to be on the url we want to be after clicking the button. It will not wait for 30 secs, but for as many secs we need until current url is the url we need to be at. Applying it in your function within the *Context.php will result in it being applied in every step you call it within your Gherkin files.

Related

Codename One RESTfulWebServiceClient Threads

I have a simple programme that calls a rest service and displays the output.
The problem is the display is being updated before the rest method returns.
I've tried to use the invoke and block, however the .find method appears to run in it's own thread? asynchronously
the Sys output goes like this;
Before
After
surname
System.out.println("Before");
userClient = new RESTfulWebServiceClient("http://localhost:8080/MyService/api/person");
Display.getInstance ()
.invokeAndBlock(() -> {
userClient.find(
new Query().id("id"), rowset -> {
for (Map m : rowset) {
person = new JSONObject(m);
System.out.println(person.getString("surname"));
}
}
}
System.out.println("After");
I have found a workaround that works.
As I can see the problem is the .find method of the RESTfulWebServiceClient class. The line NetworkManager.getInstance().addToQueue(req); creates an asynchronies call to the rest network service and returns the method before the call is made.
To get around this I recreated the RESTfulWebServiceClient class in my project and copied the source from github.
I then changed the
NetworkManager.getInstance().addToQueue(req);
to
NetworkManager.getInstance().addToQueueAndWait(req);
This causes the the method to complete the rest call before returning.

WebDriver wait until this or that element is found

In one of the application workflows I have more than 10 pages to navigate.
I have to keep clicking on the 'Next' button continuously - it makes an AJAX call to re-load new content and 'Next' button will also be reloaded.
The number of pages to navigate is not always 10. It might be anything from 10-100.
My test will be complete whenever there is a webelement found with the id 'testcomplete'.
So Currently i use ExpectedConditions()
WebDriverWait wait = new WebDriverWait(driver, 30);
//Keep clicking next
while(isNextPresent()){
NextButton.click();
}
//testcomplete reached here
System.out.println("test complete");
private boolean isNextPresent(){
try{
WebElement element = wait.until(ExpectedConditions.visibilityOf(NextButton));
return true;
}catch(Exception e){
//
}
return false;
}
Currently my code works fine. But i am trying to improve it. I hate the unnecessary wait of 30 seconds when the element with the id 'testcomplete' is present. Because that time 'NextButton' will not be present.
How can I improve this isNextPresent function? Ie, to return false immediately when there is 'testcomplete' instead of waiting for 30 seconds?
Note: I have tagged protractor as well because I also have a similar requirement in protractor.
You can combine the conditions of both elements and take an action depending on the fact which one first returns true for 'visibilityOf(myElement)'. Maybe something like this in pseudo (sorry, no IDE around):
loop(i < 30){
// wait NextBtn for 1 sec, if true click and break
// wait TestCopmlete for 1 sec
}
Use EC.or(), a la:
wait.until(ExpectedConditions.or(
ExpectedConditions.visibilityOf(NextButton),
ExpectedConditions.visibilityOf(element(by.id('testcomplete')))
));
Then after this comes back, expect the desired state:
expect(NextButton.isDisplayed()).toBeTruthy();

Selenium PageFactory - Check if on correct page

Summary
With the PageFactory, how do I ensure the correct page has been passed to the appropriate page object? For example making sure that a real login page is being passed to an instance of the LoginPage object.
Details
I notice on the PageObjects documentation that they explain how, in the constructor, that you can check to see if you are on the right page. For example
// Check that we're on the right page.
if (!"Login".equals(driver.getTitle())) {
// Alternatively, we could navigate to the login page, perhaps logging out first
throw new IllegalStateException("This is not the login page");
}
However, when reading the PageFactory docs, they don't explain how to check if the correct page has been passed in. They just go ahead and attempt to run the test. How can I best check this when using PageFactory?
You could put an assertion in your constructor that checks the URL is valid e.g.
MyPage(WebDriver driver) {
PageFactory.initElements(driver, this);
assert wait.until(currentURIIs(new URI("http://www.mydomain.com/mypage.html")));
}
The above is using the following expected condition:
public static ExpectedCondition<Boolean> currentURIIs(final URI pageURI) {
new ExpectedCondition<Boolean>() {
#Override
Boolean apply(WebDriver driver) {
new URI(driver.currentUrl) == pageURI;
}
}
}
You could of course search for an element you know is on the page, or any other uniquely identifying feature. Checking the current page URI is one possible option, there are many other things you could check to ensure you are on the correct page.
The test should call page.get(). Then the get function will call isLoaded(). If page.isLoaded() doesn't throw an exception, then it will assume that its loaded, and return.
Otherwise, it will call page.load(), and then page.isLoaded() again. (to make sure that its actually loaded).
So each of your classes that extend LoadablePageComponent need to have a isLoaded() and load() function.

Launch default web browser, but not if URL already open

I have a link on my app UI that launches a URL using System.Diagnostics.Process.Start(). If the user clicks the link several times, it opens several tabs.
Is there a way, maybe a command-line option, to still use the default web browser, but have it just reopen the same tab if the URL is already open? It would be OK if it doesn't work with every possible browser out there, but nice if it at least works with IE, Firefox and Chrome.
I doubt it, but since I didn't see any other questions/answers on this topic, I figured I'd ask.
This is somewhat of a workaround but it might get you started. I have used the System.Diagnostics.Process.ProcessId.
As an example I have used IE, I will explain later why I did this. The code is just "quick and dirty" but I just made it as proof of concept.
I have created a basic WinForm app with one button that will open google in IE, if it has already been opened by the application it will not be opened again.
I added the System.Diagnostics reference.
public int ProcessID;
public Form1()
{
InitializeComponent();
}
private void MyButton_Click(object sender, EventArgs e)
{
if (ProcessID == null)
{
StartIE();
}
else
{
if (!ProcessIsRunning())
{
StartIE();
}
}
}
private bool ProcessIsRunning()
{
bool ProcessRunning = false;
foreach (Process p in Process.GetProcesses())
{
try
{
if (p.Id == ProcessID)
{
ProcessRunning = true;
}
}
catch { }
}
return ProcessRunning;
}
private void StartIE()
{
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = "iexplore.exe";
proc.StartInfo.Arguments = "http://www.google.be";
proc.Start();
ProcessID = proc.Id;
}
This does not completely do what you requested but it might be a good start. There are a few reasons why I did it this way and what possible options are..
If you would use the url as the Filename, it would indeed open up the webpage in the default browser, it would however not return a processID. This is why the snippet shows usage of IE. (If you would use this option, you could use the System.IO.File.Exists to make sure the desired browser is installed)
If you would like to use this option, you can query the registry to pick up what te default browser is, if you have that you could launch that from the value obtained from the registry. If you then change the process.startinfo.filename to this value, then you will launch the default browser but you will still obtain a processId so this might be the way forward. You can check how to do this over here: http://social.msdn.microsoft.com/Forums/en/netfxbcl/thread/b200903e-ce69-4bd4-a436-3e20a7632dc4
Showing the internet window if it would already be opened, can be done by using the SetForegroundWindow property. As this is already documented in this article, I did not add it in this snippet.
I hope this helps to get you on your way.

Qooxdoo on window ready

I was just trying to fire an event after the qooxdoo application is ready, so I started with the "Hello World" app and appended the recommendation at the very end of the main function:
main : function(){
// Hello World part...
qx.event.Registration.addListener(window, "ready", function() { alert("application ready"); });
}
but, it didn't appear to fire in chrome or firefox (I didn't test IE), so I dug some more and found this and it worked.
if (qx && qx.event && qx.event.Registration)
{
var manager = qx.event.Registration.getManager(window);
var handler = manager.findHandler(window, "ready");
if (handler.isApplicationReady()) {
alert("application ready");
}
}
Can anyone tell my why the recommended method does not work or am I putting it in the wrong place?
Thanks!
Did you get the "recommendation" from the "From jquery to qooxdoo" document?! (It always helps if you cite your sources).
I think you are mixing things here. First, "window ready" is not the same as "application ready". I think "window ready" (as shown in a linked manual page) is a low-level event of the global JS window object. If you are running a high-level qooxdoo application (as it appears) this event has long passed when you register for it in your main method. So the event handler is never run.
In your second code sample you are not listening for an event, but checking a status with isApplicationReady(). This status can return true long after the event that turned the app from non-ready to ready has passed.
override simply the finalize function in the member area of the Application
finalize : function()
{
// Call super class
this.base(arguments);
alert("Hello World");
}
More simple!!!
QX Core Widget "appear" event is equal the same as "onReady" event such in other JS Frameworks like YUI, JQuery or whatever....
http://www.qooxdoo.org/5.0.2/api/#qx.ui.core.Widget~appear!event
has the same effect.
best, Tamer

Resources