1
0
mirror of synced 2026-05-23 19:23:20 +00:00

Compare commits

..

5 Commits

Author SHA1 Message Date
Gerard Mista 59a8490540 Artifact should have same version as release. (#974) 2022-07-01 14:21:42 -07:00
Yury Semikhatsky a127bfefb3 cherry-pick(#978,#984): recent test fixes (#985) 2022-06-30 14:47:57 -07:00
Yury Semikhatsky 06082438fe cherry-pick(#982): feat: roll driver to 1.23.1-beta, implement routeFromHar.update (#983) 2022-06-30 13:17:18 -07:00
Yury Semikhatsky b8bd59a55c chore: mark 1.23.0 (#971) 2022-06-28 09:14:00 -07:00
Yury Semikhatsky e42d7bdc9a cherry-pick(#969): feat: support ignoreCase option (#970) 2022-06-28 08:50:34 -07:00
51 changed files with 88 additions and 377 deletions
+2 -4
View File
@@ -1,5 +1,3 @@
# text files must be lf for golden file tests to work
* text=auto eol=lf
# make project show as TS on GitHub
*.js linguist-detectable=false
*.txt eol=lf
*.json eol=lf
+4 -4
View File
@@ -11,9 +11,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->104.0.5112.48<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->16.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->102.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->104.0.5112.20<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->15.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->100.0.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Headless execution is supported for all the browsers on all platforms. Check out [system requirements](https://playwright.dev/java/docs/next/intro/#system-requirements) for details.
@@ -43,7 +43,7 @@ To run Playwright simply add following dependency to your Maven project:
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.17.0</version>
<version>1.23.0</version>
</dependency>
```
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.24.1</version>
<version>1.23.0</version>
</parent>
<artifactId>driver-bundle</artifactId>
@@ -24,6 +24,7 @@ import org.junit.jupiter.api.io.TempDir;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -62,7 +63,7 @@ public class TestInstall {
}
@Test
void shouldThrowWhenBrowserPathIsInvalid(@TempDir Path tmpDir) throws NoSuchFieldException, IllegalAccessException {
void shouldThrowWhenBrowserPathIsInvalid(@TempDir Path tmpDir) throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
Map<String,String> env = new HashMap<>();
// On macOS we can only use 127.0.0.1, so pick unused port instead.
@@ -113,20 +114,12 @@ public class TestInstall {
}
@Test
void playwrightDriverAlternativeImpl() throws NoSuchFieldException, IllegalAccessException {
// Reset instance field value to null for the test.
Field field = Driver.class.getDeclaredField("instance");
field.setAccessible(true);
Object value = field.get(Driver.class);
field.set(Driver.class, null);
void playwrightDriverAlternativeImpl() {
System.setProperty("playwright.driver.impl", "com.microsoft.playwright.impl.AlternativeDriver");
RuntimeException thrown =
assertThrows(
RuntimeException.class,
() -> Driver.ensureDriverInstalled(Collections.emptyMap(), false));
assertEquals("Failed to create driver", thrown.getMessage());
field.set(Driver.class, value);
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.24.1</version>
<version>1.23.0</version>
</parent>
<artifactId>driver</artifactId>
+2 -2
View File
@@ -6,7 +6,7 @@
<groupId>org.example</groupId>
<artifactId>examples</artifactId>
<version>1.24.1</version>
<version>1.23.0</version>
<name>Playwright Client Examples</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -15,7 +15,7 @@
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.22.0</version>
<version>1.23.0</version>
</dependency>
</dependencies>
<build>
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.24.1</version>
<version>1.23.0</version>
</parent>
<artifactId>playwright</artifactId>
@@ -1075,10 +1075,6 @@ public interface Browser extends AutoCloseable {
* <p> In case this browser is connected to, clears all created contexts belonging to this browser and disconnects from the
* browser server.
*
* <p> <strong>NOTE:</strong> This is similar to force quitting the browser. Therefore, you should call {@link BrowserContext#close
* BrowserContext.close()} on any {@code BrowserContext}'s you explicitly created earlier with {@link Browser#newContext
* Browser.newContext()} **before** calling {@link Browser#close Browser.close()}.
*
* <p> The {@code Browser} object itself is considered to be disposed and cannot be used anymore.
*/
void close();
@@ -1098,11 +1094,6 @@ public interface Browser extends AutoCloseable {
boolean isConnected();
/**
* Creates a new browser context. It won't share cookies/cache with other browser contexts.
*
* <p> <strong>NOTE:</strong> If directly using this method to create {@code BrowserContext}s, it is best practice to explicilty close the returned context
* via {@link BrowserContext#close BrowserContext.close()} when your code is done with the {@code BrowserContext}, and before
* calling {@link Browser#close Browser.close()}. This will ensure the {@code context} is closed gracefully and any
* artifacts—like HARs and videos—are fully flushed and saved.
* <pre>{@code
* Browser browser = playwright.firefox().launch(); // Or 'chromium' or 'webkit'.
* // Create a new incognito browser context.
@@ -1110,10 +1101,6 @@ public interface Browser extends AutoCloseable {
* // Create a new page in a pristine context.
* Page page = context.newPage();
* page.navigate('https://example.com');
*
* // Gracefull close up everything
* context.close();
* browser.close();
* }</pre>
*/
default BrowserContext newContext() {
@@ -1121,11 +1108,6 @@ public interface Browser extends AutoCloseable {
}
/**
* Creates a new browser context. It won't share cookies/cache with other browser contexts.
*
* <p> <strong>NOTE:</strong> If directly using this method to create {@code BrowserContext}s, it is best practice to explicilty close the returned context
* via {@link BrowserContext#close BrowserContext.close()} when your code is done with the {@code BrowserContext}, and before
* calling {@link Browser#close Browser.close()}. This will ensure the {@code context} is closed gracefully and any
* artifacts—like HARs and videos—are fully flushed and saved.
* <pre>{@code
* Browser browser = playwright.firefox().launch(); // Or 'chromium' or 'webkit'.
* // Create a new incognito browser context.
@@ -1133,10 +1115,6 @@ public interface Browser extends AutoCloseable {
* // Create a new page in a pristine context.
* Page page = context.newPage();
* page.navigate('https://example.com');
*
* // Gracefull close up everything
* context.close();
* browser.close();
* }</pre>
*/
BrowserContext newContext(NewContextOptions options);
@@ -67,7 +67,7 @@ public interface BrowserContext extends AutoCloseable {
* done and its response has started loading in the popup.
* <pre>{@code
* Page newPage = context.waitForPage(() -> {
* page.locator("a[target=_blank]").click();
* page.click("a[target=_blank]");
* });
* System.out.println(newPage.evaluate("location.href"));
* }</pre>
@@ -190,7 +190,7 @@ public interface BrowserContext extends AutoCloseable {
public Boolean update;
/**
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
* will be served from the HAR file. If not specified, all requests are served from the HAR file.
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
*/
public Object url;
@@ -215,7 +215,7 @@ public interface BrowserContext extends AutoCloseable {
}
/**
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
* will be served from the HAR file. If not specified, all requests are served from the HAR file.
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
*/
public RouteFromHAROptions setUrl(String url) {
this.url = url;
@@ -223,7 +223,7 @@ public interface BrowserContext extends AutoCloseable {
}
/**
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
* will be served from the HAR file. If not specified, all requests are served from the HAR file.
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
*/
public RouteFromHAROptions setUrl(Pattern url) {
this.url = url;
@@ -404,7 +404,7 @@ public interface BrowserContext extends AutoCloseable {
* "</script>\n" +
* "<button onclick=\"onClick()\">Click me</button>\n" +
* "<div></div>");
* page.locator("button").click();
* page.click("button");
* }
* }
* }
@@ -463,7 +463,7 @@ public interface BrowserContext extends AutoCloseable {
* "</script>\n" +
* "<button onclick=\"onClick()\">Click me</button>\n" +
* "<div></div>");
* page.locator("button").click();
* page.click("button");
* }
* }
* }
@@ -533,7 +533,7 @@ public interface BrowserContext extends AutoCloseable {
* "</script>\n" +
* "<button onclick=\"onClick()\">Click me</button>\n" +
* "<div></div>\n");
* page.locator("button").click();
* page.click("button");
* }
* }
* }
@@ -27,14 +27,14 @@ import java.nio.file.Path;
* <p> Download event is emitted once the download starts. Download path becomes available once download completes:
* <pre>{@code
* // wait for download to start
* Download download = page.waitForDownload(() -> page.locator("a").click());
* Download download = page.waitForDownload(() -> page.click("a"));
* // wait for download to complete
* Path path = download.path();
* }</pre>
* <pre>{@code
* // wait for download to start
* Download download = page.waitForDownload(() -> {
* page.locator("a").click();
* page.click("a");
* });
* // wait for download to complete
* Path path = download.path();
@@ -1214,7 +1214,7 @@ public interface ElementHandle extends JSHandle {
* This method returns the bounding box of the element, or {@code null} if the element is not visible. The bounding box is
* calculated relative to the main frame viewport - which is usually the same as the browser window.
*
* <p> Scrolling affects the returned bounding box, similarly to <a
* <p> Scrolling affects the returned bonding box, similarly to <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect">Element.getBoundingClientRect</a>.
* That means {@code x} and/or {@code y} may be negative.
*
@@ -22,7 +22,7 @@ import java.nio.file.Path;
/**
* {@code FileChooser} objects are dispatched by the page in the {@link Page#onFileChooser Page.onFileChooser()} event.
* <pre>{@code
* FileChooser fileChooser = page.waitForFileChooser(() -> page.locator("upload").click());
* FileChooser fileChooser = page.waitForFileChooser(() -> page.click("upload"));
* fileChooser.setFiles(Paths.get("myfile.pdf"));
* }</pre>
*/
@@ -2366,25 +2366,9 @@ public interface Frame {
* @param eventInit Optional event-specific initialization properties.
*/
void dispatchEvent(String selector, String type, Object eventInit, DispatchEventOptions options);
/**
*
*
* @param source A selector to search for an element to drag. If there are multiple elements satisfying the selector, the first will be
* used. See <a href="https://playwright.dev/java/docs/selectors">working with selectors</a> for more details.
* @param target A selector to search for an element to drop onto. If there are multiple elements satisfying the selector, the first will
* be used. See <a href="https://playwright.dev/java/docs/selectors">working with selectors</a> for more details.
*/
default void dragAndDrop(String source, String target) {
dragAndDrop(source, target, null);
}
/**
*
*
* @param source A selector to search for an element to drag. If there are multiple elements satisfying the selector, the first will be
* used. See <a href="https://playwright.dev/java/docs/selectors">working with selectors</a> for more details.
* @param target A selector to search for an element to drop onto. If there are multiple elements satisfying the selector, the first will
* be used. See <a href="https://playwright.dev/java/docs/selectors">working with selectors</a> for more details.
*/
void dragAndDrop(String source, String target, DragAndDropOptions options);
/**
* Returns the return value of {@code expression}.
@@ -1636,7 +1636,7 @@ public interface Locator {
* This method returns the bounding box of the element, or {@code null} if the element is not visible. The bounding box is
* calculated relative to the main frame viewport - which is usually the same as the browser window.
*
* <p> Scrolling affects the returned bounding box, similarly to <a
* <p> Scrolling affects the returned bonding box, similarly to <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect">Element.getBoundingClientRect</a>.
* That means {@code x} and/or {@code y} may be negative.
*
@@ -1657,7 +1657,7 @@ public interface Locator {
* This method returns the bounding box of the element, or {@code null} if the element is not visible. The bounding box is
* calculated relative to the main frame viewport - which is usually the same as the browser window.
*
* <p> Scrolling affects the returned bounding box, similarly to <a
* <p> Scrolling affects the returned bonding box, similarly to <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect">Element.getBoundingClientRect</a>.
* That means {@code x} and/or {@code y} may be negative.
*
@@ -2013,7 +2013,7 @@ public interface Page extends AutoCloseable {
public Boolean update;
/**
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
* will be served from the HAR file. If not specified, all requests are served from the HAR file.
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
*/
public Object url;
@@ -2038,7 +2038,7 @@ public interface Page extends AutoCloseable {
}
/**
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
* will be served from the HAR file. If not specified, all requests are served from the HAR file.
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
*/
public RouteFromHAROptions setUrl(String url) {
this.url = url;
@@ -2046,7 +2046,7 @@ public interface Page extends AutoCloseable {
}
/**
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
* will be served from the HAR file. If not specified, all requests are served from the HAR file.
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
*/
public RouteFromHAROptions setUrl(Pattern url) {
this.url = url;
@@ -3581,25 +3581,9 @@ public interface Page extends AutoCloseable {
* @param eventInit Optional event-specific initialization properties.
*/
void dispatchEvent(String selector, String type, Object eventInit, DispatchEventOptions options);
/**
*
*
* @param source A selector to search for an element to drag. If there are multiple elements satisfying the selector, the first will be
* used. See <a href="https://playwright.dev/java/docs/selectors">working with selectors</a> for more details.
* @param target A selector to search for an element to drop onto. If there are multiple elements satisfying the selector, the first will
* be used. See <a href="https://playwright.dev/java/docs/selectors">working with selectors</a> for more details.
*/
default void dragAndDrop(String source, String target) {
dragAndDrop(source, target, null);
}
/**
*
*
* @param source A selector to search for an element to drag. If there are multiple elements satisfying the selector, the first will be
* used. See <a href="https://playwright.dev/java/docs/selectors">working with selectors</a> for more details.
* @param target A selector to search for an element to drop onto. If there are multiple elements satisfying the selector, the first will
* be used. See <a href="https://playwright.dev/java/docs/selectors">working with selectors</a> for more details.
*/
void dragAndDrop(String source, String target, DragAndDropOptions options);
/**
* This method changes the {@code CSS media type} through the {@code media} argument, and/or the {@code "prefers-colors-scheme"} media
@@ -280,7 +280,7 @@ public interface Route {
void resume(ResumeOptions options);
/**
* When several routes match the given pattern, they run in the order opposite to their registration. That way the last
* registered route can always override all the previous ones. In the example below, request will be handled by the
* registered route can always override all the previos ones. In the example below, request will be handled by the
* bottom-most handler first, then it'll fall back to the previous one and in the end will be aborted by the first
* registered route.
* <pre>{@code
@@ -341,7 +341,7 @@ public interface Route {
}
/**
* When several routes match the given pattern, they run in the order opposite to their registration. That way the last
* registered route can always override all the previous ones. In the example below, request will be handled by the
* registered route can always override all the previos ones. In the example below, request will be handled by the
* bottom-most handler first, then it'll fall back to the previous one and in the end will be aborted by the first
* registered route.
* <pre>{@code
@@ -63,7 +63,7 @@ public interface Selectors {
* // Use the selector prefixed with its name.
* Locator button = page.locator("tag=button");
* // Combine it with other selector engines.
* page.locator("tag=div >> text=\"Click me\"").click();
* page.click("tag=div >> text=\"Click me\"");
* // Can use it in any methods supporting selectors.
* int buttonCount = (int) page.locator("tag=button").count();
* browser.close();
@@ -98,7 +98,7 @@ public interface Selectors {
* // Use the selector prefixed with its name.
* Locator button = page.locator("tag=button");
* // Combine it with other selector engines.
* page.locator("tag=div >> text=\"Click me\"").click();
* page.click("tag=div >> text=\"Click me\"");
* // Can use it in any methods supporting selectors.
* int buttonCount = (int) page.locator("tag=button").count();
* browser.close();
@@ -131,7 +131,7 @@ public interface Selectors {
* // Use the selector prefixed with its name.
* Locator button = page.locator("tag=button");
* // Combine it with other selector engines.
* page.locator("tag=div >> text=\"Click me\"").click();
* page.click("tag=div >> text=\"Click me\"");
* // Can use it in any methods supporting selectors.
* int buttonCount = (int) page.locator("tag=button").count();
* browser.close();
@@ -166,7 +166,7 @@ public interface Selectors {
* // Use the selector prefixed with its name.
* Locator button = page.locator("tag=button");
* // Combine it with other selector engines.
* page.locator("tag=div >> text=\"Click me\"").click();
* page.click("tag=div >> text=\"Click me\"");
* // Can use it in any methods supporting selectors.
* int buttonCount = (int) page.locator("tag=button").count();
* browser.close();
@@ -192,7 +192,7 @@ public interface Tracing {
* page.navigate("https://playwright.dev");
*
* context.tracing().startChunk();
* page.locator("text=Get Started").click();
* page.click("text=Get Started");
* // Everything between startChunk and stopChunk will be recorded in the trace.
* context.tracing().stopChunk(new Tracing.StopChunkOptions()
* .setPath(Paths.get("trace1.zip")));
@@ -219,7 +219,7 @@ public interface Tracing {
* page.navigate("https://playwright.dev");
*
* context.tracing().startChunk();
* page.locator("text=Get Started").click();
* page.click("text=Get Started");
* // Everything between startChunk and stopChunk will be recorded in the trace.
* context.tracing().stopChunk(new Tracing.StopChunkOptions()
* .setPath(Paths.get("trace1.zip")));
@@ -31,7 +31,7 @@ import java.util.regex.Pattern;
* @Test
* void statusBecomesSubmitted() {
* ...
* page.locator("#submit-button").click();
* page.click("#submit-button");
* assertThat(page.locator(".status")).hasText("Submitted");
* }
* }
@@ -664,11 +664,9 @@ public interface LocatorAssertions {
*/
void hasAttribute(String name, Pattern value, HasAttributeOptions options);
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a relaxed
* regular expression.
* Ensures the {@code Locator} points to an element with given CSS class.
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
@@ -682,11 +680,9 @@ public interface LocatorAssertions {
hasClass(expected, null);
}
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a relaxed
* regular expression.
* Ensures the {@code Locator} points to an element with given CSS class.
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
@@ -698,11 +694,9 @@ public interface LocatorAssertions {
*/
void hasClass(String expected, HasClassOptions options);
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a relaxed
* regular expression.
* Ensures the {@code Locator} points to an element with given CSS class.
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
@@ -716,11 +710,9 @@ public interface LocatorAssertions {
hasClass(expected, null);
}
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a relaxed
* regular expression.
* Ensures the {@code Locator} points to an element with given CSS class.
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
@@ -732,11 +724,9 @@ public interface LocatorAssertions {
*/
void hasClass(Pattern expected, HasClassOptions options);
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a relaxed
* regular expression.
* Ensures the {@code Locator} points to an element with given CSS class.
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
@@ -750,11 +740,9 @@ public interface LocatorAssertions {
hasClass(expected, null);
}
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a relaxed
* regular expression.
* Ensures the {@code Locator} points to an element with given CSS class.
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
@@ -766,11 +754,9 @@ public interface LocatorAssertions {
*/
void hasClass(String[] expected, HasClassOptions options);
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a relaxed
* regular expression.
* Ensures the {@code Locator} points to an element with given CSS class.
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
@@ -784,11 +770,9 @@ public interface LocatorAssertions {
hasClass(expected, null);
}
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a relaxed
* regular expression.
* Ensures the {@code Locator} points to an element with given CSS class.
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
@@ -31,7 +31,7 @@ import java.util.regex.Pattern;
* @Test
* void navigatesToLoginPage() {
* ...
* page.locator("#login").click();
* page.click("#login");
* assertThat(page).hasURL(Pattern.compile(".*\/login"));
* }
* }
@@ -120,7 +120,7 @@ public interface PageAssertions {
* assertThat(page).hasURL(".com");
* }</pre>
*
* @param urlOrRegExp Expected URL string or RegExp.
* @param urlOrRegExp Expected substring or RegExp.
*/
default void hasURL(String urlOrRegExp) {
hasURL(urlOrRegExp, null);
@@ -131,7 +131,7 @@ public interface PageAssertions {
* assertThat(page).hasURL(".com");
* }</pre>
*
* @param urlOrRegExp Expected URL string or RegExp.
* @param urlOrRegExp Expected substring or RegExp.
*/
void hasURL(String urlOrRegExp, HasURLOptions options);
/**
@@ -140,7 +140,7 @@ public interface PageAssertions {
* assertThat(page).hasURL(".com");
* }</pre>
*
* @param urlOrRegExp Expected URL string or RegExp.
* @param urlOrRegExp Expected substring or RegExp.
*/
default void hasURL(Pattern urlOrRegExp) {
hasURL(urlOrRegExp, null);
@@ -151,7 +151,7 @@ public interface PageAssertions {
* assertThat(page).hasURL(".com");
* }</pre>
*
* @param urlOrRegExp Expected URL string or RegExp.
* @param urlOrRegExp Expected substring or RegExp.
*/
void hasURL(Pattern urlOrRegExp, HasURLOptions options);
}
@@ -37,7 +37,7 @@ import com.microsoft.playwright.impl.PageAssertionsImpl;
* @Test
* void statusBecomesSubmitted() {
* ...
* page.locator("#submit-button").click();
* page.click("#submit-button");
* assertThat(page.locator(".status")).hasText("Submitted");
* }
* }
@@ -36,6 +36,7 @@ import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
import static com.microsoft.playwright.impl.Serialization.gson;
import static com.microsoft.playwright.impl.Utils.isSafeCloseError;
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.readAllBytes;
import static java.util.Arrays.asList;
@@ -85,7 +85,7 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
JsonObject json = sendMessage("connect", params).getAsJsonObject();
JsonPipe pipe = connection.getExistingObject(json.getAsJsonObject("pipe").get("guid").getAsString());
Connection connection = new Connection(pipe, this.connection.env);
Connection connection = new Connection(pipe);
PlaywrightImpl playwright = connection.initializePlaywright();
if (!playwright.initializer.has("preLaunchedBrowser")) {
try {
@@ -65,7 +65,6 @@ public class Connection {
isLogging = (debug != null) && debug.contains("pw:channel");
}
LocalUtils localUtils;
final Map<String, String> env;
class Root extends ChannelOwner {
Root(Connection connection) {
@@ -80,14 +79,13 @@ public class Connection {
}
}
Connection(Transport transport, Map<String, String> env) {
this.env = env;
Connection(Transport transport) {
if (isLogging) {
transport = new TransportLogger(transport);
}
this.transport = transport;
root = new Root(this);
stackTraceCollector = StackTraceCollector.createFromEnv(env);
stackTraceCollector = StackTraceCollector.createFromEnv();
}
boolean isCollectingStacks() {
@@ -26,7 +26,6 @@ import java.nio.file.Path;
import java.util.Base64;
import java.util.Map;
import static com.microsoft.playwright.impl.LoggingSupport.isApiLoggingEnabled;
import static com.microsoft.playwright.impl.LoggingSupport.logApi;
import static com.microsoft.playwright.impl.Serialization.fromNameValues;
import static com.microsoft.playwright.impl.Serialization.gson;
@@ -67,9 +66,7 @@ public class HARRouter {
String action = response.get("action").getAsString();
if ("redirect".equals(action)) {
String redirectURL = response.get("redirectURL").getAsString();
if (isApiLoggingEnabled()) {
logApi("HAR: " + route.request().url() + " redirected to " + redirectURL);
}
logApi("HAR: " + route.request().url() + " redirected to " + redirectURL);
((RouteImpl) route).redirectNavigationRequest(redirectURL);
return;
}
@@ -86,9 +83,7 @@ public class HARRouter {
}
if ("error".equals(action)) {
if (isApiLoggingEnabled()) {
logApi("HAR: " + response.get("message").getAsString());
}
logApi("HAR: " + response.get("message").getAsString());
// Report the error, but fall through to the default handler.
}
@@ -67,8 +67,7 @@ class LocatorImpl implements Locator {
if (options.hasText != null) {
if (options.hasText instanceof Pattern) {
Pattern pattern = (Pattern) options.hasText;
String jsRegex = "/" + pattern.pattern() + "/" + toJsRegexFlags(pattern);
selector += " >> has=" + gson().toJson("text=" + jsRegex);
selector += " >> :scope:text-matches(" + escapeWithQuotes(pattern.pattern()) + ", \"" + toJsRegexFlags(pattern) + "\")";
} else if (options.hasText instanceof String) {
String text = (String) options.hasText;
selector += " >> :scope:has-text(" + escapeWithQuotes(text) + ")";
@@ -60,10 +60,6 @@ class LoggingSupport {
System.err.println(timestamp + " " + message);
}
static boolean isApiLoggingEnabled() {
return isEnabled;
}
static void logApi(String message) {
// This matches log format produced by the server.
logWithTimestamp("pw:api " + message);
@@ -43,7 +43,7 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
pb.environment().putAll(env);
Driver.setRequiredEnvironmentVariables(pb);
Process p = pb.start();
Connection connection = new Connection(new PipeTransport(p.getInputStream(), p.getOutputStream()), env);
Connection connection = new Connection(new PipeTransport(p.getInputStream(), p.getOutputStream()));
PlaywrightImpl result = connection.initializePlaywright();
result.driverProcess = p;
result.initSharedSelectors(null);
@@ -31,7 +31,6 @@ class SerializedValue{
// Possible values: { 'null, 'undefined, 'NaN, 'Infinity, '-Infinity, '-0 }
String v;
String d;
String u;
public static class R {
String p;
String f;
@@ -39,6 +39,9 @@ class Router {
}
boolean handle(RouteImpl route) {
if (times != null && times <= 0) {
return false;
}
if (!matcher.test(route.request().url())) {
return false;
}
@@ -28,19 +28,15 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Instant;
import java.util.*;
import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
import static com.microsoft.playwright.impl.Utils.fromJsRegexFlags;
class Serialization {
private static final Gson gson = new GsonBuilder().disableHtmlEscaping()
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(SameSiteAttribute.class, new SameSiteAdapter().nullSafe())
.registerTypeAdapter(BrowserChannel.class, new ToLowerCaseAndDashSerializer<BrowserChannel>())
.registerTypeAdapter(ColorScheme.class, new ToLowerCaseAndDashSerializer<ColorScheme>())
@@ -148,14 +144,6 @@ class Serialization {
result.n = (Integer) value;
} else if (value instanceof String) {
result.s = (String) value;
} else if (value instanceof Date) {
result.d = ((Date)value).toInstant().toString();
} else if (value instanceof URL) {
result.u = ((URL)value).toString();
} else if (value instanceof Pattern) {
result.r = new SerializedValue.R();
result.r.p = ((Pattern)value).pattern();
result.r.f = toJsRegexFlags(((Pattern)value));
} else {
HashableValue mapKey = new HashableValue(value);
Integer id = valueToId.get(mapKey);
@@ -219,17 +207,6 @@ class Serialization {
return (T) value.b;
if (value.s != null)
return (T) value.s;
if (value.u != null) {
try {
return (T)(new URL(value.u));
} catch (MalformedURLException e) {
throw new PlaywrightException("Unexpected value: " + value.u, e);
}
}
if (value.d != null)
return (T)(Date.from(Instant.parse(value.d)));
if (value.r != null)
return (T)(Pattern.compile(value.r.p, fromJsRegexFlags(value.r.f)));
if (value.v != null) {
switch (value.v) {
case "undefined":
@@ -28,25 +28,18 @@ import java.util.*;
import java.util.stream.Collectors;
class StackTraceCollector {
static final String PLAYWRIGHT_JAVA_SRC = "PLAYWRIGHT_JAVA_SRC";
private final List<Path> srcDirs;
private final Map<Path, String> classToSourceCache = new HashMap<>();
static StackTraceCollector createFromEnv(Map<String, String> env) {
String srcRoots = null;
if (env != null) {
srcRoots = env.get(PLAYWRIGHT_JAVA_SRC);
}
if (srcRoots == null) {
srcRoots = System.getenv(PLAYWRIGHT_JAVA_SRC);
}
static StackTraceCollector createFromEnv() {
String srcRoots = System.getenv("PLAYWRIGHT_JAVA_SRC");
if (srcRoots == null) {
return null;
}
List<Path> srcDirs = Arrays.stream(srcRoots.split(File.pathSeparator)).map(p -> Paths.get(p)).collect(Collectors.toList());
for (Path srcDir: srcDirs) {
if (!Files.exists(srcDir.toAbsolutePath())) {
throw new PlaywrightException("Source location specified in " + PLAYWRIGHT_JAVA_SRC + " doesn't exist: '" + srcDir.toAbsolutePath() + "'");
throw new PlaywrightException("Source location specified in PLAYWRIGHT_JAVA_SRC doesn't exist: '" + srcDir.toAbsolutePath() + "'");
}
}
return new StackTraceCollector(srcDirs);
@@ -307,18 +307,4 @@ class Utils {
}
return regexFlags;
}
static int fromJsRegexFlags(String regexFlags) {
int flags = 0;
if (regexFlags.contains("i")) {
flags |= Pattern.CASE_INSENSITIVE;
}
if (regexFlags.contains("s")) {
flags |= Pattern.DOTALL;
}
if (regexFlags.contains("m")) {
flags |= Pattern.MULTILINE;
}
return flags;
}
}
@@ -19,8 +19,7 @@ package com.microsoft.playwright.options;
import com.microsoft.playwright.impl.RequestOptionsImpl;
/**
* The {@code RequestOptions} allows to create form data to be sent via {@code APIRequestContext}. Playwright will automatically
* determine content type of the request.
* The {@code RequestOptions} allows to create form data to be sent via {@code APIRequestContext}.
* <pre>{@code
* context.request().post(
* "https://example.com/submit",
@@ -28,33 +27,6 @@ import com.microsoft.playwright.impl.RequestOptionsImpl;
* .setQueryParam("page", 1)
* .setData("My data"));
* }</pre>
*
* <p> **Uploading html form data**
*
* <p> {@code FormData} class can be used to send a form to the server, by default the request will use
* {@code application/x-www-form-urlencoded} encoding:
* <pre>{@code
* context.request().post("https://example.com/signup", RequestOptions.create().setForm(
* FormData.create()
* .set("firstName", "John")
* .set("lastName", "Doe")));
* }</pre>
*
* <p> You can also send files as fields of an html form. The data will be encoded using <a
* href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST">{@code multipart/form-data}</a>:
* <pre>{@code
* Path path = Paths.get("members.csv");
* APIResponse response = context.request().post("https://example.com/upload_members",
* RequestOptions.create().setMultipart(FormData.create().set("membersList", path)));
* }</pre>
*
* <p> Alternatively, you can build the file payload manually:
* <pre>{@code
* FilePayload filePayload = new FilePayload("members.csv", "text/csv",
* "Alice, 33\nJohn, 35\n".getBytes(StandardCharsets.UTF_8));
* APIResponse response = context.request().post("https://example.com/upload_members",
* RequestOptions.create().setMultipart(FormData.create().set("membersList", filePayload)));
* }</pre>
*/
public interface RequestOptions {
/**
@@ -72,12 +72,8 @@ public class TestBase {
return options;
}
Playwright.CreateOptions playwrightOptions() {
return null;
}
void initBrowserType() {
playwright = Playwright.create(playwrightOptions());
playwright = Playwright.create();
browserType = Utils.getBrowserTypeFromEnv(playwright);
}
@@ -351,7 +351,8 @@ public class TestBrowserContextHar extends TestBase {
assertEquals("3", page2.evaluate(fetchFunction, "3"));
assertEquals("3", page2.evaluate(fetchFunction, "3"));
try {
page2.evaluate(fetchFunction, "4");
Object result = page2.evaluate(fetchFunction, "4");
System.out.println(result);
fail("did not throw");
} catch (PlaywrightException e) {
}
@@ -1,46 +0,0 @@
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.stream.Collectors;
import static com.microsoft.playwright.Utils.mapOf;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestJavaSourceLocationInConstructor extends TestBase {
private static final String SRC_DIRS = System.getenv("PLAYWRIGHT_JAVA_SRC") == null ? "src/test/java" : System.getenv("PLAYWRIGHT_JAVA_SRC");
@Override
Playwright.CreateOptions playwrightOptions() {
return new Playwright.CreateOptions().setEnv(mapOf("PLAYWRIGHT_JAVA_SRC", SRC_DIRS));
}
@Test
void shouldSupportSourcesLocationPassedToPlaywrightCreate(@TempDir Path tmpDir) throws IOException {
context.tracing().start(new Tracing.StartOptions().setSources(true));
page.navigate(server.EMPTY_PAGE);
page.setContent("<button>Click</button>");
page.click("'Click'");
Path trace = tmpDir.resolve("trace1.zip");
context.tracing().stop(new Tracing.StopOptions().setPath(trace));
Map<String, byte[]> entries = Utils.parseZip(trace);
Map<String, byte[]> sources = entries.entrySet().stream().filter(e -> e.getKey().endsWith(".txt")).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
assertEquals(1, sources.size());
String path = getClass().getName().replace('.', File.separatorChar);
String[] srcRoots = SRC_DIRS.split(File.pathSeparator);
// Resolve in the last specified source dir.
Path sourceFile = Paths.get(srcRoots[srcRoots.length - 1], path + ".java");
byte[] thisFile = Files.readAllBytes(sourceFile);
assertEquals(new String(thisFile, UTF_8), new String(sources.values().iterator().next(), UTF_8));
}
}
@@ -20,11 +20,6 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.Date;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.ZonedDateTime;
import static com.microsoft.playwright.Utils.mapOf;
import static java.util.Arrays.asList;
@@ -579,56 +574,20 @@ public class TestPageEvaluate extends TestBase {
assertTrue(((String) error).contains("Error: error message"));
}
@Test
void shouldEvaluateDate() {
Object result = page.evaluate("() => ({ date: new Date('2020-05-27T01:31:38.506Z') })");
Date expected = Date.from(ZonedDateTime.parse("2020-05-27T01:31:38.506Z").toInstant());
assertEquals(mapOf("date", expected), result);
// TODO: Date values are not supported in java.
}
@Test
void shouldRoundtripDate() {
Date date = Date.from(ZonedDateTime.parse("2020-05-27T01:31:38.506Z").toInstant());
Object result = page.evaluate("date => date", date);
assertTrue(result instanceof Date);
assertEquals(date.toString(), result.toString());
// TODO: Date values are not supported in java.
}
@Test
void shouldRoundtripRegex() {
Pattern regex = Pattern.compile("hello", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
Object result = page.evaluate("regex => regex", regex);
assertTrue(result instanceof Pattern);
assertEquals(regex.toString(), result.toString());
assertEquals(regex.flags(), ((Pattern)result).flags());
// Not applicable
}
@Test
void shouldJsonValueDate() {
JSHandle resultHandle = page.evaluateHandle("() => ({ date: new Date('2020-05-27T01:31:38.506Z') })");
assertEquals(mapOf("date", Date.from(ZonedDateTime.parse("2020-05-27T01:31:38.506Z").toInstant())), resultHandle.jsonValue());
}
@Test
void shouldEvaluateUrl() throws MalformedURLException {
Object result = page.evaluate("() => ({ url: new URL('https://example.com/') })");
assertEquals(mapOf("url", new URL("https://example.com/")), result);
}
@Test
void shouldRoundtripUrl() throws MalformedURLException {
URL url = new URL("https://example.com/");
Object result = page.evaluate("url => url", url);
assertTrue(result instanceof URL);
assertEquals(url.toString(), result.toString());
}
@Test
void shouldRoundtripComplexUrl() throws MalformedURLException {
URL url = new URL("https://user:password@www.contoso.com:80/Home/Index.htm?q1=v1&q2=v2#FragmentName");
Object result = page.evaluate("url => url", url);
assertTrue(result instanceof URL);
assertEquals(url.toString(), result.toString());
// TODO: Date values are not supported in java.
}
@Test
@@ -119,33 +119,6 @@ public class TestPageLocatorQuery extends TestBase {
assertEquals("Hello \"world\"", page.locator("div", new Page.LocatorOptions().setHasText(pattern)).textContent());
}
@Test
void shouldFilterByCaseInsensitiveRegexInAChild() {
page.setContent("<div class=\"test\"><h5>Title Text</h5></div>");
Pattern pattern = Pattern.compile("^title text$", Pattern.CASE_INSENSITIVE);
assertThat(page.locator("div", new Page.LocatorOptions().setHasText(pattern))).hasText("Title Text");
}
@Test
void shouldFilterByCaseInsensitiveRegexInMultipleChildren() {
page.setContent("<div class=\"test\"><h5>Title</h5> <h2><i>Text</i></h2></div>`");
Pattern pattern = Pattern.compile("^title text$", Pattern.CASE_INSENSITIVE);
assertThat(page.locator("div", new Page.LocatorOptions().setHasText(pattern))).hasClass("test");
}
@Test
void shouldFilterByRegexWithSpecialSymbols() {
page.setContent("<div class=\"test\"><h5>First/\"and\"</h5><h2><i>Second\\</i></h2></div>");
Pattern pattern = Pattern.compile("first\\/\".*\"second\\\\$", Pattern.CASE_INSENSITIVE);
assertThat(page.locator("div", new Page.LocatorOptions().setHasText(pattern))).hasClass("test");
}
@Test
void shouldFilterByTextWithAmpersand() {
page.setContent("<div>Save & Continue</div>");
assertEquals("Save & Continue", page.locator("div",
new Page.LocatorOptions().setHasText("Save & Continue")).textContent());
}
@Test
void shouldSupportHasLocator() {
page.setContent("<div><span>hello</span></div><div><span>world</span></div>");
@@ -133,14 +133,10 @@ public class TestPageRequestFallback extends TestBase {
@Test
void shouldChainOnce() {
page.route("**/empty.html", route -> {
System.out.println("before fulfill");
route.fulfill(new Route.FulfillOptions().setStatus(200).setBody("fulfilled one"));
System.out.println("after fulfill");
}, new Page.RouteOptions().setTimes(1));
page.route("**/empty.html", route -> {
System.out.println("before fallback");
route.fallback();
System.out.println("after fallback");
}, new Page.RouteOptions().setTimes(1));
Response response = page.navigate(server.EMPTY_PAGE);
assertEquals("fulfilled one", response.text());
@@ -22,7 +22,6 @@ import com.microsoft.playwright.options.ScreenshotCaret;
import com.microsoft.playwright.options.ScreenshotScale;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import org.opentest4j.AssertionFailedError;
import javax.imageio.ImageIO;
@@ -221,7 +220,6 @@ public class TestPageScreenshot extends TestBase {
}
@Test
@DisabledIf(value="com.microsoft.playwright.TestBase#isFirefox", disabledReason="fixme")
void shouldCaptureBlinkingCaretIfExplicitlyAskedFor() {
page.setContent(" <!-- Refer to stylesheet from other origin. Accessing this\n" +
" stylesheet rules will throw.\n" +
@@ -25,7 +25,6 @@ import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.*;
@@ -140,13 +139,9 @@ public class TestWebSocket extends TestBase {
boolean[] socketError = {false};
String[] error = {null};
page.onWebSocket(ws -> {
ws.onSocketError(new Consumer<String>() {
@Override
public void accept(String e) {
ws.offSocketError(this);
error[0] = e;
socketError[0] = true;
}
ws.onSocketError(e -> {
error[0] = e;
socketError[0] = true;
});
});
page.evaluate("port => {\n" +
@@ -109,6 +109,7 @@ class Utils {
Files.createDirectories(toDir);
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipPath.toFile()))) {
for (ZipEntry zipEntry = zis.getNextEntry(); zipEntry != null; zipEntry = zis.getNextEntry()) {
System.out.println(zipEntry.getName());
Path toPath = toDir.resolve(zipEntry.getName());
if (zipEntry.isDirectory()) {
Files.createDirectories(toPath);
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.24.1</version>
<version>1.23.0</version>
<packaging>pom</packaging>
<name>Playwright Parent Project</name>
<description>Java library to automate Chromium, Firefox and WebKit with a single API.
+1 -1
View File
@@ -1 +1 @@
1.24.1
1.23.1
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>api-generator</artifactId>
<version>1.24.1</version>
<version>1.23.0</version>
<name>Playwright - API Generator</name>
<description>
This is an internal module used to generate Java API from the upstream Playwright
+1 -1
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.microsoft.playwright</groupId>
<artifactId>test-cli-version</artifactId>
<version>1.24.1</version>
<version>1.23.0</version>
<name>Test Playwright Command Line Version</name>
<properties>
<compiler.version>1.8</compiler.version>
+1 -1
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.microsoft.playwright</groupId>
<artifactId>test-local-installation</artifactId>
<version>1.24.1</version>
<version>1.23.0</version>
<name>Test local installation</name>
<description>Runs Playwright test suite (copied from playwright module) against locally cached Playwright</description>
<properties>
+1 -1
View File
@@ -9,7 +9,7 @@
</parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>test-spring-boot-starter</artifactId>
<version>1.24.1</version>
<version>1.23.0</version>
<name>Test Playwright With Spring Boot</name>
<properties>
<spring.version>2.4.3</spring.version>
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>update-version</artifactId>
<version>1.24.1</version>
<version>1.23.0</version>
<name>Playwright - Update Version in Documentation</name>
<description>
This is an internal module used to update versions in the documentation based on
+1 -3
View File
@@ -10,9 +10,7 @@ RUN apt-get update && \
# Install utilities required for downloading browsers
curl \
# Install utilities required for downloading driver
unzip \
# For the MSEdge install script
gpg && \
unzip && \
rm -rf /var/lib/apt/lists/* && \
# Create the pwuser
adduser pwuser