com.seleniumtests.connectors.selenium.SeleniumRobotGridConnector.java Source code

Java tutorial

Introduction

Here is the source code for com.seleniumtests.connectors.selenium.SeleniumRobotGridConnector.java

Source

/**
 * Orignal work: Copyright 2015 www.seleniumtests.com
 * Modified work: Copyright 2016 www.infotel.com
 *             Copyright 2017-2019 B.Hecquet
 *
 * 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 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 com.seleniumtests.connectors.selenium;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.NotImplementedException;
import org.apache.http.HttpHost;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.json.JSONException;
import org.json.JSONObject;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.remote.DesiredCapabilities;

import com.google.common.net.HttpHeaders;
import com.google.common.net.MediaType;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import com.seleniumtests.customexception.ScenarioException;
import com.seleniumtests.customexception.SeleniumGridException;
import com.seleniumtests.util.FileUtility;

import io.appium.java_client.remote.MobileCapabilityType;

public class SeleniumRobotGridConnector extends SeleniumGridConnector {

    public static final String NODE_TASK_SERVLET = "/extra/NodeTaskServlet";
    public static final String STATUS_SERVLET = "/grid/admin/StatusServlet";
    public static final String GUI_SERVLET = "/grid/admin/GuiServlet/";

    public SeleniumRobotGridConnector(String url) {
        super(url);
    }

    /**
     * In case an app is required on the node running the test, upload it to the grid hub
     * This will then be made available through HTTP GET URL to the node (appium will receive an url instead of a file)
     * 
     */
    @Override
    public void uploadMobileApp(Capabilities caps) {

        String appPath = (String) caps.getCapability(MobileCapabilityType.APP);

        // check whether app is given and app path is a local file
        if (appPath != null && new File(appPath).isFile()) {

            try (CloseableHttpClient client = HttpClients.createDefault();) {
                // zip file
                List<File> appFiles = new ArrayList<>();
                appFiles.add(new File(appPath));
                File zipFile = FileUtility.createZipArchiveFromFiles(appFiles);

                HttpHost serverHost = new HttpHost(hubUrl.getHost(), hubUrl.getPort());
                URIBuilder builder = new URIBuilder();
                builder.setPath("/grid/admin/FileServlet/");
                builder.addParameter("output", "app");
                HttpPost httpPost = new HttpPost(builder.build());
                httpPost.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.OCTET_STREAM.toString());
                FileInputStream fileInputStream = new FileInputStream(zipFile);
                InputStreamEntity entity = new InputStreamEntity(fileInputStream);
                httpPost.setEntity(entity);

                CloseableHttpResponse response = client.execute(serverHost, httpPost);
                if (response.getStatusLine().getStatusCode() != 200) {
                    throw new SeleniumGridException(
                            "could not upload application file: " + response.getStatusLine().getReasonPhrase());
                } else {
                    // set path to the mobile application as an URL on the grid hub
                    ((DesiredCapabilities) caps).setCapability(MobileCapabilityType.APP,
                            IOUtils.toString(response.getEntity().getContent()) + "/" + appFiles.get(0).getName());
                }

            } catch (IOException | URISyntaxException e) {
                throw new SeleniumGridException("could not upload application file", e);
            }
        }
    }

    /**
     * Upload a file given file path
     * @param filePath
     */
    @Override
    public void uploadFile(String filePath) {
        try (CloseableHttpClient client = HttpClients.createDefault();) {
            // zip file
            List<File> appFiles = new ArrayList<>();
            appFiles.add(new File(filePath));
            File zipFile = FileUtility.createZipArchiveFromFiles(appFiles);

            HttpHost serverHost = new HttpHost(hubUrl.getHost(), hubUrl.getPort());
            URIBuilder builder = new URIBuilder();
            builder.setPath("/grid/admin/FileServlet/");
            builder.addParameter("output", "app");
            HttpPost httpPost = new HttpPost(builder.build());
            httpPost.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.OCTET_STREAM.toString());
            FileInputStream fileInputStream = new FileInputStream(zipFile);
            InputStreamEntity entity = new InputStreamEntity(fileInputStream);
            httpPost.setEntity(entity);

            CloseableHttpResponse response = client.execute(serverHost, httpPost);
            if (response.getStatusLine().getStatusCode() != 200) {
                throw new SeleniumGridException(
                        "could not upload file: " + response.getStatusLine().getReasonPhrase());
            } else {
                // TODO call remote API
                throw new NotImplementedException("call remote Robot to really upload file");
            }

        } catch (IOException | URISyntaxException e) {
            throw new SeleniumGridException("could not upload file", e);
        }
    }

    /**
     * Left click on desktop at x,y
     * @param x      x coordinate
     * @param y      y coordinate
     */
    @Override
    public void leftClic(int x, int y) {
        if (nodeUrl == null) {
            throw new ScenarioException(
                    "You cannot click left before driver has been created and corresponding node instanciated");
        }

        logger.info(String.format("click left: %d,%d", x, y));
        try {
            Unirest.post(String.format("%s%s", nodeUrl, NODE_TASK_SERVLET)).queryString("action", "leftClic")
                    .queryString("x", x).queryString("y", y).asString();
        } catch (UnirestException e) {
            logger.warn(String.format("Could not click left: %s", e.getMessage()));
        }
    }

    /**
     * Double click on desktop at x,y
     * @param x      x coordinate
     * @param y      y coordinate
     */
    @Override
    public void doubleClick(int x, int y) {
        if (nodeUrl == null) {
            throw new ScenarioException(
                    "You cannot double click before driver has been created and corresponding node instanciated");
        }

        logger.info(String.format("double click: %d,%d", x, y));
        try {
            Unirest.post(String.format("%s%s", nodeUrl, NODE_TASK_SERVLET)).queryString("action", "doubleClick")
                    .queryString("x", x).queryString("y", y).asString();
        } catch (UnirestException e) {
            logger.warn(String.format("Could not double click: %s", e.getMessage()));
        }
    }

    /**
     * right click on desktop at x,y
     * @param x      x coordinate
     * @param y      y coordinate
     */
    @Override
    public void rightClic(int x, int y) {
        if (nodeUrl == null) {
            throw new ScenarioException(
                    "You cannot click right before driver has been created and corresponding node instanciated");
        }

        logger.info(String.format("clic right: %d,%d", x, y));
        try {
            Unirest.post(String.format("%s%s", nodeUrl, NODE_TASK_SERVLET)).queryString("action", "rightClic")
                    .queryString("x", x).queryString("y", y).asString();
        } catch (UnirestException e) {
            logger.warn(String.format("Could not click right: %s", e.getMessage()));
        }
    }

    /**
     * Take screenshot of the full desktop
     * @return a string with base64 content of the image
     */
    @Override
    public String captureDesktopToBuffer() {
        if (nodeUrl == null) {
            throw new ScenarioException(
                    "You cannot take screenshot before driver has been created and corresponding node instanciated");
        }

        logger.info("capturing desktop");
        try {
            return Unirest.get(String.format("%s%s", nodeUrl, NODE_TASK_SERVLET))
                    .queryString("action", "screenshot").asString().getBody();

        } catch (UnirestException e) {
            logger.warn(String.format("Could not capture desktop: %s", e.getMessage()));
        }
        return "";
    }

    /**
     * upload file to browser
     * @param fileName      name of the file to upload
     * @param base64Content   content of the file, encoded in base 64
     */
    @Override
    public void uploadFileToBrowser(String fileName, String base64Content) {
        if (nodeUrl == null) {
            throw new ScenarioException(
                    "You cannot upload file to browser before driver has been created and corresponding node instanciated");
        }

        logger.info("uploading file to browser: " + fileName);
        try {
            Unirest.post(String.format("%s%s", nodeUrl, NODE_TASK_SERVLET)).queryString("action", "uploadFile")
                    .queryString("content", base64Content).queryString("name", fileName).asString();
        } catch (UnirestException e) {
            logger.warn(String.format("Could send keys: %s", e.getMessage()));
        }
    }

    /**
     * Send keys to desktop
     * @param keys
     */
    @Override
    public void sendKeysWithKeyboard(List<Integer> keyCodes) {
        if (nodeUrl == null) {
            throw new ScenarioException(
                    "You cannot use keyboard before driver has been created and corresponding node instanciated");
        }

        String keyCodeString = String.join(",",
                keyCodes.stream().map(k -> Integer.toString(k)).collect(Collectors.toList()));

        logger.info("sending keys: " + keyCodes);
        try {
            Unirest.post(String.format("%s%s", nodeUrl, NODE_TASK_SERVLET)).queryString("action", "sendKeys")
                    .queryString("keycodes", keyCodeString).asString();
        } catch (UnirestException e) {
            logger.warn(String.format("Could send keys: %s", e.getMessage()));
        }
    }

    /**
     * Write text to desktop using keyboard
     * @param text
     */
    @Override
    public void writeText(String text) {
        if (nodeUrl == null) {
            throw new ScenarioException(
                    "You cannot write text before driver has been created and corresponding node instanciated");
        }

        logger.info("writing text: " + text);
        try {
            Unirest.post(String.format("%s%s", nodeUrl, NODE_TASK_SERVLET)).queryString("action", "writeText")
                    .queryString("text", text).asString();
        } catch (UnirestException e) {
            logger.warn(String.format("Could not write text: %s", e.getMessage()));
        }
    }

    /**
     * Kill process
     * @param processName
     */
    @Override
    public void killProcess(String processName) {
        if (nodeUrl == null) {
            throw new ScenarioException(
                    "You cannot kill a remote process before driver has been created and corresponding node instanciated");
        }

        logger.info("killing process: " + processName);
        try {
            Unirest.post(String.format("%s%s", nodeUrl, NODE_TASK_SERVLET)).queryString("action", "kill")
                    .queryString("process", processName).asString();
        } catch (UnirestException e) {
            logger.warn(String.format("Could not kill process %s: %s", processName, e.getMessage()));
        }
    }

    /**
     * returns the list of processes, on the node, whose name without extension is the requested one
     * e.g: getProcessList("WINWORD")
     * Case will be ignored
     */
    @Override
    public List<Integer> getProcessList(String processName) {
        if (nodeUrl == null) {
            throw new ScenarioException(
                    "You cannot get a remote process before driver has been created and corresponding node instanciated");
        }

        logger.info("getting process list for: " + processName);
        try {
            List<String> pidListStr = Arrays.asList(Unirest.get(String.format("%s%s", nodeUrl, NODE_TASK_SERVLET))
                    .queryString("action", "processList").queryString("name", processName).asString().getBody()
                    .split(","));
            return pidListStr.stream().map(Integer::valueOf).collect(Collectors.toList());
        } catch (UnirestException e) {
            logger.warn(String.format("Could not get process list of %s: %s", processName, e.getMessage()));
        }

        return new ArrayList<>();
    }

    @Override
    public void startVideoCapture() {
        if (nodeUrl == null) {
            throw new ScenarioException(
                    "You cannot start video capture before driver has been created and corresponding node instanciated");
        }

        logger.info("starting capture");
        try {
            Unirest.get(String.format("%s%s", nodeUrl, NODE_TASK_SERVLET))
                    .queryString("action", "startVideoCapture").queryString("session", sessionId).asString();
        } catch (UnirestException e) {
            logger.warn(String.format("Could start video capture: %s", e.getMessage()));
        }
    }

    @Override
    public File stopVideoCapture(String outputFile) {
        if (nodeUrl == null) {
            throw new ScenarioException(
                    "You cannot stop video capture before driver has been created and corresponding node instanciated");
        }

        logger.info("stopping capture");
        try {
            HttpResponse<InputStream> videoResponse = Unirest.get(String.format("%s%s", nodeUrl, NODE_TASK_SERVLET))
                    .queryString("action", "stopVideoCapture").queryString("session", sessionId).asBinary();
            InputStream videoI = videoResponse.getBody();

            File videoFile = new File(outputFile);
            FileOutputStream os = new FileOutputStream(videoFile);
            IOUtils.copy(videoI, os);
            os.close();

            return videoFile;

        } catch (UnirestException | IOException e) {
            logger.warn(String.format("Could not stop video capture: %s", e.getMessage()));
            return null;
        }
    }

    /**
     * @return    true: if grid hub is active and and there is at least 1 node
     *          false: if grid hub is upgrading or no node is present
     * 
     */
    @Override
    public boolean isGridActive() {
        boolean gridActive = super.isGridActive();
        if (!gridActive) {
            return false;
        }

        HttpResponse<JsonNode> response;
        try {
            response = Unirest
                    .get(String.format("http://%s:%s%s", hubUrl.getHost(), hubUrl.getPort(), STATUS_SERVLET))
                    .asJson();
            if (response.getStatus() != 200) {
                logger.warn("Cannot connect to the grid hub at " + hubUrl);
                return false;
            }
        } catch (UnirestException e) {
            logger.warn("Cannot connect to the grid hub at " + hubUrl);
            return false;
        }

        try {
            JSONObject hubStatus = response.getBody().getObject();

            return hubStatus.getJSONObject("hub").getString("status").equalsIgnoreCase("ACTIVE")
                    && hubStatus.length() > 2;
        } catch (JSONException | NullPointerException e) {
            return false;
        }
    }

}