Selenium IDE managing multiple windows

Selenium IDE is, according to many people, little bit limited. In fact, it’s not, but for some basics stuff, the test case can grow up a lot.

I wanted to manage a simple multi windows system, to be able to see by myself the results of some socket.io commands.

Let’s imagine this :

  • You got a chat system (like MSN or facebook chat for example)
  • You want to be sure two people connected recieve messages, but a third part does not
  • In this case you will need 3 windows : two sender/reciever, and another window which should see nothing

In this case you have many options : most of them need to make a program for that (a complete unit test using your favorite IDE), because you will have to handle many thread, to be able to have listener and sender working in parallel. Why not doing that using Selenium IDE ? After all selenium IDE got a openWindow, a selectWindow function, which launch kind of popup. It’s not threaded, but it is enough we need only one sender…

PS : be sure your popup blocker is turned off before doing this, or you may see nothing !

Basic windows management using Selenium IDE

Using windows in Selenium IDE is not so complicated, you got :

  • openWindow(url, windowID) : open a windows with the given url (can be empty), and the given windowID (needed to retrieve the manage the window inside test)
  • waitForPopup(windowID) : wait until a popup or a window is loaded (not the page, the window => wait the window is started by firefox)
  • selectWindow(windowID) : select a window, now the window will recieve selenium commands
  • close() : close the current window, so we must do a select window before to be sure to close the good one

With thoose commands, you can easily manage a window. But, all of thooses are needed, for each windows. This may become long and ugly if you need like 5 or 6 windows to manage at same time…

The solution here is of course extending the selenium IDE with custom commands to be able to start many windows or close many windows in a single command. Another problem : there is no windows placement on screen, they will appear everywhere (and often mask previous ones), so it may be difficult to see if everything went fine on each window.

User Extension in Selenium

Here is a basic user extension for selenium, which add three commands :

  • openMultipleWindow(number, options) : The number is the number of window to open. Options can be like this : « prefix=myWin, url=http://www.google.com », the prefix is a windowID prefix, in this example you will use like this : selectWindow(myWin1). The url is the url to load for each window. Both are not needed, you can put only one, both, or none…
  • closeMultipleWindow(number, prefix) : The number is again the number of openned window to close, and prefix is the prefix setted on openMultipleWindow, so if there is no prefix, let this blank.
  • A multiple command system which can send a command to many windows, this is usefull for repetitive actions on many windows.
/* ************************************************************************

	Licence : LGPLv3

	Version: 0.1

	Authors: deisss

	Date: 2012-08-30

	Date of last modification: 2012-08-30

	Description: Selenium user extension to add two multiple window management
************************************************************************ */
Selenium.prototype.doOpenMultipleWindow = function(maximum, options){
	/**
	* open many windows in a row
	*
	* @param maximum The number of window to open (up to 16 window)
	* @param options Add some extra options, we may have :
			{prefix : string} The prefix is used for catching the window id : like a prefix "win" with a number 2 will create win1 and win2 accessible as window ID
			{url : string} The url to load for every window
	*/

	//Getting the screen width and screen height
	var maxWidth = this.browserbot.getUserWindow().screen.availWidth;
	var maxHeight = this.browserbot.getUserWindow().screen.availHeight;

	maximum = parseInt(maximum, 10);

	var param = {
		url : "",
		prefix : ""
	};

	if(typeof(options) !== "undefined"){
		var customOptions = options.split(",");

		for(var i=0; i<customOptions.length; i++){
			var onePair = customOptions[i];
			var nameAndValue = onePair.split("=");

			// rz: using String.trim from htmlutils.js of selenium to get rid of whitespace
			var name = new String(nameAndValue[0]).trim();
			var value = new String(nameAndValue[1]).trim();
			param[name] = value;
		}
	}

	//The creation and placement is directly linked to number
	for(var i=0; i<maximum; ++i){
		var windowID = param.prefix+(i+1);
		// this.openNewWindow(param.url, windowID);
		selenium.doOpenWindow(param.url, windowID.toString());
		selenium.doWaitForPopUp(windowID.toString(), "30000");
		selenium.doWaitForPageToLoad("30000");
	}


	var dividerWidth = 1, dividerHeight = 1;
	//Now they are all created, we can place them
	switch(maximum){
		case 1:																		break;
		case 2:								dividerWidth = 2;						break;
		case 3:case 4:						dividerWidth = 2;	dividerHeight = 2;	break;
		case 5:case 6:						dividerWidth = 3;	dividerHeight = 2;	break;
		case 7:case 8:						dividerWidth = 4;	dividerHeight = 2;	break;
		case 9:case 10:case 11:case 12:		dividerWidth = 4;	dividerHeight = 3;	break;
		case 13:case 14:case 15:case 16:	dividerWidth = 4;	dividerHeight = 4;	break;
		default:
			return;
	}

	var width = maxWidth/dividerWidth,
		height = maxHeight/dividerHeight;
	for(var i=0, j=0; i<maximum; ++i){
		var windowID = param.prefix+(i+1);
		selenium.doSelectWindow(windowID.toString());
		selenium.browserbot.getUserWindow().resizeTo(width, height);

		var newWidth = (i - (j * dividerWidth)) * width, newHeight = j * height;

		//Small bug, there is a small shift, correcting it...
		if(newWidth < 0){
			newWidth = -2 * newWidth;
			newHeight = ((j - 1) * height);
		}

		LOG.info('placing ' + windowID + ' at ' + Math.round(newWidth) + ' : ' + Math.round(newHeight));

		selenium.browserbot.getUserWindow().moveTo(newWidth, newHeight);

		if( (i - 1) % dividerWidth === 0){
			++j;
		}
	}
}

