In Nightwatch, `.url()` blocks assertions for too long - selenium

It appears that using browser.url() (http://nightwatchjs.org/api/#url) to navigate causes the WebDriver client to wait until the page is fully loaded - however I have assertions I want to make before that point.
"Shows splash screen for a few seconds": function(client) {
client
.url(client.testURL)
// at this point, the splash has already come and gone, so
// this next command times out
.waitForElementVisible('#splash img', 10000)
.waitForElementNotVisible('#splash', 10000);
},
Is this possible? I think my only other option is to disable the splash in a test scenario.
I'm running these tests on Firefox v45.

You can do this by setting Firefox profile preferences as shown in below
https://github.com/nightwatchjs/nightwatch/issues/748
The preference you need to set is webdriver.load.strategy to be unstable. But this means that waiting for page to load is now all your business
var FirefoxProfile = require('firefox-profile');
function setProfile(browser, profile, callback) {
profile.encoded(function (encodedProfile) {
browser.options.desiredCapabilities['firefox_profile'] = encodedProfile;
callback();
});
}
function setFirefoxProfile(browser, done) {
var profile = new FirefoxProfile();
profile.setPreference('webdriver.load.strategy', 'unstable');
setProfile(browser, profile, done);
}
// and in my test module
before: function (browser, done) {
setFirefoxProfile(browser, done);
}

Related

How to use Puppetter with express API and properly closing the browser without affecting other concurrent request

I have a website which has some data as HTML (loaded via ajax) and I have to convert that to JSON with custom formatting.
So, for this I choose Puppeteer.
const browser = await puppeteer.launch({
headless: true, args: ['--no-sandbox']
});
const page = await browser.newPage();
This web API that I'm developing will be having concurrent web requests, so I thought browser.close() might affect the other concurrent requests, so I decide to do only page.close.
One problem that I'm facing is when I do puppeteer.launch, for each request it opens two about:blank tab in a new window.
And when browser.newPage() is requested it returns one of the blank tabs and leaves the other one opened.
That leads to multiple window opened with multiple about:blank.
Here I don't know the right way to handle this, I can't close the browser because it will close all the pages which are being used by other requests.
You are seeing an empty (about:blank) tab each time you run this code, because you are doing two things here:
Launching a new browser - which already starts with an open tab
const browser = await puppeteer.launch({
headless: true, args: ['--no-sandbox']
});
Opening a new tab.
const page = await browser.newPage();
If you don't want to have "zombie" blank tabs, then you can just reuse the initial tab like this:
const browser = await puppeteer.launch({
headless: true, args: ['--no-sandbox']
});
const currentPages = await browser.pages(); // list the opened tabs
const [page] = currentPages; // use the first (and only) opened tab.
Note that in this case, since you are just reusing the only one and initial tab, closing it with page.close() will have the same effect as closing the browser with browser.close().
Exploring some Express + Browser concurrency alternatives
Consider that a different solution would take place if you want to reuse the same browser instance for the lifetime of your Express server, ie. serve all requests on the same browser, or if you want to use a new browser instance for each individual request.
1. One browser instance per server
In this case it might make sense, depending on your requirements, in managing one tab per request.
// launch the browser instance, once
const browser = await puppeteer.launch({
headless: true, args: ['--no-sandbox']
});
// handle incoming requests
app.get("/foo", async (req, res) => {
const page = await browser.newPage();
try {
// ... execute some logic on this new page
} catch(error) {
// whoops, logic went wrong, respond with 500 or something
} finally {
// cleanup: close the opened tab, no matter how the logic resulted
await page.close()
}
})
Note that still in this scenario, the browser context would be shared across the pages, for example cookies, local storage, and so on. You have to consider this if you plan to allow concurrent requests that also can have conflicts in reusing the same shared context.
2. One browser instance per request
In this scenario you launch a new browser instance per request, you ensure each request will have a clean context and won't collide with other possible requests.
app.get("/foo", async (req, res) => {
// launch the browser instance, one per request
const browser = await puppeteer.launch({
headless: true, args: ['--no-sandbox']
});
// no need to open a new tab, reuse the first one
const [page] = await browser.pages();
try {
// ... execute some logic on the page
} catch(error) {
// whoops, logic went wrong, respond with 500 or something
} finally {
// cleanup: close the browser
// await page.close() // (not really needed if you will close the entire browser,
// and would have the same effect as browser.close()
// if you haven't opened more tabs)
await browser.close()
}
})
But consider that spining a new browser process up would also be more resource-intensive, and your request would take more time to resolve, compared to reusing an already available browser process.
EDIT: code formatting.

How do I deal with a page loading in a secondary browser tab when I run Protractor tests headless (Chrome)?

When this code clicks a “Print”-icon, a PDF-file will be generated and displayed in a new browser-tab. I want to switch to this tab, wait until the PDF has finished loading there, check a part of the URL and then close that secondary tab.
it( "should open document as PDF-file in new browser-tab", async () => {
const mUrl = "TherapyReportForm/Export";
await mTherapyReportView.btnPrintform.click();
await browser.getAllWindowHandles().then(async (handles) => {
//if there is a secondary browser-tab open...
if (handles.length > 1) {
//...click on it
await browser.driver.switchTo().window(handles[1]);
}
});
//confirm that the url of the secondary tab matches the print-url pattern
await browser.sleep( 18000 );
expect( await browser.getCurrentUrl() ).toContain( mUrl );
await browser.getAllWindowHandles().then( async (handles) => {
//if there are multiple browser-tabs open
if (handles.length > 1) {
//close the secondary and move back to first
await browser.driver.close();
await browser.driver.switchTo().window( handles[0] );
}
} );
} );
The test above works reliably, unless I run it in chromes headless-mode, then the test-run breaks at line
expect(await browser.getCurrentUrl()).toContain(mUrl);
console output
The console output proves that it switches to the secondary tab, but apparently never tries to load the url. Since it fails to close this secondary tab the entire suite will break at that point.
What am I doing wrong?
Here is a thing... downloading functionality is not available in headless chrome. That's for sure. What I'm going to talk about below, I'm a little bit uncertain if that's the case
There is no such thing as 'open' pdf file in browser. The reason is that behind scene the browser actually downloads it (maybe temporarily). This is why you'll never be able to do that in headless
But that's rather a shot in the dark

testcafe - CAS authentication

New to TestCafe. Got some simple example tests working easily. However, I wasn't able to find any examples of features that would seem to allow me to log in to my application via a CAS authentication page.
This code works to find the login button on the login page and click it.
fixture`VRT`
.page `http://myapp.com/login/`;
test('Find Login button', async t => {
const input = Selector('input.btn');
await t.click(input);
});
And this would work to type in the username and password on the login page:
test('Login using CAS', async t => {
await t
.expect("#username").exists
.typeText('#username', 'myuser')
.typeText('#password', '***')
.click('#submit');
});
But the problem is that there seems to be no way to continue from one test to another. So I can't go from the first test that opens the login page, to the next test that enters the credentials. It seems like, for TestCafe, every test has to specify its own page.
If I try to go to the CAS login page directly, by specifying it as the fixture "page", TestCafe fails to open the page, I think because the URL is extremely long.
Thanks for any suggestions.
Update:
So, using roles got me a bit further (thanks) but had to get through one more CAS page with an input button to click before getting to the page I wanted to test. Was able to add in another click to the role login:
import { Role } from 'testcafe';
import { Selector } from 'testcafe';
const finalLoginBtn = Selector('input.btn');
const myUserRole = Role('http://example.com/login', async t => {
await t
.click('input.btn')
.typeText('#username', 'my-username')
.typeText('#password', '*****')
.click('#submit')
.click(finalLoginBtn);
}, { preserveUrl: true });
fixture`VRT`
test("My User Page", async t => {
await t.navigateTo(`http://example.com`)
await t.useRole(myUserRole);
});
The TestCafe 'User Roles' functionality should suit your requirements. Please, refer to the following topic in the TestCafe documentation for details: https://devexpress.github.io/testcafe/documentation/test-api/authentication/user-roles.html

how to close authentication pop up in Selenium IE webdriver?

I've got web application with browser authentication before webpage is loaded so in automated test i am log in via http://user:password#domain but when i am entering wrong credentials, pop up would not disappear it would wait for correct credentials. But i want to test if there is a access to webpage with wrong credentials, every browser is closing without problem, but IE is throwing
modal dialog present
i was trying to use
driver.SwitchTo().Alert().Dismiss();
but it doesn't work.
any idea how to close that pop up authentication?
Authentication popup is NOT generated by Javascript / it is not a javascript alert. So It can not be handled by WebDriver.
You did not mention the programming language you use. Your sample code seems to be in C#. In Java - we have a java.awt.Robot to simulate keyboard events. You might have to find C# equivalent to press the ESC key.
Robot robot = new Robot();
//Press ESC key
robot.keyPress(InputEvent.VK_ESCAPE);
robot.keyRelease(InputEvent.VK_ESCAPE);
In project I currently work in I decided to take completely another approach. I had similar situation of NTLM authentication but I'm pretty sure that for basic authentication it will work as well. I wrote simple chrome extension which utilizes listener on chrome.webRequest.onAuthRequired. Additionally, by putting additional methods in content script to communicate with background script I've managed a way to change credentials on the fly without caring about annoying windows.
background.js:
var CurrentCredentials = {
user: undefined,
password: undefined
}
chrome.runtime.onMessage.addListener(function(request) {
if(request.type === 'SET_CREDENTIALS') {
CurrentCredentials.user = request.user;
CurrentCredentials.password = request.password;
}
});
chrome.webRequest.onAuthRequired.addListener(function(details, callback) {
if(CurrentCredentials.user !== undefined && CurrentCredentials.password !== undefined) {
return {authCredentials:
{
username: CurrentCredentials.user,
password: CurrentCredentials.password
}
};
}
}, {urls: ["http://my-server/*"]}, ["blocking"]);
and content-script.js:
var port = chrome.runtime.connect();
window.addEventListener("message", function(event) {
if (event.source !== window)
return;
if (event.data.type && (event.data.type === 'SET_CREDENTIALS')) {
chrome.runtime.sendMessage({
type: 'SET_CREDENTIALS',
user: event.data.user,
password: event.data.password
});
}
}, false);
Extension must be packed as crx and added to ChromeOptions prior to driver initialization. Additionally, it is required to set credentials BEFORE actual call to site that needs authentication, so I browse simple html file on the disk and post a chrome message while being on the page: window.postMessage({type: 'SET_CREDENTIALS', user: arguments[0], password: arguments[1]}, '*') by using IJavascriptExecutor.ExecuteScript method. After it is done, no authentication window shows up and user is authentication as expected.

How to perfectly isolate and clear environments between each test?

I'm trying to connect to SoundCloud using CasperJS. What is interesting is once you signed in and rerun the login feature later, the previous login is still active. Before going any further, here is the code:
casper.thenOpen('https://soundcloud.com/', function() {
casper.click('.header__login');
popup = /soundcloud\.com\/connect/;
casper.waitForPopup(popup, function() {
casper.withPopup(popup, function() {
selectors = {
'#username': username,
'#password': password
};
casper.fillSelectors('form.log-in', selectors, false);
casper.click('#authorize');
});
});
});
If you run this code at least twice, you should see the following error appears:
CasperError: Cannot dispatch mousedown event on nonexistent selector: .header__login
If you analyse the logs you will see that the second time, you were redirected to https://soundcloud.com/stream meaning that you were already logged in.
I did some research to clear environments between each test but it seems that the following lines don't solve the problem.
phantom.clearCookies()
casper.clear()
localStorage.clear()
sessionStorage.clear()
Technically, I'm really interested about understanding what is happening here. Maybe SoundCloud built a system to also store some variables server-side. In this case, I would have to log out before login. But my question is how can I perfectly isolate and clear everything between each test? Does someone know how to make the environment unsigned between each test?
To clear server-side session cache, calling: phantom.clearCookies(); did the trick for me. This cleared my session between test files.
Example here:
casper.test.begin("Test", {
test: function(test) {
casper.start(
"http://example.com",
function() {
... //Some testing here
}
);
casper.run(function() {
test.done();
});
},
tearDown: function(test) {
phantom.clearCookies();
}
});
If you're still having issues, check the way you are executing your tests.
Where did you call casper.clear() ?
I think you have to call it immediately after you have opened a page like:
casper.start('http://www.google.fr/', function() {
this.clear(); // javascript execution in this page has been stopped
//rest of code
});
From the doc: Clears the current page execution environment context. Useful to avoid having previously loaded DOM contents being still active.