[Zope-CVS] 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 jens at dataflake.org
Wed Oct 12 06:38:54 EDT 2005


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 selenium-devel at lists.public.thoughtworks.org, 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



More information about the Zope-CVS mailing list