Skip to content

Commit

Permalink
feat(junit): Implement automatic saving of traces and screenshots via…
Browse files Browse the repository at this point in the history
… fixtures (#1560)
  • Loading branch information
uchagani authored Aug 28, 2024
1 parent b81b144 commit 2e32eb7
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ jobs:
- name: Install MS Edge
if: matrix.browser-channel == 'msedge' && matrix.os == 'macos-latest'
shell: bash
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install msedge" -f playwright/pom.xml
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install msedge" -f playwright/pom.xml
- name: Run tests
run: mvn test --no-transfer-progress --fail-at-end -D org.slf4j.simpleLogger.showDateTime=true -D org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss
env:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,22 @@

package com.microsoft.playwright.impl.junit;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.*;
import com.microsoft.playwright.impl.Utils;
import com.microsoft.playwright.junit.Options;
import org.junit.jupiter.api.extension.*;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static com.microsoft.playwright.impl.junit.ExtensionUtils.*;
import static com.microsoft.playwright.impl.junit.PageExtension.cleanUpPage;

public class BrowserContextExtension implements ParameterResolver, AfterEachCallback {
public class BrowserContextExtension implements ParameterResolver, TestWatcher {
private static final ThreadLocal<BrowserContext> threadLocalBrowserContext = new ThreadLocal<>();

@Override
public void afterEach(ExtensionContext extensionContext) {
BrowserContext browserContext = threadLocalBrowserContext.get();
threadLocalBrowserContext.remove();
if (browserContext != null) {
browserContext.close();
}
}

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return !isClassHook(extensionContext) && isParameterSupported(parameterContext, extensionContext, BrowserContext.class);
Expand All @@ -51,6 +45,7 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte
/**
* Returns the BrowserContext that belongs to the current test. Will be created if it doesn't already exist.
* <strong>NOTE:</strong> this method is subject to change.
*
* @param extensionContext the context in which the current test or container is being executed.
* @return The BrowserContext that belongs to the current test.
*/
Expand All @@ -66,10 +61,97 @@ public static BrowserContext getOrCreateBrowserContext(ExtensionContext extensio
Browser browser = BrowserExtension.getOrCreateBrowser(extensionContext);
Browser.NewContextOptions contextOptions = getContextOptions(playwright, options);
browserContext = browser.newContext(contextOptions);
if (shouldRecordTrace(options)) {
Tracing.StartOptions startOptions = new Tracing.StartOptions().setSnapshots(true).setScreenshots(true).setTitle(extensionContext.getDisplayName());
if (System.getenv("PLAYWRIGHT_JAVA_SRC") != null) {
startOptions.setSources(true);
}
browserContext.tracing().start(startOptions);
}
threadLocalBrowserContext.set(browserContext);
return browserContext;
}

@Override
public void testSuccessful(ExtensionContext extensionContext) {
saveTraceWhenOn(extensionContext);
closeBrowserContext();
}

@Override
public void testAborted(ExtensionContext extensionContext, Throwable cause) {
saveTraceWhenOn(extensionContext);
closeBrowserContext();
}

@Override
public void testFailed(ExtensionContext extensionContext, Throwable cause) {
Options options = OptionsExtension.getOptions(extensionContext);
if (shouldRecordTrace(options)) {
saveTrace(extensionContext);
}
closeBrowserContext();
}

private static void saveTraceWhenOn(ExtensionContext extensionContext) {
Options options = OptionsExtension.getOptions(extensionContext);
if (options.trace.equals(Options.Trace.ON)) {
saveTrace(extensionContext);
}
}

private static void saveTrace(ExtensionContext extensionContext) {
BrowserContext browserContext = threadLocalBrowserContext.get();
if (browserContext == null) {
return;
}
Path outputPath = getOutputPath(extensionContext);
createOutputPath(outputPath);
Tracing.StopOptions stopOptions = new Tracing.StopOptions().setPath(outputPath.resolve("trace.zip"));
browserContext.tracing().stop(stopOptions);
}

private static void createOutputPath(Path outputPath) {
if (!Files.exists(outputPath)) {
try {
Files.createDirectories(outputPath);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

private static Path getOutputPath(ExtensionContext extensionContext) {
BrowserType browserType = BrowserExtension.getBrowser().browserType();
Path defaultOutputPath = getDefaultOutputPath(extensionContext);
String outputDirName = extensionContext.getRequiredTestClass().getName() + "." +
extensionContext.getRequiredTestMethod().getName() + "-" +
browserType.name();
return defaultOutputPath.resolve(outputDirName);
}

private static Path getDefaultOutputPath(ExtensionContext extensionContext) {
Options options = OptionsExtension.getOptions(extensionContext);
Path outputPath = options.outputDir;
if (outputPath == null) {
outputPath = Paths.get(System.getProperty("user.dir")).resolve("test-results");
}
return outputPath;
}

private void closeBrowserContext() {
cleanUpPage();
BrowserContext browserContext = threadLocalBrowserContext.get();
threadLocalBrowserContext.remove();
if (browserContext != null) {
browserContext.close();
}
}

private static boolean shouldRecordTrace(Options options) {
return options.trace.equals(Options.Trace.ON) || options.trace.equals(Options.Trace.RETAIN_ON_FAILURE);
}

private static Browser.NewContextOptions getContextOptions(Playwright playwright, Options options) {
Browser.NewContextOptions contextOptions = Utils.clone(options.contextOptions);
if (contextOptions == null) {
Expand All @@ -94,7 +176,7 @@ private static Browser.NewContextOptions getContextOptions(Playwright playwright
contextOptions.hasTouch = deviceDescriptor.hasTouch;
}

if(options.ignoreHTTPSErrors != null) {
if (options.ignoreHTTPSErrors != null) {
contextOptions.setIgnoreHTTPSErrors(options.ignoreHTTPSErrors);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ public static Browser getOrCreateBrowser(ExtensionContext extensionContext) {
return browser;
}

static Browser getBrowser() {
return threadLocalBrowser.get();
}

private static BrowserType.ConnectOptions getConnectOptions(Options options) {
BrowserType.ConnectOptions connectOptions = options.connectOptions;
if(connectOptions == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,8 @@

import static com.microsoft.playwright.impl.junit.ExtensionUtils.*;

public class PageExtension implements ParameterResolver, AfterEachCallback {
private static final ThreadLocal<Page> threadLocalPage = new ThreadLocal<>();

@Override
public void afterEach(ExtensionContext extensionContext) {
Page page = threadLocalPage.get();
threadLocalPage.remove();
if (page != null) {
page.close();
}
}
public class PageExtension implements ParameterResolver {
private static final ThreadLocal<Page> threadLocalPage = new ThreadLocal<>();

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
Expand All @@ -47,6 +38,7 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte
/**
* Returns the Page that belongs to the current test. Will be created if it doesn't already exist.
* <strong>NOTE:</strong> this method is subject to change.
*
* @param extensionContext the context in which the current test or container is being executed.
* @return The Page that belongs to the current test.
*/
Expand All @@ -61,4 +53,8 @@ public static Page getOrCreatePage(ExtensionContext extensionContext) {
threadLocalPage.set(page);
return page;
}

static void cleanUpPage() {
threadLocalPage.remove();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@

package com.microsoft.playwright.junit;

import com.microsoft.playwright.APIRequest;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.*;

import java.nio.file.Path;

/**
* <strong>NOTE:</strong> this API is experimental and is subject to changes.
Expand Down Expand Up @@ -47,6 +46,26 @@ public class Options {
// If this is set, BrowserType.connect will be used. Otherwise, BrowserType.launch will be used.
public String wsEndpoint;
public BrowserType.ConnectOptions connectOptions;
// The dir where test artifacts will be stored
public Path outputDir;
// When to record traces. Default is OFF.
public Trace trace = Trace.OFF;

public enum Trace {
OFF,
ON,
RETAIN_ON_FAILURE;
}

public Options setTrace(Trace trace) {
this.trace = trace;
return this;
}

public Options setOutputDir(Path outputDir) {
this.outputDir = outputDir;
return this;
}

public Options setWsEndpoint(String wsEndpoint) {
this.wsEndpoint = wsEndpoint;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private static boolean getHeadful() {
return headfulEnv != null && !"0".equals(headfulEnv) && !"false".equals(headfulEnv);
}

private static String getBrowserName() {
public static String getBrowserName() {
String browserName = System.getenv("BROWSER");
if (browserName == null) {
browserName = "chromium";
Expand Down

0 comments on commit 2e32eb7

Please sign in to comment.