[JAVA] Ajouter un sélecteur d'image à Selenium Grid

introduction

Il est devenu nécessaire de faire quelque chose d'un peu compliqué, comme exécuter une autre application sur le terminal de destination distant ainsi que sur le navigateur.

Ah, je ne comprends plus vraiment ça, alors je l'ai fait pour qu'il puisse être déplacé avec des images: fatigue_face:

Je n'utilise pas Stream dans mon code et j'ai fait beaucoup de choses, mais je vais l'enregistrer sous forme de mémorandum.

En raison du sélecteur d'image, Node n'est compatible qu'avec le système d'exploitation 64 bits.

Logiciel utilisé

Il s'agit du logiciel utilisé pour la vérification. Je construis une version de vérification sur Windows.

doux version Utilisation
java 64bit jdk-8.0.212.03-hotspot(AdoptOpenJDK)
selenium-server-standalone.jar 3.141.59
sikulixapi 1.1.4-SNAPSHOT Sélecteur d'image de nœud
Fonctionne uniquement en 64 bits
gson 2.8.5 entrée / sortie de fichier json
httpclient 4.5.8 Exécutez REST

Environnement

C'est un moment avec Maven.

Dossier Maven

<détails>

Fichier Maven (décompression) </ summary>

pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>selenium-grid-extend</groupId>
  <artifactId>selenium-grid-extend</artifactId>
  <version>0.0.1</version>
  <repositories>
    <repository>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
      <id>sonatype</id>
      <name>sonatype Repository</name>
      <url>http://oss.sonatype.org/content/groups/public</url>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <releases>
        <updatePolicy>never</updatePolicy>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
      <id>sonatype</id>
      <name>sonatype Repository</name>
      <url>http://oss.sonatype.org/content/groups/public</url>
    </pluginRepository>
  </pluginRepositories>
  <build>
    <sourceDirectory>src</sourceDirectory>
    <testSourceDirectory>src</testSourceDirectory>
    <resources>
      <resource>
        <directory>resource</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>resource</directory>
      </testResource>
    </testResources>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-server</artifactId>
      <version>3.141.59</version>
    </dependency>
    <dependency>
    	<groupId>com.sikulix</groupId>
    	<artifactId>sikulixapi</artifactId>
    	<version>1.1.4-SNAPSHOT</version>
    </dependency>
    <dependency>
    	<groupId>com.google.code.gson</groupId>
    	<artifactId>gson</artifactId>
    	<version>2.8.5</version>
    </dependency>
    <dependency>
    	<groupId>org.apache.httpcomponents</groupId>
    	<artifactId>httpclient</artifactId>
    	<version>4.5.8</version>
    </dependency>
  </dependencies>
  <properties>
    <java.version>1.8</java.version>
    <file.encoding>UTF-8</file.encoding>
    <project.build.sourceEncoding>${file.encoding}</project.build.sourceEncoding>
    <project.reporting.outputEncoding>${file.encoding}</project.reporting.outputEncoding>
    <maven.compiler.encoding>${file.encoding}</maven.compiler.encoding>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
  </properties>
</project>

programme

Cette fois, la requête de / grid / admin / RequestToSessionMachine ~ est envoyée au terminal de la session spécifiée dans l'URL. Tous les échanges autres que l'URL seront envoyés au format JSON.

Attention, il n'y a pas de session simplement en connectant le Node. Une session sera créée sur le Hub uniquement lorsque vous pourrez utiliser le navigateur.

Enfin, la demande envoyée à Hub comme suit

http://HubIP:HubPort/grid/admin/RequestToSessionMachine/session/99999XXXXX99999/extra/ImageSelector/doubleclick

Demandez le Node exécutant la session «99999XXXXX99999» comme suit, attendez le résultat et renvoyez-le à l'appelant tel quel.

http://NodeIP:NodePort//extra/ImageSelector/doubleclick

Programme côté hub

Il serait préférable de pouvoir l'envoyer avec TestSession.forward, mais j'ai arrêté de l'utiliser car j'avais besoin de SeleniumBasedRequest comme argument et il était difficile d'étudier ~~.

Je mets à jour le temps d'accès afin qu'il ne prenne pas le temps d'expiration de la session, mais cela peut ne pas fonctionner en fonction du délai d'expiration.

Programme côté Hub (déploiement)

RequestToSessionMachine.java


package selenium.extend.hub.servlet;

import java.io.BufferedReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.openqa.grid.common.exception.GridException;
import org.openqa.grid.internal.GridRegistry;
import org.openqa.grid.internal.TestSession;
import org.openqa.grid.internal.TestSlot;
import org.openqa.grid.web.servlet.RegistryBasedServlet;

