如何使用Selenium IDE从数据池中读取变量?

如何使用Selenium IDE从数据池中读取变量?,selenium,selenium-ide,Selenium,Selenium Ide,我正在使用Selenium IDE测试基于web的HR/SW系统 有一个屏幕,用于输入员工的假期 我有将近3000名员工 我构建了一个测试用例,使用变量为一名员工输入假期 如何在不创建测试用例3000次的情况下,为所有3000名员工重复测试用例。要做到这一点需要付出不可能的努力。注意:每个员工都有不同的假期数据(类型、开始日期、结束日期) 是否有任何方法可以使用变量可以用来读取其数据的文件(Excel…) 我的案子有什么解决办法吗 如果有人能帮助我,我将非常感激 谢谢。我认为您需要使用循环我看不

我正在使用Selenium IDE测试基于web的HR/SW系统

有一个屏幕,用于输入员工的假期

我有将近3000名员工

我构建了一个测试用例,使用变量为一名员工输入假期

如何在不创建测试用例3000次的情况下,为所有3000名员工重复测试用例。要做到这一点需要付出不可能的努力。注意:每个员工都有不同的假期数据(类型、开始日期、结束日期)

是否有任何方法可以使用变量可以用来读取其数据的文件(Excel…)

我的案子有什么解决办法吗

如果有人能帮助我,我将非常感激


谢谢。

我认为您需要使用
循环
我看不到您的代码,请检查您是否有任何问题,如果有,请评论我的答案。我不能对你的问题发表评论,因为声誉还不够。如果可能,请在问题中添加代码

您可以使用XML文件作为输入

1)首先,您必须添加以下用户扩展:

<testdata>
    <test employee="1" type="1" startDate="01.01.2013" endDate="01.02.2013" />
    <test employee="2" type="1" startDate="01.02.2013" endDate="01.03.2013" />
    <test employee="3" type="1" startDate="01.03.2013" endDate="01.04.2013" />
...
</testdata>
sideflow.js-(-此外,本文还解释了如何添加用户扩展文件)

datadriven.js

/**
 * Original Developer: Jerry Qian(qqqiansjtucs@hotmail.com)
 * Modified By: John Witchel (jwitchel@colevalleygroup.com)
 * include extension for Selenium-IDE edition
 * refer to includeCommand_2.1.3 for Selenium-Core edition
 * @version 1.3
 *
 */
function IDEIncludeCommand() {}

IDEIncludeCommand.LOG_PREFIX = "IDEIncludeCommand: ";
IDEIncludeCommand.BEGIN_TEMPLATE = "begin$Template$";
IDEIncludeCommand.END_TEMPLATE = "end$Template$";
IDEIncludeCommand.VERSION = "1.1";

IDEIncludeCommand.prototype.prepareTestCaseAsText = function(responseAsText, paramsArray) {
    /**
     * Prepare the HTML to be included in as text into the current testcase-HTML
     * Strip all but the testrows (tr)
     * Stripped will be:
     *  - whitespace (also new lines and tabs, so be careful wirt parameters relying on this),
     *  - comments (xml comments)                 
     * Replace variable according to include-parameters 
     * note: the include-variables are replaced literally. selenium does it at execution time
     * also note: all selenium-variables are available to the included commands, so mostly no include-parameters are necessary
     *
     * @param responseAsText table to be included as text (string)
     * @return testRows array of tr elements (as string!) containing the commands to be included
     * 
     * TODO:
     *  - selenium already can handle testcase-html. use selenium methods or functions instead
     *  - find better name for requester
     */
    // removing new lines, carret return and tabs from response in order to work with regexp
    var pageText = responseAsText.replace(/\r|\n|\t/g,"");
    // remove comments
    // begin comment, not a dash or if it's a dash it may not be followed by -> repeated, end comment
    pageText = pageText.replace(/<!--(?:[^-]|-(?!->))*-->/g,"");
    // find the content of the test table = <[spaces]table[char but not >]>....< /[spaces]table[chars but not >]>
    var testText = pageText.match(/<\s*table[^>]*>(.*)<\/\s*table[^>]*>/i)[1];

    // Replace <td></td> with <td>&nbsp;</td> for iE - credits Chris Astall
    // rz: somehow in my IE 7 this is not needed but is not bad as well
    testText = testText.replace(/<\s*td[^>]*>\s*<\s*\/td[^>]*>/ig,"<td></td>");// jq: no space

    // replace vars with their values in testText
    for ( var k = 0 ; k < paramsArray.length ; k++ ) {
        var pair = paramsArray[k];
        testText = testText.replace(pair[0],pair[1]);
    }

    // removes all  < /tr> 
    // in order to split on < tr>
    testText = testText.replace(/<\/\s*tr[^>]*>/ig,"");
    // split on <tr>
    var testRows = testText.split(/<\s*tr[^>]*>/i);
    return testRows;
};

IDEIncludeCommand.prototype.getIncludeDocumentBySynchronRequest = function(includeUrl) {
    /**
     * Prepare and do the XMLHttp Request synchronous as selenium should not continue execution meanwhile
     *
     * note: the XMLHttp requester is returned (instead of e.g. its text) to let the caller decide to use xml or text
     *
     * selenium-dependency: uses extended String from htmlutils
     *
     *  TODO use Ajax from prototype like this:
     *   var sjaxRequest = new Ajax.Request(url, {asynchronous:false});
     *   there is discussion about getting rid of prototype.js in developer forum.
     *   the ajax impl in xmlutils.js is not active by default in 0.8.2
     *
     * @param includeUrl URI to the include-document (document has to be from the same domain)
     * @return XMLHttp requester after receiving the response
     */
    var url = this.prepareUrl(includeUrl);
    // the xml http requester to fetch the page to include
    var requester = this.newXMLHttpRequest();
    if (!requester) {
        throw new Error("XMLHttp requester object not initialized");
    }
    requester.open("GET", url, false); // synchron mode ! (we don't want selenium to go ahead)
    try {
        requester.send(null);
    } catch(e) {
      throw new Error("Error while fetching url '" + url + "' details: " + e);
    }
    if ( requester.status != 200 && requester.status !== 0 ) {
        throw new Error("Error while fetching " + url + " server response has status = " + requester.status + ", " + requester.statusText );
    }
    return requester;
};

IDEIncludeCommand.prototype.prepareUrl = function(includeUrl) {
    /** Construct absolute URL to get include document
     * using selenium-core handling of urls (see absolutify in htmlutils.js)
     */
    var prepareUrl;
    // htmlSuite mode of SRC? TODO is there a better way to decide whether in SRC mode?
    if (window.location.href.indexOf("selenium-server") >= 0) {
        LOG.debug(IDEIncludeCommand.LOG_PREFIX + "we seem to run in SRC, do we?");
        preparedUrl = absolutify(includeUrl, htmlTestRunner.controlPanel.getTestSuiteName());
    } else {
        preparedUrl = absolutify(includeUrl, selenium.browserbot.baseUrl);
    }
    LOG.debug(IDEIncludeCommand.LOG_PREFIX + "using url to get include '" + preparedUrl + "'");
    return preparedUrl;
};

