Selenium WebDriver browser abstraction - node.js

Is there a way to use the same test code for Firefox and chrome? Kind of abstraction where I can execute piece of code for all browser driver, without repeating blocks of test code for each browser driver
for example code
var searchBox = ffDriver.findElement(webdriver.By.name('q'));
repeats for each browser driver.
var assert = require('assert'),
test = require('selenium-webdriver/testing'),
webdriver = require('selenium-webdriver'),
SeleniumServer = require('selenium-webdriver/remote').SeleniumServer;
var server = new SeleniumServer('selenium-server-standalone-2.39.0.jar', {
port: 4444
});
server.start();
var ffDriver = new webdriver.Builder()
.usingServer(server.address())
.withCapabilities(webdriver.Capabilities.firefox())
.build();
var chromeDriver = new webdriver.Builder()
.withCapabilities(webdriver.Capabilities.chrome())
.build();
test.describe('Google Search', function () {
test.it('should have a query box that you can type in ff', function () {
ffDriver.get('http://www.google.com');
var searchBox = ffDriver.findElement(webdriver.By.name('q'));
searchBox.sendKeys('webdriver');
searchBox.getAttribute('value').then(function (value) {
assert.equal(value, 'webdriver');
});
ffDriver.quit();
});
test.it('should have a query box that you can type in chrome', function () {
chromeDriver.get('http://www.google.com');
var searchBox = chromeDriver.findElement(webdriver.By.name('q'));
searchBox.sendKeys('webdriver');
searchBox.getAttribute('value').then(function (value) {
assert.equal(value, 'webdriver');
});
chromeDriver.quit();
});
});

Yes.
var chromeDriver = //Create your chromeDriver here
var ffDriver = //Create your ffDriver here
var drivers = [chromeDriver, ffDriver];//This creates an array of all of the drivers
for (var i = 0; i< drivers.length; i++){//Run the following block of code for each driver
var driver = drivers[i];//Assign the driver variable
driver.get('http://www.google.com');//Use the driver variable.
//The rest of the automation code
}
Note, that in the future, you are probably going to want more structure in your code, but the code above should work fine.

Related

Rethinkdb Node.JS .changes() unwanted looping

i have a a problem with Node JS, and rethinkdb module.
I'm currently developing program for my thesis
this pieces code of db.js:
var r = require("rethinkdb");
var host = "localhost";
var db = "example";
var port = 28015;
class dbs{
connectToDb(callback){
r.connect({
host:host,
port:port,
db:db
},function(err,connection){
return callback(err,connection);
});
}
streamAllData(tableName,callback){
this.connectToDb(function(err,conn){
r.table(tableName).changes().run(conn,function(err,cursor){
if(err){
return callback(true,err);
}
else{
cursor.next(function(err,rows){
return callback(null,rows);
});
}
});
});
}
}
and this pieces of code from server.js
var dbrs = require("./db");
var rdb = new dbrs();
var io = require("socket.io").listen(https.createServer(options,app).listen(port));
io.on("connection",function(socket){
rdb.streamAllData("userlocation",function(err,data){
socket.emit("broadcast:userlocation",data);
});
});
that result is always sending 7 same data . actually mobile phone sending cordinates to server is clean with configured interval.
this unwanted looping is always crashed my browser when im trying to draw driver location to maps.
that is a screenshot from chrome console
You method name streamAllData does not match your usage of cursor.next, which only fetches a single result. Perhaps you meant to use cursor.each instead?
See https://www.rethinkdb.com/api/javascript/next/

Make parallel requests to a Selenium Webdriver grid

