Skip to content

Commit

Permalink
[java] implement file downloads (#12979)
Browse files Browse the repository at this point in the history
* [java] download file to disk without string return

* [java] implement options method to toggle downloads enabled
  • Loading branch information
titusfortner committed Nov 1, 2023
1 parent 9b0d14f commit 96f13f8
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 75 deletions.
61 changes: 61 additions & 0 deletions java/src/org/openqa/selenium/HasDownloads.java
@@ -0,0 +1,61 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you 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 org.openqa.selenium;

import java.io.IOException;
import java.nio.file.Path;
import java.util.List;

/** Indicates that a driver supports downloading remote files. */
public interface HasDownloads {

/**
* Requires downloads to be enabled.
*
* <p>TODO: Create an example in the documentation and provide a link to it.
*
* @param capabilities the capabilities object
* @throws WebDriverException if capability to enable downloads is not set
*/
default void requireDownloadsEnabled(Capabilities capabilities) {
boolean downloadsEnabled = (boolean) capabilities.getCapability("se:downloadsEnabled");
if (!downloadsEnabled) {
throw new WebDriverException(
"You must enable downloads in order to work with downloadable files.");
}
}

/**
* Gets the downloadable files.
*
* @return a list of downloadable files for each key
*/
List<String> getDownloadableFiles();

/**
* Downloads a file to a given location.
*
* @param fileName the name of the file to be downloaded
* @param targetLocation the location where the file will be downloaded to
* @throws IOException if an I/O error occurs while downloading the file
*/
void downloadFile(String fileName, Path targetLocation) throws IOException;

/** Deletes the downloadable files. */
void deleteDownloadableFiles();
}
Expand Up @@ -19,6 +19,7 @@

import static org.openqa.selenium.remote.CapabilityType.ACCEPT_INSECURE_CERTS;
import static org.openqa.selenium.remote.CapabilityType.BROWSER_VERSION;
import static org.openqa.selenium.remote.CapabilityType.ENABLE_DOWNLOADS;
import static org.openqa.selenium.remote.CapabilityType.PAGE_LOAD_STRATEGY;
import static org.openqa.selenium.remote.CapabilityType.PLATFORM_NAME;
import static org.openqa.selenium.remote.CapabilityType.PROXY;
Expand Down Expand Up @@ -101,6 +102,11 @@ public DO setProxy(Proxy proxy) {
return (DO) this;
}

public DO setEnableDownloads(boolean enableDownloads) {
setCapability(ENABLE_DOWNLOADS, enableDownloads);
return (DO) this;
}

@Override
public Set<String> getCapabilityNames() {
TreeSet<String> names = new TreeSet<>(super.getCapabilityNames());
Expand Down
1 change: 1 addition & 0 deletions java/src/org/openqa/selenium/remote/CapabilityType.java
Expand Up @@ -30,4 +30,5 @@ public interface CapabilityType {
String TIMEOUTS = "timeouts";
String STRICT_FILE_INTERACTABILITY = "strictFileInteractability";
String UNHANDLED_PROMPT_BEHAVIOUR = "unhandledPromptBehavior";
String ENABLE_DOWNLOADS = "se:downloadsEnabled";
}
3 changes: 3 additions & 0 deletions java/src/org/openqa/selenium/remote/DriverCommand.java
Expand Up @@ -171,6 +171,9 @@ public interface DriverCommand {
String GET_FEDCM_DIALOG_TYPE = "getFedCmDialogType";
String SET_DELAY_ENABLED = "setDelayEnabled";
String RESET_COOLDOWN = "resetCooldown";
String GET_DOWNLOADABLE_FILES = "getDownloadableFiles";
String DOWNLOAD_FILE = "downloadFile";
String DELETE_DOWNLOADABLE_FILES = "deleteDownloadableFiles";

static CommandPayload NEW_SESSION(Capabilities capabilities) {
Require.nonNull("Capabilities", capabilities);
Expand Down
50 changes: 50 additions & 0 deletions java/src/org/openqa/selenium/remote/RemoteWebDriver.java
Expand Up @@ -24,8 +24,10 @@

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Base64;
Expand All @@ -51,6 +53,7 @@
import org.openqa.selenium.Cookie;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.HasCapabilities;
import org.openqa.selenium.HasDownloads;
import org.openqa.selenium.ImmutableCapabilities;
import org.openqa.selenium.JavascriptException;
import org.openqa.selenium.JavascriptExecutor;
Expand Down Expand Up @@ -82,6 +85,7 @@
import org.openqa.selenium.interactions.Sequence;
import org.openqa.selenium.internal.Debug;
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.io.Zip;
import org.openqa.selenium.json.TypeToken;
import org.openqa.selenium.logging.LocalLogs;
import org.openqa.selenium.logging.LoggingHandler;
Expand All @@ -105,6 +109,7 @@ public class RemoteWebDriver
implements WebDriver,
JavascriptExecutor,
HasCapabilities,
HasDownloads,
HasFederatedCredentialManagement,
HasVirtualAuthenticator,
Interactive,
Expand Down Expand Up @@ -707,6 +712,51 @@ public void removeVirtualAuthenticator(VirtualAuthenticator authenticator) {
ImmutableMap.of("authenticatorId", authenticator.getId()));
}

/**
* Retrieves the downloadable files as a map of file names and their corresponding URLs.
*
* @return A map containing file names as keys and URLs as values.
* @throws WebDriverException if capability to enable downloads is not set
*/
@Override
@SuppressWarnings("unchecked")
public List<String> getDownloadableFiles() {
requireDownloadsEnabled(capabilities);

Response response = execute(DriverCommand.GET_DOWNLOADABLE_FILES);
Map<String, List<String>> value = (Map<String, List<String>>) response.getValue();
return value.get("names");
}

/**
* Downloads a file from the specified location.
*
* @param fileName the name of the file to download
* @param targetLocation the location where the file should be downloaded
* @throws IOException if an I/O error occurs during the file download
*/
@SuppressWarnings("unchecked")
@Override
public void downloadFile(String fileName, Path targetLocation) throws IOException {
requireDownloadsEnabled(capabilities);

Response response = execute(DriverCommand.DOWNLOAD_FILE, Map.of("name", fileName));
String contents = ((Map<String, String>) response.getValue()).get("contents");
Zip.unzip(contents, targetLocation.toFile());
}

/**
* Deletes all downloadable files.
*
* @throws WebDriverException capability to enable downloads must be set
*/
@Override
public void deleteDownloadableFiles() {
requireDownloadsEnabled(capabilities);

execute(DriverCommand.DELETE_DOWNLOADABLE_FILES);
}

@Override
public void setDelayEnabled(boolean enabled) {
execute(DriverCommand.SET_DELAY_ENABLED(enabled));
Expand Down
Expand Up @@ -32,6 +32,8 @@
import static org.openqa.selenium.remote.DriverCommand.CLOSE;
import static org.openqa.selenium.remote.DriverCommand.DELETE_ALL_COOKIES;
import static org.openqa.selenium.remote.DriverCommand.DELETE_COOKIE;
import static org.openqa.selenium.remote.DriverCommand.DELETE_DOWNLOADABLE_FILES;
import static org.openqa.selenium.remote.DriverCommand.DOWNLOAD_FILE;
import static org.openqa.selenium.remote.DriverCommand.ELEMENT_EQUALS;
import static org.openqa.selenium.remote.DriverCommand.ELEMENT_SCREENSHOT;
import static org.openqa.selenium.remote.DriverCommand.FIND_CHILD_ELEMENT;
Expand All @@ -51,6 +53,7 @@
import static org.openqa.selenium.remote.DriverCommand.GET_CREDENTIALS;
import static org.openqa.selenium.remote.DriverCommand.GET_CURRENT_CONTEXT_HANDLE;
import static org.openqa.selenium.remote.DriverCommand.GET_CURRENT_URL;
import static org.openqa.selenium.remote.DriverCommand.GET_DOWNLOADABLE_FILES;
import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_LOCATION;
import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_RECT;
import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_SIZE;
Expand Down Expand Up @@ -237,6 +240,10 @@ public AbstractHttpCommandCodec() {
defineCommand(GET_FEDCM_DIALOG_TYPE, get(fedcm + "/getdialogtype"));
defineCommand(SET_DELAY_ENABLED, post(fedcm + "/setdelayenabled"));
defineCommand(RESET_COOLDOWN, post(fedcm + "/resetCooldown"));

defineCommand(GET_DOWNLOADABLE_FILES, get(sessionId + "/se/files"));
defineCommand(DOWNLOAD_FILE, post(sessionId + "/se/files"));
defineCommand(DELETE_DOWNLOADABLE_FILES, delete(sessionId + "/se/files"));
}

protected static CommandSpec delete(String path) {
Expand Down
2 changes: 2 additions & 0 deletions java/test/org/openqa/selenium/chrome/ChromeOptionsTest.java
Expand Up @@ -84,6 +84,7 @@ void canAddW3CCompliantOptions() {
.setAcceptInsecureCerts(true)
.setPageLoadStrategy(PageLoadStrategy.EAGER)
.setStrictFileInteractability(true)
.setEnableDownloads(true)
.setImplicitWaitTimeout(Duration.ofSeconds(1))
.setPageLoadTimeout(Duration.ofSeconds(2))
.setScriptTimeout(Duration.ofSeconds(3));
Expand All @@ -96,6 +97,7 @@ void canAddW3CCompliantOptions() {
assertThat(mappedOptions.get("acceptInsecureCerts")).isEqualTo(true);
assertThat(mappedOptions.get("pageLoadStrategy")).hasToString("eager");
assertThat(mappedOptions.get("strictFileInteractability")).isEqualTo(true);
assertThat(mappedOptions.get("se:downloadsEnabled")).isEqualTo(true);

Map<String, Long> expectedTimeouts = new HashMap<>();
expectedTimeouts.put("implicit", 1000L);
Expand Down

0 comments on commit 96f13f8

Please sign in to comment.