Skip to content

Commit

Permalink
feat(junit-playwright) (#1371)
Browse files Browse the repository at this point in the history
  • Loading branch information
uchagani authored Sep 29, 2023
1 parent fc4a536 commit fb2188c
Show file tree
Hide file tree
Showing 17 changed files with 661 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.microsoft.playwright.junit;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;

public interface BrowserContextFactory {
BrowserContext newBrowserContext(Browser browser);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.microsoft.playwright.junit;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.Playwright;

public interface BrowserFactory {
Browser newBrowser(Playwright playwright);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.microsoft.playwright.junit;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;

public class DefaultBrowserContextFactory implements BrowserContextFactory {
@Override
public BrowserContext newBrowserContext(Browser browser) {
return browser.newContext();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.microsoft.playwright.junit;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.PlaywrightException;

public class DefaultBrowserFactory implements BrowserFactory {
private final boolean headed;
private final String browserEnv;

public DefaultBrowserFactory() {
headed = isHeaded();
browserEnv = getBrowserNameFromEnv();
}

@Override
public Browser newBrowser(Playwright playwright) {
BrowserType.LaunchOptions launchOptions = new BrowserType.LaunchOptions().setHeadless(!headed);

switch (browserEnv) {
case "webkit":
return playwright.webkit().launch(launchOptions);
case "firefox":
return playwright.firefox().launch(launchOptions);
case "chromium":
return playwright.chromium().launch(launchOptions);
default:
throw new PlaywrightException("Invalid value set for BROWSER. Must be one of: chromium, firefox, webkit");
}
}

private static String getBrowserNameFromEnv() {
String browserName = System.getenv("BROWSER");
if (browserName == null) {
browserName = "chromium";
}
return browserName;
}

private static boolean isHeaded() {
String headedEnv = System.getenv("HEADED");
return headedEnv != null && !"0".equals(headedEnv) && !"false".equals(headedEnv);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.microsoft.playwright.junit;

import com.microsoft.playwright.Playwright;

public class DefaultPlaywrightFactory implements PlaywrightFactory {
@Override
public Playwright newPlaywright() {
return Playwright.create();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.microsoft.playwright.junit;

import com.microsoft.playwright.Playwright;

public interface PlaywrightFactory {
Playwright newPlaywright();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.microsoft.playwright.junit;

import com.microsoft.playwright.junit.impl.BrowserContextExtension;
import com.microsoft.playwright.junit.impl.BrowserExtension;
import com.microsoft.playwright.junit.impl.PageExtension;
import com.microsoft.playwright.junit.impl.PlaywrightExtension;
import org.junit.jupiter.api.extension.ExtendWith;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@ExtendWith({PlaywrightExtension.class, BrowserExtension.class, BrowserContextExtension.class, PageExtension.class})
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface UsePlaywright {
Class<? extends PlaywrightFactory> playwrightFactory() default DefaultPlaywrightFactory.class;

Class<? extends BrowserFactory> browserFactory() default DefaultBrowserFactory.class;

Class<? extends BrowserContextFactory> browserContextFactory() default DefaultBrowserContextFactory.class;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.microsoft.playwright.junit.impl;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.junit.BrowserContextFactory;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.extension.*;

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

public class BrowserContextExtension implements ParameterResolver, AfterEachCallback, AfterAllCallback {
private final static ThreadLocal<BrowserContext> threadLocalBrowserContext;
private final static ThreadLocal<BrowserContextFactory> threadLocalBrowserContextFactory;

static {
threadLocalBrowserContext = new ThreadLocal<>();
threadLocalBrowserContextFactory = new ThreadLocal<>();
}

@Override
public void afterEach(ExtensionContext extensionContext) {
cleanupBrowserContext();
}

@Override
public void afterAll(ExtensionContext extensionContext) {
cleanupBrowserContextFactory();
}

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
if (isClassHook(extensionContext) || !hasUsePlaywrightAnnotation(extensionContext)) {
return false;
}
Class<?> clazz = parameterContext.getParameter().getType();
return BrowserContext.class.equals(clazz);
}

@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return getOrCreateBrowserContext(extensionContext);
}

static BrowserContext getOrCreateBrowserContext(ExtensionContext extensionContext) {
BrowserContext browserContext = threadLocalBrowserContext.get();
if (browserContext != null) {
return browserContext;
}

Browser browser = BrowserExtension.getOrCreateBrowser(extensionContext);
BrowserContextFactory browserContextFactory = getBrowserContextFactoryInstance(extensionContext);
browserContext = browserContextFactory.newBrowserContext(browser);
threadLocalBrowserContext.set(browserContext);
return browserContext;
}

private static BrowserContextFactory getBrowserContextFactoryInstance(ExtensionContext extensionContext) {
if (threadLocalBrowserContextFactory.get() != null) {
return threadLocalBrowserContextFactory.get();
}

UsePlaywright usePlaywrightAnnotation = getUsePlaywrightAnnotation(extensionContext);

try {
BrowserContextFactory browserContextFactory = usePlaywrightAnnotation.browserContextFactory().newInstance();
threadLocalBrowserContextFactory.set(browserContextFactory);
return browserContextFactory;
} catch (InstantiationException | IllegalAccessException e) {
throw new PlaywrightException("Unable to create an instance of the supplied BrowserContextFactory", e);
}
}

private void cleanupBrowserContext() {
try {
BrowserContext browserContext = threadLocalBrowserContext.get();
if (browserContext != null) {
browserContext.close();
}
} finally {
threadLocalBrowserContext.remove();
}
}

private void cleanupBrowserContextFactory() {
BrowserContextFactory browserContextFactory = threadLocalBrowserContextFactory.get();
if (browserContextFactory != null) {
threadLocalBrowserContextFactory.remove();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.microsoft.playwright.junit.impl;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.junit.BrowserFactory;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.extension.*;

import static com.microsoft.playwright.junit.impl.ExtensionUtils.getUsePlaywrightAnnotation;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.hasUsePlaywrightAnnotation;

public class BrowserExtension implements ParameterResolver, AfterAllCallback {
private static final ThreadLocal<Browser> threadLocalBrowser;
private static final ThreadLocal<BrowserFactory> threadLocalBrowserFactory;

static {
threadLocalBrowser = new ThreadLocal<>();
threadLocalBrowserFactory = new ThreadLocal<>();
}

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
if (!hasUsePlaywrightAnnotation(extensionContext)) {
return false;
}
Class<?> clazz = parameterContext.getParameter().getType();
return Browser.class.equals(clazz);
}

@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return getOrCreateBrowser(extensionContext);
}

@Override
public void afterAll(ExtensionContext extensionContext) {
try {
Browser browser = threadLocalBrowser.get();
if (browser != null) {
browser.close();
}
} finally {
threadLocalBrowser.remove();
threadLocalBrowserFactory.remove();
}
}

static Browser getOrCreateBrowser(ExtensionContext extensionContext) {
Browser browser = threadLocalBrowser.get();
if (browser != null) {
return browser;
}

Playwright playwright = PlaywrightExtension.getOrCreatePlaywright(extensionContext);
BrowserFactory browserFactory = getBrowserFactoryInstance(extensionContext);
browser = browserFactory.newBrowser(playwright);
threadLocalBrowser.set(browser);
return browser;
}

private static BrowserFactory getBrowserFactoryInstance(ExtensionContext extensionContext) {
if (threadLocalBrowserFactory.get() != null) {
return threadLocalBrowserFactory.get();
}

UsePlaywright usePlaywrightAnnotation = getUsePlaywrightAnnotation(extensionContext);

try {
BrowserFactory browserFactory = usePlaywrightAnnotation.browserFactory().newInstance();
threadLocalBrowserFactory.set(browserFactory);
return browserFactory;
} catch (InstantiationException | IllegalAccessException e) {
throw new PlaywrightException("Unable to create an instance of the supplied BrowserFactory", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.microsoft.playwright.junit.impl;

import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.platform.commons.support.AnnotationSupport;

import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;

class ExtensionUtils {
static boolean hasUsePlaywrightAnnotation(ExtensionContext extensionContext) {
return AnnotationSupport.isAnnotated(extensionContext.getTestClass(), UsePlaywright.class);
}

static UsePlaywright getUsePlaywrightAnnotation(ExtensionContext extensionContext) {
return findAnnotation(extensionContext.getTestClass(), UsePlaywright.class).get();
}

static boolean isClassHook(ExtensionContext extensionContext) {
return !extensionContext.getTestMethod().isPresent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.microsoft.playwright.junit.impl;

import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.Page;
import org.junit.jupiter.api.extension.*;

import static com.microsoft.playwright.junit.impl.ExtensionUtils.hasUsePlaywrightAnnotation;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isClassHook;

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

static {
threadLocalPage = new ThreadLocal<>();
}

@Override
public void afterEach(ExtensionContext extensionContext) {
cleanupPage();
}

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
if (isClassHook(extensionContext) || !hasUsePlaywrightAnnotation(extensionContext)) {
return false;
}
Class<?> clazz = parameterContext.getParameter().getType();
return Page.class.equals(clazz);
}

@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return getOrCreatePage(extensionContext);
}

private Page getOrCreatePage(ExtensionContext extensionContext) {
Page page = threadLocalPage.get();
if (page != null) {
return page;
}

BrowserContext browserContext = BrowserContextExtension.getOrCreateBrowserContext(extensionContext);
page = browserContext.newPage();
threadLocalPage.set(page);
return page;
}

private void cleanupPage() {
try {
Page page = threadLocalPage.get();
if (page != null) {
if (!page.isClosed()) {
page.close();
}
}
} finally {
threadLocalPage.remove();
}

}
}
Loading

0 comments on commit fb2188c

Please sign in to comment.