IDEIncludeCommand.prototype.newXMLHttpRequest = function() {
    // TODO should be replaced by impl. in prototype.js or xmlextras.js
    //     but: there is discussion of getting rid of prototype.js
    //     and: currently xmlextras.js is not activated in testrunner of 0.8.2 release
    var requester = 0;
    var exception = '';
    // see http://developer.apple.com/internet/webcontent/xmlhttpreq.html
    // changed order of native and activeX to get it working with native
    //  xmlhttp in IE 7. credits dhwang
    try {
        // for IE/ActiveX
        if(window.ActiveXObject) {
            try {
                requester = new ActiveXObject("Msxml2.XMLHTTP");
            }
            catch(e) {
                requester = new ActiveXObject("Microsoft.XMLHTTP");
            }
        }
        // Native XMLHttp
        else if(window.XMLHttpRequest) {
            requester = new XMLHttpRequest();
        }
    }
    catch(e) {
        throw new Error("Your browser has to support XMLHttpRequest in order to use include \n" + e);
    }
    return requester;
};

IDEIncludeCommand.prototype.splitParamStrIntoVariables = function(paramString) {
    /**
     * Split include Parameters-String into an 2-dim array containing Variable-Name and -Value
     *
     * selenium-dependency: uses extended String from htmlutils
     *
     * TODO: write jsunit tests - this could be easy (if there were not the new RegExp)
     *
     * @param includeParameters string the parameters from include call
     * @return new 2-dim Array containing regExpName (to find a matching variablename) and value to be substituted for
     */
    var newParamsArray = new Array();
    // paramString shall contains a list of var_name=value
    var paramListPattern = /([^=,]+=[^=,]*,)*([^=,]+=[^=,]*)/;
    if (! paramString || paramString === "") {
        return newParamsArray;
    } else if (paramString.match( paramListPattern )) {
        // parse parameters to fill newParamsArray
        var pairs = paramString.split(",");
        for ( var i = 0 ; i < pairs.length ; i++ ) {
            var pair = pairs[i];
            var nameValue = pair.split("=");
            //rz: use String.trim from htmlutils.js of selenium to get rid of whitespace in variable-name(s)
            var trimmedNameValue = new String(nameValue[0]).trim();
            // the pattern to substitute is ${var_name}
            var regExpName = new RegExp("\\$\\{" + trimmedNameValue + "\\}", "g");

            if (nameValue.length < 3) {
               newParamsArray.push(new Array(regExpName,nameValue[1]));
            } else {
                var varValue = new String(nameValue[1]);
                for (var j = 2; j < nameValue.length; j++) {
                    varValue=varValue.concat("="+nameValue[j]);
                }
                newParamsArray.push(new Array(regExpName,varValue));
            }
        }
    } else {
        throw new Error("Bad format for parameters list : '" + paramString + "'");
    }
    return newParamsArray;
};

IDEIncludeCommand.prototype.doInclude = function(locator, paramString) { 
    // Rewrite logic for Selenium IDE by Jerry Qian
    var currentSelHtmlTestcase = testCase; 

    var includeCmdRow = testCase.debugContext.currentCommand();

    if (!includeCmdRow) {
        throw new Error("IDEIncludeCommand: failed to find include-row in source testtable");
    }

    var paramsArray = this.splitParamStrIntoVariables(paramString);

    var inclDoc = this.getIncludeDocumentBySynchronRequest(locator);

    // Get an array of commands from the include text with all whitespace stripped
    var includedTestCaseHtml = this.prepareTestCaseAsText(inclDoc.responseText, paramsArray);


    this.injectIncludeTestCommands(locator,includeCmdRow,includedTestCaseHtml); 
};

IDEIncludeCommand.prototype.injectIncludeTestCommands = function(locator,includeCmdRow,  testRows) { 
    // Rewrite logic for Selenium IDE by Jerry Qian
    var newCommands = new Array(); 
    // skip first element as it is empty or <tbody>
    for (var i = 1 ; i < testRows.length; i++) {
          if(i == 1){// add BEGIN-END block
            var beginCommand = new Command(IDEIncludeCommand.BEGIN_TEMPLATE,locator,"");    
            newCommands.push(beginCommand);
          } 

          var newText = testRows[i];  
          if(newText.match(/<\s*td.*colspan=.*>(.*)<\/\s*td[^>]*>/i)){//delete comment step
             continue;
          }

          // removes all  < /td> 
          // in order to split on <td>
          newText = newText.replace(/<\/\s*td[^>]*>\s*<\/\s*tbody[^>]*>/ig,""); //remove </tbody>first
          newText = newText.replace(/<\/\s*td[^>]*>/ig,""); 
          var newCols = newText.split(/<\s*td[^>]*>/i);
          var new_cmd,new_target,new_value; 

          for (var j = 1 ; j < newCols.length; j++) {//skip 0
            if(j == 1) {
                new_cmd = newCols[j].replace(/\s/g,"");//trim \s
            }else if(j == 2) {
              new_target = newCols[j].replace(/\s+$/g,"");//trim end \s
            }else if(j == 3) {
              new_value = newCols[j].replace(/\s+$/g,"");//trim end \s
            } 
          } 

          var newCommand =  new Command(new_cmd,new_target,new_value); 
          newCommands.push(newCommand); //correct all steps
    } 
    var endCommand = new Command(IDEIncludeCommand.END_TEMPLATE,locator,"");    
    newCommands.push(endCommand);//add BEGIN-END block


    var cmsBefore = testCase.commands.slice(0,testCase.debugContext.debugIndex + 1);     
    var cmdsBehind = testCase.commands.slice(testCase.debugContext.debugIndex + 1, testCase.commands.length);     
    testCase.commands = cmsBefore.concat(newCommands).concat(cmdsBehind);//Injection

    // Don't inject if it appears the injection has already been done
    // (i.e., if the next command is the BEGIN).
    if (testCase.commands.length <= testCase.debugContext.debugIndex+1
        || beginCommand.toString() != testCase.commands[testCase.debugContext.debugIndex+1].toString())
    {
        // The include command cannot be the last command in the TestCase, or else
        // the de-injection code in doEnd$Template$ will cause an error. So we'll
        // add a simple echo if it is the last.
        if (testCase.commands.length == testCase.debugContext.debugIndex+1)
        {
            // Using concat instead of push so that we don't trigger the TestCase's set-modified flag.
            testCase.commands = testCase.commands.concat(new Command("echo", "The include command cannot be the last line in a TestCase, so this command was added. It can be left in place or removed, as desired.", "The include command cannot be the last line in a TestCase, so this command was added. It can be left in place or removed, as desired."));
        }

        // This is original code.
        var cmsBefore = testCase.commands.slice(0,testCase.debugContext.debugIndex + 1);
        var cmdsBehind = testCase.commands.slice(testCase.debugContext.debugIndex + 1, testCase.commands.length);
        testCase.commands = cmsBefore.concat(newCommands).concat(cmdsBehind);//Injection
    }
};

Selenium.prototype.doInclude = function(locator, paramString) {
    LOG.debug(IDEIncludeCommand.LOG_PREFIX + "Version " + IDEIncludeCommand.VERSION);
    var ideIncludeCommand = new IDEIncludeCommand();
    ideIncludeCommand.doInclude(locator, paramString);

    // If goto scripts exist then reindex the labels.  goto_sel_ide.js creates an array of labels when the
    // script is initialized but an included file's labels are not part of that initial read, so this function
    // re-initializes that array with the included files labels (if any).  If goto_sel.ide.js is not included 
    // it's ignored.
    try {
        this.initialiseLabels();
    } 
    catch (e) {
        LOG.debug("Goto Script not used.");
    }

};

// Array to hold the starting position of the Begin$Template$ marker.  Pushing and popping the position onto an array
// allows us to correctly handle nested includes during clean up.
var beginTemplateIndex = new Array();

