Skip to content

Commit 94449c9

Browse files
authored
Replace scalajs-env-selenium with playwright tests (#318)
Tests using selenium+chrome started failing recently
1 parent 031c155 commit 94449c9

File tree

16 files changed

+1219
-142
lines changed

16 files changed

+1219
-142
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ data/
1111
.metals
1212
.vscode
1313
metals.sbt
14-
.bloop
14+
.bloop
15+
.cursor

README.md

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,10 @@ browserGeckoTestSettings
6060
- [sbt-updates](https://github.com/rtimush/sbt-updates)
6161
- [sbt-dependency-check](https://github.com/albuch/sbt-dependency-check)
6262

63-
## Sonatype setup
63+
## JS testing
6464

65-
By default, the plugins use the new `s01.oss.sonatype.org` host for releasing to Sonatype. If your project isn't yet
66-
[migrated](https://central.sonatype.org/news/20210223_new-users-on-s01/), you'll need to add the following to your
67-
root project settings:
68-
69-
```scala
70-
sonatypeCredentialHost := "oss.sonatype.org"
71-
```
65+
The browser-test-js contains a fork of [scala-js-env-playwright](https://github.com/gmkumar2005/scala-js-env-playwright),
66+
which is built using Java 11.
7267

7368
## Releasing your library
7469

browser-test-js/LICENSE-jsenv

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2024, gmkumar2005
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
1. Redistributions of source code must retain the above copyright notice, this
9+
list of conditions and the following disclaimer.
10+
11+
2. Redistributions in binary form must reproduce the above copyright notice,
12+
this list of conditions and the following disclaimer in the documentation
13+
and/or other materials provided with the distribution.
14+
15+
3. Neither the name of the copyright holder nor the names of its
16+
contributors may be used to endorse or promote products derived from
17+
this software without specific prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package jsenv;
2+
3+
/*
4+
* Copyright (c) Microsoft Corporation.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
//package com.microsoft.playwright.impl.driver.jar;
20+
// String driverImpl =
21+
// System.getProperty("playwright.driver.impl", "com.microsoft.playwright.impl.driver.jar.DriverJar");
22+
23+
import com.microsoft.playwright.impl.driver.Driver;
24+
25+
import java.io.IOException;
26+
import java.net.URI;
27+
import java.net.URISyntaxException;
28+
import java.nio.file.*;
29+
import java.util.Collections;
30+
import java.util.Map;
31+
import java.util.concurrent.TimeUnit;
32+
33+
public class DriverJar extends Driver {
34+
private static final String PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD";
35+
private static final String SELENIUM_REMOTE_URL = "SELENIUM_REMOTE_URL";
36+
private final Path driverTempDir;
37+
private Path preinstalledNodePath;
38+
39+
public DriverJar() throws IOException {
40+
// Allow specifying custom path for the driver installation
41+
// See https://github.com/microsoft/playwright-java/issues/728
42+
String alternativeTmpdir = System.getProperty("playwright.driver.tmpdir");
43+
String prefix = "playwright-java-";
44+
driverTempDir = alternativeTmpdir == null
45+
? Files.createTempDirectory(prefix)
46+
: Files.createTempDirectory(Paths.get(alternativeTmpdir), prefix);
47+
driverTempDir.toFile().deleteOnExit();
48+
String nodePath = System.getProperty("playwright.nodejs.path");
49+
if (nodePath != null) {
50+
preinstalledNodePath = Paths.get(nodePath);
51+
if (!Files.exists(preinstalledNodePath)) {
52+
throw new RuntimeException("Invalid Node.js path specified: " + nodePath);
53+
}
54+
}
55+
logMessage("created DriverJar: " + driverTempDir);
56+
}
57+
58+
@Override
59+
protected void initialize(Boolean installBrowsers) throws Exception {
60+
if (preinstalledNodePath == null && env.containsKey(PLAYWRIGHT_NODEJS_PATH)) {
61+
preinstalledNodePath = Paths.get(env.get(PLAYWRIGHT_NODEJS_PATH));
62+
if (!Files.exists(preinstalledNodePath)) {
63+
throw new RuntimeException("Invalid Node.js path specified: " + preinstalledNodePath);
64+
}
65+
} else if (preinstalledNodePath != null) {
66+
// Pass the env variable to the driver process.
67+
env.put(PLAYWRIGHT_NODEJS_PATH, preinstalledNodePath.toString());
68+
}
69+
extractDriverToTempDir();
70+
logMessage("extracted driver from jar to " + driverDir());
71+
if (installBrowsers)
72+
installBrowsers(env);
73+
}
74+
75+
private void installBrowsers(Map<String, String> env) throws IOException, InterruptedException {
76+
String skip = env.get(PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD);
77+
if (skip == null) {
78+
skip = System.getenv(PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD);
79+
}
80+
if (skip != null && !"0".equals(skip) && !"false".equals(skip)) {
81+
logMessage("Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set");
82+
return;
83+
}
84+
if (env.get(SELENIUM_REMOTE_URL) != null || System.getenv(SELENIUM_REMOTE_URL) != null) {
85+
logMessage("Skipping browsers download because `SELENIUM_REMOTE_URL` env variable is set");
86+
return;
87+
}
88+
Path driver = driverDir();
89+
if (!Files.exists(driver)) {
90+
throw new RuntimeException("Failed to find driver: " + driver);
91+
}
92+
ProcessBuilder pb = createProcessBuilder();
93+
pb.command().add("install");
94+
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
95+
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
96+
Process p = pb.start();
97+
boolean result = p.waitFor(10, TimeUnit.MINUTES);
98+
if (!result) {
99+
p.destroy();
100+
throw new RuntimeException("Timed out waiting for browsers to install");
101+
}
102+
if (p.exitValue() != 0) {
103+
throw new RuntimeException("Failed to install browsers, exit code: " + p.exitValue());
104+
}
105+
}
106+
107+
private static boolean isExecutable(Path filePath) {
108+
String name = filePath.getFileName().toString();
109+
return name.endsWith(".sh") || name.endsWith(".exe") || !name.contains(".");
110+
}
111+
112+
private FileSystem initFileSystem(URI uri) throws IOException {
113+
try {
114+
return FileSystems.newFileSystem(uri, Collections.emptyMap());
115+
} catch (FileSystemAlreadyExistsException e) {
116+
return null;
117+
}
118+
}
119+
120+
public static URI getDriverResourceURI() throws URISyntaxException {
121+
// ClassLoader classloader = Thread.currentThread().getContextClassLoader();
122+
ClassLoader classloader = DriverJar.class.getClassLoader();
123+
return classloader.getResource("driver/" + platformDir()).toURI();
124+
}
125+
126+
void extractDriverToTempDir() throws URISyntaxException, IOException {
127+
URI originalUri = getDriverResourceURI();
128+
URI uri = maybeExtractNestedJar(originalUri);
129+
130+
// Create zip filesystem if loading from jar.
131+
try (FileSystem fileSystem = "jar".equals(uri.getScheme()) ? initFileSystem(uri) : null) {
132+
Path srcRoot = Paths.get(uri);
133+
// jar file system's .relativize gives wrong results when used with
134+
// spring-boot-maven-plugin, convert to the default filesystem to
135+
// have predictable results.
136+
// See https://github.com/microsoft/playwright-java/issues/306
137+
Path srcRootDefaultFs = Paths.get(srcRoot.toString());
138+
Files.walk(srcRoot).forEach(fromPath -> {
139+
if (preinstalledNodePath != null) {
140+
String fileName = fromPath.getFileName().toString();
141+
if ("node.exe".equals(fileName) || "node".equals(fileName)) {
142+
return;
143+
}
144+
}
145+
Path relative = srcRootDefaultFs.relativize(Paths.get(fromPath.toString()));
146+
Path toPath = driverTempDir.resolve(relative.toString());
147+
try {
148+
if (Files.isDirectory(fromPath)) {
149+
Files.createDirectories(toPath);
150+
} else {
151+
Files.copy(fromPath, toPath);
152+
if (isExecutable(toPath)) {
153+
toPath.toFile().setExecutable(true, true);
154+
}
155+
}
156+
toPath.toFile().deleteOnExit();
157+
} catch (IOException e) {
158+
throw new RuntimeException("Failed to extract driver from " + uri + ", full uri: " + originalUri, e);
159+
}
160+
});
161+
}
162+
}
163+
164+
private URI maybeExtractNestedJar(final URI uri) throws URISyntaxException {
165+
if (!"jar".equals(uri.getScheme())) {
166+
return uri;
167+
}
168+
final String JAR_URL_SEPARATOR = "!/";
169+
String[] parts = uri.toString().split("!/");
170+
if (parts.length != 3) {
171+
return uri;
172+
}
173+
String innerJar = String.join(JAR_URL_SEPARATOR, parts[0], parts[1]);
174+
URI jarUri = new URI(innerJar);
175+
try (FileSystem fs = FileSystems.newFileSystem(jarUri, Collections.emptyMap())) {
176+
Path fromPath = Paths.get(jarUri);
177+
Path toPath = driverTempDir.resolve(fromPath.getFileName().toString());
178+
Files.copy(fromPath, toPath);
179+
toPath.toFile().deleteOnExit();
180+
return new URI("jar:" + toPath.toUri() + JAR_URL_SEPARATOR + parts[2]);
181+
} catch (IOException e) {
182+
throw new RuntimeException("Failed to extract driver's nested .jar from " + jarUri + "; full uri: " + uri, e);
183+
}
184+
}
185+
186+
private static String platformDir() {
187+
String name = System.getProperty("os.name").toLowerCase();
188+
String arch = System.getProperty("os.arch").toLowerCase();
189+
190+
if (name.contains("windows")) {
191+
return "win32_x64";
192+
}
193+
if (name.contains("linux")) {
194+
if (arch.equals("aarch64")) {
195+
return "linux-arm64";
196+
} else {
197+
return "linux";
198+
}
199+
}
200+
if (name.contains("mac os x")) {
201+
if (arch.equals("aarch64")) {
202+
return "mac-arm64";
203+
} else {
204+
return "mac";
205+
}
206+
}
207+
throw new RuntimeException("Unexpected os.name value: " + name);
208+
}
209+
210+
@Override
211+
public Path driverDir() {
212+
return driverTempDir;
213+
}
214+
}

0 commit comments

Comments
 (0)