mustella equivalents in java for marmotinni
Project: http://git-wip-us.apache.org/repos/asf/flex-asjs/repo Commit: http://git-wip-us.apache.org/repos/asf/flex-asjs/commit/8a08cdd2 Tree: http://git-wip-us.apache.org/repos/asf/flex-asjs/tree/8a08cdd2 Diff: http://git-wip-us.apache.org/repos/asf/flex-asjs/diff/8a08cdd2 Branch: refs/heads/develop Commit: 8a08cdd23ec008a13a38ae5fee6ca4d3bccf300b Parents: a65ada5 Author: Alex Harui <aha...@apache.org> Authored: Fri Mar 21 08:22:46 2014 -0700 Committer: Alex Harui <aha...@apache.org> Committed: Fri Mar 21 08:22:46 2014 -0700 ---------------------------------------------------------------------- .../src/marmotinni/AssertPropertyValue.java | 104 +++++++ mustella/java/src/marmotinni/AssertStep.java | 54 ++++ .../java/src/marmotinni/DispatchKeyEvent.java | 203 +++++++++++++ .../src/marmotinni/DispatchMouseClickEvent.java | 205 +++++++++++++ .../java/src/marmotinni/DispatchMouseEvent.java | 222 ++++++++++++++ .../java/src/marmotinni/MarmotinniRunner.java | 250 ++++++++++++++++ mustella/java/src/marmotinni/SetProperty.java | 119 ++++++++ mustella/java/src/marmotinni/TestCase.java | 293 +++++++++++++++++++ mustella/java/src/marmotinni/TestOutput.java | 36 +++ mustella/java/src/marmotinni/TestStep.java | 124 ++++++++ 10 files changed, 1610 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/8a08cdd2/mustella/java/src/marmotinni/AssertPropertyValue.java ---------------------------------------------------------------------- diff --git a/mustella/java/src/marmotinni/AssertPropertyValue.java b/mustella/java/src/marmotinni/AssertPropertyValue.java new file mode 100644 index 0000000..253cc49 --- /dev/null +++ b/mustella/java/src/marmotinni/AssertPropertyValue.java @@ -0,0 +1,104 @@ +/* + * + * 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 marmotinni; + +import org.openqa.selenium.JavascriptExecutor; + +import org.xml.sax.Attributes; + +/** + * AssertPropertyValue + * + * Assert if a property is set to the right value + */ +public class AssertPropertyValue extends AssertStep { + + + public AssertPropertyValue () { + } + + /** + * The name of the property to test + */ + public String propertyName; + + /** + * The value the property should have + */ + public String value; + + /** + * The value the property should have + */ + public String valueExpression; + + @Override + public void populateFromAttributes(Attributes attributes) + { + target = attributes.getValue("target"); + propertyName = attributes.getValue("propertyName"); + value = attributes.getValue("value"); + valueExpression = attributes.getValue("valueExpression"); + } + + @Override + protected void doStep() + { + + StringBuilder getScript = new StringBuilder(); + insertTargetScript(getScript, target); + getScript.append("if (typeof(target['get_' + '" + propertyName + "']) == 'function') return target['get_' + '" + propertyName + "']();"); + getScript.append(" else return target['" + propertyName + "'];"); + if (TestStep.showScripts) + System.out.println(getScript.toString()); + String actualValue = ((JavascriptExecutor)webDriver).executeScript(getScript.toString()).toString(); + String valueString = null; + if (valueExpression != null) + valueString = ((JavascriptExecutor)webDriver).executeScript(valueExpression).toString(); + else if (value != null) + valueString = value; + else + valueString = "null"; + + if (!valueString.equals(actualValue)) + { + testResult.doFail(target + "." + propertyName + " " + actualValue + " != " + valueString); + } + + } + + /** + * customize string representation + */ + @Override + public String toString() + { + String s = "AssertPropertyValue"; + if (target != null) + s += ": target = " + target.toString(); + if (propertyName != null) + s += ": propertyName = " + propertyName.toString(); + if (value != null) + s += ": value = " + value; + return s; + } + +} http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/8a08cdd2/mustella/java/src/marmotinni/AssertStep.java ---------------------------------------------------------------------- diff --git a/mustella/java/src/marmotinni/AssertStep.java b/mustella/java/src/marmotinni/AssertStep.java new file mode 100644 index 0000000..a8223f7 --- /dev/null +++ b/mustella/java/src/marmotinni/AssertStep.java @@ -0,0 +1,54 @@ +/* + * + * 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 marmotinni; + +import org.openqa.selenium.WebDriver; + +/** + * AssertStep + * + * Base class for test steps like AssertPropertyValue, etc. + */ +public class AssertStep extends TestStep { + + + public AssertStep () { + } + + /** + * Called by the test case in case you need to set up before execute() + */ + public void preview(WebDriver webDriver, TestCase testCase, TestResult testResult) + { + this.webDriver = webDriver; + this.testCase = testCase; + this.testResult = testResult; + } + + public String target; + + /** + * Called by the test case in case you need to clean up after execute() + */ + public void cleanup() + { + } +} http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/8a08cdd2/mustella/java/src/marmotinni/DispatchKeyEvent.java ---------------------------------------------------------------------- diff --git a/mustella/java/src/marmotinni/DispatchKeyEvent.java b/mustella/java/src/marmotinni/DispatchKeyEvent.java new file mode 100644 index 0000000..1361432 --- /dev/null +++ b/mustella/java/src/marmotinni/DispatchKeyEvent.java @@ -0,0 +1,203 @@ +/* + * + * 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 marmotinni; + +import java.util.ArrayList; + +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import org.xml.sax.Attributes; + +/** + * DispatchKeyEvent + * + * Dispatch keys to the currently focused element + */ +public class DispatchKeyEvent extends TestStep { + + public DispatchKeyEvent () { + } + + // These are constants from flash.ui.Keyboard. They are not + // available in non-AIR compilations, so they are reproduced here + // to avoid compile errors. + // If they change in AIR, these will need to be updated. + public static final int FLASH_UI_KEYBOARD_BACK = 0x01000016; + public static final int FLASH_UI_KEYBOARD_MENU = 0x01000012; + public static final int FLASH_UI_KEYBOARD_SEARCH = 0x0100001F; + + private StringBuilder charSequence; + private int currentRepeat; + private int currentKey; + private boolean sendBoth; + + /** + * Set the target's property to the specified value + */ + @Override + protected void doStep() + { + sendBoth = false; + + if (type == null) + { + sendBoth = true; + type = "keyDown"; + } + + int i; + int j; + int n; + charSequence = new StringBuilder(); + + try + { + for (i = 0; i < repeatCount; i++) + { + WebElement focusedElement = (WebElement)((JavascriptExecutor)webDriver).executeScript("return document.activeElement"); + if (chars != null) + { + charSequence.append(chars); + focusedElement.sendKeys(charSequence); + } + else if (keys != null) + { + n = keys.size(); + for (j = 0; j < n; i++) + { + Keys key = Keys.valueOf(keys.get(j)); + focusedElement.sendKeys(key); + // could be new focused element if tab or similar + focusedElement = (WebElement)((JavascriptExecutor)webDriver).executeScript("return document.activeElement"); + } + } + else + { + testResult.doFail("no keys specified"); + return; + } + } + } + catch (Exception e1) + { + TestOutput.logResult("Exception thrown in DispatchKeyEvent."); + testResult.doFail (e1.getMessage()); + } + + } + + /** + * (Optional) name of a UI object whose Window/Stage + * will be used to dispatch the event + */ + public String window; + + /** + * The type of the event to send (keyUp, keyDown, etc). + * If not set, we'll send both a keyDown and a keyUp + */ + public String type; + + /** + * The char or sequence of chars to send as a string/char if you don't know the charCode (optional) + */ + public String chars; + + /** + * The ctrlKey property on the KeyboardEvent (optional) + */ + public boolean ctrlKey; + + /** + * The sequence of keys (optional) e.g ["LEFT", "UP"] + */ + public ArrayList<String> keys; + + /** + * The keyLocation property on the KeyboardEvent (optional) + */ + public int keyLocation; + + /** + * The number of times to repeat the sequence (optional) + */ + public int repeatCount = 1; + + /** + * The shiftKey property on the KeyboardEvent (optional) + */ + public boolean shiftKey; + + /** + * Designate the created event to be cancelable. by default, they are not + */ + public boolean cancelable = false; + + /** + * customize string representation + */ + @Override + public String toString() + { + String s = "DispatchKeyEvent"; + if (chars != null) + s += ": char = " + chars; + if (keys != null) + s += ": keys = " + keys.toString(); + if (type != null) + s += ", type = " + type; + if (shiftKey) + s += ", shiftKey = true"; + if (ctrlKey) + s += ", ctrlKey = true"; + if (repeatCount != 0) + s += ", repeatCount = " + Integer.toString(repeatCount); + return s; + } + + @Override + public void populateFromAttributes(Attributes attributes) throws Exception + { + chars = attributes.getValue("char"); + String key = attributes.getValue("key"); + if (key != null) + { + keys = new ArrayList<String>(); + keys.add(key); + } + String keyAttr = attributes.getValue("keys"); + if (keyAttr != null) + { + keys = new ArrayList<String>(); + String keyList[] = keyAttr.split(","); + for (String keyPart : keyList) + keys.add(keyPart); + } + String keyCode = attributes.getValue("keyCode"); + if (keyCode != null) + throw new Exception("keyCode not supported"); + + } + +} http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/8a08cdd2/mustella/java/src/marmotinni/DispatchMouseClickEvent.java ---------------------------------------------------------------------- diff --git a/mustella/java/src/marmotinni/DispatchMouseClickEvent.java b/mustella/java/src/marmotinni/DispatchMouseClickEvent.java new file mode 100644 index 0000000..66c65bc --- /dev/null +++ b/mustella/java/src/marmotinni/DispatchMouseClickEvent.java @@ -0,0 +1,205 @@ +/* + * + * 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 marmotinni; + +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import org.xml.sax.Attributes; + +/** + * DispatchMouseClickEvent + * + * Dispatch click to the element under the mouse + */ +public class DispatchMouseClickEvent extends TestStep { + + /** + * Dispatch click event + */ + @Override + protected void doStep() + { + + Long x; + Long y; + if (hasLocal) + { + StringBuilder script = new StringBuilder(); + insertTargetScript(script, target); + script.append("return target.element.offsetLeft"); + if (TestStep.showScripts) + System.out.println(script); + x = (Long)((JavascriptExecutor)webDriver).executeScript(script.toString()); + script = new StringBuilder(); + insertTargetScript(script, target); + script.append("return target.element.offsetTop"); + if (TestStep.showScripts) + System.out.println(script); + y = (Long)((JavascriptExecutor)webDriver).executeScript(script.toString()); + x += localX; + y += localY; + } + else + { + x = stageX; + y = stageY; + } + + // find top-most element + StringBuilder script = new StringBuilder(); + script.append("var all = document.all;"); + script.append("var n = all.length;"); + script.append("for(var i=n-1;i>=0;i--) { "); + script.append(" var e = all[i];"); + script.append(" if (" + x + " >= e.offsetLeft && " + x + " <= e.offsetLeft + e.offsetWidth && " + y + " >= e.offsetTop && " + y + " <= e.offsetTop + e.offsetHeight)"); + script.append(" return e;"); + script.append("};"); + script.append("return null;"); + if (TestStep.showScripts) + System.out.println(script); + WebElement mouseTarget = (WebElement)((JavascriptExecutor)webDriver).executeScript(script.toString()); + try + { + mouseTarget.click(); + } + catch (Exception e1) + { + TestOutput.logResult("Exception thrown in DispatchMouseClickEvent."); + testResult.doFail (e1.getMessage()); + } + + } + + /** + * The object that should receive the mouse event + */ + public String target; + + /** + * The ctrlKey property on the MouseEvent (optional) + */ + public boolean ctrlKey; + + /** + * The delta property on the MouseEvent (optional) + */ + public int delta; + + private boolean hasLocal; + private boolean hasStage; + + /** + * The localX property on the MouseEvent (optional) + * Either set stageX/stageY or localX/localY, but not both. + */ + public long localX; + + /** + * The localY property on the MouseEvent (optional) + * Either set stageX/stageY or localX/localY, but not both. + */ + public long localY; + + /** + * The stageX property on the MouseEvent (optional) + * Either set stageX/stageY or localX/localY, but not both. + */ + public long stageX; + + /** + * The stageY property on the MouseEvent (optional) + * Either set stageX/stageY or localX/localY, but not both. + */ + public long stageY; + + /** + * The shiftKey property on the MouseEvent (optional) + */ + public boolean shiftKey; + + /** + * The relatedObject property on the MouseEvent (optional) + */ + public String relatedObject; + + /** + * customize string representation + */ + @Override + public String toString() + { + String s = "DispatchMouseClickEvent: target = "; + s += target; + if (hasLocal) + { + s += ", localX = " + Long.toString(localX); + s += ", localY = " + Long.toString(localY); + } + if (hasStage) + { + s += ", stageX = " + Long.toString(stageX); + s += ", stageY = " + Long.toString(stageY); + } + if (shiftKey) + s += ", shiftKey = true"; + if (ctrlKey) + s += ", ctrlKey = true"; + + if (relatedObject != null) + s += ", relatedObject = " + relatedObject; + if (delta != 0) + s += ", delta = " + Integer.toString(delta); + return s; + } + + @Override + public void populateFromAttributes(Attributes attributes) + { + target = attributes.getValue("target"); + String value = attributes.getValue("localX"); + if (value != null) + { + localX = Long.parseLong(value); + hasLocal = true; + } + value = attributes.getValue("localY"); + if (value != null) + { + localY = Long.parseLong(value); + hasLocal = true; + } + value = attributes.getValue("stageX"); + if (value != null) + { + stageX = Long.parseLong(value); + hasStage = true; + } + value = attributes.getValue("stageY"); + if (value != null) + { + stageY = Long.parseLong(value); + hasStage = true; + } + } + +} http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/8a08cdd2/mustella/java/src/marmotinni/DispatchMouseEvent.java ---------------------------------------------------------------------- diff --git a/mustella/java/src/marmotinni/DispatchMouseEvent.java b/mustella/java/src/marmotinni/DispatchMouseEvent.java new file mode 100644 index 0000000..0f9a874 --- /dev/null +++ b/mustella/java/src/marmotinni/DispatchMouseEvent.java @@ -0,0 +1,222 @@ +/* + * + * 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 marmotinni; + +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.remote.RemoteWebElement; + +import org.xml.sax.Attributes; + +/** + * DispatchMouseEvent + * + * Dispatch mouse event + */ +public class DispatchMouseEvent extends TestStep { + + /** + * Dispatch a mouse event + */ + @Override + protected void doStep() + { + + Long x; + Long y; + if (hasLocal) + { + StringBuilder script = new StringBuilder(); + insertTargetScript(script, target); + script.append("return target.element.offsetLeft"); + if (TestStep.showScripts) + System.out.println(script); + x = (Long)((JavascriptExecutor)webDriver).executeScript(script.toString()); + script = new StringBuilder(); + insertTargetScript(script, target); + script.append("return target.element.offsetTop"); + if (TestStep.showScripts) + System.out.println(script); + y = (Long)((JavascriptExecutor)webDriver).executeScript(script.toString()); + x += localX; + y += localY; + } + else + { + x = stageX; + y = stageY; + } + + // find top-most element + StringBuilder script = new StringBuilder(); + script.append("var all = document.all;"); + script.append("var n = all.length;"); + script.append("for(var i=n-1;i>=0;i--) { "); + script.append(" var e = all[i];"); + script.append(" if (" + x + " >= e.offsetLeft && " + x + " <= e.offsetLeft + e.offsetWidth && " + y + " >= e.offsetTop && " + y + " <= e.offsetTop + e.offsetHeight) {"); + script.append(" if (!e.id) e.id = Math.random().toString();"); + script.append(" return e;"); + script.append(" }"); + script.append("};"); + script.append("return null;"); + if (TestStep.showScripts) + System.out.println(script); + RemoteWebElement mouseTarget = (RemoteWebElement)((JavascriptExecutor)webDriver).executeScript(script.toString()); + //System.out.println("mouseTarget: " + mouseTarget.getTagName() + " " + mouseTarget.getAttribute("id")); + String actualId = mouseTarget.getAttribute("id"); + + script = new StringBuilder(); + script.append("var e = document.createEvent('MouseEvent');"); + script.append("e.initMouseEvent(\"" + type + "\", true, false, window, " + delta + ", " + x + ", " + y + ", " + localX + ", " + localY + ", " + ctrlKey + ", false, " + shiftKey + ", false, " + type.equals("mouseDown") + ", " + relatedObject + ");"); + script.append("document.getElementById('" + actualId + "').dispatchEvent(e);"); + if (TestStep.showScripts) + System.out.println(script); + try + { + ((JavascriptExecutor)webDriver).executeScript(script.toString()); + } + catch (Exception e1) + { + TestOutput.logResult("Exception thrown in DispatchMouseEvent."); + testResult.doFail (e1.getMessage()); + } + + } + + /** + * The object that should receive the mouse event + */ + public String target; + + /** + * The type/name of the event + */ + public String type; + + /** + * The ctrlKey property on the MouseEvent (optional) + */ + public boolean ctrlKey; + + /** + * The delta property on the MouseEvent (optional) + */ + public int delta; + + private boolean hasLocal; + private boolean hasStage; + + /** + * The localX property on the MouseEvent (optional) + * Either set stageX/stageY or localX/localY, but not both. + */ + public long localX; + + /** + * The localY property on the MouseEvent (optional) + * Either set stageX/stageY or localX/localY, but not both. + */ + public long localY; + + /** + * The stageX property on the MouseEvent (optional) + * Either set stageX/stageY or localX/localY, but not both. + */ + public long stageX; + + /** + * The stageY property on the MouseEvent (optional) + * Either set stageX/stageY or localX/localY, but not both. + */ + public long stageY; + + /** + * The shiftKey property on the MouseEvent (optional) + */ + public boolean shiftKey; + + /** + * The relatedObject property on the MouseEvent (optional) + */ + public String relatedObject; + + /** + * customize string representation + */ + @Override + public String toString() + { + String s = "DispatchMouseEvent: target = "; + s += target; + if (hasLocal) + { + s += ", localX = " + Long.toString(localX); + s += ", localY = " + Long.toString(localY); + } + if (hasStage) + { + s += ", stageX = " + Long.toString(stageX); + s += ", stageY = " + Long.toString(stageY); + } + if (shiftKey) + s += ", shiftKey = true"; + if (ctrlKey) + s += ", ctrlKey = true"; + + if (relatedObject != null) + s += ", relatedObject = " + relatedObject; + if (delta != 0) + s += ", delta = " + Integer.toString(delta); + return s; + } + + @Override + public void populateFromAttributes(Attributes attributes) + { + target = attributes.getValue("target"); + type = attributes.getValue("type"); + String value = attributes.getValue("localX"); + if (value != null) + { + localX = Long.parseLong(value); + hasLocal = true; + } + value = attributes.getValue("localY"); + if (value != null) + { + localY = Long.parseLong(value); + hasLocal = true; + } + value = attributes.getValue("stageX"); + if (value != null) + { + stageX = Long.parseLong(value); + hasStage = true; + } + value = attributes.getValue("stageY"); + if (value != null) + { + stageY = Long.parseLong(value); + hasStage = true; + } + } + +} http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/8a08cdd2/mustella/java/src/marmotinni/MarmotinniRunner.java ---------------------------------------------------------------------- diff --git a/mustella/java/src/marmotinni/MarmotinniRunner.java b/mustella/java/src/marmotinni/MarmotinniRunner.java new file mode 100644 index 0000000..a2f85e4 --- /dev/null +++ b/mustella/java/src/marmotinni/MarmotinniRunner.java @@ -0,0 +1,250 @@ +/* + * + * 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 marmotinni; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.chrome.ChromeDriver; + +import java.io.IOException; +import java.nio.CharBuffer; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + + +/** + * TestEngine + * + * Runs a mustella script by parsing it and executing its contents + * + */ +public class MarmotinniRunner extends DefaultHandler { + + public MarmotinniRunner () { + } + + public boolean runTest(WebDriver webDriver, String scriptName) + { + if (tagMap == null) + setupMap(); + tests = new ArrayList<TestCase>(); + + System.out.println("running script " + scriptName); + + SAXParserFactory spf = SAXParserFactory.newInstance(); + try { + + //get a new instance of parser + SAXParser sp = spf.newSAXParser(); + + //parse the file and also register this class for call backs + sp.parse(scriptName, this); + + }catch(SAXException se) { + se.printStackTrace(); + }catch(ParserConfigurationException pce) { + pce.printStackTrace(); + }catch (IOException ie) { + ie.printStackTrace(); + } + boolean success = true; + System.out.println("test case count: " + tests.size()); + for (TestCase testCase : tests) + { + this.testCase = testCase; + testCase.runTest(webDriver); + TestResult testResult = testCase.getTestResult(); + testResult.scriptName = scriptName; + if (!testResult.hasStatus()) + testResult.result = TestResult.PASS; + else + { + System.out.println("test failure"); + success = false; + } + System.out.println(testResult.toString()); + } + return success; + } + + private HashMap<String, Class<?>> tagMap = null; + + private void setupMap() + { + tagMap = new HashMap<String, Class<?>>(); + tagMap.put("SetProperty", SetProperty.class); + tagMap.put("DispatchKeyEvent", DispatchKeyEvent.class); + tagMap.put("DispatchMouseEvent", DispatchMouseEvent.class); + tagMap.put("DispatchMouseClickEvent", DispatchMouseClickEvent.class); + tagMap.put("AssertPropertyValue", AssertPropertyValue.class); + } + + private boolean verboseXMLParsing; + private ArrayList<TestCase> tests; + private TestCase testCase; + private String currentPhase; + + //Event Handlers + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (verboseXMLParsing) + System.out.println("StartElement: uri: " + uri + "localName: " + localName + "qName: " + qName); + if(qName.equalsIgnoreCase("UnitTester")) { + // this should be the top-level tag, just ignore for now. Will need to process script subtags later + } + else if (qName.length() == 0) + { + // there seems to be some empty tags + } + else if (qName.equalsIgnoreCase("testCases")) + { + // there seems to be some empty tags + } + else if (qName.equalsIgnoreCase("mx:Script")) + { + // there seems to be some empty tags + } + else if (qName.equalsIgnoreCase("mx:Metadata")) + { + // there seems to be some empty tags + } + else if (qName.equalsIgnoreCase("TestCase")) { + testCase = new TestCase(); + testCase.populateFromAttributes(attributes); + tests.add(testCase); + } + else if (tagMap.containsKey(qName)) { + Class<?> c = tagMap.get(qName); + try { + TestStep testStep = (TestStep)c.newInstance(); + testStep.populateFromAttributes(attributes); + if (currentPhase.equals("setup")) + testCase.setup.add(testStep); + else if (currentPhase.equals("body")) + testCase.body.add(testStep); + else + testCase.cleanup.add(testStep); + } + catch (Exception e) { + System.out.println(e.getMessage()); + return; + } + } + else if (qName.equals("setup")) { + currentPhase = "setup"; + testCase.setup = new ArrayList<TestStep>(); + } + else if (qName.equals("body")) { + currentPhase = "body"; + testCase.body = new ArrayList<TestStep>(); + } + else if (qName.equals("cleanup")) { + currentPhase = "cleanup"; + testCase.cleanup = new ArrayList<TestStep>(); + } + else { + System.out.println("unexpected element: " + uri + qName); + } + + } + + + public void characters(char[] ch, int start, int length) throws SAXException { + CharBuffer cb = CharBuffer.allocate(ch.length); + cb.put(ch); + String s = cb.toString(); + if (verboseXMLParsing) + if (s.trim().length() > 0) + System.out.println("unexpected characters: " + s); + } + + public void endElement(String uri, String localName, String qName) throws SAXException { + if (verboseXMLParsing) + System.out.println("EndElement: uri: " + uri + "localName: " + localName + "qName: " + qName); + } + + public static void main(String[] args) + { + ArrayList<String> scripts = new ArrayList<String>(); + Map<String, String> argsMap = new HashMap<String, String>(); + for (String arg : args) + { + String[] keyValuePair = arg.split("="); + if (keyValuePair[0].equals("script")) + scripts.add(keyValuePair[1]); + else + argsMap.put(keyValuePair[0], keyValuePair[1]); + } + final String showScriptsArg = argsMap.get("showScripts"); + TestStep.showScripts = showScriptsArg != null && showScriptsArg.equalsIgnoreCase("true"); + final String showStepsArg = argsMap.get("showSteps"); + TestCase.showSteps = showStepsArg != null && showStepsArg.equalsIgnoreCase("true"); + + final String url = argsMap.get("url"); + System.out.println(url); + + final String browser = argsMap.get("browser"); + WebDriver driver; + if (browser != null && browser.equalsIgnoreCase("chrome")) + driver = new ChromeDriver(); + else + driver = new FirefoxDriver(); + + driver.get(url); + + int exitCode = 0; + try + { + MarmotinniRunner mr = new MarmotinniRunner(); + final String verboseXMLParsingArg = argsMap.get("verboseXMLParsing"); + mr.verboseXMLParsing = verboseXMLParsingArg != null && verboseXMLParsingArg.equalsIgnoreCase("true"); + int n = scripts.size(); + for (int i = 0; i < n; i++) + { + if (!mr.runTest(driver, scripts.get(i))) + { + System.out.println("script failed"); + exitCode = 1; + } + } + } + catch (Exception e) + { + System.out.println(e.getMessage()); + exitCode = 1; + } + finally + { + driver.quit(); + } + System.exit(exitCode); + } + +} http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/8a08cdd2/mustella/java/src/marmotinni/SetProperty.java ---------------------------------------------------------------------- diff --git a/mustella/java/src/marmotinni/SetProperty.java b/mustella/java/src/marmotinni/SetProperty.java new file mode 100644 index 0000000..b05c419 --- /dev/null +++ b/mustella/java/src/marmotinni/SetProperty.java @@ -0,0 +1,119 @@ +/* + * + * 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 marmotinni; + +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import org.xml.sax.Attributes; + +/** + * SetProperty + * + * Set a property + */ +public class SetProperty extends TestStep { + + public SetProperty () { + } + + /** + * Set the target's property to the specified value + */ + @Override + protected void doStep() + { + String valueString = null; + if (valueExpression != null) + valueString = ((JavascriptExecutor)webDriver).executeScript(valueExpression).toString(); + else if (value != null) + { + if (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("true")) + valueString = value; + else { + try { + Double.parseDouble(value); + valueString = value; + } + catch (Exception e) { + valueString = "'" + value + "'"; + } + } + } + else + valueString = "null"; + + StringBuilder setScript = new StringBuilder(); + insertTargetScript(setScript, target); + setScript.append("if (typeof(target['set_' + '" + propertyName + "']) == 'function') target['set_' + '" + propertyName + "'](" + valueString + ");"); + setScript.append(" else target['" + propertyName + "']=" + valueString + ";"); + if (TestStep.showScripts) + System.out.println(setScript.toString()); + ((JavascriptExecutor)webDriver).executeScript(setScript.toString()); + } + + /** + * The object to set a property on + */ + public String target; + + /** + * The name of the property to set + */ + public String propertyName; + + /** + * The value to set + */ + public String value; + + /** + * The value to set + */ + public String valueExpression; + + /** + * customize string representation + */ + @Override + public String toString() + { + String s = "SetProperty"; + if (target != null) + s += ": target = " + target.toString(); + if (propertyName != null) + s += ": propertyName = " + propertyName.toString(); + if (value != null) + s += ": value = " + value; + return s; + } + + @Override + public void populateFromAttributes(Attributes attributes) + { + target = attributes.getValue("target"); + propertyName = attributes.getValue("propertyName"); + value = attributes.getValue("value"); + valueExpression = attributes.getValue("valueExpression"); + } + +} http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/8a08cdd2/mustella/java/src/marmotinni/TestCase.java ---------------------------------------------------------------------- diff --git a/mustella/java/src/marmotinni/TestCase.java b/mustella/java/src/marmotinni/TestCase.java new file mode 100644 index 0000000..06e0e80 --- /dev/null +++ b/mustella/java/src/marmotinni/TestCase.java @@ -0,0 +1,293 @@ +/* + * + * 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 marmotinni; + +import java.util.List; +import java.util.ArrayList; +import java.util.Date; + +import org.openqa.selenium.WebDriver; + +import org.xml.sax.Attributes; + +/** + * TestCase + * + * A set of TestSteps. + */ +public class TestCase { + + public static boolean showSteps = false; + + /** + * The history of bugs that this test case has encountered + */ + public List<String> bugs; + + /** + * The sequence of TestSteps that comprise the test setup + */ + public List<TestStep> setup; + + /** + * The sequence of TestSteps that comprise the test + */ + public List<TestStep> body; + + /** + * The sequence of TestSteps that restore the test environment + */ + public List<TestStep> cleanup; + + /** + * An identifier for the test + */ + public String testID; + + /** + * A description of the test + */ + public String description; + + /** + * keywords, summarizing the tests + */ + public String keywords; + + /** + * frequency, an estimate of the tests's intersection with real-world + * usage. 1 = most frequent usage; 2 somewhat common; 3 = unusual + */ + public String frequency; + + /** + * The current set of steps (setup, body, cleanup) we are executing + */ + private List<TestStep> currentSteps; + + /** + * Which step we're currently executing (or waiting on an event for) + */ + private int currentIndex = 0; + + /** + * Number of steps in currentSteps + */ + private int numSteps = 0; + + /** + * Storage for the cleanupAsserts + */ + private List<AssertStep> cleanupAsserts; + + /** + * Steps we have to review at the end of the body to see if + * they failed or not. These steps monitor activity like + * checking for duplicate events or making sure unwanted events + * don't fire. + */ + public List<AssertStep> getCleanupAsserts() + { + return cleanupAsserts; + } + + /** + * Storage for this tests's result + */ + private TestResult testResult; + + /** + * This tests's result + */ + public TestResult getTestResult() + { + testResult.testID = testID; + return testResult; + } + + /** + * Constructor. Create the TestResult associated with this TestCase + */ + public TestCase() { + + testResult = new TestResult(); + testResult.testID = testID; + + cleanupAsserts = new ArrayList<AssertStep>(); + } + + /** + * Called when it is time to execute + * this test case. + * + */ + public boolean runTest(WebDriver webDriver) + { + testResult.beginTime = new Date().getTime(); + + return runSetup(webDriver); + } + + /** + * Execute the setup portion of the test + */ + private boolean runSetup(WebDriver webDriver) + { + if (!testResult.hasStatus()) + { + if (setup != null) + { + testResult.phase = TestResult.SETUP; + currentIndex = 0; + currentSteps = setup; + numSteps = setup.size(); + // return if we need to wait for something + if (!runSteps(webDriver)) + return false; + + } + } + return runBody(webDriver); + } + + /** + * Execute the body portion of the test + */ + private boolean runBody(WebDriver webDriver) + { + if (!testResult.hasStatus()) + { + if (body != null) + { + testResult.phase = TestResult.BODY; + currentIndex = 0; + currentSteps = body; + numSteps = body.size(); + // return if we need to wait for something + if (!runSteps(webDriver)) + return false; + + } + } + return runCleanup(webDriver); + } + + /** + * Execute the cleanup portion of the test + */ + private boolean runCleanup(WebDriver webDriver) + { + if (!testResult.hasStatus()) + { + if (cleanup != null) + { + testResult.phase = TestResult.CLEANUP; + currentIndex = 0; + currentSteps = cleanup; + numSteps = cleanup.size(); + // return if we need to wait for something + if (!runSteps(webDriver)) + return false; + + } + } + return runComplete(); + } + + /** + * Clean up when all three phases are done. Sends an event + * to the UnitTester harness to tell it that it can run + * the next test case. + */ + private boolean runComplete() + { + int n = cleanupAsserts.size(); + for (int i = 0; i < n; i++) + { + AssertStep asrt = cleanupAsserts.get(i); + asrt.cleanup(); + } + testResult.endTime = new Date().getTime(); + return true; + } + + /** + * Go through the currentSteps, executing each one. + * Returns true if no test steps required waiting. + * Returns false if we have to wait for an event before + * continuing. + */ + private boolean runSteps(WebDriver webDriver) + { + while (currentIndex < numSteps) + { + // return if a step failed + if (testResult.hasStatus()) + return true; + + TestStep step = currentSteps.get(currentIndex); + if (!(step instanceof AssertStep)) + { + // look at subsequent steps for Asserts and set them up early + for (int j = currentIndex + 1; j < numSteps; j++) + { + // scan following asserts for AssertEvents and set them up early + TestStep nextStep = currentSteps.get(j); + if (nextStep instanceof AssertStep) + { + ((AssertStep)nextStep).preview(webDriver, this, testResult); + /* TODO: (aharui) re-enable when we need these asserts + // do a check to be sure folks are using AssertEventPropertyValue correctly + if (nextStep instanceof AssertEventPropertyValue) + { + // AEPV must follow an AssertEvent or another AEPV + if (j == 0 || !(currentSteps[j-1] instanceof AssertEvent || currentSteps[j-1] instanceof AssertEventPropertyValue)) + TestOutput.logResult("WARNING: AssertEventPropertyValue may be missing preceding AssertEvent"); + } + else if (nextStep instanceof AssertError) + { + if (step instanceof SetProperty) + SetProperty(step).expectError = true; + } + */ + } + else + break; + } + } + if (TestCase.showSteps) + System.out.println(step.toString()); + step.execute(webDriver, this, testResult); + currentIndex++; + } + return true; + } + + public void populateFromAttributes(Attributes attributes) + { + description = attributes.getValue("description"); + testID = attributes.getValue("testID"); + keywords = attributes.getValue("keywords"); + frequency = attributes.getValue("frequency"); + + } + +} http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/8a08cdd2/mustella/java/src/marmotinni/TestOutput.java ---------------------------------------------------------------------- diff --git a/mustella/java/src/marmotinni/TestOutput.java b/mustella/java/src/marmotinni/TestOutput.java new file mode 100644 index 0000000..4b63f10 --- /dev/null +++ b/mustella/java/src/marmotinni/TestOutput.java @@ -0,0 +1,36 @@ +/* + * + * 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 marmotinni; + +/** + * The class that collects TestResults for a TestCase + */ +public class TestOutput +{ + + /** + * get printable version of phase + */ + public static void logResult(String message) { + System.out.println(message); + } + +} http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/8a08cdd2/mustella/java/src/marmotinni/TestStep.java ---------------------------------------------------------------------- diff --git a/mustella/java/src/marmotinni/TestStep.java b/mustella/java/src/marmotinni/TestStep.java new file mode 100644 index 0000000..2019f7b --- /dev/null +++ b/mustella/java/src/marmotinni/TestStep.java @@ -0,0 +1,124 @@ +/* + * + * 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 marmotinni; + +import org.openqa.selenium.WebDriver; + +import org.xml.sax.Attributes; + +/** + * TestStep + * + * Base class for test steps like DispatchMouseEvent and AssertPropertyValue, etc. + */ +public class TestStep { + + public static boolean showScripts = false; + + public TestStep () { + } + + public boolean execute(WebDriver webDriver, TestCase testCase, TestResult testResult) + { + this.webDriver = webDriver; + this.testCase = testCase; + this.testResult = testResult; + + doStep(); + + // if test failed, don't bother waiting, just bail + if (testResult.hasStatus()) + { + if (waitEvent != null) + { + /* TODO: figure out how to wait */ + } + } + + return true; + } + + /** + * The name of the object to listen for an event we're waiting on + */ + public String waitTarget; + + /** + * The name of the event to listen for on the waitTarget + */ + public String waitEvent; + + /** + * The number of milliseconds to wait before giving up + */ + public int timeout = 3000; + + /** + * The TestResult for this TestCase + */ + protected TestResult testResult; + + /** + * The TestCase that this step belongs to + */ + protected TestCase testCase; + + /** + * The WebDriver for this session + */ + protected WebDriver webDriver; + + /** + * The method that gets called when it is time to perform the work in the step. + */ + protected void doStep() + { + } + + public void populateFromAttributes(Attributes attributes) throws Exception + { + waitTarget = attributes.getValue("waitTarget"); + waitEvent = attributes.getValue("waitEvent"); + } + + protected void insertTargetScript(StringBuilder sb, String target) + { + sb.append("var target = document.getElementsByTagName('body')[0];"); + sb.append("target = target.flexjs_wrapper;"); + sb.append("target = target.initialView;"); + if (target == null || target.length() == 0) + { + return; + } + String parts[] = target.split("\\."); + int n = parts.length; + for (int i = 0; i < n; i++) + { + sb.append("target = target['get_' + '" + parts[i] + "']();"); + } + + } + + public String toString() + { + return ""; + } +}