
com.applitools.eyes.selenium.capture.DomCapture Maven / Gradle / Ivy
package com.applitools.eyes.selenium.capture;
import com.applitools.connectivity.ServerConnector;
import com.applitools.eyes.*;
import com.applitools.eyes.logging.Stage;
import com.applitools.eyes.logging.Type;
import com.applitools.eyes.positioning.PositionMemento;
import com.applitools.eyes.positioning.PositionProvider;
import com.applitools.eyes.selenium.EyesSeleniumUtils;
import com.applitools.eyes.selenium.SeleniumEyes;
import com.applitools.eyes.selenium.frames.FrameChain;
import com.applitools.eyes.selenium.rendering.VisualGridEyes;
import com.applitools.eyes.selenium.wrappers.EyesSeleniumDriver;
import com.applitools.eyes.selenium.wrappers.EyesTargetLocator;
import com.applitools.eyes.visualgrid.model.RGridResource;
import com.applitools.utils.EfficientStringReplace;
import com.applitools.utils.GeneralUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.tuple.Pair;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import java.io.IOException;
import java.net.URI;
import java.util.*;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class DomCapture {
private final String CAPTURE_DOM;
private final String CAPTURE_DOM_FOR_IE;
private final String POLL_RESULT;
private final String POLL_RESULT_FOR_IE;
private final Phaser cssPhaser = new Phaser(); // Phaser for syncing all callbacks on a single Frame
private static ServerConnector serverConnector = null;
private final String testId;
private final EyesSeleniumDriver driver;
private final Logger logger;
String cssStartToken;
String cssEndToken;
final Map cssNodesToReplace = Collections.synchronizedMap(new HashMap());
private boolean shouldWaitForPhaser = false;
private final UserAgent userAgent;
public DomCapture(SeleniumEyes eyes) {
serverConnector = eyes.getServerConnector();
logger = eyes.getLogger();
driver = (EyesSeleniumDriver) eyes.getDriver();
userAgent = eyes.getUserAgent();
testId = eyes.getTestId();
try {
CAPTURE_DOM = GeneralUtils.readToEnd(DomCapture.class.getResourceAsStream("/dom-capture/dist/captureDomAndPoll.js"));
CAPTURE_DOM_FOR_IE = GeneralUtils.readToEnd(DomCapture.class.getResourceAsStream("/dom-capture/dist/captureDomAndPollForIE.js"));
POLL_RESULT = GeneralUtils.readToEnd(VisualGridEyes.class.getResourceAsStream("/dom-capture/dist/pollResult.js"));
POLL_RESULT_FOR_IE = GeneralUtils.readToEnd(VisualGridEyes.class.getResourceAsStream("/dom-capture/dist/pollResultForIE.js"));
} catch (IOException e) {
throw new EyesException("Failed getting resources for dom scripts", e);
}
}
public String getPageDom(PositionProvider positionProvider) {
PositionMemento originalPosition = positionProvider.getState();
positionProvider.setPosition(Location.ZERO);
FrameChain originalFC = driver.getFrameChain().clone();
EyesTargetLocator switchTo = (EyesTargetLocator) driver.switchTo();
switchTo.defaultContent();
String baseUrl = (String) driver.executeScript("return document.location.href");
String dom = getFrameDom(baseUrl, Collections.singletonList(baseUrl));
if (originalFC != null) {
switchTo.frames(originalFC);
}
try {
if (shouldWaitForPhaser) {
cssPhaser.awaitAdvanceInterruptibly(0, 5, TimeUnit.MINUTES);
}
} catch (InterruptedException | TimeoutException e) {
GeneralUtils.logExceptionStackTrace(logger, Stage.CHECK, Type.DOM_SCRIPT, e, testId);
}
shouldWaitForPhaser = false;
Map cssStringsToReplace = new HashMap<>();
for (String url : cssNodesToReplace.keySet()) {
try {
String escapedCss = new ObjectMapper().writeValueAsString(cssNodesToReplace.get(url).toString());
if (escapedCss.startsWith("\"") && escapedCss.endsWith("\"")) {
escapedCss = escapedCss.substring(1, escapedCss.length() - 1); // remove quotes
}
cssStringsToReplace.put(url, escapedCss);
} catch (JsonProcessingException e) {
GeneralUtils.logExceptionStackTrace(logger, Stage.CHECK, Type.DOM_SCRIPT, e);
}
}
String domJson = EfficientStringReplace.efficientStringReplace(cssStartToken, cssEndToken, dom, cssStringsToReplace);
positionProvider.restoreState(originalPosition);
return domJson;
}
public String getFrameDom(String baseUrl, List framesPath) {
String domScript = userAgent.isInternetExplorer() ? CAPTURE_DOM_FOR_IE : CAPTURE_DOM;
String pollingScript = userAgent.isInternetExplorer() ? POLL_RESULT_FOR_IE : POLL_RESULT;
List missingCssList = new ArrayList<>();
List missingFramesList = new ArrayList<>();
List data = new ArrayList<>();
Separators separators;
try {
String scriptResult = EyesSeleniumUtils.runDomScript(logger, driver, userAgent, Collections.singleton(testId),
domScript, null, pollingScript);
scriptResult = GeneralUtils.parseJsonToObject(scriptResult, String.class);
separators = parseScriptResult(scriptResult, missingCssList, missingFramesList, data);
} catch (Exception e) {
throw new EyesException("Failed running dom capture script", e);
}
cssStartToken = separators.cssStartToken;
cssEndToken = separators.cssEndToken;
fetchCssFiles(baseUrl, missingCssList, null);
Map framesData = new HashMap<>();
try {
framesData = recurseFrames(missingFramesList, framesPath);
} catch (Exception e) {
GeneralUtils.logExceptionStackTrace(logger, Stage.CHECK, Type.DOM_SCRIPT, e, testId);
}
return EfficientStringReplace.efficientStringReplace(separators.iframeStartToken, separators.iframeEndToken, data.get(0), framesData);
}
private Separators parseScriptResult(String scriptResult, List missingCssList, List missingFramesList, List data) {
String[] lines = scriptResult.split("\\r?\\n");
Separators separators = null;
try {
separators = GeneralUtils.parseJsonToObject(lines[0], Separators.class);
ArrayList> blocks = new ArrayList<>();
blocks.add(missingCssList);
blocks.add(missingFramesList);
blocks.add(data);
int blockIndex = 0;
int lineIndex = 1;
do {
String str = lines[lineIndex++];
if (separators.separator.equals(str)) {
blockIndex++;
} else {
blocks.get(blockIndex).add(str);
}
} while (lineIndex < lines.length);
logger.log(testId, Stage.CHECK, Type.DOM_SCRIPT,
Pair.of("missingCssCount", missingCssList.size()),
Pair.of("missingFramesCount", missingFramesList.size()));
} catch (IOException e) {
GeneralUtils.logExceptionStackTrace(logger, Stage.CHECK, Type.DOM_SCRIPT, e, testId);
}
return separators;
}
private void fetchCssFiles(final String baseUrl, List cssUrls, final CssTreeNode parentNode) {
for (final String cssUrl : cssUrls) {
if (cssUrl == null || cssUrl.isEmpty()) {
continue;
}
final URI uri = resolveUriString(baseUrl, cssUrl);
if (uri == null) {
continue;
}
try {
cssPhaser.register();
shouldWaitForPhaser = true;
serverConnector.downloadResource(uri, userAgent.toString(), baseUrl, new TaskListener() {
@Override
public void onComplete(RGridResource resource) {
try {
CssTreeNode node = new CssTreeNode(new String(resource.getContent()));
node.parse(logger);
List importedUrls = node.getImportedUrls();
if (!importedUrls.isEmpty()) {
fetchCssFiles(uri.toString(), importedUrls, node);
}
if (parentNode != null) {
parentNode.addChildNode(cssUrl, node);
} else {
cssNodesToReplace.put(cssUrl, node);
}
} catch (Throwable e) {
GeneralUtils.logExceptionStackTrace(logger, Stage.CHECK, Type.DOM_SCRIPT, e, testId);
} finally {
cssPhaser.arriveAndDeregister();
}
}
@Override
public void onFail() {
cssPhaser.arriveAndDeregister();
}
});
} catch (Throwable e) {
GeneralUtils.logExceptionStackTrace(logger, Stage.CHECK, Type.DOM_SCRIPT, e, testId);
}
}
}
public Map recurseFrames(List missingFramesList, List framesPath) {
Map framesData = new HashMap<>();
EyesTargetLocator switchTo = (EyesTargetLocator) driver.switchTo();
FrameChain fc = driver.getFrameChain().clone();
for (String missingFrameLine : missingFramesList) {
try {
String[] missingFrameXpaths = missingFrameLine.split(",");
for (String missingFrameXpath : missingFrameXpaths) {
WebElement frame = driver.findElement(By.xpath(missingFrameXpath));
switchTo.frame(frame);
}
String locationAfterSwitch = (String) driver.executeScript("return document.location.href");
if (framesPath.contains(locationAfterSwitch)) {
framesData.put(missingFrameLine, "");
continue;
}
List newFramePath = new ArrayList<>(framesPath);
newFramePath.add(locationAfterSwitch);
String result = getFrameDom(locationAfterSwitch, newFramePath);
framesData.put(missingFrameLine, result);
} catch (Exception e) {
GeneralUtils.logExceptionStackTrace(logger, Stage.CHECK, Type.DOM_SCRIPT, e, testId);
framesData.put(missingFrameLine, "");
} finally {
switchTo.frames(fc);
}
}
return framesData;
}
private URI resolveUriString(String baseUrl, String uri) {
if (uri.toLowerCase().startsWith("data:") || uri.toLowerCase().startsWith("javascript:")) {
return null;
}
try {
return new URI(baseUrl).resolve(uri);
} catch (Exception e) {
GeneralUtils.logExceptionStackTrace(logger, Stage.CHECK, Type.DOM_SCRIPT, e, testId);
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy