feat(junit-playwright) (#1371)

This commit is contained in:
uchagani 2023-09-29 19:41:01 -04:00 committed by GitHub
parent fc4a536308
commit fb2188cd2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 661 additions and 0 deletions

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,7 @@
package com.microsoft.playwright.junit;
import com.microsoft.playwright.Playwright;
public interface PlaywrightFactory {
Playwright newPlaywright();
}

View File

@ -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;
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}
}

View File

@ -0,0 +1,77 @@
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.junit.PlaywrightFactory;
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 PlaywrightExtension implements ParameterResolver, AfterAllCallback {
private static final ThreadLocal<Playwright> threadLocalPlaywright;
private static final ThreadLocal<PlaywrightFactory> threadLocalPlaywrightFactory;
static {
threadLocalPlaywright = new ThreadLocal<>();
threadLocalPlaywrightFactory = new ThreadLocal<>();
}
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
if (!hasUsePlaywrightAnnotation(extensionContext)) {
return false;
}
Class<?> clazz = parameterContext.getParameter().getType();
return Playwright.class.equals(clazz);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return getOrCreatePlaywright(extensionContext);
}
@Override
public void afterAll(ExtensionContext extensionContext) {
closePlaywright();
}
static Playwright getOrCreatePlaywright(ExtensionContext extensionContext) {
Playwright playwright = threadLocalPlaywright.get();
if (playwright != null) {
return playwright;
}
PlaywrightFactory playwrightFactory = getPlaywrightFactoryInstance(extensionContext);
playwright = playwrightFactory.newPlaywright();
threadLocalPlaywright.set(playwright);
return playwright;
}
private static PlaywrightFactory getPlaywrightFactoryInstance(ExtensionContext extensionContext) {
if (threadLocalPlaywrightFactory.get() != null) {
return threadLocalPlaywrightFactory.get();
}
UsePlaywright usePlaywrightAnnotation = getUsePlaywrightAnnotation(extensionContext);
try {
PlaywrightFactory playwrightFactory = usePlaywrightAnnotation.playwrightFactory().newInstance();
threadLocalPlaywrightFactory.set(playwrightFactory);
return playwrightFactory;
} catch (InstantiationException | IllegalAccessException e) {
throw new PlaywrightException("Unable to create an instance of the supplied PlaywrightFactory", e);
}
}
private static void closePlaywright() {
try {
if (threadLocalPlaywright.get() != null) {
threadLocalPlaywright.get().close();
}
} finally {
threadLocalPlaywright.remove();
}
}
}

View File

@ -0,0 +1,16 @@
package com.microsoft.playwright;
import com.microsoft.playwright.junit.BrowserContextFactory;
import com.microsoft.playwright.options.Cookie;
import java.util.Collections;
public class CustomBrowserContextFactory implements BrowserContextFactory {
@Override
public BrowserContext newBrowserContext(Browser browser) {
BrowserContext context = browser.newContext();
Cookie cookie = new Cookie("foo", "bar").setUrl("https://microsoft.com");
context.addCookies(Collections.singletonList(cookie));
return context;
}
}

View File

@ -0,0 +1,10 @@
package com.microsoft.playwright;
import com.microsoft.playwright.junit.BrowserFactory;
public class CustomBrowserFactory implements BrowserFactory {
@Override
public Browser newBrowser(Playwright playwright) {
return playwright.firefox().launch();
}
}

View File

@ -0,0 +1,93 @@
package com.microsoft.playwright;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@UsePlaywright(browserFactory = CustomBrowserFactory.class, browserContextFactory = CustomBrowserContextFactory.class)
public class TestFixturesWithCustomFactories {
private static Playwright playwrightFromBeforeAll;
private static Browser browserFromBeforeAll;
private BrowserContext browserContextFromBeforeEach;
private Page pageFromBeforeEach;
@BeforeAll
static void beforeAll(Playwright playwright, Browser browser) {
assertNotNull(playwright);
assertNotNull(browser);
assertEquals("firefox", browser.browserType().name());
playwrightFromBeforeAll = playwright;
browserFromBeforeAll = browser;
}
@AfterAll
static void afterAll(Playwright playwright, Browser browser) {
assertNotNull(playwright);
assertNotNull(browser);
assertEquals(playwrightFromBeforeAll, playwright);
assertEquals(browserFromBeforeAll, browser);
}
@BeforeEach
void beforeEach(Playwright playwright, Browser browser, BrowserContext browserContext, Page page) {
assertNotNull(playwright);
assertNotNull(browser);
assertNotNull(browserContext);
assertNotNull(page);
assertEquals(playwrightFromBeforeAll, playwright);
assertEquals(browserFromBeforeAll, browser);
assertEquals(1, browserContext.cookies().size());
assertEquals("foo", browserContext.cookies().get(0).name);
this.browserContextFromBeforeEach = browserContext;
this.pageFromBeforeEach = page;
}
@AfterEach
void afterEach(Playwright playwright, Browser browser, BrowserContext browserContext, Page page) {
assertNotNull(playwright);
assertNotNull(browser);
assertNotNull(browserContext);
assertNotNull(page);
assertEquals(playwrightFromBeforeAll, playwright);
assertEquals(browserFromBeforeAll, browser);
}
@Test
void objectsAreSameFromBeforeAll(Playwright playwright, Browser browser) {
assertNotNull(playwright);
assertNotNull(browser);
assertEquals(playwrightFromBeforeAll, playwright);
assertEquals(browserFromBeforeAll, browser);
}
@Test
void objectsAreSameFromBeforeEach(BrowserContext browserContext, Page page) {
assertNotNull(browserContext);
assertNotNull(page);
assertEquals(this.browserContextFromBeforeEach, browserContext);
assertEquals(this.pageFromBeforeEach, page);
}
@Test
void duplicatePlaywrightReturnTheSameObject(Playwright playwright1, Playwright playwright2) {
assertEquals(playwright1, playwright2);
}
@Test
void duplicateBrowserReturnTheSameObject(Browser browser1, Browser browser2) {
assertEquals(browser1, browser2);
}
@Test
void duplicateBrowserContextReturnTheSameObject(BrowserContext browserContext1, BrowserContext browserContext2) {
assertEquals(browserContext1, browserContext2);
}
@Test
void duplicatePageReturnTheSameObject(Page page1, Page page2) {
assertEquals(page1, page2);
}
}

View File

@ -0,0 +1,92 @@
package com.microsoft.playwright;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@UsePlaywright
public class TestFixturesWithDefaultFactories {
private static Playwright playwrightFromBeforeAll;
private static Browser browserFromBeforeAll;
private BrowserContext browserContextFromBeforeEach;
private Page pageFromBeforeEach;
@BeforeAll
static void beforeAll(Playwright playwright, Browser browser) {
assertNotNull(playwright);
assertNotNull(browser);
String browserName = System.getenv("BROWSER") == null ? "chromium" : System.getenv("BROWSER");
assertEquals(browserName, browser.browserType().name());
playwrightFromBeforeAll = playwright;
browserFromBeforeAll = browser;
}
@AfterAll
static void afterAll(Playwright playwright, Browser browser) {
assertNotNull(playwright);
assertNotNull(browser);
assertEquals(playwrightFromBeforeAll, playwright);
assertEquals(browserFromBeforeAll, browser);
}
@BeforeEach
void beforeEach(Playwright playwright, Browser browser, BrowserContext browserContext, Page page) {
assertNotNull(playwright);
assertNotNull(browser);
assertNotNull(browserContext);
assertNotNull(page);
assertEquals(playwrightFromBeforeAll, playwright);
assertEquals(browserFromBeforeAll, browser);
this.browserContextFromBeforeEach = browserContext;
this.pageFromBeforeEach = page;
}
@AfterEach
void afterEach(Playwright playwright, Browser browser, BrowserContext browserContext, Page page) {
assertNotNull(playwright);
assertNotNull(browser);
assertNotNull(browserContext);
assertNotNull(page);
assertEquals(playwrightFromBeforeAll, playwright);
assertEquals(browserFromBeforeAll, browser);
}
@Test
void objectsAreSameFromBeforeAll(Playwright playwright, Browser browser) {
assertNotNull(playwright);
assertNotNull(browser);
assertEquals(playwrightFromBeforeAll, playwright);
assertEquals(browserFromBeforeAll, browser);
}
@Test
void objectsAreSameFromBeforeEach(BrowserContext browserContext, Page page) {
assertNotNull(browserContext);
assertNotNull(page);
assertEquals(this.browserContextFromBeforeEach, browserContext);
assertEquals(this.pageFromBeforeEach, page);
}
@Test
void duplicatePlaywrightReturnTheSameObject(Playwright playwright1, Playwright playwright2) {
assertEquals(playwright1, playwright2);
}
@Test
void duplicateBrowserReturnTheSameObject(Browser browser1, Browser browser2) {
assertEquals(browser1, browser2);
}
@Test
void duplicateBrowserContextReturnTheSameObject(BrowserContext browserContext1, BrowserContext browserContext2) {
assertEquals(browserContext1, browserContext2);
}
@Test
void duplicatePageReturnTheSameObject(Page page1, Page page2) {
assertEquals(page1, page2);
}
}

11
pom.xml
View File

@ -54,6 +54,11 @@
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>driver</artifactId>
@ -80,6 +85,12 @@
<version>${junit.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>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>