| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.ofbiz.testtools.selenium; |
| |
| import com.thoughtworks.selenium.CommandProcessor; |
| import com.thoughtworks.selenium.HttpCommandProcessor; |
| import com.thoughtworks.selenium.SeleniumException; |
| import javolution.util.FastList; |
| import javolution.util.FastMap; |
| import org.ofbiz.base.util.Debug; |
| import org.ofbiz.base.util.UtilGenerics; |
| import org.ofbiz.base.util.UtilHttp; |
| import org.ofbiz.base.util.UtilProperties; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Map; |
| |
| public class SeleniumHtml { |
| public static final String module = SeleniumHtml.class.getName(); |
| public String host; |
| public int port; |
| public String browser; |
| public String baseUrl; |
| |
| Document document; |
| CommandProcessor commandProcessor; |
| |
| class TestSuite { |
| public File file; |
| public String name; |
| public Test tests[]; |
| public boolean result; |
| } |
| class Test { |
| public String label; |
| public File file; |
| public String name; |
| public Command commands[]; |
| public boolean result; |
| } |
| class Command { |
| public String cmd; |
| public String args[]; |
| public String result; |
| public boolean error; |
| public boolean failure; |
| } |
| |
| public SeleniumHtml() { |
| } |
| |
| public void setHost(String host) { |
| this.host = host; |
| } |
| |
| public void setPort(int port) { |
| this.port = port; |
| } |
| |
| public void setBrowser(String browser) { |
| this.browser = browser; |
| } |
| |
| public void setBaseUrl(String baseUrl) { |
| this.baseUrl = baseUrl; |
| } |
| |
| public static String runHtmlTestSuite(HttpServletRequest request, HttpServletResponse response) throws Exception { |
| Map<String, Object> parameters = UtilHttp.getParameterMap(request); |
| String para = (String) parameters.get("testSuitePath"); |
| |
| try { |
| SeleniumHtml client = new SeleniumHtml(); |
| client.host = UtilProperties.getPropertyValue("seleniumXml.properties", "serverHost", "localhost"); |
| client.port = Integer.parseInt(UtilProperties.getPropertyValue("seleniumXml.properties", "proxyPort", "4444")); |
| client.browser = UtilProperties.getPropertyValue("seleniumXml.properties", "browser", "*firefox"); |
| client.baseUrl = UtilProperties.getPropertyValue("seleniumXml.properties", "startUrlHttps", "https://localhost:8443"); |
| |
| if (Debug.infoOn()) { |
| Debug.logInfo("Parameters used for selenium: host: " + client.host |
| + ", port: " + client.port + ", browser: " + client.browser |
| + ", baseUrl: " + client.baseUrl, module); |
| } |
| |
| File testFile = new File(para.trim()); |
| if (testFile.exists()) { |
| if (Debug.infoOn()) Debug.logInfo("Running this testsuite: " + testFile.getAbsolutePath(), module); |
| |
| Map<String, Object> results = client.runSuite(testFile.getAbsolutePath()); |
| if ("true".equals(results.get("status").toString())) { |
| request.setAttribute("_EVENT_MESSAGE_LIST_", results.get("logs")); |
| } else { |
| request.setAttribute("_ERROR_MESSAGE_LIST_", results.get("logs")); |
| } |
| } |
| } catch (Exception e) { |
| Debug.logError(e.getMessage(), module); |
| return "error"; |
| } |
| return "success"; |
| } |
| |
| public Map<String, Object> runSuite(String filename) throws Exception { |
| TestSuite suite = new TestSuite(); |
| suite.file = new File(filename); |
| File suiteDirectory = suite.file.getParentFile(); |
| this.document = parseDocument(filename); |
| Element table = (Element) this.document.getElementsByTagName("table").item(0); |
| NodeList tableRows = table.getElementsByTagName("tr"); |
| Element tableNameRow = (Element) tableRows.item(0); |
| suite.name = tableNameRow.getTextContent(); |
| suite.result = true; |
| suite.tests = new Test[tableRows.getLength() - 1]; |
| |
| Map<String, Object> results = FastMap.newInstance(); |
| List<String> messages = FastList.newInstance(); |
| Map<String, Object> testResults; |
| |
| for (int i = 1; i < tableRows.getLength(); i++) { |
| Element tableRow = (Element) tableRows.item(i); |
| Element cell = (Element) tableRow.getElementsByTagName("td").item(0); |
| Element link = (Element) cell.getElementsByTagName("a").item(0); |
| Test test = new Test(); |
| test.label = link.getTextContent(); |
| test.file = new File(suiteDirectory, link.getAttribute("href")); |
| |
| SeleniumHtml subclient = new SeleniumHtml(); |
| subclient.setHost(this.host); |
| subclient.setPort(this.port); |
| subclient.setBrowser(this.browser); |
| subclient.setBaseUrl(this.baseUrl); |
| testResults = subclient.runTest(test); |
| suite.result &= test.result; |
| suite.tests[i - 1] = test; |
| messages = UtilGenerics.checkList(testResults.get("log")); |
| } |
| |
| results.put("logs", messages); |
| results.put("status", suite.result); |
| return results; |
| } |
| |
| public Map<String, Object> runTest(Test test) throws Exception { |
| String filename = test.file.toString(); |
| List<String> messages = FastList.newInstance(); |
| |
| if (Debug.infoOn()) { |
| Debug.logInfo("Running " + filename + " against " + this.host + ":" + this.port + " with " + this.browser, module); |
| } |
| this.document = parseDocument(filename); |
| |
| if (this.baseUrl == null) { |
| NodeList links = this.document.getElementsByTagName("link"); |
| if (links.getLength() != 0) { |
| Element link = (Element) links.item(0); |
| setBaseUrl(link.getAttribute("href")); |
| } |
| } |
| if (Debug.infoOn()) { |
| Debug.logInfo("Base URL=" + this.baseUrl, module); |
| } |
| |
| Node body = this.document.getElementsByTagName("body").item(0); |
| Element resultContainer = document.createElement("div"); |
| resultContainer.setTextContent("Result: "); |
| Element resultElt = document.createElement("span"); |
| resultElt.setAttribute("id", "result"); |
| resultElt.setIdAttribute("id", true); |
| resultContainer.appendChild(resultElt); |
| body.insertBefore(resultContainer, body.getFirstChild()); |
| |
| Element executionLogContainer = document.createElement("div"); |
| executionLogContainer.setTextContent("Execution Log:"); |
| Element executionLog = document.createElement("div"); |
| executionLog.setAttribute("id", "log"); |
| executionLog.setIdAttribute("id", true); |
| executionLog.setAttribute("style", "white-space: pre;"); |
| executionLogContainer.appendChild(executionLog); |
| body.appendChild(executionLogContainer); |
| |
| NodeList tableRows = document.getElementsByTagName("tr"); |
| Element theadRow = (Element) tableRows.item(0); |
| test.name = theadRow.getTextContent(); |
| |
| this.commandProcessor = new HtmlCommandProcessor(this.host, this.port, this.browser, this.baseUrl); |
| String resultState; |
| String resultLog; |
| test.result = true; |
| try { |
| this.commandProcessor.start(); |
| test.commands = new Command[tableRows.getLength() - 1]; |
| for (int i = 1; i < tableRows.getLength(); i++) { |
| Element stepRow = (Element) tableRows.item(i); |
| Command command = executeStep(stepRow); |
| messages.add(command.result + " --> " + command.cmd + " " + Arrays.asList(command.args)); |
| |
| test.commands[i - 1] = command; |
| if (command.error) { |
| test.result = false; |
| } |
| if (command.failure) { |
| test.result = false; |
| break; |
| } |
| } |
| resultState = test.result ? "PASSED" : "FAILED"; |
| resultLog = (test.result ? "Test Complete" : "Error"); |
| this.commandProcessor.stop(); |
| } catch (Exception e) { |
| test.result = false; |
| resultState = "ERROR"; |
| resultLog = "Failed to initialize session\n" + e; |
| e.printStackTrace(); |
| } |
| document.getElementById("result").setTextContent(resultState); |
| Element log = document.getElementById("log"); |
| log.setTextContent(log.getTextContent() + resultLog + "\n"); |
| |
| Map<String, Object> results = FastMap.newInstance(); |
| results.put("log", messages); |
| |
| return results; |
| } |
| |
| public Command executeStep(Element stepRow) throws Exception { |
| Command command = new Command(); |
| NodeList stepFields = stepRow.getElementsByTagName("td"); |
| String cmd = stepFields.item(0).getTextContent().trim(); |
| command.cmd = cmd; |
| ArrayList<String> argList = new ArrayList<String>(); |
| if (stepFields.getLength() == 1) { |
| // skip comments |
| command.result = "OK"; |
| return command; |
| } |
| for (int i = 1; i < stepFields.getLength(); i++) { |
| String content = stepFields.item(i).getTextContent(); |
| content = content.replaceAll(" +", " "); |
| content = content.replace('\u00A0', ' '); |
| content = content.trim(); |
| argList.add(content); |
| } |
| String args[] = argList.toArray(new String[0]); |
| command.args = args; |
| if (Debug.infoOn()) { |
| Debug.logInfo(cmd + " " + Arrays.asList(args), module); |
| } |
| try { |
| command.result = this.commandProcessor.doCommand(cmd, args); |
| command.error = false; |
| } catch (Exception e) { |
| command.result = e.getMessage(); |
| command.error = true; |
| } |
| command.failure = command.error && !cmd.startsWith("verify"); |
| return command; |
| } |
| |
| Document parseDocument(String filename) throws Exception { |
| FileReader reader = new FileReader(filename); |
| String firstLine = new BufferedReader(reader).readLine(); |
| reader.close(); |
| Document document = null; |
| if (firstLine.startsWith("<?xml")) { |
| Debug.logInfo("XML detected; using default XML parser.", module); |
| } else { |
| try { |
| Class<?> nekoParserClass = Class.forName("org.cyberneko.html.parsers.DOMParser"); |
| Object parser = nekoParserClass.newInstance(); |
| Method parse = nekoParserClass.getMethod("parse", new Class[] { String.class }); |
| Method getDocument = nekoParserClass.getMethod("getDocument", new Class[0]); |
| parse.invoke(parser, filename); |
| document = (Document) getDocument.invoke(parser); |
| } catch (Exception e) { |
| Debug.logInfo("NekoHTML HTML parser not found; HTML4 support disabled.", module); |
| } |
| } |
| if (document == null) { |
| DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
| try { // http://www.w3.org/blog/systeam/2008/02/08/w3c_s_excessive_dtd_traffic |
| factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); |
| } |
| catch (ParserConfigurationException e) { |
| Debug.logInfo("Warning: Could not disable external DTD loading", module); |
| } |
| DocumentBuilder builder = factory.newDocumentBuilder(); |
| document = builder.parse(filename); |
| } |
| return document; |
| } |
| |
| class HtmlCommandProcessor extends HttpCommandProcessor { |
| final static String INDEX_SPECIFIER = "index="; |
| final static String ID_SPECIFIER = "id="; |
| final static String LABEL_SPECIFIER = "label="; |
| final static String VALUE_SPECIFIER = "value="; |
| |
| boolean expectError; |
| |
| public HtmlCommandProcessor(String host, int port, String browser, String baseUrl) { |
| super(host, port, browser, baseUrl); |
| } |
| |
| public String doCommand(String cmd, String args[]) { |
| if (cmd.equals("store")) { |
| cmd += "Expression"; |
| } else if (cmd.equals("assertSelected") || cmd.equals("verifySelected")) { |
| if (args[1].startsWith(INDEX_SPECIFIER)) { |
| cmd += "Index"; |
| args[1] = args[1].substring(INDEX_SPECIFIER.length()); |
| } else if (args[1].startsWith(ID_SPECIFIER)) { |
| cmd += "Id"; |
| args[1] = args[1].substring(ID_SPECIFIER.length()); |
| } else if (args[1].startsWith(LABEL_SPECIFIER)) { |
| cmd += "Label"; |
| args[1] = args[1].substring(LABEL_SPECIFIER.length()); |
| } else if (args[1].startsWith(VALUE_SPECIFIER)) { |
| cmd += "Value"; |
| args[1] = args[1].substring(VALUE_SPECIFIER.length()); |
| } else { |
| cmd += "Label"; |
| } |
| } else if (cmd.endsWith("ErrorOnNext") || cmd.endsWith("FailureOnNext")) { |
| expectError = true; |
| return "OK"; |
| } else if (cmd.equals("echo")) { |
| return "OK," + args[0]; |
| } else if (cmd.equals("pause")) { |
| try { |
| Thread.sleep(Integer.parseInt(args[0])); |
| return "OK"; |
| } catch (InterruptedException e) { |
| return "ERROR: pause interrupted"; |
| } |
| } |
| try { |
| String result = super.doCommand(cmd, args); |
| if (expectError) { |
| throw new SeleniumException("ERROR: Error expected"); |
| } else { |
| return result; |
| } |
| } catch (SeleniumException e) { |
| if (expectError) { |
| expectError = false; |
| return "OK"; |
| } else { |
| throw e; |
| } |
| } |
| } |
| } |
| } |