Java tutorial
/* * Copyright 2012 ios-driver committers. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the Licence 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.uiautomation.ios.context; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.openqa.selenium.By; import org.openqa.selenium.Cookie; import org.openqa.selenium.Dimension; import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebElement; import org.uiautomation.ios.communication.WebDriverLikeCommand; import org.uiautomation.ios.server.DOMContext; import org.uiautomation.ios.server.ServerSideSession; import org.uiautomation.ios.wkrdp.MessageListener; import org.uiautomation.ios.wkrdp.RemoteExceptionException; import org.uiautomation.ios.wkrdp.WebKitSeemsCorruptedException; import org.uiautomation.ios.wkrdp.command.DOM; import org.uiautomation.ios.wkrdp.command.Page; import org.uiautomation.ios.wkrdp.events.ChildNodeRemoved; import org.uiautomation.ios.wkrdp.events.Event; import org.uiautomation.ios.wkrdp.events.EventFactory; import org.uiautomation.ios.wkrdp.events.inserted.ChildIframeInserted; import org.uiautomation.ios.wkrdp.internal.IosAtoms; import org.uiautomation.ios.wkrdp.message.ApplicationDataMessage; import org.uiautomation.ios.wkrdp.message.ApplicationSentListingMessage; import org.uiautomation.ios.wkrdp.message.IOSMessage; import org.uiautomation.ios.wkrdp.model.NodeId; import org.uiautomation.ios.wkrdp.model.RemoteObject; import org.uiautomation.ios.wkrdp.model.RemoteObjectArray; import org.uiautomation.ios.wkrdp.model.RemoteWebElement; import sun.net.dns.ResolverConfiguration; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.logging.Logger; public abstract class BaseWebInspector implements MessageListener { private static final Logger log = Logger.getLogger(BaseWebInspector.class.getName()); protected final ServerSideSession session; private boolean newPage = true; public static final Long defaultPageLoadTimeoutInMs = 30000L; private final DOMContext context; protected BaseWebInspector(ServerSideSession session) { this.session = session; this.context = new DOMContext(this); } public abstract JSONObject sendCommand(JSONObject command); public abstract int getPageIdentifier(); public RemoteWebElement getDocument() { long deadline = System.currentTimeMillis() + defaultPageLoadTimeoutInMs; return getDocument(deadline); } public RemoteWebElement getDocument(long deadline) { RemoteWebElement result = context.getDocument(); if (result == null) { result = retrieveDocumentAndCheckReady(deadline); RemoteWebElement window = getMainWindow(); context.setCurrentFrame(null, result, window); } return result; } public RemoteWebElement getMainWindow() { return new RemoteWebElement(new NodeId(0), this); } private RemoteWebElement retrieveDocumentAndCheckReady(long deadline) { RemoteWebElement element = null; String readyState = ""; while (!readyState.equals("complete")) { if (deadline > 0 && System.currentTimeMillis() > deadline) { throw new TimeoutException("Timeout waiting to get the document."); } try { log.fine("trying to get the document"); element = retrieveDocument(); log.fine("got it"); readyState = element.getRemoteObject().call(".readyState"); log.fine("ready ? " + readyState); } catch (Exception e) { log.warning("The given document is corrupted, nodeId=" + ((element != null) ? element.getNodeId() : "null") + ": " + e); throw new WebKitSeemsCorruptedException(); } } return element; } private RemoteWebElement retrieveDocument() throws Exception { JSONObject result = sendCommand(DOM.getDocument()); JSONObject root = result.getJSONObject("root"); RemoteWebElement rme = new RemoteWebElement(new NodeId(root.getInt("nodeId")), this); return rme; } public void get(String url) { try { context.eventsLock().lock(); JSONObject command = Page.navigate(url); sendCommand(command); context.newContext(); checkForPageLoad(); context.waitForLoadEvent(); // wait for everything to be ready by fetching the doc. getDocument(); } finally { context.eventsLock().unlock(); } } public String getCurrentUrl() { long deadline = System.currentTimeMillis() + getTimeout(); while (System.currentTimeMillis() < deadline) { try { return getCurrentUrlOnce(); } catch (RemoteExceptionException e) { if (!e.getMessage().contains("Inspected frame has gone")) { throw e; } log.severe("current url not ready :" + e.getMessage()); } } throw new WebDriverException("timeout waiting for the URL to be accessible."); } private String getCurrentUrlOnce() { try { RemoteWebElement document = getDocument(); String f = "(function(arg) { var url=this.URL;return url;})"; JSONObject cmd = new JSONObject(); cmd.put("method", "Runtime.callFunctionOn"); JSONArray args = new JSONArray(); cmd.put("params", new JSONObject().put("objectId", document.getRemoteObject().getId()) .put("functionDeclaration", f).put("arguments", args).put("returnByValue", true)); JSONObject response = sendCommand(cmd); return cast(response); } catch (JSONException e) { throw new WebDriverException(e); } } public String getTitle() { try { JSONObject cmd = new JSONObject(); cmd.put("method", "Runtime.evaluate"); cmd.put("params", new JSONObject().put("expression", "document.title;").put("returnByValue", true)); JSONObject response = sendCommand(cmd); return cast(response); } catch (JSONException e) { throw new WebDriverException(e); } /*String title = (String) executeScript("var state = document.title; return state",new JSONArray()); return title;*/ } public List<WebElement> findElements(By by) { return null; //To change body of implemented methods use File | Settings | File Templates. } public WebElement findElement(By by) { return null; //To change body of implemented methods use File | Settings | File Templates. } public String getPageSource() { try { JSONObject cmd = new JSONObject(); cmd.put("method", "Runtime.evaluate"); cmd.put("params", new JSONObject().put("expression", "new window.XMLSerializer().serializeToString(document);") .put("returnByValue", true)); JSONObject response = sendCommand(cmd); return cast(response); } catch (Exception e) { throw new WebDriverException(e); } } public void close() { //To change body of implemented methods use File | Settings | File Templates. } public void quit() { //To change body of implemented methods use File | Settings | File Templates. } public WebDriver.TargetLocator switchTo() { return null; //To change body of implemented methods use File | Settings | File Templates. } public WebDriver.Navigation navigate() { return null; //To change body of implemented methods use File | Settings | File Templates. } public ResolverConfiguration.Options manage() { return null; //To change body of implemented methods use File | Settings | File Templates. } // TODO freynaud fix the element swapping. public Object executeScript(String script, JSONArray args) { try { RemoteWebElement document = getDocument(); RemoteWebElement window = context.getWindow(); JSONObject cmd = new JSONObject(); JSONArray arguments = new JSONArray(); int nbParam = args.length(); for (int i = 0; i < nbParam; i++) { Object arg = args.get(i); if (arg instanceof JSONObject) { JSONObject jsonArg = (JSONObject) arg; if (jsonArg.optString("ELEMENT") != null) { // TODO use driver factory to check the pageId NodeId n = new NodeId(Integer.parseInt(jsonArg.optString("ELEMENT").split("_")[1])); RemoteWebElement rwep = new RemoteWebElement(n, this); arguments.put(new JSONObject().put("objectId", rwep.getRemoteObject().getId())); } } else if (arg instanceof JSONArray) { JSONArray jsonArr = (JSONArray) arg; // maybe by executing some JS returning the array, and using that result // as a param ? throw new WebDriverException("no support argument = array."); } else { arguments.put(new JSONObject().put("value", arg)); } } if (!context.isOnMainFrame()) { arguments.put(new JSONObject().put("objectId", document.getRemoteObject().getId())); arguments.put(new JSONObject().put("objectId", window.getRemoteObject().getId())); String contextObject = "{'document': arguments[" + nbParam + "], 'window': arguments[" + (nbParam + 1) + "]}"; script = "with (" + contextObject + ") {" + script + "}"; } cmd.put("method", "Runtime.callFunctionOn"); cmd.put("params", new JSONObject().put("objectId", document.getRemoteObject().getId()) .put("functionDeclaration", "(function() { " + script + "})") .put("arguments", arguments).put("returnByValue", false)); JSONObject response = sendCommand(cmd); checkForJSErrors(response); Object o = cast(response); return o; } catch (JSONException e) { throw new WebDriverException(e); } } public void checkForJSErrors(JSONObject response) throws JSONException { if (response.optBoolean("wasThrown")) { JSONObject details = response.getJSONObject("result"); String desc = details.optString("description"); throw new WebDriverException("JS error :" + desc); } } public <T> T cast(JSONObject response) { JSONObject body = null; try { body = response.has("result") ? response.getJSONObject("result") : response.getJSONObject("object"); } catch (JSONException e) { throw new WebDriverException(e); } if (body == null) { throw new RuntimeException("Error parsting " + response); } try { return cast_(body); } catch (JSONException e) { throw new WebDriverException(e); } } private <T> T cast_(JSONObject body) throws JSONException { List<String> primitives = new ArrayList<String>(); primitives.add("boolean"); primitives.add("number"); primitives.add("string"); String type = body.getString("type"); // handle null return if ("undefined".equals(type)) { return (T) null; } // handle primitive types. if (primitives.contains(type)) { // primitive type. Object value = body.get("value"); return (T) value; } // handle objects if ("object".equals(type)) { if (body.has("value") && body.isNull("value")) { return (T) null; } if ("array".equals(body.optString("subtype"))) { RemoteObject array = new RemoteObject(body.getString("objectId"), this); RemoteObjectArray a = new RemoteObjectArray(array); ArrayList<Object> res = new ArrayList<Object>(); for (Object ro : a) { res.add(ro); } return (T) res; } if (body.has("objectId")) { if ("node".equals(body.optString("subtype")) || "Window".equals(body.optString("className"))) { return (T) new RemoteObject(body.getString("objectId"), this); } else { RemoteObject ro = new RemoteObject(body.getString("objectId"), this); JSONObject o = new JSONObject(ro.stringify()); return (T) o; } } return (T) new RemoteObject(body.getString("objectId"), this); } throw new RuntimeException("NI " + body); } public Object executeAsyncScript(String script, Object... args) { return null; //To change body of implemented methods use File | Settings | File Templates. } public int getInnerWidth() throws JSONException { JSONObject cmd = new JSONObject(); cmd.put("method", "Runtime.evaluate"); cmd.put("params", new JSONObject().put("expression", "document.body.clientWidth;")); JSONObject response = sendCommand(cmd); return (Integer) cast(response); } public Dimension getSize() throws Exception { String f = "(function(element) { var result = " + IosAtoms.GET_INTERACTABLE_SIZE + "(window.top);" + "var res = " + IosAtoms.STRINGIFY + "(result);" + "return res; })"; JSONObject cmd = new JSONObject(); cmd.put("method", "Runtime.callFunctionOn"); cmd.put("params", new JSONObject().put("objectId", getDocument().getRemoteObject().getId()) .put("functionDeclaration", f).put("returnByValue", false)); JSONObject response = sendCommand(cmd); String s = cast(response); JSONObject o = new JSONObject(s); Dimension dim = new Dimension(o.getInt("width"), o.getInt("height")); return dim; } public RemoteWebElement findElementByCSSSelector(String cssSelector) { RemoteWebElement document = getDocument(); return document.findElementByCSSSelector(cssSelector); } public List<RemoteWebElement> findElementsByCSSSelector(String cssSelector) { RemoteWebElement document = getDocument(); return document.findElementsByCSSSelector(cssSelector); } private void flagPageLoaded() { try { JSONObject cmd = new JSONObject(); cmd.put("method", "Runtime.evaluate"); cmd.put("params", new JSONObject().put("expression", "window.top.iosdriver='" + context.getId() + "'")); sendCommand(cmd); } catch (JSONException e) { throw new WebDriverException(e); } } public String getLoadedFlag() { JSONObject cmd = new JSONObject(); try { cmd.put("method", "Runtime.evaluate"); cmd.put("params", new JSONObject().put("expression", "window.top.iosdriver")); } catch (JSONException e) { throw new WebDriverException(e); } JSONObject response = sendCommand(cmd); return cast(response); } public boolean isReady() { return "complete".equals(getDocumentReadyState()); } private String getDocumentReadyState() { String state = null; try { state = (String) executeScript("var state = document.readyState; return state", new JSONArray()); } catch (Exception e) { // Arguments should belong to the same JavaScript world as the target object. System.err.println("error, reseting because " + e.getMessage()); context.reset(); return "unknown"; } return state; } public void checkForPageLoad() { // a new page appeared. /*String id = getLoadedFlag(); //System.out.println("on a page with id =" + id + " - " + context.getId()); if (!context.getId().equals(id)) { long timeout = getTimeout(); long deadline = System.currentTimeMillis() + timeout; //context.newContext(); /*for (int i=0;i<100;i++){ try { retrieveDocument().findElementByCSSSelector("#v4-1"); //System.out.println(doc.getRemoteObject().call(".readyState")); } catch (Exception e) { System.err.println(e.getMessage()); //To change body of catch statement use File | Settings | File Templates. } } flagPageLoaded(); */ //System.out.println("on a page with id =" + getLoadedFlag()); //} } private String getMainPageReadyState() { try { JSONObject cmd = new JSONObject(); cmd.put("method", "Runtime.evaluate"); cmd.put("params", new JSONObject().put("expression", "document.readyState;").put("returnByValue", true)); JSONObject response = sendCommand(cmd); return cast(response); } catch (JSONException e) { throw new WebDriverException(e); } } private long getTimeout() { if (session == null) { return defaultPageLoadTimeoutInMs; } else { long timeout = (Long) session.configure(WebDriverLikeCommand.URL).opt("page load", defaultPageLoadTimeoutInMs); if (timeout < 0) { return defaultPageLoadTimeoutInMs; } return timeout; } } @Override public void onMessage(IOSMessage message) { // a page was loaded. if (message instanceof ApplicationSentListingMessage) { } if (message instanceof ApplicationDataMessage) { ApplicationDataMessage m = (ApplicationDataMessage) message; EventFactory EventFactory = new EventFactory(); Event e = EventFactory.createEvent(m.getMessage()); if ((e instanceof ChildIframeInserted || e instanceof ChildNodeRemoved)) { context.domHasChanged(e); } if ("Page.frameDetached".equals(m.getMessage().optString("method"))) { context.frameDied(m.getMessage()); } if ("Page.loadEventFired".equals(m.getMessage().optString("method"))) { context.signallNewPageLoadRecieved(); } if ("Profiler.resetProfiles".equals(m.getMessage().optString("method"))) { } } } public DOMContext getContext() { return context; } public void back() throws JSONException { try { String f = "(function() { var f=" + IosAtoms.BACK + "();})()"; JSONObject cmd = new JSONObject(); cmd.put("method", "Runtime.evaluate"); cmd.put("params", new JSONObject().put("expression", f).put("returnByValue", true)); JSONObject response = sendCommand(cmd); cast(response); } catch (JSONException e) { throw new WebDriverException(e); } } public void refresh() throws Exception { JSONObject cmd = new JSONObject(); cmd.put("method", "Page.reload"); JSONObject response = sendCommand(cmd); } public void forward() throws Exception { try { String f = "(function() { var f=" + IosAtoms.FORWARD + "();})()"; JSONObject cmd = new JSONObject(); cmd.put("method", "Runtime.evaluate"); cmd.put("params", new JSONObject().put("expression", f).put("returnByValue", true)); JSONObject response = sendCommand(cmd); cast(response); } catch (JSONException e) { throw new WebDriverException(e); } } public void highlightNode(NodeId nodeId) { sendCommand(DOM.highlightNode(nodeId)); } public void deleteCookie(String name, String domain) { sendCommand(Page.deleteCookie(name, domain)); } public List<Cookie> getCookies() { List<Cookie> res = new ArrayList<Cookie>(); JSONObject o = sendCommand(Page.getCookies()); JSONArray cookies = o.optJSONArray("cookies"); if (cookies != null) { for (int i = 0; i < cookies.length(); i++) { JSONObject cookie = cookies.optJSONObject(i); String name = cookie.optString("name"); String value = cookie.optString("value"); String domain = cookie.optString("domain"); String path = cookie.optString("path"); Date expiry = new Date(cookie.optLong("expires")); boolean isSecure = cookie.optBoolean("secure"); Cookie c = new Cookie(name, value, domain, path, expiry, isSecure); res.add(c); } return res; } else { // TODO } return null; } }