Skip to content

Commit f03a1d7

Browse files
authored
Documentation fix for Manage Downloads feature (SeleniumHQ#1351)
Updated the documentation to reflect the current Capabilities of “auto manage downloads” feature. [deploy site]
1 parent 5a80353 commit f03a1d7

File tree

8 files changed

+708
-147
lines changed

8 files changed

+708
-147
lines changed

website_and_docs/content/documentation/grid/configuration/cli_options.en.md

+172-32
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ pull request updating this page.
224224
| `--drain-after-session-count`| int | `1` | Drain and shutdown the Node after X sessions have been executed. Useful for environments like Kubernetes. A value higher than zero enables this feature. |
225225
| `--hub`| string | `http://localhost:4444` | The address of the Hub in a Hub-and-Node configuration. Can be a hostname or IP address (`hostname`), in which case the Hub will be assumed to be `http://hostname:4444`, the `--grid-url` will be the same `--publish-events` will be `tcp://hostname:4442` and `--subscribe-events` will be `tcp://hostname:4443`. If `hostname` contains a port number, that will be used for `--grid-url` but the URIs for the event bus will remain the same. Any of these default values may be overridden but setting the correct flags. If the hostname has a protocol (such as `https`) that will be used too. |
226226
| `--enable-cdp`| boolean | `true` | Enable CDP proxying in Grid. A Grid admin can disable CDP if the network doesnot allow websockets. True by default. |
227-
| `--downloads-path`| string | `/usr/downloads` | The default location wherein all browser triggered file downloads would be available to be retrieved from. This is usually the directory that you configure in your browser as the default location for storing downloaded files. |
227+
| `--enable-managed-downloads`| boolean | `false` | This causes the Node to auto manage files downloaded for a given session on the Node. |
228228
| `--selenium-manager`| boolean | `false` | When drivers are not available on the current system, use Selenium Manager. False by default. |
229229

230230
### Relay
@@ -352,101 +352,241 @@ driver.quit();
352352

353353
Set the custom capability to `false` in order to match the Node B.
354354

355-
#### Specifying path from where downloaded files can be retrieved
355+
#### Enabling Managed downloads by the Node
356356

357-
At times a test may need to access files that were downloaded by it on the Node. To retrieve
358-
such files, following can be done.
357+
At times a test may need to access files that were downloaded by it on the Node.
358+
To retrieve such files, following can be done.
359359

360360
##### Start the Hub
361361
```
362362
java -jar selenium-server-<version>.jar hub
363363
```
364364

365-
##### Start the Node with downloads path specified
365+
##### Start the Node with manage downloads enabled
366366
```
367-
java -jar selenium-server-<version>.jar node --downloads-path /usr/downloads
367+
java -jar selenium-server-<version>.jar node --enable-managed-downloads true
368368
```
369-
##### Important information when dowloading a file:
369+
##### Set the capability at the test level
370370

371-
* The endpoint to `GET` from is `/session/<sessionId>/se/file?filename=`
371+
Tests that want to use this feature should set the capability `"se:downloadsEnabled"`to `true`
372+
373+
```java
374+
options.setCapability("se:downloadsEnabled", true);
375+
```
376+
377+
##### How does this work
378+
379+
* The Grid infrastructure will try to match a session request with `"se:downloadsEnabled"` against ONLY those nodes which were started with `--enable-managed-downloads true`
380+
* If a session is matched, then the Node automatically sets the required capabilities to let the browser know, as to where should a file be downloaded.
381+
* The Node now allows a user to:
382+
* List all the files that were downloaded for a specific session and
383+
* Retrieve a specific file from the list of files.
384+
* The directory into which files were downloaded for a specific session gets automatically cleaned up when the session ends (or) timesout due to inactivity.
385+
386+
**Note: Currently this capability is ONLY supported on:**
387+
388+
* `Edge`
389+
* `Firefox` and
390+
* `Chrome` browser
391+
392+
##### Listing files that can be downloaded for current session:
393+
394+
* The endpoint to `GET` from is `/session/<sessionId>/se/files`.
395+
* The session needs to be active in order for the command to work.
396+
* The raw response looks like below:
397+
398+
```json
399+
{
400+
"value": {
401+
"names": [
402+
"Red-blue-green-channel.jpg"
403+
]
404+
}
405+
}
406+
```
407+
408+
In the response the list of file names appear under the key `names`.
409+
410+
411+
##### Dowloading a file:
412+
413+
* The endpoint to `POST` from is `/session/<sessionId>/se/files` with a payload of the form `{"name": "fileNameGoesHere}`
372414
* The session needs to be active in order for the command to work.
415+
* The raw response looks like below:
416+
417+
```json
418+
{
419+
"value": {
420+
"filename": "Red-blue-green-channel.jpg",
421+
"contents": "Base64EncodedStringContentsOfDownloadedFileAsZipGoesHere"
422+
}
423+
}
424+
```
425+
373426
* The response blob contains two keys,
374-
* `filename` - Same as what was specified in the request.
427+
* `filename` - The file name that was downloaded.
375428
* `contents` - Base64 encoded zipped contents of the file.
376429
* The file contents are Base64 encoded and they need to be unzipped.
377430

378-
##### Sample that retrieves the downloaded file
431+
##### List files that can be downloaded
432+
433+
The below mentioned `curl` example can be used to list all the files that were downloaded by the current session in the Node, and which can be retrieved locally.
379434

380-
Assuming the downloaded file is named `my_file.pdf`, and using `curl`, the
435+
```bash
436+
curl -X GET "http://localhost:4444/session/90c0149a-2e75-424d-857a-e78734943d4c/se/files"
437+
```
438+
439+
A sample response would look like below:
440+
441+
```json
442+
{
443+
"value": {
444+
"names": [
445+
"Red-blue-green-channel.jpg"
446+
]
447+
}
448+
}
449+
```
450+
451+
##### Retrieve a downloaded file
452+
453+
Assuming the downloaded file is named `Red-blue-green-channel.jpg`, and using `curl`, the
381454
file could be downloaded with the following command:
382455

383456
```bash
384-
curl -X GET "http://localhost:4444/session/<sessionId>/se/file?filename=my_file.pdf"
457+
curl -H "Accept: application/json" \
458+
-H "Content-Type: application/json; charset=utf-8" \
459+
-X POST -d '{"name":"Red-blue-green-channel.jpg"}' \
460+
"http://localhost:4444/session/18033434-fa4f-4d11-a7df-9e6d75920e19/se/files"
461+
```
462+
463+
A sample response would look like below:
464+
465+
```json
466+
{
467+
"value": {
468+
"filename": "Red-blue-green-channel.jpg",
469+
"contents": "UEsDBBQACAgIAJpagVYAAAAAAAAAAAAAAAAaAAAAUmVkLWJsAAAAAAAAAAAAUmVkLWJsdWUtZ3JlZW4tY2hhbm5lbC5qcGdQSwUGAAAAAAEAAQBIAAAAcNkAAAAA"
470+
}
471+
}
385472
```
386473

387-
Below is an example in Java that shows how to download a file named `my_file.pdf`.
474+
##### Complete sample code in Java
475+
476+
Below is an example in Java that does the following:
477+
478+
* Sets the capability to indicate that the test requires automatic managing of downloaded files.
479+
* Triggers a file download via a browser.
480+
* Lists the files that are available for retrieval from the remote node (These are essentially files that were downloaded in the current session)
481+
* Picks one file and downloads the file from the remote node to the local machine.
388482

389483
```java
484+
import static java.util.Collections.singletonMap;
390485
import static org.openqa.selenium.remote.http.Contents.string;
391486

392487
import java.io.File;
393-
import java.io.IOException;
488+
import java.lang.reflect.Type;
394489
import java.net.URL;
490+
import java.util.List;
395491
import java.util.Map;
396492
import java.util.concurrent.TimeUnit;
397493
import org.openqa.selenium.By;
398494
import org.openqa.selenium.WebElement;
399495
import org.openqa.selenium.firefox.FirefoxOptions;
400496
import org.openqa.selenium.io.Zip;
401497
import org.openqa.selenium.json.Json;
498+
import org.openqa.selenium.json.TypeToken;
402499
import org.openqa.selenium.remote.RemoteWebDriver;
500+
import org.openqa.selenium.remote.http.Contents;
403501
import org.openqa.selenium.remote.http.HttpClient;
404502
import org.openqa.selenium.remote.http.HttpMethod;
405503
import org.openqa.selenium.remote.http.HttpRequest;
406504
import org.openqa.selenium.remote.http.HttpResponse;
407505

408506
public class DownloadsSample {
409507

410-
public static void main(String[] args) throws InterruptedException, IOException {
411-
// Make sure the following directory exists on your machine
412-
File dirToCopyTo = new File("/usr/downloads/file");
508+
public static void main(String[] args) throws Exception {
413509
// Assuming the Grid is running locally.
414510
URL gridUrl = new URL("http://localhost:4444");
415511
RemoteWebDriver driver = new RemoteWebDriver(gridUrl, firefoxOptions());
416-
// This public website resets the available files for dowload on a daily basis,
417-
// check the name of the file that will be downloaded and replace it below.
512+
try {
513+
demoFileDownloads(driver, gridUrl);
514+
} finally {
515+
driver.quit();
516+
}
517+
}
518+
519+
private static void demoFileDownloads(RemoteWebDriver driver, URL gridUrl) throws Exception {
520+
// Make sure the following directory exists on your machine
521+
File dirToCopyTo = new File("/usr/downloads/file");
418522
driver.get("http://the-internet.herokuapp.com/download");
419523
WebElement element = driver.findElement(By.cssSelector(".example a"));
420524
element.click();
421525

422-
// The download happens in a remote Node, which makes difficult to know when the file
423-
// has been completely downloaded. For demonstration purposes, this example uses a
424-
// 10 second sleep which should be enough time for a file to be downloaded.
425-
// We strongly recommend to avoid hardcoded sleeps, and ideally, to modify your
426-
// application under test so it offers a way to know when the file has been completely
427-
// downloaded.
526+
// The download happens in a remote Node, which makes it difficult to know when the file
527+
// has been completely downloaded. For demonstration purposes, this example uses a
528+
// 10-second sleep which should be enough time for a file to be downloaded.
529+
// We strongly recommend to avoid hardcoded sleeps, and ideally, to modify your
530+
// application under test so it offers a way to know when the file has been completely
531+
// downloaded.
428532
TimeUnit.SECONDS.sleep(10);
429533

430-
HttpRequest request = new HttpRequest(HttpMethod.GET, String.format("/session/%s/se/file", driver.getSessionId()));
431-
request.addQueryParameter("filename", "my_file.pdf");
534+
//This is the endpoint which will provide us with list of files to download and also to
535+
//let us download a specific file.
536+
String uri = String.format("/session/%s/se/files", driver.getSessionId());
537+
538+
String fileToDownload = null;
539+
432540
try (HttpClient client = HttpClient.Factory.createDefault().createClient(gridUrl)) {
541+
// To list all files that are were downloaded on the remote node for the current session
542+
// we trigger GET request.
543+
HttpRequest request = new HttpRequest(HttpMethod.GET, uri);
433544
HttpResponse response = client.execute(request);
434-
Map<String, Object> map = new Json().toType(string(response), Json.MAP_TYPE);
545+
String text = string(response);
546+
Type responseType = new TypeToken<Map<String, Map<String, List<String>>>>() {
547+
}.getType();
548+
Map<String, Map<String, List<String>>> map = new Json().toType(text, responseType);
549+
Map<String, List<String>> parsedResponse = map.get("value");
550+
for (String eachFile : parsedResponse.get("names")) {
551+
if (fileToDownload == null) {
552+
// Let's say there were "n" files downloaded for the current session, we would like
553+
// to retrieve ONLY the first file.
554+
fileToDownload = eachFile;
555+
}
556+
}
557+
}
558+
try (HttpClient client = HttpClient.Factory.createDefault().createClient(gridUrl)) {
559+
// To retrieve a specific file from one or more files that were downloaded by the current session
560+
// on a remote node, we use a POST request.
561+
562+
HttpRequest request = new HttpRequest(HttpMethod.POST, uri);
563+
request.setContent(Contents.asJson(singletonMap("name", fileToDownload)));
564+
HttpResponse response = client.execute(request);
565+
String text = string(response);
566+
Type responseType = new TypeToken<Map<String, Map<String, String>>>() {
567+
}.getType();
568+
569+
Map<String, Map<String, String>> map = new Json().toType(text, responseType);
570+
Map<String, String> parsedResponse = map.get("value");
435571
// The returned map would contain 2 keys,
436572
// filename - This represents the name of the file (same as what was provided by the test)
437573
// contents - Base64 encoded String which contains the zipped file.
438-
String encodedContents = map.get("contents").toString();
574+
String encodedContents = parsedResponse.get("contents");
439575

440576
//The file contents would always be a zip file and has to be unzipped.
441577
Zip.unzip(encodedContents, dirToCopyTo);
442-
} finally {
443-
driver.quit();
578+
System.out.println("The file which was "
579+
+ "downloaded in the node is now available in the directory: "
580+
+ dirToCopyTo.getAbsolutePath());
444581
}
445582
}
446583

447584
private static FirefoxOptions firefoxOptions() {
448585
FirefoxOptions options = new FirefoxOptions();
449-
// Options specific for Firefox to avoid prompting a dialog for downloads. They might
586+
// This capability tells the Grid, that this test should be routed ONLY to a node that can
587+
// auto manage downloads.
588+
options.setCapability("se:downloadsEnabled", true);
589+
// Options specific for Firefox to avoid prompting a dialog for downloads. They might
450590
// change in the future, so please refer to the Firefox documentation for up to date details.
451591
options.addPreference("browser.download.manager.showWhenStarting", false);
452592
options.addPreference("browser.helperApps.neverAsk.saveToDisk",

0 commit comments

Comments
 (0)