feat: allow using preinstalled node.js (#1030)
This commit is contained in:
+30
-6
@@ -29,7 +29,9 @@ 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";
|
||||
static final String PLAYWRIGHT_NODEJS_PATH = "PLAYWRIGHT_NODEJS_PATH";
|
||||
private final Path driverTempDir;
|
||||
private Path preinstalledNodePath;
|
||||
|
||||
public DriverJar() throws IOException {
|
||||
// Allow specifying custom path for the driver installation
|
||||
@@ -40,11 +42,27 @@ public class DriverJar extends Driver {
|
||||
? 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(Map<String, String> env, Boolean installBrowsers) throws Exception {
|
||||
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 " + driverPath());
|
||||
if (installBrowsers)
|
||||
@@ -68,9 +86,8 @@ public class DriverJar extends Driver {
|
||||
if (!Files.exists(driver)) {
|
||||
throw new RuntimeException("Failed to find driver: " + driver);
|
||||
}
|
||||
ProcessBuilder pb = new ProcessBuilder(driver.toString(), "install");
|
||||
pb.environment().putAll(env);
|
||||
setRequiredEnvironmentVariables(pb);
|
||||
ProcessBuilder pb = createProcessBuilder();
|
||||
pb.command().add("install");
|
||||
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
|
||||
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
|
||||
Process p = pb.start();
|
||||
@@ -89,9 +106,10 @@ public class DriverJar extends Driver {
|
||||
return name.endsWith(".sh") || name.endsWith(".exe") || !name.contains(".");
|
||||
}
|
||||
|
||||
private void extractDriverToTempDir() throws URISyntaxException, IOException {
|
||||
void extractDriverToTempDir() throws URISyntaxException, IOException {
|
||||
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
|
||||
URI originalUri = classloader.getResource("driver/" + platformDir()).toURI();
|
||||
URI originalUri = classloader.getResource(
|
||||
"driver/" + platformDir()).toURI();
|
||||
URI uri = maybeExtractNestedJar(originalUri);
|
||||
|
||||
// Create zip filesystem if loading from jar.
|
||||
@@ -103,6 +121,12 @@ public class DriverJar extends Driver {
|
||||
// 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 {
|
||||
|
||||
+57
-21
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
package com.microsoft.playwright.impl.driver.jar;
|
||||
|
||||
import com.microsoft.playwright.impl.driver.Driver;
|
||||
import com.microsoft.playwright.impl.driver.jar.DriverJar;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
@@ -25,6 +25,8 @@ import org.junit.jupiter.api.io.TempDir;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
@@ -32,6 +34,8 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.microsoft.playwright.impl.driver.jar.DriverJar.PLAYWRIGHT_NODEJS_PATH;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestInstall {
|
||||
@@ -57,6 +61,7 @@ public class TestInstall {
|
||||
// Clear system property to ensure that the driver is loaded from jar.
|
||||
System.clearProperty("playwright.cli.dir");
|
||||
System.clearProperty("playwright.driver.tmpdir");
|
||||
System.clearProperty("playwright.nodejs.path");
|
||||
// Clear system property to ensure that the default driver is loaded.
|
||||
System.clearProperty("playwright.driver.impl");
|
||||
}
|
||||
@@ -72,27 +77,20 @@ public class TestInstall {
|
||||
env.put("PLAYWRIGHT_BROWSERS_PATH", tmpDir.toString());
|
||||
env.put("PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD", "false");
|
||||
|
||||
// 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);
|
||||
|
||||
for (int i = 0; i < 2; i++){
|
||||
RuntimeException exception = assertThrows(RuntimeException.class, () -> Driver.ensureDriverInstalled(env, true));
|
||||
String message = exception.getMessage();
|
||||
assertTrue(message.contains("Failed to create driver"), message);
|
||||
}
|
||||
|
||||
field.set(Driver.class, value);
|
||||
}
|
||||
|
||||
@Test
|
||||
void playwrightCliInstalled() throws Exception {
|
||||
Path cli = Driver.ensureDriverInstalled(Collections.emptyMap(), false);
|
||||
assertTrue(Files.exists(cli));
|
||||
Driver driver = Driver.createAndInstall(Collections.emptyMap(), false);
|
||||
assertTrue(Files.exists(driver.driverPath()));
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder(cli.toString(), "install");
|
||||
ProcessBuilder pb = driver.createProcessBuilder();
|
||||
pb.command().add("install");
|
||||
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
|
||||
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
|
||||
Process p = pb.start();
|
||||
@@ -109,24 +107,62 @@ public class TestInstall {
|
||||
|
||||
@Test
|
||||
void playwrightDriverDefaultImpl() {
|
||||
assertDoesNotThrow(() -> Driver.ensureDriverInstalled(Collections.emptyMap(), false));
|
||||
assertDoesNotThrow(() -> Driver.createAndInstall(Collections.emptyMap(), false));
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
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);
|
||||
@Test
|
||||
void canPassPreinstalledNodeJsAsSystemProperty(@TempDir Path tmpDir) throws IOException, URISyntaxException, InterruptedException {
|
||||
String nodePath = extractNodeJsToTemp();
|
||||
System.setProperty("playwright.nodejs.path", nodePath);
|
||||
Driver driver = Driver.createAndInstall(Collections.emptyMap(), false);
|
||||
canSpecifyPreinstalledNodeJsShared(driver, tmpDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
void canSpecifyPreinstalledNodeJsAsEnv(@TempDir Path tmpDir) throws IOException, URISyntaxException, InterruptedException {
|
||||
String nodePath = extractNodeJsToTemp();
|
||||
Driver driver = Driver.createAndInstall(singletonMap(PLAYWRIGHT_NODEJS_PATH, nodePath), false);
|
||||
canSpecifyPreinstalledNodeJsShared(driver, tmpDir);
|
||||
}
|
||||
|
||||
|
||||
private static String extractNodeJsToTemp() throws URISyntaxException, IOException {
|
||||
DriverJar auxDriver = new DriverJar();
|
||||
auxDriver.extractDriverToTempDir();
|
||||
String nodePath = auxDriver.driverPath().getParent().resolve(isWindows() ? "node.exe" : "node").toString();
|
||||
return nodePath;
|
||||
}
|
||||
|
||||
private static boolean isWindows() {
|
||||
String name = System.getProperty("os.name").toLowerCase();
|
||||
return name.contains("win");
|
||||
}
|
||||
|
||||
private static void canSpecifyPreinstalledNodeJsShared(Driver driver, Path tmpDir) throws IOException, URISyntaxException, InterruptedException {
|
||||
Path builtinNode = driver.driverPath().getParent().resolve("node");
|
||||
assertFalse(Files.exists(builtinNode), builtinNode.toString());
|
||||
Path builtinNodeExe = driver.driverPath().getParent().resolve("node.exe");
|
||||
assertFalse(Files.exists(builtinNodeExe), builtinNodeExe.toString());
|
||||
|
||||
ProcessBuilder pb = driver.createProcessBuilder();
|
||||
pb.command().add("--version");
|
||||
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
|
||||
Path out = tmpDir.resolve("out.txt");
|
||||
pb.redirectOutput(out.toFile());
|
||||
Process p = pb.start();
|
||||
boolean result = p.waitFor(1, TimeUnit.MINUTES);
|
||||
assertTrue(result, "Timed out waiting for version to be printed");
|
||||
String stdout = new String(Files.readAllBytes(out), StandardCharsets.UTF_8);
|
||||
assertTrue(stdout.contains("Version "), stdout);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user