I'm trying to use a selenium server grid to run multiple commands in parallel.
Here is my first test code:
var webdriver = require('selenium-webdriver');
for(var u = 0; u < 3; u++) {
makeScreenshot('foo/test' + u + '.png');
}
function makeScreenshot(path) {
var driver = new webdriver.Builder().forBrowser('firefox').usingServer('http://someurl:44111/wd/hub/').build();
console.log('Get');
driver.get('http://www.somepage.com').then(function() {
console.log('Screenshot');
driver.takeScreenshot().then(function(data){
console.log(path);
//var decodedImage = new Buffer(data, 'base64')
driver.quit();
});
});
}
That is the result:
Get
Get
Get
Screenshot
foo/test0.png
Screenshot
foo/test1.png
Screenshot
foo/test2.png
screenshot of requests
The "Get" appears immediately in sequence, "driver.get" creates a promise. My idea here is that the three requests are made asynchronously and thus appear almost simultaneously. But as you can see in the screenshot they will be made one after the other.
The grid definitely has enough selenium instances so why isn't the driver working in parallel?
It seems to me that "new webdriver.Builder()" creates some kind of singleton that doesn't work async but waits for the previous request to finish!?
Thanks for any help!
The answer may be multiple control flows:
WebDriverJS supports defining "parallel" flows using
webdriver.promise.createFlow(). This function accepts a callback which
will be passed the newly created flow. Tasks scheduled within this
flow will be synchronized with each other, but will remain independent
of any other control flows. Each call to createFlow() returns a
promise that will resolve when the flow has completed.
The example at the end of the chapter (which I'll quite verbatim) shows multiple Google search terms being tested concurrently:
var terms = [
'javascript',
'selenium',
'webdriver'
];
var flows = terms.map(function(term) {
return webdriver.promise.createFlow(function() {
var driver = new webdriver.Builder().build();
driver.get('http://www.google.com');
driver.findElement(webdriver.By.name('q')).sendKeys(term);
driver.findElement(webdriver.By.name('btnG')).click();
driver.getTitle().then(function(title) {
if (title !== (term + ' - Google Search')) {
throw Error('Unexpected title: ' + title);
}
});
});
});
webdriver.promise.fullyResolved(flows).then(function() {
console.log('All tests passed!');
});
It should be easy enough to add your custom driver build and lookups into that example. Perhaps the following:
var flows = [0,1,2,3].map(function(index) {
return webdriver.promise.createFlow(function() {
var driver = new webdriver.Builder().forBrowser('firefox').usingServer('http://someurl:44111/wd/hub/').build();
console.log('Get');
driver.get('http://www.somepage.com').then(function() {
console.log('Screenshot');
driver.takeScreenshot().then(function(data){
console.log('foo/test' + index + '.png');
//var decodedImage = new Buffer(data, 'base64')
driver.quit();
});
});
});
});

Selecting Frames in node.js

For some reason I can't switch to nested frames in selenium-webdriver using node.js. I have tried setting timeouts to let the page load or timeouts to give the driver time to switch to another frame, nothing worked. This question is most likely a continuation of this. I am getting a NoSuchFrameError.
HTML - full url here
<!DOCTYPE html>
<html>
<head>
<title>HTML Target Frames</title>
</head>
<frameset rows="16%,84%">
<frame src="./framesHtml/top.htm" name="top_page" >
<frameset cols="50%,50%">
<frame src="./framesHtml/menu.htm" name="menu_page" >
<frame src="./framesHtml/main.html" name="main_page" >
</frameset>
</frameset>
</html>
Function switching to main_page
var webdriver = require('selenium-webdriver');
var chrome = require('selenium-webdriver/chrome');
var chromePath = require('selenium-chromedriver').path;
var FrameHandler = require('../../JS-Selenium-Toolkit/src/FrameHandler');
var expect = require('chai').expect;
describe('FrameHandler', function () {
it('Should pass if the set frame is main_page', function (done) {
this.timeout(15000);
var service = new chrome.ServiceBuilder(chromePath).build();
chrome.setDefaultService(service);
var chromeDriver = new webdriver.Builder()
.withCapabilities(webdriver.Capabilities.chrome())
.build();
var frameHandler = new FrameHandler(chromeDriver);
//check current frame name
frameHandler.getCurrentFrameName(function (name) {
console.log(name + ' current frame inside second function');
});
chromeDriver.get('http://orasi.github.io/Selenium-Java-Core/sites/unitTests/orasi/utils/frameHandler.html').then(function () {
frameHandler.switchToFrame('top_page').then(function () {
frameHandler.switchToFrame('main_page').then(function () {
frameHandler.getCurrentFrameName(function (name) {
console.log(name + ' this frame was switched to ');
expect(name).to.equal('main_page');
done();
});
});
});
});
});
});
FrameHandler Object
var webdriver = require('selenium-webdriver');
var FrameHandler = function (driver) {
this.switchToFrame = function (name)
{
if (typeof name !== 'string' || name === '' || !name)
{
console.log('error');
}
else
{
console.log(this.getCurrentFrameName(function (name) {
console.log(name + ' this is the current frame before switch');
}));
console.log(name + ' switch to this frame');
return driver.switchTo().frame(name);
}
};
this.getCurrentFrameName = function (callback)
{
driver.executeScript('return self.name').then(function (name)
{
return callback(name);
});
};
};
module.exports = FrameHandler;
I haven't verified myself with your example, but the consensus is that you cannot switch directly to a nested <frame> from the top-level, you need to switch into a parent <frame> first.
In other words, into 'top_page', then into 'main_page'.
See the top answers to:
Unable to click with Selenium in nested frames
How to navigate a subframe inside a frameset using Selenium WebDriver?
After further testing and switching drivers I found that the chrome driver doesn't support nested framesets. I tried going from top_page to menu_page and also tried just going directly to the menu_page, neither worked. I tried all types of solutions and was unable to get anything to work until I switched from the chrome driver to ie or firefox. I also tried removing the nested frameset in the html file and then running the chrome driver, the test case passed. So this is in fact an issue with nested framesets and the chrome driver. I really need to get a fix for this as this can affect automation using the chrome driver.

simulate multiple socket.io connection

Not a duplicate of : this question, as I'm trying to use the link posted as answer to solve my problem.
I'm creating a little dummy socket client to help testing one of my product, it looks like so :
var ee = require('events').EventEmitter;
require('http').globalAgent.maxSockets = 1000;
function Dummy(){
this.config = require('../config/credentials.js');
this.socket = require('socket.io-client')(this.config.socketIO.url);
var self = this;
this.socket.on('task', function(task) {
self.createTask(task);
});
}
util.inherits(Dummy, ee);
module.exports = Dummy;
Dummy.prototype.createTask = function(name){
var self = this;
setInterval(function sendStuff(){
self.socket.emit("msg")
}, 1000);
};
On its own, it works fine; However, when I try to launch many of them like so :
for (var i = 0; i < 100; i++) {
fakeClients.push(new Dummy());
};
Is appears to pool connections and shows as one client only.
Based on this link, I thought that by using socket.io-client, I'd avoid the pooling behaviour, yet it doesn't work. Am I doing something wrong?
I've simplified the loop btw, I actually make sure there's a delay between creations to avoid sync heartbeats.
Ideas?
Found the answer, it goes like this :
function Dummy(){
this.config = require('../config/credentials.js');
this.socket = require('socket.io-client').connect(this.config.socketIO.url, { "force new connection": true });
var self = this;
this.socket.on('task', function(task) {
self.createTask(task);
});
}
By using the connect() function, we can set the force new connection flag to true and avoid the pooling. Simple!

chrome extension connection issues

I have written an extension for google chrome and I have a bug I need a help solving.
what I do is using either a text selection or an input of text search for photos on flickr and then create a results tab.
The extension works most of the times. but sometimes it creates a blank tab with no results and when I repeat the same search it then shows results. I figured that it's something to do with the html files messaging maybe something to do with them communicating. I have to say that I always receive the results from flickr so that the request/responce with flickr works ok. Sometimes the error happens when I play with other tabs or do something on other tabs while waiting for results. can you please help me figure out where's the fault?
the background file:
function searchSelection(info,tab){
var updated;
if(info.selectionText==null){
var value = prompt("Search Flickr", "Type in the value to search");
updated=makeNewString(value);
}
else{
updated=makeNewString(info.selectionText);
}
var resultHtml;
var xhReq = new XMLHttpRequest();
xhReq.open(
"GET",
"http://api.flickr.com/services/rest/?method=flickr.photos.search&text="+updated+
"&api_key=a0a60c4e0ed00af8d70800b0987cae70&content_type=7&sort=relevance&per_page=500",
true);
xhReq.onreadystatechange = function () {
if (xhReq.readyState == 4) {
if (xhReq.status == 200) {
chrome.tabs.executeScript(tab.id, {code:"document.body.style.cursor='auto';"});
var photos = xhReq.responseXML.getElementsByTagName("photo");
if(photos.length==0){
alert("No results found for this selection");
chrome.tabs.executeScript(tab.id, {code:"document.body.style.cursor='auto';"});
return;
}
var myJSPhotos=[];
for(var i=0; i<photos.length; i++){
var data={"id":photos[i].getAttribute("id"),"owner":photos[i].getAttribute("owner"),
"secret":photos[i].getAttribute("secret"),"server":photos[i].getAttribute("server"),
"farm":photos[i].getAttribute("farm"),"title":photos[i].getAttribute("title")};
myJSPhotos[i]=data;
}
chrome.tabs.create({"url":"results.html"},function(thistab){
var port= chrome.tabs.connect(thistab.id);
port.postMessage({photos:myJSPhotos});
});
}
};
};
xhReq.send(null);
chrome.tabs.executeScript(tab.id, {code:"document.body.style.cursor='wait';"});
}
var context="selection";
var id = chrome.contextMenus.create({"title": "search Flickr", "contexts":[context,'page'],"onclick":searchSelection});
results html: has only a reference to the js file res.js
res.js :
chrome.extension.onConnect.addListener(function(port) {
port.onMessage.addListener(function(msg) {
//*****//
var photos=msg.photos;
createPage(photos);
});
});
I have to mention that when the tab is empty if I put alert on the //*****// part it won't
fire.
but when I print out the photos.length at the tab create call back function part it prints out the correct result.
Try to set "run_at":"document_start" option for your res.js in the manifest.
I think callback from chrome.tabs.create is fired right away without waiting for page scripts to be loaded, so you might try something like this instead:
//global vars
var createdTabId = null;
var myJSPhotos = null;
xhReq.onreadystatechange = function () {
//assign myJSPhotos to a global var
chrome.tabs.create({"url":"results.html"},function(thistab){
createdTabId = thistab.id;
});
}
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if(changeInfo.status == "complete" && tab.id == createdTabId) {
createdTabId = null;
//now page is loaded and content scripts injected
var port = chrome.tabs.connect(tab.id);
port.postMessage({photos:myJSPhotos});
}
});

Resources