Hi
I want to write a Protractor test that waits until the current URL to change, so I have been looking at browser.wait(), combined with browser.getCurrentUrl().
However, I am not sure of how to use the two together: the browser.wait() method accepts as its first parameter a function that will return a boolean value – however, browser.getCurrentUrl() returns a promise.
My first attempt, which failed (as I expected), looks like this:
var currentUrl;
browser.getCurrentUrl().then(function (url) {
currentUrl = url;
}).then(function () {
return browser.wait(function () {
return browser.getCurrentUrl().then(function (url) {
return url !== currentUrl;
});
});
}).then(function () {
// continue testing
});
Can anyone help?
many thanks
Matt
How is this failing? Is it a too early timeout issue?
I can't find in the docs what's the default timeout when no timeout param is specified in browser.wait()
It times out after 30s (as I set in my protractor.conf.js file) – which seems to indicate that the browser.wait() never returns.
I tried again, this time specifying a timeout parameter of 12000 – all the tests timed out again, this time after 12 seconds.
I can't recall exactly why but at some point i had to switch from browser.getCurrentUrl() to browser.driver.getCurrentUrl() and even created this helper function:
var waitForCurrentUrl = function(timeout) {
if (timeout == null) {
timeout = browser.manage().timeouts().pageLoadTimeout;
};
return browser.driver.wait(function() {
// Return a condition. Code will continue to run until is true
return browser.driver.getCurrentUrl().then(function(url) {
return url;
}, function(err) {
// errored .. TODO: retry
throw err;
});
}, timeout, 'Expectation error: Timed out waiting for current url');
};
Great, thanks Leo!
I'll give that a go.
If it works, I'll suggest a document change to @juliemr :-)
Hmmm... looks like I was right originally – using browser.getCurrentUrl() and browser.wait() are working fine for me at the moment:
var currentUrl;
browser.getCurrentUrl().then(function(url) {
currentUrl = url;
}
).then(function() {
browser.wait(function() {
return browser.getCurrentUrl().then(function (url) {
return url !== currentUrl;
});
});
}
).then(function () {
// continue testing
});
However, I am running into problems when I try to use getText() in a similar fashion:
browser.wait(function () {
return methodThatReturnsWebElementViaPromise().then(function (ele) {
return ele.getText(); // <-- occasional failure point
}
).then(function (text) {
return text === 'changed text';
}
);
}).then(function () {
// continue testing
});
It works sometimes, but on other times it fails with this message: StaleElementReferenceError: stale element reference: element is not attached to the page document
So what I think is causing the error is that by the time the call to getText() is executed, the contents of the element changed, thereby making the reference to it stale.
What do you think?
And can you suggest any workaround? (@juliemr?)
Reference for testing the URL: https://github.com/angular/protractor/blob/master/spec/withLoginConf.js#L24
For the second question (getText), I'm guessing that your methodThatReturnsWebElementViaPromise is only getting called one time (because the promise is resolved once and then later invocations just return the value), instead of being called every time. It seems like you want to re-find the element every time?
Ok, I ended up finding a solution:
function waitForElementTextToChange (elementId, textToWaitFor) {
return browser.wait(function () {
return element(by.id(elementId)).getText().then(function (text) {
return text === textToWaitFor;
},
function () {
return element(by.id(elementId)).getText().then(function (text) {
return text === textToWaitFor;
});
}
);
});
}
You can see that I call the element(by.id(elementId)).getText() a second time, in the error handler for the first time – this is because I consistently found that StaleElementReferenceErrors would be thrown, and very unpredictably (but sometimes not at all).
I'm guessing this is because of some kind of race condition: in the time between the calls to element(by.id(elementId)) and getText(), Angular had updated the DOM with a new value – just guesswork though.
So it's not an ideal solution, but it works consistently. And if I understand it correctly, any error that occurs on the second getText() call will be propagated back up the chain.
Note also that I simplified my code so that I'm only dealing with a DOM ID, and no longer needing to use the method that returned a promise.
In case it may help others in future, here is the method I have written to wait for the URL to change to a particular value (as specified by a regex) :
/**
* @name waitForUrlToChangeTo
* @description Wait until the URL changes to match a provided regex
* @param {RegExp} urlRegex wait until the URL changes to match this regex
* @returns {!webdriver.promise.Promise} Promise
*/
function waitForUrlToChangeTo(urlRegex) {
var currentUrl;
return browser.getCurrentUrl().then(function storeCurrentUrl(url) {
currentUrl = url;
}
).then(function waitForUrlToChangeTo() {
return browser.wait(function waitForUrlToChangeTo() {
return browser.getCurrentUrl().then(function compareCurrentUrl(url) {
return urlRegex.test(url);
});
});
}
);
}
Thanks @mcalthrop, both functions work pretty well!
Excellent! Glad they helped.
Thanks for this @mcalthrop these two functions have proved to be very helpful.
Great thanks @mcalthrop
Thanks, @mcalthrop. Very helpful.
Can anyone of you please explain me how to write "pageLoadTimeout()" for selenium webdriver on Node.JS by Jasmine?
Thanks.
This worked very well for me
`````` js
var expectedUrl = "/url-your-waiting-for";
var source = Rx.Observable
.interval(200)
.take(20) // number of attempts / timeout
.flatMap(function () {
return browser.driver.getCurrentUrl();
})
.filter(function (url) {
return url == expectedUrl; // you can tune this
}).take(1) // after we get a url the matches we're done
.defaultIfEmpty(); // if after timeout hit nothing is found in the stream we emmit en empty element
var subscription = source.subscribe(
function (url) {
if (url) {
next();
} else {
next("Timeouted out waiting for url: " + expectedUrl);
}
},
function (err) {
next(err);
});```
``````
I had to use browser.driver.wait and browser.driver.getCurrentUrl
Unable to use the @juliemr wait for url after login promise https://github.com/angular/protractor/blob/master/spec/withLoginConf.js#L28 with @mattfritz cucumber framework https://github.com/mattfritz/protractor-cucumber-framework
If the promise and the test passed there is no problem and everything works pretty good. The problem is when the promise fail testing process stop and others test won't run.
Any idea to keep the process running? This test fail but the other could pass.
For example:
[10:54:33] I/hosted - Using the selenium server at http://localhost:4444/wd/hub
[10:54:33] I/launcher - Running 1 instances of WebDriver
Feature: xxx login
As a user of xxx login
I should be able to login in xxx using an existent user
@critical
Scenario: Check xxx login
Given The xxx login page is open
Then The title should contain "xxx"
[10:54:57] E/launcher - url has not changed to /lobby/
Wait timed out after 10020ms
[10:54:57] E/launcher - Error: url has not changed to /lobby/
Wait timed out after 10020ms
at /Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/node_modules/selenium-webdriver/lib/promise.js:2364:22
at ManagedPromise.invokeCallback_ (/Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/node_modules/selenium-webdriver/lib/promise.js:1379:14)
at TaskQueue.execute_ (/Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/node_modules/selenium-webdriver/lib/promise.js:2913:14)
at TaskQueue.executeNext_ (/Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/node_modules/selenium-webdriver/lib/promise.js:2896:21)
at /Users/adolfocabrera/Mavrix/xxx/Selenium/xxx/node_modules/selenium-webdriver/lib/promise.js:2775:27
at /Users/adolfocabrera/Mavrixxxx/Selenium/xxx/node_modules/selenium-webdriver/lib/promise.js:639:7
at process._tickCallback (internal/process/next_tick.js:103:7)
From: Task: url has not changed to /lobby/
at ControlFlow.wait (/Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/node_modules/selenium-webdriver/lib/promise.js:2352:17)
at WebDriver.wait (/Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/node_modules/selenium-webdriver/lib/webdriver.js:712:29)
at ProtractorBrowser.to.(anonymous function) [as wait] (/Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/node_modules/protractor/built/browser.js:68:29)
at waitForUrlToChangeTo (/Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/features/utils/wait.utils.js:19:32)
at ManagedPromise.invokeCallback_ (/Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/node_modules/selenium-webdriver/lib/promise.js:1379:14)
at TaskQueue.execute_ (/Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/node_modules/selenium-webdriver/lib/promise.js:2913:14)
at TaskQueue.executeNext_ (/Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/node_modules/selenium-webdriver/lib/promise.js:2896:21)
at /Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/node_modules/selenium-webdriver/lib/promise.js:2775:27
at /Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/node_modules/selenium-webdriver/lib/promise.js:639:7
at process._tickCallback (internal/process/next_tick.js:103:7)
[10:54:57] E/launcher - Process exited with error code 199
npm ERR! Darwin 15.6.0
npm ERR! argv "/Users/adolfocabrera/.nvm/versions/node/v5.11.1/bin/node" "/Users/adolfocabrera/.nvm/versions/node/v5.11.1/bin/npm" "run" "test-desktop-local"
npm ERR! node v5.11.1
npm ERR! npm v3.10.5
npm ERR! code ELIFECYCLE
npm ERR! [email protected] test-desktop-local: `protractor conf.js --seleniumAddress 'http://localhost:4444/wd/hub'`
npm ERR! Exit status 199
npm ERR!
npm ERR! Failed at the [email protected] test-desktop-local script 'protractor conf.js --seleniumAddress 'http://localhost:4444/wd/hub''.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the protractor-cucumber-tests package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! protractor conf.js --seleniumAddress 'http://localhost:4444/wd/hub'
npm ERR! You can get information on how to open an issue for this project with:
npm ERR! npm bugs protractor-cucumber-tests
npm ERR! Or if that isn't available, you can get their info via:
npm ERR! npm owner ls protractor-cucumber-tests
npm ERR! There is likely additional logging output above.
npm ERR! Please include the following file with any support request:
npm ERR! /Users/adolfocabrera/xxx/QA-testing/Selenium/xxx/npm-debug.log
@aluzardo When you are using protractor-cucumber framework it is advisable to use cucumberjs timeouts rather than protractor's wait's, You have to increase you default timeout for such scenarios, basically when the promise is not resolved in the specific default time i.e 5000 ms currently in cucumberJS , you would get such an exception. So I would suggest you increase setDefaultTimeout : 60 * 1000 at the global level so that your promises get enough time be resolved and your scripts would be running!
Thanks @idaneliz
I increased the time some time ago https://github.com/aluzardo/protractor-cucumber-tests/blob/master/features/support/env.js
I tried to increase more the time but I got the same error.
@aluzardo There is no issue with your setup. Instead of using callbacks, you should return your promises, cucumberjs supports promises, It's just that your scenario takes some time to get resolved, so your step definition should know that, currently it waits for the callback. For more details please checkout these SO threads -
protractor cucumber tests shown passed
configure protractor with cucumber
It's more of a support question , you would get answers at faster pace if you raise it in Stackoverflow or Gitter!
@idaneliz @juliemr and @mattfritz
I found that if I use the 3.3.0 protractor version everything work pretty good.
The problem is using the version ^4.0.2
It is the only clue that I could found. I am going to use the 3.3.0 protractor as a temporary fix.
I have noticed the browser.wait commands broken after upgrading to 4.0.4 as well. @juliemr any ideas? These were working perfectly with the older version of protractor.
Ok, I know this is way way late but I believe you can do something like this:
var EC = browser.ExpectedConditions;
browser.wait(EC.urlContains('Expected URL'), timeout); // Checks that the current URL contains the expected text
browser.wait(EC.urlIs('Expected URL'), timeout); // Checks that the current URL matches the expected text
Thanks @pittgoose , it works for me. I spent few days to search for solution.
In case it may help others in future, here is the method I have written to wait for the URL to change to a particular value (as specified by a regex) :
/** * @name waitForUrlToChangeTo * @description Wait until the URL changes to match a provided regex * @param {RegExp} urlRegex wait until the URL changes to match this regex * @returns {!webdriver.promise.Promise} Promise */ function waitForUrlToChangeTo(urlRegex) { var currentUrl; return browser.getCurrentUrl().then(function storeCurrentUrl(url) { currentUrl = url; } ).then(function waitForUrlToChangeTo() { return browser.wait(function waitForUrlToChangeTo() { return browser.getCurrentUrl().then(function compareCurrentUrl(url) { return urlRegex.test(url); }); }); } ); }
Nice, can you share where you defined this method globally?
What this
بتاريخ ٠٣/١٢/٢٠١٩ ١٠:٤٩ م، كتب "Emanuel" notifications@github.com:
In case it may help others in future, here is the method I have written to
wait for the URL to change to a particular value (as specified by a regex) :/** * @name waitForUrlToChangeTo * @description Wait until the URL changes to match a provided regex * @param {RegExp} urlRegex wait until the URL changes to match this regex * @returns {!webdriver.promise.Promise} Promise */function waitForUrlToChangeTo(urlRegex) {
var currentUrl;return browser.getCurrentUrl().then(function storeCurrentUrl(url) { currentUrl = url; } ).then(function waitForUrlToChangeTo() { return browser.wait(function waitForUrlToChangeTo() { return browser.getCurrentUrl().then(function compareCurrentUrl(url) { return urlRegex.test(url); }); }); } );}
Nice, can you share where you defined this method globally?
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/angular/protractor/issues/610?email_source=notifications&email_token=ANOOAMURALFHEX4YOP34GXTQW2Z55A5CNFSM4ANFSW6KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEF2S6OQ#issuecomment-561327930,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/ANOOAMQP5Z3PFJACAGR6K7TQW2Z55ANCNFSM4ANFSW6A
.
Most helpful comment
Ok, I know this is way way late but I believe you can do something like this: