Selenium Node API web driver wait timeout handler - selenium

I am new to Selenium web drive. Trying to do some page automation and are using driver.wait functions to wait for a selector rendering first and then do some operations later.
Was wondering if Selenium has a way to pass in a timeout handler to manage timeout if the element is not showing up after x seconds.
Here's my code:
driver.wait(function () {
return driver.isElementPresent(webdriver.By.css('input[id="searchMap"]'));
}, 10000);
So after 10 secs if input[id="searchMap"] does not show up, Selenium script will end and Error is thrown.
I am looking for something like this:
driver.wait(function () {
return driver.isElementPresent(webdriver.By.css('input[id="searchMap"]'));
}, 10000, function fail(){
console.log("Time is up!");
});

Found a solution myself. Have to use catch for Selenium promise class.
http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/promise_exports_Promise.html
Here is my code:
driver.wait(function () {
return driver.isElementPresent(webdriver.By.css('div.info-page'));
}, 10000).catch(function(e){
console.log('Catching Error');
});

Related

Webdriver IO - Unable to find element with CSS selector with ID

I am writing a simple WDIO selector with following statement. I was able to uniquely idenitfy it in browser. Not sure why WDIO was not able to set the value.
Given(/^I am on the login page$/, function () {
browser.url('https://secure-sandbox.modulrfinance.com/')
browser.maximizeWindow()
});
When(/^I enter password as \"([^\"]*)\"$/, function (password:string) {
//const txt_username = $('#username-inp')
//const txt_password = $('#password-inp')
$('#username-inp').setValue('')
$('#password-inp').setValue(password)
});
Then(/^I should see a flash message under username saying \"([^\"]*)\"$/, async function () {
const banner = await $('#.ng-star-inserted')
expect(banner).toHaveText('This field is required1')
});```
**error**
**[0-0] error: 'no such element',
[0-0] message: 'no such element: Unable to locate element: {"method":"css selector","selector":"#password-inp"}\n'** +
Seems like you are testing Single Page Application written in angular. You have to deal with the asynchronous behavior of Angular, by adding appropriate waits(Implicit, Explicit or Fluent waits).
Check this example.

How to close the safari pop up dialogue when running automate script with nightwatch on BrowserStack?

I use Browserstack to do the E2E testing, now I met a problem when I try to run the mobile automate script in safari on Browserstack, there will have a pop-up dialogue show when I click a button which will result in opening a new tab, the dialogue show message like this: 'this site is attempting to open a popup window', I must close it and the script can continue executing.
Now the problem is:
1. When I click the button which will trigger this pop-up dialogue, there will always show an exception in the log: 'Error while running .clickElement() protocol action: Appium error: An unknown server-side error occurred while processing the command. Original error: Did not get any response after 20s'.
2. I can use the XPath to locate the button on the pop-up dialogue and click it to close the dialogue, but it takes serval minutes, is there another way to do this operation more efficient?
const { client } = require('nightwatch-api')
const { Given, Then, When} = require('cucumber')
Given('open mobile 163 news', async function () {
await client.url('https://3g.163.com/news/article/EJN99AOF000189FH.html?clickfrom=index2018_news_newslist#offset=0')
})
When('choose share by QQ', async function () {
await client.waitForElementVisible('.sharelogo')
await client.click('.sharelogo')
})
Then('the popup should show', async function () {
await client.waitForElementVisible('.qzone')
await client.click('.qzone')
await client.setContext('NATIVE_APP')
await client.source(function(res){
console.log(res.value)
})
await client.useXpath()
await client.click('//*[#name="Allow"]')
await client.contexts(function(result) {
client.setContext(result.value[result.value.length - 1])
client.useCss()
})
})
Have you tried adding the capability 'nativeWebTap' and setting it to the value 'true' in the test scripts?

Mocha Before Hook not working asynchronously

I can't seem to get the before Hook to be asynchronous.
I've used a before hook to start it up, which it does, but I'm trying to pick up when it fails, so that I can fail/skip the whole block (since there is no server to test with).
I've used the [async] before hook all throughout my code, but in this case it's not waiting for my done to be fired.
Basically, got a bunch of MochaJS tests, and for one block I need to start up a Web Server so that I can make Requests to it to test the RESTful API. I want to make sure that it started successfully, hence the async method.
describe('RESTful Tests', function(){
// Start up the whole express server for these API tests
before(function(done) {
try {
server.on('error', (e) => {
console.error("HERE");
done();
});
listener = server.listen(port, function() {
console.log("Server Started");
done();
});
console.error("AFTER LISTEN");
} catch(err) {
console.error("ON Caught");
done();
}
console.error("At End!!");
});
This runs, and shows as an 1) "before all" hook in the test, but it does not wait for the done to be called. I'm only getting the output...
RESTful Tests
AFTER LISTEN
At End!!
1) "before all" hook
None of the other items show up, but I'll get an exception (expected as I've blocked the port) about the EADDRINUSE. I'm really struggling to find a way to catch this.
I understand that it's in a different (kinda) "thread", so I wouldn't pick up the error, but still.... why is the asynchronous done method not being obeyed?
Seems some of the documentation out there is a bit misleading...
The Node/ExpressJS documentation state to put the .on('error' method on the server object. Seems in this case it needed to be on the listener object!
So my final [working] code looks like this...
listener = server.listen(port, function() {
done();
}).on('error', (function(e) {
this.skip(e.code);
done();
}).bind(this));
Yay!!! :)

How to add wait for the test?

I am using WebdriverIO for the following test
webdriverio
.remote(options)
.init()
.url('http://www.google.com')
.getTitle().then(function(title) {
console.log('Title was: ' + title);
})
.end();
However, my ISP provide a web virus checking page between google.com, so the test always return the virus checking page title. How can I make sure the result always return Google?
Using pauses in your tests is a bad practice. It's better to use explicit waits instead.
E.g. you can use waitUntil method, provided by Wdio API. So, you can create helper function to wait for expected URL.
Here is the example in ES6:
function waitForUrl(url, timeout) {
browser.waitUntil(() => browser.getUrl().includes(url)
}, timeout, `Expected url must be ${url}`)
}
You can simply pass function (or promise) as waitUntil condition along with timeout (in ms), error message and interval (also in ms, default value is 500ms). So, waitUntil will be waiting until that condition is fulfilled with a truthy value. Otherwise, error will be thrown.
You can use the pause(time) function to pause your test for a while till you are redirected to Google.
webdriverio
.remote(options)
.init()
.url('http://www.google.com')
.pause(5000)
.getTitle().then(function(title) {
console.log('Title was: ' + title);
})
.end();

How to set jasmine for karma e2e for testing angular app?

I try to create e2e tests with karma and jasmine with yeoman. In my karma-e2e.conf.js I add jasmine:
files = [
JASMINE,
JASMINE_ADAPTER,
ANGULAR_SCENARIO,
ANGULAR_SCENARIO_ADAPTER,
'test/e2e/**/*.js'
];
A need async testing so I need to use runs, waits, waitsFor (https://github.com/pivotal/jasmine/wiki/Asynchronous-specs)
But if I try to use it:
it('test', function () {
runs(function () {
...
});
});
Scenatio test runner returns this:
TypeError: Cannot call method 'runs' of null
at runs (http://localhost:8080/adapter/lib/jasmine.js:562:32)
at Object.<anonymous> (http://localhost:8080/base/test/e2e/eduUser.js:42:3)
at Object.angular.scenario.SpecRunner.run (http://localhost:8080/adapter/lib/angular-scenario.js:27057:15)
at Object.run (http://localhost:8080/adapter/lib/angular-scenario.js:10169:18)
I don't know where the problem is. Can you help me please?
Angular e2e tests with Karma don't and can't use the JASMINE adapter. Instead you have the ANGULAR_SCENARIO_ADAPTER which has a similar feel to writing Jasmine tests.
All commands in the adapter's API are asynchronous anyway. For example element('#nav-items').count() doesn't return a number, it returns a Future object. Future objects are placed in a queue and executed asynchronously as the runner progresses. To quote the API docs:
expect(future).{matcher}:
[...] All API statements return a future object, which get a value assigned after they are executed.
If you need to run your own asynchronous test code, you can extend the adapter's DSL, this is easier than it might sound. The idea is that you return your own Future which can be evaluated by a matcher such as toBe(). There are some examples on how to do this in the e2e-tests.js Gist from Vojta. Just remember to call done(null, myRetrunValue); when your test code is successful (myRetrunValue is the value evaluated by your matcher). Or done('Your own error message'); if you want the test to fail.
UPDATE: In response to question below. To simulate a login, first add a function called login to the dsl:
angular.scenario.dsl('login', function() {
return function(selector) {
// #param {DOMWindow} appWindow The window object of the iframe (the application)
// #param {jQuery} $document jQuery wrapped document of the application
// #param {function(error, value)} done Callback that should be called when done
// (will basically call the next item in the queuue)
return this.addFutureAction('Logging in', function(appWindow, $document, done) {
// You can do normal jQuery/jqLite stuff here on $document, just call done() when your asynchronous tasks have completed
// Create some kind of listener to handle when your login is complete
$document.one('loginComplete', function(e){
done(null, true);
}).one('loginError', function(e){
done('Login error', false);
});
// Simulate the button click
var loginButton = $document.find(selector || 'button.login');
loginButton.click();
})
};
});
And then call:
beforeEach( function()
{
expect( login('button.login') ).toBeTruthy();
});