How do you poll for a condition in Intern / Leadfoot (not browser / client side)? - selenium

I'm trying to verify that an account was created successfully, but after clicking the submit button, I need to wait until the next page has loaded and verify that the user ended up at the correct URL.
I'm using pollUntil to check the URL client side, but that results in Detected a page unload event; script execution does not work across page loads. in Safari at least. I can add a sleep, but I was wondering if there is a better way.
Questions:
How can you poll on something like this.remote.getCurrentUrl()? Basically I want to do something like this.remote.waitForCurrentUrlToEqual(...), but I'm also curious how to poll on anything from Selenium commands vs using pollUntil which executes code in the remote browser.
I'm checking to see if the user ended up at a protected URL after logging in here. Is there a better way to check this besides polling?
Best practices: do I need to make an assertion with Chai or is it even possible when I'm polling and waiting for stuff as my test? For example, in this case, I'm just trying to poll to make sure we ended up at the right URL within 30 seconds and I don't have an explicit assertion. I'm just assuming the test will fail, but it won't say why. If the best practice is to make an assertion here, how would I do it here or any time I'm using wait?
Here's an example of my code:
'create new account': function() {
return this.remote
// Hidden: populate all account details
.findByClassName('nextButton')
.click()
.end()
.then(pollUntil('return location.pathname === "/protected-page" ? true : null', [], 30000));
}

The pollUntil helper works by running an asynchronous script in the browser to check a condition, so it's not going to work across page loads (because the script disappears when a page loads). One way to poll the current remote URL would be to write a poller that would run as part of your functional test, something like (untested):
function pollUrl(remote, targetUrl, timeout) {
return function () {
var dfd = new Deferred();
var endTime = Number(new Date()) + timeout;
(function poll() {
remote.getCurrentUrl().then(function (url) {
if (url === targetUrl) {
dfd.resolve();
}
else if (Number(new Date()) < endTime) {
setTimeout(poll, 500);
}
else {
var error = new Error('timed out; final url is ' + url);
dfd.reject(error);
}
});
})();
return dfd.promise;
}
}
You could call it as:
.then(pollUrl(this.remote, '/protected-page', 30000))
When you're using something like pollUntil, there's no need (or place) to make an assertion. However, with your own polling function you could have it reject its promise with an informative error.

Related

How to make Selenium WebdriverJS code execute in sequence

I'm relatively new to using WebDriverJS and trying out a simple script to begin with.
However, am facing a lot of issues and did not find any resources that were helpful.
Scenario being Tested:
Launch browser
Navigate to google.com
Capture Title of the page
Add a wait statement (driver.sleep)
Enter some text in Search box
Here is the code snippet:
var webdriver = require('selenium-webdriver'),
By = webdriver.By,
until = webdriver.until;
var driver = new webdriver.Builder().forBrowser('chrome').build();
driver.get("http://www.google.com");
driver.getTitle().then(function(title) {
console.log("Title is: " + title);
});
console.log('Before sleep');
driver.sleep(10000);
console.log('After sleep');
driver.findElement(By.name('q')).sendKeys("Hello");
Here is the output:
Before sleep
After sleep
DevTools listening on ws://127.0.0.1:52449/devtools/browser/aea4d9eb-20ee-4f10-b53f-c2003c751796
Title is:
As can be seen, it is a very straight forward scenario. However none of it is working as expected.
Below are my queries/ observations:
console.log for Before/ After sleep is executed as the very first statement even before browser is launched whereas it is not clearly the intention.
Title is returned an empty String. No value printed.
driver.sleep() never waited for the specified duration. All commands got immediately executed. How to make driver hard wait when driver.sleep is not working?
Tried adding implicit wait, however that resulted in error as well.
What are the best practices to be followed?
I did not find very many helpful webdriver javascript resources and it is not clear how to proceed.
Any guidance is appreciated. TIA.!
I referred the documentation as well and similar steps are given there. Not sure if there is some issue from my end. https://github.com/SeleniumHQ/selenium/wiki/WebDriverJs
Assuming that you example is written in JavaScript and runs on Node.js, it looks to be as if you would miss all the waiting for asynchronous functions to have finished processing. Please be aware that most functions return a promise and you must wait for the promise to be resolved.
Consider the following example code:
const {Builder, By, Key, until} = require('selenium-webdriver');
(async function example() {
let driver = await new Builder().forBrowser('firefox').build();
try {
await driver.get('http://www.google.com/ncr');
await driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN);
await driver.wait(until.titleIs('webdriver - Google Search'), 1000);
} finally {
await driver.quit();
}
})();

TestCafe : How can I make TestCafe to run some application code only once before all the fixtures

