package net.sf.sahi.client; import java.util.ArrayList; import java.util.List; import net.sf.sahi.util.Utils; /** * Sahi - Web Automation and Test Tool * * Copyright 2006 V Narayan Raman * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * ElementStub is a representation of a particular HTML DOM element on the browser.<br/> * It translates to its corresponding Sahi javascript API. */ public class ElementStub { private final String elementType; private Object[] identifiers; private final Browser browser; private int index = -1; public ElementStub(String elementType, Browser browser, Object... args) { this.elementType = elementType; this.browser = browser; this.identifiers = args; } public String toString() { StringBuffer sb = new StringBuffer(); boolean start = true; for (Object o : identifiers) { if (!start) { sb.append(", "); sb.append(getArgument(o, -1)); } else { sb.append(getArgument(o, index)); } start = false; } return "_sahi._" + elementType + "(" + sb.toString() + ")"; } private Object getArgument(Object o, int ix) { String s = o.toString(); if (o instanceof String) { if (ix != -1) { s = s + "[" + ix + "]"; } if (!(s.length() > 1 && s.startsWith("/") && s.endsWith("/"))) { return "\"" + Utils.escapeDoubleQuotesAndBackSlashes(s) + "\""; } } return s; } /** * Performs a click on this element.<br/> * Internally calls browser.click(this); * * @throws ExecutionException */ public void click() throws ExecutionException { browser.click(this); } /** * Performs a double click on this element.<br/> * Internally calls browser.doubleClick(this); * * @throws ExecutionException */ public void doubleClick() throws ExecutionException { browser.doubleClick(this); } /** * Performs a right click on this element.<br/> * Internally calls browser.rightClick(this); * * @throws ExecutionException */ public void rightClick() throws ExecutionException { browser.rightClick(this); } /** * Checks this element (applicable to checkboxes and radio buttons).<br/> * Internally calls browser.check(this); * * @throws ExecutionException */ public void check() throws ExecutionException { browser.check(this); } /** * Unchecks this element (applicable to checkboxes).<br/> * Internally calls browser.uncheck(this); * * @throws ExecutionException */ public void uncheck() throws ExecutionException { browser.uncheck(this); } /** * Brings focus on the element. * * @param element * @throws ExecutionException */ public void focus() throws ExecutionException { browser.focus(this); } /** * Removes focus from the element. * * @param element * @throws ExecutionException */ public void removeFocus() throws ExecutionException { browser.removeFocus(this); } /** * Drags the element and drops it on another element. * * @param dropElement Element to be dropped on * @throws ExecutionException */ public void dragAndDropOn(ElementStub dropElement) throws ExecutionException { browser.dragDrop(this, dropElement); } /** * Drags the element and drops it at the given x, y coordinate. * * @param dropElement Element to be dropped on * @param x X coordinate * @param y Y coordinate * @throws ExecutionException */ public void dragAndDropOn(int x, int y) throws ExecutionException { browser.dragDropXY(this, x, y); } /** * Performs a mouseover on this element.<br/> * Same as hover().<br/> * Internally calls browser.mouseOver(this); * * @throws ExecutionException */ public void mouseOver() throws ExecutionException { browser.mouseOver(this); } /** * Performs a mousedown on this element.<br/> * Internally calls browser.mouseDown(this); * * @throws ExecutionException */ public void mouseDown() throws ExecutionException { browser.mouseDown(this); } /** * Performs a mouseup on this element.<br/> * Internally calls browser.mouseUp(this); * * @throws ExecutionException */ public void mouseUp() throws ExecutionException { browser.mouseUp(this); } /** * Performs a mouseover on this element.<br/> * Same as mouseOver().<br/> * Internally calls browser.mouseOver(this); * * @throws ExecutionException */ public void hover() throws ExecutionException { this.mouseOver(); } /** * Sets the value of this form element.<br/> * This method will do nothing for elements which do not have a value attribute.<br/> * Internally calls browser.setValue(this, value); * * @param value * @throws ExecutionException */ public void setValue(String value) throws ExecutionException { browser.setValue(this, value); } /** * Sets the value of this file upload element.<br/> * This method will do nothing for elements which are not file upload fields.<br/> * Internally calls browser.setFile(this, value); * * @param value * @throws ExecutionException */ public void setFile(String value) throws ExecutionException { browser.setFile(this, value); } /** * Sets the value of a select element; Will unselect previously selected option(s).<br/> * This method will do nothing for elements which are not select fields.<br/> * Internally calls browser.choose(this, value, false); * * @param value * @throws ExecutionException */ public void choose(String value) throws ExecutionException { choose(value, false); } /** * Sets the value of a select element; Will not unselect previously selected option(s) if append is true.<br/> * This method will do nothing for elements which are not select fields.<br/> * Internally calls browser.choose(this, values, append); * * @param value * @param append: if true, options are selected without unselecting previous options in multi-select box * @throws ExecutionException */ public void choose(String value, boolean append) throws ExecutionException { browser.choose(this, value, append); } /** * Selects multiple options of a select element; Will unselect previously selected option(s).<br/> * This method will do nothing for elements which are not select fields.<br/> * Internally calls browser.choose(this, values, false); * * @param values String array of option identifiers * @throws ExecutionException */ public void choose(String[] values) throws ExecutionException { choose(values, false); } /** * Selects multiple options of a select element; Will not unselect previously selected option(s) if append is true.<br/> * This method will do nothing for elements which are not select fields.<br/> * Internally calls browser.choose(this, values, append); * * @param values String array of option identifiers * @param append: if true, options are selected without unselecting previous options in multi-select box * @throws ExecutionException */ public void choose(String[] values, boolean append) throws ExecutionException { browser.choose(this, values, append); } /** * Returns the inner text of an element.<br/> * same as getText().<br/> * Internally calls browser.getText(this); * * @return * @throws ExecutionException */ public String text() throws ExecutionException { return getText(); } /** * Returns the inner text of an element.<br/> * same as text().<br/> * Internally calls browser.getText(this); * * @return * @throws ExecutionException */ public String getText() throws ExecutionException { return browser.getText(this); } /** * Writes the text into the Rich Text Editor (RTE).<br/> * Internally calls browser.rteWrite(this, value); * * @param value * @throws ExecutionException */ public void rteWrite(String value) throws ExecutionException { browser.rteWrite(this, value); } /** * Fetches the text of a Rich Text Editor (RTE).<br/> * * @return text of the RTE */ public String rteText() { return browser.fetch("_sahi._rteText(" + this + ")"); } /** * Fetches the HTML of a Rich Text Editor (RTE).<br/> * This will be browser dependent * * @return HTML of the RTE */ public String rteHTML() { return browser.fetch("_sahi._rteHTML(" + this + ")"); } public String fetch() throws ExecutionException { return fetch(null); } /** * Checks if an element is present on the browser.<br/> * Internally calls browser.exists(this); * Retries a few times if false. This can be controlled with script.max_reattempts_on_error in sahi.properties. * Use exists(true) to return in a single try. * * @return true if the element exists on the browser */ public boolean exists() { return browser.exists(this); } /** * Checks if an element is present on the browser.<br/> * Internally calls browser.exists(this); * * @param optimistic: if true will not retry till exists returns true. * @return true if the element exists on the browser */ public boolean exists(boolean optimistic) { return browser.exists(this, optimistic); } /** * Fetches the value of a form field from the browser.<br/> * Same as getValue().<br/> * Internally calls browser.getValue(this) * * @return value of the form field * @throws ExecutionException */ public String value() throws ExecutionException { return getValue(); } /** * Fetches the value of a form field from the browser.<br/> * Same as value().<br/> * Internally calls browser.getValue(this) * * @return value of the form field * @throws ExecutionException */ public String getValue() throws ExecutionException { return browser.getValue(this); } /** * Returns the selected text visible in a select box (<select> tag).<br/> * Same as getSelectedText().<br/> * Internally calls browser.getSelectedText(this); * * @return selected text */ public String selectedText() throws ExecutionException { return getSelectedText(); } /** * Checks for visibility of this element.<br/> * If an element is hidden via style display attribute set to "none" or <br/> * if the element is hidden via style visibility attribute set to "hidden",<br/> * isVisible() returns false. * <p/> * Retries a few times if false. This can be controlled with script.max_reattempts_on_error in sahi.properties. * Use exists(true) to return in a single try. * * @return true if the element is visible on the screen */ public boolean isVisible() throws ExecutionException { return browser.isVisible(this, false); } /** * Checks for visibility of this element.<br/> * If an element is hidden via style display attribute set to "none" or <br/> * if the element is hidden via style visibility attribute set to "hidden",<br/> * isVisible() returns false. * * @param optimistic: if true, Sahi will return in a single try. If false, Sahi will retry a few times. * @return true if the element is visible on the screen */ public boolean isVisible(boolean optimistic) throws ExecutionException { return browser.isVisible(this, optimistic); } /** * Returns the selected text visible in a select box (<select> tag).<br/> * Same as selectedText().<br/> * Internally calls browser.getSelectedText(this); * * @return selected text */ public String getSelectedText() throws ExecutionException { return browser.getSelectedText(this); } /** * Returns true if the element contains the input text * * @param text * @return true if the element contains the input text */ public boolean containsText(String text) { return browser.containsText(this, text); } /** * Returns true if the element's innerHTML contains the input html * * @param html * @return true if the element's innerHTML contains the input html */ public boolean containsHTML(String html) { return browser.containsHTML(this, html); } /** * Returns the computed css style <br/> * eg. browser.div("blackdiv").style("backgroundColor") * * @param el * @param attribute * @return the computed css style */ public String style(String html) { return browser.style(this, html); } /** * Establishes an "in" relation with another element.<br/> * Eg. {@code browser.link("delete").in(browser.cell("user1").parentNode()).click();}<br/> * This clicks on the delete link in the same row as cell with content "user1" * * @param inEl * @return Element with relation established */ public ElementStub in(ElementStub inEl) { return relation("in", inEl); } /** * Establishes an "under" relation with another element.<br/> * Checks for coordinate based alignment under a particular element within a specific threshold.<br/> * Eg. {@code browser.checkbox(0).near(browser.cell("Ram")).under(browser.cell("Delete user"))} * * @param underEl * @return Element with relation established */ public ElementStub under(ElementStub underEl) { return relation("under", underEl); } /** * Establishes a "near" relation with another element.<br/> * Eg. {@code browser.link("delete").near(browser.cell("user1")).click();}<br/> * This clicks on the delete link near a cell with content "user1" * * @param nearEl * @return Element with relation established */ public ElementStub near(ElementStub nearEl) { return relation("near", nearEl); } /** * Establishes a "near" relation with another element.<br/> * Eg. {@code browser.link("delete").near(browser.cell("user1")).click();}<br/> * This clicks on the delete link near a cell with content "user1" * * @param nearEl * @return Element with relation established */ public ElementStub above(ElementStub aboveEl) { return relation("above", aboveEl); } public ElementStub aboveOrUnder(ElementStub aboveOrUnderEl) { return relation("aboveOrUnder", aboveOrUnderEl); } public ElementStub leftOf(ElementStub leftOfEl) { return relation("leftOf", leftOfEl); } public ElementStub rightOf(ElementStub rightOfEl) { return relation("rightOf", rightOfEl); } public ElementStub leftOrRightOf(ElementStub leftOrRightOfEl) { return relation("leftOf", leftOrRightOfEl); } private ElementStub relation(String relationType, ElementStub inEl) { Object[] newArray = new Object[this.identifiers.length + 1]; System.arraycopy(this.identifiers, 0, newArray, 0, this.identifiers.length); newArray[this.identifiers.length] = new ElementStub(relationType, this.browser, inEl); this.identifiers = newArray; return this; } /** * Fetches the string value of a property of this element.<br/> * Eg.<br/> * browser.div("content").fetch("innerHTML") * * @return * @throws ExecutionException */ public String fetch(String string) throws ExecutionException { string = (string != null) ? (this + "." + string) : this.toString(); String value = browser.fetch(string); if (value.equals("undefined") || value.equals("null")) return null; return value; } /** * Returns true if the element is checked. Is meaningful only for radio buttons and checkboxes<br/> * Eg.<br/> * browser.radio("female").checked() * * @return * @throws ExecutionException */ public boolean checked() throws ExecutionException { return browser.checked(this); } /** * @return the first parentNode */ public ElementStub parentNode() { return new ElementStub("parentNode", this.browser, this); } /** * Returns the first parentNode of given tagName.<br/> * Eg.<br/> * browser.link("click me").parentNode("TABLE") will ignore TDs, TRs etc. * and will directly return the parent table. * * @param tagName * @return the first parentNode of given tagName */ public ElementStub parentNode(String tagName) { return new ElementStub("parentNode", this.browser, this, tagName); } private ElementStub parentNode(String tagName, String occurrence) { return new ElementStub("parentNode", this.browser, this, tagName, occurrence); } /** * Returns the nth parentNode of given tagName. * * @param tagName * @param occurrence * @return */ public ElementStub parentNode(String tagName, int occurrence) { return parentNode(tagName, "" + occurrence); } /** * Returns a list of element stubs similar to this one<br/><br/> * <p/> * Eg.<br/> * <pre> * browser.span("/Delete/").collectSimilar() * browser.div("css-class-name").in(browser.div("container")).collectSimilar() * </pre> * * @return */ public List<ElementStub> collectSimilar() { int count = this.countSimilar(); List<ElementStub> els = new ArrayList<ElementStub>(); for (int i = 0; i < count; i++) { final ElementStub el = new ElementStub(elementType, browser, identifiers); el.setIndex(i); els.add(el); } return els; } // called only from test void setIndex(int index) { this.index = index; } /** * Returns a count of elements similar to this * * @return */ public int countSimilar() { StringBuffer sb = new StringBuffer(); boolean start = true; for (Object o : identifiers) { if (!start) { sb.append(", "); } start = false; sb.append(getArgument(o, -1)); } String toFetch = "_sahi._count(\"" + "_" + elementType + "\", " + sb.toString() + ")"; final String countStr = browser.fetch(toFetch); try { return Integer.parseInt(countStr); } catch (Exception e) { return 0; } } /** * Performs a keyUp on this element.<br/> * Internally calls browser.keyUp; * * @param keyCode * @param charCode */ public void keyUp(int keyCode, int charCode) { browser.keyUp(this, keyCode, charCode); } public String getAttribute(String attribute) throws ExecutionException { return browser.getAttribute(this, attribute); } public void blur() throws ExecutionException { browser.blur(this); } /** * Performs a keyDown on this element.<br/> * Internally calls browser.keyDown; * * @param keyCode * @param charCode */ public void keyDown(int keyCode, int charCode) { browser.keyDown(this, keyCode, charCode); } public void keyPress(String keySequence, String combo) { browser.keyPress(this, keySequence, combo); } /** * Highlights this element.<br/> * Internally calls browser.highlight(this); * * @throws ExecutionException */ public void highlight() throws ExecutionException { browser.highlight(this); } }