/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.test.ui;
import java.io.File;
import org.apache.commons.io.FileUtils;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Generates debugging information on test failure:
* <ul>
* <li>captures a screenshot of the browser window</li>
* <li>logs the URL of the current page</li>
* <li>logs the source of the current page</li>
* </ul>
* NOTE: The reason we also log when a test starts and passes is simply to overcome a deficiency in error reporting in
* Jenkins. The reason is that Jenkins bases its test reporting on the Maven Surefire plugin reporting which itself is
* using a file to report test status. Since ui-tests are using a test suite, {@link PageObjectSuite}, there's only a
* single file generated and it's only generated when all tests have finished executing. Thus if a test hangs there
* won't be any file generated and looking at the Jenkins UI it won't be possible to see which tests have executed.
* <p>
* Normally each JUnit Test Runner knows what test is executing and when it's finished and thus can report them in its
* own console (as this is the case for IDEs for example). Again the issue here is that Jenkins doesn't have any JUnit
* Test Runner but instead is calling JUnit by delegation to the Maven Surefire plugin.
*
* @version $Id: 113fd89702dfca6ab833b73dc87675edd31de43f $
* @since 4.3
*/
public class TestDebugger extends TestWatcher
{
/**
* Logging helper object.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(TestDebugger.class);
/**
* The folder where to save the screenshot.
*/
private static final String SCREENSHOT_DIR = System.getProperty("screenshotDirectory");
/**
* The object used to get the debugging information on test failure.
*/
private final WebDriver driver;
/**
* Creates a new test rule that generates debugging information on test failure.
*
* @param driver the object used to get the debugging information on test failure
*/
public TestDebugger(WebDriver driver)
{
this.driver = driver;
}
@Override
protected void starting(Description description)
{
LOGGER.info("{} started", getTestName(description));
}
@Override
protected void succeeded(Description description)
{
LOGGER.info("{} passed", getTestName(description));
}
@Override
protected void failed(Throwable e, Description description)
{
LOGGER.info("{} failed", getTestName(description));
takeScreenshot(description);
LOGGER.info("Current page URL is [{}]", driver.getCurrentUrl());
LOGGER.info("Current page source is [{}]", driver.getPageSource());
}
/**
* @param description the test description
* @return the test name (using the format TestSimpleClassName-TestMethodName)
*/
private String getTestName(Description description)
{
return description.getTestClass().getSimpleName() + "-" + description.getMethodName();
}
/**
* Captures a screenshot of the browser window.
*
* @param description the description of the failing test
*/
private void takeScreenshot(Description description)
{
takeScreenshot(getTestName(description));
}
/**
* Captures a screenshot of the browser window.
*
* @param testName the name of the file in which the screenshot will be taken. A ".png" suffix will be appended
*/
public void takeScreenshot(String testName)
{
if (!(driver instanceof TakesScreenshot)) {
LOGGER.warn("The WebDriver that is currently used doesn't support taking screenshots.");
return;
}
try {
File sourceFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
File screenshotFile;
if (SCREENSHOT_DIR != null) {
File screenshotDir = new File(SCREENSHOT_DIR);
screenshotDir.mkdirs();
screenshotFile = new File(screenshotDir, testName + ".png");
} else {
screenshotFile = new File(new File(System.getProperty("java.io.tmpdir")), testName + ".png");
}
FileUtils.copyFile(sourceFile, screenshotFile);
LOGGER.info("Screenshot for failing test [{}] saved at [{}].", testName, screenshotFile.getAbsolutePath());
} catch (Exception e) {
LOGGER.error("Failed to take screenshot for failing test [{}].", testName, e);
}
}
}