Skip to content

Commit

Permalink
Release/v2.0 (#14)
Browse files Browse the repository at this point in the history
* get main in sync with develop (#10) (#11)

* Update to v2 (#13)

* Simplify parameter resolvers and add APIRequestContext

* Add tests for APITestContext. add ability to declare rest config at parameter level

* Store APIContext after creating

* Update readme

* update pom to 2.0

* Bump version to v2.0
  • Loading branch information
uchagani authored Apr 11, 2022
1 parent 2e0dbb7 commit fbe96c4
Show file tree
Hide file tree
Showing 36 changed files with 522 additions and 234 deletions.
66 changes: 53 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,34 @@
isolated environments for each test and exposes Playwright-related objects as test parameters for you to use in your
tests.

## Note

`junit-playwright` has recently been updated to v2.0. This brings some breaking changes. Please see the documentation in
the wiki for v1 docs. It is recommended to upgrade to v2.0. Migration help can be found at the end of this readme.

## Installation

```xml

<dependency>
<groupId>io.github.uchagani</groupId>
<artifactId>junit-playwright</artifactId>
<version>1.1</version>
<version>2.0</version>
</dependency>
```

## Getting Started

`junit-playwright` will inject Playwright objects in your tests.
`junit-playwright` will inject Playwright objects in your tests. There are two different interfaces that you can implement based on the type of testing that you want to do: Browser or API.

## Browser Testing

### Create a config class

Create a class and implement the `PlaywrightConfig` interface
Create a class and implement the `PlaywrightBrowserConfig` interface

```java
public class DefaultConfig implements PlaywrightConfig {
public class DefaultBrowserConfig implements PlaywrightBrowserConfig {

@Override
public BrowserConfig getBrowserConfig() {
Expand All @@ -35,27 +42,25 @@ public class DefaultConfig implements PlaywrightConfig {
}
```

`PlaywrightConfig` has one method: `getBrowserConfig`. Through the `BrowserConfig` object you can specify your
`PlaywrightBrowserConfig` has one method: `getBrowserConfig`. Through the `BrowserConfig` object you can specify your
playwright-related config. The API is similar to playwright-java. All the options that you would specify to initialize
Playwright, Browser, BrowserContext, or Page you can do via `BrowserConfig` object.

### Writing tests

To inject Playwright objects into your test there are two parts:

1. Add the `@InjectPlaywright` annotation to your test class and specify your config class.
1. Add the `@UseBrowserConfig` annotation to your test class and specify your config class.
2. Add the Playwright object that you need to interact with in your test as a test parameter.

```java

@InjectPlaywright(DefaultConfig.class)
@UseBrowserConfig(DefaultBrowserConfig.class)
public class InjectBrowserTests {

@Test
public void someTest(Page page) {
page.navigate("https://playwright.dev/java/");
}

}
```

Expand All @@ -66,6 +71,33 @@ public class InjectBrowserTests {
* [BrowserContext](https://playwright.dev/java/docs/api/class-browsercontext)
* [Page](https://playwright.dev/java/docs/api/class-page)

## API Testing

Two use Playwright's [APIRequestContext](https://playwright.dev/java/docs/api/class-apirequestcontext) you need to implement the `PlaywrightRestConfig` interface:

```java
public class DefaultRestConfig implements PlaywrightRestConfig {
@Override
public RestConfig getRestConfig() {
return new RestConfig();
}
}
```

Then you can use this config in your tests:

```java
@UseRestConfig(DefaultRestConfig.class)
public class APIRequestContextTests {
@Test
public void someAPITest(APIRequestContext request) {
request.get("https://api.coindesk.com/v1/bpi/currentprice.json");
}
}
```

`@UseRestConfig` annotation can be used either at the class level, method level, or parameter level.

## Running tests in parallel

`playwright-junit` makes it easy to run tests in parallel. Each test will get an isolated Playwright environment. All
Expand All @@ -90,27 +122,35 @@ configs and create tests.

## Advanced

You can override the config for a particular test method by adding the `@InjectPlaywright` annotation over a test
You can override the config for a particular test method by adding the `@UseBrowserConfig` annotation over a test
method:

```java

@InjectPlaywright(DefaultConfig.class)
@UseBrowserConfig(DefaultConfig.class)
public class InjectBrowserTests {
@Test
public void createAChromeBrowser(Browser browser) {
//Browser is configured with the `DefaultConfig` specified at the class level
}

@Test
@InjectPlaywright(OverrideConfig.class)
@UseBrowserConfig(OverrideConfig.class)
public void createAFirefoxBrowser(Browser browser) {
//For this test, use the browser configured in the `OverrideConfig` class
}
}
```

## Requirements

* Java 8+
* Playwright 1.18.0+
* JUnit 5.6+
* JUnit 5.6+

## Migrating from v1

To migrate to v2.x from v1.x, there are a couple of changes that you need to make:

1. Change `PlaywrightConfig` to `PlaywrightBrowserConfig`.
2. Change `@InjectPlaywright` to `@UserBrowserConfig`.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.github.uchagani</groupId>
<artifactId>junit-playwright</artifactId>
<version>1.3</version>
<version>2.0</version>

<name>junit-playwright</name>
<description>junit-playwright allows you to easily run Playwright-Java tests in parallel</description>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.github.uchagani.jp;

import com.microsoft.playwright.APIRequest;
import com.microsoft.playwright.APIRequestContext;
import com.microsoft.playwright.Playwright;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;

import static io.github.uchagani.jp.AnnotationUtils.isAnnotationPresentOnClassOrMethod;
import static io.github.uchagani.jp.ExtensionUtils.*;

public class APIRequestContextParameterResolver implements ParameterResolver {
private static final String id = ".apiRequestContext.";

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
Class<?> parameterType = parameterContext.getParameter().getType();
return parameterType.equals(APIRequestContext.class) && (parameterContext.isAnnotated(UseRestConfig.class)
|| isAnnotationPresentOnClassOrMethod(extensionContext, UseRestConfig.class));
}

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

public static void closeAPIRequestContext(ExtensionContext extensionContext) {
APIRequestContext apiRequestContext = getObjectFromStore(extensionContext, id, APIRequestContext.class);
if (apiRequestContext != null) {
apiRequestContext.dispose();
}
}

private static Playwright getPlaywright(ExtensionContext extensionContext) {
try {
return PlaywrightParameterResolver.getPlaywright(extensionContext);
} catch (Exception ex) {
Playwright playwright = Playwright.create();
PlaywrightParameterResolver.savePlaywrightInStore(extensionContext, playwright);
return playwright;
}
}

public static APIRequestContext getAPIRequestContext(ParameterContext parameterContext, ExtensionContext extensionContext) {
APIRequestContext apiRequestContext = getObjectFromStore(extensionContext, id, APIRequestContext.class);
if (apiRequestContext == null) {
RestConfig restConfig = getRestConfig(parameterContext, extensionContext);
Playwright playwright = getPlaywright(extensionContext);
apiRequestContext = createAPIRequestContext(playwright, restConfig);
saveAPIRequestContextInStore(extensionContext, apiRequestContext);
}
return apiRequestContext;
}

public static void saveAPIRequestContextInStore(ExtensionContext extensionContext, APIRequestContext apiRequestContext) {
saveObjectInStore(extensionContext, id, apiRequestContext);
}

private static APIRequestContext createAPIRequestContext(Playwright playwright, RestConfig restConfig) {
APIRequest.NewContextOptions options = restConfig.getAPIRequestContextOptions();
if (options == null) {
return playwright.request().newContext();
}
return playwright.request().newContext(options);
}
}
27 changes: 27 additions & 0 deletions src/main/java/io/github/uchagani/jp/AnnotationUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.github.uchagani.jp;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;

import java.lang.annotation.Annotation;

public class AnnotationUtils {
static boolean isAnnotationPresentOnClassOrMethod(ExtensionContext extensionContext, Class<? extends Annotation> annotation) {
return extensionContext.getRequiredTestClass().isAnnotationPresent(annotation) ||
extensionContext.getRequiredTestMethod().isAnnotationPresent(annotation);
}

static void ensureAnnotationIsPresentOnClassOrMethod(ExtensionContext extensionContext, Class<? extends Annotation> annotation) {
if (!isAnnotationPresentOnClassOrMethod(extensionContext, annotation)) {
String message = String.format("Class or Method is not annotated with %s", annotation.getName());
throw new RuntimeException(message);
}
}

static void ensureAnnotationIsPresentOnClassOrMethodOrParameter(ParameterContext parameterContext, ExtensionContext extensionContext, Class<? extends Annotation> annotation) {
if (!isAnnotationPresentOnClassOrMethod(extensionContext, annotation) && !parameterContext.isAnnotated(annotation)) {
String message = String.format("Class or Method or Parameter is not annotated with %s", annotation.getName());
throw new RuntimeException(message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,24 @@
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;

import static io.github.uchagani.jp.AnnotationUtils.isAnnotationPresentOnClassOrMethod;
import static io.github.uchagani.jp.ExtensionUtils.*;

public class BrowserContextParameterResolver implements ParameterResolver {
private static final String browserContextId = ".browserContext.";
private static final String id = ".browserContext.";

public static BrowserContext getBrowserContext(ExtensionContext extensionContext) {
String id = extensionContext.getUniqueId() + browserContextId;
BrowserContext browserContext = extensionContext.getStore(PlaywrightParameterResolver.junitPlaywrightNamespace).get(id, BrowserContext.class);
BrowserContext browserContext = getObjectFromStore(extensionContext, id, BrowserContext.class);

if (browserContext == null) {
Browser browser = BrowserParameterResolver.getBrowser(extensionContext);
BrowserConfig browserConfig = PlaywrightParameterResolver.getPlaywrightConfig(extensionContext).getBrowserConfig();
BrowserConfig browserConfig = getBrowserConfig(extensionContext);

if (browserConfig.getCreateMethod() == BrowserCreateMethod.LAUNCH_PERSISTENT_CONTEXT) {
return getBrowserContext(extensionContext);
}

Browser.NewContextOptions newContextOptions = browserConfig.getNewContextOptions();

if (newContextOptions == null) {
browserContext = browser.newContext();
} else {
browserContext = browser.newContext(newContextOptions);
}
browserContext = createBrowserContext(browser, browserConfig);

if (browserConfig.getEnableTracing()) {
browserContext.tracing().start(getTraceStartOptions());
Expand All @@ -42,25 +38,32 @@ public static BrowserContext getBrowserContext(ExtensionContext extensionContext
}

public static void saveBrowserContextInStore(ExtensionContext extensionContext, BrowserContext browserContext) {
extensionContext.getStore(PlaywrightParameterResolver.junitPlaywrightNamespace).put(extensionContext.getUniqueId() + browserContextId, browserContext);
saveObjectInStore(extensionContext, id, browserContext);
}

private static BrowserContext createBrowserContext(Browser browser, BrowserConfig config) {
Browser.NewContextOptions newContextOptions = config.getNewContextOptions();
if (newContextOptions == null) {
return browser.newContext();
} else {
return browser.newContext(newContextOptions);
}
}

private static Tracing.StartOptions getTraceStartOptions() {
Tracing.StartOptions startOptions = new Tracing.StartOptions()
.setSnapshots(true)
.setScreenshots(true);

if (System.getenv("PLAYWRIGHT_JAVA_SRC") != null) {
startOptions.setSources(true);
}

return startOptions;
}

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
Class<?> parameterType = parameterContext.getParameter().getType();
return PlaywrightParameterResolver.injectPlaywrightAnnotationPresent(extensionContext) && parameterType.equals(BrowserContext.class);
return isAnnotationPresentOnClassOrMethod(extensionContext, UseBrowserConfig.class) && parameterType.equals(BrowserContext.class);
}

@Override
Expand Down
Loading

0 comments on commit fbe96c4

Please sign in to comment.