// Mark the beginning of the include
Selenium.prototype.doBegin$Template$ = function(locator){
    LOG.info("Begin Template " + locator + " at position " + testCase.debugContext.debugIndex);
    // Add the current position to the tail of the beginTemplateIndex
    beginTemplateIndex.push(testCase.debugContext.debugIndex);
};

// Clean up everything between the closest Begin$Template$ and this $End$Template$, and pop the position off the array.
Selenium.prototype.doEnd$Template$ = function(locator){

    // Remove the last Begin$Template$ from the tail of the beginTemplateIndex
    var currentBeginTemplateIndex = beginTemplateIndex.pop();
    LOG.info("End Template " + locator + " at position " + currentBeginTemplateIndex);

    // Delete the commands that we injected in injectIncludeTestCommands.
    testCase.commands = 
        testCase.commands.slice(0,currentBeginTemplateIndex).concat(
            testCase.commands.slice(testCase.debugContext.debugIndex+1, testCase.commands.length));

    // Set the current command to the next one after the injected block.
    testCase.debugContext.debugIndex = currentBeginTemplateIndex-1;

    //Must refresh to syncup UI
    editor.view.refresh();
};
/************************************ DATADRIVEN EXTENSION START ********************************************/
/*
NAME:
    datadriven

    Licensed under Apache License v2
    http://www.apache.org/licenses/LICENSE-2.0

PURPOSE:
    Basic data driven testing.

    Full documentation at http://wiki.openqa.org/display/SEL/datadriven


EXAMPLE USE:
    The structure of your data driven test case will be;
    -------------------------------------------
    COMMAND       |TARGET          |VALUE
    -------------------------------------------
    loadTestData  |<file path>     |
    while         |!testdata.EOF() |
    testcommand1  |                |
    testcommand...|                |
    testcommandn  |                |
    endWhile      |                |
    -------------------------------------------

AUTHOR:
    Jonathan McBrien
    jonathan@mcbrien.org
    2008-10-22: v0.1:   Initial version.
    2009-01-16: v0.2:   Updated for Firefox 3.
                xmlTestData.prototype.load now uses the include extension's getIncludeDocumentBySynchronRequest method for better portability.
                (Why reinvent the wheel? :) - with appreciation to the include extension's authors.)
*/

XML.serialize = function(node) {
    if (typeof XMLSerializer != "undefined")
        return (new XMLSerializer()).serializeToString(node) ;
    else if (node.xml) return node.xml;
    else throw "XML.serialize is not supported or can't serialize " + node;
}

function xmlTestData() {
    this.xmlDoc = null;
    this.testdata = null;
    this.index = null;
}


xmlTestData.prototype.load = function(xmlloc) {
    loader = new IDEIncludeCommand();
    var xmlHttpReq = loader.getIncludeDocumentBySynchronRequest(xmlloc);
    this.xmlDoc = xmlHttpReq.responseXML;

    this.index = 0;
    this.testdata = this.xmlDoc.getElementsByTagName("test");

    if (this.testdata == null || this.testdata.length == 0) {
        throw new Error("Test data couldn't be loaded or test data was empty.");
    }
}

xmlTestData.prototype.EOF = function() {
    if (this.index != null && this.index < this.testdata.length) return false;
    return true;
}

xmlTestData.prototype.more = function() {
    return !this.EOF();
}

xmlTestData.prototype.next = function() {
    if (this.EOF()) {
        LOG.error("No test data.");
        return;
    }

    LOG.info(XML.serialize(this.testdata[this.index])); // Log should anything go wrong while testing with this data.

    if (this.testdata[this.index].attributes.length != this.testdata[0].attributes.length) {
        LOG.error("Inconsistent attribute length in test data.");
        return;
    }

    for (i=0; i<this.testdata[this.index].attributes.length; i++){
        if (null == this.testdata[0].getAttribute(this.testdata[this.index].attributes[i].nodeName)) {
            LOG.error("Inconsistent attribute names in test data.");
            return;
        }

        selenium.doStore(this.testdata[this.index].attributes[i].nodeValue, this.testdata[this.index].attributes[i].nodeName);
    }

    this.index++;
}

Selenium.prototype.testdata = null;

Selenium.prototype.doLoadTestData = function(xmlloc) {
    testdata = new xmlTestData();
    testdata.load(xmlloc);
};

Selenium.prototype.doNextTestData = function() {
    testdata.next();
};

/************************************ DATADRIVEN EXTENSION END **********************************************/
include.js

/**
 * Original Developer: Jerry Qian(qqqiansjtucs@hotmail.com)
 * Modified By: John Witchel (jwitchel@colevalleygroup.com)
 * include extension for Selenium-IDE edition
 * refer to includeCommand_2.1.3 for Selenium-Core edition
 * @version 1.3
 *
 */
function IDEIncludeCommand() {}

IDEIncludeCommand.LOG_PREFIX = "IDEIncludeCommand: ";
IDEIncludeCommand.BEGIN_TEMPLATE = "begin$Template$";
IDEIncludeCommand.END_TEMPLATE = "end$Template$";
IDEIncludeCommand.VERSION = "1.1";

IDEIncludeCommand.prototype.prepareTestCaseAsText = function(responseAsText, paramsArray) {
    /**
     * Prepare the HTML to be included in as text into the current testcase-HTML
     * Strip all but the testrows (tr)
     * Stripped will be:
     *  - whitespace (also new lines and tabs, so be careful wirt parameters relying on this),
     *  - comments (xml comments)                 
     * Replace variable according to include-parameters 
     * note: the include-variables are replaced literally. selenium does it at execution time
     * also note: all selenium-variables are available to the included commands, so mostly no include-parameters are necessary
     *
     * @param responseAsText table to be included as text (string)
     * @return testRows array of tr elements (as string!) containing the commands to be included
     * 
     * TODO:
     *  - selenium already can handle testcase-html. use selenium methods or functions instead
     *  - find better name for requester
     */
    // removing new lines, carret return and tabs from response in order to work with regexp
    var pageText = responseAsText.replace(/\r|\n|\t/g,"");
    // remove comments
    // begin comment, not a dash or if it's a dash it may not be followed by -> repeated, end comment
    pageText = pageText.replace(/<!--(?:[^-]|-(?!->))*-->/g,"");
    // find the content of the test table = <[spaces]table[char but not >]>....< /[spaces]table[chars but not >]>
    var testText = pageText.match(/<\s*table[^>]*>(.*)<\/\s*table[^>]*>/i)[1];

    // Replace <td></td> with <td>&nbsp;</td> for iE - credits Chris Astall
    // rz: somehow in my IE 7 this is not needed but is not bad as well
    testText = testText.replace(/<\s*td[^>]*>\s*<\s*\/td[^>]*>/ig,"<td></td>");// jq: no space

    // replace vars with their values in testText
    for ( var k = 0 ; k < paramsArray.length ; k++ ) {
        var pair = paramsArray[k];
        testText = testText.replace(pair[0],pair[1]);
    }

    // removes all  < /tr> 
    // in order to split on < tr>
    testText = testText.replace(/<\/\s*tr[^>]*>/ig,"");
    // split on <tr>
    var testRows = testText.split(/<\s*tr[^>]*>/i);
    return testRows;
};

