Skip to content

Instantly share code, notes, and snippets.

@gitgrimbo
Last active May 1, 2018 09:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gitgrimbo/9e13b1172f2ba6f784ddcd23f5f1c6dd to your computer and use it in GitHub Desktop.
Save gitgrimbo/9e13b1172f2ba6f784ddcd23f5f1c6dd to your computer and use it in GitHub Desktop.
BMP/Selenium - Performance Timing Collection
package grimbo.bmp;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.util.log.Log;
import org.openqa.selenium.WebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.lightbody.bmp.core.har.Har;
import net.lightbody.bmp.core.har.HarPage;
import net.lightbody.bmp.core.har.HarPageTimings;
public class PerformanceTimingCollector {
private static final Logger log = LoggerFactory.getLogger(PerformanceTimingCollector.class);
private WebDriver driver;
/**
* pageRef mapped to performance timing JSON string.
*/
private Map<String, String> performanceTimingDataMap = new HashMap<>();
public PerformanceTimingCollector(WebDriver driver) {
this.driver = driver;
}
public void writeHarTo(Har har, File file) throws IOException {
writeHarTo(har, new FileWriter(file));
}
public void writeHarTo(Har har, OutputStream stream) throws IOException {
writeHarTo(har, new OutputStreamWriter(stream, StandardCharsets.UTF_8));
}
public void writeHarTo(Har har, Writer writer) throws IOException {
try {
String harStr = addPerformanceTimingDataToHar(har);
writer.write(harStr);
} catch (ScriptException e) {
throw new RuntimeException(e);
} finally {
IOUtils.closeQuietly(writer);
}
}
/**
* Gets the performance timing data for the current page (via WebDriver) and stores this in-memory against the
* HarPage's id. Also sets the HarPage's PageTiming's onLoad and onContentLoad times based on the performance
* timings.
*
* @param page
*/
public void endPageWithPerformanceTiming(HarPage page) {
try {
String performanceTimingStr = WebDriverUtils.getWindowPerformanceTiming(driver);
String pageId = page.getId();
performanceTimingDataMap.put(pageId, performanceTimingStr);
HarPageTimings pageTimings = getHarTimingsFromPerformanceTimingStr(performanceTimingStr);
// https://www.w3.org/TR/navigation-timing/#processing-model
page.getPageTimings().setOnLoad(pageTimings.getOnLoad());
// https://www.w3.org/TR/navigation-timing/#dom-performancetiming-domcomplete
page.getPageTimings().setOnContentLoad(pageTimings.getOnContentLoad());
} catch (ScriptException e) {
Log.warn("Could not retrieve performanceTiming data from driver.");
}
}
private String addPerformanceTimingDataToHar(Har har) throws IOException, ScriptException {
StringWriter sw = new StringWriter();
har.writeTo(sw);
String harStr = sw.toString();
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
engine.put("harStr", harStr);
engine.put("performanceTimingDataMap", performanceTimingDataMap);
//@formatter:off
engine.eval(js(new String[] {
"har = JSON.parse(harStr);",
"har.log.pages.forEach(function(page) {",
" var data = performanceTimingDataMap[page.id];",
" if (data) page.performanceTiming = JSON.parse(data);",
"});"
}));
//@formatter:on
harStr = (String) engine.eval("JSON.stringify(har)");
return harStr;
}
private HarPageTimings getHarTimingsFromPerformanceTimingStr(String performanceTimingStr) throws ScriptException {
// Using fetchStart rather than navigationStart as the 'start' point,
// relative to which the other timings are taken
String onLoadScript = "performanceTiming.loadEventEnd - performanceTiming.fetchStart";
String onContentLoadScript = "performanceTiming.domComplete- performanceTiming.fetchStart";
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
engine.put("performanceTimingStr", performanceTimingStr);
engine.eval("performanceTiming = JSON.parse(performanceTimingStr)");
long onLoad = new BigDecimal(engine.eval(onLoadScript).toString()).longValue();
long onContentLoad = new BigDecimal(engine.eval(onContentLoadScript).toString()).longValue();
return new HarPageTimings(onContentLoad, onLoad);
}
private String js(String[] lines) {
return String.join("\n", lines);
}
}
package grimbo.bmp;
import java.util.function.Function;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
public class WebDriverUtils {
public static <T extends WebDriver, V> V waitUntil(WebDriver driver, long timeOutInSeconds,
Function<? super WebDriver, V> isTrue) {
return new WebDriverWait(driver, timeOutInSeconds).until(isTrue);
}
public static boolean waitUntilInvisible(WebDriver driver, long timeOutInSeconds, By locator) {
return new WebDriverWait(driver, timeOutInSeconds)
.until(ExpectedConditions.invisibilityOfElementLocated(locator));
}
public static String getWindowPerformanceTiming(WebDriver driver) {
JavascriptExecutor executor = (JavascriptExecutor) driver;
//@formatter:off
String[] lines = { //
"var performance = window.performance || window.webkitPerformance || window.mozPerformance || window.msPerformance || {};",
"var timing = performance.timing || {};",
"return JSON.stringify(timing);"
};
//@formatter:on
return (String) executor.executeScript(String.join("", lines));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment