Skip to content

Commit

Permalink
[js] Add support for /session/:sessionId/chromium/send_command (#5159)
Browse files Browse the repository at this point in the history
  • Loading branch information
jleyba committed Jan 8, 2018
1 parent 17240b3 commit f16c321
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 1 deletion.
3 changes: 3 additions & 0 deletions javascript/node/selenium-webdriver/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ mode.
- Added setChromeService, setEdgeService, & setFirefoxService
- Removed setEnableNativeEvents
- Removed setScrollBehavior
* Changes to `chrome.Driver`
- Added sendDevToolsCommand
- Added setDownloadPath
* Changes to `chrome.Options`
- Now extends the `Capabilities` class
- Removed from/toCapabilities
Expand Down
52 changes: 51 additions & 1 deletion javascript/node/selenium-webdriver/chrome.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ const http = require('./http');
const io = require('./io');
const {Browser, Capabilities, Capability} = require('./lib/capabilities');
const command = require('./lib/command');
const error = require('./lib/error');
const logging = require('./lib/logging');
const promise = require('./lib/promise');
const Symbols = require('./lib/symbols');
Expand All @@ -159,7 +160,8 @@ const CHROMEDRIVER_EXE =
const Command = {
LAUNCH_APP: 'launchApp',
GET_NETWORK_CONDITIONS: 'getNetworkConditions',
SET_NETWORK_CONDITIONS: 'setNetworkConditions'
SET_NETWORK_CONDITIONS: 'setNetworkConditions',
SEND_DEVTOOLS_COMMAND: 'sendDevToolsCommand',
};


Expand Down Expand Up @@ -193,6 +195,10 @@ function configureExecutor(executor) {
Command.SET_NETWORK_CONDITIONS,
'POST',
'/session/:sessionId/chromium/network_conditions');
executor.defineCommand(
Command.SEND_DEVTOOLS_COMMAND,
'POST',
'/session/:sessionId/chromium/send_command');
}


Expand Down Expand Up @@ -363,6 +369,12 @@ class Options extends Capabilities {
* > in Chrome 60. Users are encouraged to set an initial window size with
* > the {@link #windowSize windowSize({width, height})} option.
*
* > __NOTE__: For security, Chrome disables downloads by default when
* > in headless mode (to prevent sites from silently downloading files to
* > your machine). After creating a session, you may call
* > {@link ./chrome.Driver#setDownloadPath setDownloadPath} to re-enable
* > downloads, saving files in the specified directory.
*
* @return {!Options} A self reference.
*/
headless() {
Expand Down Expand Up @@ -751,6 +763,44 @@ class Driver extends webdriver.WebDriver {
new command.Command(Command.SET_NETWORK_CONDITIONS)
.setParameter('network_conditions', spec));
}

/**
* Sends an arbitrary devtools command to the browser.
*
* @param {string} cmd The name of the command to send.
* @param {Object=} params The command parameters.
* @return {!Promise<void>} A promise that will be resolved when the command
* has finished.
* @see <https://chromedevtools.github.io/devtools-protocol/>
*/
sendDevToolsCommand(cmd, params = {}) {
return this.execute(
new command.Command(Command.SEND_DEVTOOLS_COMMAND)
.setParameter('cmd', cmd)
.setParameter('params', params));
}

/**
* Sends a DevTools command to change Chrome's download directory.
*
* @param {string} path The desired download directory.
* @return {!Promise<void>} A promise that will be resolved when the command
* has finished.
* @see #sendDevToolsCommand
*/
async setDownloadPath(path) {
if (!path || typeof path !== 'string') {
throw new error.InvalidArgumentError('invalid download path');
}
const stat = await io.stat(path);
if (!stat.isDirectory()) {
throw new error.InvalidArgumentError('not a directory: ' + path);
}
return this.sendDevToolsCommand('Page.setDownloadBehavior', {
'behavior': 'allow',
'downloadPath': path
});
}
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<!DOCTYPE html>
<h1>Hello, world!</h1>
93 changes: 93 additions & 0 deletions javascript/node/selenium-webdriver/test/chrome/devtools_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// 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.

'use strict';

const assert = require('assert');
const fs = require('fs');
const path = require('path');

const chrome = require('../../chrome');
const error = require('../../lib/error');
const fileServer = require('../../lib/test/fileserver');
const io = require('../../io');
const test = require('../../lib/test');
const webdriver = require('../..');

test.suite(function(env) {
let driver;

before(async function() {
driver = await env.builder()
.setChromeOptions(new chrome.Options().headless())
.build();
});
after(() => driver.quit());

it('can send commands to devtools', async function() {
await driver.get(test.Pages.ajaxyPage);
assert.equal(await driver.getCurrentUrl(), test.Pages.ajaxyPage);

await driver.sendDevToolsCommand(
'Page.navigate', {url: test.Pages.echoPage});
assert.equal(await driver.getCurrentUrl(), test.Pages.echoPage);
});

describe('setDownloadPath', function() {
it('can enable downloads in headless mode', async function() {
const dir = await io.tmpDir();
await driver.setDownloadPath(dir);

const url = fileServer.whereIs('/data/chrome/download.html');
await driver.get(`data:text/html,<!DOCTYPE html>
<div><a download="" href="${url}">Go!</a></div>`);

await driver.findElement({css: 'a'}).click();

const downloadPath = path.join(dir, 'download.html');
await driver.wait(() => io.exists(downloadPath), 1000);

const goldenPath =
path.join(__dirname, '../../lib/test/data/chrome/download.html');
assert.equal(
fs.readFileSync(downloadPath, 'utf8'),
fs.readFileSync(goldenPath, 'utf8'));
});

it('throws if path is not a directory', async function() {
await assertInvalidArgumentError(() => driver.setDownloadPath());
await assertInvalidArgumentError(() => driver.setDownloadPath(null));
await assertInvalidArgumentError(() => driver.setDownloadPath(''));
await assertInvalidArgumentError(() => driver.setDownloadPath(1234));

const file = await io.tmpFile();
await assertInvalidArgumentError(() => driver.setDownloadPath(file));

async function assertInvalidArgumentError(fn) {
try {
await fn();
return Promise.reject(Error('should have failed'));
} catch (err) {
if (err instanceof error.InvalidArgumentError) {
return;
}
throw err;
}
}
});
});
}, {browsers: ['chrome']});

0 comments on commit f16c321

Please sign in to comment.