feat(junit-playwright) (#1412)

This commit is contained in:
uchagani 2023-12-04 20:32:59 -05:00 committed by GitHub
parent d73f953e68
commit f28ca44fa0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 534 additions and 2 deletions

View File

@ -36,7 +36,7 @@ import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.Serialization.toJsonArray;
class Utils {
public class Utils {
static <F, T> T convertType(F f, Class<T> t) {
if (f == null) {
return null;
@ -80,7 +80,7 @@ class Utils {
}
}
static <T> T clone(T f) {
public static <T> T clone(T f) {
if (f == null) {
return f;
}

View File

@ -0,0 +1,112 @@
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.options.ViewportSize;
import java.nio.file.Path;
public class Options {
public String baseUrl;
public Path storageStatePath;
public ViewportSize viewportSize;
public String channel;
public Boolean headless;
public String browserName = "chromium";
public BrowserType.LaunchOptions launchOptions;
public Browser.NewContextOptions contextOption;
public APIRequest.NewContextOptions apiRequestOptions;
public Playwright.CreateOptions playwrightCreateOptions;
public Playwright.CreateOptions getPlaywrightCreateOptions() {
return playwrightCreateOptions;
}
public Options setPlaywrightCreateOptions(Playwright.CreateOptions playwrightCreateOptions) {
this.playwrightCreateOptions = playwrightCreateOptions;
return this;
}
public BrowserType.LaunchOptions getLaunchOptions() {
return launchOptions;
}
public Options setLaunchOptions(BrowserType.LaunchOptions launchOptions) {
this.launchOptions = launchOptions;
return this;
}
public Browser.NewContextOptions getContextOption() {
return contextOption;
}
public Options setContextOption(Browser.NewContextOptions contextOption) {
this.contextOption = contextOption;
return this;
}
public APIRequest.NewContextOptions getApiRequestOptions() {
return apiRequestOptions;
}
public Options setApiRequestOptions(APIRequest.NewContextOptions apiRequestOptions) {
this.apiRequestOptions = apiRequestOptions;
return this;
}
public String getBaseUrl() {
return baseUrl;
}
public Options setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
return this;
}
public Path getStorageStatePath() {
return storageStatePath;
}
public Options setStorageStatePath(Path storageStatePath) {
this.storageStatePath = storageStatePath;
return this;
}
public String getBrowserName() {
return browserName;
}
public Options setBrowserName(String browserName) {
this.browserName = browserName;
return this;
}
public String getChannel() {
return channel;
}
public Options setChannel(String channel) {
this.channel = channel;
return this;
}
public Boolean isHeadless() {
return headless;
}
public Options setHeadless(Boolean headless) {
this.headless = headless;
return this;
}
public ViewportSize getViewportSize() {
return viewportSize;
}
public Options setViewportSize(ViewportSize viewportSize) {
this.viewportSize = viewportSize;
return this;
}
}

View File

@ -0,0 +1,17 @@
package com.microsoft.playwright.junit;
import com.microsoft.playwright.junit.impl.*;
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({OptionsExtension.class, PlaywrightExtension.class, BrowserExtension.class, BrowserContextExtension.class,
PageExtension.class, APIRequestContextExtension.class})
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface UsePlaywright {
Class<? extends Options> options() default Options.class;
}

View File

@ -0,0 +1,45 @@
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.APIRequestContext;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.junit.Options;
import org.junit.jupiter.api.extension.*;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isParameterSupported;
public class APIRequestContextExtension implements ParameterResolver, BeforeEachCallback, AfterAllCallback {
private static final ThreadLocal<APIRequestContext> threadLocalAPIRequestContext = new ThreadLocal<>();
@Override
public void beforeEach(ExtensionContext extensionContext) {
threadLocalAPIRequestContext.remove();
}
@Override
public void afterAll(ExtensionContext extensionContext) {
threadLocalAPIRequestContext.remove();
}
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return isParameterSupported(parameterContext, extensionContext, APIRequestContext.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return getOrCreateAPIRequestContext(extensionContext);
}
static APIRequestContext getOrCreateAPIRequestContext(ExtensionContext extensionContext) {
APIRequestContext apiRequestContext = threadLocalAPIRequestContext.get();
if (apiRequestContext != null) {
return apiRequestContext;
}
Options options = OptionsExtension.getOptions(extensionContext);
Playwright playwright = PlaywrightExtension.getOrCreatePlaywright(extensionContext);
apiRequestContext = playwright.request().newContext(options.getApiRequestOptions());
threadLocalAPIRequestContext.set(apiRequestContext);
return apiRequestContext;
}
}

View File

@ -0,0 +1,68 @@
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.impl.Utils;
import com.microsoft.playwright.junit.Options;
import org.junit.jupiter.api.extension.*;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isClassHook;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isParameterSupported;
public class BrowserContextExtension implements ParameterResolver, AfterEachCallback {
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);
}
@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;
}
Options options = OptionsExtension.getOptions(extensionContext);
Browser browser = BrowserExtension.getOrCreateBrowser(extensionContext);
Browser.NewContextOptions contextOptions = getContextOptions(options);
browserContext = browser.newContext(contextOptions);
threadLocalBrowserContext.set(browserContext);
return browserContext;
}
private static Browser.NewContextOptions getContextOptions(Options options) {
Browser.NewContextOptions contextOptions = Utils.clone(options.getContextOption());
if (contextOptions == null) {
contextOptions = new Browser.NewContextOptions();
}
if (options.getBaseUrl() != null) {
contextOptions.setBaseURL(options.getBaseUrl());
}
if (options.getStorageStatePath() != null) {
contextOptions.setStorageStatePath(options.getStorageStatePath());
}
if (options.getViewportSize() != null) {
contextOptions.setViewportSize(options.getViewportSize());
}
return contextOptions;
}
}

View File

@ -0,0 +1,79 @@
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.impl.Utils;
import com.microsoft.playwright.junit.Options;
import org.junit.jupiter.api.extension.*;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isParameterSupported;
public class BrowserExtension implements ParameterResolver, AfterAllCallback {
private static final ThreadLocal<Browser> threadLocalBrowser = new ThreadLocal<>();
@Override
public void afterAll(ExtensionContext extensionContext) {
Browser browser = threadLocalBrowser.get();
threadLocalBrowser.remove();
if (browser != null) {
browser.close();
}
}
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return isParameterSupported(parameterContext, extensionContext, Browser.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return getOrCreateBrowser(extensionContext);
}
static Browser getOrCreateBrowser(ExtensionContext extensionContext) {
Browser browser = threadLocalBrowser.get();
if (browser != null) {
return browser;
}
Options options = OptionsExtension.getOptions(extensionContext);
Playwright playwright = PlaywrightExtension.getOrCreatePlaywright(extensionContext);
BrowserType.LaunchOptions launchOptions = getLaunchOptions(options);
switch (options.getBrowserName()) {
case "webkit":
browser = playwright.webkit().launch(launchOptions);
break;
case "firefox":
browser = playwright.firefox().launch(launchOptions);
break;
case "chromium":
browser = playwright.chromium().launch(launchOptions);
break;
default:
throw new PlaywrightException("Invalid browser name.");
}
threadLocalBrowser.set(browser);
return browser;
}
private static BrowserType.LaunchOptions getLaunchOptions(Options options) {
BrowserType.LaunchOptions launchOptions = Utils.clone(options.getLaunchOptions());
if (launchOptions == null) {
launchOptions = new BrowserType.LaunchOptions();
}
if (options.isHeadless() != null) {
launchOptions.setHeadless(options.isHeadless());
}
if (options.getChannel() != null) {
options.setChannel(options.getChannel());
}
return launchOptions;
}
}

View File

@ -0,0 +1,30 @@
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
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();
}
static boolean isParameterSupported(ParameterContext parameterContext, ExtensionContext extensionContext, Class<?> subject) {
if (!hasUsePlaywrightAnnotation(extensionContext)) {
return false;
}
Class<?> clazz = parameterContext.getParameter().getType();
return subject.equals(clazz);
}
}

