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

com.seleniumtests.uipage.htmlelements.PictureElement Maven / Gradle / Ivy

There is a newer version: 4.23.18
Show newest version
/**
 * Orignal work: Copyright 2015 www.seleniumtests.com
 * Modified work: Copyright 2016 www.infotel.com
 *
 * 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.uipage.htmlelements;

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;

import javax.swing.ImageIcon;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.openqa.selenium.Rectangle;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.support.events.EventFiringWebDriver;
import org.openqa.selenium.support.ui.SystemClock;

import com.seleniumtests.core.SeleniumTestsContextManager;
import com.seleniumtests.customexception.ConfigurationException;
import com.seleniumtests.customexception.ImageSearchException;
import com.seleniumtests.customexception.ScenarioException;
import com.seleniumtests.driver.DriverMode;
import com.seleniumtests.driver.WebUIDriver;
import com.seleniumtests.driver.screenshots.ScreenshotUtil;
import com.seleniumtests.uipage.ReplayOnError;
import com.seleniumtests.util.helper.WaitHelper;
import com.seleniumtests.util.imaging.ImageDetector;
import com.seleniumtests.util.logging.SeleniumRobotLogger;

/**
 * Element which is found inside driver snapshot
 * @author behe
 *
 */
public class PictureElement {
	
	private File objectPictureFile;
	private String resourcePath;
	private HtmlElement intoElement;
	private Rectangle detectedObjectRectangle;
	private double pictureSizeRatio;
	private Robot robot;
	private EventFiringWebDriver driver;
	private ImageDetector detector;
	private ScreenshotUtil screenshotUtil;
	private SystemClock clock = new SystemClock();

	private static Logger logger = SeleniumRobotLogger.getLogger(PictureElement.class);
	
	public PictureElement() {
		// for mocks
	}

	public PictureElement(String label, String resourcePath, HtmlElement intoElement) {
		this(label, createFileFromResource(resourcePath), intoElement);
		this.resourcePath = resourcePath;
		driver = (EventFiringWebDriver)WebUIDriver.getWebDriver();
		
	}
	
	/**
	 * 
	 * @param label
	 * @param pictureFile
	 * @param intoElement	HtmlElement inside of which our picture is. It allows scrolling to the zone where 
	 * 						picture is searched before doing capture
	 */
	public PictureElement(String label, File pictureFile, HtmlElement intoElement) {
		this.intoElement = intoElement;
		if (isRobotUsable()) {
			try {
				robot = new Robot();
			} catch (AWTException e) {
				throw new ScenarioException("Cannot create robot", e);
			}
		}
		detector = new ImageDetector();
		setObjectPictureFile(pictureFile);
		screenshotUtil = new ScreenshotUtil();
	}
	
	private static File createFileFromResource(String resource)  {
		try {
			File tempFile = File.createTempFile("img", null);
			tempFile.deleteOnExit();
			FileUtils.copyInputStreamToFile(Thread.currentThread().getContextClassLoader().getResourceAsStream(resource), tempFile);
			
			return tempFile;
		} catch (IOException e) {
			throw new ConfigurationException("Resource cannot be found", e);
		}
	}
	
