From 174f111172029f976183d356b31e5e36b53c4411 Mon Sep 17 00:00:00 2001 From: Paul Bors Date: Wed, 15 Apr 2015 11:45:20 -0400 Subject: [PATCH 1/4] Proxy the original Selenium reset() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of overwriting (not overloading) Selenium’s reset() function, it’s better to use the proxy design pattern so that if the original function changes we won’t break Selenium IDE’s init code by still using an outdated cut-n-paste ver of the imll --- sideflow.js | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/sideflow.js b/sideflow.js index c72e321..e02ea15 100644 --- a/sideflow.js +++ b/sideflow.js @@ -1,16 +1,14 @@ var gotoLabels= {}; var whileLabels = {}; -// overload the original Selenium reset function -Selenium.prototype.reset = function() { - // reset the labels - this.initialiseLabels(); - // proceed with original reset code - this.defaultTimeout = Selenium.DEFAULT_TIMEOUT; - this.browserbot.selectWindow("null"); - this.browserbot.resetPopups(); -} - +(function() { + // Proxy design pattern - Overwrite Selenium's reset() function and then invoke the original impl + var proxied = Selenium.prototype.reset; + Selenium.prototype.reset = function() { + this.initialiseLabels(); + return proxied.apply( this, arguments ); + }; +})(); /* * --- Initialize Conditional Elements --- * @@ -66,8 +64,7 @@ Selenium.prototype.initialiseLabels = function() break; } } - -} +}; Selenium.prototype.continueFromRow = function( row_num ) { @@ -75,10 +72,10 @@ Selenium.prototype.continueFromRow = function( row_num ) throw new Error( "Invalid row_num specified." ); } testCase.debugContext.debugIndex = row_num; -} +}; // do nothing. simple label -Selenium.prototype.doLabel = function(){}; +Selenium.prototype.doLabel = function() {}; Selenium.prototype.doGotoLabel = function( label ) { @@ -93,7 +90,7 @@ Selenium.prototype.doGoto = Selenium.prototype.doGotoLabel; Selenium.prototype.doGotoIf = function( condition, label ) { if( eval(condition) ) this.doGotoLabel( label ); -} +}; Selenium.prototype.doWhile = function( condition ) { @@ -103,7 +100,7 @@ Selenium.prototype.doWhile = function( condition ) if( undefined == end_while_row ) throw new Error( "Corresponding 'endWhile' is not found." ); this.continueFromRow( end_while_row ); } -} +}; Selenium.prototype.doEndWhile = function() { @@ -111,7 +108,7 @@ Selenium.prototype.doEndWhile = function() var while_row = whileLabels.ends[ last_row ] - 1; if( undefined == while_row ) throw new Error( "Corresponding 'While' is not found." ); this.continueFromRow( while_row ); -} +}; Selenium.prototype.doPush= function(value, varName) { @@ -123,4 +120,4 @@ Selenium.prototype.doPush= function(value, varName) } else { storedVars[varName].push(value); } -} +}; From f895df49078d630fd28064026df16e597ae0215b Mon Sep 17 00:00:00 2001 From: Paul Bors Date: Thu, 16 Apr 2015 18:33:40 -0400 Subject: [PATCH 2/4] Issue #12: sideflow.js is not working with "mvn selenium:selenese" * Refactored the script and adopted Selenium's OOP way of doing things * Ported the script to support the Maven Selenium Plugin's selenese target --- README.md | 26 ++++-- sideflow.js | 261 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 185 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index db92db9..5d624e2 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,30 @@ sideflow - a flow control extension for Selenium IDE === +Lets you do goto and while loops in the Selenium IDE Firefox add-on as well as via the _selenese_ goal in the +Maven Selenium Plugin. This is a Selenium IDE compatible port of the flow control extension from +[http://wiki.openqa.org/display/SEL/flowControl](http://wiki.openqa.org/display/SEL/flowControl) which is the older +outdated version of [https://addons.mozilla.org/en-us/firefox/addon/flow-control/](https://addons.mozilla.org/en-us/firefox/addon/flow-control/). -Lets you do goto and while loops in the Selenium IDE Firefox add-on. This is a Selenium -IDE compatible port of the flow control extension from http://wiki.openqa.org/display/SEL/flowControl. - - +![picture](http://4.bp.blogspot.com/_Vi1folaOZAs/R7N1_L5KjDI/AAAAAAAAAGQ/PyozuVCHBC4/s400/flow_control_ide.png") Requirements --- Selenium IDE v1.0.5 + +Selenium Maven Plugin v2.3 + Install --- -- Get Selenium IDE for Firefox from http://seleniumhq.org -- Launch Selenium IDE from Firefox and open the options menu -- Add the sideflow.js file to the "Selenium Core extensions (user-extensions.js)" field +__Firefox Selenium IDE__ + + * Get Selenium IDE for Firefox from [http://www.SeleniumHQ.org/projects/ide/](http://www.SeleniumHQ.org/projects/ide/) + * Launch Selenium IDE from Firefox and open the _Options_ menu + * Add the sideflow.js file to the "Selenium Core extensions (user-extensions.js)" field + +__Selenium Maven Plugin ( _selenese_ )__ + + * Install and configure your Selenium Maven Plugin in your POM + * Configure the plugin _userExtensions_ property + * Configure your execution to use the _selenese_ goal Commands --- @@ -28,7 +38,6 @@ Commands Resources --- - - [Full Documentation](http://51elliot.blogspot.com/2008/02/selenium-ide-goto.html) - Selenium HQ [SeleniumHQ](http://seleniumhq.org/) @@ -36,6 +45,7 @@ Authors --- - Andrey Yegorov - original flow control extension - Darren DeRidder - Selenium IDE port +- Paul Bors - Selenium Maven Plugin port License --- diff --git a/sideflow.js b/sideflow.js index e02ea15..09bf65d 100644 --- a/sideflow.js +++ b/sideflow.js @@ -1,123 +1,196 @@ -var gotoLabels= {}; -var whileLabels = {}; +var SideFlowUtils = classCreate(); +objectExtend(SideFlowUtils.prototype, { + banner: "SideFlow", + initialize: function() {}, + isIDE: function() { + return (Application.prototype != undefined); + }, + formatMessage: function(msg, error) { + return this.banner + ((error) ? "[" + error.lineNumber + "] " : " ") + msg; + }, + debug: function(msg, error) { + LOG.debug(this.formatMessage(msg, error)); + }, + info: function(msg, error) { + LOG.info(this.formatMessage(msg, error)); + }, + warn: function(msg, error) { + LOG.warn(this.formatMessage(msg, error)); + }, + error: function(msg, error) { + LOG.error(this.formatMessage(msg, error)); + }, + throwError: function(err, error) { + throw new SeleniumError(this.formatMessage(err, error)); + }, + getTestCase: function() { + if(this.isIDE()) { + return testCase; + } + return new testFrame.getCurrentTestCase(); + }, + getCommands: function() { + var seleniumCommands; + if(this.isIDE()) { + seleniumCommands = this.getTestCase().commands; + } else { + seleniumCommands = this.getTestCase().commandRows; + } + return seleniumCommands; + } +}); -(function() { - // Proxy design pattern - Overwrite Selenium's reset() function and then invoke the original impl - var proxied = Selenium.prototype.reset; - Selenium.prototype.reset = function() { - this.initialiseLabels(); - return proxied.apply( this, arguments ); - }; -})(); +var SideFlow = classCreate(); +objectExtend(SideFlow.prototype, { + gotoLabels: {}, + whileLabels: { ends: {}, whiles: {} }, + utils: null, -/* - * --- Initialize Conditional Elements --- * - * Run through the script collecting line numbers of all conditional elements - * There are three a results arrays: goto labels, while pairs and forEach pairs - * - */ -Selenium.prototype.initialiseLabels = function() -{ - gotoLabels = {}; - whileLabels = { ends: {}, whiles: {} }; - var command_rows = []; - var numCommands = testCase.commands.length; - for (var i = 0; i < numCommands; ++i) { - var x = testCase.commands[i]; - command_rows.push(x); - } - var cycles = []; - var forEachCmds = []; - for( var i = 0; i < command_rows.length; i++ ) { - if (command_rows[i].type == 'command') - switch( command_rows[i].command.toLowerCase() ) { - case "label": - gotoLabels[ command_rows[i].target ] = i; - break; - case "while": - case "endwhile": - cycles.push( [command_rows[i].command.toLowerCase(), i] ) - break; - case "foreach": - case "endforeach": - forEachCmds.push( [command_rows[i].command.toLowerCase(), i] ) - break; + /* + * --- Initialize Conditional Elements --- * + * Run through the script collecting line numbers of all conditional elements + * There are three a results arrays: goto labels, while pairs and forEach pairs + */ + initialize: function(sideFlowUtils) { + this.utils = sideFlowUtils; + this.utils.debug("detected runtime = Selenium " + (this.utils.isIDE() ? "IDE" : "RC"), new Error()); + try { + var commands = this.utils.getCommands(), + command_rows = [], + cycles = [], + forEachCmds = []; + for (var i = 0; i < commands.length; ++i) { + command_rows.push(commands[i]); + } + for (var i = 0; i < command_rows.length; i++) { + if (this.utils.isIDE() && (command_rows[i].type == 'command')) { + switch (command_rows[i].command.toLowerCase()) { + case "label": + this.gotoLabels[ command_rows[i].target ] = i; + break; + case "while": + case "endwhile": + cycles.push([command_rows[i].command.toLowerCase(), i]); + break; + case "foreach": + case "endforeach": + forEachCmds.push([command_rows[i].command.toLowerCase(), i]); + break; + } + } else { + var command = command_rows[i].trElement.cells[0].innerHTML; + var target = command_rows[i].trElement.cells[1].innerHTML; + var value = command_rows[i].trElement.cells[2].innerHTML; + switch (command.toLowerCase()) { + case "label": + this.gotoLabels[ target ] = i; + break; + case "while": + case "endwhile": + cycles.push([command.toLowerCase(), i]); + break; + case "foreach": + case "endforeach": + forEachCmds.push([command.toLowerCase(), i]); + break; + } + } + } + } catch (err) { + this.utils.error(err, new Error()); + this.utils.throwError(err, new Error()); } - } - var i = 0; - while( cycles.length ) { - if( i >= cycles.length ) { - throw new Error( "non-matching while/endWhile found" ); + var i = 0; + while (cycles.length) { + if (i >= cycles.length) { + this.utils.throwError("non-matching while/endWhile found", new Error()); + } + switch (cycles[i][0]) { + case "while": + if (( i + 1 < cycles.length ) && ( "endwhile" == cycles[i + 1][0] )) { + // pair found + this.whileLabels.ends[ cycles[i + 1][1] ] = cycles[i][1]; + this.whileLabels.whiles[ cycles[i][1] ] = cycles[i + 1][1]; + cycles.splice(i, 2); + i = 0; + } else ++i; + break; + case "endwhile": + ++i; + break; + } } - switch( cycles[i][0] ) { - case "while": - if( ( i+1 < cycles.length ) && ( "endwhile" == cycles[i+1][0] ) ) { - // pair found - whileLabels.ends[ cycles[i+1][1] ] = cycles[i][1]; - whileLabels.whiles[ cycles[i][1] ] = cycles[i+1][1]; - cycles.splice( i, 2 ); - i = 0; - } else ++i; - break; - case "endwhile": - ++i; - break; + }, + continueFromRow: function (row_num) { + if (row_num == undefined || row_num == null || row_num < 0) { + this.utils.throwError("Invalid row_num specified.", new Error()); + } + if (this.utils.isIDE()) { + this.utils.getTestCase().debugContext.debugIndex = row_num; + } else { + this.utils.getTestCase().nextCommandRowIndex = row_num; } } -}; +}); -Selenium.prototype.continueFromRow = function( row_num ) -{ - if(row_num == undefined || row_num == null || row_num < 0) { - throw new Error( "Invalid row_num specified." ); - } - testCase.debugContext.debugIndex = row_num; -}; +var sideFlow = null; +(function() { + // Proxy design pattern - Overwrite Selenium's reset() function and then invoke the original impl + var proxied = Selenium.prototype.reset; + Selenium.prototype.reset = function() { + sideFlow = new SideFlow(new SideFlowUtils()); + proxied.apply( this, arguments ); + }; +})(); // do nothing. simple label -Selenium.prototype.doLabel = function() {}; - -Selenium.prototype.doGotoLabel = function( label ) -{ - if( undefined == gotoLabels[label] ) { - throw new Error( "Specified label '" + label + "' is not found." ); +Selenium.prototype.doLabel = function(){}; +Selenium.prototype.doGotoLabel = function( label ) { + if( undefined == sideFlow.gotoLabels[label] ) { + sideFlow.utils.throwError( "Specified label '" + label + "' is not found.", new Error() ); } - this.continueFromRow( gotoLabels[ label ] ); + sideFlow.continueFromRow( sideFlow.gotoLabels[ label ] ); }; Selenium.prototype.doGoto = Selenium.prototype.doGotoLabel; -Selenium.prototype.doGotoIf = function( condition, label ) -{ +Selenium.prototype.doGotoIf = function( condition, label ) { if( eval(condition) ) this.doGotoLabel( label ); }; -Selenium.prototype.doWhile = function( condition ) -{ +Selenium.prototype.doWhile = function( condition ) { if( !eval(condition) ) { - var last_row = testCase.debugContext.debugIndex; - var end_while_row = whileLabels.whiles[ last_row ]; - if( undefined == end_while_row ) throw new Error( "Corresponding 'endWhile' is not found." ); - this.continueFromRow( end_while_row ); + var last_row; + if(sideFlow.utils.isIDE()) { + last_row = sideFlow.utils.getTestCase().debugContext.debugIndex; + } else { + last_row = sideFlow.utils.getTestCase().nextCommandRowIndex; + } + var end_while_row = sideFlow.whileLabels.whiles[ last_row ]; + if( undefined == end_while_row ) sideFlow.utils.throwError( "Corresponding 'endWhile' is not found.", new Error() ); + sideFlow.continueFromRow( end_while_row ); } }; -Selenium.prototype.doEndWhile = function() -{ - var last_row = testCase.debugContext.debugIndex; - var while_row = whileLabels.ends[ last_row ] - 1; - if( undefined == while_row ) throw new Error( "Corresponding 'While' is not found." ); - this.continueFromRow( while_row ); +Selenium.prototype.doEndWhile = function() { + var last_row; + if(sideFlow.utils.isIDE()) { + last_row = sideFlow.utils.getTestCase().debugContext.debugIndex; + } else { + last_row = sideFlow.utils.getTestCase().nextCommandRowIndex; + } + var while_row = sideFlow.whileLabels.ends[ last_row ] - 1; + if( undefined == while_row ) sideFlow.utils.throwError( "Corresponding 'While' is not found.", new Error() ); + sideFlow.continueFromRow( while_row ); }; -Selenium.prototype.doPush= function(value, varName) -{ +Selenium.prototype.doPush = function(value, varName) { if(!storedVars[varName]) { storedVars[varName] = new Array(); - } + } if(typeof storedVars[varName] !== 'object') { - throw new Error("Cannot push value onto non-array " + varName); + sideFlow.utils.throwError("Cannot push value onto non-array " + varName, new Error()); } else { storedVars[varName].push(value); } -}; +}; \ No newline at end of file From d3458f7e4aaca45e45dc0a3c7e385d48cde7931f Mon Sep 17 00:00:00 2001 From: Paul Bors Date: Thu, 16 Apr 2015 19:18:45 -0400 Subject: [PATCH 3/4] Issue #12: sideflow.js is not working with "mvn selenium:selenese" * Fixed SideFlow.initialize() for command loop in the IDE not to fall through the RC for comments --- sideflow.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/sideflow.js b/sideflow.js index 09bf65d..9fce524 100644 --- a/sideflow.js +++ b/sideflow.js @@ -63,19 +63,21 @@ objectExtend(SideFlow.prototype, { command_rows.push(commands[i]); } for (var i = 0; i < command_rows.length; i++) { - if (this.utils.isIDE() && (command_rows[i].type == 'command')) { - switch (command_rows[i].command.toLowerCase()) { - case "label": - this.gotoLabels[ command_rows[i].target ] = i; - break; - case "while": - case "endwhile": - cycles.push([command_rows[i].command.toLowerCase(), i]); - break; - case "foreach": - case "endforeach": - forEachCmds.push([command_rows[i].command.toLowerCase(), i]); - break; + if (this.utils.isIDE()) { + if((command_rows[i].type == 'command')) { + switch (command_rows[i].command.toLowerCase()) { + case "label": + this.gotoLabels[ command_rows[i].target ] = i; + break; + case "while": + case "endwhile": + cycles.push([command_rows[i].command.toLowerCase(), i]); + break; + case "foreach": + case "endforeach": + forEachCmds.push([command_rows[i].command.toLowerCase(), i]); + break; + } } } else { var command = command_rows[i].trElement.cells[0].innerHTML; From 107c0716e8e6522524368b1a6c6cf843361631d0 Mon Sep 17 00:00:00 2001 From: Paul Bors Date: Thu, 16 Apr 2015 22:20:45 -0400 Subject: [PATCH 4/4] Issue #12: sideflow.js is not working with "mvn selenium:selenese" * Fixed SideFlow.initialize() and how the testFrame changes on page re-load for Selenium RC in Selenium Maven Plugin * Fixed the while loop off by a row bug * Refactored some of the undefined checks to also check for isNaN() --- sideflow.js | 90 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 38 deletions(-) diff --git a/sideflow.js b/sideflow.js index 9fce524..0979041 100644 --- a/sideflow.js +++ b/sideflow.js @@ -1,7 +1,23 @@ var SideFlowUtils = classCreate(); objectExtend(SideFlowUtils.prototype, { + testCase: null, banner: "SideFlow", - initialize: function() {}, + initialize: function() { }, + setTestCase: function(testCase) { + this.testCase = testCase; + }, + getTestCase: function() { + return this.testCase; + }, + getCommands: function() { + var seleniumCommands; + if(this.isIDE()) { + seleniumCommands = this.getTestCase().commands; + } else { + seleniumCommands = this.getTestCase().commandRows; + } + return seleniumCommands; + }, isIDE: function() { return (Application.prototype != undefined); }, @@ -22,21 +38,6 @@ objectExtend(SideFlowUtils.prototype, { }, throwError: function(err, error) { throw new SeleniumError(this.formatMessage(err, error)); - }, - getTestCase: function() { - if(this.isIDE()) { - return testCase; - } - return new testFrame.getCurrentTestCase(); - }, - getCommands: function() { - var seleniumCommands; - if(this.isIDE()) { - seleniumCommands = this.getTestCase().commands; - } else { - seleniumCommands = this.getTestCase().commandRows; - } - return seleniumCommands; } }); @@ -45,7 +46,6 @@ objectExtend(SideFlow.prototype, { gotoLabels: {}, whileLabels: { ends: {}, whiles: {} }, utils: null, - /* * --- Initialize Conditional Elements --- * * Run through the script collecting line numbers of all conditional elements @@ -54,15 +54,20 @@ objectExtend(SideFlow.prototype, { initialize: function(sideFlowUtils) { this.utils = sideFlowUtils; this.utils.debug("detected runtime = Selenium " + (this.utils.isIDE() ? "IDE" : "RC"), new Error()); + if(this.utils.isIDE()) { + this.utils.setTestCase(testCase); + } else { + this.utils.setTestCase(testFrame.getCurrentTestCase()); + } try { var commands = this.utils.getCommands(), command_rows = [], cycles = [], forEachCmds = []; - for (var i = 0; i < commands.length; ++i) { + for(var i = 0; i < commands.length; ++i) { command_rows.push(commands[i]); } - for (var i = 0; i < command_rows.length; i++) { + for(var i = 0; i < command_rows.length; i++) { if (this.utils.isIDE()) { if((command_rows[i].type == 'command')) { switch (command_rows[i].command.toLowerCase()) { @@ -104,18 +109,20 @@ objectExtend(SideFlow.prototype, { } var i = 0; while (cycles.length) { - if (i >= cycles.length) { + if(i >= cycles.length) { this.utils.throwError("non-matching while/endWhile found", new Error()); } - switch (cycles[i][0]) { + switch(cycles[i][0]) { case "while": - if (( i + 1 < cycles.length ) && ( "endwhile" == cycles[i + 1][0] )) { + if(( i + 1 < cycles.length ) && ( "endwhile" == cycles[i + 1][0] )) { // pair found this.whileLabels.ends[ cycles[i + 1][1] ] = cycles[i][1]; this.whileLabels.whiles[ cycles[i][1] ] = cycles[i + 1][1]; cycles.splice(i, 2); i = 0; - } else ++i; + } else { + ++i; + } break; case "endwhile": ++i; @@ -124,13 +131,13 @@ objectExtend(SideFlow.prototype, { } }, continueFromRow: function (row_num) { - if (row_num == undefined || row_num == null || row_num < 0) { + if((row_num == undefined) || (row_num == null) || (row_num < 0) || isNaN(row_num)) { this.utils.throwError("Invalid row_num specified.", new Error()); } - if (this.utils.isIDE()) { + if(this.utils.isIDE()) { this.utils.getTestCase().debugContext.debugIndex = row_num; } else { - this.utils.getTestCase().nextCommandRowIndex = row_num; + this.utils.getTestCase().nextCommandRowIndex = row_num + 1; } } }); @@ -146,30 +153,35 @@ var sideFlow = null; })(); // do nothing. simple label -Selenium.prototype.doLabel = function(){}; -Selenium.prototype.doGotoLabel = function( label ) { - if( undefined == sideFlow.gotoLabels[label] ) { +Selenium.prototype.doLabel = function() { }; + +Selenium.prototype.doGotoLabel = function(label) { + if(undefined == sideFlow.gotoLabels[label]) { sideFlow.utils.throwError( "Specified label '" + label + "' is not found.", new Error() ); } - sideFlow.continueFromRow( sideFlow.gotoLabels[ label ] ); + sideFlow.continueFromRow( sideFlow.gotoLabels[label] ); }; Selenium.prototype.doGoto = Selenium.prototype.doGotoLabel; -Selenium.prototype.doGotoIf = function( condition, label ) { - if( eval(condition) ) this.doGotoLabel( label ); +Selenium.prototype.doGotoIf = function(condition, label) { + if(eval(condition)) { + this.doGotoLabel( label ); + } }; -Selenium.prototype.doWhile = function( condition ) { - if( !eval(condition) ) { +Selenium.prototype.doWhile = function(condition) { + if(!eval(condition)) { var last_row; if(sideFlow.utils.isIDE()) { last_row = sideFlow.utils.getTestCase().debugContext.debugIndex; } else { - last_row = sideFlow.utils.getTestCase().nextCommandRowIndex; + last_row = sideFlow.utils.getTestCase().nextCommandRowIndex - 1; } var end_while_row = sideFlow.whileLabels.whiles[ last_row ]; - if( undefined == end_while_row ) sideFlow.utils.throwError( "Corresponding 'endWhile' is not found.", new Error() ); + if((undefined == end_while_row) || isNaN(end_while_row)) { + sideFlow.utils.throwError( "Corresponding 'endWhile' is not found.", new Error() ); + } sideFlow.continueFromRow( end_while_row ); } }; @@ -179,10 +191,12 @@ Selenium.prototype.doEndWhile = function() { if(sideFlow.utils.isIDE()) { last_row = sideFlow.utils.getTestCase().debugContext.debugIndex; } else { - last_row = sideFlow.utils.getTestCase().nextCommandRowIndex; + last_row = sideFlow.utils.getTestCase().nextCommandRowIndex - 1; } var while_row = sideFlow.whileLabels.ends[ last_row ] - 1; - if( undefined == while_row ) sideFlow.utils.throwError( "Corresponding 'While' is not found.", new Error() ); + if((undefined == while_row) || isNaN(while_row)) { + sideFlow.utils.throwError( "Corresponding 'While' is not found.", new Error() ); + } sideFlow.continueFromRow( while_row ); };