Added selenium extensions, updated readme.txt
authorMartin Taal <martin.taal@openbravo.com>
Tue, 09 Aug 2011 11:02:13 +0200
changeset 13 dcddf071b072
parent 12 64951e667255
child 14 acb15813d12f
Added selenium extensions, updated readme.txt
readme.txt
selenium/selenium-ide-example.png
selenium/user-extensions-ide.js
selenium/user-extensions.js
selenium/user-guide.html
--- a/readme.txt	Mon Aug 01 13:36:25 2011 +0200
+++ b/readme.txt	Tue Aug 09 11:02:13 2011 +0200
@@ -15,6 +15,7 @@
 smartclientSDK/source/copyright.txt --> sources
 smartclientRuntime/isomorphic -->isomorphic (note see remove not-needed skins step below)
 smartclientSDK/isomorphic/system/reference --> isomorphic/system/reference
+smartclientSDK/tools/selenium --> selenium
 
 2) the copy of the isomorphic directory will also copy many not needed skins, 
 remove all skins directories except for the following ones (from isomorphic/skins):
@@ -59,4 +60,4 @@
 isomorphic/system/reference/inlineExamples/serverExamples
 isomorphic/system/reference/inlineExamples/dataIntegration
 
-Now build Openbravo.
\ No newline at end of file
+Now build Openbravo.
Binary file selenium/selenium-ide-example.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/selenium/user-extensions-ide.js	Tue Aug 09 11:02:13 2011 +0200
@@ -0,0 +1,379 @@
+/*
+ * Isomorphic SmartClient
+ * Version 8.0
+ * Copyright(c) 1998 and beyond Isomorphic Software, Inc. All rights reserved.
+ * "SmartClient" is a trademark of Isomorphic Software, Inc.
+ *
+ * licensing@smartclient.com
+ *
+ * http://smartclient.com/license
+ */
+
+function findScLocator(element, autWindow) {
+    //the element selenium passes is a "safe" XPCNativeWrappers wrapper of the real element. XPCNativeWrappers are used to protect
+    //the chrome code working with content objects and there's no way to access the real "underlying" element object.
+    //example of an element passed here is [object XPCNativeWrapper [object HTMLInputElement]]
+
+    //see https://developer.mozilla.org/en/wrappedJSObject
+    //https://developer.mozilla.org/en/XPCNativeWrapper
+    if (autWindow == null) autWindow = this.window;
+    if (autWindow.wrappedJSObject) {
+        autWindow = autWindow.wrappedJSObject;
+    }
+
+    if(hasSC(autWindow)) {
+        var e;
+        try {
+            var e = convertToLiveElement(element, autWindow);
+            
+            // Second parameter tells the autoTest subsystem *not* to return
+            // a locator if the element relies on native event handling and we
+            // dont' have a direct locator to pick it up. EG: Don't returns 
+            // the locator for a canvas handle when a click occurred on a link embedded
+            // in a canvas.
+            var scLocator = autWindow.isc.AutoTest.getLocator(e, true);
+
+            if(scLocator != null && scLocator != "") {
+                return "scLocator=" + scLocator;
+            } else {
+                return null;
+            }
+        } catch(ex) {
+            alert('caught error ' + ex + ' for element ' + e + ' with id' + e.id);
+            return null;
+        }
+    } else {
+        return null;
+    }
+}
+
+
+function convertToLiveElement(element, autWindow) {
+    var id = element.id,
+        nullID;
+    if (id == null || id === undefined || id == '') {
+        //assign an id to the element if one does not exist so that it can be located by SC
+        id = "sel_" + autWindow.isc.ClassFactory.getNextGlobalID();
+        element.id = id;
+        nullID = true;
+    }
+
+    //The sc classes are loaded in wrappedJSObject window, and not the window reference held by Locators.
+    //see https://developer.mozilla.org/en/wrappedJSObject
+    var e = autWindow.document.getElementById(id);
+    
+    // reset ID to null - this way if we *don't* get a SmartClient locator
+    // normal page locator strategy will work
+    if (nullID) {
+        element.id = null;
+    }
+    return e;
+}
+
+LocatorBuilders.add('sc', findScLocator);
+// add SC Locator to the head of the priority of builders.
+LocatorBuilders.order = ['sc', 'id', 'link', 'name', 'dom:name', 'xpath:link', 'xpath:img', 'xpath:attributes', 'xpath:href', 'dom:index', 'xpath:position'];
+
+//override the default clickLocator so that duplicate click events are not recorded
+Recorder.removeEventHandler('clickLocator');
+Recorder.addEventHandler('clickLocator', 'click', function(event) {
+        if (event.button == 0) {
+
+        // === start sc specific code ===
+        var autWindow = this.window;
+        if (autWindow.wrappedJSObject) {
+            autWindow = autWindow.wrappedJSObject;
+        }
+        if(hasSC(autWindow)) {
+            var element = this.clickedElement;
+          
+            var scLocator = findScLocator(element, autWindow);
+        
+            //if an scLocator is found, then this event will be captured by the scClickLocator mousedown event recorder
+            // 'return' so that we don't get duplicate records
+            if(scLocator != null) {
+                return;
+            }
+        }
+        // === end sc specific code ===
+            
+        var clickable = this.findClickableElement(event.target);
+        if (clickable) {
+            // prepend any required mouseovers. These are defined as
+            // handlers that set the "mouseoverLocator" attribute of the
+            // interacted element to the locator that is to be used for the
+            // mouseover command. For example:
+            //
+            // Recorder.addEventHandler('mouseoverLocator', 'mouseover', function(event) {
+            //     var target = event.target;
+            //     if (target.id == 'mmlink0') {
+            //         this.mouseoverLocator = 'img' + target._itemRef;
+            //     }
+            //     else if (target.id.match(/^mmlink\d+$/)) {
+            //         this.mouseoverLocator = 'lnk' + target._itemRef;
+            //     }
+            // }, { alwaysRecord: true, capture: true });
+            //
+            if (this.mouseoverLocator) {
+                this.record('mouseOver', this.mouseoverLocator, '');
+                delete this.mouseoverLocator;
+            }
+            this.record("click", this.findLocators(event.target), '');
+        } else {
+            var target = event.target;
+            this.callIfMeaningfulEvent(function() {
+                    this.record("click", this.findLocators(target), '');
+                });
+        }
+    }
+	}, { capture: true });
+
+
+Recorder.addEventHandler('scMouseDownLocator', 'mousedown', function(event) {
+    if (event.button == 0) {
+        var autWindow = this.window;
+        if (autWindow.wrappedJSObject) {
+            autWindow = autWindow.wrappedJSObject;
+        }
+        if(hasSC(autWindow)) {
+            var element = this.clickedElement;
+            //if the element clicked is a form text input based field or textarea do not record it as a Selenium SC click
+            //as we want the default behaviour Selenium IDE not registering click events on these to be used. This is particularly
+            //important since the Selenium "type" command for text / textarea based input fields is registered only on blur and recording
+            //clicks on these fields results in out of order replays. The Selenium "type" command does not require the field to be focused first.
+            if(element.tagName && ((element.tagName.toLowerCase() == "input" &&
+                                       (element.type == "text" || element.type == "password" || element.type == "file")) ||
+                                  element.tagName.toLowerCase() == "textarea")) {
+                return;
+            }
+            var canvas = autWindow.isc.AutoTest.locateCanvasFromDOMElement(element);
+            var scLocator = findScLocator(element, autWindow);
+            setSCContextValue(autWindow, "mouseDownTarget", canvas);
+            setSCContextValue(autWindow, "mouseDownLocator", scLocator);
+            setSCContextValue(autWindow, "mouseDownCoords",
+                                [autWindow.isc.EH.getX(), autWindow.isc.EH.getY()]);
+        }
+    }
+}, { capture: true });
+
+Recorder.addEventHandler('scMouseUpLocator', 'mouseup', function(event) {
+    if (event.button == 0) {
+        var autWindow = this.window;
+        if (autWindow.wrappedJSObject) {
+            autWindow = autWindow.wrappedJSObject;
+        }
+        if(hasSC(autWindow)) {
+            var element = event.target;
+            
+            //if the element clicked is a form text input based field or textarea do not record it as a Selenium SC click
+            //as we want the default behaviour Selenium IDE not registering click events on these to be used. This is particularly
+            //important since the Selenium "type" command for text / textarea based input fields is registered only on blur and recording
+            //clicks on these fields results in out of order replays. The Selenium "type" command does not require the field to be focused first.
+            if(element.tagName && ((element.tagName.toLowerCase() == "input" &&
+                                       (element.type == "text" || element.type == "password" || element.type == "file")) ||
+                                  element.tagName.toLowerCase() == "textarea")) {
+                return;
+            }
+            var scLocator = findScLocator(element, autWindow);
+            var mouseDownLocator = getSCContextValue(autWindow, "mouseDownLocator");
+                
+            // if mouseDown occurred over an sc-significant element, but mouseup didn't,
+            // we may still need to fire drag stop handlers.
+            if (scLocator == null && mouseDownLocator == null) return;
+            
+            // Are we finishing a drag operation?
+            if (mouseDownLocator && autWindow.isc.EH.dragging) {
+                // 2 possibilities: 
+                // dragAndDropToObject - records a source and target object and at playback
+                // coords will be determined from those elements
+                // dragAndDrop - records a source and a px offset - at playback time we'll
+                // move by that number of pixels.
+                
+                // If the current target matches the mouse-down target, or we have no SC element
+                // at the current location, treat as absolute coordinates
+                var dragToObject = (scLocator != null && scLocator != mouseDownLocator);
+                
+                // If the current target != the mouse down target and both are valid SC elements
+                // its still worth discounting cases where we obviously aren't attempting to do
+                // a drag and drop over the target object.
+                // This is important to catch as in these cases absolute pixel offsets are likely
+                // more appropriate than the center of whatever element we ended up over.
+                if (dragToObject && 
+                    (autWindow.isc.EH.dragTarget == null ||
+                     autWindow.isc.EH.dragTarget.canDrop == null ||
+                     autWindow.isc.EH.dragOperation == autWindow.isc.EH.DRAG_RESIZE)) 
+                {
+                    dragToObject = false;
+                }
+                
+                // mouse over new target - record as "dragAndDropToObject". Coords will be
+                // derived from the live DOM elements
+                if (dragToObject) {
+                    this.record(
+                        "dragAndDropToObject", 
+                        mouseDownLocator,
+                        scLocator
+                    );
+                // mouse is over drag target (or no SC-significant target) - 
+                // record as simple "dragAndDrop" and record offset based on mouse down vs
+                // mouse up position.
+                // This will handle drag reposition etc.
+                } else {
+                    var startCoords = getSCContextValue(autWindow, "mouseDownCoords");
+                    var offsetX = autWindow.isc.EH.getX() - startCoords[0],
+                        offsetY = autWindow.isc.EH.getY() - startCoords[1];
+                        
+                    this.record(
+                        "dragAndDrop",
+                        mouseDownLocator,
+                        (offsetX > 0 ? "+" : "") + offsetX + "," +
+                        (offsetY > 0 ? "+" : "") + offsetY
+                    );
+                }
+            // Not a drag/drop interaction - perform click operation.
+            } else {
+                
+                // don't fire click if mouseDown locator or mouse up locator are unset.
+                if (mouseDownLocator != null && scLocator != null) {
+
+                    // If this event is within the double click interval since the last SC click we
+                    // recorded, record as a double-click event
+                    var EH = autWindow.isc.EH,
+                        time = autWindow.isc.timeStamp(),
+                        withinDoubleClickInterval = false;
+                    if (EH.lastClickTime != null) {
+                        var completeTime = EH._lastClickCompleteTime || EH.$k9;
+                        withinDoubleClickInterval = 
+                         ((completeTime - EH.lastClickTime) < EH.DOUBLE_CLICK_DELAY) ?
+                            time - EH.lastClickTime < EH.DOUBLE_CLICK_DELAY :
+                            ((time - completeTime) < 100);
+                    }
+    
+                    if (withinDoubleClickInterval) {
+                        this.record("secondClick", scLocator, '');
+                    } else {
+                        this.record("click", scLocator, '');
+                    }
+                }
+            }
+            
+            // clear out mouseDown context vars
+            setSCContextValue(autWindow, "mouseDownTarget", null);
+            setSCContextValue(autWindow, "mouseDownLocator", null);
+            setSCContextValue(autWindow, "mouseDownCoords", null);
+        }
+    }
+}, { capture: true });
+
+Recorder.addEventHandler('scContextMenuLocator', 'mousedown', function(event) {
+    if (event.button == 2) {
+        var autWindow = this.window;
+        if (autWindow.wrappedJSObject) {
+            autWindow = autWindow.wrappedJSObject;
+        }
+        if(hasSC(autWindow)) {
+            var element = this.clickedElement;
+           
+            var scLocator = findScLocator(element,autWindow);
+            
+            if(scLocator != null) {
+                this.record("contextMenu", scLocator, '');
+                delete this.click;
+            }
+        }
+    }
+}, { capture: true });
+
+CommandBuilders.add('action', function(window) {
+    var autWindow = window;
+    if (autWindow.wrappedJSObject) {
+        autWindow = autWindow.wrappedJSObject;
+    }
+
+    if(hasSC(autWindow)) {
+        var element = this.getRecorder(window).clickedElement;
+      
+        var scLocator = findScLocator(element,autWindow);
+        
+        if(scLocator != null) {
+            return {
+                command: "click",
+                target: scLocator
+            };
+        } else {
+            return {
+                command: "click",
+                disabled : true
+            };
+        }
+    } else {
+        return {
+                command: "click",
+                disabled : true
+            };
+    }
+});
+
+
+CommandBuilders.add('accessor', function(window) {
+    var autWindow = window;
+    if (autWindow.wrappedJSObject) {
+        autWindow = autWindow.wrappedJSObject;
+    }
+    var result = { accessor: "table", disabled: true };
+    if(hasSC(autWindow)) {
+        var element = this.getRecorder(window).clickedElement;
+
+        if (!element) return result;
+        
+        var e = convertToLiveElement(element, autWindow);
+        
+        var listGrid = autWindow.isc.AutoTest.locateCanvasFromDOMElement(e);
+
+        if(listGrid == null || !listGrid.isA("GridRenderer")) return result;
+
+        var cellXY = listGrid.getCellFromDomElement(e);
+        if(cellXY == null) return result;
+        var row = cellXY[0];
+        var col = cellXY[1];
+        //the locator can return a GridBody
+        if(listGrid.grid) {
+            listGrid = listGrid.grid;
+        }
+
+        var record = listGrid.getRecord(Number(row));
+
+        var value = listGrid.getCellValue(record, row, col);
+
+        result.target = 'scLocator=' + listGrid.getLocator() + '.' + row + '.' + col;
+        result.value = value;
+        result.disabled = false;
+        return result;
+    }
+
+    return result;
+});
+
+function hasSC(autWindow) {
+    var hasSC = !(autWindow.isc === undefined);
+    if(hasSC && autWindow.isc.AutoTest === undefined) {
+        //this should never be the case with newer SC versions as AutoTest is part of core
+        autWindow.isc.loadAutoTest();
+    }
+    if(hasSC && autWindow.isc.Canvas.getCanvasLocatorFallbackPath === undefined) {
+        autWindow.isc.ApplyAutoTestMethods();
+    }
+    return hasSC;
+}
+
+function setSCContextValue(autWindow, fieldName, value) {
+    if (!hasSC(autWindow)) return;
+    if (autWindow.isc.SeleniumContext == null) autWindow.isc.SeleniumContext = {};
+    autWindow.isc.SeleniumContext[fieldName] = value;
+}
+
+function getSCContextValue(autWindow, fieldName) {
+    if (!hasSC(autWindow)) return;
+    if (autWindow.isc.SeleniumContext == null) return null;
+    return autWindow.isc.SeleniumContext[fieldName];
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/selenium/user-extensions.js	Tue Aug 09 11:02:13 2011 +0200
@@ -0,0 +1,559 @@
+/*
+ * Isomorphic SmartClient
+ * Version 8.0
+ * Copyright(c) 1998 and beyond Isomorphic Software, Inc. All rights reserved.
+ * "SmartClient" is a trademark of Isomorphic Software, Inc.
+ *
+ * licensing@smartclient.com
+ *
+ * http://smartclient.com/license
+ */
+
+// Warn if we're being loaded twice as things will be very likely to not work in this case
+if (Selenium.prototype.sc_userExtensions_loaded) {
+    LOG.warn("SmartClient user-extensions.js is being loaded more than once - " +
+        "check for this file being included multiple times in the Selenium core extensions.");
+}
+Selenium.prototype.sc_userExtensions_loaded = true;
+
+PageBot.prototype.getAutWindow = function() {
+    var autWindow = this.browserbot.getUserWindow();
+    
+    // if the user window is the dev console, redirect to the actual app window
+    if (autWindow.targetWindow != null) autWindow = autWindow.targetWindow;
+    // If SmartClient isn't loaded on the target window, just bail.
+    if (autWindow.isc == null) return;
+
+    if (autWindow.isc.AutoTest === undefined) {
+        //this should never be the case with newer SC versions as AutoTest is part of core
+        autWindow.isc.loadAutoTest();
+    } else if (autWindow.isc.Canvas.getCanvasLocatorFallbackPath === undefined) {
+        autWindow.isc.ApplyAutoTestMethods();
+    }
+    autWindow.isc.EventHandler.useNativeEventTime = false;
+    return autWindow;
+};
+
+Selenium.prototype.getAutWindow = PageBot.prototype.getAutWindow;
+
+// All locateElementBy* methods are added as locator-strategies.
+PageBot.prototype.locateElementByScID = function(idLocator, inDocument, inWindow) {
+    LOG.debug("Locate Element with SC ID=" + idLocator + ", inDocument=" + inDocument + ", inWindow=" + inWindow.location.href);
+    var autWindow = this.getAutWindow();
+
+    idLocator = idLocator.replace(/'/g, "");
+    idLocator = idLocator.replace(/"/g, "");
+
+    var scObj = autWindow[idLocator];
+    if(scObj == null || scObj === undefined) {
+        LOG.info("Unable to locate SC element with ID " + idLocator);
+        return null;
+    } else {
+        LOG.debug('Found SC object ' + scObj);
+    }
+
+    var scLocator = "//" + scObj.getClassName() + "[ID=\"" + idLocator + "\"]";
+    LOG.debug("Using SC Locator " + scLocator);
+    var elem = autWindow.isc.AutoTest.getElement(scLocator);
+    LOG.info("Returning element :: " + elem + " for SC locator " + scLocator);
+    return elem;
+};
+
+PageBot.prototype.locateElementByScLocator = function(scLocator, inDocument, inWindow) {
+    LOG.debug("Locate Element with SC Locator=" + scLocator + ", inDocument=" + inDocument + 
+        ", inWindow=" + inWindow.location.href);
+
+    //support scLocators with the direct ID of the widget specified
+    if(scLocator.indexOf("/") == -1) {
+        LOG.debug("Using ID locator");
+        return this.locateElementByScID(scLocator, inDocument, inWindow);
+    }
+    var autWindow = this.getAutWindow();
+    var elem = autWindow.isc.AutoTest.getElement(scLocator);
+    LOG.debug("Returning element :: " + elem + " for SC locator " + scLocator);
+
+    return elem;
+};
+
+Selenium.prototype.orig_doType = Selenium.prototype.doType;
+
+Selenium.prototype.doType = function(locator, value) {
+    /**
+   * Sets the value of an input field, as though you typed it in.
+   *
+   * <p>Can also be used to set the value of combo boxes, check boxes, etc. In these cases,
+   * value should be the value of the option selected, not the visible text.</p>
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @param value the value to type
+   */
+   Selenium.prototype.orig_doType.call(this, locator, value);
+
+    //Selenium doesn't actually simulate a user typing into an input box so for SmartClient FormItem's manually register the change.
+    if(this.hasSC()) {
+        var autWindow = this.getAutWindow(),
+            formItem,
+            scLocator = locator;
+            
+        if (scLocator.indexOf("scLocator=") != -1) {
+            scLocator = scLocator.substring("scLocator=".length);
+            formItem = autWindow.isc.AutoTest.getLocatorFormItem(scLocator);
+        } else if (scLocator.indexOf("scID=") != -1) {
+            var ID = scLocator.substring("scID=".length);
+            formItem = autWindow[ID];
+        }
+        if(formItem != null) {
+            formItem.updateValue();
+        }
+    }
+};
+
+Selenium.prototype.orig_doClick = Selenium.prototype.doClick;
+
+Selenium.prototype.doClick = function(locator, eventParams)
+{
+    LOG.info("Located in doScClick : " + locator);
+    var isSCLocator = locator && 
+                        ((locator.substring(0, "scLocator".length) == "scLocator") ||
+                         (locator.substring(0, "scID".length) == "scID")),
+        element = this.page().findElement(locator);
+    if(this.hasSC() && isSCLocator) {
+        var autWindow = this.getAutWindow();
+      
+        var canvas = autWindow.isc.AutoTest.locateCanvasFromDOMElement(element);
+        //if the clicked element does not correspond to a SmartClient widget, then perform the default SmartClient click operation
+        if(canvas == null) {
+            Selenium.prototype.orig_doClick.call(this, locator, eventParams);
+            return;
+        }
+        LOG.debug("Located canvas " + canvas + " for locator " + locator);
+
+        var coords = this.getSCLocatorCoords(autWindow, locator);
+        if (coords == null) return;
+        var clientX = coords[0];
+        var clientY = coords[1];
+  
+        // Ensure we explicitly indicate whether this is a second click within double-click delay
+        // This makes SC logic fire double click on the second click, regardless of the
+        // playback timing
+        if (autWindow.isc.EH._isSecondClick == null) {
+            autWindow.isc.EH._isSecondClick = false;
+        }
+      
+        LOG.debug("clientX = " + clientX + ", clientY=" + clientY);
+
+        //fire a sequence of mousedown, mouseup and click operation to trigger a SmartClient click event
+        this.browserbot.triggerMouseEvent(element, "mouseover", true, clientX, clientY);
+        this.browserbot.triggerMouseEvent(element, "mousedown", true, clientX, clientY);
+        this.browserbot.triggerMouseEvent(element, "mouseup", true, clientX, clientY);
+        this.browserbot.clickElement(element);
+        
+        autWindow.isc.EH._isSecondClick = null;
+    } else {
+        Selenium.prototype.orig_doClick.call(this, locator, eventParams);
+    }
+};
+
+// Special secondClick event - second half of a double-click
+Selenium.prototype.doSecondClick = function (locator, eventParams) 
+{
+    if (!this.hasSC()) return this.doClick(locator, eventParams);
+    
+    var autWindow = this.getAutWindow();
+    autWindow.isc.EH._isSecondClick = true;
+    this.doClick(locator, eventParams);
+    autWindow.isc.EH._isSecondClick = null;
+}
+
+// ensure playback of mouseDown / mouseUp on SmartClient locators behaves as expected.
+Selenium.prototype.orig_doMouseDown = Selenium.prototype.doMouseDown;
+
+Selenium.prototype.doMouseDown = function(locator, eventParams)
+{
+    LOG.info("Located in doMouseDown : " + locator);
+    var isSCLocator = locator && 
+                        ((locator.substring(0, "scLocator".length) == "scLocator") ||
+                         (locator.substring(0, "scID".length) == "scID")),
+        element = this.page().findElement(locator);
+    if(this.hasSC() && isSCLocator) {
+        var autWindow = this.getAutWindow();
+      
+        var canvas = autWindow.isc.AutoTest.locateCanvasFromDOMElement(element);
+        //if the clicked element does not correspond to a SmartClient widget, then perform the default SmartClient click operation
+        if(canvas == null) {
+            Selenium.prototype.orig_doMouseDown.call(this, locator, eventParams);
+            return;
+        }
+        LOG.debug("Located canvas " + canvas + " for locator " + locator);
+
+        var coords = this.getSCLocatorCoords(autWindow, locator);
+        if (coords == null) return;
+        var clientX = coords[0];
+        var clientY = coords[1];
+  
+      
+        LOG.debug("clientX = " + clientX + ", clientY=" + clientY);
+
+        // fire mouseover / mouseDown
+        // This will set up for SmartClient click, doubleclick or drag event as appropriate
+        this.browserbot.triggerMouseEvent(element, "mouseover", true, clientX, clientY);
+        this.browserbot.triggerMouseEvent(element, "mousedown", true, clientX, clientY);
+    } else {
+        Selenium.prototype.orig_doMouseDown.call(this, locator, eventParams);
+    }
+};
+
+
+Selenium.prototype.orig_doMouseUp = Selenium.prototype.doMouseUp;
+
+Selenium.prototype.doMouseUp = function(locator, eventParams)
+{
+    LOG.info("Located in doMouseUp : " + locator);
+    var isSCLocator = locator && 
+                        ((locator.substring(0, "scLocator".length) == "scLocator") ||
+                         (locator.substring(0, "scID".length) == "scID")),
+        element = this.page().findElement(locator);
+    if(this.hasSC() && isSCLocator) {
+        var autWindow = this.getAutWindow();
+      
+        var canvas = autWindow.isc.AutoTest.locateCanvasFromDOMElement(element);
+        //if the clicked element does not correspond to a SmartClient widget, then perform the default SmartClient click operation
+        if(canvas == null) {
+            Selenium.prototype.orig_doMouseUp.call(this, locator, eventParams);
+            return;
+        }
+        LOG.debug("Located canvas " + canvas + " for locator " + locator);
+
+        var coords = this.getSCLocatorCoords(autWindow, locator);
+        if (coords == null) return;
+        
+        var clientX = coords[0];
+        var clientY = coords[1];
+  
+        LOG.debug("clientX = " + clientX + ", clientY=" + clientY);
+
+        // fire mouseUp and click to trigger a SmartClient click event
+        // We should have already fired mouseDown
+        this.browserbot.triggerMouseEvent(element, "mouseup", true, clientX, clientY);
+        this.browserbot.clickElement(element);
+        
+    } else {
+        Selenium.prototype.orig_doMouseUp.call(this, locator, eventParams);
+    }
+};
+
+
+Selenium.prototype.orig_doDoubleClick = Selenium.prototype.doDoubleClick;
+
+Selenium.prototype.doDoubleClick = function(locator, eventParams)
+{
+    LOG.info("Locator in doDoubleClick : " + locator);
+    var element = this.page().findElement(locator);
+    
+    if(this.hasSC()) {
+        var autWindow = this.getAutWindow();
+        var canvas = autWindow.isc.AutoTest.locateCanvasFromDOMElement(element);
+        //if the clicked element does not correspond to a SmartClient widget, then perform the default SmartClient doubleclick operation
+        if(canvas == null) {
+            Selenium.prototype.orig_doDoubleClick.call(this, locator, eventParams);
+            return;
+        }
+        LOG.debug("Located canvas " + canvas + " for locator " + locator);
+        var coords = this.getSCLocatorCoords(autWindow, locator);
+        if (coords == null) return;
+        var clientX = coords[0];
+        var clientY = coords[1];
+
+        LOG.debug("clientX = " + clientX + ", clientY=" + clientY);
+
+        //fire a sequence of events to trigger a SmartClient doubleclick event
+        this.browserbot.triggerMouseEvent(element, "mouseover", true, clientX, clientY);
+        this.browserbot.triggerMouseEvent(element, "mousedown", true, clientX, clientY);
+        this.browserbot.triggerMouseEvent(element, "mouseup", true, clientX, clientY);
+        this.browserbot.clickElement(element);
+        this.browserbot.triggerMouseEvent(element, "mousedown", true, clientX, clientY);
+        this.browserbot.triggerMouseEvent(element, "mouseup", true, clientX, clientY);
+        this.browserbot.clickElement(element);
+
+    } else {
+        Selenium.prototype.orig_doDoubleClick.call(this, locator, eventParams);
+    }
+};
+
+Selenium.prototype.orig_doContextMenu = Selenium.prototype.doContextMenu;
+
+Selenium.prototype.doContextMenu = function(locator, eventParams)
+{
+    LOG.info("Locator in doContextMenu : " + locator);
+    var element = this.page().findElement(locator);
+    if(this.hasSC()) {
+        var autWindow = this.getAutWindow();
+        var canvas = autWindow.isc.AutoTest.locateCanvasFromDOMElement(element);
+        if(canvas == null) {
+            Selenium.prototype.orig_doContextMenu.call(this, locator, eventParams);
+            return;
+        }
+        LOG.debug("Located canvas " + canvas + " for locator " + locator);
+
+        var coords = this.getSCLocatorCoords(autWindow, scLocator);
+        var clientX = coords[0];
+        var clientY = coords[1];
+
+        LOG.debug("clientX = " + clientX + ", clientY=" + clientY);
+        this.browserbot.triggerMouseEvent(element, "contextmenu", true, clientX, clientY);
+    } else {
+        Selenium.prototype.orig_doContextMenu.call(this, locator, eventParams);
+    }
+};
+
+
+Selenium.prototype.hasSC = function() {
+    var autWindow = this.browserbot.getUserWindow();
+    if (autWindow.targetWindow != null) autWindow = autWindow.targetWindow;
+    return !(autWindow.isc === undefined);
+};
+
+
+Selenium.prototype.orig_getTable = Selenium.prototype.getTable;
+
+Selenium.prototype.getTable = function(tableCellAddress) {
+/**
+ * Gets the text from a cell of a table. The cellAddress syntax
+ * tableLocator.row.column, where row and column start at 0.
+ *
+ * @param tableCellAddress a cell address, e.g. "foo.1.4"
+ * @return string the text from the specified cell
+ */
+
+    if(this.hasSC()) {
+        // This regular expression matches "tableName.row.column"
+        // For example, "mytable.3.4"
+        var pattern = /(.*)\.(\d+)\.(\d+)/;
+
+        if(!pattern.test(tableCellAddress)) {
+            throw new SeleniumError("Invalid target format. Correct format is tableLocator.rowNum.columnNum");
+        }
+
+        var pieces = tableCellAddress.match(pattern);
+
+        var tableName = pieces[1];
+        var row = pieces[2];
+        var col = pieces[3];
+
+        var element = this.browserbot.findElement(tableName);
+
+        var autWindow = this.getAutWindow();
+
+        var listGrid = autWindow.isc.AutoTest.locateCanvasFromDOMElement(element);
+        if(listGrid == null) {
+            return Selenium.prototype.orig_getTable.call(this, tableCellAddress);
+        }
+        //the locator can return a GridBody
+        if(listGrid.grid) {
+            listGrid = listGrid.grid;
+        }
+        
+        LOG.debug("Found ListGrid " + listGrid.getClassName());
+        
+        var record = listGrid.getRecord(Number(row));
+        LOG.debug("Record for row " + row + " is " + record);
+        return listGrid.getCellValue(record, row, col);
+    } else {
+        Selenium.prototype.orig_getTable.call(this, tableCellAddress);
+    }    
+};
+
+Selenium.prototype.orig_doMouseOver = Selenium.prototype.doMouseOver;
+
+Selenium.prototype.doMouseOver = function(locator, eventParams) {
+    /**
+   * Simulates a user hovering a mouse over the specified element.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   */
+
+    LOG.info("Locator in doMouseOver : " + locator);
+    var element = this.page().findElement(locator);
+    if(this.hasSC()) {
+        var autWindow = this.getAutWindow();
+        var canvas = autWindow.isc.AutoTest.locateCanvasFromDOMElement(element);
+        if(canvas == null) {
+            Selenium.prototype.orig_doMouseOver.call(this, locator);
+            return;
+        }
+        LOG.debug("Located canvas " + canvas + " for locator " + locator);
+
+        var coords = this.getSCLocatorCoords(autWindow, locator);
+        var clientX = coords[0];
+        var clientY = coords[1];
+
+        LOG.debug("clientX = " + clientX + ", clientY=" + clientY);
+        this.browserbot.triggerMouseEvent(element, "mouseover", true, clientX, clientY);
+    } else {
+        Selenium.prototype.orig_doMouseOver.call(this, locator);
+    }
+
+};
+
+
+Selenium.prototype.orig_doMouseMove = Selenium.prototype.doMouseMove;
+
+Selenium.prototype.doMouseMove = function(locator, eventParams) {
+    /**
+   * Simulates a user hovering a mouse over the specified element.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   */
+
+    LOG.info("Locator in doMouseMove : " + locator);
+    var element = this.page().findElement(locator);
+    if(this.hasSC()) {
+        var autWindow = this.getAutWindow();
+        var canvas = autWindow.isc.AutoTest.locateCanvasFromDOMElement(element);
+        if(canvas == null) {
+            Selenium.prototype.orig_doMouseMove.call(this, locator);
+            return;
+        }
+        LOG.debug("Located canvas " + canvas + " for locator " + locator);
+
+        var coords = this.getSCLocatorCoords(autWindow, locator);
+        var clientX = coords[0];
+        var clientY = coords[1];
+
+        LOG.debug("clientX = " + clientX + ", clientY=" + clientY);
+        autWindow.isc.EH.immediateMouseMove = true;
+        this.browserbot.triggerMouseEvent(element, "mousemove", true, clientX, clientY);
+        autWindow.isc.EH.immediateMouseMove = null;
+    } else {
+        Selenium.prototype.orig_doMouseMove.call(this, locator);
+    }
+
+};
+
+// Override drag and drop for SC components
+Selenium.prototype.orig_doDragAndDrop = Selenium.prototype.doDragAndDrop;
+Selenium.prototype.doDragAndDrop = function (locator, eventParams) {
+    var isSCLocator = locator && 
+                        ((locator.substring(0, "scLocator".length) == "scLocator") ||
+                         (locator.substring(0, "scID".length) == "scID")),
+        element = this.page().findElement(locator);
+    if (this.hasSC() && isSCLocator) {
+        var autWindow = this.getAutWindow();
+      
+        var canvas = autWindow.isc.AutoTest.locateCanvasFromDOMElement(element);
+        //if the clicked element does not correspond to a SmartClient widget, then perform the default SmartClient click operation
+        if(canvas == null) {
+            Selenium.prototype.orig_doDragAndDrop.call(this, locator, eventParams);
+            return;
+        }
+        LOG.debug("Located canvas " + canvas + " for locator " + locator);
+
+        var coords = this.getSCLocatorCoords(autWindow, locator);
+        if (coords == null) return;
+        var clientX = coords[0];
+        var clientY = coords[1];
+  
+      
+        LOG.debug("clientX = " + clientX + ", clientY=" + clientY);
+
+        autWindow.isc.EH.immediateMouseMove = true;
+
+        // fire mouseover / mouseDown / mousemove at original coordinates
+        this.browserbot.triggerMouseEvent(element, "mouseover", true, clientX, clientY);
+        this.browserbot.triggerMouseEvent(element, "mousedown", true, clientX, clientY);
+        this.browserbot.triggerMouseEvent(element, "mousemove", true, clientX, clientY);
+        // now trigger mousemove and mouseup at drop coordinates
+        // eventParams should contain offset as string like "+100,-25"
+        var delta = eventParams.split(",");
+        clientX += parseInt(delta[0]);
+        clientY += parseInt(delta[1]);
+        
+        this.browserbot.triggerMouseEvent(element, "mousemove", true, clientX, clientY);
+        this.browserbot.triggerMouseEvent(element, "mouseup", true, clientX, clientY);
+        
+        autWindow.isc.EH.immediateMouseMove = null;
+
+    } else {
+        Selenium.prototype.orig_doDragAndDrop.call(this, locator, eventParams);
+    }
+}
+
+Selenium.prototype.orig_doDragAndDropToObject = Selenium.prototype.dragAndDropToObject;
+Selenium.prototype.doDragAndDropToObject = function (locator, targetLocator) {
+    var isSCLocator = locator && 
+                        ((locator.substring(0, "scLocator".length) == "scLocator") ||
+                         (locator.substring(0, "scID".length) == "scID")),
+        element = this.page().findElement(locator);
+        
+    if (this.hasSC() && isSCLocator) {
+        var autWindow = this.getAutWindow();
+        var canvas = autWindow.isc.AutoTest.locateCanvasFromDOMElement(element);
+        //if the clicked element does not correspond to a SmartClient widget, then perform the default SmartClient click operation
+        if(canvas == null) {
+            Selenium.prototype.orig_doDragAndDropToObject.call(this, locator, targetLocator);
+            return;
+        }
+        LOG.debug("Located canvas " + canvas + " for locator " + locator);
+        var coords = this.getSCLocatorCoords(autWindow, locator);
+        if (coords == null) return;
+
+        var clientX = coords[0];
+        var clientY = coords[1];
+  
+      
+        LOG.debug("clientX = " + clientX + ", clientY=" + clientY);
+
+        autWindow.isc.EH.immediateMouseMove = true;
+
+        // fire mouseover / mouseDown / mousemove at original coordinates
+        this.browserbot.triggerMouseEvent(element, "mouseover", true, clientX, clientY);
+        this.browserbot.triggerMouseEvent(element, "mousedown", true, clientX, clientY);
+        this.browserbot.triggerMouseEvent(element, "mousemove", true, clientX, clientY);
+        // now trigger mousemove and mouseup at drop coordinates
+        
+        var dropElement = this.page().findElement(targetLocator)
+        var isSCTarget = targetLocator.indexOf("scLocator") != -1;
+        if (isSCTarget) {
+            var targetCoords = this.getSCLocatorCoords(autWindow, targetLocator);
+            if (targetCoords != null) coords = targetCoords;
+        // In this case we've got a drag from a SmartClient component to some arbitrary
+        // element on the page.
+        } else {
+            var targetLeft = autWindow.isc.Element.getLeftOffset(dropElement);
+            var targetTop = autWindow.isc.Element.getTopOffset(dropElement);
+            coords = [targetLeft,targetTop];
+        }
+
+        this.browserbot.triggerMouseEvent(dropElement, "mouseover", true, coords[0], coords[1]);
+        this.browserbot.triggerMouseEvent(dropElement, "mousemove", true, coords[0], coords[1]);
+        this.browserbot.triggerMouseEvent(dropElement, "mouseup", true, coords[0], coords[1]);
+        
+        autWindow.isc.EH.immediateMouseMove = null;
+
+    } else {
+        Selenium.prototype.orig_doDragAndDropToObject.call(this, locator, targetLocator);
+    }
+    
+}
+
+Selenium.prototype.getSCLocatorCoords = function (autWindow, scLocator) {
+    if (scLocator.indexOf("scLocator=") != -1) {
+        scLocator = scLocator.substring("scLocator=".length);
+        var coords = autWindow.isc.AutoTest.getPageCoords(scLocator);
+        LOG.debug("Determining page coordinates for SC Locator:" + scLocator + ": " + coords);
+        return coords;
+    } else if (scLocator.indexOf("scID=") != -1) {
+        var ID = scLocator.substring("scID=".length);
+        var canvas = autWindow[ID];
+        if (canvas != null && autWindow.isc.isA.Canvas(canvas) &&
+            canvas.isDrawn() && canvas.isVisible()) 
+        {
+            var left = canvas.getPageLeft() + parseInt(canvas.getVisibleWidth()/2),
+                top = canvas.getPageTop() + parseInt(canvas.getVisibleHeight()/2);
+            LOG.debug("Determining page coordinates for SC canvas:" + ID + ": " + [left,top]);
+            return [left,top];
+        }
+    }
+    LOG.debug("Unable to determine page coordinates for SC Locator:" + scLocator);
+    return null;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/selenium/user-guide.html	Tue Aug 09 11:02:13 2011 +0200
@@ -0,0 +1,120 @@
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <title>Using Selenium with SmartClient</title>
+</head>
+<body>
+    <p>
+        <a href="http://seleniumhq.org/">Selenium</a> is a powerful and popular tool which can be used to test your SmartClient applications.
+        Selenium executes tests against your running application in a browser emulating user interaction and asserting various conditions.
+        Selenium provides a record/playback tool for authoring tests without learning a test scripting language. You must be familiar with
+        <a href="http://seleniumhq.org/">Selenium</a> and use of <a href="http://seleniumhq.org/projects/ide/">Selenium IDE</a> before proceeding. Refer to the documentation on the <a href="http://seleniumhq.org/">Selenium</a>
+        site.
+    </p>
+
+    <p>
+        Use of Selenium with SmartClient applications is no different than using Selenium to write and run test cases with any other application with
+        the exception on on caveat. Selenium supports the concept of <a href="http://seleniumhq.org/docs/04_selenese_commands.html#locating-elements">Locators</a>
+        in order to specify the element you'd like a given Selenium command to operate on. For example Selenium supports XPath based locators, and DOM ID based locators.
+        XPath based locators are extremely fragile due to complexity of certain highly nested DOM elements you need access to combined with the fact that
+        XPath support varies across browsers and so your tests might not work across different browsers.
+    </p>
+    <p>
+        SmartClient occasionally renders a different DOM structure depending on the browser for performance for rendering the UI such that it appears identical across various browsers.
+        As a result using DOM ID or DOM XPath based locators with SmartClient applications is not advisable. Instead SmartClient supports a new Selenium
+        locator which is an xpath-like string used by Selenium to robustly identify DOM elements within a SmartClient application. SmartClient locators for Selenium
+        are prefixed by "scLocator=" and have a readable XPath like value even for cells in ListGrid's or TreeGrids. Typically these locators will not be hand-written and
+        are generated by <a href="http://seleniumhq.org/projects/ide/">Selenium IDE</a>, Selenium's test recording tool.
+        One primary locator is based on the ID of the SmartClient widget and has the syntax <b>ID=&lt;Canvas ID&gt;</b>. This 
+        simplifies the task of writing tests if you know the ID of the Canvas. For reference, the scLocator syntax for 
+        ListGrid cells and DynamicForm FormItem&quot;s can be found at the end of this document.
+    </p>
+
+    <h4>Setup Instructions</h4>
+    <ul>
+    <li>SmartClient ships with a Selenium user extension Javascript file : user-extensions.js. When running the Selenium tests make sure you place this file at the appropriate location.
+        Refer to the Selenium Documentation for mode details.
+    </li>
+    <li>In order to create tests, we suggest you use Selenium IDE. By default, Selenium looks for a file called "user-extensions.js", and loads the javascript code found in that file.
+        In the standard Selenium distribution, this file does not exist. You should place this file in this common location.
+        Refer to the Selenium documentation if you need additional information. Once you have
+        Selenium IDE installed, you will need to use the SmartClient user-extensions.js file with Selenium IDE. This is installed by
+        putting the pathname to its location on your computer in the Selenium Core extensions field of Selenium-IDE’s Options=>Options=>General tab.
+        Additional Details on how this can be setup can be found <a href="http://seleniumhq.org/docs/08_user_extensions.html#using-user-extensions-with-selenium-ide">here</a>.
+    </li>
+    <li>
+        You will also need to configure Selenium IDE with a SmartClient provided Selenium IDE extensions javascript file : user-extensions-ide.js This is installed by
+        putting the pathname to its location on your computer in the Selenium IDE  extensions field of Selenium-IDE’s Options=>Options=>General tab.
+    </li>
+    </ul>
+    <p>That's it, we're done configuring the environment.</p>
+    <p>
+        Note: Tests recorded using Selenium IDE can be played back using <a href="http://seleniumhq.org/projects/remote-control/">Selenium Remote Control</a>
+        The user-extensions-ide.js file is not required for playback of SmartClient-aware tests using 
+        Selenium RC, but the user-extensions.js file will be. Instructions for using user-extensions.js with
+        Selenium RC can be found <a href="http://seleniumhq.org/docs/08_user_extensions.html#using-user-extensions-with-selenium-rc">here</a>
+    </p>
+    <h4>Recording Selenium tests with Selenium IDE</h4>
+    <p>
+        Once you have your application running in Firefox, open Selenium IDE from the Tools ==> Selenium IDE menu option. If the Selenium IDE is in record mode,
+        then clicking or carrying out other operations like typing in a text field with automatically record the appropriate Selenium commands with the SmartClient locator.
+        There's no need for you to manually enter the locator, the recorder does this for you! Sometimes users many want finder grain control of what Selenium command
+        is created instead of having the Selenium IDE recorder do this automatically. For example if you want to verify the value of a particular cell in a ListGrid.
+        Instead on typing in the command "verifyTable" and manually enter the SmartClient Locator (scLocator), you can simply right click on the table cell or any other
+        SmartClient widget and the most suitable Selenium commands will appear in the context menu along with the scLocator path for the clicked element. See image below.
+    </p>
+    <img src="selenium-ide-example.png"/>
+
+	<hr>
+
+	<p>
+	<h4><u>Common scLocator syntax</u></h4>	
+	<h4><u>List Grid cells</u></h4>
+	<b>//ListGrid[ID="itemList"]/body/row[itemID=1996||itemName=Sugar White 1KG||SKU=85201400||1]/col[fieldName=SKU||1]</b>
+	<ul>
+	<li>This assumes the ListGrid has an explicit ID</li>
+	<li>the 'body' part might be 'frozenBody' if the field in question was frozen</li>
+	<li>row[......] identifies the row (record) 
+	<li>itemID=<value> - that's the primary key field from the dataSource the grid is bound to 
+	<li>itemName=<value> - that's the title field value for the record 
+	<li>SKU=... - that's the cell the user clicked's value 
+	<li>1 - that's the index of the row (rowNum) 
+	<li>col[.....] - identifies the column in the grid 
+	<li>fieldName=... - field name for the field the user clicked 
+	<li> 1 - that's the index of the column 
+	</ul>	
+	</p>
+	<p>
+	<h4><u>Form Items</u></h4>
+	<b>//DynamicForm[ID="autoTestForm"]/item[name=textField||title=textField||value=test||index=0||Class=TextItem]/element</b>
+		<p>This example is the data element (text entry box) for a text field 
+	<ul> 
+	<li>this form has an explicit ID 
+	<li>item[...] identifies the item 
+	<li>name (field name, if set) 
+	<li>title (title, if set) 
+	<li>value (current value if set) 
+	<li>index (index in the form items array) 
+	<li>Class (SC class of the item - in this case TextItem) after the "/" we identify the part of the item in question options here include: 
+	<li>"element" - the data element 
+	<li>"canvas" - for CanvasItems - points to the canvas embedded in the item 
+	<li>in this case the xpath might continue to contain, for example children of the canvas or elements within it (cells in a listGrid, etc) 
+	<li>"textbox" - the "text box" - this is the area where content is written out for items without a 'data element' - like header items 
+	<li>"[icon=<...>]" - the icon element -- "<...>" would contain the "name" 
+	of the icon
+	</ul>	
+	</p>
+	
+	<hr>
+	
+	<p>
+	<h4><u>Known Limitations</u></h4>
+	<ul>
+		<li>Support for multi-select for SelectItems with selection mode "grid" (non-default)</li>
+		<li>Support for Drag &amp; Drop due to limitations in Selenium</li>
+	</ul>	
+		
+</body>
+</html>
\ No newline at end of file