In my application, I want to set up some test data from the UI before running any Fixture. I want to do this set up only once and don't want to do this before each fixture.
Can someone please help me on how to do this ?
I tried to use approach mentioned on below thread but I cannot use test controller - t inside before.
https://testcafe-discuss.devexpress.com/t/run-the-same-before-and-after-hook-for-all-fixtures-and-configure-a-baseurl/551
I have an idea you can check if you feel it works for you as below, you will still use beforeEach in this case as you wish to access to t:
let didSetup = false;
fixture`yourFixture`
.beforeEach(async t => {
if (!didSetup) {
// You set up things here
await yourSetup();
didSetup = true;
}
// Otherwise won't do anything
})

Make PhantomJs wait for an Ajax Request before generating a pdf

Using the example from phantomjs works like a charm
https://github.com/ariya/phantomjs/blob/master/examples/rasterize.js?utm_content=bufferda3e0&utm_source=buffer&utm_medium=twitter&utm_campaign=Buffer
However what happens if the url needs to make an Ajax request in order to load it's data? Can I fire a custom event so the pdf is only generated then ?
(I don't want to guess how much time the request will take and set a timeout)
The common solution to this problem is to wait for an element that will appear on the page after AJAX request has finished.
Include the waitFor function from this example and wait for the first function passed as argument to waitFor to return true, then it will run the function passed as the second argument.
page.open("https://example.com/ajaxified/", function (status) {
waitFor(function() {
return page.evaluate(function() {
return document.querySelectorAll(".report").length > 0;
});
}, function() {
page.render("report.pdf");
phantom.exit();
});
});

Capybara, Poltergeist, PhantomJS, evaluate_script, callback

I am using the following code, which uses the imagesLoaded package with a callback to tell me when an element with a particular csspath has finished loading all of its images:
imagesLoadedScript = "imagesLoaded( '#{csspath}', { background: true }, function(message) { console.log('PHANTOM CLIENT REPORTING: #{csspath} Images Loaded'); return message; })"
imagesLoadedScript = imagesLoadedScript.strip.gsub(/\s+/,' ')
#session.evaluate_script(imagesLoadedScript)
The timing of the console.log statement, on inspection of PhantomJS logs with debug on, indicates that Capybara/Poltergiest is not waiting for the images to load, as expected, before it moves on to the next statement. I also cannot return a true (or false) value from inside the callback as I would like.
Capybara responds with
{"command_id":"678f1e2e-4820-4631-8cd6-413ce6f4b66f","response":"(cyclic structure)"}
Anyone have any ideas on how to return a value from inside a callback in a function executed via evaluate_script?
Many thanks.
TLDR; You can't
evaluate_script doesn't support asynchronous functions - you must return the result you want from the function passed in. One way to do what you want would be to execute the imagesLoaded script and have the callback set a global variable, and then loop on an evaluate_script fetching the result of the global until it's what you want - A very basic implementation would be something like
imagesLoadedScript = "window.allImagesLoaded = false; imagesLoaded( '#{csspath}', { background: true }, function() { window.my_images_loaded = true })"
#session.execute_script(imagesLoadedScript)
while !#session.evaluate_script('window.allImagesLoaded')
sleep 0.05
end
Obviously this could be made more flexible with a timeout ability, etc.
A second option would to write a custom capybara selector type for images with a loaded filter, although with the need for background image checking it would become pretty complicated and probably too slow to be useful.
Just in case someone finds this later.
I did roughly what Thomas Walpole suggested in his answer, in a more roundabout fashion, but taking advantage of Poltergeist's inherent waiting capabilities;
#to check that the target has loaded its images, run images loaded
#after a small timeout to allow the page to get the images
#append a marker div to the dom if the images have successfully loaded
imagesLoadedScript = "var item = document.querySelector('#{csspath}');
window.scroll(0, item.offsetTop);
function imagesDone(path, fn) {
imagesLoaded( path, function(instance) {
console.log('PHANTOM CLIENT REPORTING: ' + path + ' Images Loaded');
fn(true);
})
}
setTimeout(function(){
imagesDone('#{csspath}', function(done) {
var markerDiv = document.createElement('div');
markerDiv.id = 'ImagesLoadedMarker';
document.getElementsByTagName('html')[0].appendChild(markerDiv);
});
}, 1000)"
#then we strip the new lines and spaces that we added to make it readable
imagesLoadedScript = imagesLoadedScript.strip.gsub(/\s+/,' ')
#now we just execute the script as we do not need a return value
#session.execute_script(imagesLoadedScript)
#then we check for the marker, using capybara's inbuilt waiting time
if #session.has_xpath? "//*[#id ='ImagesLoadedMarker']"
Rails.logger.debug "!!!!! PhantomClient: Images Loaded Reporting: #{csspath} Images Loaded: Check Time #{Time.now} !!!!!"
#session.save_screenshot(file_path, :selector => csspath)
else
Rails.logger.debug "!!!!! PhantomClient: Images Loaded Reporting: #{csspath} Images NOT Loaded: Check Time #{Time.now} !!!!!"
#session.save_screenshot(file_path, :selector => csspath)
end

How do I get data from a background page to the content script in google chrome extensions

I've been trying to send data from my background page to a content script in my chrome extension. i can't seem to get it to work. I've read a few posts online but they're not really clear and seem quite high level. I've got managed to get the oauth working using the Oauth contacts example on the Chrome samples. The authentication works, i can get the data and display it in an html page by opening a new tab.
I want to send this data to a content script.
i'm having a lot of trouble with this and would really appreciate if someone could outline the explicit steps you need to follow to send data from a bg page to a content script or even better some code. Any takers?
the code for my background page is below (i've excluded the oauth paramaeters and other )
` function onContacts(text, xhr) {
contacts = [];
var data = JSON.parse(text);
var realdata = data.contacts;
for (var i = 0, person; person = realdata.person[i]; i++) {
var contact = {
'name' : person['name'],
'emails' : person['email']
};
contacts.push(contact); //this array "contacts" is read by the
contacts.html page when opened in a new tab
}
chrome.tabs.create({ 'url' : 'contacts.html'}); sending data to new tab
//chrome.tabs.executeScript(null,{file: "contentscript.js"});
may be this may work?
};
function getContacts() {
oauth.authorize(function() {
console.log("on authorize");
setIcon();
var url = "http://mydataurl/";
oauth.sendSignedRequest(url, onContacts);
});
};
chrome.browserAction.onClicked.addListener(getContacts);`
As i'm not quite sure how to get the data into the content script i wont bother posting the multiple versions of my failed content scripts. if I could just get a sample on how to request the "contacts" array from my content script, and how to send the data from the bg page, that would be great!
You have two options getting the data into the content script:
Using Tab API:
http://code.google.com/chrome/extensions/tabs.html#method-executeScript
Using Messaging:
http://code.google.com/chrome/extensions/messaging.html
Using Tab API
I usually use this approach when my extension will just be used once in a while, for example, setting the image as my desktop wallpaper. People don't set a wallpaper every second, or every minute. They usually do it once a week or even day. So I just inject a content script to that page. It is pretty easy to do so, you can either do it by file or code as explained in the documentation:
chrome.tabs.executeScript(tab.id, {file: 'inject_this.js'}, function() {
console.log('Successfully injected script into the page');
});
Using Messaging
If you are constantly need information from your websites, it would be better to use messaging. There are two types of messaging, Long-lived and Single-requests. Your content script (that you define in the manifest) can listen for extension requests:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == 'ping')
sendResponse({ data: 'pong' });
else
sendResponse({});
});
And your background page could send a message to that content script through messaging. As shown below, it will get the currently selected tab and send a request to that page.
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {method: 'ping'}, function(response) {
console.log(response.data);
});
});
Depends on your extension which method to use. I have used both. For an extension that will be used like every second, every time, I use Messaging (Long-Lived). For an extension that will not be used every time, then you don't need the content script in every single page, you can just use the Tab API executeScript because it will just inject a content script whenever you need to.
Hope that helps! Do a search on Stackoverflow, there are many answers to content scripts and background pages.
To follow on Mohamed's point.
If you want to pass data from the background script to the content script at initialisation, you can generate another simple script that contains only JSON and execute it beforehand.
Is that what you are looking for?
Otherwise, you will need to use the message passing interface
In the background page:
// Subscribe to onVisited event, so that injectSite() is called once at every pageload.
chrome.history.onVisited.addListener(injectSite);
function injectSite(data) {
// get custom configuration for this URL in the background page.
var site_conf = getSiteConfiguration(data.url);
if (site_conf)
{
chrome.tabs.executeScript({ code: 'PARAMS = ' + JSON.stringify(site_conf) + ';' });
chrome.tabs.executeScript({ file: 'site_injection.js' });
}
}
In the content script page (site_injection.js)
// read config directly from background
console.log(PARAM.whatever);
I thought I'd update this answer for current and future readers.
According to the Chrome API, chrome.extension.onRequest is "[d]eprecated since Chrome 33. Please use runtime.onMessage."
See this tutorial from the Chrome API for code examples on the messaging API.
Also, there are similar (newer) SO posts, such as this one, which are more relevant for the time being.