IDEIncludeCommand.prototype.getIncludeDocumentBySynchronRequest = function(includeUrl) {
    /**
     * Prepare and do the XMLHttp Request synchronous as selenium should not continue execution meanwhile
     *
     * note: the XMLHttp requester is returned (instead of e.g. its text) to let the caller decide to use xml or text
     *
     * selenium-dependency: uses extended String from htmlutils
     *
     *  TODO use Ajax from prototype like this:
     *   var sjaxRequest = new Ajax.Request(url, {asynchronous:false});
     *   there is discussion about getting rid of prototype.js in developer forum.
     *   the ajax impl in xmlutils.js is not active by default in 0.8.2
     *
     * @param includeUrl URI to the include-document (document has to be from the same domain)
     * @return XMLHttp requester after receiving the response
     */
    var url = this.prepareUrl(includeUrl);
    // the xml http requester to fetch the page to include
    var requester = this.newXMLHttpRequest();
    if (!requester) {
        throw new Error("XMLHttp requester object not initialized");
    }
    requester.open("GET", url, false); // synchron mode ! (we don't want selenium to go ahead)
    try {
        requester.send(null);
    } catch(e) {
      throw new Error("Error while fetching url '" + url + "' details: " + e);
    }
    if ( requester.status != 200 && requester.status !== 0 ) {
        throw new Error("Error while fetching " + url + " server response has status = " + requester.status + ", " + requester.statusText );
    }
    return requester;
};

IDEIncludeCommand.prototype.prepareUrl = function(includeUrl) {
    /** Construct absolute URL to get include document
     * using selenium-core handling of urls (see absolutify in htmlutils.js)
     */
    var prepareUrl;
    // htmlSuite mode of SRC? TODO is there a better way to decide whether in SRC mode?
    if (window.location.href.indexOf("selenium-server") >= 0) {
        LOG.debug(IDEIncludeCommand.LOG_PREFIX + "we seem to run in SRC, do we?");
        preparedUrl = absolutify(includeUrl, htmlTestRunner.controlPanel.getTestSuiteName());
    } else {
        preparedUrl = absolutify(includeUrl, selenium.browserbot.baseUrl);
    }
    LOG.debug(IDEIncludeCommand.LOG_PREFIX + "using url to get include '" + preparedUrl + "'");
    return preparedUrl;
};

IDEIncludeCommand.prototype.newXMLHttpRequest = function() {
    // TODO should be replaced by impl. in prototype.js or xmlextras.js
    //     but: there is discussion of getting rid of prototype.js
    //     and: currently xmlextras.js is not activated in testrunner of 0.8.2 release
    var requester = 0;
    var exception = '';
    // see http://developer.apple.com/internet/webcontent/xmlhttpreq.html
    // changed order of native and activeX to get it working with native
    //  xmlhttp in IE 7. credits dhwang
    try {
        // for IE/ActiveX
        if(window.ActiveXObject) {
            try {
                requester = new ActiveXObject("Msxml2.XMLHTTP");
            }
            catch(e) {
                requester = new ActiveXObject("Microsoft.XMLHTTP");
            }
        }
        // Native XMLHttp
        else if(window.XMLHttpRequest) {
            requester = new XMLHttpRequest();
        }
    }
    catch(e) {
        throw new Error("Your browser has to support XMLHttpRequest in order to use include \n" + e);
    }
    return requester;
};

IDEIncludeCommand.prototype.splitParamStrIntoVariables = function(paramString) {
    /**
     * Split include Parameters-String into an 2-dim array containing Variable-Name and -Value
     *
     * selenium-dependency: uses extended String from htmlutils
     *
     * TODO: write jsunit tests - this could be easy (if there were not the new RegExp)
     *
     * @param includeParameters string the parameters from include call
     * @return new 2-dim Array containing regExpName (to find a matching variablename) and value to be substituted for
     */
    var newParamsArray = new Array();
    // paramString shall contains a list of var_name=value
    var paramListPattern = /([^=,]+=[^=,]*,)*([^=,]+=[^=,]*)/;
    if (! paramString || paramString === "") {
        return newParamsArray;
    } else if (paramString.match( paramListPattern )) {
        // parse parameters to fill newParamsArray
        var pairs = paramString.split(",");
        for ( var i = 0 ; i < pairs.length ; i++ ) {
            var pair = pairs[i];
            var nameValue = pair.split("=");
            //rz: use String.trim from htmlutils.js of selenium to get rid of whitespace in variable-name(s)
            var trimmedNameValue = new String(nameValue[0]).trim();
            // the pattern to substitute is ${var_name}
            var regExpName = new RegExp("\\$\\{" + trimmedNameValue + "\\}", "g");

            if (nameValue.length < 3) {
               newParamsArray.push(new Array(regExpName,nameValue[1]));
            } else {
                var varValue = new String(nameValue[1]);
                for (var j = 2; j < nameValue.length; j++) {
                    varValue=varValue.concat("="+nameValue[j]);
                }
                newParamsArray.push(new Array(regExpName,varValue));
            }
        }
    } else {
        throw new Error("Bad format for parameters list : '" + paramString + "'");
    }
    return newParamsArray;
};

IDEIncludeCommand.prototype.doInclude = function(locator, paramString) { 
    // Rewrite logic for Selenium IDE by Jerry Qian
    var currentSelHtmlTestcase = testCase; 

    var includeCmdRow = testCase.debugContext.currentCommand();

    if (!includeCmdRow) {
        throw new Error("IDEIncludeCommand: failed to find include-row in source testtable");
    }

    var paramsArray = this.splitParamStrIntoVariables(paramString);

    var inclDoc = this.getIncludeDocumentBySynchronRequest(locator);

    // Get an array of commands from the include text with all whitespace stripped
    var includedTestCaseHtml = this.prepareTestCaseAsText(inclDoc.responseText, paramsArray);


    this.injectIncludeTestCommands(locator,includeCmdRow,includedTestCaseHtml); 
};

IDEIncludeCommand.prototype.injectIncludeTestCommands = function(locator,includeCmdRow,  testRows) { 
    // Rewrite logic for Selenium IDE by Jerry Qian
    var newCommands = new Array(); 
    // skip first element as it is empty or <tbody>
    for (var i = 1 ; i < testRows.length; i++) {
          if(i == 1){// add BEGIN-END block
            var beginCommand = new Command(IDEIncludeCommand.BEGIN_TEMPLATE,locator,"");    
            newCommands.push(beginCommand);
          } 

          var newText = testRows[i];  
          if(newText.match(/<\s*td.*colspan=.*>(.*)<\/\s*td[^>]*>/i)){//delete comment step
             continue;
          }

          // removes all  < /td> 
          // in order to split on <td>
          newText = newText.replace(/<\/\s*td[^>]*>\s*<\/\s*tbody[^>]*>/ig,""); //remove </tbody>first
          newText = newText.replace(/<\/\s*td[^>]*>/ig,""); 
          var newCols = newText.split(/<\s*td[^>]*>/i);
          var new_cmd,new_target,new_value; 

          for (var j = 1 ; j < newCols.length; j++) {//skip 0
            if(j == 1) {
                new_cmd = newCols[j].replace(/\s/g,"");//trim \s
            }else if(j == 2) {
              new_target = newCols[j].replace(/\s+$/g,"");//trim end \s
            }else if(j == 3) {
              new_value = newCols[j].replace(/\s+$/g,"");//trim end \s
            } 
          } 

          var newCommand =  new Command(new_cmd,new_target,new_value); 
          newCommands.push(newCommand); //correct all steps
    } 
    var endCommand = new Command(IDEIncludeCommand.END_TEMPLATE,locator,"");    
    newCommands.push(endCommand);//add BEGIN-END block


    var cmsBefore = testCase.commands.slice(0,testCase.debugContext.debugIndex + 1);     
    var cmdsBehind = testCase.commands.slice(testCase.debugContext.debugIndex + 1, testCase.commands.length);     
    testCase.commands = cmsBefore.concat(newCommands).concat(cmdsBehind);//Injection

    // Don't inject if it appears the injection has already been done
    // (i.e., if the next command is the BEGIN).
    if (testCase.commands.length <= testCase.debugContext.debugIndex+1
        || beginCommand.toString() != testCase.commands[testCase.debugContext.debugIndex+1].toString())
    {
        // The include command cannot be the last command in the TestCase, or else
        // the de-injection code in doEnd$Template$ will cause an error. So we'll
        // add a simple echo if it is the last.
        if (testCase.commands.length == testCase.debugContext.debugIndex+1)
        {
            // Using concat instead of push so that we don't trigger the TestCase's set-modified flag.
            testCase.commands = testCase.commands.concat(new Command("echo", "The include command cannot be the last line in a TestCase, so this command was added. It can be left in place or removed, as desired.", "The include command cannot be the last line in a TestCase, so this command was added. It can be left in place or removed, as desired."));
        }

        // This is original code.
        var cmsBefore = testCase.commands.slice(0,testCase.debugContext.debugIndex + 1);
        var cmdsBehind = testCase.commands.slice(testCase.debugContext.debugIndex + 1, testCase.commands.length);
        testCase.commands = cmsBefore.concat(newCommands).concat(cmdsBehind);//Injection
    }
};

Selenium.prototype.doInclude = function(locator, paramString) {
    LOG.debug(IDEIncludeCommand.LOG_PREFIX + "Version " + IDEIncludeCommand.VERSION);
    var ideIncludeCommand = new IDEIncludeCommand();
    ideIncludeCommand.doInclude(locator, paramString);

    // If goto scripts exist then reindex the labels.  goto_sel_ide.js creates an array of labels when the
    // script is initialized but an included file's labels are not part of that initial read, so this function
    // re-initializes that array with the included files labels (if any).  If goto_sel.ide.js is not included 
    // it's ignored.
    try {
        this.initialiseLabels();
    } 
    catch (e) {
        LOG.debug("Goto Script not used.");
    }

};

// Array to hold the starting position of the Begin$Template$ marker.  Pushing and popping the position onto an array
// allows us to correctly handle nested includes during clean up.
var beginTemplateIndex = new Array();

// Mark the beginning of the include
Selenium.prototype.doBegin$Template$ = function(locator){
    LOG.info("Begin Template " + locator + " at position " + testCase.debugContext.debugIndex);
    // Add the current position to the tail of the beginTemplateIndex
    beginTemplateIndex.push(testCase.debugContext.debugIndex);
};

// Clean up everything between the closest Begin$Template$ and this $End$Template$, and pop the position off the array.
Selenium.prototype.doEnd$Template$ = function(locator){

    // Remove the last Begin$Template$ from the tail of the beginTemplateIndex
    var currentBeginTemplateIndex = beginTemplateIndex.pop();
    LOG.info("End Template " + locator + " at position " + currentBeginTemplateIndex);

    // Delete the commands that we injected in injectIncludeTestCommands.
    testCase.commands = 
        testCase.commands.slice(0,currentBeginTemplateIndex).concat(
            testCase.commands.slice(testCase.debugContext.debugIndex+1, testCase.commands.length));

    // Set the current command to the next one after the injected block.
    testCase.debugContext.debugIndex = currentBeginTemplateIndex-1;

    //Must refresh to syncup UI
    editor.view.refresh();
};
/************************************ DATADRIVEN EXTENSION START ********************************************/
/*
NAME:
    datadriven

    Licensed under Apache License v2
    http://www.apache.org/licenses/LICENSE-2.0

PURPOSE:
    Basic data driven testing.

    Full documentation at http://wiki.openqa.org/display/SEL/datadriven


EXAMPLE USE:
    The structure of your data driven test case will be;
    -------------------------------------------
    COMMAND       |TARGET          |VALUE
    -------------------------------------------
    loadTestData  |<file path>     |
    while         |!testdata.EOF() |
    testcommand1  |                |
    testcommand...|                |
    testcommandn  |                |
    endWhile      |                |
    -------------------------------------------

AUTHOR:
    Jonathan McBrien
    jonathan@mcbrien.org
    2008-10-22: v0.1:   Initial version.
    2009-01-16: v0.2:   Updated for Firefox 3.
                xmlTestData.prototype.load now uses the include extension's getIncludeDocumentBySynchronRequest method for better portability.
                (Why reinvent the wheel? :) - with appreciation to the include extension's authors.)
*/

XML.serialize = function(node) {
    if (typeof XMLSerializer != "undefined")
        return (new XMLSerializer()).serializeToString(node) ;
    else if (node.xml) return node.xml;
    else throw "XML.serialize is not supported or can't serialize " + node;
}

function xmlTestData() {
    this.xmlDoc = null;
    this.testdata = null;
    this.index = null;
}


xmlTestData.prototype.load = function(xmlloc) {
    loader = new IDEIncludeCommand();
    var xmlHttpReq = loader.getIncludeDocumentBySynchronRequest(xmlloc);
    this.xmlDoc = xmlHttpReq.responseXML;

    this.index = 0;
    this.testdata = this.xmlDoc.getElementsByTagName("test");

    if (this.testdata == null || this.testdata.length == 0) {
        throw new Error("Test data couldn't be loaded or test data was empty.");
    }
}

xmlTestData.prototype.EOF = function() {
    if (this.index != null && this.index < this.testdata.length) return false;
    return true;
}

xmlTestData.prototype.more = function() {
    return !this.EOF();
}

xmlTestData.prototype.next = function() {
    if (this.EOF()) {
        LOG.error("No test data.");
        return;
    }

    LOG.info(XML.serialize(this.testdata[this.index])); // Log should anything go wrong while testing with this data.

    if (this.testdata[this.index].attributes.length != this.testdata[0].attributes.length) {
        LOG.error("Inconsistent attribute length in test data.");
        return;
    }

    for (i=0; i<this.testdata[this.index].attributes.length; i++){
        if (null == this.testdata[0].getAttribute(this.testdata[this.index].attributes[i].nodeName)) {
            LOG.error("Inconsistent attribute names in test data.");
            return;
        }

        selenium.doStore(this.testdata[this.index].attributes[i].nodeValue, this.testdata[this.index].attributes[i].nodeName);
    }

    this.index++;
}

Selenium.prototype.testdata = null;

Selenium.prototype.doLoadTestData = function(xmlloc) {
    testdata = new xmlTestData();
    testdata.load(xmlloc);
};

Selenium.prototype.doNextTestData = function() {
    testdata.next();
};

/************************************ DATADRIVEN EXTENSION END **********************************************/
目前我找不到上两篇文章的链接,因此我会在文章的最后给出它们的代码。代码取自我的工作目录。您只需将代码放入相应的文件中,然后将文件添加到SelemiunIDE用户扩展并使用它们

2)创建一个XML文件test\u data.XML:

<testdata>
    <test employee="1" type="1" startDate="01.01.2013" endDate="01.02.2013" />
    <test employee="2" type="1" startDate="01.02.2013" endDate="01.03.2013" />
    <test employee="3" type="1" startDate="01.03.2013" endDate="01.04.2013" />
