CVS: Products/Zelenium/selenium - SeleniumLog.html:1.1 domviewer.html:1.3 htmlutils.js:1.5 selenium-api.js:1.5 selenium-browserbot.js:1.5 selenium-commandhandlers.js:1.4 selenium-domviewer.js:1.2 selenium-executionloop.js:1.5 selenium-fitrunner.js:1.6 selenium-logging.js:1.4 selenium.css:1.3 version.txt:1.2

Jens Vagelpohl <[email protected]>
Newsgroups gmane.comp.web.zope.public-cvs
Message-ID <[email protected]>
Update of /cvs-repository/Products/Zelenium/selenium
In directory cvs.zope.org:/tmp/cvs-serv7171/selenium

Modified Files:
	domviewer.html htmlutils.js selenium-api.js 
	selenium-browserbot.js selenium-commandhandlers.js 
	selenium-domviewer.js selenium-executionloop.js 
	selenium-fitrunner.js selenium-logging.js selenium.css 
	version.txt 
Added Files:
	SeleniumLog.html 
Log Message:
- Upgrade to selenium version 0.6


=== Added File Products/Zelenium/selenium/SeleniumLog.html ===
<html>

<head>
<title>Selenium Log Console</title>
<link id="cssLink" rel="stylesheet" href="selenium.css" />

</head>
<body id="logging-console">

<script language="JavaScript">

var logLevels = {
    debug: 0,
    info: 1,
    warn: 2,
    error: 3
};

function getThresholdLevel() {
    var buttons = document.getElementById('logLevelChooser').level;
    for (var i = 0; i < buttons.length; i++) {
        if (buttons[i].checked) {
            return buttons[i].value;
        }
    }
}

function append(message, logLevel) {
    if (logLevels[logLevel] < logLevels[getThresholdLevel()]) {
        return;
    }
    var log = document.getElementById('log');
    var newEntry = document.createElement('li');
    newEntry.className = logLevel;
    newEntry.appendChild(document.createTextNode(message));
    log.appendChild(newEntry);
    if (newEntry.scrollIntoView) {
        newEntry.scrollIntoView();
    }
}

</script>

<div id="banner">
  <form id="logLevelChooser">
      <input id="level-error" type="radio" name="level" 
             value="error" /><label for="level-error">Error</label>
      <input id="level-warn" type="radio" name="level"
             value="warn" /><label for="level-warn">Warn</label>
      <input id="level-info" type="radio" name="level" checked="yes"
             value="info" /><label for="level-info">Info</label>
      <input id="level-debug" type="radio" name="level" 
             value="debug" /><label for="level-debug">Debug</label>
  </form>
  <h1>Selenium Log Console</h1>
</div>

<ul id="log"></ul>

</body>
</html>


=== Products/Zelenium/selenium/domviewer.html 1.2 => 1.3 ===
--- Products/Zelenium/selenium/domviewer.html:1.2	Fri May  6 07:46:12 2005
+++ Products/Zelenium/selenium/domviewer.html	Wed Oct 12 06:38:23 2005
@@ -4,9 +4,8 @@
     <head>
         <title>DOM Viewer</title>
         <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
-        <link href="/styles/default.css" rel="dom-styles/stylesheet" 
-            type="text/css" />
-        <script type="text/javascript" src="selenium-domviewer.js" />
+        <link rel="stylesheet" type="text/css" href="dom-styles/default.css"/>
+        <script type="text/javascript" src="selenium-domviewer.js"></script>
     </head>
 	<body onload="loadDomViewer();">
 		<h3>DOM Viewer</h3>


=== Products/Zelenium/selenium/htmlutils.js 1.4 => 1.5 ===
--- Products/Zelenium/selenium/htmlutils.js:1.4	Tue Jun  7 13:17:46 2005
+++ Products/Zelenium/selenium/htmlutils.js	Wed Oct 12 06:38:23 2005
@@ -19,21 +19,19 @@
 // make it possible to handle elements in a way that is 
 // compatible with both IE-like and Mozilla-like browsers
 
-function trim() {
+String.prototype.trim = function() {
   var result = this.replace( /^\s+/g, "" );// strip leading
   return result.replace( /\s+$/g, "" );// strip trailing
-}
-String.prototype.trim = trim;
-
-function toCamelCase() {
+};
+String.prototype.lcfirst = function() {
    return this.charAt(0).toLowerCase() + this.substr(1);
-}
-String.prototype.toCamelCase = toCamelCase;
-
-function startsWith(str) {
+};
+String.prototype.ucfirst = function() {
+   return this.charAt(0).toUpperCase() + this.substr(1);
+};
+String.prototype.startsWith = function(str) {
     return this.indexOf(str) == 0;
-}
-String.prototype.startsWith = startsWith;
+};
 
 // Returns the text in this element
 function getText(element) {
@@ -128,10 +126,158 @@
   return 'anonymous';
 }
 
-function describe(object) {
+function describe(object, delimiter) {
     var props = new Array();
     for (var prop in object) {
         props.push(prop + " -> " + object[prop]);
     }
-    return props.join('\n');
+    return props.join(delimiter || '\n');
 }
+
+PatternMatcher = function(pattern) {
+    this.selectStrategy(pattern);
+};
+PatternMatcher.prototype = {
+    
+    selectStrategy: function(pattern) {
+        this.pattern = pattern;
+        var strategyName = 'glob'; // by default
+        if (/^([a-zA-Z]+):(.*)/.test(pattern)) {
+            strategyName = RegExp.$1;
+            pattern = RegExp.$2;
+        }
+        var matchStrategy = PatternMatcher.strategies[strategyName];
+        if (!matchStrategy) {
+            throw new SeleniumError("cannot find PatternMatcher.strategies." + strategyName);
+        }
+        this.matcher = new matchStrategy(pattern);
+    },
+    
+    matches: function(actual) {
+        return this.matcher.matches(actual + '');
+        // Note: appending an empty string avoids a Konqueror bug
+    }
+
+};
+
+/**
+ * A "static" convenience method for easy matching
+ */
+PatternMatcher.matches = function(pattern, actual) {
+    return new PatternMatcher(pattern).matches(actual);
+};
+
+PatternMatcher.strategies = {
+
+    /**
+     * Exact matching, e.g. "exact:***"
+     */
+    exact: function(expected) {
+        this.expected = expected;
+        this.matches = function(actual) {
+            return actual == this.expected;
+        };
+    },
+
+    /**
+     * Match by regular expression, e.g. "regexp:^[0-9]+$"
+     */
+    regexp: function(regexpString) {
+        this.regexp = new RegExp(regexpString);
+        this.matches = function(actual) {
+            return this.regexp.test(actual);
+        };
+    },
+
+    /**
+     * "glob" (aka "wildmat") patterns, e.g. "glob:one,two,*"
+     */
+    glob: function(globString) {
+        this.regexp = new RegExp(PatternMatcher.regexpFromGlob(globString));
+        this.matches = function(actual) {
+            return this.regexp.test(actual);
+        };
+    }
+    
+};
+
+PatternMatcher.regexpFromGlob = function(glob) {
+    var re = glob;
+    re = re.replace(/([.^$+(){}\[\]\\|])/g, "\\$1");
+    re = re.replace(/\?/g, "(.|[\r\n])");
+    re = re.replace(/\*/g, "(.|[\r\n])*");
+    return "^" + re + "$";
+};
+
+var Assert = {
+
+    fail: function(message) {
+        throw new AssertionFailedError(message);
+    },
+
+    /*
+     * Assert.equals(comment?, expected, actual)
+     */
+    equals: function() {
+        var args = new AssertionArguments(arguments);
+        if (args.expected === args.actual) {
+            return;
+        }
+        Assert.fail(args.comment + 
+                    "Expected '" + args.expected + 
+                    "' but was '" + args.actual + "'");
+    },
+
+    /*
+     * Assert.matches(comment?, pattern, actual)
+     */
+    matches: function() {
+        var args = new AssertionArguments(arguments);
+        if (PatternMatcher.matches(args.expected, args.actual)) {
+            return;
+        }
+        Assert.fail(args.comment + 
+                    "Actual value '" + args.actual + 
+                    "' did not match '" + args.expected + "'");
+    },
+    
+    /*
+     * Assert.notMtches(comment?, pattern, actual)
+     */
+    notMatches: function() {
+        var args = new AssertionArguments(arguments);
+        if (!PatternMatcher.matches(args.expected, args.actual)) {
+            return;
+        }
+        Assert.fail(args.comment + 
+                    "Actual value '" + args.actual + 
+                    "' did match '" + args.expected + "'");
+    }
+
+};
+
+// Preprocess the arguments to allow for an optional comment.
+function AssertionArguments(args) {
+    if (args.length == 2) {
+        this.comment = "";
+        this.expected = args[0];
+        this.actual = args[1];
+    } else {
+        this.comment = args[0] + "; ";
+        this.expected = args[1];
+        this.actual = args[2];
+    }
+}
+
+
+
+function AssertionFailedError(message) {
+    this.isAssertionFailedError = true;
+    this.failureMessage = message;
+}
+
+function SeleniumError(message) {
+    var error = new Error(message);
+    error.isSeleniumError = true;
+    return error;
+};


=== Products/Zelenium/selenium/selenium-api.js 1.4 => 1.5 ===
--- Products/Zelenium/selenium/selenium-api.js:1.4	Tue Jun  7 13:17:46 2005
+++ Products/Zelenium/selenium/selenium-api.js	Wed Oct 12 06:38:23 2005
@@ -17,30 +17,18 @@
 
 storedVars = new Object();
 
-var nextExecution;
-function executeNext() {
-    LOG.debug("CODED - LOAD");
-    if (nextExecution) {
-        nextExecution();
-    }
-    nextExecution = null;
-}
-
-var assert = new Assert();
 function Selenium(browserbot) {
     this.browserbot = browserbot;
+    this.optionLocatorFactory = new OptionLocatorFactory();
     this.page = function() {
         return browserbot.getCurrentPage();
     };
-
-    var self = this;
-
-    this.callOnNextPageLoad = function(callback) {
-        nextExecution = callback;
-        self.browserbot.callOnNextPageLoad(executeNext);
-    };
 }
 
+Selenium.createForFrame = function(frame) {
+    return new Selenium(BrowserBot.createForFrame(frame));
+};
+
 /*
  * Reset the browserbot when an error occurs..
  */
@@ -76,12 +64,16 @@
 };
 
 /**
- * Select the option by label from the located select element.
- * TODO fail if it's not a select.
+ * Select the option from the located select element.
  */
