-
-
Save klepikov/5457750 to your computer and use it in GitHub Desktop.
import java.io.IOException; | |
import java.net.URL; | |
import java.util.Date; | |
import java.util.List; | |
import java.util.logging.Level; | |
import org.json.*; | |
import org.openqa.selenium.*; | |
import org.openqa.selenium.chrome.*; | |
import org.openqa.selenium.logging.*; | |
import org.openqa.selenium.remote.*; | |
public class Test { | |
private static final String WEBDRIVER_SERVER_URL = "http://localhost:9515/"; | |
private static String androidPackage = null; // Assigned in main() | |
private WebDriver driver; | |
public void testGoogleSearch() throws Exception { | |
driver.get("http://www.google.com/news"); | |
WebElement element = driver.findElement(By.name("q")); | |
element.sendKeys("Selenium Conference 2013"); | |
element.submit(); | |
driver.findElement(By.linkText("Web")).click(); | |
} | |
public void setUp() throws Exception { | |
DesiredCapabilities caps = DesiredCapabilities.chrome(); | |
if (null != androidPackage) { | |
ChromeOptions chromeOptions = new ChromeOptions(); | |
chromeOptions.setExperimentalOptions("androidPackage", androidPackage); | |
caps.setCapability(ChromeOptions.CAPABILITY, chromeOptions); | |
} | |
LoggingPreferences logPrefs = new LoggingPreferences(); | |
logPrefs.enable(LogType.BROWSER, Level.ALL); | |
logPrefs.enable(LogType.PERFORMANCE, Level.INFO); | |
caps.setCapability(CapabilityType.LOGGING_PREFS, logPrefs); | |
driver = new Augmenter().augment(new RemoteWebDriver(new URL(WEBDRIVER_SERVER_URL), caps)); | |
Capabilities actualCaps = ((HasCapabilities) driver).getCapabilities(); | |
System.out.println("Actual caps: " + actualCaps); | |
} | |
public void tearDown() throws Exception { | |
try { | |
Logs logs = driver.manage().logs(); | |
System.out.println("Log types: " + logs.getAvailableLogTypes()); | |
printLog(LogType.BROWSER); | |
submitPerformanceResult("Test.testGoogleSearch", logs.get(LogType.PERFORMANCE).getAll()); | |
} finally { | |
driver.quit(); | |
} | |
} | |
void printLog(String type) { | |
List<LogEntry> entries = driver.manage().logs().get(type).getAll(); | |
System.out.println(entries.size() + " " + type + " log entries found"); | |
for (LogEntry entry : entries) { | |
System.out.println( | |
new Date(entry.getTimestamp()) + " " + entry.getLevel() + " " + entry.getMessage()); | |
} | |
} | |
void submitPerformanceResult(String name, List<LogEntry> perfLogEntries) | |
throws IOException, JSONException { | |
JSONArray devToolsLog = new JSONArray(); | |
System.out.println(perfLogEntries.size() + " performance log entries found"); | |
for (LogEntry entry : perfLogEntries) { | |
JSONObject message = new JSONObject(entry.getMessage()); | |
JSONObject devToolsMessage = message.getJSONObject("message"); | |
// System.out.println( | |
// devToolsMessage.getString("method") + " " + message.getString("webview")); | |
devToolsLog.put(devToolsMessage); | |
} | |
byte[] screenshot = null; | |
if (null == androidPackage) { // Chrome on Android does not yet support screenshots | |
screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES); | |
} | |
String resultUrl = new WebPageTest(new URL("http://localhost:8888/"), "Test", name) | |
.submitResult(devToolsLog, screenshot); | |
System.out.println("Result page: " + resultUrl); | |
} | |
public static void main(String[] argv) throws Exception { | |
if (argv.length > 0 && "--android".equals(argv[0])) { | |
// androidPackage = "com.google.android.apps.chrome_dev"; | |
androidPackage = "com.google.android.apps.chrome"; | |
} | |
Test test = new Test(); | |
test.setUp(); | |
try { | |
test.testGoogleSearch(); | |
} finally { | |
test.tearDown(); | |
} | |
} | |
} |
# This does not include WebPageTest submission, only an illustration | |
# how to use the Logging API from the WebDriver Python bindings. | |
from selenium import webdriver | |
from selenium.webdriver.common import keys | |
driver = webdriver.Chrome( | |
executable_path="chromedriver2_server", | |
desired_capabilities={'loggingPrefs': {'profiler': 'INFO'}}) | |
# Hack the Logging API into the Python remote driver. | |
# Not implemented in Selenium, patch welcome!! | |
driver.command_executor._commands.update({ | |
'getAvailableLogTypes': ('GET', '/session/$sessionId/log/types'), | |
'getLog': ('POST', '/session/$sessionId/log')}) | |
try: | |
print 'Available log types:', driver.execute('getAvailableLogTypes')['value'] | |
driver.get('http://news.google.com') | |
elem = driver.find_element_by_name('q') # Find the query box | |
elem.send_keys('GTAC 2013' + keys.Keys.RETURN) | |
elem = driver.find_element_by_link_text('Web') | |
elem.click() | |
print 'Profiler log:', driver.execute('getLog', {'type': 'performance'})['value'] | |
finally: | |
driver.quit() |
import java.io.*; | |
import java.net.*; | |
import java.util.zip.*; | |
import org.json.*; | |
public class WebPageTest { | |
private final String location; | |
private final URL createTestUrl; | |
private final URL workDoneUrl; | |
private final String mimeBoundary = "-----CorrectBatteryHorseStaple"; | |
public WebPageTest(URL baseUrl, String location, String testUrl) throws IOException { | |
this.location = location; | |
this.createTestUrl = new URL(baseUrl, | |
"runtest.php?location=" + location + "&url=" + URLEncoder.encode(testUrl, "UTF-8") + | |
"&fvonly=1&f=json"); | |
this.workDoneUrl = new URL(baseUrl, "work/workdone.php"); | |
} | |
public String submitResult(JSONArray devToolsLog, byte[] screenshot) throws IOException, JSONException { | |
JSONObject testDescriptor = createTest(); | |
postResult(testDescriptor, devToolsLog, screenshot); | |
return testDescriptor.getJSONObject("data").getString("userUrl"); | |
} | |
private void writeResultsZip(OutputStream os, JSONArray devToolsLog, byte[] screenshot) | |
throws IOException, JSONException { | |
ZipOutputStream zos = new ZipOutputStream(os); | |
if (null != devToolsLog) { | |
zos.putNextEntry(new ZipEntry("1_devtools.json")); | |
OutputStreamWriter writer = new OutputStreamWriter(zos); | |
devToolsLog.write(writer); | |
writer.flush(); | |
zos.closeEntry(); | |
} | |
if (null != screenshot) { | |
zos.putNextEntry(new ZipEntry("1_screen.png")); | |
zos.write(screenshot); | |
zos.closeEntry(); | |
} | |
zos.finish(); | |
} | |
private JSONObject createTest() throws IOException, JSONException { | |
HttpURLConnection http = (HttpURLConnection) createTestUrl.openConnection(); | |
if (HttpURLConnection.HTTP_OK != http.getResponseCode()) { | |
throw new IOException("WebPateTest test creation failed for location " + location + ": " + | |
http.getResponseCode() + " " +http.getResponseMessage()); | |
} | |
Reader reader = new InputStreamReader(http.getInputStream(), "UTF-8"); | |
try { | |
return new JSONObject(new JSONTokener(reader)); | |
} finally { | |
reader.close(); | |
} | |
} | |
private void postResult(JSONObject testDescriptor, JSONArray devToolsLog, byte[] screenshot) | |
throws IOException, JSONException { | |
HttpURLConnection http = (HttpURLConnection) workDoneUrl.openConnection(); | |
http.setDoOutput(true); | |
http.addRequestProperty("Content-Type", "multipart/form-data; boundary=" + | |
mimeBoundary); | |
http.setChunkedStreamingMode(4096); | |
writeMultipartContent(http.getOutputStream(), testDescriptor, devToolsLog, screenshot); | |
http.getInputStream().close(); | |
if (HttpURLConnection.HTTP_OK != http.getResponseCode()) { | |
throw new IOException("Result submission failed for " + | |
testDescriptor.getJSONObject("data").getString("userUrl") + " : " + | |
http.getResponseCode() + " " +http.getResponseMessage()); | |
} | |
} | |
private void writeMultipartContent( | |
OutputStream os, JSONObject testDescriptor, JSONArray devToolsLog, byte[] screenshot) | |
throws IOException, JSONException { | |
String testId = testDescriptor.getJSONObject("data").getString("testId"); | |
OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8"); | |
startMimePart(writer, "location", null); | |
writer.write("\r\n" + location + "\r\n"); | |
startMimePart(writer, "id", null); | |
writer.write("\r\n" + testId + "\r\n"); | |
startMimePart(writer, "done", null); | |
writer.write("\r\n1\r\n"); | |
startMimePart(writer, "file", "1_results.zip"); | |
writer.write("Content-Type: application/zip\r\n"); | |
writer.write("Content-Transfer-Encoding: binary\r\n\r\n"); | |
writer.flush(); | |
writeResultsZip(os, devToolsLog, screenshot); | |
writer.write("\r\n--"); | |
writer.write(mimeBoundary); | |
writer.write("--\r\n"); | |
writer.flush(); | |
} | |
private void startMimePart(Writer writer, String name, String filename) throws IOException { | |
writer.write("--"); | |
writer.write(mimeBoundary); | |
writer.write("\r\n"); | |
writer.write("Content-Disposition: form-data; name=\""); | |
writer.write(name); | |
if (null != filename) { | |
writer.write("\"; filename=\""); | |
writer.write(filename); | |
} | |
writer.write("\"\r\n"); | |
} | |
} |
OK, I debugged the problem. The exception in this case is misleading and is happening because WebPageTest
doesn't detect the case where server connection is OK, but test creation returns failure in JSON. I have a patch: replace line #53 (return new JSONObject(new JSONTokener(reader));
) with the following:
JSONObject result = new JSONObject(new JSONTokener(reader));
if (!result.has("data")) {
throw new IOException("WebPageTest test creation failed for location " + location + ": " + result);
}
return result;
Now you get much nicer error messages. For example:
java.io.IOException: WebPageTest test creation failed for location Test: {"statusCode":400,"statusTe
xt":"Error submitting url for testing"}
But there is still a problem, which puzzles me. "Test" location is recognized (otherwise the error message is telling about an invalid location), but is there something wrong with it? What am I missing?
On a side note, does anybody have experience in presenting this performance data in any tool other than WebpageTest? The talk mentioned other tools, but no specific examples were provided.
Web page test gives you an option to export the tool as a har (HTTP Archive) file . You can then use http://www.softwareishard.com/har/viewer/ to view the har file
Update 6/3/2014 (Issue Fixed): Issue appears to have resolved itself after moving to a more recent version of the WPT www. Now after submitting multiple test run results to WPT server, the results page is updated immediately and no longer waiting for tests to complete.
We got the code working on our private instance. Thanks a bunch Michael. One issue we are hitting is the following:
After submitting a result and viewing the result page e.g. http:///results.php?test=140603_66_G , it always shows the following until a timeout is hit after 5+ minutes,
Test is partially complete (1 of 1 tests).
This page will refresh as tests complete.
After the timeout is reached and the page refreshes we are able to see the summarized results with the table showing Load Time, First Byte etc. for the median run at the top of the page. My question, is there a way to notify the server that we are done submitting results and to go ahead completing the test run and compile the summary results?
Looking at the current code for workdone.php it looks like the following snippet might be what needs to be done but I’m not 100% sure. If anyone could offer any guidance it would be much appreciated
// do any post-processing when the full test is complete that doesn't rely on testinfo
if ($done) {
logTestMsg($id, "Test Complete");
// send an async request to the post-processing code so we don't block
SendAsyncRequest("/work/postprocess.php?test=$id");
}
How to convert the logs to HAR format? Is there any API java? I know that exists in php on the site WebPageTest, but i would like in java.
Thanks!
Is there any code in java that converts this log to HAR format?
How do these Network.responseReceived timing values relate to the timings you would see in Chrome Developer Tools or a WPT waterfall. e.g. TTFB, total request time, etc?
"timing":{
"connectEnd":10.477000032551587,"connectStart":9.941000025719404,
"dnsEnd":9.941000025719404,"dnsStart":9.901000070385635,
"proxyEnd":-1,"proxyStart":-1,
"receiveHeadersEnd":20.198000012896955,
"requestTime":1403001765.7676198,
"sendEnd":11.696000001393259,"sendStart":11.64400007110089,
"sslEnd":-1,"sslStart":-1
}
Do I need to calculate them based on the original Timeline.eventRecorded or Network.requestWillBeSent events for the matching GET request?
Never mind, this answers my question:
https://github.com/WPO-Foundation/webpagetest/blob/bbbe1d5d7031e54787704c14b8c764a8c7f51512/agent/browser/chrome/extension/wpt/chromeDebugger.js
I am using the code in Test.java but when running this code, i am getting an exception:
org.openqa.selenium.WebDriverException: unknown error: log type 'performance' not found
Hi Anky1987,
Did you find any solution. I too am getting the same error?
Thanks
Puneesh
@klepikov is it possible to add the capability to an existing webdriver in the middle of a test? I'm trying to figure out how to log a specific page load, and not the whole workflow. (eg. we have to login to access our app, and i don't want to time the login, and landing page load).
The upload to a WebPagetest instance is somewhat easier now. You can do a post to /import.php and it will return a test ID. There is also a UI on /import.php for testing it out (post fields are the same as the form fields in the UI).
Issue "org.openqa.selenium.WebDriverException: unknown error: log type 'performance' not found" has been fixed refer this thread
https://code.google.com/p/selenium/issues/detail?id=8457#c10
Thanks for the post.. very valuable. I am getting one issue with the above code.
"unknown error: unhandled inspector error: {"code":-32601,"message":"'Timeline.start' wasn't found"}".
Any idea? thanks in advance.
@klepikov Could you please be so kind and provide demo example of submitting performance logs to WebPageTest in Python?
@klepikov: I run your code. Performance record type appears to only include network activity. I am more interested in client CPU activity. I tried to change code by adding adding/enabling timeline but still got only Network activity records.
Could you please suggest how to retrieve client CPU activity.
Map<String, Object> perfLogPrefs = new HashMap<String, Object>();
perfLogPrefs.put("traceCategories", "browser,devtools.timeline,devtools"); // comma-separated trace categories
perfLogPrefs.put("enableTimeline", true);
@klepikov: Could you please be so kind and provide demo example of submitting performance logs to WebPageTest in Python?
Could you please provide the demo example of submitting performance logs to WebPageTest using C# with Selenium?
Also is it possible to download the HAR file from WebPageTest using Logging API(REST call) with C#?
am trying to extract the browser console logs for my tests which are running on standalone-chrome-debug container on different server. I do not get the logs when in run on docker container.
However when i run the same test on my local machine with chrome driver, the logs are getting extracted as expected. Does any one has idea if we can extract the browser console logs for the test running on docker container.
Following way i am trying to achieve this.
logPrefs.enable(LogType.BROWSER, Level.ALL);
public LogEntries getBrowserConsoleLogs() {
return driver.manage().logs().get(LogType.BROWSER);
}```
Can someone help me understand in the WebPageTest what is the baseURL? and the location? because i get the below error while running
Exception in thread "main" org.json.JSONException: A JSONObject text must begin with '{' at 1 [character 2 line 1]
at org.json.JSONTokener.syntaxError(JSONTokener.java:507)
at org.json.JSONObject.(JSONObject.java:225)
at video.ui.automation.stepDefinition.WebPageTest.createTest(WebPageTest.java:57)
at video.ui.automation.stepDefinition.WebPageTest.submitResult(WebPageTest.java:24)
Thanks a lot for posting the code! This looks very promising for our needs of capturing performance baseline in feature tests.
I'm trying to make it work with a private instance of WebPagetest 2.13. I've setup the instance on a built-in Apache HTTP Server in OS X Mavericks and copied
locations.ini.sample
tolocations.ini
. The server renders the home page at http://localhost/index.php and I can see "WebPagetest.org - Dulles, VA" location.When I try to submit performance logs to the server I'm getting:
I'll try to troubleshoot, but do you have any ideas about the cause in the meantime?
Just a side note, on a Mac, I had to add
Options FollowSymLinks
in Apache config under<Directory "/[skipped]/webpagetest">
because I was getting the following error in Apache logs on all URLs underwebpagetest/
:[error] [client ::1] Options FollowSymLinks or SymLinksIfOwnerMatch is off which implies that RewriteRule directive is forbidden