Java tutorial
/* * Copyright 2011-2012 SugarCRM Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you * may not use this file except in compliance with the License. You * may 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. Please see the License for the specific language * governing permissions and limitations under the License. */ package org.sugarcrm.voodoodriver; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; import org.openqa.selenium.Alert; import org.openqa.selenium.By; import org.openqa.selenium.ElementNotVisibleException; import org.openqa.selenium.NoAlertPresentException; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.NoSuchFrameException; import org.openqa.selenium.StaleElementReferenceException; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.Select; /** * This is the heart of all of VooDooDriver. This class handles * executing all of the SODA language commands in the web browser. * * @author trampus * */ public class EventLoop implements Runnable { private Events testEvents = null; private Browser Browser = null; private VDDHash sodaVars = null; private Reporter report = null; private VDDHash hijacks = null; private Date threadTime = null; private volatile Thread runner; private volatile Boolean threadStop = false; private VDDHash ElementStore = null; private String currentHWnd = null; private int attachTimeout = 0; private String csvOverrideFile = null; private VDDHash whitelist = null; private ArrayList<Plugin> plugins = null; private String testName = ""; private int eventTimeout = 0; private long DEFAULT_WAIT_DURATION = 5; /** * The class Constructor. * * @param browser {@link Browser} * @param events {@link Events} * @param reporter {@link Reporter} * @param gvars {@link VDDHash} * @param hijacks {@link VDDHash} * @param oldvars {@link VDDHash} * @param plugins {@link Events} * @param testName the current running test */ public EventLoop(Browser browser, Events events, Reporter reporter, VDDHash gvars, VDDHash hijacks, VDDHash oldvars, ArrayList<Plugin> plugins, String testName, int eventTimeout) { testEvents = events; this.Browser = browser; this.report = reporter; this.hijacks = hijacks; this.testName = testName; this.whitelist = new VDDHash(); this.eventTimeout = eventTimeout; if (oldvars != null) { sodaVars = oldvars; } else { sodaVars = new VDDHash(); } this.ElementStore = new VDDHash(); if (gvars != null) { int len = gvars.keySet().size(); for (int i = 0; i <= len - 1; i++) { String key = gvars.keySet().toArray()[i].toString(); String value = gvars.get(key).toString(); System.out.printf("--)'%s' => '%s'\n", key, value); this.sodaVars.put(key, value); } } this.plugins = new ArrayList<Plugin>(); if (plugins != null) { this.plugins.addAll(plugins); } this.stampEvent(); SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy"); this.sodaVars.put("currentdate", df.format(new Date())); this.threadTime = new Date(); String hwnd = this.Browser.getDriver().getWindowHandle(); this.setCurrentHWND(hwnd); this.runner = new Thread(this, "EventLoop-Thread"); runner.start(); } /** * Sets the timeout in seconds for the Attach command to give up on retrying * to find the wanted window to attach to. * * @param timeout int */ public void setAttachTimeout(int timeout) { this.attachTimeout = timeout; } /** * Checks to see if a browser window exists. * * @param hwnd {@link String} * @return true if the window exists */ private boolean windowExists(String hwnd) { Set<String> windows = null; try { windows = this.Browser.getDriver().getWindowHandles(); } catch (org.openqa.selenium.WebDriverException e) { /* * When running the IE driver, if the window is closed, an * exception is thrown. */ return false; } for (int i = 0; i < windows.size(); i++) { if (hwnd.equals(windows.toArray()[i].toString())) { return true; } } return false; } /** * Sets the active browser window for VDD to use. * * @param hwnd {@link String} */ private void setCurrentHWND(String hwnd) { this.currentHWnd = hwnd; } /** * Gets the current browser window handle. * * @return {@link String} */ private String getCurrentHWND() { return this.currentHWnd; } /** * * @param event */ private void assertPage(VDDHash event) { boolean assertpage = true; if (event.containsKey("assertPage")) { assertpage = this.clickToBool(event.get("assertPage").toString()); } if (assertpage) { this.Browser.assertPage(this.whitelist); } } private void saveElement(VDDHash event, WebElement element) { if (!event.containsKey("save")) { return; } String name = event.get("save").toString(); if (this.ElementStore.containsKey(name)) { String msg = String.format("Found existing saved element var: '%s', Overwriting now.", name); this.report.Warn(msg); } if (element == null) { String msg = String.format("Element trying to be saved: '%s => 'NULL', not storing NULL element!", name); this.report.ReportError(msg); } else { this.ElementStore.put(name, element); String msg = String.format("Stored HTML element to be referenced by: '%s'.", name); this.report.Log(msg); } } public VDDHash getSodaVars() { return this.sodaVars; } public void appedSodaVars(VDDHash vars) { int len = 0; if (vars == null) { return; } len = vars.keySet().size() - 1; for (int i = 0; i <= len; i++) { String name = vars.keySet().toArray()[i].toString(); String value = vars.get(name).toString(); this.sodaVars.put(name, value); } } public boolean isAlive() { return this.runner.isAlive(); } public Thread getThread() { return this.runner; } public void stop() { synchronized (this.threadStop) { this.threadStop = true; this.runner.interrupt(); } } public boolean isStopped() { boolean result = false; synchronized (this.threadStop) { result = this.threadStop; } return result; } public void run() { System.out.printf("Thread Running...\n"); this.threadTime = new Date(); int i = 0; int event_count = this.testEvents.size() - 1; this.firePlugin(null, PluginEvent.BEFORETEST); while ((!this.threadStop) && (i <= event_count)) { handleSingleEvent(this.testEvents.get(i), null); i += 1; } this.firePlugin(null, PluginEvent.AFTERTEST); } private void resetThreadTime() { synchronized (this.threadTime) { this.threadTime = new Date(); } } public Date getThreadTime() { Date tmp = null; synchronized (this.threadTime) { tmp = this.threadTime; } return tmp; } private void processEvents(Events events, WebElement parent) { int event_count = events.size() - 1; for (int i = 0; i <= event_count; i++) { if (isStopped()) { break; } handleSingleEvent(events.get(i), parent); } } public Events getElements() { return testEvents; } private boolean handleSingleEvent(VDDHash event, WebElement parent) { boolean result = false; WebElement element = null; Elements type = (Elements) event.get("type"); if (isStopped()) { return result; } this.resetThreadTime(); switch (type) { case BROWSER: result = browserEvent(event, parent); break; case THEAD: element = theadEvent(event, parent); break; case TBODY: element = tbodyEvent(event, parent); break; case PUTS: result = putsEvent(event); break; case WAIT: result = waitEvent(event); break; case TEXTFIELD: element = textfieldEvent(event, parent); break; case PASSWORD: element = passwordEvent(event, parent); break; case BUTTON: element = buttonEvent(event, parent); break; case CSV: result = csvEvent(event); break; case LINK: element = linkEvent(event, parent); break; case CHECKBOX: element = checkboxEvent(event, parent); break; case VAR: result = varEvent(event); break; case SCRIPT: result = scriptEvent(event); break; case DIV: element = divEvent(event, parent); break; case ATTACH: result = attachEvent(event); break; case TABLE: element = tableEvent(event, parent); break; case FORM: element = formEvent(event, parent); break; case SELECT: element = selectEvent(event, parent); break; case OPTION: element = optionEvent(event, parent); break; case STAMP: result = stampEvent(event); break; case TIMESTAMP: result = stampEvent(event); break; case SPAN: element = spanEvent(event, parent); break; case HIDDEN: result = hiddenEvent(event, parent); break; case TR: element = trEvent(event, parent); break; case TH: element = thEvent(event, parent); break; case TD: element = tdEvent(event, parent); break; case FILEFIELD: element = filefieldEvent(event, parent); break; case IMAGE: element = imageEvent(event, parent); break; case DND: result = dndEvent(event); break; case TEXTAREA: element = textareaEvent(event, parent); break; case LI: element = liEvent(event, parent); break; case RADIO: element = radioEvent(event, parent); break; case EXECUTE: result = executeEvent(event); break; case JAVASCRIPT: result = javascriptEvent(event); break; case UL: result = ulEvent(event); break; case OL: result = olEvent(event); break; case MAP: result = mapEvent(event); break; case AREA: result = areaEvent(event); break; case PLUGINLOADER: result = pluginloaderEvent(event); break; case JAVAPLUGIN: result = javapluginEvent(event, parent); break; case DELETE: result = deleteEvent(event); break; case ALERT: result = alertEvent(event); break; case SCREENSHOT: result = screenshotEvent(event); break; case FRAME: result = frameEvent(event); break; case WHITELIST: result = whitelistEvent(event); break; case INPUT: element = inputEvent(event, parent); break; case EMAIL: element = emailEvent(event, parent); break; case H1: element = miscEvent(event, parent); break; case H2: element = miscEvent(event, parent); break; case H3: element = miscEvent(event, parent); break; case H4: element = miscEvent(event, parent); break; case H5: element = miscEvent(event, parent); break; case H6: element = miscEvent(event, parent); break; case I: element = miscEvent(event, parent); break; case B: element = miscEvent(event, parent); break; case STRIKE: element = miscEvent(event, parent); break; case S: element = miscEvent(event, parent); break; case U: element = miscEvent(event, parent); break; default: System.out.printf("(!)Unknown command: '%s'!\n", type.toString()); System.exit(1); } this.firePlugin(null, type, PluginEvent.AFTEREVENT); this.resetThreadTime(); if (element != null) { this.saveElement(event, element); } this.assertPage(event); return result; } /** * Log when an {@link ElementNotVisibleException} is caught * * If this exception is caught for a required element, the * message is reported as an error. Otherwise it is logged * normally. * * @param required whether the element was required */ private void logElementNotVisible(boolean required, VDDHash event) { String how = event.get("how").toString(); String what = this.replaceString(event.get(how).toString()); String msg = ("The element you are trying to access (" + how + " = " + what + ") is not visible"); if (required) { this.report.ReportError("Error: " + msg + "!"); } else { this.report.Log(msg + ", but required = false."); } } private boolean whitelistEvent(VDDHash event) { boolean result = false; String action = null; String name = null; String msg = ""; this.report.Log("Whitelist event starting..."); if (event.containsKey("name")) { name = event.get("name").toString(); name = this.replaceString(name); } if (name == null) { this.report.ReportError("Error: Missing 'name' attribute!"); this.report.Log("Whitelist event finished."); return false; } if (event.containsKey("action")) { action = event.get("action").toString(); action = this.replaceString(action); action = action.toLowerCase(); } if ((action == null) || (!action.contains("add") && !action.contains("delete"))) { msg = String.format("Error: action is an unknow type: '%s'!", action); this.report.ReportError(msg); this.report.Log("Whitelist event finished."); return false; } if (action.contains("add")) { String tmp = event.get("content").toString(); tmp = this.replaceString(tmp); this.whitelist.put(name, tmp); msg = String.format("Added whitelist item: '%s' => '%s'.", name, tmp); this.report.Log(msg); } else { this.whitelist.remove(name); msg = String.format("Deleted whitelist item: '%s'.", name); this.report.Log(msg); } this.report.Log("Whitelist event finished."); return result; } private boolean frameEvent(VDDHash event) { boolean result = false; int index = -1; String frameid = null; this.report.Log("Frame event starting."); if (event.containsKey("index")) { String tmp = event.get("index").toString(); tmp = this.replaceString(tmp); index = Integer.valueOf(tmp); } if (event.containsKey("id")) { frameid = event.get("id").toString(); frameid = this.replaceString(frameid); } if (event.containsKey("name")) { frameid = event.get("name").toString(); frameid = this.replaceString(frameid); } try { if (index > -1) { this.report.Log("Switching to frame by index: '" + index + "'."); this.Browser.getDriver().switchTo().frame(index); } else { this.report.Log("Switching to frame by name: '" + frameid + "'."); this.Browser.getDriver().switchTo().frame(frameid); } if (event.containsKey("children")) { this.processEvents((Events) event.get("children"), null); } this.report.Log("Switching back to default frame."); this.Browser.getDriver().switchTo().defaultContent(); } catch (NoSuchFrameException exp) { this.report.ReportError("Failed to find frame!"); } catch (Exception exp) { this.report.ReportException(exp); } this.report.Log("Frame event finished."); return result; } private boolean screenshotEvent(VDDHash event) { boolean result = false; String filename = ""; this.report.Log("Screenshot event starting."); if (!event.containsKey("file")) { result = false; this.report.ReportError("Error: screenshot command missing 'file' attribute!"); this.report.Log("Screenshot event finished."); return result; } else { filename = event.get("file").toString(); filename = this.replaceString(filename); Utils.takeScreenShot(filename, this.report); } this.report.Log("Screenshot event finished."); return result; } private boolean alertEvent(VDDHash event) { boolean result = false; boolean alert_var = false; boolean exists = true; boolean user_exists_true = false; boolean required = true; String msg = ""; String alert_text = ""; int timeout = 2; this.report.Log("Alert event starting."); if (!event.containsKey("alert") && !event.containsKey("exists")) { result = false; this.report.ReportError("Alert event missing alert attribute!"); return result; } if (event.containsKey("alert")) { String tmp = event.get("alert").toString(); tmp = this.replaceString(tmp); alert_var = this.clickToBool(tmp); } if (event.containsKey("exists")) { String tmp = event.get("exists").toString(); tmp = this.replaceString(tmp); exists = this.clickToBool(tmp); if (exists) { user_exists_true = true; } } if (event.containsKey("timeout")) { try { timeout = Integer.valueOf(replaceString((String) event.get("timeout"))); } catch (NullPointerException e) { this.report.ReportError("No value specified for timeout."); } catch (NumberFormatException e) { this.report.ReportError("Invalid integer value for timeout."); } } if (event.containsKey("required")) { String s = (String) event.get("required"); required = this.clickToBool(this.replaceString(s)); } try { Alert alert = null; while (timeout > 0) { try { alert = this.Browser.getDriver().switchTo().alert(); break; } catch (NoAlertPresentException e) { try { Thread.sleep(1000); } catch (InterruptedException i) { } timeout -= 1; } } if (alert == null) { throw new NoAlertPresentException(); } alert_text = alert.getText(); msg = String.format("Found Alert dialog: '%s'.", alert_text); this.report.Log(msg); handleVars(alert_text, event); if (event.containsKey("assert")) { String ass = event.get("assert").toString(); ass = this.replaceString(ass); this.report.Assert(ass, alert_text); } if (event.containsKey("assertnot")) { String ass = event.get("assertnot").toString(); ass = this.replaceString(ass); this.report.AssertNot(ass, alert_text); } if (alert_var) { this.report.Log("Alert is being Accepted."); alert.accept(); } else { this.report.Log("Alert is being Dismissed."); alert.dismiss(); } try { this.Browser.getDriver().switchTo().defaultContent(); } catch (Exception e) { /* * Bug 53577: if this alert is put up in response to a * window.close, switching back to the default content * will throw an exception. Curiously, it's a javascript * exception rather than NoSuchFrameException or * NoSuchWindowException that would be expected. Catch * generic "Exception" in case the Selenium folks fix the * Javascript error. */ this.report.Log("Unable to switch back to window. Is it closed?"); } Thread.currentThread(); Thread.sleep(1000); if (user_exists_true) { this.report.Assert("Alert dialog does exist.", true, true); } this.firePlugin(null, Elements.ALERT, PluginEvent.AFTERDIALOGCLOSED); result = true; } catch (NoAlertPresentException exp) { if (!exists) { msg = String.format("Expected alert dialog does not exist.", exists); this.report.Assert(msg, false, false); result = true; } else if (required) { this.report.ReportError("Error: No alert dialog found!"); result = false; } } catch (Exception exp) { this.report.ReportException(exp); result = false; } this.report.Log("Alert event finished."); return result; } private boolean deleteEvent(VDDHash event) { boolean result = false; this.report.Log("Delete event starting."); if (event.containsKey("name")) { String name = event.get("name").toString(); name = this.replaceString(name); if (this.ElementStore.containsKey(name)) { this.ElementStore.remove(name); this.report.Log("Deleted stored var."); result = true; } else { result = false; String msg = String.format("Error: failed to find variable to delete by name: '%s'!", name); this.report.ReportError(msg); } } else { this.report.ReportError("Error: delete command missing name attribute!"); result = false; } this.report.Log("Finsihed Delete event."); return result; } /** * Execute a <javaplugin> event. * * @param event the <javaplugin> event * @param parent the parent HTML element * @return whether plugin execution succeeded */ private boolean javapluginEvent(VDDHash event, WebElement parent) { String classname; Plugin plugin = null; PluginData data = new PluginData(); boolean result; this.report.Log("Javaplugin event started."); if (!event.containsKey("classname")) { report.ReportError("Javaplugin event missing attribute 'classname'!"); report.Log("Javaplugin event finished."); return false; } classname = (String) event.get("classname"); for (Plugin p : this.plugins) { if (p.matches(classname)) { plugin = p; break; } } if (plugin == null) { report.ReportError("Failed to find a loaded plugin with classname " + classname); report.Log("Javaplugin event finished."); return false; } if (event.containsKey("args")) { String[] args = (String[]) event.get("args"); if (args != null) { for (int k = 0; k < args.length; k++) { args[k] = replaceString(args[k]); } } data.setArgs(args); } data.setElement(parent); data.setBrowser(this.Browser); data.setSodaVars(this.sodaVars); data.setHijacks(this.hijacks); data.setTestName(this.testName); result = plugin.execute(data, report); if (result == false) { report.ReportError("Javaplugin failed"); } report.Log("Javaplugin event finished."); return result; } /** * Execute a <pluginloader> event * * @param event the <pluginloader> event * @return whether loading the new plugin succeeded */ private boolean pluginloaderEvent(VDDHash event) { String classname; String classfile; boolean result = true; report.Log("PluginLoader event started."); if (!event.containsKey("file")) { report.ReportError("Missing 'file' attribute for <pluginloader>"); report.Log("PluginLoader event finished."); return false; } if (!event.containsKey("classname")) { report.ReportError("Missing 'classname' attribute for <pluginloader>"); report.Log("PluginLoader event finished."); return false; } classfile = (String) event.get("file"); classname = (String) event.get("classname"); report.Log("Loading plugin with classname=" + classname); try { this.plugins.add(new JavaPlugin(classname, classfile)); } catch (PluginException e) { report.ReportError("Failed to load plugin " + classname); report.ReportException(e); result = false; } report.Log("PluginLoader event finished."); return result; } private boolean ulEvent(VDDHash event) { boolean required = true; boolean click = false; boolean result = false; WebElement element = null; this.report.Log("UL event Started."); this.resetThreadTime(); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, null, required); if (element == null) { this.report.Log("UL event finished."); result = false; return result; } this.checkDisabled(event, element); if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } if (click) { this.report.Log("UL click started."); this.firePlugin(element, Elements.UL, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.UL, PluginEvent.AFTERCLICK); this.report.Log("UL click finished."); } if (event.containsKey("children")) { this.processEvents((Events) event.get("children"), element); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { element = null; this.report.ReportException(exp); } this.report.Log("UL event Finished."); return result; } private boolean areaEvent(VDDHash event) { boolean required = true; boolean click = false; boolean result = false; WebElement element = null; this.report.Log("Area event Started."); this.resetThreadTime(); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, null, required); if (element == null) { this.report.Log("Area event finished."); result = false; return result; } this.checkDisabled(event, element); if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } if (click) { this.report.Log("Area click started."); this.firePlugin(element, Elements.AREA, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.AREA, PluginEvent.AFTERCLICK); this.report.Log("Area click finished."); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { element = null; this.report.ReportException(exp); } this.report.Log("Area event Finished."); return result; } private boolean mapEvent(VDDHash event) { boolean required = true; boolean click = false; boolean result = false; WebElement element = null; this.report.Log("Map event Started."); this.resetThreadTime(); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, null, required); if (element == null) { this.report.Log("Map event finished."); result = false; return result; } if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } this.checkDisabled(event, element); if (click) { this.report.Log("Map click started."); this.firePlugin(element, Elements.MAP, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.MAP, PluginEvent.AFTERCLICK); this.report.Log("Map click finished."); } if (event.containsKey("children")) { this.processEvents((Events) event.get("children"), element); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { element = null; this.report.ReportException(exp); } this.report.Log("Map event Finished."); return result; } private boolean olEvent(VDDHash event) { boolean required = true; boolean click = false; boolean result = false; WebElement element = null; this.report.Log("OL event Started."); this.resetThreadTime(); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, null, required); if (element == null) { this.report.Log("OL event finished."); result = false; return result; } if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } this.checkDisabled(event, element); if (click) { this.report.Log("OL click started."); this.firePlugin(element, Elements.OL, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.OL, PluginEvent.AFTERCLICK); this.report.Log("OL click finished."); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { element = null; this.report.ReportException(exp); } this.report.Log("OL event Finished."); return result; } private boolean javascriptEvent(VDDHash event) { boolean result = false; String scriptdata = ""; this.report.Log("Javascript event starting."); if (event.containsKey("content")) { scriptdata = event.get("content").toString(); } if (event.containsKey("file")) { scriptdata = ""; String filename = event.get("file").toString(); filename = this.replaceString(filename); File fd = new File(filename); try { BufferedReader reader = new BufferedReader(new FileReader(fd)); String tmp; while ((tmp = reader.readLine()) != null) { scriptdata = scriptdata.concat(tmp); } result = true; } catch (Exception exp) { this.report.ReportException(exp); result = false; } } this.Browser.executeJS(scriptdata, null); this.report.Log("Javascript event finished."); return result; } private boolean executeEvent(VDDHash event) { boolean result = false; Process proc = null; int proc_ret = 0; this.report.Log("Execute event starting...\n"); this.resetThreadTime(); if (event.containsKey("args")) { String[] list = (String[]) event.get("args"); int len = list.length - 1; for (int i = 0; i <= len; i++) { System.out.printf("(%s) => '%s'\n", i, list[i]); } try { this.report.Log("Executing process now..."); proc = Runtime.getRuntime().exec(list); this.resetThreadTime(); proc.waitFor(); this.resetThreadTime(); this.report.Log("Process finished executing."); proc_ret = proc.exitValue(); if (proc_ret != 0) { String msg = String.format("Error the command being executed returned a non-zero value: '%s'!", proc_ret); this.report.ReportError(msg); } else { this.report.Log("Execute was successful."); result = true; } } catch (Exception exp) { this.report.ReportException(exp); result = false; } } else { this.report.ReportError("Error no args for Execute Event!"); result = false; this.report.Log("Execute event finished."); return result; } return result; } private boolean dndEvent(VDDHash event) { boolean result = true; String src = null; String dst = null; if (event.containsKey("src")) { src = event.get("src").toString(); } if (event.containsKey("dst")) { dst = event.get("dst").toString(); } if (src == null) { this.report.ReportError("DnD command is missing 'src' attribute!"); result = false; } if (dst == null) { this.report.ReportError("DnD command is missing 'dst' attribute!"); result = false; } if (result) { WebElement Esrc = (WebElement) this.ElementStore.get(src); WebElement Edst = (WebElement) this.ElementStore.get(dst); VDDMouse mouse = new VDDMouse(this.report); mouse.DnD(Esrc, Edst); } this.report.Log("DND event finished."); return result; } /** * Handle an <image> event. * * @param event the ℑth> event * @param parent this element's parent * @return the a {@link WebElement} or null */ private WebElement imageEvent(VDDHash event, WebElement parent) { boolean required = true; boolean click = false; WebElement element = null; this.report.Log("Image event Started."); this.resetThreadTime(); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Image event finished."); return element; } if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } this.checkDisabled(event, element); handleVars(element.getAttribute("src"), event); if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (click) { this.report.Log("Image click started."); this.firePlugin(element, Elements.IMAGE, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.IMAGE, PluginEvent.AFTERCLICK); this.report.Log("Image click finished."); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { element = null; this.report.ReportException(exp); } this.report.Log("Image event Finished."); return element; } private WebElement filefieldEvent(VDDHash event, WebElement parent) { boolean required = true; WebElement element = null; this.report.Log("FileField event Started."); this.resetThreadTime(); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("FileField event finished."); return element; } this.firePlugin(element, Elements.FILEFIELD, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("set")) { String setvalue = event.get("set").toString(); setvalue = this.replaceString(setvalue); setvalue = FilenameUtils.separatorsToSystem(setvalue); setvalue = (new File(setvalue)).getAbsolutePath(); this.report.Log(String.format("Setting filefield to: '%s'.", setvalue)); element.sendKeys(setvalue); this.report.Log("Finished set."); } String value = element.getAttribute("value"); handleVars(value, event); } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { element = null; this.report.ReportException(exp); } this.report.Log("FileField event finished.."); this.resetThreadTime(); return element; } private WebElement liEvent(VDDHash event, WebElement parent) { boolean required = true; boolean click = false; WebElement element = null; this.report.Log("LI event Started."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("LI event finished."); return element; } if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } this.checkDisabled(event, element); String value = element.getText(); handleVars(value, event); if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (click) { this.report.Log("Click element."); this.firePlugin(element, Elements.LI, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.LI, PluginEvent.AFTERCLICK); this.report.Log("Click finished."); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); } if (event.containsKey("children")) { this.processEvents((Events) event.get("children"), element); } this.report.Log("LI event finished."); return element; } private WebElement trEvent(VDDHash event, WebElement parent) { boolean required = true; boolean click = false; WebElement element = null; this.report.Log("TR event Started."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("TR event finished."); return element; } String value = element.getText(); handleVars(value, event); this.checkDisabled(event, element); if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } if (click) { this.report.Log("Click element."); this.firePlugin(element, Elements.TR, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.TR, PluginEvent.AFTERCLICK); this.report.Log("Click finished."); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); } if (event.containsKey("children")) { this.processEvents((Events) event.get("children"), element); } this.report.Log("TR event finished."); return element; } /** * Handle a <th> event * * @param event the <th> event * @param parent this element's parent * @return the a {@link WebElement} or null */ private WebElement thEvent(VDDHash event, WebElement parent) { boolean required = true; boolean click = false; WebElement element = null; this.report.Log("TH event starting."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("TH event finished."); return element; } this.firePlugin(element, Elements.TH, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } String value = element.getText(); handleVars(value, event); if (click) { this.firePlugin(element, Elements.TH, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.TH, PluginEvent.AFTERCLICK); } if (event.containsKey("assert")) { String src = element.getText(); String val = this.replaceString(event.get("assert").toString()); this.report.Assert(val, src); } if (event.containsKey("assertnot")) { String src = element.getText(); String val = this.replaceString(event.get("assertnot").toString()); this.report.AssertNot(val, src); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("children") && element != null) { this.processEvents((Events) event.get("children"), element); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.report.Log("TH event finished."); return element; } private WebElement tdEvent(VDDHash event, WebElement parent) { boolean required = true; boolean click = false; WebElement element = null; this.report.Log("TD event Started."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("TD event finished."); return element; } String value = element.getText(); handleVars(value, event); this.checkDisabled(event, element); if (event.containsKey("assert")) { String src = element.getText(); String val = this.replaceString(event.get("assert").toString()); this.report.Assert(val, src); } if (event.containsKey("assertnot")) { String src = element.getText(); String val = this.replaceString(event.get("assertnot").toString()); this.report.AssertNot(val, src); } if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } if (click) { this.report.Log("Click element."); this.firePlugin(element, Elements.TD, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.TD, PluginEvent.AFTERCLICK); this.report.Log("Click finished."); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { element = null; this.report.ReportException(exp); } if (event.containsKey("children")) { this.processEvents((Events) event.get("children"), element); } this.report.Log("TD event finished."); return element; } private boolean hiddenEvent(VDDHash event, WebElement parent) { boolean result = false; boolean required = true; WebElement element = null; this.report.Log("Hidden event Started."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { result = false; this.report.Log("Hidden event finished."); return result; } String value = element.getAttribute("value"); handleVars(value, event); result = true; } catch (Exception exp) { this.report.ReportException(exp); result = false; } this.report.Log("Hidden event finished."); return result; } private boolean stampEvent() { return stampEvent(null); } private boolean stampEvent(VDDHash event) { SimpleDateFormat f = null; try { f = new SimpleDateFormat(event.get("format").toString()); } catch (NullPointerException e) { // "format" was not specified - NBD } catch (IllegalArgumentException e) { this.report .ReportError("Invalid Date format for timestamp '" + event.get("format") + "'. Using default."); } if (f == null) { f = new SimpleDateFormat("yyMMdd_hhmmss"); } String date_str = f.format(new Date()); this.report.Log(String.format("Setting STAMP => '%s'.", date_str)); this.sodaVars.put("stamp", date_str); return true; } private WebElement spanEvent(VDDHash event, WebElement parent) { boolean required = true; boolean click = false; WebElement element = null; this.report.Log("span event starting."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Span event finished."); return element; } this.firePlugin(element, Elements.SPAN, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } String value = element.getText(); handleVars(value, event); if (click) { this.firePlugin(element, Elements.SPAN, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.SPAN, PluginEvent.AFTERCLICK); } if (event.containsKey("assert")) { String src = element.getText(); String val = this.replaceString(event.get("assert").toString()); this.report.Assert(val, src); } if (event.containsKey("assertnot")) { String src = element.getText(); String val = this.replaceString(event.get("assertnot").toString()); this.report.AssertNot(val, src); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("children") && element != null) { this.processEvents((Events) event.get("children"), element); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.report.Log("Span event finished."); return element; } private WebElement inputEvent(VDDHash event, WebElement parent) { boolean required = true; WebElement element = null; this.report.Log("Input event starting."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Input event finished."); return element; } this.firePlugin(element, Elements.INPUT, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); String value = element.getAttribute("value"); handleVars(value, event); if (event.containsKey("assert")) { String src = element.getText(); String val = this.replaceString(event.get("assert").toString()); this.report.Assert(val, src); } if (event.containsKey("assertnot")) { String src = element.getText(); String val = this.replaceString(event.get("assertnot").toString()); this.report.AssertNot(val, src); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.report.Log("Input event finished."); return element; } /** * <email> event * * @param event the <email> event * @param parent this element's parent * @return the <email> {@link WebElement} or null */ private WebElement emailEvent(VDDHash event, WebElement parent) { boolean required = true; WebElement element = null; this.resetThreadTime(); this.report.Log("Starting email event."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Finished email event."); return null; } this.firePlugin(element, Elements.EMAIL, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("clear")) { if (this.clickToBool(event.get("clear").toString())) { this.report.Log("Clearing email element."); clearText(element); } } if (event.containsKey("set")) { String value = event.get("set").toString(); value = this.replaceString(value); this.report.Log(String.format("Setting value to: '%s'.", value)); clearText(element); element.sendKeys(value); } if (event.containsKey("append")) { String value = event.get("append").toString(); value = this.replaceString(value); element.sendKeys(value); } if (event.containsKey("jscriptevent")) { String jev = (String) event.get("jscriptevent"); this.report.Log("Firing Javascript Event: " + jev); this.Browser.fire_event(element, jev); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("assert")) { String assvalue = event.get("assert").toString(); assvalue = this.replaceString(assvalue); this.report.Assert(assvalue, element.getAttribute("value")); } if (event.containsKey("assertnot")) { String assvalue = event.get("assertnot").toString(); assvalue = this.replaceString(assvalue); this.report.AssertNot(assvalue, element.getAttribute("value")); } String value = element.getAttribute("value"); handleVars(value, event); } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.resetThreadTime(); this.report.Log("Finished email event."); return element; } private WebElement radioEvent(VDDHash event, WebElement parent) { boolean required = true; boolean click = false; WebElement element = null; this.report.Log("Radio event starting."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Radio event finished."); return element; } this.firePlugin(element, Elements.RADIO, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } if (event.containsKey("set")) { this.report .Warn("Using the 'set' command for a radio element is not supported anymore! Use click!"); click = this.clickToBool(event.get("set").toString()); } String value = element.getAttribute("value"); handleVars(value, event); if (click) { this.firePlugin(element, Elements.RADIO, PluginEvent.BEFORECLICK); this.report.Log("Clicking Element."); element.click(); this.report.Log("Click finished."); this.firePlugin(element, Elements.RADIO, PluginEvent.AFTERCLICK); } if (event.containsKey("assert")) { String src = element.getText(); String val = this.replaceString(event.get("assert").toString()); this.report.Assert(val, src); } if (event.containsKey("assertnot")) { String src = element.getText(); String val = this.replaceString(event.get("assertnot").toString()); this.report.AssertNot(val, src); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("checked")) { boolean ischecked = element.isSelected(); boolean expected = this.clickToBool(event.get("checked").toString()); String msg = ""; msg = String.format("Radio control's current checked state: '%s', was expecting: '%s'!", ischecked, expected); this.report.Assert(msg, ischecked, expected); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.report.Log("Radio event finished."); return element; } /** * Handle a <select> event * * @param event the <select> event * @param parent this element's parent * @return the a {@link WebElement} or null */ private WebElement selectEvent(VDDHash event, WebElement parent) { boolean required = true, multiselect = true; WebElement element = null; this.report.Log("Select event Started."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { Select sel = null; element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Select event finished."); return null; } sel = new Select(element); this.firePlugin(element, Elements.SELECT, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); handleVars(element.getAttribute("value"), event); if (event.containsKey("assert") || event.containsKey("assertnot")) { boolean wantSel = event.containsKey("assert"); String val = this.replaceString(event.get(wantSel ? "assert" : "assertnot").toString()); boolean optionFound = false; for (WebElement opt : sel.getOptions()) { if (opt.getText().contains(val)) { boolean issel = opt.isSelected(); this.report.Assert("Select option '" + val + "' is " + (issel ? "" : "not ") + "selected", wantSel ^ issel, false); optionFound = true; break; } } if (!optionFound) { this.report.ReportError("Failed to find select option '" + val + "'!"); } } if (event.containsKey("assertselected")) { boolean anySelected = sel.getAllSelectedOptions().size() > 0; boolean shouldBeSelected = clickToBool(event.get("assertselected").toString()); report.Assert("Option " + (anySelected ? "" : "not ") + "selected", anySelected, shouldBeSelected); } if (event.containsKey("included") || event.containsKey("notincluded")) { boolean wantOpt = event.containsKey("included"); String val = this.replaceString(event.get(!wantOpt ? "notincluded" : "included").toString()); boolean haveOpt = false; for (WebElement opt : sel.getOptions()) { if (opt.getText().contains(val)) { haveOpt = true; break; } } String m = String.format("Select option %s%s found and%s expected", val, haveOpt ? "" : " not", wantOpt ? "" : " not"); this.report.Assert(m, wantOpt ^ haveOpt, false); } if (event.containsKey("multiselect") && !this.clickToBool(event.get("multiselect").toString()) && sel.isMultiple()) { multiselect = false; } if (event.containsKey("clear") && this.clickToBool(event.get("clear").toString()) && sel.isMultiple()) { this.report.Log("Clearing select element."); sel.deselectAll(); } try { if (event.containsKey("set") || event.containsKey("setreal")) { boolean useVal = event.containsKey("setreal"); String val = this.replaceString(event.get(useVal ? "setreal" : "set").toString()); this.report .Log("Setting option by " + (useVal ? "value" : "visible text") + ": '" + val + "'."); if (multiselect == false) { /* * isMultiple() must be true in order for * multiselect to be false */ sel.deselectAll(); } try { if (useVal) { sel.selectByValue(val); } else { sel.selectByVisibleText(val); } this.firePlugin(element, Elements.SELECT, PluginEvent.AFTERSET); } catch (NoSuchElementException e) { this.report.ReportError("Option with " + (useVal ? "value" : "visible text") + " '" + val + "' does not exist"); } } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); try { Thread.sleep(1000); } catch (InterruptedException e) { } this.report.Log("Javascript event finished."); } if (event.containsKey("click") && this.clickToBool(event.get("click").toString())) { this.firePlugin(element, Elements.SELECT, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.SELECT, PluginEvent.AFTERCLICK); } if (element.isDisplayed() && event.containsKey("children")) { this.processEvents((Events) event.get("children"), element); } } catch (StaleElementReferenceException e) { /* * Selecting a value has the potential to refresh the page * (Bug 49533). */ this.report.Log("Page refreshed; select element no longer exists."); element = null; } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); } this.report.Log("Select event finished."); return element; } /** * Handle an <option> event * * @param event the <option> event * @param parent this element's parent * @return the a {@link WebElement} or null */ private WebElement optionEvent(VDDHash event, WebElement parent) { boolean click = true; boolean required = true; WebElement element = null; this.report.Log("Option event starting."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Option event finished."); return element; } this.firePlugin(element, Elements.OPTION, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("assert")) { String val = this.replaceString(event.get("assert").toString()); this.report.Assert(val, element.getText()); } if (event.containsKey("assertnot")) { String val = this.replaceString(event.get("assertnot").toString()); this.report.AssertNot(val, element.getText()); } if (event.containsKey("jscriptevent")) { String ev = (String) event.get("jscriptevent"); this.report.Log("Firing Javascript Event: " + ev); this.Browser.fire_event(element, ev); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } if (click) { this.firePlugin(element, Elements.OPTION, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.OPTION, PluginEvent.AFTERCLICK); } } catch (ElementNotVisibleException e) { logElementNotVisible(required, event); } catch (Exception e) { this.report.ReportException(e); } this.report.Log("Option event finished."); return element; } private WebElement formEvent(VDDHash event, WebElement parent) { boolean required = true; boolean click = false; WebElement element = null; this.report.Log("Form event starting."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Form event finished."); return element; } this.firePlugin(element, Elements.FORM, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } if (click) { this.firePlugin(element, Elements.FORM, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.FORM, PluginEvent.AFTERCLICK); } if (event.containsKey("children") && element != null) { this.processEvents((Events) event.get("children"), element); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); } this.report.Log("Form event finished."); return element; } private WebElement tableEvent(VDDHash event, WebElement parent) { boolean required = true; boolean click = false; WebElement element = null; this.report.Log("Table event started."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Table event finished."); return element; } this.firePlugin(element, Elements.TABLE, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } if (click) { this.firePlugin(element, Elements.TABLE, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.TABLE, PluginEvent.AFTERCLICK); } if (event.containsKey("assert")) { String src = element.getText(); String value = event.get("assert").toString(); value = this.replaceString(value); this.report.Assert(value, src); } if (event.containsKey("assertnot")) { String src = element.getText(); String value = event.get("assertnot").toString(); value = this.replaceString(value); this.report.AssertNot(value, src); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("children")) { this.processEvents((Events) event.get("children"), element); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.report.Log("Table event finished."); return element; } private boolean attachEvent(VDDHash event) { boolean result = false; Set<String> handles = null; int len = 0; boolean use_URL = false; String finder = ""; String found_handle = null; String msg = ""; int timeout = 10; int index = -1; try { this.report.Log("Attach event starting."); String currentWindow = this.Browser.getDriver().getWindowHandle(); this.report.Log(String.format("Current Window Handle: '%s'.", currentWindow)); if (event.containsKey("index")) { use_URL = false; String tmp_index = event.get("index").toString(); tmp_index = this.replaceString(tmp_index); if (!Utils.isInt(tmp_index)) { msg = String.format("Error: index is not an integer: '%s'!", tmp_index); this.report.ReportError(msg); this.report.Log("Attach event finished."); return false; } timeout = 0; // causes the for loop to do nothing this is a hack. should make this method better later ... // index = Integer.valueOf(tmp_index); finder = tmp_index; } else if (event.containsKey("url")) { use_URL = true; finder = event.get("url").toString(); } else { use_URL = false; finder = event.get("title").toString(); } finder = this.replaceString(finder); for (int timer = 0; timer <= timeout; timer++) { handles = this.Browser.getDriver().getWindowHandles(); len = handles.size() - 1; for (int i = 0; i <= len; i++) { String tmp_handle = handles.toArray()[i].toString(); String tmp_url = this.Browser.getDriver().switchTo().window(tmp_handle).getCurrentUrl(); String tmp_title = this.Browser.getDriver().switchTo().window(tmp_handle).getTitle(); this.report.Log(String.format("[%d]: Window Handle: '%s'", i, tmp_handle)); this.report.Log(String.format("[%d]: Window Title: '%s'", i, tmp_title)); this.report.Log(String.format("[%d]: Window URL: '%s'", i, tmp_url)); TextFinder f = new TextFinder(finder); if (use_URL && f.findExact(tmp_url)) { found_handle = tmp_handle; this.report.Log(String.format("Found Window URL '%s'", finder)); break; } else if (!use_URL && f.findExact(tmp_title)) { found_handle = tmp_handle; this.report.Log(String.format("Found Window Title '%s'", finder)); break; } } // end for loop // if (found_handle != null) { break; } Thread.sleep(1000); } // end timer loop // if (index != -1) { handles = this.Browser.getDriver().getWindowHandles(); len = handles.size() - 1; if (index > len) { msg = String.format("Error: index: '%d' is greater then the number of windows found: '%d'!", index, len); found_handle = null; } else { found_handle = handles.toArray()[index].toString(); } } if (found_handle == null) { msg = String.format("Failed to find window matching: '%s!'", finder); this.report.ReportError(msg); result = false; this.Browser.getDriver().switchTo().window(currentWindow); Thread.sleep(2000); // helps control when the page is loaded, might // remove this later... // return result; } this.Browser.getDriver().switchTo().window(found_handle); this.setCurrentHWND(found_handle); msg = String.format("Switching to window handle: '%s'.", found_handle); this.report.Log(msg); if (event.containsKey("children")) { this.processEvents((Events) event.get("children"), null); } this.Browser.getDriver().switchTo().window(currentWindow); this.Browser.setBrowserOpened(); this.setCurrentHWND(currentWindow); msg = String.format("Switching back to window handle: '%s'.", currentWindow); this.report.Log(msg); if (this.attachTimeout > 0) { long tout = this.attachTimeout * 1000; Thread.currentThread(); msg = String.format("Waiting '%s' seconds before executing next event.", this.attachTimeout); this.report.Log(msg); Thread.sleep(tout); } } catch (Exception exp) { this.report.ReportException(exp); result = false; } this.report.Log("Attach event finished."); return result; } private String replaceString(String str) { String result = str; Pattern patt = null; Matcher matcher = null; this.resetThreadTime(); patt = Pattern.compile("\\{@[\\w\\.]+\\}", Pattern.CASE_INSENSITIVE); matcher = patt.matcher(str); while (matcher.find()) { String m = matcher.group(); String tmp = m; tmp = tmp.replace("{@", ""); tmp = tmp.replace("}", ""); if (this.hijacks.containsKey(tmp)) { String value = this.hijacks.get(tmp).toString(); result = result.replace(m, value); } else if (this.sodaVars.containsKey(tmp)) { String value = this.sodaVars.get(tmp).toString(); result = result.replace(m, value); } } result = result.replaceAll("\\\\n", "\n"); this.resetThreadTime(); return result; } private WebElement divEvent(VDDHash event, WebElement parent) { boolean required = true; boolean click = false; WebElement element = null; this.report.Log("Div event starting."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Div event finished."); return element; } this.firePlugin(element, Elements.DIV, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("assert")) { String src = element.getText(); String value = event.get("assert").toString(); value = this.replaceString(value); this.report.Assert(value, src); } if (event.containsKey("assertnot")) { String src = element.getText(); String value = event.get("assertnot").toString(); value = this.replaceString(value); this.report.AssertNot(value, src); } String value = element.getText(); handleVars(value, event); if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } if (click) { this.firePlugin(element, Elements.DIV, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.DIV, PluginEvent.AFTERCLICK); } if (event.containsKey("children")) { this.processEvents((Events) event.get("children"), element); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.report.Log("Div event finished."); return element; } private boolean scriptEvent(VDDHash event) { boolean result = false; TestLoader loader = null; String testfile = ""; File fd = null; Events newEvents = null; testfile = event.get("file").toString(); testfile = this.replaceString(testfile); try { fd = new File(testfile); if (!fd.exists()) { String msg = String.format("Failed to find file: '%s'!", testfile); this.report.ReportError(msg); return false; } fd = null; loader = new TestLoader(new File(testfile), null); newEvents = loader.getEvents(); if (newEvents == null) { this.report.ReportError("Failed to load script '" + testfile + "'"); return false; } this.processEvents(newEvents, null); } catch (Exception exp) { this.report.ReportException(exp); result = false; } return result; } /* * varEvent -- method This method sets a SODA var that can then be used in * the follow script. * * Input: event: A Soda event. * * Output: returns true on success or false on fail. */ private boolean varEvent(VDDHash event) { boolean result = false; String var_name = ""; String var_value = ""; try { if (event.containsKey("set")) { var_name = event.get("var").toString(); var_name = this.replaceString(var_name); var_value = event.get("set").toString(); var_value = this.replaceString(var_value); this.sodaVars.put(var_name, var_value); String tmp_value = var_value; tmp_value = tmp_value.replaceAll("\n", "\\\n"); this.report.Log("Setting SODA variable: '" + var_name + "' => '" + tmp_value + "'."); } if (event.containsKey("unset")) { var_name = event.get("var").toString(); var_name = this.replaceString(var_name); this.report.Log("Unsetting SODA variable: '" + var_name + "'."); if (!this.sodaVars.containsKey(var_name)) { this.report.Log("SODA variable: '" + var_name + "' not found, nothing to unset."); } else { this.sodaVars.remove(var_name); } } } catch (Exception exp) { this.report.ReportException(exp); result = false; } return result; } private WebElement checkboxEvent(VDDHash event, WebElement parent) { boolean click = false; boolean required = true; WebElement element = null; this.resetThreadTime(); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { return element; } this.firePlugin(element, Elements.CHECKBOX, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); if (click) { this.firePlugin(element, Elements.CHECKBOX, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.CHECKBOX, PluginEvent.AFTERCLICK); } } if (event.containsKey("set")) { String msg = ""; String set = event.get("set").toString(); set = this.replaceString(set); if (Boolean.valueOf(set) == element.isSelected()) { msg = String.format("Checkbox current checked state is already: '%s', skipping click.", set); } else { msg = String.format("Checkbox's state is '%s', clicking to set state to '%s'.", element.isSelected(), set); element.click(); this.firePlugin(element, Elements.CHECKBOX, PluginEvent.AFTERCLICK); } this.report.Log(msg); } if (event.containsKey("assert")) { String src = String.valueOf(element.isSelected()); String value = event.get("assert").toString(); value = this.replaceString(value); this.report.Assert(value, src); } if (event.containsKey("assertnot")) { String src = String.valueOf(element.isSelected()); String value = event.get("assertnot").toString(); value = this.replaceString(value); this.report.AssertNot(value, src); } String value = element.getAttribute("value"); handleVars(value, event); } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); } this.resetThreadTime(); return element; } /** * Handle a <link> event * * "Link" is a VDD-specific synonym for the HTML anchor (a) tag. * * @param event the <link> event * @param parent this element's parent * @return the a {@link WebElement} or null */ private WebElement linkEvent(VDDHash event, WebElement parent) { boolean click = true; boolean required = true; boolean exists = true; WebElement element = null; this.resetThreadTime(); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } if (event.containsKey("exists")) { String tmp = event.get("exists").toString(); tmp = this.replaceString(tmp); exists = this.clickToBool(tmp); } try { this.report.Log("Link Event Started."); String how = event.get("how").toString(); how = this.replaceString(how); String value = event.get(how).toString(); value = this.replaceString(value); element = this.findElement(event, parent, required); if (element == null) { if (required && exists) { String msg = String.format("Failed to find link: '%s' => '%s'!", how, value); this.report.Log(msg); } if (exists != true) { this.report.Assert("Link does not exist.", false, false); } element = null; this.report.Log("Link Event Finished."); return element; } value = element.getText(); handleVars(value, event); this.firePlugin(element, Elements.LINK, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("alert")) { boolean alert = this.clickToBool(event.get("alert").toString()); this.report.Log(String.format("Setting Alert Hack to: '%s'", alert)); this.Browser.alertHack(alert); this.report.Warn("You are using a deprecated alert hack, please use the <alert> command!"); } if (event.containsKey("children")) { this.processEvents((Events) event.get("children"), null); } if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } if (click) { value = this.replaceString(value); this.report.Log(String.format("Clicking Link: '%s' => '%s'", how, value)); this.firePlugin(element, Elements.LINK, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.LINK, PluginEvent.AFTERCLICK); } else { String msg = String.format("Found Link: '%s' but not clicking as click => '%s'.", value, click); this.report.Log(msg); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.resetThreadTime(); this.report.Log("Link Event Finished."); return element; } private boolean csvEvent(VDDHash event) { boolean result = false; CSV csv = null; CSVData csv_data = null; String var_name = null; String csv_filename = ""; String msg = ""; if (event.containsKey("var")) { var_name = event.get("var").toString(); } this.resetThreadTime(); this.report.Log("CSV event starting..."); if (this.csvOverrideFile != null) { csv_filename = event.get("file").toString(); csv_filename = this.replaceString(csv_filename); msg = String.format("Found existing csv override file: '%s', replacing expected file: '%s'.", this.csvOverrideFile, csv_filename); event.put("file", this.csvOverrideFile); event.remove("csv"); // just in cause someone wanted to override and override. // this.csvOverrideFile = null; } if (event.containsKey("override")) { String csv_txt = event.get("override").toString(); csv_txt = this.replaceString(csv_txt); this.csvOverrideFile = csv_txt; msg = String.format("Setting CSV file override to file: '%s'.", this.csvOverrideFile); this.report.Log(msg); this.report.Log("CSV event finished."); return true; } csv_filename = event.get("file").toString(); csv_filename = replaceString(csv_filename); msg = String.format("Processing CSV file: '%s'...", csv_filename); this.report.Log(msg); csv = new CSV(csv_filename, this.report); csv_data = csv.getData(); for (int i = 0; i <= csv_data.size() - 1; i++) { int keys_len = csv_data.get(i).keySet().size() - 1; for (int key_index = 0; key_index <= keys_len; key_index++) { String key = csv_data.get(i).keySet().toArray()[key_index].toString(); String sodavar_name = var_name + "." + key; String sodavar_value = csv_data.get(i).get(key).toString(); if (this.hijacks.containsKey(sodavar_name)) { sodavar_value = this.hijacks.get(sodavar_name).toString(); this.report.Log("Hijacking SodaVar: '" + sodavar_name + "' => '" + sodavar_value + "'."); } this.sodaVars.put(sodavar_name, sodavar_value); } if (event.containsKey("children")) { this.processEvents((Events) event.get("children"), null); } } this.report.Log("CSV event finished."); this.resetThreadTime(); return result; } /** * Duration of the current wait event. */ private long waitDuration = 0; /** * Get the duration of a wait event in progress. * * <p>This is 0 unless a wait event is in progress, in which case * it's set to the length of the wait in milliseconds. This * information is used by <code>Test.runTest</code> to extend the * watchdog timeout by the duration of this wait.</p> */ public long getWaitDuration() { return waitDuration; } private boolean waitEvent(VDDHash event) { boolean result = false; long timeout = DEFAULT_WAIT_DURATION; this.resetThreadTime(); this.report.Log("Starting Wait event."); if (event.containsKey("timeout")) { timeout = (new Integer(event.get("timeout").toString())).intValue(); this.report.Log("Setting timeout to " + timeout + " seconds."); } this.report.Log("Waiting " + timeout + " seconds."); try { this.waitDuration = timeout * 1000; while (timeout-- > 0) { if (isStopped()) { break; } try { Thread.sleep(1000); } catch (InterruptedException e) { } } } finally { this.resetThreadTime(); this.waitDuration = 0; } this.report.Log("Wait event finished."); return true; } private boolean browserEvent(VDDHash event, WebElement parent) { boolean result = false; boolean assertPage = true; this.resetThreadTime(); this.report.Log("Browser event starting..."); try { if (event.containsKey("action")) { int retry = 2; while (retry-- > 0) { try { switch (BrowserActions.valueOf(event.get("action").toString().toUpperCase())) { case REFRESH: this.report.Log("Calling Browser event refresh."); this.Browser.refresh(); break; case CLOSE: this.report.Log("Calling Browser event close."); this.Browser.close(); break; case BACK: this.report.Log("Calling Browser event back."); this.Browser.back(); break; case FORWARD: this.report.Log("Calling Browser event forward."); this.Browser.forward(); break; case MAXIMIZE: this.report.Log("Calling Browser event maximize."); this.Browser.maximize(); break; case RESTORE: this.report.Log("Calling Browser event restore."); this.Browser.restore(); break; } break; } catch (org.openqa.selenium.WebDriverException e) { /* * Presumably an UnhandledAlertException. However, * FirefoxDriver can't be counted on to throw that, * so catch the parent exception and handle the * alert. If there is no alert, a new exception * will be thrown (which is OK), and this exception * will be reported regardless. */ this.report.unhandledAlert(e); if (retry >= 0) { this.report.Log("Retrying browser action..."); } } } } else { int event_count = event.keySet().size() - 1; for (int i = 0; i <= event_count; i++) { String key = event.keySet().toArray()[i].toString(); String key_id = "BROWSER_" + key; BrowserMethods method = null; if (BrowserMethods.isMember(key_id)) { method = BrowserMethods.valueOf(key_id); } else { continue; } String value = ""; switch (method) { case BROWSER_url: String url = event.get(key).toString(); url = this.replaceString(url); this.report.Log(String.format("URL: '%s'", url)); if (this.Browser.isClosed()) { this.Browser.newBrowser(); this.setCurrentHWND(this.Browser.getDriver().getWindowHandle()); } this.Browser.url(url); break; case BROWSER_assert: value = event.get("assert").toString(); value = this.replaceString(value); if (parent != null) { result = this.Browser.Assert(value, parent); } else { result = this.Browser.Assert(value); } if (!result) { String msg = String.format("Browser Assert Failed to find this in page: '%s'", value); this.report.Log(msg); } break; case BROWSER_assertnot: value = event.get("assertnot").toString(); value = this.replaceString(value); if (parent != null) { result = this.Browser.AssertNot(value, parent); } else { result = this.Browser.AssertNot(value); } if (!result) { String msg = String.format("Browser AssertNot Found text in page: '%s'", value); this.report.Log(msg); } break; case BROWSER_assertPage: assertPage = this.clickToBool(event.get("assertPage").toString()); this.report.Log(String.format("Browser assertPage => '%s'.", assertPage)); break; default: System.out.printf("(!)ERROR: Unknown browser method: '%s'!\n", key_id); System.exit(3); } } } } catch (Exception exp) { this.report.ReportException(exp); } this.resetThreadTime(); this.report.Log("Browser Event finished."); return result; } /** * <thead> event * * @param event the <thead> event * @param parent this element's parent * @return the <thead> {@link WebElement} or null */ private WebElement theadEvent(VDDHash event, WebElement parent) { boolean required = true; boolean click = false; WebElement element = null; this.report.Log("thead event starting."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("thead event finished."); return element; } this.firePlugin(element, Elements.THEAD, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); //?? handleVars(element.getText(), event); if (event.containsKey("click") && this.clickToBool(event.get("click").toString())) { this.firePlugin(element, Elements.THEAD, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.THEAD, PluginEvent.AFTERCLICK); } if (event.containsKey("assert")) { String src = element.getText(); String val = this.replaceString(event.get("assert").toString()); this.report.Assert(val, src); } if (event.containsKey("assertnot")) { String src = element.getText(); String val = this.replaceString(event.get("assertnot").toString()); this.report.AssertNot(val, src); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("children") && element != null) { this.processEvents((Events) event.get("children"), element); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.report.Log("thead event finished."); return element; } /** * <tbody> event * * @param event the <tbody> event * @param parent this element's parent * @return the <tbody> {@link WebElement} or null */ private WebElement tbodyEvent(VDDHash event, WebElement parent) { boolean required = true; boolean click = false; WebElement element = null; this.report.Log("tbody event starting."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("thead event finished."); return element; } this.firePlugin(element, Elements.TBODY, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); //?? handleVars(element.getText(), event); if (event.containsKey("click") && this.clickToBool(event.get("click").toString())) { this.firePlugin(element, Elements.TBODY, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, Elements.TBODY, PluginEvent.AFTERCLICK); } if (event.containsKey("assert")) { String src = element.getText(); String val = this.replaceString(event.get("assert").toString()); this.report.Assert(val, src); } if (event.containsKey("assertnot")) { String src = element.getText(); String val = this.replaceString(event.get("assertnot").toString()); this.report.AssertNot(val, src); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("children") && element != null) { this.processEvents((Events) event.get("children"), element); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.report.Log("tbody event finished."); return element; } /** * Miscellaneous events. * * This meta-event is for elements that don't have any features * that distinguish themselves from other elements. Currently, * H1..H6, I, B, STRIKE, S, and U elements are handled here. * * @param event the event * @param parent this element's parent * @return the matching {@link WebElement} or null */ private WebElement miscEvent(VDDHash event, WebElement parent) { Elements type = (Elements) event.get("type"); String eventName = type.toString().toLowerCase(); boolean required = true; WebElement element = null; this.report.Log(eventName + " event starting."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log(eventName + " event finished."); return null; } this.firePlugin(element, type, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); String value = element.getText(); handleVars(value, event); if (event.containsKey("assert")) { String src = element.getText(); String val = this.replaceString(event.get("assert").toString()); this.report.Assert(val, src); } if (event.containsKey("assertnot")) { String src = element.getText(); String val = this.replaceString(event.get("assertnot").toString()); this.report.AssertNot(val, src); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("click") && this.clickToBool(event.get("click").toString())) { this.firePlugin(element, type, PluginEvent.BEFORECLICK); element.click(); this.firePlugin(element, type, PluginEvent.AFTERCLICK); } if (event.containsKey("children") && element != null) { this.processEvents((Events) event.get("children"), element); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.report.Log(eventName + " event finished."); return element; } /** * The value of the current event's timeout attribute. * * <p>Part of the doFindElements hack below.</p> */ private int hackTimeout; /** * Internal findElements wrapper. * * <p>This method is somewhat of a hack. The FindElements class on * the dev_v2 branch cleans up and centralizes all element finding. * Backporting, however, would be difficult and probably * counterproductive.</p> * * @param by how to look for the element * @param el parent element or null */ private List<WebElement> findElementsInternal(By by, WebElement el) throws NoSuchElementException { long end = System.currentTimeMillis() + this.hackTimeout * 1000; do { List<WebElement> lst = null; try { if (el != null) { lst = el.findElements(by); } else { lst = this.Browser.getDriver().findElements(by); } if (lst != null && lst.size() > 0) { return lst; } Thread.sleep(100); } catch (Exception e) { } } while (System.currentTimeMillis() < end); throw new NoSuchElementException("Failed to find element by " + by); } private WebElement findElement(VDDHash event, WebElement parent, boolean required) { WebElement element = null; By by = null; boolean href = false; boolean alt = false; boolean text = false; boolean value = false; boolean exists = true; String how = ""; String what = ""; int index = -1; int timeout = this.eventTimeout; String msg = ""; if (event.containsKey("exists")) { exists = this.clickToBool(replaceString((String) event.get("exists"))); } if (event.containsKey("timeout")) { timeout = Integer.valueOf(event.get("timeout").toString()); msg = String.format("Resetting default element finding timeout to: '%d' seconds.", timeout); this.report.Log(msg); } this.hackTimeout = timeout; // Hackily plumb the timeout attribute if (event.containsKey("index")) { String inx = event.get("index").toString(); inx = this.replaceString(inx); if (!Utils.isInt(inx)) { msg = String.format("Error: index value: '%s' is not an integer!", inx); this.report.ReportError(msg); return null; } index = Integer.valueOf(inx).intValue(); } this.resetThreadTime(); try { msg = ""; how = event.get("how").toString(); what = event.get(how).toString(); what = this.replaceString(what); String dowhat = event.get("do").toString(); this.report.Log("Trying to find page element '" + dowhat + "' by: " + "'" + how + "' => '" + what + "'" + ((index > -1 && !how.equals("index")) ? " index => '" + index + "'" : "") + "."); if (how.matches("class") && what.matches(".*\\s+.*")) { String elem_type = event.get("do").toString(); String old_how = how; how = "css"; String css_sel = String.format("%s[%s=\"%s\"]", elem_type, old_how, what); what = css_sel; } if (how.contains("index")) { how = "tagname"; what = event.get("do").toString(); if (what.contains("link")) { what = "a"; } if (what.contains("image")) { what = "img"; } } switch (ElementHow.valueOf(how.toUpperCase())) { case ID: by = By.id(what); break; case CLASS: by = By.className(what); break; case CSS: by = By.cssSelector(what); break; case LINK: by = By.linkText(what); break; case HREF: by = By.tagName("a"); href = true; break; case TEXT: text = true; break; case NAME: by = By.name(what); break; case PARLINK: by = By.partialLinkText(what); break; case TAGNAME: by = By.tagName(what); break; case XPATH: by = By.xpath(what); break; case VALUE: value = true; break; case ALT: alt = true; break; default: /* not reached */ this.report.ReportError(String.format("Error: findElement, unknown how: '%s'!\n", how)); System.exit(4); break; } if (href) { element = this.findElementByHref(event.get("href").toString(), parent); } else if (alt) { element = this.findElementByAlt(event.get("alt").toString(), parent); } else if (value) { element = this.slowFindElement((String) event.get("html_tag"), (String) event.get("html_type"), what, parent, index); } else if (text) { element = this.findElementByText((String) event.get("html_tag"), (String) event.get("text"), parent, index); } else { List<WebElement> elements; elements = findElementsInternal(by, parent); elements = filterElements(elements, event); index = (index < 0) ? 0 : index; if (index >= elements.size()) { String m = ((index > 0) ? "No elements found." : String.format("Index (%d) out of bounds.", index)); throw new NoSuchElementException(m); } element = elements.get(index); } } catch (NoSuchElementException exp) { element = null; } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.resetThreadTime(); if (element == null && exists == true) { if (required) { msg = String.format("Failed to find element: '%s' => '%s'", how, what); this.report.ReportError(msg); } else { msg = String.format("Failed to find element, but required => 'false' : '%s' => '%s'", how, what); this.report.Log(msg); } } else if (element != null && exists != true) { this.report.ReportError("Found element with exist => 'false'!"); } else if (element == null && exists != true) { this.report.Log("Did not find element as exist => 'false'."); } else { this.report.Log("Found element."); } return element; } /** * Determine whether tag is found in filter * * @param tag HTML tag to search for * @param filter list of acceptable HTML tags * @return true if tag is in filter, false otherwise */ private boolean checkMatch(String tag, String filter) { if (filter == null) { return true; } else if (tag == null) { return false; } String filters[] = filter.split("\\|"); for (int k = 0; k < filters.length; k++) { if (tag.equals(filters[k])) { return true; } } return false; } /** * Filter elements by HTML element type * * Take a list of {@link WebElement}s found using the event selection * criteria and filter through by HTML tag type. * * @param elements element list returned based on event * @param event current event * @return list of elements filtered by HTML tag */ private List<WebElement> filterElements(List<WebElement> elements, VDDHash event) { ArrayList<WebElement> filtered = new ArrayList<WebElement>(elements.size()); String html_tag = (String) event.get("html_tag"); String html_type = (String) event.get("html_type"); for (int k = 0; k < elements.size(); k++) { WebElement e = elements.get(k); if (checkMatch(e.getTagName(), html_tag) && checkMatch(e.getAttribute("type"), html_type)) { filtered.add(e); } } return filtered; } /** * Find an input element using its value attribute. * * This uses the Selectors API to search through the elements on * the page for those with matching values. * * @param tag the element's tag name * @param type the element's tag type * @param how the value being searched for * @param parent root element of the search or null * @param index index into the list of found elements * @return the element found or null */ @SuppressWarnings("unchecked") private WebElement slowFindElement(String tag, String type, String how, WebElement parent, int index) { WebElement element = null; ArrayList<WebElement> list = new ArrayList<WebElement>(); String msg = ""; String js = ""; msg = String.format("Looking for elements by value is very very slow! You should never do this!"); this.report.Log(msg); msg = String.format("Looking for element: '%s' => '%s'.", tag, how); this.report.Log(msg); String root = (parent == null) ? "document" : "arguments[0]"; String[] tags = tag.split("\\|"); String[] types = (type == null) ? new String[0] : type.split("\\|"); if (types.length == 0 || this.Browser instanceof IE) { /* * IE only supports the Selectors API with versions 8 and * greater, and then only when rendering the document in * standards mode. Given the special tags required for IE to * enter standards mode, it is safe to assume that any page * VDD is testing will probably be in quirks mode, making the * Selectors API unavailable. So in the case of IE, look for * matching elements by iterating through all elements. * Slow, but it'll at least work. */ String tagFinder = ""; for (int k = 0; k < tags.length; k++) { tagFinder += String.format("finder(%s.getElementsByTagName('%s'))%s", root, tags[k], (k == tags.length - 1) ? "" : ","); } String typeFilter = ""; for (int k = 0; k < types.length; k++) { typeFilter += String.format("args[k].type == '%s'%s", types[k], (k == types.length - 1) ? "" : " || "); } if (typeFilter.length() == 0) { typeFilter = "true"; // Accept all types if none was specified } js = String.format("function finder(args) {\n" + " var found = [];\n" + " for (var k = 0; k < args.length; k++) {\n" + " if (args[k].value == '%s' &&\n" + " (%s)) {\n" + " found.push(args[k]);\n" + " }\n" + " }\n" + " return found;\n" + "}\n" + "return [].concat(%s);\n", how, typeFilter, tagFinder); } else { /* * Not IE, use the Selectors API. */ String selectors = ""; for (int i = 0; i < tags.length; i++) { for (int j = 0; j < types.length; j++) { selectors += String.format("%s[type=\"%s\"][value=\"%s\"]%s", tags[i], types[j], how, ((i == tags.length - 1) && (j == types.length - 1)) ? "" : ","); } } js = String.format("return %s.querySelectorAll('%s', true);", root, selectors); } list = (ArrayList<WebElement>) this.Browser.executeJS(js, parent); if (list.size() > 0) { element = list.get(index < 0 ? 0 : index); } return element; } private WebElement findElementByHref(String href, WebElement parent) { WebElement element = null; List<WebElement> list = null; href = this.replaceString(href); list = findElementsInternal(By.tagName("a"), parent); int len = list.size() - 1; for (int i = 0; i <= len; i++) { String value = list.get(i).getAttribute("href"); if (value != null && href.compareTo(value) == 0) { element = list.get(i); break; } } return element; } /** * Find an image element based on its alt text. * * This routine returns the first image encountered with matching * alt text. * * @param alt alt text to search for * @param parent parent element or null * @return matching {@link WebElement} or null */ private WebElement findElementByAlt(String alt, WebElement parent) { By by = By.tagName("img"); List<WebElement> elementList = null; alt = this.replaceString(alt); elementList = findElementsInternal(by, parent); for (WebElement element : elementList) { String elementAlt = element.getAttribute("alt"); if (elementAlt != null && elementAlt.equals(alt)) { return element; } } return null; } /** * Find an element by its innerText attribute. * * @param tag the HTML tag of the element * @param text the text to search for * @return matching {@link WebElement} or null */ private WebElement findElementByText(String tag, String text, WebElement parent, int index) { By by = null; List<WebElement> elements = null; text = this.replaceString(text); if (tag.equals("a")) { by = By.linkText(text); } else { by = By.tagName(tag); } elements = findElementsInternal(by, parent); if (!tag.equals("a")) { ArrayList<WebElement> discard = new ArrayList<WebElement>(); for (WebElement e : elements) { if (!e.getText().contains(text)) { discard.add(e); // Can't delete elements while iterating } } elements.removeAll(discard); } index = (index < 0) ? 0 : index; // -1 is default value if (index >= elements.size()) { return null; } return elements.get(index); } /* * clickToBool -- method This method converts a string into a boolean type. * * Input: clickstr: a string containing "true" or "false". Case doesn't * matter. * * Output: returns a boolean type. */ private boolean clickToBool(String clickstr) { boolean result = false; if (clickstr.toLowerCase().contains("true") || clickstr.toLowerCase().contains("false")) { result = Boolean.valueOf(clickstr).booleanValue(); } else { if (clickstr.contains("0")) { result = false; } else { result = true; } } return result; } /** * Perform pre-fire checks for plugin execution. * * If this routine returns false, plugin execution will be skipped. * * @return true if plugins can execute, false otherwise */ private boolean pluginPrefireCheck() { if (this.plugins.size() == 0) { return false; } if (!this.windowExists(this.getCurrentHWND())) { this.report.Log("Browser window closed. Skipping plugin execution."); return false; } return true; } /** * Execute all plugins. * * @param element the element on the current HTML page * @return true if all plugins succeeded, false otherwise */ private boolean firePlugin(WebElement element) { boolean result = true; PluginData data = new PluginData(); if (!pluginPrefireCheck()) { return true; } data.setElement(element); data.setBrowser(this.Browser); data.setSodaVars(this.sodaVars); data.setHijacks(this.hijacks); data.setTestName(this.testName); for (Plugin plugin : this.plugins) { result &= plugin.execute(data, this.report); } return result; } /** * Execute all plugins that match a plugin event. * * @param element the element on the current HTML page * @param eventType the type of plugin event * @return true if all plugins succeeded, false otherwise */ private boolean firePlugin(WebElement element, PluginEvent eventType) { boolean result = true; PluginData data = new PluginData(); if (!pluginPrefireCheck()) { return true; } data.setElement(element); data.setBrowser(this.Browser); data.setSodaVars(this.sodaVars); data.setHijacks(this.hijacks); data.setTestName(this.testName); for (Plugin plugin : this.plugins) { if (plugin.matches(eventType)) { result &= plugin.execute(data, this.report); } } return result; } /** * Execute a plugin that matches the current element and plugin event. * * @param element the element on the current HTML page * @param type the element's type * @param eventType the type of plugin event * @return true if all plugins succeeded, false otherwise */ private boolean firePlugin(WebElement element, Elements elementType, PluginEvent eventType) { boolean result = true; PluginData data = new PluginData(); if (!pluginPrefireCheck()) { return true; } data.setElement(element); data.setBrowser(this.Browser); data.setSodaVars(this.sodaVars); data.setHijacks(this.hijacks); data.setTestName(this.testName); for (Plugin plugin : this.plugins) { if (plugin.matches(elementType, eventType)) { result &= plugin.execute(data, this.report); } } return result; } private WebElement buttonEvent(VDDHash event, WebElement parent) { boolean click = true; boolean required = true; WebElement element = null; this.resetThreadTime(); this.report.Log("Starting button event."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Finished button event."); return null; } String value = element.getAttribute("value"); handleVars(value, event); this.firePlugin(element, Elements.BUTTON, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("click")) { click = this.clickToBool(event.get("click").toString()); } if (event.containsKey("alert")) { boolean alert = this.clickToBool(event.get("alert").toString()); this.report.Log(String.format("Setting Alert Hack to: '%s'", alert)); this.Browser.alertHack(alert); this.report.Warn("You are using a deprecated alert hack, please use the <alert> command!"); } if (click) { this.firePlugin(element, Elements.BUTTON, PluginEvent.BEFORECLICK); this.report.Log("Clicking button."); element.click(); this.report.Log("Finished clicking button."); this.firePlugin(element, Elements.BUTTON, PluginEvent.AFTERCLICK); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.resetThreadTime(); this.report.Log("Finished button event."); return element; } private WebElement textareaEvent(VDDHash event, WebElement parent) { boolean required = true; WebElement element = null; this.resetThreadTime(); this.report.Log("Starting textarea event."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Finished textarea event."); return null; } String value = element.getAttribute("value"); handleVars(value, event); this.firePlugin(element, Elements.TEXTAREA, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("set")) { value = event.get("set").toString(); value = this.replaceString(value); this.report.Log(String.format("Setting Value to: '%s'.", value)); clearText(element); element.sendKeys(value); } else if (event.containsKey("clear")) { if (this.clickToBool(event.get("clear").toString())) { this.report.Log("Clearing textarea."); clearText(element); } } else if (event.containsKey("append")) { value = event.get("append").toString(); value = this.replaceString(value); this.report.Log(String.format("Appending Value to: '%s'.", value)); element.sendKeys(value); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("assert")) { String assvalue = event.get("assert").toString(); assvalue = this.replaceString(assvalue); this.report.Assert(assvalue, element.getAttribute("value")); } if (event.containsKey("assertnot")) { String assvalue = event.get("assertnot").toString(); assvalue = this.replaceString(assvalue); this.report.AssertNot(assvalue, element.getAttribute("value")); } if (event.containsKey("click") && this.clickToBool(event.get("click").toString())) { this.firePlugin(element, Elements.TEXTAREA, PluginEvent.BEFORECLICK); this.report.Log("Clicking textarea."); element.click(); this.report.Log("Finished clicking textarea."); this.firePlugin(element, Elements.TEXTAREA, PluginEvent.AFTERCLICK); } } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.resetThreadTime(); this.report.Log("Finished textarea event."); return element; } /** * Clear a text, password, or email input or a textfield element. * * Using javascript on IE instead of WebElement.clear() is needed * due to Selenium issue 3402. * * @param e the text element to clear */ void clearText(WebElement e) { if (this.Browser instanceof IE) { this.Browser.executeJS("arguments[0].value = '';", e); } else { e.clear(); } } /** * <textfield> event * * @param event the <textfield> event * @param parent this element's parent * @return the <textfield> {@link WebElement} or null */ private WebElement textfieldEvent(VDDHash event, WebElement parent) { boolean required = true; WebElement element = null; this.resetThreadTime(); this.report.Log("Starting textfield event."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Finished textfield event."); return null; } this.firePlugin(element, Elements.TEXTFIELD, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("clear")) { if (this.clickToBool(event.get("clear").toString())) { this.report.Log("Clearing textfield."); clearText(element); } } if (event.containsKey("set")) { String value = event.get("set").toString(); value = this.replaceString(value); this.report.Log(String.format("Setting Value to: '%s'.", value)); clearText(element); element.sendKeys(value); } if (event.containsKey("append")) { String value = event.get("append").toString(); value = this.replaceString(value); element.sendKeys(value); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("assert")) { String assvalue = event.get("assert").toString(); assvalue = this.replaceString(assvalue); this.report.Assert(assvalue, element.getAttribute("value")); } if (event.containsKey("assertnot")) { String assvalue = event.get("assertnot").toString(); assvalue = this.replaceString(assvalue); this.report.AssertNot(assvalue, element.getAttribute("value")); } String value = element.getAttribute("value"); handleVars(value, event); } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.resetThreadTime(); this.report.Log("Finished textfield event."); return element; } /** * Handle a <password> event * * @param event the <password> event * @param parent this element's parent * @return the <input type="password" {@link WebElement} or null */ private WebElement passwordEvent(VDDHash event, WebElement parent) { boolean required = true; WebElement element = null; this.resetThreadTime(); this.report.Log("Starting password event."); if (event.containsKey("required")) { required = this.clickToBool(event.get("required").toString()); } try { element = this.findElement(event, parent, required); if (element == null) { this.report.Log("Finished password event."); return null; } this.firePlugin(element, Elements.PASSWORD, PluginEvent.AFTERFOUND); this.checkDisabled(event, element); if (event.containsKey("clear")) { if (this.clickToBool(event.get("clear").toString())) { this.report.Log("Clearing password field."); clearText(element); } } if (event.containsKey("set")) { String value = event.get("set").toString(); value = this.replaceString(value); this.report.Log(String.format("Setting Value to: '%s'.", value)); clearText(element); element.sendKeys(value); } if (event.containsKey("append")) { String value = event.get("append").toString(); value = this.replaceString(value); element.sendKeys(value); } if (event.containsKey("jscriptevent")) { this.report.Log("Firing Javascript Event: " + event.get("jscriptevent").toString()); this.Browser.fire_event(element, event.get("jscriptevent").toString()); Thread.sleep(1000); this.report.Log("Javascript event finished."); } if (event.containsKey("assert")) { String assvalue = event.get("assert").toString(); assvalue = this.replaceString(assvalue); this.report.Assert(assvalue, element.getAttribute("value")); } if (event.containsKey("assertnot")) { String assvalue = event.get("assertnot").toString(); assvalue = this.replaceString(assvalue); this.report.AssertNot(assvalue, element.getAttribute("value")); } String value = element.getAttribute("value"); handleVars(value, event); } catch (ElementNotVisibleException exp) { logElementNotVisible(required, event); } catch (Exception exp) { this.report.ReportException(exp); element = null; } this.resetThreadTime(); this.report.Log("Finished password event."); return element; } private boolean putsEvent(VDDHash event) { boolean result = false; String msg = ""; if (event.containsKey("txt")) { msg = this.replaceString(event.get("txt").toString()); } else { msg = this.replaceString(event.get("text").toString()); } this.resetThreadTime(); this.report.Log(msg); result = true; this.resetThreadTime(); return result; } private void checkDisabled(VDDHash event, WebElement element) { String value = null; if (!event.containsKey("disabled")) { return; } value = event.get("disabled").toString(); value = this.replaceString(value); try { Utils.isEnabled(element, this.report, Boolean.valueOf(value)); } catch (Exception exp) { this.report.ReportException(exp); } } private void handleVars(String value, VDDHash event) { if (event.containsKey("var")) { String name = event.get("var").toString(); VDDHash tmp = new VDDHash(); tmp.put("set", value); tmp.put("var", name); this.varEvent(tmp); } } }