/*
* Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
*
* Copyright 2013 - 2015 the original author or authors.
*
* 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 org.sakuli.actions.screenbased;
import org.sakuli.actions.Action;
import org.sakuli.actions.ModifySahiTimer;
import org.sakuli.actions.environment.Environment;
import org.sakuli.actions.logging.LogToResult;
import org.sakuli.loader.BeanLoader;
import org.sakuli.loader.ScreenActionLoader;
import org.sikuli.script.Screen;
import java.io.IOException;
import java.nio.file.Path;
/**
* @author Tobias Schneck
*/
public class Region implements Action {
private RegionImpl regionImpl;
private TypingUtil<Region> typingUtil;
/**
* Creates a new Region of the complete screen.
*/
public Region() {
this(false);
}
/**
* Creates a new Region of the complete screen.
*
* @param resumeOnException if true, the test execution won't stop on an occurring error.
*/
public Region(boolean resumeOnException) {
this.regionImpl = new RegionImpl(resumeOnException, getScreenActionLoader());
typingUtil = new TypingUtil<>(this);
}
/**
* Creates a new Region from the position parameters.
*
* @param x x position of a rectangle on the screen.
* @param y x position of a rectangle on the screen.
* @param w weight of the rectangle stating by x,y
* @param h height of the rectangle stating by x,y
*/
public Region(int x, int y, int w, int h) {
this(x, y, w, h, false);
}
/**
* Creates a new Region from the position parameters.
*
* @param resumeOnException if true, the test execution won't stop on an occurring error.
* @param x x position of a rectangle on the screen.
* @param y x position of a rectangle on the screen.
* @param w weight of the rectangle stating by x,y
* @param h height of the rectangle stating by x,y
*/
public Region(int x, int y, int w, int h, boolean resumeOnException) {
this.regionImpl = new RegionImpl(x, y, w, h, resumeOnException, getScreenActionLoader());
typingUtil = new TypingUtil<>(this);
}
/**
* Wrapper for the {@link org.sikuli.script.Region} objects.
*/
public Region(org.sikuli.script.Region region, boolean resumeOnException) {
this.regionImpl = new RegionImpl(region, resumeOnException, getScreenActionLoader());
typingUtil = new TypingUtil<>(this);
}
/**
* Wrapper for the {@link RegionImpl} objects.
*/
public Region(RegionImpl regionImpl) {
this.regionImpl = regionImpl;
typingUtil = new TypingUtil<>(this);
}
protected ScreenActionLoader getScreenActionLoader() {
return BeanLoader.loadScreenActionLoader();
}
/**
* Finds an image inside this region immediately.
*
* @param imageName name of the preloaded picture
* @return the found {@link Region} or if the target can't be found {@code null}.
*/
@LogToResult(message = "find the image in this region")
public Region find(String imageName) {
return update(regionImpl.find(imageName));
}
/**
* Finds a target in this {@link Region} immediately;
*
* @return the found {@link Region} or if the target can't be found {@code null}.
*/
@LogToResult(message = "find in this region another region")
public Region findRegion(Region region) {
return update(regionImpl.findRegion(region.getRegionImpl()));
}
/**
* Check whether the given image is visible in the region.
*
* @return this {@link Region} or null
*/
@LogToResult(message = "check if this image is visible in this region")
public Region exists(String imageName) {
return update(regionImpl.exists(imageName));
}
/**
* Check whether the given image is visible in the region in x seconds.
*
* @return this {@link Region} or null
*/
@LogToResult(message = "check if this region is visible")
public Region exists(String imageName, int seconds) {
return update(regionImpl.exists(imageName, seconds));
}
/**
* makes a mouse click on the center of the {@link Region}.
*
* @return the {@link Region} or NULL on errors.
*/
@ModifySahiTimer
@LogToResult
public Region click() {
return update(regionImpl.clickMe());
}
/**
* makes a double click on the center of the {@link Region}.
*
* @return the {@link Region} or NULL on errors.
*/
@ModifySahiTimer
@LogToResult
public Region doubleClick() {
return update(regionImpl.doubleClickMe());
}
/**
* makes a rigth click on the center of the {@link Region}.
*
* @return the {@link Region} or NULL on errors.
*/
@ModifySahiTimer
@LogToResult
public Region rightClick() {
return update(regionImpl.rightClickMe());
}
/**
* Move the mouse pointer to the center of the {@link Region} and "hovers" it.
*
* @return the {@link Region} or NULL on errors.
*/
@ModifySahiTimer
@LogToResult
public Region mouseMove() {
return update(regionImpl.mouseMoveMe());
}
/**
* Low-level mouse action to press the assigned {@link MouseButton} on the current position.
* <p>
* Example: Press and release the right mouse button vor 3 seconds on a specified region:
* <br>
* {@code
* Region region = new Region().find("your-pattern.png");
* region.mouseDown(MouseButton.RIGHT).sleep(3).mouseUp(MouseButton.RIGHT);
* }
* </p>
*
* @return the {@link Region} or NULL on errors.
*/
@ModifySahiTimer
@LogToResult
public Region mouseDown(MouseButton mouseButton) {
return update(regionImpl.mouseDown(mouseButton));
}
/**
* Low-level mouse action to release the assigned {@link MouseButton}.
* <p>
* Example: Press and release the right mouse button vor 3 seconds on a specified region:
* <br>
* {@code
* Region region = new Region().find("your-pattern.png");
* region.mouseDown(MouseButton.RIGHT).sleep(3).mouseUp(MouseButton.RIGHT);
* }
* </p>
*
* @return the {@link Region} or NULL on errors.
*/
@ModifySahiTimer
@LogToResult
public Region mouseUp(MouseButton mouseButton) {
return update(regionImpl.mouseUp(mouseButton));
}
/**
* Drag from region's current position and drop at given targetRegion and using the left mouse.
*
* @param targetRegion {@link Region} object of the target
* @return the tragetRegion or null on failure
*/
@ModifySahiTimer
@LogToResult
public Region dragAndDropTo(Region targetRegion) {
return update(regionImpl.dragAndDropTo(targetRegion.getRegionImpl()));
}
/**
* Blocks and waits until a target which is specified by the optImageName is found in the hole {@link Screen} within
* a given time period in seconds.
*
* @param imageName name of the image pattern
* @param seconds the maximum time to waitFor in seconds
* @return a {@link Region} object representing the region occupied by the found target, or null if the target can
* not be found within the given time.
*/
@LogToResult(message = "wait for image for x seconds")
public Region waitForImage(String imageName, int seconds) {
return update(regionImpl.waitForImage(imageName, seconds));
}
/**
* {@link TypingUtil#paste(String)}.
*/
@ModifySahiTimer
@LogToResult(logClassInstance = false)
public Region paste(String text) {
return typingUtil.paste(text);
}
/**
* {@link TypingUtil#pasteMasked(String)}.
*/
@ModifySahiTimer
@LogToResult(logClassInstance = false, logArgs = false)
public Region pasteMasked(String text) {
return typingUtil.pasteMasked(text);
}
/**
* {@link TypingUtil#pasteAndDecrypt(String)}.
*/
@ModifySahiTimer
@LogToResult(logClassInstance = false, logArgs = false)
public Region pasteAndDecrypt(String text) {
return typingUtil.pasteAndDecrypt(text);
}
/**
* See {@link TypingUtil#type(String, String)}.
*/
@ModifySahiTimer
@LogToResult(message = "type over system keyboard", logClassInstance = false)
public Region type(String text) {
return typingUtil.type(text, null);
}
/**
* See {@link TypingUtil#type(String, String)}.
*/
@ModifySahiTimer
@LogToResult(message = "type with pressed modifiers", logClassInstance = false)
public Region type(String text, String optModifiers) {
return typingUtil.type(text, optModifiers);
}
/**
* See {@link TypingUtil#typeMasked(String, String)}.
*/
@ModifySahiTimer
@LogToResult(message = "type over system keyboard", logClassInstance = false, logArgs = false)
public Region typeMasked(String text) {
return typingUtil.typeMasked(text, null);
}
/**
* See {@link TypingUtil#typeMasked(String, String)}.
*/
@ModifySahiTimer
@LogToResult(message = "type with pressed modifiers", logClassInstance = false, logArgs = false)
public Region typeMasked(String text, String optModifiers) {
return typingUtil.typeMasked(text, optModifiers);
}
/**
* See {@link TypingUtil#typeAndDecrypt(String, String)} .
*/
@ModifySahiTimer
@LogToResult(message = "decrypt and type over system keyboard", logClassInstance = false, logArgs = false)
public Region typeAndDecrypt(String text) {
return typingUtil.typeAndDecrypt(text, null);
}
/**
* See {@link TypingUtil#typeAndDecrypt(String, String)} .
*/
@ModifySahiTimer
@LogToResult(message = "decrypt and type with pressed modifiers", logClassInstance = false, logArgs = false)
public Region typeAndDecrypt(String text, String optModifiers) {
return typingUtil.typeAndDecrypt(text, optModifiers);
}
/**
* See {@link TypingUtil#keyDown(String)}.
*/
@ModifySahiTimer
@LogToResult(message = "press key down", logClassInstance = false)
public Region keyDown(String keys) {
return typingUtil.keyDown(keys);
}
/**
* See {@link TypingUtil#keyUp(String)}.
*/
@ModifySahiTimer
@LogToResult(message = "press key up", logClassInstance = false)
public Region keyUp(String keys) {
return typingUtil.keyUp(keys);
}
/**
* See {@link TypingUtil#write(String)}.
*/
@ModifySahiTimer
@LogToResult(message = "interpret and write the following expresion", logClassInstance = false)
public Region write(String text) {
return typingUtil.write(text);
}
/**
* delete a amount of chars in a field
*
* @param amountOfChars number of chars to delete
* @return this {@link Region} or null on errors
*/
@ModifySahiTimer
@LogToResult
public Region deleteChars(int amountOfChars) {
return update(regionImpl.deleteChars(amountOfChars));
}
/**
* {@link TypingUtil#mouseWheelDown(int)}
*/
@LogToResult(message = "move mouse wheel down x times")
public Region mouseWheelDown(int steps) {
return typingUtil.mouseWheelDown(steps);
}
/**
* {@link TypingUtil#mouseWheelUp(int)}
*/
@LogToResult(message = "move mouse wheel up x times")
public Region mouseWheelUp(int steps) {
return typingUtil.mouseWheelUp(steps);
}
/**
* Set a offset to a specific {@link Region} and returns the new {@link Region} object. The offset function will
* move the Region's rectangle with x to the right and with y to the left. The size of the rectangle will be the
* same.
*
* @param offsetX x-value for the offset action
* @param offsetY y-value for the offset action
* @return a {@link Region} with the new coordinates
*/
@LogToResult(message = "move this region")
public Region move(int offsetX, int offsetY) {
return update(regionImpl.move(offsetX, offsetY));
}
/**
* create a region enlarged range pixels on each side
*
* @param range of pixels
* @return a new {@link Region}
*/
@LogToResult(message = "grow on each side")
public Region grow(int range) {
return update(regionImpl.grow(range));
}
/**
* create a region with enlarged range pixels
*
* @param width in pixels to grow in both directions
* @param height in pixels to grow in both directions
* @return a new {@link Region}
*/
@LogToResult(message = "grow on each side with width and height")
public Region grow(int width, int height) {
return update(regionImpl.grow(width, height));
}
/**
* @return a new {@link Region} that is defined above the current region’s top border with a height of range number
* of pixels.
*/
@LogToResult()
public Region above(int range) {
return update(regionImpl.above(range));
}
/**
* @return a new {@link Region} that is defined below the current region’s top border with a height of range number
* of pixels.
*/
@LogToResult()
public Region below(int range) {
return update(regionImpl.below(range));
}
/**
* @return a new {@link Region} that is defined on the left the current region’s top border with a height of range
* number of pixels.
*/
@LogToResult()
public Region left(int range) {
return update(regionImpl.left(range));
}
/**
* @return a new {@link Region} that is defined on the right the current region’s top border with a height of range
* number of pixels.
*/
@LogToResult()
public Region right(int range) {
return update(regionImpl.right(range));
}
/**
* @return height as int value
*/
@LogToResult
public int getH() {
return regionImpl.getH();
}
/**
* set the height, based form the upper left corner downsides
*/
@LogToResult(message = "set new height for this region")
public Region setH(int height) {
this.regionImpl.setH(height);
return this;
}
/**
* @return width as int value
*/
@LogToResult
public int getW() {
return regionImpl.getW();
}
/**
* set the width, based form the upper left corner to the right
*/
@LogToResult(message = "set new width for this region")
public Region setW(int width) {
this.regionImpl.setW(width);
return this;
}
/**
* @return X coordinate of the upper left corner
*/
@LogToResult
public int getX() {
return regionImpl.getX();
}
/**
* set the X coordinate of the upper left corner.
*/
@LogToResult(message = "set new x coordinate for this region")
public Region setX(int x) {
this.regionImpl.setX(x);
return this;
}
/**
* @return Y coordinate of the upper left corner
*/
@LogToResult
public int getY() {
return regionImpl.getY();
}
/**
* set the Y coordinate of the upper left corner.
*/
@LogToResult(message = "set new y coordinate for this region")
public Region setY(int y) {
this.regionImpl.setY(y);
return this;
}
/**
* highlights this {@link Region} for x seconds
*/
@LogToResult
public Region highlight(int seconds) {
regionImpl.highlight(seconds);
return this;
}
/**
* highlights this {@link Region} for the deault time
*/
@LogToResult
public Region highlight() {
regionImpl.highlight(getLoader().getSettings().DefaultHighlightTime);
return this;
}
/**
* Takes a screenshot of the current Region in the screen and saves it the current testcase folder with the assigned
* filename. If an absolute Path is assigned like e.g. `/home/user/test.jpg`, the screenshot will be saved at that place.
*
* @param filename name of the screenshot file, e.g. `region_screenshot`
* @return {@link Path} to the created screenshot OR null on errors
*/
@LogToResult
public Path takeScreenshot(String filename) {
Path filePath = RegionImpl.resolveTakeScreenshotFolder(filename, getLoader());
try {
return getLoader().getScreenshotActions().takeScreenshot(
filePath.getFileName().toString(),
filePath.getParent(),
regionImpl.getRect());
} catch (IOException e) {
getLoader().getExceptionHandler().handleException(e);
}
return null;
}
/**
* Blocks the current testcase execution for x seconds
*
* @param seconds to sleep
* @return this {@link Region} or NULL on errors.
*/
@LogToResult(message = "sleep and do nothing for x seconds", logClassInstance = false)
public Region sleep(Integer seconds) {
return typingUtil.sleep(seconds * 1000L);
}
/**
* Blocks the current testcase execution for x seconds
*
* @param seconds to sleep
* @return this {@link Region} or NULL on errors.
*/
@LogToResult(message = "sleep and do nothing for x seconds", logClassInstance = false)
public Region sleep(Double seconds) {
return typingUtil.sleep((long) (seconds * 1000L));
}
/**
* Blocks the current testcase execution for x milliseconds
*
* @param milliseconds to sleep
* @return this {@link Environment} or NULL on errors.
*/
@LogToResult(message = "sleep and do nothing for x milliseconds", logClassInstance = false)
public Region sleepMs(Integer milliseconds) {
return typingUtil.sleep(Long.valueOf(milliseconds));
}
/**
* @return from this region a extracted Text via OCR as {@link String}
*/
@LogToResult(message = "extract text via OCR from Region")
public String extractText() {
return regionImpl.extractText();
}
@Override
public String toString() {
return regionImpl.toString();
}
/**
* @return gets the inherit java object for not yet wrapped methods
*/
public RegionImpl getRegionImpl() {
return regionImpl;
}
/**
* updats the inherit java object after modifaction with {@link #getRegionImpl}.
*/
public void setRegionImpl(RegionImpl regionImpl) {
this.regionImpl = regionImpl;
}
private Region update(RegionImpl regionImpl) {
return regionImpl != null ? new Region(regionImpl, getResumeOnException()) : null;
}
private Region update(org.sikuli.script.Region region) {
return region != null ? new Region(region, getResumeOnException()) : null;
}
@Override
public boolean getResumeOnException() {
return regionImpl.getResumeOnException();
}
@Override
public ScreenActionLoader getLoader() {
return regionImpl.getLoader();
}
@Override
public RegionImpl getActionRegion() {
return regionImpl;
}
}