eu.tsystems.mms.tic.testframework.utils.JSUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of driver-ui Show documentation
Show all versions of driver-ui Show documentation
Testerra test automation framework - driver-ui module
/*
* Testerra
*
* (C) 2020, Peter Lehmann, T-Systems Multimedia Solutions GmbH, Deutsche Telekom AG
*
* Deutsche Telekom AG and all other contributors /
* copyright owners license this file to you 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 eu.tsystems.mms.tic.testframework.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import eu.tsystems.mms.tic.testframework.constants.JSMouseAction;
import eu.tsystems.mms.tic.testframework.exceptions.NotYetImplementedException;
import eu.tsystems.mms.tic.testframework.exceptions.SystemException;
import eu.tsystems.mms.tic.testframework.pageobjects.GuiElement;
import eu.tsystems.mms.tic.testframework.pageobjects.UiElement;
import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.GuiElementData;
import eu.tsystems.mms.tic.testframework.pageobjects.layout.Layout;
import java.io.IOException;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.io.IOUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Point;
import org.openqa.selenium.Rectangle;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* JavaScript Utils.
*
* @author pele
* // TODO Move this class to driver-ui-desktop
*/
public final class JSUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(JSUtils.class);
public void addJavascriptResources(WebDriver webDriver, Stream resourceFiles) {
executeScript(webDriver, readScriptResources(resourceFiles));
}
/**
* try to implement javascript on page
*
* @param driver .
* @param resourceFile .
* @param id .
* @deprecated Use {@link #addJavascriptResources(WebDriver, Stream)} instead
*/
@Deprecated
public static void implementJavascriptOnPage(final WebDriver driver, final String resourceFile, final String id) {
// TODO rnhb&pele: find a solution that is not conflicting with GuiElement activity (maybe run this before
// GuiElement) --> Switch to default content will end in "GuiElement not found", because the frame of GuiElement
// isn't active anymore
// driver.switchTo().defaultContent();
if (isJavascriptImplementedOnPage(driver, id)) {
LOGGER.warn("Already injected: " + resourceFile);
return;
}
String inline = readScriptResources(Stream.of(resourceFile));
implementJavascriptOnPage(id, driver, inline);
}
/**
* @deprecated Use {@link #addJavascriptResources(WebDriver, Stream)} instead
*/
private static void implementJavascriptOnPage(
String scriptId,
WebDriver driver,
String script
) {
try {
if (script.length() > 0) {
Gson gson = new GsonBuilder().create();
JavascriptExecutor javascriptExecutor = (JavascriptExecutor) driver;
String injectedScript = String.format("var script=document.createElement('script');" +
"var text=document.createTextNode(%s);" +
"script.type='text/javascript';" +
"script.id=%s;" +
"script.appendChild(text);" +
"document.body.appendChild(script);",
gson.toJson(script),
gson.toJson(scriptId)
);
javascriptExecutor.executeScript(injectedScript);
}
} catch (Exception e) {
LOGGER.error("Error executing javascript: " + e.getMessage(), e);
}
}
/**
* executing async script
*
* @param driver .
* @param script .
* @param parameters .
* @return .
*/
public static Object executeAsyncScript(final WebDriver driver, final String script, final Object... parameters) {
JavascriptExecutor javascriptExecutor = (JavascriptExecutor) driver;
return javascriptExecutor.executeAsyncScript(script, parameters);
}
/**
* Try to execute javascript. If an error occurs it will be thrown.
*
* @param driver .
* @param script .
* @param parameters .
* @return .
*/
public static Object executeScriptWOCatch(final WebDriver driver, final String script, final Object... parameters) {
JavascriptExecutor javascriptExecutor = (JavascriptExecutor) driver;
return javascriptExecutor.executeScript(script, parameters);
}
/**
* Try to execute javascript. If an error occurs it will be logged only.
*
* @param driver .
* @param script .
* @param parameters .
* @return .
*/
public static Object executeScript(final WebDriver driver, final String script, final Object... parameters) {
try {
return executeScriptWOCatch(driver, script, parameters);
} catch (Exception e) {
String message = String.format("Error executing Javascript\n-----\n%s\n-----", script);
if (parameters.length > 0) {
message += "\nwith parameters:\n" + Arrays.stream(parameters).map(o -> (o==null?"null":o.toString())).collect(Collectors.joining("\n"));
message += "\n-----";
}
LOGGER.error(message, e);
return null;
}
}
public static String readScriptResources(Stream resourceFiles) {
StringBuilder sb = new StringBuilder();
resourceFiles.forEach(resourceFile -> {
try {
sb.append(new String(IOUtils.toByteArray(Objects.requireNonNull(JSUtils.class.getClassLoader().getResourceAsStream(resourceFile)))));
} catch (IOException e) {
LOGGER.error("Unable to read resourceFile", e);
}
});
return sb.toString();
}
private static boolean isJavascriptImplementedOnPage(final WebDriver driver, final String id) {
List script = driver.findElements(By.xpath(".//script[@id='" + id + "']"));
return script.size() > 0;
}
/**
* Execute a JS MouseAction on a driver session.
*
* @param driver .
* @param containerWebElement .
* @param type .
* @param x .
* @param y .
*/
public static void executeJavaScriptMouseAction(
final WebDriver driver,
WebElement containerWebElement,
JSMouseAction type,
int x,
int y
) {
String aimedElement = "arguments[0]";
if (containerWebElement == null) {
aimedElement = "document";
}
((JavascriptExecutor) driver).executeScript("var evt = document.createEvent('MouseEvents');"
+ " evt.initMouseEvent('" + type.getActionCommand() + "',true, true, window, 0, 0, 0,"
+ x + "," + y + ","
+ " false, false, false, false, 0,null);" +
" " + aimedElement + ".dispatchEvent(evt);", containerWebElement);
}
/**
* Scroll to top of page.
*
* @param webDriver WebDriver instance.
*/
public void scrollToTop(WebDriver webDriver) {
final JavascriptExecutor jsExecutor = (JavascriptExecutor) webDriver;
jsExecutor.executeScript("scroll(0,0);");
}
/**
* Scrolls an element to the top of a page.
* @param webDriver
* @param webElement
*/
public void scrollElementToTop(WebDriver webDriver, WebElement webElement) {
executeScript(
webDriver,
"window.scrollTo({ top: arguments[0].offsetTop, left: arguments[0].offsetLeft });",
webElement
);
}
/**
* Get a javascript based selector including frame hierarchy by extracting the By-Property of GuiElement
* Uses Reflection
*
* @param element GuiElement
* @return String
*/
@Deprecated
public static String getJavaScriptSelector(GuiElement element) {
final String jsFrameExpander = ".contentDocument";
String hierarchyFrameSelector = "document";
GuiElementData guiElementData = element.getData();
// Run through frame hierarchy
GuiElementData parentData = guiElementData.getParent();
while (parentData != null && parentData.isFrame()) {
hierarchyFrameSelector = pGetSimpleJsSelector(parentData, hierarchyFrameSelector) + jsFrameExpander;
parentData = parentData.getParent();
}
// get js selector
final String fullSelector = pGetSimpleJsSelector(guiElementData, hierarchyFrameSelector);
return fullSelector;
}
/**
* Gets the selector without frame hierarchy
*
* @param guiElementData GuiElement
* @param documentSelector String Current Selector
* @return String
*/
private static String pGetSimpleJsSelector(GuiElementData guiElementData, final String documentSelector) {
final String jsById = documentSelector + ".getElementById(\"###\")";
final String jsByClassName = documentSelector + ".getElementsByClassName(\"###\")[0]";
final String jsByTagName = documentSelector + ".getElementsByTagName(\"###\")[0]";
//
final String jsByCssSelector = documentSelector + ".querySelector(\"###\")";
final String jsByName = documentSelector + ".querySelector(*[name='###'])";
final String jsByXpath = documentSelector + ".evaluate(\"###\", " + documentSelector
+ ", null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue";
// TODO [erku] that is xpath ..maybe we can create some TesterraJS-methods
final String jsByLinkText = documentSelector + ".evaluate(\".//a[text()='###']\", " + documentSelector
+ ", null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue";
final String jsByPartialLinkText = documentSelector + ".evaluate(\".//a[contains(text(), '###')]\", "
+ documentSelector + ", null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue";
// By-Class does not allow to access the selector field
// ... use .toString() and extract selector.
final By by = guiElementData.getLocate().getBy();
final String beautifiedSelector = beautifySelectorString(by);
String jsSelector = "";
if (by instanceof By.ById) {
jsSelector = jsById;
} else if (by instanceof By.ByCssSelector) {
jsSelector = jsByCssSelector;
} else if (by instanceof By.ByXPath) {
jsSelector = jsByXpath;
} else if (by instanceof By.ByClassName) {
jsSelector = jsByClassName;
} else if (by instanceof By.ByLinkText) {
jsSelector = jsByLinkText;
} else if (by instanceof By.ByPartialLinkText) {
jsSelector = jsByPartialLinkText;
} else if (by instanceof By.ByTagName) {
jsSelector = jsByTagName;
} else if (by instanceof By.ByName) {
jsSelector = jsByName;
}
return jsSelector.replace("###", beautifiedSelector);
}
/**
* Replaces unnecessary parts of String
*
* @param selector By Selector
* @return String
*/
private static String beautifySelectorString(By selector) {
return selector.toString().replaceFirst("By.*:", "").trim();
}
/**
* @deprecated Use by deprecated {@link Layout}
*/
@Deprecated
public static Map getElementInnerBorders(UiElement guiElement) {
String cmd = "el = arguments[0];" +
"bl = window.getComputedStyle(el, null).getPropertyValue('border-left-width');" +
"br = window.getComputedStyle(el, null).getPropertyValue('border-right-width');" +
"bt = window.getComputedStyle(el, null).getPropertyValue('border-top-width');" +
"bb = window.getComputedStyle(el, null).getPropertyValue('border-bottom-width');" +
"" +
"pl = window.getComputedStyle(el, null).getPropertyValue('padding-left');" +
"pr = window.getComputedStyle(el, null).getPropertyValue('padding-right');" +
"pt = window.getComputedStyle(el, null).getPropertyValue('padding-top');" +
"pb = window.getComputedStyle(el, null).getPropertyValue('padding-bottom');" +
"" +
"bl = bl.replace('px','');" +
"br = br.replace('px','');" +
"bt = bt.replace('px','');" +
"bb = bb.replace('px','');" +
"" +
"pl = pl.replace('px','');" +
"pr = pr.replace('px','');" +
"pt = pt.replace('px','');" +
"pb = pb.replace('px','');" +
"" +
"l = el.getBoundingClientRect().x;" +
"w = el.getBoundingClientRect().width;" +
"t = el.getBoundingClientRect().y;" +
"h = el.getBoundingClientRect().height;" +
"" +
"il = parseInt(l);" +
"iw = parseInt(w);" +
"it = parseInt(t);" +
"ih = parseInt(h);" +
"" +
"ibl = parseInt(bl);" +
"ibr = parseInt(br);" +
"ibt = parseInt(bt);" +
"ibb = parseInt(bb);" +
"" +
"ipl = parseInt(pl);" +
"ipr = parseInt(pr);" +
"ipt = parseInt(pt);" +
"ipb = parseInt(pb);" +
"" +
"x = il + ipl + ibl;" +
"xx = il + iw - ibr - ipr;" +
"y = it + ipt + ibt;" +
"yy = it + ih - ibb - ipb;" +
"" +
"return {left:x, right:xx, top:y, bottom:yy};";
WebDriver driver = guiElement.getWebDriver();
Map out = new ConcurrentHashMap<>();
guiElement.findWebElement(webElement -> {
try {
Object o = executeScript(driver, cmd, webElement);
if (o != null && o instanceof Map) {
Map map = (Map) o;
for (Object key : map.keySet()) {
Object value = map.get(key);
if (key instanceof String && value instanceof Long) {
out.put((String) key, (Long) value);
}
}
out.keySet().forEach(key -> LOGGER.info(key + "=" + out.get(key)));
if (out.keySet().size() != 4) {
throw new RuntimeException("Could not get element border via JS call");
}
}
} catch (Exception e) {
LOGGER.error("Could not determine element inner left position", e);
}
});
return out;
}
/**
* Will get the viewport by executing JavaScript to determine innerwith and offsets
*
* @param driver {@link WebDriver}
* @return Rectangle
*/
public Rectangle getViewport(WebDriver driver) {
Object result = JSUtils.executeScript(driver, "return [window.pageXOffset.toString(), window.pageYOffset.toString(), window.innerWidth.toString(), window.innerHeight.toString()];");
if (result != null) {
final ArrayList list = (ArrayList)result;
List numbers = list.stream().map(Double::valueOf).collect(Collectors.toList());
return new Rectangle(numbers.get(0).intValue(), numbers.get(1).intValue(), numbers.get(3).intValue(), numbers.get(2).intValue());
} else {
return new Rectangle(-1,-1,-1,-1);
}
}
private static int getJSValueAsInt(Object in) {
if (in instanceof Double) {
return (int) (double) (Double) in;
}
if (in instanceof Long) {
return (int) (long) (Long) in;
}
LOGGER.error("Cannot cast JS value into int: " + in);
if (in == null) {
return 0;
}
return 0;
}
enum Where {
CENTER,
TOP_LEFT
}
public static Point getElementLocationInParent(UiElement guiElement, Where where) {
AtomicReference atomicPoint = new AtomicReference<>();
guiElement.findWebElement(webElement -> {
Object o = executeScript(guiElement.getWebDriver(), "return arguments[0].getBoundingClientRect();", webElement);
if (o == null) {
throw new SystemException("Could not get information about web element, please see the logs");
}
Map m = (Map) o;
int left = getJSValueAsInt(m.get("left"));
int width = getJSValueAsInt(m.get("width"));
int height = getJSValueAsInt(m.get("height"));
int top = getJSValueAsInt(m.get("top"));
switch (where) {
case CENTER:
atomicPoint.set(new Point(left + width / 2, top + height / 2));
break;
case TOP_LEFT:
atomicPoint.set(new Point(left, top));
break;
default:
throw new NotYetImplementedException("" + where);
}
});
return atomicPoint.get();
}
/**
* Scrolls the element to the center of the viewport
*/
public void scrollToCenter(WebDriver webDriver, WebElement webElement, Point offset) {
JSUtils.executeScript(
webDriver,
String.format("const elementRect = arguments[0].getBoundingClientRect();\n" +
"const absoluteElementTop = elementRect.top + window.pageYOffset;\n" +
"const absoluteElementLeft = elementRect.left + window.pageXOffset;\n" +
"const middle = absoluteElementTop - (window.innerHeight / 2);\n" +
"const center = absoluteElementLeft - (window.innerWidth / 2);\n" +
"window.scrollTo(center+%d, middle+%d);", offset.x, offset.y),
webElement
);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy