All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.seleniumtests.connectors.selenium.SeleniumRobotGridConnector Maven / Gradle / Ivy

There is a newer version: 5.1.15
Show newest version
/**
 * 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.awt.Point;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

import com.seleniumtests.core.SeleniumTestsContextManager;
import com.seleniumtests.util.logging.DebugMode;
import io.appium.java_client.android.options.UiAutomator2Options;
import io.appium.java_client.ios.options.XCUITestOptions;
import io.appium.java_client.remote.options.BaseOptions;
import io.appium.java_client.remote.options.SupportsAppOption;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.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.openqa.selenium.Capabilities;
import org.openqa.selenium.MutableCapabilities;
import org.openqa.selenium.Platform;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;

import com.google.common.net.HttpHeaders;
import com.google.common.net.MediaType;
import com.seleniumtests.browserfactory.SeleniumRobotCapabilityType;
import com.seleniumtests.customexception.ConfigurationException;
import com.seleniumtests.customexception.ScenarioException;
import com.seleniumtests.customexception.SeleniumGridException;
import com.seleniumtests.util.FileUtility;

import kong.unirest.GetRequest;
import kong.unirest.HttpRequestWithBody;
import kong.unirest.HttpResponse;
import kong.unirest.Unirest;
import kong.unirest.UnirestException;
import kong.unirest.UnirestInstance;

public class SeleniumRobotGridConnector extends SeleniumGridConnector {

	private static final String ONLY_MAIN_SCREEN = "onlyMainScreen";
	private static final String Y_FIELD = "y";
	private static final String X_FIELD = "x";
	private static final String NAME_FIELD = "name";
	private static final String SESSION_FIELD = "session";
	private static final String OUTPUT_FIELD = "output";
	private static final String FILE_FIELD = "file";
	private static final String UPLOAD_FOLDER = "upload";
	private static final String ACTION_FIELD = "action";
	public static final String NODE_TASK_SERVLET = "/extra/NodeTaskServlet";
	public static final String FILE_SERVLET = "/extra/FileServlet";
	public static final String GUI_SERVLET = "/grid/admin/GuiServlet";
	
	
	private String nodeServletUrl;
	private URL hubServletUrl;
	
	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 MutableCapabilities uploadMobileApp(Capabilities caps) {
		SupportsAppOption mobileOptions;
		if (new BaseOptions(caps).getPlatformName() == null) {
			return (MutableCapabilities) caps;
		} else if (new BaseOptions(caps).getPlatformName().is(Platform.ANDROID)) {
			mobileOptions = new UiAutomator2Options(caps);
		} else if (new BaseOptions(caps).getPlatformName().is(Platform.IOS)) {
			mobileOptions = new XCUITestOptions(caps);
		} else return (MutableCapabilities) caps;

		// check whether app is given and app path is a local file
		if (mobileOptions.getApp().isPresent() && mobileOptions.getApp().get() != null && new File(String.valueOf(mobileOptions.getApp().get())).isFile()) {
			
			try (CloseableHttpClient client = HttpClients.createDefault();) {
				// zip file
				List appFiles = new ArrayList<>();
				appFiles.add(new File(String.valueOf(mobileOptions.getApp().get())));
				File zipFile = FileUtility.createZipArchiveFromFiles(appFiles);
				
				HttpHost serverHost = new HttpHost(hubServletUrl.getHost(), hubServletUrl.getPort());
				URIBuilder builder = new URIBuilder();
	        	builder.setPath("/grid/admin/FileServlet");
	        	builder.addParameter(OUTPUT_FIELD, "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
					mobileOptions.setApp(IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8) + "/" + appFiles.get(0).getName());
		        }
				// update original caps so that any change made here can be reused later
				return new MutableCapabilities(mobileOptions);
		        
			} catch (IOException | URISyntaxException e) {
				throw new SeleniumGridException("could not upload application file", e);
			}
		}
		return (MutableCapabilities) caps;
	}
	
	/**
	 * Upload file to node
	 * @param filePath			the file to upload
	 * @param returnLocalFile	if true, returned path will be the local path on grid node. If false, we get file://upload/file//
	 * @return
	 */
	@Override
	public String uploadFileToNode(String filePath, boolean returnLocalFile) {
		
		if (nodeUrl == null) {
			throw new ScenarioException("You cannot upload file to browser before driver has been created and corresponding node instanciated");
		}
		
		// zip file
		File zipFile = null;
		try {
			List appFiles = new ArrayList<>();
			appFiles.add(new File(filePath));
			zipFile = FileUtility.createZipArchiveFromFiles(appFiles);
		} catch (IOException e1) {
			throw new SeleniumGridException("Error in uploading file, when zipping: " + e1.getMessage());
		}

		logger.info("uploading file to node: " + zipFile.getName());
		try {
			HttpRequestWithBody req = Unirest.post(String.format("%s%s", nodeServletUrl, FILE_SERVLET))
					.header(HttpHeaders.CONTENT_TYPE, MediaType.OCTET_STREAM.toString())
					.queryString(OUTPUT_FIELD, "file");
			if (returnLocalFile) {
				req = req.queryString("localPath", "true");
			}
		
			HttpResponse response = req.field("upload", zipFile)
					.asString();
			
			if (response.getStatus() != 200) {
				throw new SeleniumGridException(String.format("Error uploading file: %s", response.getBody()));
			} else {
				return response.getBody();
			}
		} catch (UnirestException e) {
			throw new SeleniumGridException(String.format("Cannot upload file: %s", e.getMessage()));
		}

	}
	
	@Override
	public File downloadFileFromNode(String filePath) {
		
		if (nodeUrl == null) {
			throw new ScenarioException("You cannot download file before driver has been created and corresponding node instanciated");
		}
		
		if (!filePath.startsWith(UPLOAD_FOLDER)) {
			throw new ScenarioException(String.format("File path %s is invalid, only path in 'upload' folder are allowed", filePath));
		}
		
		Path targetFile = Paths.get(FileUtils.getTempDirectoryPath(), new File(filePath).getName());
		
		logger.info("downloading file from node: {}", filePath);
		try {
			HttpResponse response = Unirest.get(String.format("%s%s", nodeServletUrl, FILE_SERVLET))
					.queryString(FILE_FIELD, "file:" + filePath)
					.asFile(targetFile.toString(), StandardCopyOption.REPLACE_EXISTING)
					;

			if (response.getStatus() != 200) {
				throw new SeleniumGridException(String.format("Error downloading file %s: %s", filePath, response.getBody()));
			} else {
				return response.getBody();
			}
		} catch (UnirestException e) {
			throw new SeleniumGridException(String.format("Cannot download file: %s", filePath, e.getMessage()));
		}

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

			HttpHost serverHost = new HttpHost(hubServletUrl.getHost(), hubServletUrl.getPort());
			URIBuilder builder = new URIBuilder();
        	builder.setPath("/grid/admin/FileServlet/");
        	builder.addParameter(OUTPUT_FIELD, "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 {
	        	throw new NotImplementedException("call remote Robot to really upload file");
	        }
	        
		} catch (IOException | URISyntaxException e) {
			throw new SeleniumGridException("could not upload file", e);
		}
	}
	

	/**
	 * Get position of mouse pointer
	 * @return
	 */
	@Override
	public Point getMouseCoordinates() {
		if (nodeUrl == null) {
			throw new ScenarioException("You cannot get mouse coordinates before driver has been created and corresponding node instanciated");
		}
		
		String responseBody = null;
		logger.info("Mouse coordinates");
		try {
			// we get a string with x,y
			HttpResponse response = Unirest.get(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
					.queryString(ACTION_FIELD, "mouseCoordinates")
					.asString();
			if (response.getStatus() != 200) {
				logger.error(String.format("Mouse coordinates error: %s", response.getBody()));
				return new Point(0,0);
			}
			responseBody = response.getBody();
			String[] coords = responseBody.split(",");
			return new Point(Integer.parseInt(coords[0]), Integer.parseInt(coords[1]));
			
			
		} catch (UnirestException e) {
			logger.warn(String.format("Could not get mouse coordinates: %s", e.getMessage()));
		} catch (IndexOutOfBoundsException | NumberFormatException e) {
			logger.error(String.format("mouse coordinates '%s' are invalid", responseBody));
		}
		return new Point(0,0);
	}

	/**
	 * Left click on desktop at x,y
	 * @param x		x coordinate
	 * @param y		y coordinate
	 */
	@Override
	public void leftClic(int x, int y) {
		leftClic(false, x, y);
	}
		
	/**
	 * Left click on desktop at x,y
	 * @param x		x coordinate
	 * @param y		y coordinate
	 * @param onlyMainScreen	if true, click coordinates are on the main screen
	 */
	@Override
	public void leftClic(boolean onlyMainScreen, 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 {
			HttpResponse response = Unirest.post(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
				.queryString(ACTION_FIELD, "leftClic")
				.queryString(X_FIELD, x)
				.queryString(Y_FIELD, y)
				.queryString(ONLY_MAIN_SCREEN, onlyMainScreen)
				.asString();
			if (response.getStatus() != 200) {
				logger.error(String.format("Left click error: %s", response.getBody()));
			}
		} 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) {
		doubleClick(false, x, y);
	}
	
	/**
	 * Double click on desktop at x,y
	 * @param x		x coordinate
	 * @param y		y coordinate
	 * @param onlyMainScreen	if true, click coordinates are on the main screen
	 */
	@Override
	public void doubleClick(boolean onlyMainScreen, 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 {
			HttpResponse response = Unirest.post(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
			.queryString(ACTION_FIELD, "doubleClick")
			.queryString(X_FIELD, x)
			.queryString(Y_FIELD, y)
			.queryString(ONLY_MAIN_SCREEN, onlyMainScreen)
			.asString();
			if (response.getStatus() != 200) {
				logger.error(String.format("Double click error: %s", response.getBody()));
			}
		} 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) {
		rightClic(false, x, y);
	}
	
	/**
	 * right click on desktop at x,y
	 * @param x		x coordinate
	 * @param y		y coordinate
	 * @param onlyMainScreen	if true, click coordinates are on the main screen
	 */
	@Override
	public void rightClic(boolean onlyMainScreen, 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 {
			HttpResponse response = Unirest.post(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
				.queryString(ACTION_FIELD, "rightClic")
				.queryString(X_FIELD, x)
				.queryString(Y_FIELD, y)
				.queryString(ONLY_MAIN_SCREEN, onlyMainScreen)
				.asString();
			if (response.getStatus() != 200) {
				logger.error(String.format("Right click error: %s", response.getBody()));
			}
		} 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() {
		return captureDesktopToBuffer(false);
	}
	
	@Override
	public String captureDesktopToBuffer(boolean onlyMainScreen) {
		if (nodeUrl == null) {
			throw new ScenarioException("You cannot take screenshot before driver has been created and corresponding node instanciated");
		}
		
		logger.info("capturing desktop");
		try {
			HttpResponse response =  Unirest.get(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
				.queryString(ACTION_FIELD, "screenshot")
				.queryString(ONLY_MAIN_SCREEN, onlyMainScreen)
				.asString();
			
			if (response.getStatus() != 200) {
				logger.error(String.format("capture desktop error: %s", response.getBody()));
			} else {
				return response.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 {
			byte[] byteArray = base64Content.getBytes();
			byte[] decodeBuffer = Base64.decodeBase64(byteArray);
		
			HttpResponse response = Unirest.post(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
				.header(HttpHeaders.CONTENT_TYPE, MediaType.OCTET_STREAM.toString())
				.queryString(ACTION_FIELD, "uploadFile")
				.queryString(NAME_FIELD, fileName)
				.body(decodeBuffer)
				.asString();
			
			if (response.getStatus() != 200) {
				logger.error(String.format("Error uploading file: %s", response.getBody()));
			}
		} catch (UnirestException e) {
			logger.warn(String.format("Cannot upload file: %s", e.getMessage()));
		} 
	}
	
	/**
	 * Send keys to desktop
	 * @param keyCodes		List of KeyEvent
	 */
	@Override
	public void sendKeysWithKeyboard(List 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 {
			HttpResponse response = Unirest.post(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
				.queryString(ACTION_FIELD, "sendKeys")
				.queryString("keycodes", keyCodeString).asString();
			
			if (response.getStatus() != 200) {
				logger.error(String.format("Send keys error: %s", response.getBody()));
			}
		} catch (UnirestException e) {
			logger.warn(String.format("Could send keys: %s", e.getMessage()));
		}
	}
	
	/**
	 * Display running step
	 * @param stepName
	 */
	@Override
	public void displayRunningStep(String stepName) {
		if (nodeUrl == null) {
			throw new ScenarioException("You cannot display running step before driver has been created and corresponding node instanciated");
		}
		
		try {
			HttpResponse response = Unirest.post(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
					.queryString(ACTION_FIELD, "displayRunningStep")
					.queryString("stepName", stepName)
					.queryString(SESSION_FIELD, sessionId)
					.asString();
			
			if (response.getStatus() != 200) {
				logger.error(String.format("display running step error: %s", response.getBody()));
			}
		} catch (UnirestException e) {
			logger.warn(String.format("Could not display running step: %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.substring(0, Math.min(2, text.length())) + "****");
		try {
			HttpResponse response = Unirest.post(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
				.queryString(ACTION_FIELD, "writeText")
				.queryString("text", text).asString();
		
			if (response.getStatus() != 200) {
				logger.error(String.format("Write text error: %s", response.getBody()));
			}
		} 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 {
			HttpResponse response = Unirest.post(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
				.queryString(ACTION_FIELD, "kill")
				.queryString("process", processName).asString();
			
			if (response.getStatus() != 200) {
				logger.error(String.format("kill process error: %s", response.getBody()));
			}
		} catch (UnirestException e) {
			logger.warn(String.format("Could not kill process %s: %s", processName, e.getMessage()));
		}
	}
	

	/**
	 * Execute command
	 * @param program	name of the program
	 * @param args		arguments of the program
	 */
	@Override
	public String executeCommand(String program, String ... args) {
		return executeCommand(program, null, args);
	}
	
	/**
	 * Execute command with timeout
	 * @param program	name of the program
	 * @param timeout	if null, default timeout will be applied
	 * @param args		arguments of the program
	 */
	@Override
	public String executeCommand(String program, Integer timeout, String ... args) {
		if (nodeUrl == null) {
			throw new ScenarioException("You cannot execute a remote process before driver has been created and corresponding node instanciated");
		}
		
		logger.info("execute program: " + program);
		try (UnirestInstance unirest = Unirest.spawnInstance()
				
				) {
			
			HttpRequestWithBody req = unirest.post(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
				.queryString(ACTION_FIELD, "command")
				.queryString(NAME_FIELD, program);
			
			if (getSessionId() != null) {
				req = req.queryString(SESSION_FIELD, getSessionId().toString());
			}
			
			int i = 0;
			for (String arg: args) {
				req = req.queryString("arg" + i, arg);
				i++;
			}
			if (timeout != null) {
				unirest.config().socketTimeout((timeout + 5 ) * 1000);
				req = req.queryString("timeout", timeout);
			}
			
			HttpResponse response = req.asString();
			return response.getBody();
		} catch (UnirestException e) {
			logger.warn(String.format("Could not execute process %s: %s", program, e.getMessage()));
			return "";
		}
	}
	
	/**
	 * 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 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 {
			HttpResponse response =  Unirest.get(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
				.queryString(ACTION_FIELD, "processList")
				.queryString(NAME_FIELD, processName)
				.asString();
			
			if (response.getStatus() != 200) {
				logger.error(String.format("get process list error: %s", response.getBody()));
			} else {
				List pidListStr = Arrays.asList(
					response.getBody()
					.trim()
					.split(","));
				return pidListStr.stream()
						.filter(p -> !p.isEmpty())
						.map(Integer::parseInt).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 {
			HttpResponse response = Unirest.get(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET))
				.queryString(ACTION_FIELD, "startVideoCapture")
				.queryString(SESSION_FIELD, sessionId)
				.asString();
			
			if (response.getStatus() != 200) {
				logger.error(String.format("start video capture error: %s", response.getBody()));
			}
			
		} 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 (UnirestInstance unirest = Unirest.spawnInstance()) {
			
			// delete file if it exists as '.asFile()' will not overwrite it
			deleteExistingVideo(outputFile);

			long start = System.currentTimeMillis();

			ExecutorService executor = Executors.newSingleThreadExecutor();
			Future future = executor.submit(() -> {
				GetRequest getRequest = unirest.get(String.format("%s%s", nodeServletUrl, NODE_TASK_SERVLET));
				if (SeleniumTestsContextManager.getGlobalContext().getDebug().contains(DebugMode.NETWORK)) {
					getRequest = getRequest
							.downloadMonitor((b, fileName, bytesWritten, totalBytes) -> {
								logger.info(String.format("File %s: %d/%d", fileName, bytesWritten, totalBytes));
							});
				}
				HttpResponse videoResponse = getRequest
						.socketTimeout(60000)
						.queryString(ACTION_FIELD, "stopVideoCapture")
						.queryString(SESSION_FIELD, sessionId)
						.asFile(outputFile);

				if (videoResponse.getStatus() != 200) {
					logger.error(String.format("stop video capture error: %s", videoResponse.getBody()));
					return null;
				} else {
					logger.info(String.format("Video file downloaded (%d kb in %d ms)", videoResponse.getBody().length() / 1000, System.currentTimeMillis() - start));
					return videoResponse.getBody();
				}
			});
			File videoFile = future.get(60, TimeUnit.SECONDS);
			future.cancel(true);
			return videoFile;

		} catch (UnirestException e) {
			logger.warn(String.format("Could not stop video capture: %s", e.getMessage()));
			return null;
		} catch (InterruptedException | ExecutionException | TimeoutException e) {
			logger.warn("Video file not get due to " + e.getClass().getName());
			return null;
		}
	}
	
	private void deleteExistingVideo(String outputFile) {
		if (new File(outputFile).exists()) {
			try {
				Files.delete(Paths.get(outputFile));
					
			} catch (Exception e) {
				logger.warn("Error deleting previous video file, there may be a problem getting the new one: " + e.getMessage());
			}
		}
	}
	
	/**
	 * For TESTING only
	 */
	public void setNullNodeUrl() {
		super.setNodeUrl(null);
	}
	

	@Override
	public void setNodeUrl(String nodeUrl) {
		super.setNodeUrl(nodeUrl);
		int nodePort;
		try {
			nodePort = new URL(nodeUrl).getPort();
		} catch (MalformedURLException e) {
			throw new ConfigurationException("Node URL is invalid: " + nodeUrl);
		}
		nodeServletUrl = nodeUrl.replace(Integer.toString(nodePort), Integer.toString(nodePort + 10));
	}

	@Override
	protected void setHubUrl(String hubUrl) {
		super.setHubUrl(hubUrl);
		
		try {
			hubServletUrl = new URL(hubUrl.replace(Integer.toString(hubPort), Integer.toString(hubPort + 10)));
		} catch (MalformedURLException e) {
			throw new ConfigurationException("Hub URL is invalid: " + nodeUrl);
		}
	}
	
	public String getNodeServletUrl() {
		return nodeServletUrl;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy