Skip to content

Commit 2de5561

Browse files
krmahadevandiemol
andauthored
[Grid] Support auto downloads in Grid (#11702)
* [Grid] Support auto downloads in Grid Fixes #11656 #11658 Following has been done: * Turn ON managing download folders via the flag “-—enable-manage-downloads” * Enabled support for Chrome|Edge|Firefox browsers. * File downloads will be done only in a session aware directory for a given web driver session. After session is killed, the directory gets cleaned up as well. * [grid] Renaming to manage downloads enabled and removing out of scope logic to determine Node match and client side validations * [grid] Using the temp file system utility With this, it will be transparent for the user where files are written, and since we use the caches then the deletion happens when the session is closed. Also, we do not need the `--base-dir-downloads` parameter. * [grid] Adding a cleanup executor for downloaded files * [grid] Adding e2e test and fixing bug found while adding test * [grid] Removing test --------- Co-authored-by: Diego Molina <[email protected]> Co-authored-by: Diego Molina <[email protected]>
1 parent e6a7987 commit 2de5561

File tree

16 files changed

+700
-151
lines changed

16 files changed

+700
-151
lines changed
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<!DOCTYPE html>
2+
<html lang="en" class="h-100">
3+
4+
<head>
5+
<title>Downloads</title>
6+
7+
<meta charset="utf-8">
8+
<meta name="viewport" content="width=device-width, initial-scale=1">
9+
10+
<link href="//cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
11+
</head>
12+
13+
<body class="d-flex flex-column h-100">
14+
<main class="flex-shrink-2">
15+
<div class="container">
16+
<div class="row">
17+
<div class="col-12">
18+
<h1 class="display-6">Downloads</h1>
19+
</div>
20+
</div>
21+
<div class="row">
22+
<div class="col-md-12 py-2">
23+
<div class="tp-align-right mt-3">
24+
<a href="file_1.txt" id="file-1" download>
25+
File 1
26+
</a>
27+
</div>
28+
<div class="form-group tp-align-right mt-3">
29+
<a href="file_2.jpg" id="file-2" download>
30+
File 2
31+
</a>
32+
</div>
33+
</div>
34+
35+
</div>
36+
</div>
37+
</main>
38+
</body>
39+
40+
</html>

common/src/web/downloads/file_1.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello, World!

common/src/web/downloads/file_2.jpg

13.8 KB
Loading

java/src/org/openqa/selenium/grid/data/DefaultSlotMatcher.java

+17
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ public boolean matches(Capabilities stereotype, Capabilities capabilities) {
6464
return false;
6565
}
6666

67+
if (!managedDownloadsEnabled(stereotype, capabilities)) {
68+
return false;
69+
}
70+
6771
if (!platformVersionMatch(stereotype, capabilities)) {
6872
return false;
6973
}
@@ -106,6 +110,19 @@ private Boolean initialMatch(Capabilities stereotype, Capabilities capabilities)
106110
.orElse(true);
107111
}
108112

