Skip to content

Commit f28ca44

Browse files
authored
feat(junit-playwright) (#1412)
1 parent d73f953 commit f28ca44

File tree

12 files changed

+534
-2
lines changed

12 files changed

+534
-2
lines changed

playwright/src/main/java/com/microsoft/playwright/impl/Utils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
import static com.microsoft.playwright.impl.Serialization.toJsonArray;
3838

39-
class Utils {
39+
public class Utils {
4040
static <F, T> T convertType(F f, Class<T> t) {
4141
if (f == null) {
4242
return null;
@@ -80,7 +80,7 @@ static <F, T> T convertType(F f, Class<T> t) {
8080
}
8181
}
8282

83-
static <T> T clone(T f) {
83+
public static <T> T clone(T f) {
8484
if (f == null) {
8585
return f;
8686
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package com.microsoft.playwright.junit;
2+
3+
import com.microsoft.playwright.APIRequest;
4+
import com.microsoft.playwright.Browser;
5+
import com.microsoft.playwright.BrowserType;
6+
import com.microsoft.playwright.Playwright;
7+
import com.microsoft.playwright.options.ViewportSize;
8+
9+
import java.nio.file.Path;
10+
11+
public class Options {
12+
public String baseUrl;
13+
public Path storageStatePath;
14+
public ViewportSize viewportSize;
15+
public String channel;
16+
public Boolean headless;
17+
public String browserName = "chromium";
18+
public BrowserType.LaunchOptions launchOptions;
19+
public Browser.NewContextOptions contextOption;
20+
public APIRequest.NewContextOptions apiRequestOptions;
21+
public Playwright.CreateOptions playwrightCreateOptions;
22+
23+
public Playwright.CreateOptions getPlaywrightCreateOptions() {
24+
return playwrightCreateOptions;
25+
}
26+
27+
public Options setPlaywrightCreateOptions(Playwright.CreateOptions playwrightCreateOptions) {
28+
this.playwrightCreateOptions = playwrightCreateOptions;
29+
return this;
30+
}
31+
32+
public BrowserType.LaunchOptions getLaunchOptions() {
33+
return launchOptions;
34+
}
35+
36+
public Options setLaunchOptions(BrowserType.LaunchOptions launchOptions) {
37+
this.launchOptions = launchOptions;
38+
return this;
39+
}
40+
41+
public Browser.NewContextOptions getContextOption() {
42+
return contextOption;
43+
}
44+
45+
public Options setContextOption(Browser.NewContextOptions contextOption) {
46+
this.contextOption = contextOption;
47+
return this;
48+
}
49+
50+
public APIRequest.NewContextOptions getApiRequestOptions() {
51+
return apiRequestOptions;
52+
}
53+
54+
public Options setApiRequestOptions(APIRequest.NewContextOptions apiRequestOptions) {
55+
this.apiRequestOptions = apiRequestOptions;
56+
return this;
57+
}
58+
59+
public String getBaseUrl() {
60+
return baseUrl;
61+
}
62+
63+
public Options setBaseUrl(String baseUrl) {
64+
this.baseUrl = baseUrl;
65+
return this;
66+
}
67+
68+
public Path getStorageStatePath() {
69+
return storageStatePath;
70+
}
71+
72+
public Options setStorageStatePath(Path storageStatePath) {
73+
this.storageStatePath = storageStatePath;
74+
return this;
75+
}
76+
77+
public String getBrowserName() {
78+
return browserName;
79+
}
80+
81+
public Options setBrowserName(String browserName) {
82+
this.browserName = browserName;
83+
return this;
84+
}
85+
86+
public String getChannel() {
87+
return channel;
88+
}
89+
90+
public Options setChannel(String channel) {
91+
this.channel = channel;
92+
return this;
93+
}
94+
95+
public Boolean isHeadless() {
96+
return headless;
97+
}
98+
99+
public Options setHeadless(Boolean headless) {
100+
this.headless = headless;
101+
return this;
102+
}
103+
104+
public ViewportSize getViewportSize() {
105+
return viewportSize;
106+
}
107+
108+
public Options setViewportSize(ViewportSize viewportSize) {
109+
this.viewportSize = viewportSize;
110+
return this;
111+
}
112+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.microsoft.playwright.junit;
2+
3+
import com.microsoft.playwright.junit.impl.*;
4+
import org.junit.jupiter.api.extension.ExtendWith;
5+
6+
import java.lang.annotation.ElementType;
7+
import java.lang.annotation.Retention;
8+
import java.lang.annotation.RetentionPolicy;
9+
import java.lang.annotation.Target;
10+
11+
@ExtendWith({OptionsExtension.class, PlaywrightExtension.class, BrowserExtension.class, BrowserContextExtension.class,
12+
PageExtension.class, APIRequestContextExtension.class})
13+
@Retention(RetentionPolicy.RUNTIME)
14+
@Target(ElementType.TYPE)
15+
public @interface UsePlaywright {
16+
Class<? extends Options> options() default Options.class;
17+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.microsoft.playwright.junit.impl;
2+
3+
import com.microsoft.playwright.APIRequestContext;
4+
import com.microsoft.playwright.Playwright;
5+
import com.microsoft.playwright.junit.Options;
6+
import org.junit.jupiter.api.extension.*;
7+
8+
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isParameterSupported;
9+
10+
public class APIRequestContextExtension implements ParameterResolver, BeforeEachCallback, AfterAllCallback {
11+
private static final ThreadLocal<APIRequestContext> threadLocalAPIRequestContext = new ThreadLocal<>();
12+
13+
@Override
14+
public void beforeEach(ExtensionContext extensionContext) {
15+
threadLocalAPIRequestContext.remove();
16+
}
17+
18+
@Override
19+
public void afterAll(ExtensionContext extensionContext) {
20+
threadLocalAPIRequestContext.remove();
21+
}
22+
23+
@Override
24+
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
25+
return isParameterSupported(parameterContext, extensionContext, APIRequestContext.class);
26+
}
27+
28+
@Override
29+
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
30+
return getOrCreateAPIRequestContext(extensionContext);
31+
}
32+
33+
static APIRequestContext getOrCreateAPIRequestContext(ExtensionContext extensionContext) {
34+
APIRequestContext apiRequestContext = threadLocalAPIRequestContext.get();
35+
if (apiRequestContext != null) {
36+
return apiRequestContext;
37+
}
38+
39+
Options options = OptionsExtension.getOptions(extensionContext);
40+
Playwright playwright = PlaywrightExtension.getOrCreatePlaywright(extensionContext);
41+
apiRequestContext = playwright.request().newContext(options.getApiRequestOptions());
42+
threadLocalAPIRequestContext.set(apiRequestContext);
43+
return apiRequestContext;
44+
}
45+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.microsoft.playwright.junit.impl;
2+
3+
import com.microsoft.playwright.Browser;
4+
import com.microsoft.playwright.BrowserContext;
5+
import com.microsoft.playwright.impl.Utils;
6+
import com.microsoft.playwright.junit.Options;
7+
import org.junit.jupiter.api.extension.*;
8+
9+
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isClassHook;
10+
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isParameterSupported;
11+
12+
public class BrowserContextExtension implements ParameterResolver, AfterEachCallback {
13+
private static final ThreadLocal<BrowserContext> threadLocalBrowserContext = new ThreadLocal<>();
14+
15+
@Override
16+
public void afterEach(ExtensionContext extensionContext) {
17+
BrowserContext browserContext = threadLocalBrowserContext.get();
18+
threadLocalBrowserContext.remove();
19+
if (browserContext != null) {
20+
browserContext.close();
21+
}
22+
}
23+
24+
@Override
25+
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
26+
return !isClassHook(extensionContext) && isParameterSupported(parameterContext, extensionContext, BrowserContext.class);
27+
}
28+
29+
@Override
30+
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
31+
return getOrCreateBrowserContext(extensionContext);
32+
}
33+
34+
static BrowserContext getOrCreateBrowserContext(ExtensionContext extensionContext) {
35+
BrowserContext browserContext = threadLocalBrowserContext.get();
36+
if (browserContext != null) {
37+
return browserContext;
38+
}
39+
40+
Options options = OptionsExtension.getOptions(extensionContext);
41+
Browser browser = BrowserExtension.getOrCreateBrowser(extensionContext);
42+
Browser.NewContextOptions contextOptions = getContextOptions(options);
43+
browserContext = browser.newContext(contextOptions);
44+
threadLocalBrowserContext.set(browserContext);
45+
return browserContext;
46+
}
47+
48+
private static Browser.NewContextOptions getContextOptions(Options options) {
49+
Browser.NewContextOptions contextOptions = Utils.clone(options.getContextOption());
50+
if (contextOptions == null) {
51+
contextOptions = new Browser.NewContextOptions();
52+
}
53+
54+
if (options.getBaseUrl() != null) {
55+
contextOptions.setBaseURL(options.getBaseUrl());
56+
}
57+
58+
if (options.getStorageStatePath() != null) {
59+
contextOptions.setStorageStatePath(options.getStorageStatePath());
60+
}
61+
62+
if (options.getViewportSize() != null) {
63+
contextOptions.setViewportSize(options.getViewportSize());
64+
}
65+
66+
return contextOptions;
67+
}
68+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.microsoft.playwright.junit.impl;
2+
3+
import com.microsoft.playwright.Browser;
4+
import com.microsoft.playwright.BrowserType;
5+
import com.microsoft.playwright.Playwright;
6+
import com.microsoft.playwright.PlaywrightException;
7+
import com.microsoft.playwright.impl.Utils;
8+
import com.microsoft.playwright.junit.Options;
9+
import org.junit.jupiter.api.extension.*;
10+
11+
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isParameterSupported;
12+
13+
public class BrowserExtension implements ParameterResolver, AfterAllCallback {
14+
private static final ThreadLocal<Browser> threadLocalBrowser = new ThreadLocal<>();
15+
16+
@Override
17+
public void afterAll(ExtensionContext extensionContext) {
18+
Browser browser = threadLocalBrowser.get();
19+
threadLocalBrowser.remove();
20+
if (browser != null) {
21+
browser.close();
22+
}
23+
}
24+
25+
@Override
26+
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
27+
return isParameterSupported(parameterContext, extensionContext, Browser.class);
28+
}
29+
30+
@Override
31+
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
32+
return getOrCreateBrowser(extensionContext);
33+
}
34+
35+
static Browser getOrCreateBrowser(ExtensionContext extensionContext) {
36+
Browser browser = threadLocalBrowser.get();
37+
if (browser != null) {
38+
return browser;
39+
}
40+
41+
Options options = OptionsExtension.getOptions(extensionContext);
42+
Playwright playwright = PlaywrightExtension.getOrCreatePlaywright(extensionContext);
43+
BrowserType.LaunchOptions launchOptions = getLaunchOptions(options);
44+
45+
switch (options.getBrowserName()) {
46+
case "webkit":
47+
browser = playwright.webkit().launch(launchOptions);
48+
break;
49+
case "firefox":
50+
browser = playwright.firefox().launch(launchOptions);
51+
break;
52+
case "chromium":
53+
browser = playwright.chromium().launch(launchOptions);
54+
break;
55+
default:
56+
throw new PlaywrightException("Invalid browser name.");
57+
}
58+
59+
threadLocalBrowser.set(browser);
60+
return browser;
61+
}
62+
63+
private static BrowserType.LaunchOptions getLaunchOptions(Options options) {
64+
BrowserType.LaunchOptions launchOptions = Utils.clone(options.getLaunchOptions());
65+
if (launchOptions == null) {
66+
launchOptions = new BrowserType.LaunchOptions();
67+
}
68+
69+
if (options.isHeadless() != null) {
70+
launchOptions.setHeadless(options.isHeadless());
71+
}
72+
73+
if (options.getChannel() != null) {
74+
options.setChannel(options.getChannel());
75+
}
76+
77+
return launchOptions;
78+
}
79+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.microsoft.playwright.junit.impl;
2+
3+
import com.microsoft.playwright.junit.UsePlaywright;
4+
import org.junit.jupiter.api.extension.ExtensionContext;
5+
import org.junit.jupiter.api.extension.ParameterContext;
6+
import org.junit.platform.commons.support.AnnotationSupport;
7+
8+
import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;
9+
10+
class ExtensionUtils {
11+
static boolean hasUsePlaywrightAnnotation(ExtensionContext extensionContext) {
12+
return AnnotationSupport.isAnnotated(extensionContext.getTestClass(), UsePlaywright.class);
13+
}
14+
15+
static UsePlaywright getUsePlaywrightAnnotation(ExtensionContext extensionContext) {
16+
return findAnnotation(extensionContext.getTestClass(), UsePlaywright.class).get();
17+
}
18+
19+
static boolean isClassHook(ExtensionContext extensionContext) {
20+
return !extensionContext.getTestMethod().isPresent();
21+
}
22+
23+
static boolean isParameterSupported(ParameterContext parameterContext, ExtensionContext extensionContext, Class<?> subject) {
24+
if (!hasUsePlaywrightAnnotation(extensionContext)) {
25+
return false;
26+
}
27+
Class<?> clazz = parameterContext.getParameter().getType();
28+
return subject.equals(clazz);
29+
}
30+
}

0 commit comments

Comments
 (0)