...
</testdata>

...
3)在测试用例中使用如下代码:

<testdata>
    <test employee="1" type="1" startDate="01.01.2013" endDate="01.02.2013" />
    <test employee="2" type="1" startDate="01.02.2013" endDate="01.03.2013" />
    <test employee="3" type="1" startDate="01.03.2013" endDate="01.04.2013" />
...
</testdata>
${DataPath}-包含XML文件的目录的完整路径

<!--BEGIN LOOP-->
<tr>
    <td>loadTestData</td>
    <td>file://${DataDir}/test_data.xml</td>
    <td></td>
</tr>
<tr>
    <td>while</td>
    <td>!testdata.EOF()</td>
    <td></td>
</tr>
<tr>
    <td>nextTestData</td>
    <td></td>
    <td></td>
</tr>
<tr>
    <td>echo</td>
    <td>employee=${employee} type=${type} ...</td>
    <td></td>
</tr>
...
<!--END LOOP-->
<tr>
    <td>endWhile</td>
    <td></td>
    <td></td>
</tr>

loadTestData
文件://${DataDir}/test_data.xml
虽然
!testdata.EOF()
下一个测试数据
回声
employee=${employee}type=${type}。。。
...
结束时
已使用的用户扩展:

<testdata>
    <test employee="1" type="1" startDate="01.01.2013" endDate="01.02.2013" />
    <test employee="2" type="1" startDate="01.02.2013" endDate="01.03.2013" />
    <test employee="3" type="1" startDate="01.03.2013" endDate="01.04.2013" />
...
</testdata>
include.js

/**
 * Original Developer: Jerry Qian(qqqiansjtucs@hotmail.com)
 * Modified By: John Witchel (jwitchel@colevalleygroup.com)
 * include extension for Selenium-IDE edition
 * refer to includeCommand_2.1.3 for Selenium-Core edition
 * @version 1.3
 *
 */
function IDEIncludeCommand() {}

IDEIncludeCommand.LOG_PREFIX = "IDEIncludeCommand: ";
IDEIncludeCommand.BEGIN_TEMPLATE = "begin$Template$";
IDEIncludeCommand.END_TEMPLATE = "end$Template$";
IDEIncludeCommand.VERSION = "1.1";

IDEIncludeCommand.prototype.prepareTestCaseAsText = function(responseAsText, paramsArray) {
    /**
     * Prepare the HTML to be included in as text into the current testcase-HTML
     * Strip all but the testrows (tr)
     * Stripped will be:
     *  - whitespace (also new lines and tabs, so be careful wirt parameters relying on this),
     *  - comments (xml comments)                 
     * Replace variable according to include-parameters 
     * note: the include-variables are replaced literally. selenium does it at execution time
     * also note: all selenium-variables are available to the included commands, so mostly no include-parameters are necessary
     *
     * @param responseAsText table to be included as text (string)
     * @return testRows array of tr elements (as string!) containing the commands to be included
     * 
     * TODO:
     *  - selenium already can handle testcase-html. use selenium methods or functions instead
     *  - find better name for requester
     */
    // removing new lines, carret return and tabs from response in order to work with regexp
    var pageText = responseAsText.replace(/\r|\n|\t/g,"");
    // remove comments
    // begin comment, not a dash or if it's a dash it may not be followed by -> repeated, end comment
    pageText = pageText.replace(/<!--(?:[^-]|-(?!->))*-->/g,"");
    // find the content of the test table = <[spaces]table[char but not >]>....< /[spaces]table[chars but not >]>
    var testText = pageText.match(/<\s*table[^>]*>(.*)<\/\s*table[^>]*>/i)[1];

    // Replace <td></td> with <td>&nbsp;</td> for iE - credits Chris Astall
    // rz: somehow in my IE 7 this is not needed but is not bad as well
    testText = testText.replace(/<\s*td[^>]*>\s*<\s*\/td[^>]*>/ig,"<td></td>");// jq: no space

    // replace vars with their values in testText
    for ( var k = 0 ; k < paramsArray.length ; k++ ) {
        var pair = paramsArray[k];
        testText = testText.replace(pair[0],pair[1]);
    }

    // removes all  < /tr> 
    // in order to split on < tr>
    testText = testText.replace(/<\/\s*tr[^>]*>/ig,"");
    // split on <tr>
    var testRows = testText.split(/<\s*tr[^>]*>/i);
    return testRows;
};

IDEIncludeCommand.prototype.getIncludeDocumentBySynchronRequest = function(includeUrl) {
    /**
     * Prepare and do the XMLHttp Request synchronous as selenium should not continue execution meanwhile
     *
     * note: the XMLHttp requester is returned (instead of e.g. its text) to let the caller decide to use xml or text
     *
     * selenium-dependency: uses extended String from htmlutils
     *
     *  TODO use Ajax from prototype like this:
     *   var sjaxRequest = new Ajax.Request(url, {asynchronous:false});
     *   there is discussion about getting rid of prototype.js in developer forum.
     *   the ajax impl in xmlutils.js is not active by default in 0.8.2
     *
     * @param includeUrl URI to the include-document (document has to be from the same domain)
     * @return XMLHttp requester after receiving the response
     */
    var url = this.prepareUrl(includeUrl);
    // the xml http requester to fetch the page to include
    var requester = this.newXMLHttpRequest();
    if (!requester) {
        throw new Error("XMLHttp requester object not initialized");
    }
    requester.open("GET", url, false); // synchron mode ! (we don't want selenium to go ahead)
    try {
        requester.send(null);
    } catch(e) {
      throw new Error("Error while fetching url '" + url + "' details: " + e);
    }
    if ( requester.status != 200 && requester.status !== 0 ) {
        throw new Error("Error while fetching " + url + " server response has status = " + requester.status + ", " + requester.statusText );
    }
    return requester;
};

IDEIncludeCommand.prototype.prepareUrl = function(includeUrl) {
    /** Construct absolute URL to get include document
     * using selenium-core handling of urls (see absolutify in htmlutils.js)
     */
    var prepareUrl;
    // htmlSuite mode of SRC? TODO is there a better way to decide whether in SRC mode?
    if (window.location.href.indexOf("selenium-server") >= 0) {
        LOG.debug(IDEIncludeCommand.LOG_PREFIX + "we seem to run in SRC, do we?");
        preparedUrl = absolutify(includeUrl, htmlTestRunner.controlPanel.getTestSuiteName());
    } else {
        preparedUrl = absolutify(includeUrl, selenium.browserbot.baseUrl);
    }
    LOG.debug(IDEIncludeCommand.LOG_PREFIX + "using url to get include '" + preparedUrl + "'");
    return preparedUrl;
};

IDEIncludeCommand.prototype.newXMLHttpRequest = function() {
    // TODO should be replaced by impl. in prototype.js or xmlextras.js
    //     but: there is discussion of getting rid of prototype.js
    //     and: currently xmlextras.js is not activated in testrunner of 0.8.2 release
    var requester = 0;
    var exception = '';
    // see http://developer.apple.com/internet/webcontent/xmlhttpreq.html
    // changed order of native and activeX to get it working with native
    //  xmlhttp in IE 7. credits dhwang
    try {
        // for IE/ActiveX
        if(window.ActiveXObject) {
            try {
                requester = new ActiveXObject("Msxml2.XMLHTTP");
            }
            catch(e) {
                requester = new ActiveXObject("Microsoft.XMLHTTP");
            }
        }
        // Native XMLHttp
        else if(window.XMLHttpRequest) {
            requester = new XMLHttpRequest();
        }
    }
    catch(e) {
        throw new Error("Your browser has to support XMLHttpRequest in order to use include \n" + e);
    }
    return requester;
};

IDEIncludeCommand.prototype.splitParamStrIntoVariables = function(paramString) {
    /**
     * Split include Parameters-String into an 2-dim array containing Variable-Name and -Value
     *
     * selenium-dependency: uses extended String from htmlutils
     *
     * TODO: write jsunit tests - this could be easy (if there were not the new RegExp)
     *
     * @param includeParameters string the parameters from include call
     * @return new 2-dim Array containing regExpName (to find a matching variablename) and value to be substituted for
     */
    var newParamsArray = new Array();
    // paramString shall contains a list of var_name=value
    var paramListPattern = /([^=,]+=[^=,]*,)*([^=,]+=[^=,]*)/;
    if (! paramString || paramString === "") {
        return newParamsArray;
    } else if (paramString.match( paramListPattern )) {
        // parse parameters to fill newParamsArray
        var pairs = paramString.split(",");
        for ( var i = 0 ; i < pairs.length ; i++ ) {
            var pair = pairs[i];
            var nameValue = pair.split("=");
            //rz: use String.trim from htmlutils.js of selenium to get rid of whitespace in variable-name(s)
            var trimmedNameValue = new String(nameValue[0]).trim();
            // the pattern to substitute is ${var_name}
            var regExpName = new RegExp("\\$\\{" + trimmedNameValue + "\\}", "g");

            if (nameValue.length < 3) {
               newParamsArray.push(new Array(regExpName,nameValue[1]));
            } else {
                var varValue = new String(nameValue[1]);
                for (var j = 2; j < nameValue.length; j++) {
                    varValue=varValue.concat("="+nameValue[j]);
                }
                newParamsArray.push(new Array(regExpName,varValue));
            }
        }
    } else {
        throw new Error("Bad format for parameters list : '" + paramString + "'");
    }
    return newParamsArray;
};

IDEIncludeCommand.prototype.doInclude = function(locator, paramString) { 
    // Rewrite logic for Selenium IDE by Jerry Qian
    var currentSelHtmlTestcase = testCase; 

    var includeCmdRow = testCase.debugContext.currentCommand();

    if (!includeCmdRow) {
        throw new Error("IDEIncludeCommand: failed to find include-row in source testtable");
    }

    var paramsArray = this.splitParamStrIntoVariables(paramString);

    var inclDoc = this.getIncludeDocumentBySynchronRequest(locator);

    // Get an array of commands from the include text with all whitespace stripped
    var includedTestCaseHtml = this.prepareTestCaseAsText(inclDoc.responseText, paramsArray);


    this.injectIncludeTestCommands(locator,includeCmdRow,includedTestCaseHtml); 
};

IDEIncludeCommand.prototype.injectIncludeTestCommands = function(locator,includeCmdRow,  testRows) { 
    // Rewrite logic for Selenium IDE by Jerry Qian
    var newCommands = new Array(); 
    // skip first element as it is empty or <tbody>
    for (var i = 1 ; i < testRows.length; i++) {
          if(i == 1){// add BEGIN-END block
            var beginCommand = new Command(IDEIncludeCommand.BEGIN_TEMPLATE,locator,"");    
            newCommands.push(beginCommand);
          } 

          var newText = testRows[i];  
          if(newText.match(/<\s*td.*colspan=.*>(.*)<\/\s*td[^>]*>/i)){//delete comment step
             continue;
          }

          // removes all  < /td> 
          // in order to split on <td>
          newText = newText.replace(/<\/\s*td[^>]*>\s*<\/\s*tbody[^>]*>/ig,""); //remove </tbody>first
          newText = newText.replace(/<\/\s*td[^>]*>/ig,""); 
          var newCols = newText.split(/<\s*td[^>]*>/i);
          var new_cmd,new_target,new_value; 

          for (var j = 1 ; j < newCols.length; j++) {//skip 0
            if(j == 1) {
                new_cmd = newCols[j].replace(/\s/g,"");//trim \s
            }else if(j == 2) {
              new_target = newCols[j].replace(/\s+$/g,"");//trim end \s
            }else if(j == 3) {
              new_value = newCols[j].replace(/\s+$/g,"");//trim end \s
            } 
          } 

          var newCommand =  new Command(new_cmd,new_target,new_value); 
          newCommands.push(newCommand); //correct all steps
    } 
    var endCommand = new Command(IDEIncludeCommand.END_TEMPLATE,locator,"");    
    newCommands.push(endCommand);//add BEGIN-END block


    var cmsBefore = testCase.commands.slice(0,testCase.debugContext.debugIndex + 1);     
    var cmdsBehind = testCase.commands.slice(testCase.debugContext.debugIndex + 1, testCase.commands.length);     
    testCase.commands = cmsBefore.concat(newCommands).concat(cmdsBehind);//Injection

    // Don't inject if it appears the injection has already been done
    // (i.e., if the next command is the BEGIN).
    if (testCase.commands.length <= testCase.debugContext.debugIndex+1
        || beginCommand.toString() != testCase.commands[testCase.debugContext.debugIndex+1].toString())
    {
        // The include command cannot be the last command in the TestCase, or else
        // the de-injection code in doEnd$Template$ will cause an error. So we'll
        // add a simple echo if it is the last.
        if (testCase.commands.length == testCase.debugContext.debugIndex+1)
        {
            // Using concat instead of push so that we don't trigger the TestCase's set-modified flag.
            testCase.commands = testCase.commands.concat(new Command("echo", "The include command cannot be the last line in a TestCase, so this command was added. It can be left in place or removed, as desired.", "The include command cannot be the last line in a TestCase, so this command was added. It can be left in place or removed, as desired."));
        }

        // This is original code.
        var cmsBefore = testCase.commands.slice(0,testCase.debugContext.debugIndex + 1);
        var cmdsBehind = testCase.commands.slice(testCase.debugContext.debugIndex + 1, testCase.commands.length);
        testCase.commands = cmsBefore.concat(newCommands).concat(cmdsBehind);//Injection
    }
};

Selenium.prototype.doInclude = function(locator, paramString) {
    LOG.debug(IDEIncludeCommand.LOG_PREFIX + "Version " + IDEIncludeCommand.VERSION);
    var ideIncludeCommand = new IDEIncludeCommand();
    ideIncludeCommand.doInclude(locator, paramString);

    // If goto scripts exist then reindex the labels.  goto_sel_ide.js creates an array of labels when the
    // script is initialized but an included file's labels are not part of that initial read, so this function
    // re-initializes that array with the included files labels (if any).  If goto_sel.ide.js is not included 
    // it's ignored.
    try {
        this.initialiseLabels();
    } 
    catch (e) {
        LOG.debug("Goto Script not used.");
    }

};

// Array to hold the starting position of the Begin$Template$ marker.  Pushing and popping the position onto an array
// allows us to correctly handle nested includes during clean up.
var beginTemplateIndex = new Array();

// Mark the beginning of the include
Selenium.prototype.doBegin$Template$ = function(locator){
    LOG.info("Begin Template " + locator + " at position " + testCase.debugContext.debugIndex);
    // Add the current position to the tail of the beginTemplateIndex
    beginTemplateIndex.push(testCase.debugContext.debugIndex);
};