113+
private Boolean managedDownloadsEnabled(Capabilities stereotype, Capabilities capabilities) {
114+
// First lets check if user wanted a Node with managed downloads enabled
115+
Object raw = capabilities.getCapability("se:downloadsEnabled");
116+
if (raw == null || !Boolean.parseBoolean(raw.toString())) {
117+
// User didn't ask. So lets move on to the next matching criteria
118+
return true;
119+
}
120+
// User wants managed downloads enabled to be done on this Node, let's check the stereotype
121+
raw = stereotype.getCapability("se:downloadsEnabled");
122+
// Try to match what the user requested
123+
return raw != null && Boolean.parseBoolean(raw.toString());
124+
}
125+
109126
private Boolean platformVersionMatch(Capabilities stereotype, Capabilities capabilities) {
110127
/*
111128
This platform version match is not W3C compliant but users can add Appium servers as

java/src/org/openqa/selenium/grid/node/Node.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
package org.openqa.selenium.grid.node;
1919

2020
import com.google.common.collect.ImmutableMap;
21+
2122
import org.openqa.selenium.BuildInfo;
2223
import org.openqa.selenium.Capabilities;
2324
import org.openqa.selenium.NoSuchSessionException;
25+
import org.openqa.selenium.WebDriverException;
2426
import org.openqa.selenium.grid.data.CreateSessionRequest;
2527
import org.openqa.selenium.grid.data.CreateSessionResponse;
2628
import org.openqa.selenium.grid.data.NodeId;
@@ -41,13 +43,13 @@
4143
import org.openqa.selenium.remote.tracing.SpanDecorator;
4244
import org.openqa.selenium.remote.tracing.Tracer;
4345
import org.openqa.selenium.status.HasReadyState;
44-
import org.openqa.selenium.WebDriverException;
4546

4647
import java.io.IOException;
4748
import java.net.URI;
4849
import java.util.Map;
4950
import java.util.ServiceLoader;
5051
import java.util.Set;
52+
import java.util.UUID;
5153
import java.util.logging.Logger;
5254
import java.util.stream.Collectors;
5355
import java.util.stream.StreamSupport;
@@ -210,16 +212,21 @@ public ImmutableMap<String, String> getOsInfo() {
210212
return OS_INFO;
211213
}
212214

213-
public abstract Either<WebDriverException, CreateSessionResponse> newSession(CreateSessionRequest sessionRequest);
215+
public abstract Either<WebDriverException, CreateSessionResponse> newSession(
216+
CreateSessionRequest sessionRequest);
214217

215218
public abstract HttpResponse executeWebDriverCommand(HttpRequest req);
216219

217220
public abstract Session getSession(SessionId id) throws NoSuchSessionException;
218221

219-
public TemporaryFilesystem getTemporaryFilesystem(SessionId id) throws IOException {
222+
public TemporaryFilesystem getUploadsFilesystem(SessionId id) throws IOException {
220223
throw new UnsupportedOperationException();
221224
}
222225

226+
public TemporaryFilesystem getDownloadsFilesystem(UUID uuid) throws IOException {
227+
throw new UnsupportedOperationException();
228+
}
229+
223230
public abstract HttpResponse uploadFile(HttpRequest req, SessionId id);
224231

225232
public abstract HttpResponse downloadFile(HttpRequest req, SessionId id);

java/src/org/openqa/selenium/grid/node/config/NodeFlags.java

+10-6
Original file line numberDiff line numberDiff line change
@@ -230,12 +230,16 @@ public class NodeFlags implements HasRoles {
230230
private String nodeImplementation = DEFAULT_NODE_IMPLEMENTATION;
231231

232232
@Parameter(
233-
names = {"--downloads-path"},
234-
description = "The default location wherein all browser triggered file downloads would be "
235-
+ "available to be retrieved from. This is usually the directory that you configure in "
236-
+ "your browser as the default location for storing downloaded files.")
237-
@ConfigValue(section = NODE_SECTION, name = "downloads-path", example = "")
238-
private String downloadsPath = "";
233+
names = {"--enable-managed-downloads"},
234+
arity = 1,
235+
description = "When enabled, the Grid node will automatically do the following: " +
236+
"1. Creates a directory named '$HOME/.cache/selenium/downloads/' which "
237+
+ "will now represent the directory into which files downloaded by "
238+
+ "Chrome/Firefox/Edge browser will be under. " +
239+
"2. For every new session, a sub-directory will be created/deleted so that "
240+
+ "all files that were downloaded for a given session are stored in.")
241+
@ConfigValue(section = NODE_SECTION, name = "enable-managed-downloads", example = "false")
242+
public Boolean managedDownloadsEnabled;
239243

240244
@Override
241245
public Set<Role> getRoles() {

java/src/org/openqa/selenium/grid/node/config/NodeOptions.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.openqa.selenium.json.JsonOutput;
4141
import org.openqa.selenium.net.NetworkUtils;
4242
import org.openqa.selenium.net.Urls;
43+
import org.openqa.selenium.remote.Browser;
4344
import org.openqa.selenium.remote.service.DriverService;
4445

4546
import java.io.File;
@@ -149,8 +150,9 @@ public Optional<URI> getPublicGridUri() {
149150
}
150151
}
151152

152-
public Optional<String> getDownloadsPath() {
153-
return config.get(NODE_SECTION, "downloads-path");
153+
public boolean isManagedDownloadsEnabled() {
154+
return config.getBool(NODE_SECTION, "enable-managed-downloads")
155+
.orElse(Boolean.FALSE);
154156
}
155157

156158
public Node getNode() {
@@ -634,9 +636,17 @@ private Capabilities enhanceStereotype(Capabilities capabilities) {
634636
.setCapability("se:vncEnabled", true)
635637
.setCapability("se:noVncPort", noVncPort());
636638
}
639+
if (isManagedDownloadsEnabled() && canConfigureDownloadsDir(capabilities)) {
640+
capabilities = new PersistentCapabilities(capabilities)
641+
.setCapability("se:downloadsEnabled", true);
642+
}
637643
return capabilities;
638644
}
639645

646+
private boolean canConfigureDownloadsDir(Capabilities caps) {
647+
return Browser.FIREFOX.is(caps) || Browser.CHROME.is(caps) || Browser.EDGE.is(caps);
648+
}
649+
640650
private void report(Map.Entry<WebDriverInfo, Collection<SessionFactory>> entry) {
641651
StringBuilder caps = new StringBuilder();
642652
try (JsonOutput out = JSON.newOutput(caps)) {

0 commit comments

Comments
 (0)