Selenium.prototype.doCloseMultipleWindow = function(maximum, prefix){
	/**
	* Close many windows in a row, according to shared prefix
	*
	* @param maximum The number of window to close
	* @param prefix The prefix for windows name
	*/
	for(var i=0; i<maximum; ++i){
		var windowID = prefix + (i + 1);
		this.doSelectWindow(windowID);
		this.doClose(windowID);
	}
}


Selenium.prototype.doCommandMultipleWindow = function(include, executeCommand){
	/**
	* Execute a command on many window
	*
	* @param include The include use the window specified in parameter (with prefix included !), and launch the command on every selected include
	*	ex include : win1, win2, win4
	* @param executeCommand The command to execute
	*/

	var param = include.split(new RegExp('[, ]+', 'g'));

	//Trimming command
	executeCommand = executeCommand.replace(/^\s*([\S\s]*)\b\s*$/, '$1');

	//Splitting the command
	var brace = executeCommand.indexOf('(');
	var commands = new Array(
		executeCommand.substring(0, brace),
		executeCommand.substring(brace+1)
	);

	//Removing last bracket
	commands[1] = commands[1].substring(0, commands[1].length - 1);

	var parameters = {target:"",value:""};
	var tmp = this.browserbot.getCurrentWindow().JSON.parse(commands[1]);

	if(typeof(tmp.target) !== "undefined"){
		parameters.target = tmp.target;
	}

	if(typeof(tmp.value) !== "undefined"){
		parameters.value = tmp.value;
	}

	var command = {
		command : commands[0],
		target : parameters.target,
		value : parameters.value
	}

	if(param === []){
		throw new SeleniumError("You must provide include parameter: '" + command.command + "'");
	}

	LOG.info("Executing: | " + command.command + " | " + command.target + " | " + command.value + " |");

	//Converting to real function name
	if(command.command.substring(0, 6) == "assert"){
		command.command = "is" + command.command.charAt(6).toUpperCase() + command.command.substring(7);
	}else{
		command.command = "do" + command.command.charAt(0).toUpperCase() + command.command.slice(1);
	}

	//Now trying to load function
	for(var i=0; i<param.length; ++i){
		var windowID = param[i];
		this.doSelectWindow(windowID);
		this[command.command](command.target, command.value);
	}
}

According to number of windows you open, the placement will be placed like small square, the system can create up to 16 window, after that number, the placement will not be done anymore…

Loading extension into selenium IDE

To use this extension, you need to load it inside Selenium IDE :
First load Selenium IDE :
Selenium Start

Now selenium should be ready :
Selenium Ready

In the selenium IDE base page, go to « Options » (on top) => « Options… », you should see the option panel, feel the user-extension.js field with the simulator file :
Selenium extension

Now the file is ready, here is a basic selenium test using it (the first window created by system will be win1, it always start at 1) :

Command Target Value
openMultipleWindow 6 prefix=win
selectWindow win1
open http://www.google.com
selectWindow win2
open https://twitter.com/
commandMultipleWindow win4, win5 open({« target »: »/ », »value »: » »})
closeMultipleWindow 6 win

You should see 6 windows appearing, the first one loading google, the second one loading twitter, the 4 and 5 will both load the root url : you can see here the commandMultipleWindow in action which is a launching a command on many window. The commandMultipleWindow is usefull, but due to parameters limitations, I have to create a kind of small trick : the window to use command are using the target field, separated with coma, while the command is listed in a single line on value field. Using a mix between « commandName(JSON Object with ‘target’ field or ‘value’ or both) ».
After that they should all close…

You can find here a basic example which load 16 windows.

UPDATE 2012-10-02 : the commandMultipleWindow got a bug when using a css selector with « ( » or « ) » this was creating array element. This has been changed by a simple indexOf type to do the same and not interfer with the css selector system.

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :