-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ability storing of webdriver console and network logs after failing tests #8926
Changes from 5 commits
0fa6a51
f8cba6f
b5f4e52
f34a6a7
a362d20
8c668f0
b2167c8
360b707
ac41500
5e1ab4f
0d00d06
b3fe3f4
85f4efc
9861ae2
b1eab7c
97d9bca
3db9bf2
ce0cf6b
cec456d
423417f
44b7466
a2a2be2
ae5ede8
be9f972
d33badc
c8930f2
66c3ef5
925fbf7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,6 +50,7 @@ | |
import org.eclipse.che.selenium.core.pageobject.PageObjectsInjector; | ||
import org.eclipse.che.selenium.core.user.InjectTestUser; | ||
import org.eclipse.che.selenium.core.user.TestUser; | ||
import org.eclipse.che.selenium.core.utils.BrowserLogsUtil; | ||
import org.eclipse.che.selenium.core.workspace.InjectTestWorkspace; | ||
import org.eclipse.che.selenium.core.workspace.TestWorkspace; | ||
import org.eclipse.che.selenium.core.workspace.TestWorkspaceLogsReader; | ||
|
@@ -93,6 +94,10 @@ public abstract class SeleniumTestHandler | |
@Named("tests.htmldumps_dir") | ||
private String htmldumpsDir; | ||
|
||
@Inject | ||
@Named("tests.webDriverLogsDir") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use the same test properties naming pattern, please: |
||
private String webDriverLogsDir; | ||
|
||
@Inject | ||
@Named("tests.workspacelogs_dir") | ||
private String workspaceLogsDir; | ||
|
@@ -302,6 +307,7 @@ private void onTestFinish(ITestResult result) { | |
captureScreenshot(result); | ||
captureHtmlSource(result); | ||
captureTestWorkspaceLogs(result); | ||
storeWebDriverLogs(result); | ||
} | ||
} | ||
|
||
|
@@ -464,6 +470,28 @@ private void captureScreenshotsFromOpenedWindows( | |
}); | ||
} | ||
|
||
private void storeWebDriverLogs(ITestResult result) { | ||
Set<SeleniumWebDriver> webDrivers = new HashSet<>(); | ||
Object testInstance = result.getInstance(); | ||
collectInjectedWebDrivers(testInstance, webDrivers); | ||
webDrivers.forEach(webDriver -> getWebDriverLog(result, webDriver)); | ||
} | ||
|
||
private void getWebDriverLog(ITestResult result, SeleniumWebDriver webDriver) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
try { | ||
String testReference = getTestReference(result); | ||
String filename = NameGenerator.generate(testReference + "_", 4) + ".log"; | ||
Path webdriverLogDirectory = Paths.get(webDriverLogsDir, filename); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Files.createDirectories(webdriverLogDirectory.getParent()); | ||
Files.write( | ||
webdriverLogDirectory, | ||
BrowserLogsUtil.getCombinedLogs(webDriver).getBytes(Charset.forName("UTF-8")), | ||
StandardOpenOption.CREATE); | ||
} catch (WebDriverException | IOException e) { | ||
LOG.error(format("Can't get of the logs from WebDriver for test %s", result), e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} | ||
|
||
private void dumpHtmlCodeFromTheCurrentPage(ITestResult result, SeleniumWebDriver webDriver) { | ||
String testReference = getTestReference(result); | ||
String filename = NameGenerator.generate(testReference + "_", 8) + ".html"; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,12 +11,14 @@ | |
package org.eclipse.che.selenium.core.utils; | ||
|
||
import static org.openqa.selenium.logging.LogType.BROWSER; | ||
import static org.openqa.selenium.logging.LogType.PERFORMANCE; | ||
|
||
import com.google.inject.Inject; | ||
import com.google.inject.Singleton; | ||
import com.google.gson.JsonElement; | ||
import com.google.gson.JsonObject; | ||
import com.google.gson.JsonParser; | ||
import java.net.URL; | ||
import java.util.List; | ||
import org.eclipse.che.selenium.core.SeleniumWebDriver; | ||
import org.openqa.selenium.WebDriver; | ||
import org.openqa.selenium.logging.LogEntry; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
@@ -25,27 +27,115 @@ | |
* Read and store browser logs to the test logs. Log level and type are defined in {@link | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Javadocs doesn't take into account performance logs. And this class stores nothing. |
||
* org.eclipse.che.selenium.core.SeleniumWebDriver#doCreateDriver(URL)} | ||
*/ | ||
@Singleton | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did you remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because this class does not need use DI |
||
public class BrowserLogsUtil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Class name is outdated so as it is not about just BROWSER logs, but PERFORMANCE logs as well. So, lets make the name more general: |
||
private final SeleniumWebDriver seleniumWebDriver; | ||
private final Logger LOG = LoggerFactory.getLogger(BrowserLogsUtil.class); | ||
|
||
@Inject | ||
public BrowserLogsUtil(SeleniumWebDriver seleniumWebDriver) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are injecting |
||
this.seleniumWebDriver = seleniumWebDriver; | ||
} | ||
private static final Logger LOG = LoggerFactory.getLogger(BrowserLogsUtil.class); | ||
|
||
/** | ||
* read logs from browser console | ||
* | ||
* @return log messages from browser console | ||
*/ | ||
public List<LogEntry> getLogs() { | ||
public static List<LogEntry> getConsoleLogs(WebDriver seleniumWebDriver) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets name the method as |
||
return seleniumWebDriver.manage().logs().get(BROWSER).getAll(); | ||
} | ||
|
||
/** | ||
* get all logs from the active webdriver session | ||
* | ||
* @return all types of performance logs | ||
*/ | ||
public static List<LogEntry> getPerformanceLogs(WebDriver seleniumWebDriver) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets name the method as |
||
return seleniumWebDriver.manage().logs().get(PERFORMANCE).getAll(); | ||
} | ||
|
||
/** store browser logs to the test logs */ | ||
public void storeLogs() { | ||
getLogs().forEach(logEntry -> LOG.info("{} {}", logEntry.getLevel(), logEntry.getMessage())); | ||
public static void storeLogsToConsoleOutput(WebDriver seleniumWebDriver) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
getConsoleLogs(seleniumWebDriver) | ||
.forEach(logEntry -> LOG.info("{} {}", logEntry.getLevel(), logEntry.getMessage())); | ||
} | ||
|
||
/** | ||
* combine the Network and Browser logs | ||
* | ||
* @return logs from browser console and requests/responses on CHE api | ||
*/ | ||
public static String getCombinedLogs(WebDriver seleniumWebDriver) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method return NetworkTraffic + WebDriver java script console logs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understood it. But method name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or name it |
||
StringBuilder combinedLogs = | ||
new StringBuilder("Browser console logs:\n").append("---------------------\n"); | ||
getConsoleLogs(seleniumWebDriver) | ||
.forEach( | ||
logEntry -> | ||
combinedLogs | ||
.append(String.format("%s %s \n", logEntry.getLevel(), logEntry.getMessage())) | ||
.append("\n")); | ||
return combinedLogs.append(getNetworkDataSentOnCheApi(seleniumWebDriver)).toString(); | ||
} | ||
|
||
/** filter data and get requests/responses that has been sent on CHE /api/ URL */ | ||
public static String getNetworkDataSentOnCheApi(WebDriver seleniumWebDriver) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
StringBuilder data = new StringBuilder("Network logs: \n").append("---------------\n"); | ||
JsonParser jsonParser = new JsonParser(); | ||
getPerformanceLogs(seleniumWebDriver) | ||
.forEach( | ||
logEntry -> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest to use |
||
JsonElement jsonElement = jsonParser.parse(logEntry.getMessage()); | ||
JsonObject jsonMessageNode = | ||
jsonElement.getAsJsonObject().get("message").getAsJsonObject(); | ||
String networkValue = jsonMessageNode.get("method").getAsString(); | ||
|
||
if (networkValue.equals("Network.requestWillBeSent")) { | ||
data.append(getRequestsSentOnChe(jsonMessageNode)); | ||
|
||
} else if (networkValue.equals("Network.responseReceived")) { | ||
data.append(getResponsesSentOnChe(jsonMessageNode)); | ||
} | ||
}); | ||
return data.toString(); | ||
} | ||
|
||
/** | ||
* check that current request contains invocation to СHE api URL and provide information about URL | ||
* and http method/status | ||
* | ||
* @param requestMessage json representation of the message object from the log | ||
* @return info about request from the WebDriver | ||
*/ | ||
private static String getRequestsSentOnChe(JsonObject requestMessage) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
JsonObject requestNode = requestMessage.getAsJsonObject("params").getAsJsonObject("request"); | ||
StringBuilder requestInfo = new StringBuilder(); | ||
if (isLogEntryContainsApiUrl(requestNode)) { | ||
requestInfo | ||
.append("Request Info :---------------> \n") | ||
.append("Method: " + requestNode.get("method")) | ||
.append("\n") | ||
.append("URL: " + requestNode.get("url")) | ||
.append("\n\n"); | ||
} | ||
return requestInfo.toString(); | ||
} | ||
|
||
/** | ||
* check that current response contains invocation to СHE api URL and provide information about | ||
* URL and http method/status | ||
* | ||
* @param requestMessage json representation of the message object from the log | ||
* @return info about request from the WebDriver | ||
*/ | ||
private static String getResponsesSentOnChe(JsonObject requestMessage) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am sorry: typo from my side: |
||
JsonObject responseNode = requestMessage.getAsJsonObject("params").getAsJsonObject("response"); | ||
StringBuilder responceInfo = new StringBuilder(); | ||
if (isLogEntryContainsApiUrl(responseNode)) { | ||
responceInfo | ||
.append("Response Info : <--------------- \n") | ||
.append("Method: " + responseNode.get("status")) | ||
.append("\n") | ||
.append("URL: " + responseNode.get("url")) | ||
.append("\n\n"); | ||
} | ||
return responceInfo.toString(); | ||
} | ||
|
||
private static boolean isLogEntryContainsApiUrl(JsonObject node) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return (node.get("url").isJsonNull()) ? false : node.get("url").getAsString().contains("/api/"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there could be requests to other resources with "/api/" in URL. It's more reliable to check on Che hostname as well. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,13 +10,15 @@ | |
*/ | ||
package org.eclipse.che.selenium.opendeclaration; | ||
|
||
import static org.eclipse.che.selenium.core.utils.BrowserLogsUtil.storeLogsToConsoleOutput; | ||
import static org.eclipse.che.selenium.pageobject.CodenvyEditor.MarkersType.WARNING_MARKER; | ||
import static org.testng.Assert.fail; | ||
|
||
import com.google.inject.Inject; | ||
import java.net.URL; | ||
import java.nio.file.Paths; | ||
import org.eclipse.che.commons.lang.NameGenerator; | ||
import org.eclipse.che.selenium.core.SeleniumWebDriver; | ||
import org.eclipse.che.selenium.core.client.TestProjectServiceClient; | ||
import org.eclipse.che.selenium.core.project.ProjectTemplates; | ||
import org.eclipse.che.selenium.core.utils.BrowserLogsUtil; | ||
|
@@ -48,6 +50,7 @@ public class Eclipse0093Test { | |
@Inject private CodenvyEditor editor; | ||
@Inject private TestProjectServiceClient testProjectServiceClient; | ||
@Inject private BrowserLogsUtil browserLogsUtil; | ||
@Inject private SeleniumWebDriver webDriver; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's better to avoid injection of WebDriver into the test. Lets inject webdriver in the same way as it was done in SeleniumWebDriverHelper class. |
||
|
||
@BeforeClass | ||
public void prepare() throws Exception { | ||
|
@@ -77,7 +80,7 @@ private void waitMarkerInPosition() throws Exception { | |
logExternalLibraries(); | ||
logProjectTypeChecking(); | ||
logProjectLanguageChecking(); | ||
browserLogsUtil.storeLogs(); | ||
storeLogsToConsoleOutput(webDriver); | ||
|
||
// remove try-catch block after issue has been resolved | ||
fail("Known issue https://github.com/eclipse/che/issues/7161", ex); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was
Level.SEVERE
used for some reasons.@Ohrimenko1988: please, comment on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was used because only browser errors have useful information for us. But it only in case for browser's console output. In other cases (for example "Networking"), logs of other levels may be useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ALL levels log also can have important info