Chromium/Chrome headless - file download not working? - chromium

I downloaded the latest version of chromium, to test out the headless feature.
When I run (as root, because I'm still testing things):
./chrome --no-sandbox http://cp7.awardspace.com/speed-test/awardspace-data1mb.zip
In the GUI terminal, it opens Chromium and downloads the file.
If I'm trying to run it headless, I enter the following:
./chrome --no-sandbox --headless http://cp7.awardspace.com/speed-test/awardspace-data1mb.zip
The terminal outputs some information, no window gets opened, but also: I don't have the file downloaded anywhere.
I have been scouting the internet and discussion groups for more information, but cannot find anything.
Is file downloading not working in headless mode for Chromium?

That's a reported bug in headless implementation:
https://bugs.chromium.org/p/chromium/issues/detail?id=696481

Use ChromeDriverService and POST session/{sessionId}/chromium/send_command
JSON for POST:
{
"cmd": "Page.setDownloadBehavior",
"params": {
"behavior": "allow",
"downloadPath": "C:\\Download\\Path"
}
}
C# Solution
Add reference to System.Net.Http and use NuGet to install Newtonsoft.JSON.
public static IWebDriver Driver { get; private set; }
public void SetDriver()
{
var chromeOptions = new ChromeOptions();
chromeOptions.AddArguments("--headless", "--window-size=1920,1080");
var driverService = ChromeDriverService.CreateDefaultService();
Driver = new ChromeDriver(driverService, chromeOptions);
Task.Run(() => AllowHeadlessDownload(driverService));
}
static async Task AllowHeadlessDownload(ChromeDriverService driverService )
{
var jsonContent = new JObject(
new JProperty("cmd", "Page.setDownloadBehavior"),
new JProperty("params",
new JObject(new JObject(
new JProperty("behavior", "allow"),
new JProperty("downloadPath", #"C:\Download\Path")))));
var content = new StringContent(jsonContent.ToString(), Encoding.UTF8, "application/json");
var sessionIdProperty = typeof(ChromeDriver).GetProperty("SessionId");
var sessionId = sessionIdProperty.GetValue(Driver, null) as SessionId;
using (var client = new HttpClient())
{
client.BaseAddress = driverService.ServiceUrl;
var result = await client.PostAsync("session/" + sessionId.ToString() + "/chromium/send_command", content);
var resultContent = await result.Content.ReadAsStringAsync();
}
}

In Java use following code :
System.setProperty("webdriver.chrome.driver", "/usr/local/bin/chromedriver");
ChromeOptions options = new ChromeOptions();
options.addArguments("--test-type");
options.addArguments("--headless");
options.addArguments("--disable-extensions"); //to disable browser extension popup
ChromeDriverService driverService = ChromeDriverService.createDefaultService();
ChromeDriver driver = new ChromeDriver(driverService, options);
Map<String, Object> commandParams = new HashMap<>();
commandParams.put("cmd", "Page.setDownloadBehavior");
Map<String, String> params = new HashMap<>();
params.put("behavior", "allow");
params.put("downloadPath", "//home//vaibhav//Desktop");
commandParams.put("params", params);
ObjectMapper objectMapper = new ObjectMapper();
HttpClient httpClient = HttpClientBuilder.create().build();
String command = objectMapper.writeValueAsString(commandParams);
String u = driverService.getUrl().toString() + "/session/" + driver.getSessionId() + "/chromium/send_command";
HttpPost request = new HttpPost(u);
request.addHeader("content-type", "application/json");
request.setEntity(new StringEntity(command));
httpClient.execute(request);
driver.get("http://www.seleniumhq.org/download/");
driver.findElement(By.linkText("32 bit Windows IE")).click();

The following code works in C# using ChromeDriver 2.46
private ChromeDriver GetDriver()
{
var options = new ChromeOptions();
options.AddArguments("headless");
options.AddUserProfilePreference("download.prompt_for_download", "false");
options.AddUserProfilePreference("download.directory_upgrade", "true");
options.AddUserProfilePreference("download.prompt_for_download", "false");
options.AddUserProfilePreference("safebrowsing.enabled", "false");
options.AddUserProfilePreference("safebrowsing.disable_download_protection", "true");
options.AddArguments("--disable-web-security");
var curr = Directory.GetCurrentDirectory();
options.AddUserProfilePreference("download.default_directory", curr);
var driver = new ChromeDriver(options);
Log.Info($"Started Chrome Driver with options: {options.ToJsonNoTypes()}");
var param = new Dictionary<string, object>();
param.Add("behavior", "allow");
param.Add("downloadPath", curr);
driver.ExecuteChromeCommand("Page.setDownloadBehavior", param);
return driver;
}

Note: Not exactly answer to the question, but solves the problem
I researched a lot on making headless chrome download with different parameters/options/preferences, but nothing worked. Then I used standard Java way of downloading file using Apache Commons's FileUtils
FileUtils.copyURLToFile(URI, FILE);

I was able to download files with chrome headless thanks to Chrome Remote Interface
public void TryEnableFileDownloading(string downloadPath)
{
TrySendCommand("Page.setDownloadBehavior", new Dictionary<string, object>()
{
["behavior"] = "allow",
["downloadPath"] = downloadPath
});
}
Full code for integration with selenium could be found here
https://github.com/cezarypiatek/Tellurium/blob/master/Src/MvcPages/SeleniumUtils/ChromeRemoteInterface/ChromeRemoteInterface.cs
More info about setDownloadBehavior and Chrome Remote interface
https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-setDownloadBehavior

I tried today in IdeaJ editor, Java and Maven on Windows 10, and it's working fine:
ChromeOptions ds = getDesiredCapabilities(browserName);
ChromeDriverService driverService = ChromeDriverService.createDefaultService();
ChromeDriver driver = new ChromeDriver(driverService, ds);
Map<String, Object> commandParams = new HashMap<>();
commandParams.put("cmd", "Page.setDownloadBehavior");
Map<String, String> params = new HashMap<>();
params.put("behavior", "allow");
params.put("downloadPath", System.getProperty("user.home") + File.separator + "Downloads");
commandParams.put("params", params);
ObjectMapper objectMapper = new ObjectMapper();
HttpClient httpClient = HttpClientBuilder.create().build();
String command = objectMapper.writeValueAsString(commandParams);
String u = driverService.getUrl().toString() + "/session/" + driver.getSessionId() + "/chromium/send_command";
HttpPost request = new HttpPost(u);
request.addHeader("content-type", "application/json");
request.setEntity(new StringEntity(command));
httpClient.execute(request);
User this configured "driver" instance to navigate to download file either by URL or by Json/AngularJs download action.
Also it sometimes behaved to corrupt file while downloading due to internet slowness.
More on this, same configuration will work for both Google Chrome UI or Headless execution in latest chrome driver 77~

Related

Selenium: How to drag and drop in Chrome using C#

The below code works in Firefox but not in Chrome. From what I could find online it seems that Actions.DragAndDrop does not work with Chrome. I am trying to move SVG elements.
Is there an alternative?
var action = new Actions(driver);
action
.DragAndDropToOffset(middle, 100, 100)
.Build()
.Perform();
Selenium.Webdriver: v3.141.0
Selenium.Webdriver.ChromeDriver: v76.0.3809.68
Selenium.Firefox.Webdriver: v0.24.0
Chrome: Version 76.0.3809.100 (Official Build) (64-bit)
This is what I use in Chrome. Remember when using drag and drop you need a starting element to click, and a second element where you are going to drop it.
Call:
var ele1 = Driver.FindElement(By.Xpath("//button[#class='cz2__images__image-content cz2__images--draggable']"));
var ele2 = Driver.FindElement(By.Xpath("//button[#class='Destination']"));
DragAndDrop(ele1, ele2);
Method:
public static void DragAndDrop(IWebElement element1, IWebElement element2)
{
WaitForElementEnabled(element1);
WaitForElementEnabled(element2);
var builder = new Actions(Driver);
var dragAndDrop = builder.ClickAndHold(element1).MoveToElement(element2).Release(element1).Build();
dragAndDrop.Perform();
}
or....
public static void test ()
{
var test1 = _webDriver.FindElement(By.Id("myid"));
var test2 = _webDriver.FindElement(By.Id("myid2"));
Actions builder1 = new Actions(_webDriver);
IAction dragAndDrop1 = builder1.ClickAndHold(test1).MoveToElement(test2).Release(test1).Build();
dragAndDrop1.Perform();
}
public void DragAndDropItem(IWebElement from, IWebElement to)
{
Actions action = new Actions(_driver);
action.DragAndDrop(from, to).Build().Perform();
}

Unable to download file using phantomjs

When I click on a button in chrome, it downloads a jpg file.
But when I am using phantomjs it doesn't download the jpg nor gives any error.
example: https://deepak5j.github.io/HelloProjectPage/download.html
How to download file with phantomjs ?
Using casperjs (based on phantomjs) :
var casper = require("casper").create();
casper.start('https://deepak5j.github.io/HelloProjectPage/download.html', function() {
var url = 'https://raw.githubusercontent.com/Deepak5j/WebImages/master/Tiles/';
this.download(url, 'sun_tile.jpg');
});
casper.run(function() {
this.exit();
});
Button click with phantom will not download file but after click you can get file as input stream. So you can save input stream to file. Code below will give an idea:
driver.findElement(By.className("Download")).click();
List<LogEntry> harLogEntries = driver.manage().logs().get("har").getAll();
LogEntry lastLogEntry = harLogEntries.get(harLogEntries.size() - 1);
String lastRequestUrl = getRequestUrlFromHarLogEntry(lastLogEntry);
DefaultHttpClient client;
HttpResponse response;
HttpGet get = new HttpGet(lastRequestUrl);
response = client.execute(get);
InputStream dataStream = response.getEntity().getContent();
I will share getRequestUrlFromHarLogEntry below:
private String getRequestUrlFromHarLogEntry(LogEntry logEntry)
throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> message = objectMapper.readValue(logEntry.getMessage(), Map.class);
Map<String, Object> log = (Map<String, Object>) message.get("log");
List<Object> entries = (List<Object>) log.get("entries");
Map<String, Object> lastEntry = (Map<String, Object>) entries.get(entries.size() - 1);
Map<String, Object> request = (Map<String, Object>) lastEntry.get("request");
String url = (String) request.get("url");
return url;
}
Hope this helps..

With Selenium 3.8.1, Firefox, Chrome, and IE driver capabilities are deprecated.

Can anybody advise how to remove warnings from FirefoxDriver (capabilities) and
ChromeDriver (capabilities)?
FIREFOX
{
#Override
public DesiredCapabilities getDesiredCapabilities ()
{
DesiredCapabilities capabilities = DesiredCapabilities.firefox ();
return capabilities;
}
#Override
public WebDriver getWebDriverObject (DesiredCapabilities capabilities)
{
return new FirefoxDriver (capabilities);
}
},
CHROME_Original
{
#Override
public DesiredCapabilities getDesiredCapabilities ()
{
DesiredCapabilities capabilities = DesiredCapabilities.chrome ();
capabilities.setCapability ("chrome.switches", Arrays.asList ("--no-default-browser-check"));
HashMap<String, String> chromePreferences = new HashMap<String, String> ();
chromePreferences.put ("profile.password_manager_enabled", "false");
capabilities.setCapability ("chrome.prefs", chromePreferences);
return capabilities;
}
#Override
public WebDriver getWebDriverObject (DesiredCapabilities capabilities)
{
return new ChromeDriver (capabilities);
}
},
Try to use the following code:
ChromeOptions options = new ChromeOptions();
options.setCapability("chrome.switches",Arrays.asList("--no-default-browser-check"));
HashMap<String, Boolean>chromePreferences = new HashMap<>();
chromePreferences.put("profile.password_manager_enabled", false);
options.setCapability("chrome.prefs", chromePreferences);
ChromeDriver driver = new ChromeDriver(options);
The ChromeDriver constructor now takes in ChromeOptions object as a parameter
Manipulating DesiredCapabilities directly has been deprecated in favor of type-safe “Options” classes (FirefoxOptions InternetExplorerOptions, etc). This has the advantage of helping you avoid setting incorrect or invalid values for the driver. You get rid of the deprecation warnings by changing your code to use the newer, safer construct.

Selenium 2.53 : csv files not downloading automatically using the new Marionette Firefox driver

I have a automation requirement where after a certain step a csv file needs to be downloaded and saved into a folder. In order to achieve the same ,The Firefox driver has been setup as below.
FirefoxProfile profile = new FirefoxProfile();
profile.SetPreference("browser.download.folderList", 2);
try
{
profile.SetPreference("browser.download.manager.showWhenStarting", false);
}
catch
{
}
profile.SetPreference("browser.download.dir", Config.I.TempDirectory);
profile.SetPreference("browser.helperApps.alwaysAsk.force", false);
profile.SetPreference("browser.helperApps.neverAsk.saveToDisk", "text/csv");
After profile has been set with required preferences. The Firefox driver was initialized as required for using the new Marionette driver
public static RemoteWebDriver GetFirefoxDriver(FirefoxProfile profile = null)
{
FirefoxDriverService firefoxService = FirefoxDriverService.CreateDefaultService();
// this should be moved to a config file.
firefoxService.FirefoxBinaryPath = Config.I.FirefoxBinaryPath;
if (profile == null)
{
var firefoxOptions = new FirefoxProfileOptions() { IsMarionette = true };
return new FirefoxDriver(firefoxService, firefoxOptions, TimeSpan.FromSeconds(20));
}
else
{
var firefoxOption = new FirefoxProfileOptions(profile) { IsMarionette = true };
var driver = new FirefoxDriver(firefoxService, firefoxOption, TimeSpan.FromSeconds(30));
return driver;
}
}
/// <summary>
/// Extending the firefox options, so that both profile and service can be set for driver at the time of initialization,
/// Need to check whether the same needs to be here or not.
/// </summary>
public class FirefoxProfileOptions : FirefoxOptions
{
private DesiredCapabilities _capabilities;
public FirefoxProfileOptions()
: base()
{
_capabilities = DesiredCapabilities.Firefox();
_capabilities.SetCapability("marionette", this.IsMarionette);
}
public FirefoxProfileOptions(FirefoxProfile profile)
: this()
{
_capabilities.SetCapability(FirefoxDriver.ProfileCapabilityName, profile.ToBase64String());
}
public override void AddAdditionalCapability(string capabilityName, object capabilityValue)
{
_capabilities.SetCapability(capabilityName, capabilityValue);
}
}
The code was working properly before we upgraded to selenium 2.53.1 and Firefox 48. Any suggestions or pointers in the right direction would be of great help.

Not able to download .csv file through Google Chrome in headless mode

I have been trying to download a csv file using selenium in Chrome headless mode but it is not working for my scenario. I tried to run the same script without headless and it works. I tried the same headless configuration with https://www.mockaroo.com/ site and it worked. Below is the chrome headless configuration that I'm using
System.setProperty("webdriver.chrome.driver", Constants.CHROME_DRIVER);
ChromeOptions options = new ChromeOptions();
options.addArguments("--test-type");
options.addArguments("--headless");
options.addArguments("--disable-extensions"); //to disable browser extension popup
options.addArguments("window-size=1980,1080");
options.addArguments("--disable-web-security");
options.addArguments("--no-sandbox");
options.addArguments("--disable-gpu");
options.addArguments("--disable-popup-blocking");
options.addArguments("--start-maximized");
options.addArguments("--allow-running-insecure-content");
options.setExperimentalOption("w3c", false);
options.addArguments("--ignore-certificate-errors");
options.addArguments("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36");
DesiredCapabilities cap = DesiredCapabilities.chrome();
cap.setCapability(ChromeOptions.CAPABILITY, options);
// set performance logger
// this sends Network.enable to chromedriver
LoggingPreferences logPrefs = new LoggingPreferences();
logPrefs.enable(LogType.PERFORMANCE, Level.ALL);
cap.setCapability("goog:loggingPrefs", logPrefs);
ChromeDriverService driverService = ChromeDriverService.createDefaultService();
driver = new ChromeDriver(driverService, cap);
String downloadFilepath = "C:\\Users\\User\\Downloads";
System.out.println(downloadFilepath);
Map < String, Object > commandParams = new HashMap<String, Object>();
commandParams.put("cmd", "Page.setDownloadBehavior");
Map < String, Object > params = new HashMap<String, Object>();
params.put("behavior", "allow");
params.put("download.prompt_for_download", false);
params.put("downloadPath", downloadFilepath);
commandParams.put("params", params);
ObjectMapper objectMapper = new ObjectMapper();
HttpClient httpClient = HttpClientBuilder.create().build();
try {
String command = objectMapper.writeValueAsString(commandParams);
String u = driverService.getUrl().toString() + "/session/" + ((ChromeDriver)driver).getSessionId() + "/chromium/send_command";
System.out.println("U: " + u.toString());
HttpPost request = new HttpPost(u);
request.addHeader("content-type", "application/json");
request.setEntity(new StringEntity(command));
httpClient.execute(request);
} catch (Exception e) { }
driver.get(prop.getProperty("url"));
With the above driver configuration (after authenticating the user), I'm clicking a button which submits the form data and in response, I am getting:
Content-Disposition: attachment;filename=WIS_mop_mis3.csv
which downloads the file.
Please find the logs below where you can see the highlighted part where it says that net::ERR_ABORTED but I am getting this error for both headless & without headless;
Performance logs for headless mode
Anyone have any idea, where I am doing wrong.
Edit: The application flow is when user clicks on the export button a new window gets opened where user fills the data and clicks on a button which calls a function for some sort of validation. After validation, it submits the form using document.formName.submit().
I am able to figure out the solution. Below is the working code
System.setProperty("webdriver.chrome.driver", Constants.CHROME_DRIVER);
ChromeOptions options = new ChromeOptions();
options.addArguments("--test-type");
options.addArguments("--headless");
options.addArguments("--disable-extensions"); //to disable browser extension popup
options.addArguments("window-size=1980,1080");
options.addArguments("--disable-web-security");
options.addArguments("--no-sandbox");
options.addArguments("--disable-gpu");
options.addArguments("--disable-popup-blocking");
options.addArguments("--start-maximized");
options.addArguments("--allow-running-insecure-content");
options.setExperimentalOption("w3c", false);
options.addArguments("--ignore-certificate-errors");
options.addArguments("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36");
HashMap<String, Object> chromePreferences = new HashMap<String, Object>();
// This part was missing in my old code.
chromePreferences.put("download.prompt_for_download", false);
chromePreferences.put("download.directory_upgrade", true);
chromePreferences.put("safebrowsing.enabled", false);
chromePreferences.put("profile.default_content_settings.popups", 0);
chromePreferences.put("safebrowsing.disable_download_protection", true);
chromePreferences.put("download.default_directory", downloadFilepath);
options.setExperimentalOption("prefs", chromePreferences);
DesiredCapabilities cap = DesiredCapabilities.chrome();
cap.setCapability(ChromeOptions.CAPABILITY, options);
// set performance logger
// this sends Network.enable to chromedriver
LoggingPreferences logPrefs = new LoggingPreferences();
logPrefs.enable(LogType.PERFORMANCE, Level.ALL);
cap.setCapability("goog:loggingPrefs", logPrefs);
ChromeDriverService driverService = ChromeDriverService.createDefaultService();
driver = new ChromeDriver(driverService, cap);
String downloadFilepath = "C:\\Users\\User\\Downloads";
System.out.println(downloadFilepath);
Map < String, Object > commandParams = new HashMap<String, Object>();
commandParams.put("cmd", "Page.setDownloadBehavior");
Map < String, Object > params = new HashMap<String, Object>();
params.put("behavior", "allow");
params.put("downloadPath", downloadFilepath);
commandParams.put("params", params);
ObjectMapper objectMapper = new ObjectMapper();
HttpClient httpClient = HttpClientBuilder.create().build();
try {
String command = objectMapper.writeValueAsString(commandParams);
String u = driverService.getUrl().toString() + "/session/" + ((ChromeDriver)driver).getSessionId() + "/chromium/send_command";
System.out.println("U: " + u.toString());
HttpPost request = new HttpPost(u);
request.addHeader("content-type", "application/json");
request.setEntity(new StringEntity(command));
httpClient.execute(request);
} catch (Exception e) { }
This post helped me to fix this issue https://stackoverflow.com/a/52384942/6017332