	/**
	 * Check whether Robot is usable
	 * It's only available in local mode (for now)
	 * @return
	 */
	public boolean isRobotUsable() {
		if (SeleniumTestsContextManager.getThreadContext().getRunMode() == DriverMode.LOCAL
				&& !SeleniumTestsContextManager.isMobileTest()) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Search the picture in the screenshot taken by Robot or WebDriver
	 * Robot is used in Desktop mode
	 * WebDriver is used in mobile, because Robot is not available for mobile platforms
	 * 
	 * @param searchOnly
	 */
	public void findElement(boolean searchOnly) {
		
		// scroll to element where our picture is
		if (intoElement != null) {
			ImageIcon image = new ImageIcon(objectPictureFile.getAbsolutePath());
			intoElement.scrollToElement(200 + image.getIconHeight());
		}
		
		File screenshotFile;
		if (robot == null || searchOnly) {
			screenshotFile = screenshotUtil.captureWebPageToFile();
		} else {
			screenshotFile = screenshotUtil.captureDesktopToFile();
		}
		if (screenshotFile == null) {
			throw new WebDriverException("Screenshot does not exist");
		}
		detector.setSceneImage(screenshotFile);
		detector.detectCorrespondingZone();
		detectedObjectRectangle = detector.getDetectedRectangle();
		pictureSizeRatio = detector.getSizeRatio();
	}


	/**
	 * Click in the center of the found picture
	 */
	public void click() {
		clickAt(0, 0);
	}
	
	/**
	 * Click at the coordinates xOffset, yOffset of the center of the found picture. Use negative offset to click on the left or
	 * top of the picture center
	 * In case the size ratio between searched picture and found picture is not 1, then, offset is
	 * the source offset so that it's compatible with any screen size and resolution
	 */
	@ReplayOnError
	public void clickAt(int xOffset, int yOffset) {
//		findElement(true);
//		
//		Point intoElementPos = intoElement.getCoordinates().inViewPort();
//		int relativeX = detectedObjectRectangle.x - intoElementPos.x;
//		int relativeY = detectedObjectRectangle.y - intoElementPos.y;
//		
//		new Actions(driver).moveToElement(intoElement, relativeX + xOffset, relativeY + yOffset).click().build().perform();
		
		if (robot == null) {
			throw new ScenarioException("click on picture is not supported on mobile devices and remote mode");
		}
		findElement(false);
		
	    robot.mouseMove(detectedObjectRectangle.x + detectedObjectRectangle.width / 2 + (int)(xOffset * pictureSizeRatio), 
	    			    detectedObjectRectangle.y + detectedObjectRectangle.height / 2 +(int)(yOffset * pictureSizeRatio));    
	    robot.mousePress(InputEvent.BUTTON1_MASK);
	    robot.mouseRelease(InputEvent.BUTTON1_MASK);
	}
	
	public void sendKeys(final CharSequence text, int xOffset, int yOffset) {
		clickAt(xOffset, yOffset);
		
		for (int i=0; i < text.length(); i++) {
			char ch = text.charAt(i);
			int keyCode = KeyEvent.getExtendedKeyCodeForChar((int)ch);
			boolean shift = false;
			if ((Character.isUpperCase(ch) && Character.isLetter(ch))
					|| Character.isDigit(ch)) {
				shift = true;
			}
			
			try {
				if (shift) {
					robot.keyPress(KeyEvent.VK_SHIFT);
				}
				robot.keyPress(keyCode);
				robot.keyRelease(keyCode);
				if (shift) {
					robot.keyRelease(KeyEvent.VK_SHIFT);
				}
			} catch (IllegalArgumentException e) {
				logger.warn(String.format("Character %s could not be written", text.charAt(i)), e);
			}
		}
	}

	public void sendKeys(final CharSequence text) {
		sendKeys(text, 0, 0);
	}
	
	/**
	 * Returns true in cas the searched picture is found
	 * @deprecated use isElementPresentInstead
	 * @return
	 */
	@Deprecated
	public boolean isVisible() {
		try {
			findElement(true);
			return true;
		} catch (ImageSearchException e) {
			return false;
		}
	}
	
	public boolean isElementPresent() {
		return isElementPresent(0);
	}
	
	/**
	 * check whether picture is present or not
	 * @param waitMs
	 * @return
	 */
	public boolean isElementPresent(int waitMs) {
		long end = clock.laterBy(waitMs);
		while (clock.isNowBefore(end) || waitMs == 0) {
			try {
				findElement(true);
				return true;
			} catch (ImageSearchException e) {
				if (waitMs == 0) {
					return false;
				}
				WaitHelper.waitForMilliSeconds(200);
				continue;
			}
		}
		return false;
		
	}
	
	@Override
	public String toString() {
		if (resourcePath != null) {
			return "Picture from resource " + resourcePath;
		} else {
			return "Picture from file " + objectPictureFile.getAbsolutePath();
		}
	}
	
	
	public void setRobot(Robot robot) {
		this.robot = robot;
	}

	public void setObjectPictureFile(File objectPictureFile) {
		this.objectPictureFile = objectPictureFile;
		detector.setObjectImage(objectPictureFile);
	}
	
	// TODO: actions for mobile
	// https://discuss.appium.io/t/tapping-on-the-screen-by-using-coordinates/5529/7
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy