Last active
May 1, 2018 09:42
-
-
Save gitgrimbo/9e13b1172f2ba6f784ddcd23f5f1c6dd to your computer and use it in GitHub Desktop.
BMP/Selenium - Performance Timing Collection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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