-Selenium.prototype.doSelect = function(locator, optionText) {
+Selenium.prototype.doSelect = function(locator, optionLocator) {
     var element = this.page().findElement(locator);
-    this.page().selectOptionWithLabel(element, optionText);
+    if (!("options" in element)) {
+        throw new SeleniumError("Specified element is not a Select (has no options)");
+    }
+    var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
+    var option = locator.findOption(element);
+    this.page().selectOption(element, option);
 };
 
 /*
@@ -107,6 +99,13 @@
 };
 
 /*
+ * Instruct Selenium what to answear on the next prompt dialog it encounters
+ */
+Selenium.prototype.doAnswerOnNextPrompt = function(answer) {
+    this.browserbot.setNextPromptResult(answer);
+};
+
+/*
  * Simulate the browser back button
  */
 Selenium.prototype.doGoBack = function() {
@@ -114,93 +113,114 @@
 };
 
 /*
- *  Asserts that the supplied message was received as an alert
+ * Close the browser window or tab
  */
- Selenium.prototype.assertAlert = function(expectedAlert) {
-    if ( this.browserbot.hasAlerts()) {
-        
-        receivedAlert = this.browserbot.getNextAlert();
-        if ( receivedAlert != expectedAlert ) {
-           assert.fail("The alert was [" + receivedAlert + "]");
-        }
-                          
-    } else {
-        assert.fail("There were no alerts");
+Selenium.prototype.doClose = function() {
+    this.page().close();
+};
+
+/*
+ * Explicitly fire an event
+ */
+Selenium.prototype.doFireEvent = function(locator, event) {
+    var element = this.page().findElement(locator);
+    triggerEvent(element, event, false);
+};
+
+/*
+ * Get an alert message, or fail if there were no alerts.
+ */
+Selenium.prototype.getAlert = function() {
+    if (!this.browserbot.hasAlerts()) {
+        Assert.fail("There were no alerts");
     }
- };
+    return this.browserbot.getNextAlert();
+};
 
-  /*
-  *  Asserts that the supplied message was received as a confirmation
-  */
-  Selenium.prototype.assertConfirmation = function(expectedConfirmation) {
-     if ( this.browserbot.hasConfirmations()) {
-         
-         receivedConfirmation = this.browserbot.getNextConfirmation();
-         if ( receivedConfirmation != expectedConfirmation ) {
-            assert.fail("The confirmation message was [" + receivedConfirmation + "]");
-         }
-                           
-     } else {
-         assert.fail("There were no confirmations");
-     }
-  };
+/*
+ * Get a confirmation message, or fail if there were no confirmations.
+ */
+Selenium.prototype.getConfirmation = function() {
+    if (!this.browserbot.hasConfirmations()) {
+        Assert.fail("There were no confirmations");
+    }
+    return this.browserbot.getNextConfirmation();
+};
  
 /*
- * Verify the location of the current page.
+ * Get a prompt message, or fail if there were no prompts.
  */
-Selenium.prototype.assertAbsoluteLocation = function(expectedLocation) {
-    this.assertMatches(expectedLocation, this.page().location);
+Selenium.prototype.getPrompt = function() {
+    if (! this.browserbot.hasPrompts()) {
+        Assert.fail("There were no prompts");
+    }
+    return this.browserbot.getNextPrompt();
 };
 
+/*
+ * Get the location of the current page.
+ */
+Selenium.prototype.getAbsoluteLocation = function() {
+    return this.page().location;
+};
 
 /*
- * Verify the location of the current page ends with the expected location
+ * Verify the location of the current page ends with the expected location.
+ * If a querystring is provided, this is checked as well.
  */
 Selenium.prototype.assertLocation = function(expectedLocation) {
-    var docLocation = this.page().location.toString();
-    if (docLocation.length != docLocation.indexOf(expectedLocation) + expectedLocation.length)
-    {
-        assert.fail("Expected location to end with '" + expectedLocation
-             + "' but was '" + docLocation + "'");
+    var docLocation = this.page().location;
+    var searchPos = expectedLocation.lastIndexOf('?');
+
+    if (searchPos == -1) {
+        Assert.matches('*' + expectedLocation, docLocation.pathname);
+    }
+    else {
+        var expectedPath = expectedLocation.substring(0, searchPos);
+        Assert.matches('*' + expectedPath, docLocation.pathname);
+
+        var expectedQueryString = expectedLocation.substring(searchPos);
+        Assert.equals(expectedQueryString, docLocation.search);
     }
 };
 
 /*
- * Verify the title of the current page.
+ * Get the title of the current page.
  */
-Selenium.prototype.assertTitle = function(expectedTitle) {
-    this.assertMatches(expectedTitle, this.page().title());
+Selenium.prototype.getTitle = function() {
+    return this.page().title();
 };
 
+
 /*
- * Verify the value of a form element.
+ * Get the (trimmed) value of a form element.
+ * This is used to generate assertValue, verifyValue, ...
  */
-Selenium.prototype.assertValue = function(locator, expectedValue) {
-    var element = this.page().findElement(locator);
-    var actualValue = getInputValue(element);
-    this.assertMatches(expectedValue, actualValue.trim());
-};
+Selenium.prototype.getValue = function(locator) {
+    var element = this.page().findElement(locator)
+    return getInputValue(element).trim();
+}
 
-/*
- * Verifies that the text of the located element matches the expected content.
+/**
+ * Get the (trimmed) text of a form element.
+ * This is used to generate assertText, verifyText, ...
  */
-Selenium.prototype.assertText = function(locator, expectedContent) {
+Selenium.prototype.getText = function(locator) {
     var element = this.page().findElement(locator);
-    var actualText = getText(element);
-    this.assertMatches(expectedContent, actualText.trim());
+    return getText(element).trim();
 };
 
 /*
- * Asserts that the text for a single cell within and HTML table matches the expected content.
+ * Return the text for a single cell within an HTML table.
  * The table locator syntax is table.row.column.
  */
-Selenium.prototype.assertTable = function(tableLocator, expectedContent) {
+Selenium.prototype.getTable = function(tableLocator) {
     // This regular expression matches "tableName.row.column"
     // For example, "mytable.3.4"
     pattern = /(.*)\.(\d+)\.(\d+)/;
 
     if(!pattern.test(tableLocator)) {
-        assert.fail("Invalid target format. Correct format is tableName.rowNum.columnNum");
+        throw new SeleniumError("Invalid target format. Correct format is tableName.rowNum.columnNum");
     }
 
     pieces = tableLocator.match(pattern);
@@ -211,24 +231,24 @@
 
     var table = this.page().findElement(tableName);
     if (row > table.rows.length) {
-        assert.fail("Cannot access row " + row + " - table has " + table.rows.length + " rows");
+        Assert.fail("Cannot access row " + row + " - table has " + table.rows.length + " rows");
     }
     else if (col > table.rows[row].cells.length) {
-        assert.fail("Cannot access column " + col + " - table row has " + table.rows[row].cells.length + " columns");
+        Assert.fail("Cannot access column " + col + " - table row has " + table.rows[row].cells.length + " columns");
     }
     else {
         actualContent = getText(table.rows[row].cells[col]);
-        this.assertMatches(expectedContent, actualContent.trim());
+        return actualContent.trim();
     }
 };
 
 /**
- * Verify the label of the option that is selected.
+ * Verify that the selected option satisfies the option locator.
  */
-Selenium.prototype.assertSelected = function(target, expectedLabel) {
+Selenium.prototype.assertSelected = function(target, optionLocator) {
     var element = this.page().findElement(target);
-    var selectedLabel = element.options[element.selectedIndex].text;
-    this.assertMatches(expectedLabel, selectedLabel);
+    var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
+    locator.assertSelected(element);
 };
 
 String.prototype.parseCSV = function() {
@@ -247,20 +267,19 @@
     var element = this.page().findElement(target);
 
     var expectedOptionLabels = options.parseCSV();
-    assert.equals("Wrong number of options.", expectedOptionLabels.length, element.options.length);
+    Assert.equals("Wrong number of options", expectedOptionLabels.length, element.options.length);
 
     for (var i = 0; i < element.options.length; i++) {
-        this.assertMatches(expectedOptionLabels[i], element.options[i].text);
+        Assert.matches(expectedOptionLabels[i], element.options[i].text);
     }
 };
 
 /**
- * Verify the value of an element attribute. The syntax for returning an element attribute
- * is <element-locator>@attribute-name
+ * Get the value of an element attribute. The syntax for returning an element attribute
+ * is <element-locator>@attribute-name.  Used to generate assert, verify, assertNot...
  */
-Selenium.prototype.assertAttribute = function(target, expected) {
-    var attributeValue = this.page().findAttribute(target);
-    this.assertMatches(expected, attributeValue);
+Selenium.prototype.getAttribute = function(target) {
+    return this.page().findAttribute(target);
 };
 
 /*
@@ -270,9 +289,9 @@
     var allText = this.page().bodyText();
 
     if(allText == "") {
-        assert.fail("Page text not found");
+        Assert.fail("Page text not found");
     } else if(allText.indexOf(expectedText) == -1) {
-        assert.fail("'" + expectedText + "' not found in page text.");
+        Assert.fail("'" + expectedText + "' not found in page text.");
     }
 };
 
@@ -283,9 +302,9 @@
     var allText = this.page().bodyText();
 
     if(allText == "") {
-        assert.fail("Page text not found");
+        Assert.fail("Page text not found");
     } else if(allText.indexOf(unexpectedText) != -1) {
-        assert.fail("'" + unexpectedText + "' was found in page text.");
+        Assert.fail("'" + unexpectedText + "' was found in page text.");
     }
 };
 
@@ -296,7 +315,7 @@
     try {
         this.page().findElement(locator);
     } catch (e) {
-        assert.fail("Element " + locator + " not found.");
+        Assert.fail("Element " + locator + " not found.");
     }
 };
 
@@ -310,7 +329,7 @@
     catch (e) {
         return;
     }
-    assert.fail("Element " + locator + " found.");
+    Assert.fail("Element " + locator + " found.");
 };
 
 /*
@@ -321,10 +340,10 @@
     try {
         element = this.page().findElement(locator);
     } catch (e) {
-        assert.fail("Element " + locator + " not present.");
+        Assert.fail("Element " + locator + " not present.");
     }
     if (! this.isVisible(element)) {
-        assert.fail("Element " + locator + " not visible.");
+        Assert.fail("Element " + locator + " not visible.");
     }
 };
 
@@ -339,7 +358,7 @@
         return;
     }
     if (this.isVisible(element)) {
-        assert.fail("Element " + locator + " is visible.");
+        Assert.fail("Element " + locator + " is visible.");
     }
 };
 
@@ -383,7 +402,7 @@
         //   currentStyle is not identical to getComputedStyle()
         //   ... but it's good enough for "visibility"
     }
-    throw new Error("cannot determine effective stylesheet in this browser");
+    throw new SeleniumError("cannot determine effective stylesheet in this browser");
 };
 
 /**
@@ -392,10 +411,10 @@
 Selenium.prototype.assertEditable = function(locator) {
     var element = this.page().findElement(locator);
     if (element.value == undefined) {
-        assert.fail("Element " + locator + " is not an input.");
+        Assert.fail("Element " + locator + " is not an input.");
     }
     if (element.disabled) {
-        assert.fail("Element " + locator + " is disabled.");
+        Assert.fail("Element " + locator + " is disabled.");
     }
 };
 
@@ -408,7 +427,7 @@
         return; // not an input
     }
     if (element.disabled == false) {
-        assert.fail("Element " + locator + " is editable.");
+        Assert.fail("Element " + locator + " is editable.");
     }
 };
 
@@ -464,12 +483,31 @@
 };
 
 /*
+ * Store the value of an element attribute in a variable
+ */
+Selenium.prototype.doStoreAttribute = function(target, varName) {
+    storedVars[varName] = this.page().findAttribute(target);
+};
+
+/*
  * Store the result of a literal value
  */
 Selenium.prototype.doStore = function(value, varName) {
     storedVars[varName] = value;
 };
 
+
+/*
+ * Wait for the target to have the specified value by polling.
+ * The polling is done in TestLoop.kickoffNextCommandExecution()
+ */
+Selenium.prototype.doWaitForValue = function (target, value) {
+    var e = this.page().findElement(target);
+    testLoop.waitForCondition = function () {
+        return (e.value == value);
+    };
+};
+
 /**
  * Evaluate a parameter, performing javascript evaluation and variable substitution.
  * If the string matches the pattern "javascript{ ... }", evaluate the string between the braces.
@@ -507,76 +545,139 @@
     return stringResult;
 };
 
-function Assert() {
-    this.equals = function()
-    {
-        if (arguments.length == 2)
-        {
-            var comment = "";
-            var expected = arguments[0];
-            var actual = arguments[1];
-        }
-        else {
-            var comment = arguments[0] + " ";
-            var expected = arguments[1];
-            var actual = arguments[2];
-        }
 
-        if (expected === actual) {
-            return;
+/**
+ *  Factory for creating "Option Locators".
+ *  An OptionLocator is an object for dealing with Select options (e.g. for
+ *  finding a specified option, or asserting that the selected option of 
+ *  Select element matches some condition.
+ *  The type of locator returned by the factory depends on the locator string:
+ *     label=<exp>  (OptionLocatorByLabel)
+ *     value=<exp>  (OptionLocatorByValue)
+ *     index=<exp>  (OptionLocatorByIndex)
+ *     id=<exp>     (OptionLocatorById)
+ *     <exp> (default is OptionLocatorByLabel).
+ */
+function OptionLocatorFactory() {
+}
+
+OptionLocatorFactory.prototype.fromLocatorString = function(locatorString) {
+    var locatorType = 'label';
+    var locatorValue = locatorString;
+    // If there is a locator prefix, use the specified strategy
+    var result = locatorString.match(/^([a-zA-Z]+)=(.*)/);
+    if (result) {
+        locatorType = result[1];
+        locatorValue = result[2];
+    }
+    if (this.optionLocators == undefined) {
+        this.registerOptionLocators();
+    }
+    if (this.optionLocators[locatorType]) {
+        return new this.optionLocators[locatorType](locatorValue);
+    }
+    throw new SeleniumError("Unkown option locator type: " + locatorType);
+};
+
+/**
+ * To allow for easy extension, all of the option locators are found by
+ * searching for all methods of OptionLocatorFactory.prototype that start
+ * with "OptionLocatorBy".
+ * TODO: Consider using the term "Option Specifier" instead of "Option Locator".
+ */
+OptionLocatorFactory.prototype.registerOptionLocators = function() {
+    this.optionLocators={};
+    for (var functionName in this) {
+      var result = /OptionLocatorBy([A-Z].+)$/.exec(functionName);
+      if (result != null) {
+          var locatorName = result[1].lcfirst();
+          this.optionLocators[locatorName] = this[functionName];
+      }
+    }
+};
+
+/**
+ *  OptionLocator for options identified by their labels.
+ */
+OptionLocatorFactory.prototype.OptionLocatorByLabel = function(label) {
+    this.label = label;
+    this.labelMatcher = new PatternMatcher(this.label);
+    this.findOption = function(element) {
+        for (var i = 0; i < element.options.length; i++) {
+            if (this.labelMatcher.matches(element.options[i].text)) {
+                return element.options[i];
+            }
         }
-        var errorMessage = comment + "Expected '" + expected + "' but was '" + actual + "'";
+        throw new SeleniumError("Option with label '" + this.label + "' not found");
+    };
 
-        throw new AssertionFailedError(errorMessage);
+    this.assertSelected = function(element) {
+        var selectedLabel = element.options[element.selectedIndex].text;
+        Assert.matches(this.label, selectedLabel);
     };
+};
 
-    this.fail = function(message)
-    {
-        throw new AssertionFailedError(message);
+/**
+ *  OptionLocator for options identified by their values.
+ */
+OptionLocatorFactory.prototype.OptionLocatorByValue = function(value) {
+    this.value = value;
+    this.valueMatcher = new PatternMatcher(this.value);
+    this.findOption = function(element) {
+        for (var i = 0; i < element.options.length; i++) {
+            if (this.valueMatcher.matches(element.options[i].value)) {
+                return element.options[i];
+            }
+        }
+        throw new SeleniumError("Option with value '" + this.value + "' not found");
     };
-}
 
-function AssertionFailedError(message) {
-    this.isAssertionFailedError = true;
-    this.failureMessage = message;
-}
+    this.assertSelected = function(element) {
+        var selectedValue = element.options[element.selectedIndex].value;
+        Assert.matches(this.value, selectedValue);
+    };
+};
 
-/*
- * assertMatches(comment?, pattern, actual)
+/**
+ *  OptionLocator for options identified by their index.
  */
-Selenium.prototype.assertMatches = function() {
-    if (arguments.length == 2)
-    {
-        var comment = "";
-        var pattern = arguments[0];
-        var actual = arguments[1];
-    }
-    else {
-        var comment = arguments[0] + "; ";
-        var pattern = arguments[1];
-        var actual = arguments[2];
+OptionLocatorFactory.prototype.OptionLocatorByIndex = function(index) {
+    this.index = Number(index);
+    if (isNaN(this.index) || this.index < 0) {
+        throw new SeleniumError("Illegal Index: " + index);
     }
 
-    if (this.matches(pattern, actual)) {
-        return;
-    }
+    this.findOption = function(element) {
+        if (element.options.length <= this.index) {
+            throw new SeleniumError("Index out of range.  Only " + element.options.length + " options available");
+        }
+        return element.options[this.index];
+    };
+
+    this.assertSelected = function(element) {
+        Assert.equals(this.index, element.selectedIndex);
+    };
+};
+
+/**
+ *  OptionLocator for options identified by their id.
+ */
+OptionLocatorFactory.prototype.OptionLocatorById = function(id) {
+    this.id = id;
+    this.idMatcher = new PatternMatcher(this.id);
+    this.findOption = function(element) {
+        for (var i = 0; i < element.options.length; i++) {
+            if (this.idMatcher.matches(element.options[i].id)) {
+                return element.options[i];
+            }
+        }
+        throw new SeleniumError("Option with id '" + this.id + "' not found");
+    };
 
-    var errorMessage = comment + 
-        "Actual value '" + actual + "' did not match '" + pattern + "'";
-    assert.fail(errorMessage);
-};
-
-Selenium.prototype.globToRegexp = function(glob) {
-    var pattern = glob;
-    pattern = pattern.replace(/([.^$+(){}[\]\\|])/g, "\\$1");
-    pattern = pattern.replace(/\?/g, ".");
-    pattern = pattern.replace(/\*/g, ".*");
-    return "^" + pattern + "$";
-};
-
-Selenium.prototype.matches = function(pattern, actual) {
-    var regexp = new RegExp(this.globToRegexp(pattern));
-    // Work around Konqueror bug when matching empty strings.
-    var testString = '' + actual;
-    return regexp.test(testString);
+    this.assertSelected = function(element) {
+        var selectedId = element.options[element.selectedIndex].id;
+        Assert.matches(this.id, selectedId);
+    };
 };
+
+


=== Products/Zelenium/selenium/selenium-browserbot.js 1.4 => 1.5 ===
--- Products/Zelenium/selenium/selenium-browserbot.js:1.4	Tue Jun  7 13:17:46 2005
+++ Products/Zelenium/selenium/selenium-browserbot.js	Wed Oct 12 06:38:23 2005
@@ -42,47 +42,51 @@
 var geckoResult = /^Mozilla\/5\.0 .*Gecko\/(\d{8}).*$/.exec(navigator.userAgent);
 var geckoVersion = geckoResult == null ? null : geckoResult[1];
 
-function createBrowserBot(frame) {
-    if (isIE) {
-        return new IEBrowserBot(frame);
-    }
-    else if (isKonqueror) {
-        return new KonquerorBrowserBot(frame);
-    }
-    else if (isSafari) {
-        return new SafariBrowserBot(frame);
-    }
-    else {
-        // Use mozilla by default
-        return new MozillaBrowserBot(frame);
-    }
-}
+BrowserBot = function(frame) {
+    this.frame = frame;
+    this.currentPage = null;
+    this.currentWindowName = null;
+
+    this.modalDialogTest = null;
+    this.recordedAlerts = new Array();
+    this.recordedConfirmations = new Array();
+    this.recordedPrompts = new Array();
+    this.openedWindows = {};
+    this.nextConfirmResult = true;
+    this.nextPromptResult = '';
+    this.newPageLoaded = false;
 
-function createPageBot(windowObject) {
+    var self = this;
+    this.recordPageLoad = function() {
+        LOG.debug("Page load detected, location=" + self.getCurrentWindow().location);
+        self.currentPage = null;
+        self.newPageLoaded = true;
+    };
+
+    this.isNewPageLoaded = function() {
+        return self.newPageLoaded;
+    };
+};
+
+BrowserBot.createForFrame = function(frame) {
+    var browserbot;
     if (isIE) {
-        return new IEPageBot(windowObject);
+        browserbot = new IEBrowserBot(frame);
     }
     else if (isKonqueror) {
-        return new KonquerorPageBot(windowObject);
+        browserbot = new KonquerorBrowserBot(frame);
     }
     else if (isSafari) {
-        return new SafariPageBot(windowObject);
+        browserbot = new SafariBrowserBot(frame);
     }
     else {
         // Use mozilla by default
-        return new MozillaPageBot(windowObject);
+        browserbot = new MozillaBrowserBot(frame);
     }
-}
 
-BrowserBot = function(frame) {
-    this.frame = frame;
-    this.currentPage = null;
-    this.currentWindowName = null;
-
-    this.modalDialogTest = null;
-    this.recordedAlerts = new Array();
-    this.recordedConfirmations = new Array();
-    this.nextConfirmResult = true;
+    // Modify the test IFrame so that page loads are detected.
+    addLoadListener(browserbot.getFrame(), browserbot.recordPageLoad);
+    return browserbot;
 };
 
 BrowserBot.prototype.doModalDialogTest = function(test) {
@@ -93,6 +97,10 @@
     this.nextConfirmResult = false;
 };
 
+BrowserBot.prototype.setNextPromptResult = function(result) {
+    this.nextPromptResult = result;
+};
+
 BrowserBot.prototype.hasAlerts = function() {
     return (this.recordedAlerts.length > 0) ;
 };
@@ -109,6 +117,13 @@
     return this.recordedConfirmations.shift();
 };
 
+BrowserBot.prototype.hasPrompts = function() {
+    return (this.recordedPrompts.length > 0) ;
+};
+
+BrowserBot.prototype.getNextPrompt = function() {
+    return this.recordedPrompts.shift();
+};
 
 BrowserBot.prototype.getFrame = function() {
     return this.frame;
@@ -126,21 +141,25 @@
     }
 };
 
-BrowserBot.prototype.openLocation = function(target, onloadCallback) {
+BrowserBot.prototype.openLocation = function(target) {
     // We're moving to a new page - clear the current one
     this.currentPage = null;
-    // Window doesn't fire onload event when setting src to the current value,
-    // so we set it to blank first.
-    this.getFrame().src = "about:blank";
-    this.getFrame().src = target;
+    this.newPageLoaded = false;
+
+    this.setIFrameLocation(this.getFrame(), target);
+};
+
+BrowserBot.prototype.setIFrameLocation = function(iframe, location) {
+    iframe.src = location;
 };
 
 BrowserBot.prototype.getCurrentPage = function() {
     if (this.currentPage == null) {
         var testWindow = this.getCurrentWindow();
         this.modifyWindowToRecordPopUpDialogs(testWindow, this);
-        this.modifyWindowToClearPageCache(testWindow, this);
-        this.currentPage = createPageBot(testWindow);
+        this.modifySeparateTestWindowToDetectPageLoads(testWindow);
+        this.currentPage = PageBot.createForWindow(testWindow);
+        this.newPageLoaded = false;
     }
 
     return this.currentPage;
@@ -157,30 +176,69 @@
         browserBot.nextConfirmResult = true;
         return result;
     };
-};
 
-BrowserBot.prototype.modifyWindowToClearPageCache = function(windowToModify, browserBot) {
-    var clearCachedPage = function() {
-        LOG.debug("UNLOAD: clearCachedPage()");
-        browserbot.currentPage = null;
+    windowToModify.prompt = function(message) {
+        browserBot.recordedPrompts.push(message);
+        var result = !browserBot.nextConfirmResult ? null : browserBot.nextPromptResult;
+        browserBot.nextConfirmResult = true;
+        browserBot.nextPromptResult = '';
+        return result;
     };
 
-    if (window.addEventListener) {
-        windowToModify.addEventListener("unload", clearCachedPage, true);
-    } else if (window.attachEvent) {
-        windowToModify.attachEvent("onunload", clearCachedPage);
+    // Keep a reference to all popup windows by name
+    // note that in IE the "windowName" argument must be a valid javascript identifier, it seems.
+    var originalOpen = windowToModify.open;
+    windowToModify.open = function(url, windowName, windowFeatures, replaceFlag) {
+        var openedWindow = originalOpen(url, windowName, windowFeatures, replaceFlag);
+        selenium.browserbot.openedWindows[windowName] = openedWindow;
+        return openedWindow;
+    };
+};
+
+/**
+ * The main IFrame has a single, long-lived onload handler that clears
+ * Browserbot.currentPage and sets the "newPageLoaded" flag. For separate
+ * windows, we need to attach a handler each time. This uses the
+ * "callOnWindowPageTransition" mechanism, which is implemented differently
+ * for different browsers.
+ */
+BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(windowToModify) {
+    if (this.currentWindowName != null) {
+        this.callOnWindowPageTransition(this.recordPageLoad, windowToModify);
     }
 };
 
+/**
+ * Call the supplied function when a the current page unloads and a new one loads.
+ * This is done with an "unload" handler which attaches a "load" handler.
+ */
+BrowserBot.prototype.callOnWindowPageTransition = function(loadFunction, windowObject) {
+    var attachLoadListener = function() {
+        if (windowObject && !windowObject.closed) {
+            addLoadListener(windowObject, loadFunction);
+        }
+    };
+
+    var unloadFunction = function() {
+        window.setTimeout(attachLoadListener, 0);
+    };
+    addUnloadListener(windowObject, unloadFunction);
+};
+
 BrowserBot.prototype.getContentWindow = function() {
     return this.getFrame().contentWindow || frames[this.getFrame().id];
 };
 
 BrowserBot.prototype.getTargetWindow = function(windowName) {
-    var evalString = "this.getContentWindow().window." + windowName;
-    var targetWindow = eval(evalString);
+    LOG.debug("getTargetWindow(" + windowName + ")");
+    // First look in the map of opened windows
+    var targetWindow = this.openedWindows[windowName];
     if (!targetWindow) {
-        throw new Error("Window does not exist");
+        var evalString = "this.getContentWindow().window." + windowName;
+        targetWindow = eval(evalString);
+    }
+    if (!targetWindow) {
+        throw new SeleniumError("Window does not exist");
     }
     return targetWindow;
 };
@@ -193,44 +251,6 @@
     return testWindow;
 };
 
-BrowserBot.prototype.callOnNextPageLoad = function(onloadCallback) {
-    if (this.currentWindowName == null) {
-        this.callOnFramePageTransition(onloadCallback, this.getFrame());
-    }
-    else {
-        this.callOnWindowPageTransition(onloadCallback, this.getCurrentWindow());
-    }
-};
-
-BrowserBot.prototype.callOnFramePageTransition = function(loadFunction, frameObject) {
-    try {
-        addLoadListener(frameObject, loadFunction);
-    } catch (e) {
-        LOG.debug("Got on error adding LoadListener in BrowserBot.prototype.callOnFramePageTransition." +
-                  "This occurs on the second and all subsequent calls in Safari");
-    }
-};
-
-BrowserBot.prototype.callOnWindowPageTransition = function(loadFunction, windowObject) {
-    var unloadFunction = function() {
-        window.setTimeout(function() {addLoadListener(windowObject, loadFunction);}, 0);
-    };
-    addUnloadListener(windowObject, unloadFunction);
-};
-
-/**
- * Handle the initial page load in a new popup window.
- * TODO - something like this should allow us to wait for a new popup window - currently need to pause...
- */
-//function callOnWindowInitialLoad(loadFunction, windowObject) {
-//    if (!(isSafari || isKonqueror)) {
-//        addLoadListener(windowObject, loadFunction);
-//    }
-//    else {
-//        this.pollForLoad(loadFunction, windowObject, windowObject.document);
-//    }
-//}
-
 function MozillaBrowserBot(frame) {
     BrowserBot.call(this, frame);
 }
@@ -241,10 +261,24 @@
 }
 KonquerorBrowserBot.prototype = new BrowserBot;
 
+KonquerorBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
+    // Window doesn't fire onload event when setting src to the current value,
+    // so we set it to blank first.
+    iframe.src = "about:blank";
+    iframe.src = location;
+};
+
+/**
+ * Call the supplied function when a the current page unloads and a new one loads.
+ * This is done by polling continuously until the document changes and is fully loaded.
+ */
 KonquerorBrowserBot.prototype.callOnWindowPageTransition = function(loadFunction, windowObject) {
     // Since the unload event doesn't fire in Safari 1.3, we start polling immediately
     // This works in Konqueror as well
-    this.pollForLoad(loadFunction, windowObject, windowObject.document);
+    if (windowObject && !windowObject.closed) {
+        LOG.debug("Starting pollForLoad");
+        this.pollForLoad(loadFunction, windowObject, windowObject.document);
+    }
 };
 
 /**
@@ -254,16 +288,21 @@
  * is different from the original one.
  */
 KonquerorBrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument) {
+    if (windowObject.closed) {
+        return;
+    }
+
     var sameDoc = (originalDocument === windowObject.document);
     var rs = windowObject.document.readyState;
 
     if (!sameDoc && rs == 'complete') {
-        LOG.debug("poll: " + rs + " (" + sameDoc + ")");
+        LOG.debug("pollForLoad complete: " + rs + " (" + sameDoc + ")");
         loadFunction();
         return;
     }
     var self = this;
-    window.setTimeout(function() {self.pollForLoad(loadFunction, windowObject, originalDocument);}, 100);
+    LOG.debug("pollForLoad continue");
+    window.setTimeout(function() {self.pollForLoad(loadFunction, windowObject, originalDocument);}, 500);
 };
 
 function SafariBrowserBot(frame) {
@@ -271,15 +310,7 @@
 }
 SafariBrowserBot.prototype = new BrowserBot;
 
-/**
- * Since Safari 1.3 doesn't trigger unload, we clear cached page as soon as
- * we know that we're expecting a new page.
- */
-SafariBrowserBot.prototype.callOnNextPageLoad = function(onloadCallback) {
-    this.currentPage = null;
-    BrowserBot.prototype.callOnNextPageLoad.call(this, onloadCallback);
-};
-
+SafariBrowserBot.prototype.setIFrameLocation = KonquerorBrowserBot.prototype.setIFrameLocation;
 SafariBrowserBot.prototype.callOnWindowPageTransition = KonquerorBrowserBot.prototype.callOnWindowPageTransition;
 SafariBrowserBot.prototype.pollForLoad = KonquerorBrowserBot.prototype.pollForLoad;
 
@@ -287,6 +318,8 @@
     BrowserBot.call(this, frame);
 }
 IEBrowserBot.prototype = new BrowserBot;
+IEBrowserBot.prototype.callOnWindowPageTransition = KonquerorBrowserBot.prototype.callOnWindowPageTransition;
+IEBrowserBot.prototype.pollForLoad = KonquerorBrowserBot.prototype.pollForLoad;
 
 IEBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
     BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
@@ -341,7 +374,7 @@
     if (pageWindow) {
         this.currentWindow = pageWindow;
         this.currentDocument = pageWindow.document;
-        this.location = pageWindow.location.pathname;
+        this.location = pageWindow.location;
         this.title = function() {return this.currentDocument.title;};
     }
 
@@ -368,7 +401,7 @@
     this.findElementBy = function(locatorType, locator, inDocument) {
         var locatorFunction = this.locationStrategies[locatorType];
         if (! locatorFunction) {
-            throw new Error("Unrecognised locator type: '" + locatorType + "'");
+            throw new SeleniumError("Unrecognised locator type: '" + locatorType + "'");
         }
         return locatorFunction.call(this, locator, inDocument);
     };
@@ -377,13 +410,33 @@
      * The implicit locator, that is used when no prefix is supplied.
      */
     this.locationStrategies['implicit'] = function(locator, inDocument) {
-        return this.locateElementByIdentifier(locator, inDocument)
-               || this.locateElementByDomTraversal(locator, inDocument)
-               || this.locateElementByXPath(locator, inDocument);
+        if (locator.startsWith('//')) {
+            return this.locateElementByXPath(locator, inDocument);
+        } 
+        if (locator.startsWith('document.')) {
+            return this.locateElementByDomTraversal(locator, inDocument);
+        }
+        return this.locateElementByIdentifier(locator, inDocument);
     };
     
 };
 
+PageBot.createForWindow = function(windowObject) {
+    if (isIE) {
+        return new IEPageBot(windowObject);
+    }
+    else if (isKonqueror) {
+        return new KonquerorPageBot(windowObject);
+    }
+    else if (isSafari) {
+        return new SafariPageBot(windowObject);
+    }
+    else {
+        // Use mozilla by default
+        return new MozillaPageBot(windowObject);
+    }
+};
+
 MozillaPageBot = function(pageWindow) {
     PageBot.call(this, pageWindow);
 };
@@ -412,9 +465,9 @@
     var locatorString = locator;
     
     // If there is a locator prefix, use the specified strategy
-    var result = locator.match(/^([a-z]+)=(.+)/);
+    var result = locator.match(/^([A-Za-z]+)=(.+)/);
     if (result) {
-        locatorType = result[1];
+        locatorType = result[1].toLowerCase();
         locatorString = result[2];
     }
 
@@ -430,7 +483,7 @@
     }
 
     // Element was not found by any locator function.
-    throw new Error("Element " + locator + " not found");
+    throw new SeleniumError("Element " + locator + " not found");
 };
 
 /**
@@ -505,9 +558,6 @@
 * begin with "//".
 */
 PageBot.prototype.locateElementByXPath = function(xpath, inDocument) {
-    if (xpath.slice(0,2) != "//") {
-        return null;
-    }
 
     // Trim any trailing "/": not valid xpath, and remains from attribute
     // locator.
@@ -603,7 +653,7 @@
     var links = inDocument.getElementsByTagName('a');
     for (var i = 0; i < links.length; i++) {
         var element = links[i];
-        if (getText(element) == linkText) {
+        if (PatternMatcher.matches(linkText, getText(element))) {
             return element;
         }
     }
@@ -636,32 +686,22 @@
 };
 
 /*
-* Selects the first option with a matching label from the select box element
-* provided. If no matching element is found, nothing happens.
+* Select the specified option and trigger the relevant events of the element.
 */
-PageBot.prototype.selectOptionWithLabel = function(element, stringValue) {
+PageBot.prototype.selectOption = function(element, option) {
     triggerEvent(element, 'focus', false);
-    for (var i = 0; i < element.options.length; i++) {
-        var option = element.options[i];
-        if (option.text == stringValue) {
-            if (!option.selected) {
-                option.selected = true;
-                triggerEvent(element, 'change', true);
-            }
-            triggerEvent(element, 'blur', false);
-            return;
-        }
+    if (!option.selected) {
+        option.selected = true;
+        triggerEvent(element, 'change', true);
     }
-    throw new Error("Option with label '" + stringValue + "' not found");
+    triggerEvent(element, 'blur', false);
 };
 
 PageBot.prototype.replaceText = function(element, stringValue) {
     triggerEvent(element, 'focus', false);
     triggerEvent(element, 'select', true);
     element.value=stringValue;
-    if (isIE || isKonqueror || isSafari) {
-        triggerEvent(element, 'change', true);
-    }
+    triggerEvent(element, 'change', true);
     triggerEvent(element, 'blur', false);
 };
 
@@ -780,17 +820,40 @@
     triggerEvent(element, 'focus', false);
 
     var wasChecked = element.checked;
+
+    // Set a flag that records if the page will unload - this isn't always accurate, because
+    // <a href="javascript:alert('foo'):"> triggers the onbeforeunload event, even thought the page won't unload
+    var pageUnloading = false;
+    var pageUnloadDetector = function() {pageUnloading = true;};
+    this.currentWindow.attachEvent("onbeforeunload", pageUnloadDetector);
+
     element.click();
 
-    if (this.windowClosed()) {
-        return;
-    }
-    // Onchange event is not triggered automatically in IE.
-    if (isDefined(element.checked) && wasChecked != element.checked) {
-        triggerEvent(element, 'change', true);
-    }
+    // If the page is going to unload - still attempt to fire any subsequent events.
+    // However, we can't guarantee that the page won't unload half way through, so we need to handle exceptions.
+    try {
+        this.currentWindow.detachEvent("onbeforeunload", pageUnloadDetector);
 
-    triggerEvent(element, 'blur', false);
+        if (this.windowClosed()) {
+            return;
+        }
+
+        // Onchange event is not triggered automatically in IE.
+        if (isDefined(element.checked) && wasChecked != element.checked) {
+            triggerEvent(element, 'change', true);
+        }
+
+        triggerEvent(element, 'blur', false);
+    }
+    catch (e) {
+        // If the page is unloading, we may get a "Permission denied" or "Unspecified error".
+        // Just ignore it, because the document may have unloaded.
+        if (pageUnloading) {
+            LOG.warn("Caught exception when firing events on unloading page: " + e.message);
+            return;
+        }
+        throw e;
+    }
 };
 
 PageBot.prototype.windowClosed = function(element) {
@@ -860,4 +923,8 @@
 
 PageBot.prototype.goForward = function() {
     this.currentWindow.history.forward();
+};
+
+PageBot.prototype.close = function() {
+    this.currentWindow.close();
 };


=== Products/Zelenium/selenium/selenium-commandhandlers.js 1.3 => 1.4 ===
--- Products/Zelenium/selenium/selenium-commandhandlers.js:1.3	Fri May  6 07:46:12 2005
+++ Products/Zelenium/selenium/selenium-commandhandlers.js	Wed Oct 12 06:38:23 2005
@@ -35,6 +35,11 @@
         var handler = new AssertHandler(assertion, haltOnFailure);
         this.asserts[name] = handler;
     };
+    
+    self.registerAssertUsingMatcherHandler = function(name, matcherHandler, haltOnFailure) {
+        var handler = new AssertUsingMatcherHandler(matcherHandler, haltOnFailure);
+        this.asserts[name] = handler;
+    }
 
     this.getCommandHandler = function(name) {
         return this.actions[name] || this.accessors[name] || this.asserts[name] || null;
@@ -50,7 +55,7 @@
         for (var functionName in commandObject) {
             var result = /^do([A-Z].+)$/.exec(functionName);
             if (result != null) {
-                var actionName = toCamelCase(result[1]);
+                var actionName = result[1].lcfirst();
 
                 // Register the action without the wait flag.
                 var action = commandObject[functionName];
@@ -80,20 +85,85 @@
         }
     };
 
+    // Given an accessor function, return a function that matches against the command.value
+    // the value returned by the accessor when applied to a command.target.
+    // Used by commands that take a target and a value (e.g. assertValue | target | value)
+    this.createMatcherHandlerFromSingleArgAccessor = function(accessor) {
+        return function(seleniumApi, command) {
+            var accessorResult = accessor.call(seleniumApi, command.target);
+            if (PatternMatcher.matches(command.value, accessorResult)) {
+                return new MatcherHandlerResult(true, "Actual value '" + accessorResult + "' did match '" + command.value + "'");
+            } else {
+                return new MatcherHandlerResult(false, "Actual value '" + accessorResult + "' did not match '" + command.value + "'");
+            }
+        };
+    };
+    
+    // Given an accessor function, return a function that matches against the command.target
+    // the value returned by the (no-arg) accessor returns a value that matches against the command.target
+    // Used by commands that only take a target (e.g. assertTitle | target | &nbsp;)
+    this.createMatcherHandlerFromNoArgAccessor = function(accessor) {
+        return function(seleniumApi, command) {
+            var accessorResult = accessor.call(seleniumApi);
+            if (PatternMatcher.matches(command.target, accessorResult)) {
+                return new MatcherHandlerResult(true, "Actual value '" + accessorResult + "' did match '" + command.target + "'");
+            } else {
+                return new MatcherHandlerResult(false, "Actual value '" + accessorResult + "' did not match '" + command.target + "'");
+            }
+        };
+    };
+    
+    // Given a matcherHandler function, return a function that returns the same result
+    // as the matcherHandler, but with the result negated.
+    // Used to create assertNot and verifyNot commands (and soon hopefully waitForNot commands).
+    this.createMatcherHandlerNegator = function(matcherHandler) {
+        return function(seleniumApi, command) {
+            var result = matcherHandler(seleniumApi, command);
+            result.didMatch = ! result.didMatch;
+            return result;
+        };
+    };
+    
+
+    // Methods of the form getFoo(target) result in commands:
+    // getFoo, assertFoo, verifyFoo, assertNotFoo, verifyNotFoo
     var registerAllAccessors = function(commandObject) {
         for (var functionName in commandObject) {
-            if (/^get[A-Z].+$/.exec(functionName) != null) {
+            var match = /^get([A-Z].+)$/.exec(functionName);
+            if (match != null) {
                 var accessor = commandObject[functionName];
+                var baseName = match[1];
                 self.registerAccessor(functionName, accessor);
+                
+                if (accessor.length > 1) {
+                    continue;
+                }
+                var matcherHandler;
+                if (accessor.length == 1) {
+                    matcherHandler = self.createMatcherHandlerFromSingleArgAccessor(accessor);
+                } else {
+                    matcherHandler = self.createMatcherHandlerFromNoArgAccessor(accessor);
+                }
+                // Register an assert with the "assert" prefix, and halt on failure.
+                self.registerAssertUsingMatcherHandler("assert" + baseName, matcherHandler, true);
+                // Register a verify with the "verify" prefix, and do not halt on failure.
+                self.registerAssertUsingMatcherHandler("verify" + baseName, matcherHandler, false);
+                
+                var negativeMatcherHandler = self.createMatcherHandlerNegator(matcherHandler);
+                // Register an assertNot with the "assertNot" prefix, and halt on failure.
+                self.registerAssertUsingMatcherHandler("assertNot"+baseName, negativeMatcherHandler, true);
+                // Register a verifyNot with the "verifyNot" prefix, and do not halt on failure.
+                self.registerAssertUsingMatcherHandler("verifyNot"+baseName, negativeMatcherHandler, false);
             }
         }
     };
-
-    function toCamelCase(aString) {
-        return aString.charAt(0).toLowerCase() + aString.substr(1);
-    }
+    
 }
 
+function MatcherHandlerResult(didMatch, message) {
+    this.didMatch = didMatch;
+    this.message = message;
+}
 
 // NOTE: The CommandHandler is effectively an abstract base for ActionHandler,
 //      AccessorHandler and AssertHandler.
@@ -115,10 +185,10 @@
 ActionHandler.prototype = new CommandHandler;
 ActionHandler.prototype.execute = function(seleniumApi, command) {
     if ( seleniumApi.browserbot.hasAlerts() ) {
-        throw new Error("There was an unexpected Alert! [" + seleniumApi.browserbot.getNextAlert() + "]");
+        throw new SeleniumCommandError("There was an unexpected Alert! [" + seleniumApi.browserbot.getNextAlert() + "]");
     }
     if ( seleniumApi.browserbot.hasConfirmations() ) {
-        throw new Error("There was an unexpected Confirmation! [" + seleniumApi.browserbot.getNextConfirmation() + "]");
+        throw new SeleniumCommandError("There was an unexpected Confirmation! [" + seleniumApi.browserbot.getNextConfirmation() + "]");
     }
     var processState = this.executor.call(seleniumApi, command.target, command.value);
     // If the handler didn't return a wait flag, check to see if the
@@ -140,14 +210,19 @@
     return result;
 };
 
-function AssertHandler(assertion, haltOnFailure) {
+/**
+ * Abstract handler for assertions and verifications.
+ * Subclasses need to override executeAssertion() which in turn
+ * should throw an AssertFailedError if the assertion is to fail. 
+ */
+function AbstractAssertHandler(assertion, haltOnFailure) {
     CommandHandler.call(this, "assert", haltOnFailure || false, assertion);
 }
-AssertHandler.prototype = new CommandHandler;
-AssertHandler.prototype.execute = function(seleniumApi, command) {
+AbstractAssertHandler.prototype = new CommandHandler;
+AbstractAssertHandler.prototype.execute = function(seleniumApi, command) {
     var result = new CommandResult();
     try {
-        var processState = this.executor.call(seleniumApi, command.target, command.value);
+        this.executeAssertion(seleniumApi, command);
         result.passed = true;
     } catch (e) {
         // If this is not a AssertionFailedError, or we should haltOnFailure, rethrow.
@@ -155,8 +230,7 @@
             throw e;
         }
         if (this.haltOnFailure) {
-            var error = new Error(e.failureMessage);
-            error.message = e.failureMessage;
+            var error = new SeleniumCommandError(e.failureMessage);
             throw error;
         }
         result.failed = true;
@@ -165,6 +239,32 @@
     return result;
 };
 
+/**
+ * Simple assertion handler whose command is expected to do the actual assertion.
+ */
+function AssertHandler(assertion, haltOnFailure) {
+    CommandHandler.call(this, "assert", haltOnFailure || false, assertion);
+};
+AssertHandler.prototype = new AbstractAssertHandler;
+AssertHandler.prototype.executeAssertion = function(seleniumApi, command) {
+        this.executor.call(seleniumApi, command.target, command.value);
+};
+
+/**
+ * Assertion handler whose command is expected to be a matcher-handler
+ */
+function AssertUsingMatcherHandler(matcherHandler, haltOnFailure) {
+    CommandHandler.call(this, "assert", haltOnFailure || false, matcherHandler);
+};
+AssertUsingMatcherHandler.prototype = new AbstractAssertHandler;
+AssertUsingMatcherHandler.prototype.executeAssertion = function(seleniumApi, command) {
+        var matcherResult = this.executor(seleniumApi, command);
+        if (!matcherResult.didMatch) {
+            Assert.fail(matcherResult.message);
+        }
+};
+
+
 function CommandResult(processState) {
     this.processState = processState;
     this.result = "OK";
@@ -174,4 +274,13 @@
     this.command = command;
     this.target = target;
     this.value = value;
-}
\ No newline at end of file
+}
+
+// TODO: dkemp - This is the same as SeleniumError as defined in selenium-browserbot.js
+// I defined a new error simply to avoid creating a new dependency.
+// Need to revisit to avoid this duplication.
+function SeleniumCommandError(message) {
+    var error = new Error(message);
+    error.isSeleniumError = true;
+    return error;
+};


=== Products/Zelenium/selenium/selenium-domviewer.js 1.1 => 1.2 ===
--- Products/Zelenium/selenium/selenium-domviewer.js:1.1	Mon May  2 23:48:16 2005
+++ Products/Zelenium/selenium/selenium-domviewer.js	Wed Oct 12 06:38:23 2005
@@ -1,193 +1,188 @@
 var HIDDEN="hidden";
 var LEVEL = "level";
-var PLUS_SRC="dom-images/butplus.gif"
-var MIN_SRC="dom-images/butmin.gif"
+var PLUS_SRC="dom-images/butplus.gif";
+var MIN_SRC="dom-images/butmin.gif";
 var newRoot;
 var maxColumns=1;
 
-function loadDomViewer(){
-	if(window.opener != null){
-		//Open in a new window. 
-		// Start with specified root object.
-		// It should be defined as a "newRoot" variable 
-		// in the tested page. Could be any element in 
-		// the DOM structure of the page that opened it.
-		// Default to document if no object is specified.
-		newRoot  = window.opener.newRoot;
-	
-		if (newRoot==null)
-			newRoot = window.opener.document;
-	
-		document.writeln(displayDOM(newRoot));
-		document.close();
-	}else{
-		newRoot = parent.document.all['myiframe'].contentWindow.document;
-		newRoot.writeln(displayDOM(newRoot));
-		newRoot.close();
-	}
+function loadDomViewer() {
+    // See if the rootDocument variable has been set on this window.
+    var rootDocument = window.rootDocument;
+
+    // If not look to the opener for an explicity rootDocument variable, otherwise, use the opener document
+    if (!rootDocument && window.opener) {
+        rootDocument = window.opener.rootDocument || window.opener.document;
+    }
+
+    if (rootDocument) {
+        document.body.innerHTML = displayDOM(rootDocument);
+    }
+    else {
+        document.body.innerHTML = "<b>Must specify rootDocument for window. This can be done by setting the rootDocument variable on this window, or on the opener window for a popup window.</b>";
+    }
 }
 
 
 function displayDOM(root){
-	var str = "<html><head><title>DOM Viewerr</title><script type='text/javascript' src='domviewer.js'><!-- comments --> </script><link href='dom-styles/default.css' rel='stylesheet' type='text/css' /></head><body>";
-	str+="<table>";
-	str += treeTraversal(root,0);
-	// to make table columns work well.
-	str += "<tr>";
-	for (var i=0; i < maxColumns; i++) {
-	    str+= "<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>";
-	}
-	str += "</tr>";
-	str += "</table></body></html>";
-	return str;
+    var str = "";
+    str+="<table>";
+    str += treeTraversal(root,0);
+    // to make table columns work well.
+    str += "<tr>";
+    for (var i=0; i < maxColumns; i++) {
+        str+= "<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>";
+    }
+    str += "</tr>";
+    str += "</table>";
+    return str;
 }
 
 function checkForChildren(element){
-	if(!element.hasChildNodes())
-		return false;
-	
-	var nodes = element.childNodes;
-	var size = nodes.length;
-	var count=0;
-	
-	for(var i=0; i< size; i++){
-		var node = nodes.item(i);
-		//if(node.toString()=="[object Text]"){
-		//this is equalent to the above
-		//but will work with more browsers
-		if(node.nodeType!=1){
-			count++;
-		}
-	}
-	
-	if(count == size)
-		return false;
-	else
-		return true;
+    if(!element.hasChildNodes())
+        return false;
+    
+    var nodes = element.childNodes;
+    var size = nodes.length;
+    var count=0;
+    
+    for(var i=0; i< size; i++){
+        var node = nodes.item(i);
+        //if(node.toString()=="[object Text]"){
+        //this is equalent to the above
+        //but will work with more browsers
+        if(node.nodeType!=1){
+            count++;
+        }
+    }
+    
+    if(count == size)
+        return false;
+    else
+        return true;
 }
 
 function treeTraversal(root, level){
-	var str = "";
-	var nodes= null;
-	var size = null;
-	//it is supposed to show the last node, 
-	//but the last node is always nodeText type
-	//and we don't show it
-	if(!root.hasChildNodes())
-		return "";//displayNode(root,level,false);
-
-	nodes = root.childNodes;
-	size = nodes.length;
-
-	for(var i=0; i< size; i++){
-		var element = nodes.item(i);
-		//if the node is textNode, don't display
-		if(element.nodeType==1){
-			str+= displayNode(element,level,checkForChildren(element));
-			str+=treeTraversal(element, level+1);	
-		}
-	}
-	return str;
+    var str = "";
+    var nodes= null;
+    var size = null;
+    //it is supposed to show the last node, 
+    //but the last node is always nodeText type
+    //and we don't show it
+    if(!root.hasChildNodes())
+        return "";//displayNode(root,level,false);
+    
+    nodes = root.childNodes;
+    size = nodes.length;
+
+    for(var i=0; i< size; i++){
+        var element = nodes.item(i);
+        //if the node is textNode, don't display
+        if(element.nodeType==1){
+            str+= displayNode(element,level,checkForChildren(element));
+            str+=treeTraversal(element, level+1);	
+        }
+    }
+    return str;
 }
 
-
 function displayNode(element, level, isLink){
-        nodeContent = getNodeContent(element);
-        columns = Math.round((nodeContent.length / 12) + 0.5);
-        if (columns + level > maxColumns) {
-            maxColumns = columns + level;
-        }
-		var str ="<tr class='"+LEVEL+level+"'>";
-		for (var i=0; i < level; i++)
-			str+= "<td> </td>";
-		str+="<td colspan='"+ columns +"' class='box"+" boxlevel"+level+"' >";
-		if(isLink){
-			str+='<a onclick="hide(this);" href="javascript:void(this);">';
-			str+='<img src="'+MIN_SRC+'" />';
-		}
-        str += nodeContent;
-		if(isLink)
-			str+="</a></td></tr>";
-		return str;
+    nodeContent = getNodeContent(element);
+    columns = Math.round((nodeContent.length / 12) + 0.5);
+    if (columns + level > maxColumns) {
+        maxColumns = columns + level;
+    }
+    var str ="<tr class='"+LEVEL+level+"'>";
+    for (var i=0; i < level; i++)
+        str+= "<td> </td>";
+    str+="<td colspan='"+ columns +"' class='box"+" boxlevel"+level+"' >";
+    if(isLink){
+        str+='<a onclick="hide(this);return false;" href="javascript:void();">';
+        str+='<img src="'+MIN_SRC+'" />';
+    }
+    str += nodeContent;
+    if(isLink)
+        str+="</a></td></tr>";
+    return str;
 }
 
 function getNodeContent(element) {
-        str = "";
-		id ="";
-		if (element.id != null && element.id != "") {
-		    id = " ID(" + element.id +")";
-		}
-		name ="";
-		if (element.name != null && element.name != "") {
-		    name = " NAME(" + element.name + ")";
-		}
-		value ="";
-		if (element.value != null && element.value != "") {
-		    value = " VALUE(" + element.value + ")";
-		}
-		href ="";
-		if (element.href != null && element.href != "") {
-		    href = " HREF(" + element.href + ")";
-		}
-		text ="";
-		if (element.text != null && element.text != "" && element.text != "undefined") {
-		    text = " #TEXT(" + trim(element.text) +")";
-		}
-		str+=" <b>"+ element.nodeName + id + name + value + href + text + "</b>";	
-		return str;
+    str = "";
+    id ="";
+    if (element.id != null && element.id != "") {
+        id = " ID(" + element.id +")";
+    }
+    name ="";
+    if (element.name != null && element.name != "") {
+        name = " NAME(" + element.name + ")";
+    }
+    value ="";
+    if (element.value != null && element.value != "") {
+        value = " VALUE(" + element.value + ")";
+    }
+    href ="";
+    if (element.href != null && element.href != "") {
+        href = " HREF(" + element.href + ")";
+    }
+    text ="";
+    if (element.text != null && element.text != "" && element.text != "undefined") {
+        text = " #TEXT(" + trim(element.text) +")";
+    }
+    str+=" <b>"+ element.nodeName + id + name + value + href + text + "</b>";	
+    return str;
 
 }
 
 function trim(val) {
-        val2 = val.substring(0,20) + "                   ";
-        var spaceChr = String.fromCharCode(32);
-        var length = val2.length;
-        var retVal = "";
-        var ix = length -1;
-
-        while(ix > -1){
-            if(val2.charAt(ix) == spaceChr) {
-            } else {
-                retVal = val2.substring(0, ix +1);
-                break;
-            }
-            ix = ix-1;
-        }
-        if (val.length > 20) {
-            retVal += "...";
+    val2 = val.substring(0,20) + "                   ";
+    var spaceChr = String.fromCharCode(32);
+    var length = val2.length;
+    var retVal = "";
+    var ix = length -1;
+
+    while(ix > -1){
+        if(val2.charAt(ix) == spaceChr) {
+        } else {
+            retVal = val2.substring(0, ix +1);
+            break;
         }
-        return retVal;
+        ix = ix-1;
+    }
+    if (val.length > 20) {
+        retVal += "...";
+    }
+    return retVal;
 }
 
 function hide(hlink){
-	var isHidden = false;
-	var image = hlink.firstChild;
-	if(image.src.toString().indexOf(MIN_SRC)!=-1){
-		image.src=PLUS_SRC;
-		isHidden=true;
-	}else{
-		image.src=MIN_SRC;
-	}
-	var rowObj= hlink.parentNode.parentNode;
-	var rowLevel = parseInt(rowObj.className.substring(LEVEL.length));
-	
-	var sibling = rowObj.nextSibling;
-	var siblingLevel = sibling.className.substring(LEVEL.length);
-	if(siblingLevel.indexOf(HIDDEN)!=-1){
-		siblingLevel = siblingLevel.substring(0,siblingLevel.length - HIDDEN.length-1);
-	}
-	siblingLevel=parseInt(siblingLevel);
-	while(sibling!=null && rowLevel<siblingLevel){
-		if(isHidden){
-			sibling.className += " "+ HIDDEN;
-		}else if(!isHidden && sibling.className.indexOf(HIDDEN)!=-1){
-			var str = sibling.className;
-			sibling.className=str.substring(0, str.length - HIDDEN.length-1);
-		}
-		sibling = sibling.nextSibling;
-		siblingLevel = parseInt(sibling.className.substring(LEVEL.length));
-	}
+    var isHidden = false;
+    var image = hlink.firstChild;
+    if(image.src.toString().indexOf(MIN_SRC)!=-1){
+        image.src=PLUS_SRC;
+        isHidden=true;
+    }else{
+        image.src=MIN_SRC;
+    }
+    var rowObj= hlink.parentNode.parentNode;
+    var rowLevel = parseInt(rowObj.className.substring(LEVEL.length));
+	
+    var sibling = rowObj.nextSibling;
+    var siblingLevel = sibling.className.substring(LEVEL.length);
+    if(siblingLevel.indexOf(HIDDEN)!=-1){
+        siblingLevel = siblingLevel.substring(0,siblingLevel.length - HIDDEN.length-1);
+    }
+    siblingLevel=parseInt(siblingLevel);
+    while(sibling!=null && rowLevel<siblingLevel){
+        if(isHidden){
+            sibling.className += " "+ HIDDEN;
+        }else if(!isHidden && sibling.className.indexOf(HIDDEN)!=-1){
+            var str = sibling.className;
+            sibling.className=str.substring(0, str.length - HIDDEN.length-1);
+        }
+        sibling = sibling.nextSibling;
+        siblingLevel = parseInt(sibling.className.substring(LEVEL.length));
+    }
 }
 
-
-
+function LOG(message) {
+    window.opener.LOG.warn(message);
+}


=== Products/Zelenium/selenium/selenium-executionloop.js 1.4 => 1.5 ===
--- Products/Zelenium/selenium/selenium-executionloop.js:1.4	Tue Jun  7 13:17:46 2005
+++ Products/Zelenium/selenium/selenium-executionloop.js	Wed Oct 12 06:38:23 2005
@@ -23,8 +23,6 @@
 function TestLoop(commandFactory) {
     this.commandFactory = commandFactory;
 
-    var self = this;
-
     this.start = function() {
         selenium.reset();
         this.continueCurrentTest();
@@ -50,10 +48,9 @@
 
         if (!command) return TEST_FINISHED;
 
-        // Make the current row blue
         this.commandStarted(command);
 
-        LOG.debug("Executing: |" + command.command + " | " + command.target + " | " + command.value + " |");
+        LOG.info("Executing: |" + command.command + " | " + command.target + " | " + command.value + " |");
 
         var result;
         try {
@@ -67,27 +64,21 @@
 
             result = handler.execute(selenium, command);
         } catch (e) {
-            LOG.error(e);
-            // TODO: only throw typed errors from commands so that we can perform better error handling
-            // to differentiate between expected command errors and unexpected javascript errors.
-            if (e instanceof TypeError) {
-                // Not a command error.
-                throw e;
-            }
-            this.commandError(e.message);
+            this.handleCommandError(e);
             return TEST_FINISHED;
         }
 
-        // Record the result so that we can continue the execution using window.setTimeout()
+        // Record the result so that we can continue the execution using
+        // window.setTimeout()
         this.lastCommandResult = result;
         if (result.processState == SELENIUM_PROCESS_WAIT) {
-            // Since we're waiting for page to reload, we can't continue command execution
-            // directly, we need use a page load listener.
-
-            // TODO there is a potential race condition by attaching a load listener after
-            // the command has completed execution.
-            selenium.callOnNextPageLoad(function() {eval("testLoop.continueCommandExecutionWithDelay()");});
+            this.waitForCondition = function() {
+                return selenium.browserbot.isNewPageLoaded();
+            };
+        }
 
+        if (this.waitForCondition) {
+            this.pollUntilConditionIsTrue();
         } else {
             // Continue processing
             this.continueCommandExecutionWithDelay();
@@ -97,9 +88,38 @@
         return TEST_CONTINUE;
     };
 
+    this.handleCommandError = function(e) {
+       if (!e.isSeleniumError) {
+            LOG.exception(e);
+            var msg = "Selenium failure. Please report to [email protected], with details from the logs at the base of the page.";
+            if (e.message) {
+               msg += "  The error message is: " + e.message;
+            }
+            this.commandError(msg);
+        } else {
+            LOG.error(e.message);
+            this.commandError(e.message);
+        }
+    };
+
+    /**
+     * Busy wait for waitForCondition() to become true, and then continue
+     * command execution.
+     */
+    this.pollUntilConditionIsTrue = function () {
+        if (this.waitForCondition()) {
+            this.waitForCondition = null;
+            this.continueCommandExecutionWithDelay();
+        } else {
+            window.setTimeout("testLoop.pollUntilConditionIsTrue()", 10);
+        }
+    };
+
+
     /**
-    * Continues the command execution, after waiting for the specified delay.
-    */
+     * Continue the command execution, after waiting for the specified
+     * delay.
+     */
     this.continueCommandExecutionWithDelay = function() {
         // Get the interval to use for this command execution, using the pauseInterval as
         // specified. Reset the pause interval, since it's a one-off thing.
@@ -108,7 +128,7 @@
 
         if (interval < 0) {
             // Enable the "next/continue" button
-            this.waitingForNext();
+            this.pause();
         }
         else {
             // Continue processing
@@ -117,8 +137,8 @@
     };
 
     /**
-    * Finishes the execution of the previous command, and continues the test
-    */
+     * Finish the execution of the previous command, and continue the test.
+     */
     this.finishCommandExecution = function() {
         this.commandComplete(this.lastCommandResult);
         this.continueCurrentTest();
@@ -142,15 +162,15 @@
 
 TestLoop.prototype.testComplete = noop;
 
-TestLoop.prototype.waitingForNext = noop;
+TestLoop.prototype.pause = noop;
 
 function noop() {
 
 };
 
 /**
- * A selenium command that tells selenium to expect a failure on the next command
- * execution. This command temporarily installs a new CommandFactory, that generates
+ * Tell Selenium to expect a failure on the next command execution. This
+ * command temporarily installs a CommandFactory that generates
  * CommandHandlers that expect a failure.
  */
 Selenium.prototype.assertFailureOnNext = function(message) {
@@ -165,8 +185,8 @@
 };
 
 /**
- * A selenium command that tells selenium to expect a failure on the next command
- * execution. This command temporarily installs a new CommandFactory, that generates
+ * Tell Selenium to expect an error on the next command execution. This
+ * command temporarily installs a CommandFactory that generates
  * CommandHandlers that expect a failure.
  */
 Selenium.prototype.assertErrorOnNext = function(message) {
@@ -193,7 +213,7 @@
                 result.failureMessage = "Command should have failed.";
             }
             else {
-                if (baseFailureMessage != expectedErrorMessage) {
+                if (! PatternMatcher.matches(expectedErrorMessage, baseFailureMessage)) {
                     result.failed = true;
                     result.failureMessage = "Expected failure message '" + expectedErrorMessage
                                             + "' but was '" + baseFailureMessage + "'";
@@ -216,7 +236,7 @@
         return null;
     }
     return baseResult.failureMessage;
- };
+};
 
 function executeCommandAndReturnErrorMessage(baseHandler, originalArguments) {
     try {
@@ -226,5 +246,5 @@
     catch (expected) {
         return expected.message;
     }
- };
+};
 


=== Products/Zelenium/selenium/selenium-fitrunner.js 1.5 => 1.6 ===
--- Products/Zelenium/selenium/selenium-fitrunner.js:1.5	Tue Jun  7 13:17:46 2005
+++ Products/Zelenium/selenium/selenium-fitrunner.js	Wed Oct 12 06:38:23 2005
@@ -103,8 +103,7 @@
 
 function loadSuiteFrame() {
     var testAppFrame = document.getElementById('myiframe');
-    browserbot = createBrowserBot(testAppFrame);
-    selenium = new Selenium(browserbot);
+    selenium = Selenium.createForFrame(testAppFrame);
     registerCommandHandlers();
 
     //set the runInterval if there is a queryParameter for it
@@ -334,10 +333,7 @@
         var testFrame = getTestFrame();
         addLoadListener(testFrame, startTest);
 
-        // Window doesn't fire onload event when setting src to the current value,
-        // so we set it to blank first.
-        testFrame.src = "about:blank";
-        testFrame.src = testLink.href;
+        selenium.browserbot.setIFrameLocation(testFrame, testLink.href);
     }
 }
 
@@ -491,7 +487,7 @@
     testLoop.commandComplete = commandComplete;
     testLoop.commandError = commandError;
     testLoop.testComplete = testComplete;
-    testLoop.waitingForNext = function() {
+    testLoop.pause = function() {
         document.getElementById('continueTest').disabled = false;
     };
     return testLoop;
@@ -519,16 +515,11 @@
 
 function scrollIntoView(element) {
     if (element.scrollIntoView) {
-        element.scrollIntoView();
+        element.scrollIntoView(false);
         return;
     }
-
-    // For Konqueror, we have to create a remove an element.
-    var anchor = element.ownerDocument.createElement("a");
-    anchor.innerHTML = "!CURSOR!";
-    element.appendChild(anchor, element);
-//    anchor.focus();
-    element.removeChild(anchor);
+    // TODO: work out how to scroll browsers that don't support
+    // scrollIntoView (like Konqueror)
 }
 
 function commandStarted() {
@@ -594,10 +585,13 @@
 }
 
 function getCellText(rowNumber, columnNumber) {
-    return getText(inputTableRows[rowNumber].cells[columnNumber]);
+    var cell = inputTableRows[rowNumber].cells[columnNumber];
+    if (! cell.cachedText) {
+        cell.cachedText = getText(cell);
+    }
+    return cell.cachedText;
 }
 
 Selenium.prototype.doPause = function(waitTime) {
-    selenium.callOnNextPageLoad(null);
     testLoop.pauseInterval = waitTime;
-};
\ No newline at end of file
+};


=== Products/Zelenium/selenium/selenium-logging.js 1.3 => 1.4 ===
--- Products/Zelenium/selenium/selenium-logging.js:1.3	Tue Jun  7 13:17:46 2005
+++ Products/Zelenium/selenium/selenium-logging.js	Wed Oct 12 06:38:23 2005
@@ -14,75 +14,75 @@
 *  limitations under the License.
 */
 
-var LEVEL_DEBUG = 0;
-var LEVEL_INFO = 1;
-var LEVEL_WARN = 2;
-var LEVEL_ERROR = 3;
-
-function Logger(logLevel) {
-    this.level = logLevel;
-    this.logConsole = document.getElementById('logging-console');
-    this.logList = document.getElementById('log-list');
-    this.hide();
+var Logger = function() {
+    this.logWindow = null;
 }
+Logger.prototype = {
 
-Logger.prototype.show = function() {
-   this.logConsole.style.display = "";
-};
-
-Logger.prototype.hide = function() {
-   this.logConsole.style.display = "none";
-};
-
-Logger.prototype.clear = function() {
-    while (this.logList.hasChildNodes()) {
-        this.logList.removeChild(this.logList.firstChild);
-    }
-};
+    getLogWindow: function() {
+        if (this.logWindow && this.logWindow.closed) {
+            this.logWindow = null;
+        }
+        return this.logWindow;
+    },
+    
+    openLogWindow: function() {
+        this.logWindow = window.open(
+            "SeleniumLog.html", "SeleniumLog",
+            "width=600,height=250,bottom=0,right=0,status,scrollbars,resizable"
+        );
+        return this.logWindow;
+    },
+    
+    show: function() {
+        if (! this.getLogWindow()) {
+            this.openLogWindow();
+        }
+    },
+
+    log: function(message, className) {
+        var logWindow = this.getLogWindow();
+        if (logWindow) {
+            if (logWindow.append) {
+                logWindow.append(message, className);
+            }
+        }
+    },
 
-Logger.prototype.debug = function(message) {
-    if (this.level <= LEVEL_DEBUG) {
+    debug: function(message) {
         this.log(message, "debug");
-    }
-};
+    },
 
-Logger.prototype.info = function(message) {
-    if (this.level <= LEVEL_INFO) {
+    info: function(message) {
         this.log(message, "info");
-    }
-};
+    },
 
-Logger.prototype.warn = function(message) {
-    if (this.level <= LEVEL_WARN) {
+    warn: function(message) {
         this.log(message, "warn");
-    }
-};
+    },
 
-Logger.prototype.error = function(message) {
-    if (this.level <= LEVEL_ERROR) {
+    error: function(message) {
         this.log(message, "error");
-    }
-};
+    },
 
-Logger.prototype.log = function(message, className) {
-    var loggingNode = document.createElement('li');
-    loggingNode.className = className;
-    loggingNode.appendChild(document.createTextNode(message));
+    exception: function(exception) {
+        var msg = "Unexpected Exception: " + describe(exception, ', ');
+        this.error(msg);
+    }
 
-    this.logList.appendChild(loggingNode);
-    this.show();
 };
 
+var LOG = new Logger();
+
 function noop() {};
 
-function DummyLogger() {
+var DummyLogger = function() {};
+DummyLogger.prototype = {
+    show: noop,
+    log: noop,
+    debug: noop,
+    info: noop,
+    warn: noop,
+    error: noop
 };
 
-DummyLogger.prototype.show = noop;
-DummyLogger.prototype.hide = noop;
-DummyLogger.prototype.clear = noop;
-DummyLogger.prototype.log = noop;
-DummyLogger.prototype.debug = noop;
-DummyLogger.prototype.info = noop;
-DummyLogger.prototype.warn = noop;
-DummyLogger.prototype.error = noop;
\ No newline at end of file


=== Products/Zelenium/selenium/selenium.css 1.2 => 1.3 ===
--- Products/Zelenium/selenium/selenium.css:1.2	Mon May  2 23:48:16 2005
+++ Products/Zelenium/selenium/selenium.css	Wed Oct 12 06:38:23 2005
@@ -67,28 +67,32 @@
 }
 
 #controlPanel {
-    padding: 5px;
+    padding: 0.5ex;
     background: #eee;
     overflow: auto;
+    font-size: 75%;
+    text-align: center;
 }
 
-#controlPanel table {
-    font-size: 75%;
+#controlPanel fieldset {
+    margin: 0.3ex;
+    padding: 0.3ex;
 }
 
-#controlPanel th, #controlPanel td {
-    border: 0;
+#controlPanel fieldset legend {
+    color: black;
 }
 
-#controls {
-    color: inherit;
-    width: 100%;
+#controlPanel button {
+    margin: 0.5ex;
 }
 
-#controls td {
-    margin: 1px;
-    padding: 1px;
-    text-align: center;
+#controlPanel table {
+    font-size: 100%;
+}
+
+#controlPanel th, #controlPanel td {
+    border: 0;
 }
 
 h1 {
@@ -117,9 +121,7 @@
 }
 
 #stats {
-    border-top: 1px solid #999;
-    border-bottom: 1px solid #999;
-    margin-top: 10px;
+    margin: 0.5em auto 0.5em auto;
 }
 
 #stats th, #stats td {
@@ -163,25 +165,47 @@
 }
 
 /*---( Logging Console )---*/
+
 #logging-console {
-   background: #FFF;
-   padding: 5px;
-   border: 1px solid #888;
-   font-size: 10px;
+    background: #fff;
+    font-size: 75%;
+}
+
+#logging-console #banner {
+    display: block;
+    width: 100%;
+    position: fixed;
+    top: 0;
+    background: #ddd;
+    border-bottom: 1px solid #666;
 }
 
-#logging-console h1 {
-   font-weight: bold;
+#logging-console #logLevelChooser {
+    float: right;
+    margin: 3px;
 }
 
 #logging-console ul {
-   list-style-type: none;
-   margin: 0px;
-   padding: 0px;
-   clear: both;
+    list-style-type: none;
+    margin: 0px;
+    margin-top: 3em;
+    padding-left: 5px;
 }
 
-#logging-console ul li.error {
-	font-weight: bold;
-	color: red;
-}
\ No newline at end of file
+#logging-console li {
+    margin: 2px;
+    border-top: 1px solid #ccc;
+}
+
+#logging-console li.error {
+    font-weight: bold;
+    color: red;
+}
+
+#logging-console li.warn {
+    color: red;
+}
+
+#logging-console li.debug {
+    color: green;
+}


=== Products/Zelenium/selenium/version.txt 1.1 => 1.2 ===
--- Products/Zelenium/selenium/version.txt:1.1	Tue Jun  7 13:17:47 2005
+++ Products/Zelenium/selenium/version.txt	Wed Oct 12 06:38:23 2005
@@ -1 +1 @@
-0.4.0
\ No newline at end of file
+0.6.0
\ No newline at end of file

_______________________________________________
Zope-CVS maillist  -  [email protected]
http://mail.zope.org/mailman/listinfo/zope-cvs

Zope CVS instructions: http://dev.zope.org/CVS
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.