// Clean up everything between the closest Begin$Template$ and this $End$Template$, and pop the position off the array.
Selenium.prototype.doEnd$Template$ = function(locator){

    // Remove the last Begin$Template$ from the tail of the beginTemplateIndex
    var currentBeginTemplateIndex = beginTemplateIndex.pop();
    LOG.info("End Template " + locator + " at position " + currentBeginTemplateIndex);

    // Delete the commands that we injected in injectIncludeTestCommands.
    testCase.commands = 
        testCase.commands.slice(0,currentBeginTemplateIndex).concat(
            testCase.commands.slice(testCase.debugContext.debugIndex+1, testCase.commands.length));

    // Set the current command to the next one after the injected block.
    testCase.debugContext.debugIndex = currentBeginTemplateIndex-1;

    //Must refresh to syncup UI
    editor.view.refresh();
};
/************************************ DATADRIVEN EXTENSION START ********************************************/
/*
NAME:
    datadriven

    Licensed under Apache License v2
    http://www.apache.org/licenses/LICENSE-2.0

PURPOSE:
    Basic data driven testing.

    Full documentation at http://wiki.openqa.org/display/SEL/datadriven


EXAMPLE USE:
    The structure of your data driven test case will be;
    -------------------------------------------
    COMMAND       |TARGET          |VALUE
    -------------------------------------------
    loadTestData  |<file path>     |
    while         |!testdata.EOF() |
    testcommand1  |                |
    testcommand...|                |
    testcommandn  |                |
    endWhile      |                |
    -------------------------------------------

AUTHOR:
    Jonathan McBrien
    jonathan@mcbrien.org
    2008-10-22: v0.1:   Initial version.
    2009-01-16: v0.2:   Updated for Firefox 3.
                xmlTestData.prototype.load now uses the include extension's getIncludeDocumentBySynchronRequest method for better portability.
                (Why reinvent the wheel? :) - with appreciation to the include extension's authors.)
*/

XML.serialize = function(node) {
    if (typeof XMLSerializer != "undefined")
        return (new XMLSerializer()).serializeToString(node) ;
    else if (node.xml) return node.xml;
    else throw "XML.serialize is not supported or can't serialize " + node;
}

function xmlTestData() {
    this.xmlDoc = null;
    this.testdata = null;
    this.index = null;
}


xmlTestData.prototype.load = function(xmlloc) {
    loader = new IDEIncludeCommand();
    var xmlHttpReq = loader.getIncludeDocumentBySynchronRequest(xmlloc);
    this.xmlDoc = xmlHttpReq.responseXML;

    this.index = 0;
    this.testdata = this.xmlDoc.getElementsByTagName("test");

    if (this.testdata == null || this.testdata.length == 0) {
        throw new Error("Test data couldn't be loaded or test data was empty.");
    }
}

xmlTestData.prototype.EOF = function() {
    if (this.index != null && this.index < this.testdata.length) return false;
    return true;
}

xmlTestData.prototype.more = function() {
    return !this.EOF();
}

xmlTestData.prototype.next = function() {
    if (this.EOF()) {
        LOG.error("No test data.");
        return;
    }

    LOG.info(XML.serialize(this.testdata[this.index])); // Log should anything go wrong while testing with this data.

    if (this.testdata[this.index].attributes.length != this.testdata[0].attributes.length) {
        LOG.error("Inconsistent attribute length in test data.");
        return;
    }

    for (i=0; i<this.testdata[this.index].attributes.length; i++){
        if (null == this.testdata[0].getAttribute(this.testdata[this.index].attributes[i].nodeName)) {
            LOG.error("Inconsistent attribute names in test data.");
            return;
        }

        selenium.doStore(this.testdata[this.index].attributes[i].nodeValue, this.testdata[this.index].attributes[i].nodeName);
    }

    this.index++;
}

Selenium.prototype.testdata = null;

Selenium.prototype.doLoadTestData = function(xmlloc) {
    testdata = new xmlTestData();
    testdata.load(xmlloc);
};

Selenium.prototype.doNextTestData = function() {
    testdata.next();
};

/************************************ DATADRIVEN EXTENSION END **********************************************/
/**
*原发展商:钱杰瑞(qqqiansjtucs@hotmail.com)
*修改人:John Witchel(jwitchel@colevalleygroup.com)
*包括Selenium IDE版本的扩展
*有关Selenium Core版本,请参阅includeCommand_2.1.3
*@version 1.3
*
*/
函数IDEIncludeCommand(){}
IDEIncludeCommand.LOG_PREFIX=“IDEIncludeCommand:”;
IDEIncludeCommand.BEGIN_TEMPLATE=“BEGIN$TEMPLATE$”;
IDEIncludeCommand.END_TEMPLATE=“END$TEMPLATE$”;
IDEIncludeCommand.VERSION=“1.1”;
IDEIncludeCommand.prototype.PrepareTestCaseAstText=函数(responseAsText,paramsArray){
/**
*准备要作为文本包含在当前测试用例HTML中的HTML
*除去所有测试行(tr)
*将包括:
*-空白(也包括新行和制表符,因此请注意依赖于此的wirt参数),
*-注释(xml注释)
*根据包含参数替换变量
*注意:include变量被逐字替换。selenium在执行时进行替换
*还要注意:所有selenium变量都可用于包含的命令,因此通常不需要包含参数
*
*@param responsesText表将作为文本(字符串)包含
*@return testRows tr元素数组(作为字符串!),包含要包含的命令
* 
*待办事项:
*-selenium已经可以处理testcase html。请改用selenium方法或函数
*-为请求者找到更好的名称
*/
//从响应中删除新行、carret return和tab以使用regexp
var pageText=responsesText.replace(/\r |\n |\t/g,“”);
//删除评论
//开始评论,而不是破折号,或者如果是破折号,后面可能不会跟着->重复,结束评论
pageText=pageText.replace(//g,“”);
//查找测试表的内容=]>..]>
var testText=pageText.match(/]*>(.*]*>/i)[1];
//替换为iE-学分Chris Astall
//rz:在我的IE 7中,这是不需要的,但也不错
testText=testText.replace(//*>\s*]*>/ig,“”;//jq:无空格
//用testText中的值替换变量
对于(var k=0;k
//为了在
testText=testText.replace(/]*>/ig,“”);
//分开
var testRows=testText.split(/]*>/i);
返回测试行;
};
IDEIncludeCommand.prototype.getIncludeDocumentBySynchronRequest=函数(includeUrl){
/**
*准备并同步执行XMLHttp请求,因为selenium不应同时继续执行
*
*注意:返回XMLHttp请求程序(而不是它的文本)让调用方决定使用xml或文本
*
*selenium依赖项:使用htmlutils中的扩展字符串
*
*TODO使用原型中的Ajax,如下所示:
*var sjaxRequest=new Ajax.Request(url,{asynchronous:false});
*在开发者论坛上有关于去掉prototype.js的讨论。
*默认情况下,xmlutils.js中的ajax impl在0.8.2中不处于活动状态
*
*@param includeUrl URI到包含文档(文档必须来自同一域)
*@return XMLHttp请求者在收到响应后返回
*/
var url=this.prepareUrl(includeUrl);
//获取要包含的页面的xml http请求程序
var requester=this.newXMLHttpRequest();
如果(!请求者){
抛出新错误(“XMLHttp请求程序对象未初始化”);
}
open(“GET”,url,false);//同步模式!(我们不希望selenium继续)
试一试{
requester.send(空);
}捕获(e){
抛出新错误(“获取url'+url+”详细信息时出错:“+e”);