View File

@ -0,0 +1,34 @@
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.junit.Options;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.getUsePlaywrightAnnotation;
public class OptionsExtension implements AfterAllCallback {
private static final ThreadLocal<Options> threadLocalOptions = new ThreadLocal<>();
@Override
public void afterAll(ExtensionContext extensionContext) {
threadLocalOptions.remove();
}
static Options getOptions(ExtensionContext extensionContext) {
Options options = threadLocalOptions.get();
if (options != null) {
return options;
}
UsePlaywright usePlaywrightAnnotation = getUsePlaywrightAnnotation(extensionContext);
try {
options = usePlaywrightAnnotation.options().newInstance();
threadLocalOptions.set(options);
} catch (InstantiationException | IllegalAccessException e) {
throw new PlaywrightException("Failed to create options", e);
}
return options;
}
}

View File

@ -0,0 +1,43 @@
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.isClassHook;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isParameterSupported;
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();
}
}
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return !isClassHook(extensionContext) && isParameterSupported(parameterContext, extensionContext, Page.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return getOrCreatePage(extensionContext);
}
static 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;
}
}

View File

@ -0,0 +1,43 @@
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.junit.Options;
import org.junit.jupiter.api.extension.*;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isParameterSupported;
public class PlaywrightExtension implements ParameterResolver, AfterAllCallback {
private static final ThreadLocal<Playwright> threadLocalPlaywright = new ThreadLocal<>();
@Override
public void afterAll(ExtensionContext extensionContext) {
Playwright playwright = threadLocalPlaywright.get();
threadLocalPlaywright.remove();
if (playwright != null) {
playwright.close();
}
}
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return isParameterSupported(parameterContext, extensionContext, Playwright.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return getOrCreatePlaywright(extensionContext);
}
static Playwright getOrCreatePlaywright(ExtensionContext extensionContext) {
Playwright playwright = threadLocalPlaywright.get();
if (playwright != null) {
return playwright;
}
Options options = OptionsExtension.getOptions(extensionContext);
playwright = Playwright.create(options.getPlaywrightCreateOptions());
threadLocalPlaywright.set(playwright);
return playwright;
}
}

