From a7830eb7b29cc41f0b25e06c9c05d1ed273a4b1b Mon Sep 17 00:00:00 2001 From: ArcticFoox Date: Tue, 24 Jun 2025 21:54:51 +0900 Subject: [PATCH 1/8] fix: Replaced classLoader in DriverJar --- .../com/microsoft/playwright/impl/driver/jar/DriverJar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver-bundle/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java b/driver-bundle/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java index 3fe7537d8..654ff6732 100644 --- a/driver-bundle/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java +++ b/driver-bundle/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java @@ -114,7 +114,7 @@ private FileSystem initFileSystem(URI uri) throws IOException { } public static URI getDriverResourceURI() throws URISyntaxException { - ClassLoader classloader = Thread.currentThread().getContextClassLoader(); + ClassLoader classloader = DriverJar.class.getClassLoader(); return classloader.getResource("driver/" + platformDir()).toURI(); } From 2b8f04d764995c79984590ac5e4ec4975cab1954 Mon Sep 17 00:00:00 2001 From: ArcticFoox Date: Fri, 4 Jul 2025 17:42:50 +0900 Subject: [PATCH 2/8] test: add classLoader test --- tools/test-spring-classloader/pom.xml | 36 +++ .../playwright/springboottest/DriverJar.java | 209 ++++++++++++++++++ .../playwright/springboottest/TestApp.java | 92 ++++++++ 3 files changed, 337 insertions(+) create mode 100644 tools/test-spring-classloader/pom.xml create mode 100644 tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/DriverJar.java create mode 100644 tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/TestApp.java diff --git a/tools/test-spring-classloader/pom.xml b/tools/test-spring-classloader/pom.xml new file mode 100644 index 000000000..2248743f6 --- /dev/null +++ b/tools/test-spring-classloader/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.3 + + com.microsoft.playwright + test-spring-classloader + 1.50.0-SNAPSHOT + Test Playwright With Spring Boot + + 2.4.3 + + + + org.springframework.boot + spring-boot-starter + + + com.microsoft.playwright + playwright + ${project.version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/DriverJar.java b/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/DriverJar.java new file mode 100644 index 000000000..b2bd09cbc --- /dev/null +++ b/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/DriverJar.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.microsoft.playwright.springboottest; + +import com.microsoft.playwright.impl.driver.Driver; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.*; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class DriverJar extends Driver { + private static final String PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD"; + private static final String SELENIUM_REMOTE_URL = "SELENIUM_REMOTE_URL"; + private final Path driverTempDir; + private Path preinstalledNodePath; + + public DriverJar() throws IOException { + // Allow specifying custom path for the driver installation + // See https://github.com/microsoft/playwright-java/issues/728 + String alternativeTmpdir = System.getProperty("playwright.driver.tmpdir"); + String prefix = "playwright-java-"; + driverTempDir = alternativeTmpdir == null + ? Files.createTempDirectory(prefix) + : Files.createTempDirectory(Paths.get(alternativeTmpdir), prefix); + driverTempDir.toFile().deleteOnExit(); + String nodePath = System.getProperty("playwright.nodejs.path"); + if (nodePath != null) { + preinstalledNodePath = Paths.get(nodePath); + if (!Files.exists(preinstalledNodePath)) { + throw new RuntimeException("Invalid Node.js path specified: " + nodePath); + } + } + logMessage("created DriverJar: " + driverTempDir); + } + + @Override + protected void initialize(Boolean installBrowsers) throws Exception { + if (preinstalledNodePath == null && env.containsKey(PLAYWRIGHT_NODEJS_PATH)) { + preinstalledNodePath = Paths.get(env.get(PLAYWRIGHT_NODEJS_PATH)); + if (!Files.exists(preinstalledNodePath)) { + throw new RuntimeException("Invalid Node.js path specified: " + preinstalledNodePath); + } + } else if (preinstalledNodePath != null) { + // Pass the env variable to the driver process. + env.put(PLAYWRIGHT_NODEJS_PATH, preinstalledNodePath.toString()); + } + extractDriverToTempDir(); + logMessage("extracted driver from jar to " + driverDir()); + if (installBrowsers) + installBrowsers(env); + } + + private void installBrowsers(Map env) throws IOException, InterruptedException { + String skip = env.get(PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD); + if (skip == null) { + skip = System.getenv(PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD); + } + if (skip != null && !"0".equals(skip) && !"false".equals(skip)) { + logMessage("Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set"); + return; + } + if (env.get(SELENIUM_REMOTE_URL) != null || System.getenv(SELENIUM_REMOTE_URL) != null) { + logMessage("Skipping browsers download because `SELENIUM_REMOTE_URL` env variable is set"); + return; + } + Path driver = driverDir(); + if (!Files.exists(driver)) { + throw new RuntimeException("Failed to find driver: " + driver); + } + ProcessBuilder pb = createProcessBuilder(); + pb.command().add("install"); + pb.redirectError(ProcessBuilder.Redirect.INHERIT); + pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); + Process p = pb.start(); + boolean result = p.waitFor(10, TimeUnit.MINUTES); + if (!result) { + p.destroy(); + throw new RuntimeException("Timed out waiting for browsers to install"); + } + if (p.exitValue() != 0) { + throw new RuntimeException("Failed to install browsers, exit code: " + p.exitValue()); + } + } + + private static boolean isExecutable(Path filePath) { + String name = filePath.getFileName().toString(); + return name.endsWith(".sh") || name.endsWith(".exe") || !name.contains("."); + } + + private FileSystem initFileSystem(URI uri) throws IOException { + try { + return FileSystems.newFileSystem(uri, Collections.emptyMap()); + } catch (FileSystemAlreadyExistsException e) { + return null; + } + } + + public static URI getDriverResourceURI() throws URISyntaxException { + ClassLoader classloader = com.microsoft.playwright.impl.driver.jar.DriverJar.class.getClassLoader(); + return classloader.getResource("driver/" + platformDir()).toURI(); + } + + void extractDriverToTempDir() throws URISyntaxException, IOException { + URI originalUri = getDriverResourceURI(); + URI uri = maybeExtractNestedJar(originalUri); + + // Create zip filesystem if loading from jar. + try (FileSystem fileSystem = "jar".equals(uri.getScheme()) ? initFileSystem(uri) : null) { + Path srcRoot = Paths.get(uri); + // jar file system's .relativize gives wrong results when used with + // spring-boot-maven-plugin, convert to the default filesystem to + // have predictable results. + // See https://github.com/microsoft/playwright-java/issues/306 + Path srcRootDefaultFs = Paths.get(srcRoot.toString()); + Files.walk(srcRoot).forEach(fromPath -> { + if (preinstalledNodePath != null) { + String fileName = fromPath.getFileName().toString(); + if ("node.exe".equals(fileName) || "node".equals(fileName)) { + return; + } + } + Path relative = srcRootDefaultFs.relativize(Paths.get(fromPath.toString())); + Path toPath = driverTempDir.resolve(relative.toString()); + try { + if (Files.isDirectory(fromPath)) { + Files.createDirectories(toPath); + } else { + Files.copy(fromPath, toPath); + if (isExecutable(toPath)) { + toPath.toFile().setExecutable(true, true); + } + } + toPath.toFile().deleteOnExit(); + } catch (IOException e) { + throw new RuntimeException("Failed to extract driver from " + uri + ", full uri: " + originalUri, e); + } + }); + } + } + + private URI maybeExtractNestedJar(final URI uri) throws URISyntaxException { + if (!"jar".equals(uri.getScheme())) { + return uri; + } + final String JAR_URL_SEPARATOR = "!/"; + String[] parts = uri.toString().split("!/"); + if (parts.length != 3) { + return uri; + } + String innerJar = String.join(JAR_URL_SEPARATOR, parts[0], parts[1]); + URI jarUri = new URI(innerJar); + try (FileSystem fs = FileSystems.newFileSystem(jarUri, Collections.emptyMap())) { + Path fromPath = Paths.get(jarUri); + Path toPath = driverTempDir.resolve(fromPath.getFileName().toString()); + Files.copy(fromPath, toPath); + toPath.toFile().deleteOnExit(); + return new URI("jar:" + toPath.toUri() + JAR_URL_SEPARATOR + parts[2]); + } catch (IOException e) { + throw new RuntimeException("Failed to extract driver's nested .jar from " + jarUri + "; full uri: " + uri, e); + } + } + + private static String platformDir() { + String name = System.getProperty("os.name").toLowerCase(); + String arch = System.getProperty("os.arch").toLowerCase(); + + if (name.contains("windows")) { + return "win32_x64"; + } + if (name.contains("linux")) { + if (arch.equals("aarch64")) { + return "linux-arm64"; + } else { + return "linux"; + } + } + if (name.contains("mac os x")) { + if (arch.equals("aarch64")) { + return "mac-arm64"; + } else { + return "mac"; + } + } + throw new RuntimeException("Unexpected os.name value: " + name); + } + + @Override + public Path driverDir() { + return driverTempDir; + } +} diff --git a/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/TestApp.java b/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/TestApp.java new file mode 100644 index 000000000..5d04da5de --- /dev/null +++ b/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/TestApp.java @@ -0,0 +1,92 @@ +package com.microsoft.playwright.springboottest; + +import com.microsoft.playwright.*; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.util.concurrent.CompletableFuture; + +@SpringBootApplication +public class TestApp implements CommandLineRunner { + + public static void main(String[] args) { + SpringApplication.run(TestApp.class, args); + } + + public void run(String... args) { + System.out.println("Starting original Playwright test..."); + CompletableFuture voidCompletableFuture = CompletableFuture.runAsync(() -> { + try (Playwright playwright = Playwright.create()) { + System.out.println("original Playwright test started, waiting for completion..."); + BrowserType browserType = getBrowserTypeFromEnv(playwright); + System.out.println("Running original test with " + browserType.name()); + Browser browser = browserType.launch(); + BrowserContext context = browser.newContext(); + Page page = context.newPage(); + System.out.println(page.evaluate("'SUCCESS: did evaluate in page'")); + } catch (Exception e) { + System.out.println("FAILED: " + e.toString()); + for (StackTraceElement ste : e.getStackTrace()) { + System.out.println("\tat " + ste); + } + } + }); + + System.out.println("original Playwright test is running asynchronously, main thread will wait for it to complete."); + + voidCompletableFuture.join(); + + System.out.println("original Playwright test completed."); + + + System.out.println("Starting new Playwright test..."); + + // Set the new driver implementation to use the DriverJar class + System.setProperty( "playwright.driver.impl", "com.microsoft.playwright.springboottest.DriverJar" ); + + CompletableFuture voidCompletableFuture2 = CompletableFuture.runAsync(() -> { + try (Playwright playwright = Playwright.create()) { + System.out.println("new Playwright test started, waiting for completion..."); + BrowserType browserType = getBrowserTypeFromEnv(playwright); + System.out.println("Running new test with " + browserType.name()); + Browser browser = browserType.launch(); + BrowserContext context = browser.newContext(); + Page page = context.newPage(); + System.out.println(page.evaluate("'SUCCESS: did evaluate in page'")); + } catch (Exception e) { + System.out.println("FAILED: " + e.toString()); + for (StackTraceElement ste : e.getStackTrace()) { + System.out.println("\tat " + ste); + } + } + }); + + System.out.println("new Playwright test is running asynchronously, main thread will wait for it to complete."); + + voidCompletableFuture2.join(); + + System.out.println("new Playwright test completed."); + + } + + static BrowserType getBrowserTypeFromEnv(Playwright playwright) { + String browserName = System.getenv("BROWSER"); + + if (browserName == null) { + browserName = "chromium"; + } + + switch (browserName) { + case "webkit": + return playwright.webkit(); + case "firefox": + return playwright.firefox(); + case "chromium": + return playwright.chromium(); + default: + throw new IllegalArgumentException("Unknown browser: " + browserName); + } + } + +} From 3c925b1796382592c344869ac6f3f66adfcd9bfe Mon Sep 17 00:00:00 2001 From: ArcticFoox Date: Mon, 21 Jul 2025 16:27:58 +0900 Subject: [PATCH 3/8] test: add classloader docker ci test --- .github/workflows/test_docker.yml | 6 + .../package_and_run_classloader_test.sh | 8 + .../playwright/springboottest/DriverJar.java | 209 ------------------ .../playwright/springboottest/TestApp.java | 39 +--- 4 files changed, 19 insertions(+), 243 deletions(-) create mode 100644 tools/test-spring-classloader/package_and_run_classloader_test.sh delete mode 100644 tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/DriverJar.java diff --git a/.github/workflows/test_docker.yml b/.github/workflows/test_docker.yml index df1e84c72..07f9de637 100644 --- a/.github/workflows/test_docker.yml +++ b/.github/workflows/test_docker.yml @@ -36,3 +36,9 @@ jobs: run: | CONTAINER_ID="$(docker run --rm -e CI --ipc=host -v $(pwd):/root/playwright --name playwright-docker-test -d -t playwright-java:localbuild-${{ matrix.flavor }} /bin/bash)" docker exec "${CONTAINER_ID}" /root/playwright/tools/test-local-installation/create_project_and_run_tests.sh + - name: Test ClassLoader + env: + BROWSER: ${{ matrix.browser }} + run: | + CONTAINER_ID="$(docker run --rm -e CI --ipc=host -v $(pwd):/root/playwright --name playwright-docker-test -d -t playwright-java:localbuild-${{ matrix.flavor }} /bin/bash)" + docker exec "${CONTAINER_ID}" /root/playwright/tools/test-spring-classloader/package_and_run_classloader_test.sh diff --git a/tools/test-spring-classloader/package_and_run_classloader_test.sh b/tools/test-spring-classloader/package_and_run_classloader_test.sh new file mode 100644 index 000000000..3873a48c5 --- /dev/null +++ b/tools/test-spring-classloader/package_and_run_classloader_test.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e +set +x + +cd "$(dirname "$0")" +mvn package -D skipTests --no-transfer-progress +java -jar target/test-spring-classloader*.jar diff --git a/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/DriverJar.java b/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/DriverJar.java deleted file mode 100644 index b2bd09cbc..000000000 --- a/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/DriverJar.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.microsoft.playwright.springboottest; - -import com.microsoft.playwright.impl.driver.Driver; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.*; -import java.util.Collections; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -public class DriverJar extends Driver { - private static final String PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD"; - private static final String SELENIUM_REMOTE_URL = "SELENIUM_REMOTE_URL"; - private final Path driverTempDir; - private Path preinstalledNodePath; - - public DriverJar() throws IOException { - // Allow specifying custom path for the driver installation - // See https://github.com/microsoft/playwright-java/issues/728 - String alternativeTmpdir = System.getProperty("playwright.driver.tmpdir"); - String prefix = "playwright-java-"; - driverTempDir = alternativeTmpdir == null - ? Files.createTempDirectory(prefix) - : Files.createTempDirectory(Paths.get(alternativeTmpdir), prefix); - driverTempDir.toFile().deleteOnExit(); - String nodePath = System.getProperty("playwright.nodejs.path"); - if (nodePath != null) { - preinstalledNodePath = Paths.get(nodePath); - if (!Files.exists(preinstalledNodePath)) { - throw new RuntimeException("Invalid Node.js path specified: " + nodePath); - } - } - logMessage("created DriverJar: " + driverTempDir); - } - - @Override - protected void initialize(Boolean installBrowsers) throws Exception { - if (preinstalledNodePath == null && env.containsKey(PLAYWRIGHT_NODEJS_PATH)) { - preinstalledNodePath = Paths.get(env.get(PLAYWRIGHT_NODEJS_PATH)); - if (!Files.exists(preinstalledNodePath)) { - throw new RuntimeException("Invalid Node.js path specified: " + preinstalledNodePath); - } - } else if (preinstalledNodePath != null) { - // Pass the env variable to the driver process. - env.put(PLAYWRIGHT_NODEJS_PATH, preinstalledNodePath.toString()); - } - extractDriverToTempDir(); - logMessage("extracted driver from jar to " + driverDir()); - if (installBrowsers) - installBrowsers(env); - } - - private void installBrowsers(Map env) throws IOException, InterruptedException { - String skip = env.get(PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD); - if (skip == null) { - skip = System.getenv(PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD); - } - if (skip != null && !"0".equals(skip) && !"false".equals(skip)) { - logMessage("Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set"); - return; - } - if (env.get(SELENIUM_REMOTE_URL) != null || System.getenv(SELENIUM_REMOTE_URL) != null) { - logMessage("Skipping browsers download because `SELENIUM_REMOTE_URL` env variable is set"); - return; - } - Path driver = driverDir(); - if (!Files.exists(driver)) { - throw new RuntimeException("Failed to find driver: " + driver); - } - ProcessBuilder pb = createProcessBuilder(); - pb.command().add("install"); - pb.redirectError(ProcessBuilder.Redirect.INHERIT); - pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); - Process p = pb.start(); - boolean result = p.waitFor(10, TimeUnit.MINUTES); - if (!result) { - p.destroy(); - throw new RuntimeException("Timed out waiting for browsers to install"); - } - if (p.exitValue() != 0) { - throw new RuntimeException("Failed to install browsers, exit code: " + p.exitValue()); - } - } - - private static boolean isExecutable(Path filePath) { - String name = filePath.getFileName().toString(); - return name.endsWith(".sh") || name.endsWith(".exe") || !name.contains("."); - } - - private FileSystem initFileSystem(URI uri) throws IOException { - try { - return FileSystems.newFileSystem(uri, Collections.emptyMap()); - } catch (FileSystemAlreadyExistsException e) { - return null; - } - } - - public static URI getDriverResourceURI() throws URISyntaxException { - ClassLoader classloader = com.microsoft.playwright.impl.driver.jar.DriverJar.class.getClassLoader(); - return classloader.getResource("driver/" + platformDir()).toURI(); - } - - void extractDriverToTempDir() throws URISyntaxException, IOException { - URI originalUri = getDriverResourceURI(); - URI uri = maybeExtractNestedJar(originalUri); - - // Create zip filesystem if loading from jar. - try (FileSystem fileSystem = "jar".equals(uri.getScheme()) ? initFileSystem(uri) : null) { - Path srcRoot = Paths.get(uri); - // jar file system's .relativize gives wrong results when used with - // spring-boot-maven-plugin, convert to the default filesystem to - // have predictable results. - // See https://github.com/microsoft/playwright-java/issues/306 - Path srcRootDefaultFs = Paths.get(srcRoot.toString()); - Files.walk(srcRoot).forEach(fromPath -> { - if (preinstalledNodePath != null) { - String fileName = fromPath.getFileName().toString(); - if ("node.exe".equals(fileName) || "node".equals(fileName)) { - return; - } - } - Path relative = srcRootDefaultFs.relativize(Paths.get(fromPath.toString())); - Path toPath = driverTempDir.resolve(relative.toString()); - try { - if (Files.isDirectory(fromPath)) { - Files.createDirectories(toPath); - } else { - Files.copy(fromPath, toPath); - if (isExecutable(toPath)) { - toPath.toFile().setExecutable(true, true); - } - } - toPath.toFile().deleteOnExit(); - } catch (IOException e) { - throw new RuntimeException("Failed to extract driver from " + uri + ", full uri: " + originalUri, e); - } - }); - } - } - - private URI maybeExtractNestedJar(final URI uri) throws URISyntaxException { - if (!"jar".equals(uri.getScheme())) { - return uri; - } - final String JAR_URL_SEPARATOR = "!/"; - String[] parts = uri.toString().split("!/"); - if (parts.length != 3) { - return uri; - } - String innerJar = String.join(JAR_URL_SEPARATOR, parts[0], parts[1]); - URI jarUri = new URI(innerJar); - try (FileSystem fs = FileSystems.newFileSystem(jarUri, Collections.emptyMap())) { - Path fromPath = Paths.get(jarUri); - Path toPath = driverTempDir.resolve(fromPath.getFileName().toString()); - Files.copy(fromPath, toPath); - toPath.toFile().deleteOnExit(); - return new URI("jar:" + toPath.toUri() + JAR_URL_SEPARATOR + parts[2]); - } catch (IOException e) { - throw new RuntimeException("Failed to extract driver's nested .jar from " + jarUri + "; full uri: " + uri, e); - } - } - - private static String platformDir() { - String name = System.getProperty("os.name").toLowerCase(); - String arch = System.getProperty("os.arch").toLowerCase(); - - if (name.contains("windows")) { - return "win32_x64"; - } - if (name.contains("linux")) { - if (arch.equals("aarch64")) { - return "linux-arm64"; - } else { - return "linux"; - } - } - if (name.contains("mac os x")) { - if (arch.equals("aarch64")) { - return "mac-arm64"; - } else { - return "mac"; - } - } - throw new RuntimeException("Unexpected os.name value: " + name); - } - - @Override - public Path driverDir() { - return driverTempDir; - } -} diff --git a/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/TestApp.java b/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/TestApp.java index 5d04da5de..54076d441 100644 --- a/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/TestApp.java +++ b/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/TestApp.java @@ -15,12 +15,12 @@ public static void main(String[] args) { } public void run(String... args) { - System.out.println("Starting original Playwright test..."); + // use CompletableFuture to run Playwright asynchronously CompletableFuture voidCompletableFuture = CompletableFuture.runAsync(() -> { try (Playwright playwright = Playwright.create()) { - System.out.println("original Playwright test started, waiting for completion..."); + System.out.println("Playwright classLoader test started, waiting for completion..."); BrowserType browserType = getBrowserTypeFromEnv(playwright); - System.out.println("Running original test with " + browserType.name()); + System.out.println("Running test with " + browserType.name()); Browser browser = browserType.launch(); BrowserContext context = browser.newContext(); Page page = context.newPage(); @@ -33,40 +33,11 @@ public void run(String... args) { } }); - System.out.println("original Playwright test is running asynchronously, main thread will wait for it to complete."); + System.out.println("Playwright classLoader test is running asynchronously, main thread will wait for it to complete."); voidCompletableFuture.join(); - System.out.println("original Playwright test completed."); - - - System.out.println("Starting new Playwright test..."); - - // Set the new driver implementation to use the DriverJar class - System.setProperty( "playwright.driver.impl", "com.microsoft.playwright.springboottest.DriverJar" ); - - CompletableFuture voidCompletableFuture2 = CompletableFuture.runAsync(() -> { - try (Playwright playwright = Playwright.create()) { - System.out.println("new Playwright test started, waiting for completion..."); - BrowserType browserType = getBrowserTypeFromEnv(playwright); - System.out.println("Running new test with " + browserType.name()); - Browser browser = browserType.launch(); - BrowserContext context = browser.newContext(); - Page page = context.newPage(); - System.out.println(page.evaluate("'SUCCESS: did evaluate in page'")); - } catch (Exception e) { - System.out.println("FAILED: " + e.toString()); - for (StackTraceElement ste : e.getStackTrace()) { - System.out.println("\tat " + ste); - } - } - }); - - System.out.println("new Playwright test is running asynchronously, main thread will wait for it to complete."); - - voidCompletableFuture2.join(); - - System.out.println("new Playwright test completed."); + System.out.println("Playwright classLoader test completed."); } From 04b40cf18153c3e648f9025d543c99b2eb7bea7f Mon Sep 17 00:00:00 2001 From: ArcticFoox Date: Thu, 24 Jul 2025 18:06:58 +0900 Subject: [PATCH 4/8] refactor: delete classloader test and add async execution support in springboot test --- .github/workflows/test_docker.yml | 2 +- .../package_and_run_async_test.sh} | 2 +- .../playwright/springboottest/TestApp.java | 20 ++++++ tools/test-spring-classloader/pom.xml | 36 ----------- .../playwright/springboottest/TestApp.java | 63 ------------------- 5 files changed, 22 insertions(+), 101 deletions(-) rename tools/{test-spring-classloader/package_and_run_classloader_test.sh => test-spring-boot-starter/package_and_run_async_test.sh} (63%) delete mode 100644 tools/test-spring-classloader/pom.xml delete mode 100644 tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/TestApp.java diff --git a/.github/workflows/test_docker.yml b/.github/workflows/test_docker.yml index 07f9de637..fc6d77614 100644 --- a/.github/workflows/test_docker.yml +++ b/.github/workflows/test_docker.yml @@ -41,4 +41,4 @@ jobs: BROWSER: ${{ matrix.browser }} run: | CONTAINER_ID="$(docker run --rm -e CI --ipc=host -v $(pwd):/root/playwright --name playwright-docker-test -d -t playwright-java:localbuild-${{ matrix.flavor }} /bin/bash)" - docker exec "${CONTAINER_ID}" /root/playwright/tools/test-spring-classloader/package_and_run_classloader_test.sh + docker exec "${CONTAINER_ID}" /root/playwright/tools/test-spring-boot-starter/package_and_run_async_test.sh diff --git a/tools/test-spring-classloader/package_and_run_classloader_test.sh b/tools/test-spring-boot-starter/package_and_run_async_test.sh similarity index 63% rename from tools/test-spring-classloader/package_and_run_classloader_test.sh rename to tools/test-spring-boot-starter/package_and_run_async_test.sh index 3873a48c5..ac6875fce 100644 --- a/tools/test-spring-classloader/package_and_run_classloader_test.sh +++ b/tools/test-spring-boot-starter/package_and_run_async_test.sh @@ -5,4 +5,4 @@ set +x cd "$(dirname "$0")" mvn package -D skipTests --no-transfer-progress -java -jar target/test-spring-classloader*.jar +java -jar target/test-spring-boot-starter*.jar --async diff --git a/tools/test-spring-boot-starter/src/main/java/com/microsoft/playwright/springboottest/TestApp.java b/tools/test-spring-boot-starter/src/main/java/com/microsoft/playwright/springboottest/TestApp.java index 35e669ec9..1a5cc8fef 100644 --- a/tools/test-spring-boot-starter/src/main/java/com/microsoft/playwright/springboottest/TestApp.java +++ b/tools/test-spring-boot-starter/src/main/java/com/microsoft/playwright/springboottest/TestApp.java @@ -5,6 +5,8 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import java.util.concurrent.CompletableFuture; + @SpringBootApplication public class TestApp implements CommandLineRunner { @@ -14,6 +16,24 @@ public static void main(String[] args) { @Override public void run(String... args) { + if (args.length == 0) { + runSync(); + } else { + if ("--async".equals(args[0])) { + runAsync(); + } + else { + runSync(); + } + } + } + + private void runAsync() { + CompletableFuture voidCompletableFuture = CompletableFuture.runAsync(this::runSync); + voidCompletableFuture.join(); + } + + private void runSync() { try (Playwright playwright = Playwright.create()) { BrowserType browserType = getBrowserTypeFromEnv(playwright); System.out.println("Running test with " + browserType.name()); diff --git a/tools/test-spring-classloader/pom.xml b/tools/test-spring-classloader/pom.xml deleted file mode 100644 index 2248743f6..000000000 --- a/tools/test-spring-classloader/pom.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.4.3 - - com.microsoft.playwright - test-spring-classloader - 1.50.0-SNAPSHOT - Test Playwright With Spring Boot - - 2.4.3 - - - - org.springframework.boot - spring-boot-starter - - - com.microsoft.playwright - playwright - ${project.version} - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - diff --git a/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/TestApp.java b/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/TestApp.java deleted file mode 100644 index 54076d441..000000000 --- a/tools/test-spring-classloader/src/main/java/com/microsoft/playwright/springboottest/TestApp.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.microsoft.playwright.springboottest; - -import com.microsoft.playwright.*; -import org.springframework.boot.CommandLineRunner; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import java.util.concurrent.CompletableFuture; - -@SpringBootApplication -public class TestApp implements CommandLineRunner { - - public static void main(String[] args) { - SpringApplication.run(TestApp.class, args); - } - - public void run(String... args) { - // use CompletableFuture to run Playwright asynchronously - CompletableFuture voidCompletableFuture = CompletableFuture.runAsync(() -> { - try (Playwright playwright = Playwright.create()) { - System.out.println("Playwright classLoader test started, waiting for completion..."); - BrowserType browserType = getBrowserTypeFromEnv(playwright); - System.out.println("Running test with " + browserType.name()); - Browser browser = browserType.launch(); - BrowserContext context = browser.newContext(); - Page page = context.newPage(); - System.out.println(page.evaluate("'SUCCESS: did evaluate in page'")); - } catch (Exception e) { - System.out.println("FAILED: " + e.toString()); - for (StackTraceElement ste : e.getStackTrace()) { - System.out.println("\tat " + ste); - } - } - }); - - System.out.println("Playwright classLoader test is running asynchronously, main thread will wait for it to complete."); - - voidCompletableFuture.join(); - - System.out.println("Playwright classLoader test completed."); - - } - - static BrowserType getBrowserTypeFromEnv(Playwright playwright) { - String browserName = System.getenv("BROWSER"); - - if (browserName == null) { - browserName = "chromium"; - } - - switch (browserName) { - case "webkit": - return playwright.webkit(); - case "firefox": - return playwright.firefox(); - case "chromium": - return playwright.chromium(); - default: - throw new IllegalArgumentException("Unknown browser: " + browserName); - } - } - -} From 999cef62f9317e95bf73e60c82c58608d33f631c Mon Sep 17 00:00:00 2001 From: ArcticFoox Date: Fri, 25 Jul 2025 19:53:09 +0900 Subject: [PATCH 5/8] refactor: simplify if statement --- .../microsoft/playwright/springboottest/TestApp.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tools/test-spring-boot-starter/src/main/java/com/microsoft/playwright/springboottest/TestApp.java b/tools/test-spring-boot-starter/src/main/java/com/microsoft/playwright/springboottest/TestApp.java index 1a5cc8fef..6a36d530f 100644 --- a/tools/test-spring-boot-starter/src/main/java/com/microsoft/playwright/springboottest/TestApp.java +++ b/tools/test-spring-boot-starter/src/main/java/com/microsoft/playwright/springboottest/TestApp.java @@ -5,6 +5,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import java.util.Arrays; import java.util.concurrent.CompletableFuture; @SpringBootApplication @@ -16,15 +17,10 @@ public static void main(String[] args) { @Override public void run(String... args) { - if (args.length == 0) { - runSync(); + if (Arrays.asList(args).contains("--async")) { + runAsync(); } else { - if ("--async".equals(args[0])) { - runAsync(); - } - else { - runSync(); - } + runSync(); } } From 2b3449878a9598a9a607c6eb3eb514c292994a25 Mon Sep 17 00:00:00 2001 From: ArcticFoox Date: Sat, 26 Jul 2025 12:08:52 +0900 Subject: [PATCH 6/8] fix: update docker test workflow to manage container lifecycle --- .github/workflows/test_docker.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test_docker.yml b/.github/workflows/test_docker.yml index fc6d77614..34cba82ad 100644 --- a/.github/workflows/test_docker.yml +++ b/.github/workflows/test_docker.yml @@ -32,13 +32,19 @@ jobs: run: | ARCH="${{ matrix.runs-on == 'ubuntu-24.04-arm' && 'arm64' || 'amd64' }}" bash utils/docker/build.sh --$ARCH ${{ matrix.flavor }} playwright-java:localbuild-${{ matrix.flavor }} - - name: Test + - name: Start container run: | - CONTAINER_ID="$(docker run --rm -e CI --ipc=host -v $(pwd):/root/playwright --name playwright-docker-test -d -t playwright-java:localbuild-${{ matrix.flavor }} /bin/bash)" - docker exec "${CONTAINER_ID}" /root/playwright/tools/test-local-installation/create_project_and_run_tests.sh + CONTAINER_ID=$(docker run --rm -e CI --ipc=host -v "$(pwd)":/root/playwright --name playwright-docker-test -d -t playwright-java:localbuild-${{ matrix.flavor }} /bin/bash) + echo "CONTAINER_ID=$CONTAINER_ID" >> $GITHUB_ENV + + - name: Run test in container + run: | + docker exec "$CONTAINER_ID" /root/playwright/tools/test-local-installation/create_project_and_run_tests.sh + - name: Test ClassLoader - env: - BROWSER: ${{ matrix.browser }} run: | - CONTAINER_ID="$(docker run --rm -e CI --ipc=host -v $(pwd):/root/playwright --name playwright-docker-test -d -t playwright-java:localbuild-${{ matrix.flavor }} /bin/bash)" - docker exec "${CONTAINER_ID}" /root/playwright/tools/test-spring-boot-starter/package_and_run_async_test.sh + docker exec "${CONTAINER_ID}" /root/playwright/tools/test-spring-boot-starter/package_and_run_async_test.sh + + - name: Stop container + run: | + docker stop "$CONTAINER_ID" From ef25b16cf6fba8a65da919f7e9c70824cc1127c8 Mon Sep 17 00:00:00 2001 From: ArcticFoox Date: Tue, 29 Jul 2025 18:38:43 +0900 Subject: [PATCH 7/8] fix: add execute permission for async test script in docker workflow --- .github/workflows/test_docker.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test_docker.yml b/.github/workflows/test_docker.yml index 34cba82ad..0d138644b 100644 --- a/.github/workflows/test_docker.yml +++ b/.github/workflows/test_docker.yml @@ -43,6 +43,7 @@ jobs: - name: Test ClassLoader run: | + docker exec "${CONTAINER_ID}" chmod +x /root/playwright/tools/test-spring-boot-starter/package_and_run_async_test.sh docker exec "${CONTAINER_ID}" /root/playwright/tools/test-spring-boot-starter/package_and_run_async_test.sh - name: Stop container From 3da9be6efdf390f102a270857930a727c531e720 Mon Sep 17 00:00:00 2001 From: ArcticFoox Date: Thu, 31 Jul 2025 18:55:27 +0900 Subject: [PATCH 8/8] fix: delete permission grant step and add executable bit in package_and_run_async_test.sh file --- .github/workflows/test_docker.yml | 1 - tools/test-spring-boot-starter/package_and_run_async_test.sh | 0 2 files changed, 1 deletion(-) mode change 100644 => 100755 tools/test-spring-boot-starter/package_and_run_async_test.sh diff --git a/.github/workflows/test_docker.yml b/.github/workflows/test_docker.yml index 0d138644b..34cba82ad 100644 --- a/.github/workflows/test_docker.yml +++ b/.github/workflows/test_docker.yml @@ -43,7 +43,6 @@ jobs: - name: Test ClassLoader run: | - docker exec "${CONTAINER_ID}" chmod +x /root/playwright/tools/test-spring-boot-starter/package_and_run_async_test.sh docker exec "${CONTAINER_ID}" /root/playwright/tools/test-spring-boot-starter/package_and_run_async_test.sh - name: Stop container diff --git a/tools/test-spring-boot-starter/package_and_run_async_test.sh b/tools/test-spring-boot-starter/package_and_run_async_test.sh old mode 100644 new mode 100755