org.uiautomation.ios.wkrdp.model.RemoteWebNativeBackedElement.java Source code

Java tutorial

Introduction

Here is the source code for org.uiautomation.ios.wkrdp.model.RemoteWebNativeBackedElement.java

Source

/*
 * 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.wkrdp.model;

import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebDriverException;
import org.uiautomation.ios.IOSCapabilities;
import org.uiautomation.ios.UIAModels.UIAElement;
import org.uiautomation.ios.UIAModels.UIAScrollView;
import org.uiautomation.ios.UIAModels.UIAWebView;
import org.uiautomation.ios.UIAModels.predicate.AndCriteria;
import org.uiautomation.ios.UIAModels.predicate.Criteria;
import org.uiautomation.ios.UIAModels.predicate.LabelCriteria;
import org.uiautomation.ios.UIAModels.predicate.NameCriteria;
import org.uiautomation.ios.UIAModels.predicate.OrCriteria;
import org.uiautomation.ios.UIAModels.predicate.TypeCriteria;
import org.uiautomation.ios.client.uiamodels.impl.RemoteIOSDriver;
import org.uiautomation.ios.communication.device.DeviceType;
import org.uiautomation.ios.context.BaseWebInspector;
import org.uiautomation.ios.server.ServerSideSession;
import org.uiautomation.ios.server.application.ContentResult;
import org.uiautomation.ios.utils.XPath2Engine;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

public class RemoteWebNativeBackedElement extends RemoteWebElement {

    private static final Logger log = Logger.getLogger(RemoteWebNativeBackedElement.class.getName());

    private final ServerSideSession session;
    private final RemoteIOSDriver nativeDriver;
    private final List<Character> specialKeys = new ArrayList<Character>() {
        {
            this.add(Keys.DELETE.toString().charAt(0));
            this.add(Keys.ENTER.toString().charAt(0));
            this.add(Keys.RETURN.toString().charAt(0));
            this.add(Keys.SHIFT.toString().charAt(0));
        }
    };

    public RemoteWebNativeBackedElement(NodeId id, BaseWebInspector inspector, ServerSideSession session) {
        super(id, inspector);
        this.session = session;
        this.nativeDriver = session.getNativeDriver();
    }

    private static String normalizeDateValue(String value) {
        // convert MM/DD/YYYY to YYYY-MM-DD
        int sep1 = value.indexOf('/');
        int sep2 = value.lastIndexOf('/');
        if (sep1 == -1 || sep2 == -1)
            return value;

        String mm = value.substring(0, sep1);
        String dd = value.substring(sep1 + 1, sep2);
        String yyyy = value.substring(sep2 + 1);

        return yyyy + '-' + to2CharDateDigit(mm) + '-' + to2CharDateDigit(dd);
    }

    private static String to2CharDateDigit(String text) {
        return (text.length() == 1) ? '0' + text : text;
    }

    private boolean isSafari() {
        return session.getApplication().isSafari();
    }

    public void nativeClick() {

        if ("option".equalsIgnoreCase(getTagName())) {
            click();

        } else {
            try {
                ((JavascriptExecutor) nativeDriver).executeScript(getNativeElementClickOnIt());
                getInspector().checkForPageLoad();
            } catch (Exception e) {
                throw new WebDriverException(e);
            }
        }

    }

    @Override
    public Point getLocation() throws Exception {
        // web stuff.
        //scrollIntoViewIfNeeded();
        Point po = findPosition();

        Dimension dim = getInspector().getSize();
        int webPageWidth = getInspector().getInnerWidth();
        if (dim.getWidth() != webPageWidth) {
            log.fine("BUG : dim.getWidth()!=webPageWidth");
        }

        Criteria c = new TypeCriteria(UIAWebView.class);
        String json = c.stringify().toString();
        StringBuilder script = new StringBuilder();
        script.append("var root = UIAutomation.cache.get('1');");
        script.append("var webview = root.element(-1," + json + ");");
        script.append("var webviewSize = webview.rect();");
        script.append("var ratio = webviewSize.size.width / " + dim.getWidth() + ";");
        int top = po.getY();
        int left = po.getX();
        script.append("var top = (" + top + "*ratio )+1;");
        script.append("var left = (" + left + "*ratio)+1;");

        script.append("var x = left;");
        boolean ipad = session.getCapabilities().getDevice() == DeviceType.ipad;

        if (isSafari()) {
            if (ipad) {
                // for ipad, the adress bar h is fixed @ 96px.
                script.append("var y = top+96;");
            } else {

                List<ContentResult> results = session.getApplication().getCurrentDictionary()
                        .getPotentialMatches("Address");
                if (results.size() != 1) {
                    log.warning("translation returned " + results.size());
                }
                ContentResult result = results.get(0);
                String addressL10ned = result.getL10nFormatted();
                Criteria c2 = new AndCriteria(new TypeCriteria(UIAElement.class), new NameCriteria(addressL10ned),
                        new LabelCriteria(addressL10ned));
                script.append("var addressBar = root.element(-1," + c2.stringify().toString() + ");");
                script.append("var addressBarSize = addressBar.rect();");
                script.append("var delta = addressBarSize.origin.y +39;");
                script.append("if (delta<20){delta=20;};");
                script.append("var y = top+delta;");
            }
        } else {
            Criteria wv = new TypeCriteria(UIAScrollView.class);
            script.append("var webview = root.element(-1," + wv.stringify().toString() + ");");
            script.append("var size = webview.rect();");
            script.append("var offsetY = size.origin.y;");
            // UIAWebView.y
            script.append("var y = top+offsetY;");
            //script.append("var y = top+64;");
        }
        script.append("return new Array(parseInt(x), parseInt(y));");

        Object response = ((JavascriptExecutor) nativeDriver).executeScript(String.valueOf(script));

        int x = ((ArrayList<Long>) response).get(0).intValue();
        int y = ((ArrayList<Long>) response).get(1).intValue();

        return new Point(x, y);
    }

    private String getNativeElementClickOnIt() throws Exception {
        // web stuff.
        scrollIntoViewIfNeeded();
        Point location = getLocation();
        String script = "UIATarget.localTarget().tap({'x':x_coord,'y':y_coord});";
        script = script.replace("x_coord", String.valueOf(location.getX())).replace("y_coord",
                String.valueOf(location.getY()));
        return script;
    }

    private String getKeyboardTypeStringSegement(String value) {
        StringBuilder script = new StringBuilder();
        script.append("keyboard.typeString('");
        // got to love java...
        // first replacing a \ (backslash) with \\ (double backslash)
        // and then ' (single quote) with \' (backslash, single quote)
        script.append(value.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'"));
        script.append("');");
        return script.toString();
    }

    private String getReferenceToTapByXpath(XPath2Engine xPath2Engine, String xpath) {
        StringBuilder script = new StringBuilder();
        script.append("UIAutomation.cache.get(");
        script.append((String) xPath2Engine.findElementByXpath(xpath).get("ELEMENT"));
        script.append(", false).tap();");
        return script.toString();
    }

    // TODO freynaud use keyboard.js bot.Keyboard.prototype.moveCursor = function(element)
    private String getNativeElementClickOnItAndTypeUsingKeyboardScript(String value) throws Exception {

        StringBuilder script = new StringBuilder();
        script.append("var keyboard = UIAutomation.cache.get('1').keyboard();");

        Boolean keyboardResigned = false;

        StringBuilder current = new StringBuilder();
        XPath2Engine xpathEngine = null;
        for (int i = 0; i < value.length(); i++) {
            int idx = specialKeys.indexOf(value.charAt(i));
            if (idx >= 0) {
                if (xpathEngine == null) {
                    xpathEngine = XPath2Engine.getXpath2Engine(nativeDriver);
                }
                if (current.length() > 0) {
                    script.append(getKeyboardTypeStringSegement(current.toString()));
                    current = new StringBuilder();
                }
                switch (idx) {
                case 0:
                    // DELETE
                    // TODO, i don't like this xpath... should find the key in a better way
                    // (like keyboard.shift)
                    script.append(getReferenceToTapByXpath(xpathEngine,
                            "//UIAKeyboard/UIAKey["
                                    + (nativeDriver.getCapabilities().getDevice() == DeviceType.ipad ? "11]"
                                            : "last()-3]")));
                    break;
                case 1:
                case 2:
                    // ENTER / RETURN
                    // TODO another smelly xpath.
                    script.append(getReferenceToTapByXpath(xpathEngine, "//UIAKeyboard/UIAButton["
                            + (nativeDriver.getCapabilities().getDevice() == DeviceType.ipad ? "1]" : "2]")));
                    keyboardResigned = true;
                    break;
                case 3:
                    // SHIFT
                    script.append("keyboard.shift();");
                    break;
                default:
                    throw new RuntimeException("Special key found in the list but not taken care of??");
                }
            } else {
                current.append(value.charAt(i));
            }
        }
        if (current.length() > 0) {
            script.append(getKeyboardTypeStringSegement(current.toString()));
        }

        if (!keyboardResigned) {
            script.append("keyboard.hide();");
        }
        return script.toString();
    }

    public void setValueNative(String value) throws Exception {
        String type = getAttribute("type");
        if ("date".equalsIgnoreCase(type)) {
            value = normalizeDateValue(value);
            setValueAtoms(value);
            return;
        }
        // iphone on telephone inputs only shows the keypad keyboard.
        if ("tel".equalsIgnoreCase(type) && nativeDriver.getCapabilities().getDevice() == DeviceType.iphone) {
            value = replaceLettersWithNumbersKeypad(value,
                    (String) nativeDriver.getCapabilities().getCapability(IOSCapabilities.LOCALE));
        }
        ((JavascriptExecutor) nativeDriver).executeScript(getNativeElementClickOnIt());
        Thread.sleep(750);
        setCursorAtTheEnd();
        ((JavascriptExecutor) nativeDriver)
                .executeScript(getNativeElementClickOnItAndTypeUsingKeyboardScript(value));
    }

    // TODO actually handle more locales
    private String replaceLettersWithNumbersKeypad(String str, String locale) {
        if (locale.toLowerCase().startsWith("en")) {
            return str.replaceAll("[AaBbCc]", "2").replaceAll("[DdEeFf]", "3").replaceAll("[GgHhIi]", "4")
                    .replaceAll("[JjKkLl]", "5").replaceAll("[MmNnOo]", "6").replaceAll("[PpQqRrSs]", "7")
                    .replaceAll("[TtUuVv]", "8").replaceAll("[WwXxYyZz]", "9").replaceAll("-", "");
        }
        return str.replaceAll("-", "");
    }

}