import com.google.gson.Gson;
import com.google.gson.JsonObject;

public class RequestToSessionMachine extends RegistryBasedServlet {

    private static final Pattern SESSION_ID_PATTERN = Pattern.compile("/grid/admin/RequestToSessionMachine/session/([^/]+).*");

    public RequestToSessionMachine() {
        this(null);
    }

    public RequestToSessionMachine(GridRegistry registry) {
        super(registry);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        process(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        process(request, response);
    }

    protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("Start RequestToSessionMachine");

        response.setContentType("application/json");
        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());

        JsonObject json = new JsonObject();

        CloseableHttpClient client = null;
        CloseableHttpResponse res = null;

        try {
            //Obtenez les informations associées à l'ID de session de l'URL
            TestSession session = getActiveTestSession(getSessionIdFromPath(request.getRequestURI()));

            if (session != null) {

                //Réinitialiser le temps d'accès à la session(Prolonger le délai d'expiration)
                session.setIgnoreTimeout(false);

                //Générer l'URL de connexion au nœud avec session
                TestSlot slot = session.getSlot();
                URL remoteRequestURL = new URL(slot.getRemoteURL(), trimSessionPath(request.getRequestURI()));

                //Demander Json du corps au nœud tel quel
                client = HttpClients.createDefault();

                HttpPost httpPost = new HttpPost(remoteRequestURL.toURI());
                httpPost.setHeader("Content-type", "application/json; charset=UTF-8");

                BufferedReader bufferReaderBody = new BufferedReader(request.getReader());
                StringBuilder jsonBody = new StringBuilder();
                String line = null;

                while ((line = bufferReaderBody.readLine()) != null) {
                    jsonBody.append(line);
                }

                StringEntity entity = new StringEntity(jsonBody.toString(), StandardCharsets.UTF_8);
                httpPost.setEntity(entity);

                res = client.execute(httpPost);

                //Réinitialiser le temps d'accès à la session(Prolonger le délai d'expiration)
                session.setIgnoreTimeout(false);

                int status = res.getStatusLine().getStatusCode();
                response.setStatus(status);

                if (status == 200) {
                    Gson gson = new Gson();
                    json = gson.fromJson(EntityUtils.toString(res.getEntity(), StandardCharsets.UTF_8), JsonObject.class);
                } else {
                    json.addProperty("error", "Response Code " + status);
                }

            } else {
                json.addProperty("error", "No Match Active Test Session for Session ID");
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
            json.addProperty("error", e.getMessage());
        } catch (URISyntaxException e) {
            e.printStackTrace();
            json.addProperty("error", e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            json.addProperty("error", e.getMessage());
        } finally {
            try {
                if (res != null) {
                    res.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                throw new GridException(e.getMessage());
            } finally {
                if (client != null) {
                    try {
                        client.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        throw new GridException(e.getMessage());
                    }
                }
            }
        }

        System.out.println("ResponseJson:" + json.toString());
        response.getWriter().print(json);
        response.getWriter().close();

        System.out.println("End RequestToSessionMachine");
    }

    private TestSession getActiveTestSession(String sessionId) {
        Iterator<TestSession> itr = super.getRegistry().getActiveSessions().iterator();
        TestSession session = null;

        System.out.println("Active Session Size:" + super.getRegistry().getActiveSessions().size());
        System.out.println("Search Session ID:" + sessionId);

        while (itr.hasNext()) {
            TestSession s = itr.next();
            if (s.getExternalKey().getKey().equals(sessionId)) {
                session = s;
                break;
            }
        }

        return session;
    }

    private String getSessionIdFromPath(String pathInfo) {
        Matcher matcher = SESSION_ID_PATTERN.matcher(pathInfo);
        if (matcher.matches()) {
            return matcher.group(1);
        }
        throw new IllegalArgumentException("Invalid request. Session Id is not present");
    }

    private String trimSessionPath(String pathInfo) {
        return pathInfo.replaceFirst("/grid/admin/RequestToSessionMachine/session/" + getSessionIdFromPath(pathInfo), "");
    }

}

Programme côté nœud

À l'aide de l'API de sikulix, double-cliquez sur la partie correspondante sur le bureau à partir de l'image JSON Base64 envoyée.

sikulix-api Veuillez consulter la page officielle pour le contenu qui peut être utilisé.

<détails>

Programme côté nœud (extension) </ summary>

ImageSelector.java


package selenium.extend.node.servlet;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.sikuli.script.FindFailed;
import org.sikuli.script.Image;
import org.sikuli.script.Screen;

import com.google.gson.Gson;
import com.google.gson.JsonObject;

public class ImageSelector extends HttpServlet {

    private static final String DOUBLECLICK = "/extra/ImageSelector/doubleclick";

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        JsonObject jsonResponse = null;
        if (request.getRequestURI().startsWith(DOUBLECLICK)) {
            jsonResponse = doubleClick(request, response);

        } else {
            jsonResponse = new JsonObject();
            jsonResponse.addProperty("error", "Request Command is No Support");
        }

        response.getWriter().print(jsonResponse);
        response.getWriter().close();
    }

    protected JsonObject doubleClick(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("Start ImageSelector");

        response.setContentType("application/json");
        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
        response.setStatus(200);

        JsonObject json = new JsonObject();

        try {
            //Extraire les informations du corps à sélectionner dans json
            BufferedReader bufferReaderBody = new BufferedReader(request.getReader());
            StringBuilder jsonBody = new StringBuilder();
            String line = null;

            while ((line = bufferReaderBody.readLine()) != null) {
                jsonBody.append(line);
            }

            Gson gson = new Gson();
            JsonObject reqJson = gson.fromJson(jsonBody.toString(), JsonObject.class);
            String imageBase64 = reqJson.get("imageBase64").getAsString();

            String[] parts = imageBase64.split(",");
            String imageString = parts[1];

            byte[] imageByte = Base64.getDecoder().decode(imageString);
            ByteArrayInputStream bis = new ByteArrayInputStream(imageByte);
            Image image = new Image(ImageIO.read(bis));

            bis.close();

            //Recherchez l'image sur tout l'écran et double-cliquez
            Screen sc = new Screen();
            //Réglage du temps d'attente
            sc.setAutoWaitTimeout(30);

            sc.doubleClick(image);

            json.addProperty("info", "Done DoubleClick");

        } catch (IOException e) {
            e.printStackTrace();
            json.addProperty("error", e.getMessage());
        } catch (FindFailed e) {
            e.printStackTrace();
            json.addProperty("error", e.getMessage());
        }

        System.out.println("End ImageSelector");

        return json;
    }

}

Créer un fichier jar

Jar pour le chargement dans Selenium Grid. La structure des répertoires est la suivante.

Il existe un système AllNodes créé dans l'article précédent, mais cette fois ce n'est pas nécessaire. aWS050632.JPG

Exportez uniquement les fichiers suivants vers jar. Le nom est "extend.jar". aWS050634.JPG

Démarrez l'environnement d'exécution

Placez "extend.jar" dans les répertoires Hub et Node et chargez-le.

Lancement du hub

<détails>

Structure du répertoire Hub et commandes (décompression) </ summary>

C:.
│  start-hub.bat
│
└─lib
        commons-logging-1.2.jar
        extend.jar
        gson-2.8.5.jar
        httpclient-4.5.8.jar
        httpcore-4.4.11.jar
        selenium-server-standalone-3.141.59.jar

start-hub.bat


java -cp lib/* org.openqa.grid.selenium.GridLauncherV3 -role hub -servlets "selenium.extend.hub.servlet.RequestToSessionMachine"

Au démarrage, le chemin d'accès à RequestToSessionMachine s'affiche comme indiqué sur la 4ème ligne.

C:\selenium>java -cp lib/* org.openqa.grid.selenium.GridLauncherV3 -role hub -servlets "selenium.extend.hub.servlet.RequestToSessionMachine"
15:57:41.639 INFO [GridLauncherV3.parse] - Selenium server version: 3.141.59, revision: e82be7d358
15:57:41.764 INFO [GridLauncherV3.lambda$buildLaunchers$5] - Launching Selenium Grid hub on port XXXX
15:57:41.858 INFO [Hub.<init>] - binding selenium.extend.hub.servlet.RequestToSessionMachine to /grid/admin/RequestToSessionMachine/*
2019-05-31 15:57:42.242:INFO::main: Logging initialized @1617ms to org.seleniumhq.jetty9.util.log.StdErrLog
15:57:42.757 INFO [Hub.start] - Selenium Grid hub is up and running
15:57:42.773 INFO [Hub.start] - Nodes should register to http://XXX.XXX.XXX.XXX:XXXX/grid/register/
15:57:42.773 INFO [Hub.start] - Clients should connect to http://XXX.XXX.XXX.XXX:XXXX/wd/hub

Démarrage du nœud

<détails>

Structure du répertoire Hub et commandes (décompression) </ summary>

C:.
│  chromedriver.exe
│  NodeConfigBrowser.json
│  start-node.bat
│
└─lib
        commons-logging-1.2.jar
        extend.jar
        gson-2.8.5.jar
        httpclient-4.5.8.jar
        httpcore-4.4.11.jar
        jna-4.5.2.jar
        jna-platform-4.5.2.jar
        selenium-server-standalone-3.141.59.jar
        sikulix2tigervnc-2.0.0-SNAPSHOT.jar
        sikulixapi-1.1.4-SNAPSHOT.jar

NodeConfigBrowser.json


{
 "capabilities": [
    {
     "platform": "WINDOWS",
     "browserName": "chrome",
     "maxInstances": 1,
     "seleniumProtocol": "WebDriver"
    }
  ],
 "hub": "http://XXXXXXXX:XXXX/grid/register",
 "register": true
}

start-node.bat


java -Dwebdriver.chrome.driver=chromedriver.exe -cp lib/* org.openqa.grid.selenium.GridLauncherV3 -role node -servlets "selenium.extend.node.servlet.ImageSelector" -nodeConfig NodeConfigBrowser.json

Au démarrage, le chemin d'accès à ImageSelector s'affiche comme indiqué sur la 4ème ligne.

C:\selenium-node>java -Dwebdriver.chrome.driver=chromedriver.exe -cp lib/* org.openqa.grid.selenium.GridLauncherV3 -role node -servlets "selenium.extend.node.servlet.ImageSelector" -nodeConfig NodeConfigBrowser.json
16:01:08.578 INFO [GridLauncherV3.parse] - Selenium server version: 3.141.59, revision: e82be7d358
16:01:08.704 INFO [GridLauncherV3.lambda$buildLaunchers$7] - Launching a Selenium Grid node on port XXXX
16:01:09.126 INFO [SelfRegisteringRemote.addExtraServlets] - binding selenium.extend.node.servlet.ImageSelector to /extra/ImageSelector/*
2019-05-31 16:01:09.220:INFO::main: Logging initialized @981ms to org.seleniumhq.jetty9.util.log.StdErrLog
16:01:09.485 INFO [WebDriverServlet.<init>] - Initialising WebDriverServlet
16:01:09.594 INFO [SeleniumServer.boot] - Selenium Server is up and running on port 42345
16:01:09.594 INFO [GridLauncherV3.lambda$buildLaunchers$7] - Selenium Grid node is up and ready to register to the hub
16:01:09.750 INFO [SelfRegisteringRemote$1.run] - Starting auto registration thread. Will try to register every 5000 ms.
16:01:10.237 INFO [SelfRegisteringRemote.registerToHub] - Registering the node to the hub: http://XXX.XXX.XXX.XXX:XXXX/grid/register
16:01:10.549 INFO [SelfRegisteringRemote.registerToHub] - The node is registered to the hub and ready to use

Essayez de bouger

Vous pouvez utiliser curl ou quoi que ce soit, mais comme il est gênant de générer une session, nous utiliserons l'environnement créé dans l'article précédent.

Programme d'essai

L'image de la destination de l'opération est convertie en base64 avec un service Web approprié, et les informations sont RESTées.

Il aurait pu être plus facile de confirmer à l'avenir s'il avait été incorporé dans le programme de conversion base64.

Programme de test (déploiement)

GoogleTest.java


package test;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import org.openqa.selenium.Point;

import com.google.gson.JsonObject;

import page.Google;

public class GoogleTest extends TestBase {

    @Test
    public void image() throws Exception {

        driver.get(Google._url);
        //Déplacer le navigateur vers une position invisible
        driver.manage().window().setPosition(new Point(-2000, 0));

        CloseableHttpClient client = null;
        CloseableHttpResponse res = null;

        JsonObject json = new JsonObject();

        try {

            //Génération d'URL de connexion au nœud
            URL remoteRequestURL = new URL("http://localhost:4444/grid/admin/RequestToSessionMachine/session/" + driver.getSessionId() + "/extra/ImageSelector/doubleclick");

            //Demander Json du corps au nœud tel quel
            client = HttpClients.createDefault();

            HttpPost httpPost = new HttpPost(remoteRequestURL.toURI());
            httpPost.setHeader("Content-type", "application/json; charset=UTF-8");

            json.addProperty("imageBase64", "data:image/png;base64,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");

            StringEntity entity = new StringEntity(json.toString(), StandardCharsets.UTF_8);
            httpPost.setEntity(entity);

            res = client.execute(httpPost);

            System.out.println(res.getStatusLine().getStatusCode());
            System.out.println(EntityUtils.toString(res.getEntity(), StandardCharsets.UTF_8));

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (res != null) {
                    res.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (client != null) {
                    try {
                        client.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
  1. Lancez Chrome
  2. Accéder à google
  3. Déplacez Chrome hors de vue
  4. Double-cliquez sur l'image ci-dessous

aaWS050367.png

Cela fonctionne en fait comme ceci du côté du nœud: kissing_heart: ggggg1.gif