View File

@ -0,0 +1,55 @@
package com.microsoft.playwright;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@UsePlaywright
public class TestPlaywrightFixtures {
private static Playwright playwrightFromBeforeAll;
private static Browser browserFromBeforeAll;
private BrowserContext browserContextFromBeforeEach;
private Page pageFromBeforeEach;
private static APIRequestContext apiRequestContextFromBeforeAll;
private APIRequestContext apiRequestContextFromBeforeEach;
@BeforeAll
public static void beforeAll(Playwright playwright, Browser browser, APIRequestContext apiRequestContext) {
assertNotNull(playwright);
assertNotNull(browser);
assertNotNull(apiRequestContext);
playwrightFromBeforeAll = playwright;
browserFromBeforeAll = browser;
apiRequestContextFromBeforeAll = apiRequestContext;
}
@BeforeEach
public void beforeEach(Playwright playwright, Browser browser, BrowserContext browserContext, Page page, APIRequestContext apiRequestContext) {
assertEquals(playwrightFromBeforeAll, playwright);
assertEquals(browserFromBeforeAll, browser);
assertNotEquals(apiRequestContextFromBeforeAll, apiRequestContext);
assertNotNull(browserContext);
assertNotNull(page);
browserContextFromBeforeEach = browserContext;
pageFromBeforeEach = page;
apiRequestContextFromBeforeEach = apiRequestContext;
}
@Test
public void objectShouldBeSameAsBeforeAll(Playwright playwright, Browser browser) {
assertEquals(playwrightFromBeforeAll, playwright);
assertEquals(browserFromBeforeAll, browser);
}
@Test
public void objectShouldBeSameAsBeforeEach(BrowserContext browserContext, Page page, APIRequestContext apiRequestContext) {
assertEquals(browserContextFromBeforeEach, browserContext);
assertEquals(pageFromBeforeEach, page);
assertEquals(apiRequestContextFromBeforeEach, apiRequestContext);
}
}

View File

@ -86,6 +86,12 @@
<version>${websocket.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>