According to this article, Selenium 4 alpha has a sendDevToolsCommand that sends an arbitrary DevTools command to the browser and returns a promise that will be resolved when the command has finished:
Added “sendDevToolsCommand” and “setDownloadPath” for chrome.Driver.
But I can't seem to find how to use it. It sounds a bit like using JavaScript executor in Selenium.
Can someone provide an example usage? I'm using Selenium + Java.
The command to call the devtool api was added a few years back in the Chrome driver.
You can already use it with Selenium even if the method is not yet present:
Take full page screenshot
Print To PDF
Inject some Javascript before the page loads
Block a network URL
Save/restore the cookies for all domains
Get transparent screenshot
This command gives you access to the devtools api, which is used by ChromeDriver internally to drive the browser.
The method takes the name of the command as first argument and a dictionary of parameters as second argument. To figure out how to call a command, add puppeteer in your searches. For instance puppeteer set download location.
Note that executeCdpCommand is implemented in the Java master branch, so it should be available in the next release.
I couldn't find the sendDevToolsCommand in the Selenium documentation yet, but the source actually has the setDownloadPath that you also mentioned above defined right below, which actually uses the sendDevToolsCommand. Based on that usage, it seems like you could do something like:
const { Builder } = require("selenium-webdriver");
const driverInstance = await new Builder()
.withCapabilities({ browserName: "chrome" })
.build();
driverInstance.sendDevToolsCommand('Page.setDownloadBehavior', {
behavior: 'allow',
downloadPath: path
})
or for a visually obvious example:
await driverInstance.sendDevToolsCommand("Emulation.setDefaultBackgroundColorOverride", {
color: { r: 0, g: 255, b: 0, a: 1 } // watch out, it's bright!
});
where the first argument is a Chrome Devtools Protocol Domain method (e.g. or Page.setDownloadBehavior or Emulation.setCPUThrottlingRate) and the second argument is an object containing the options for that Domain method (as described in the same protocol docs).
Edit: just tested and the above works :)
I'm excited that this was added because it means that, in addition to network throttling, it should be pretty trivial to add cpu throttling to Selenium tests now! Something like:
driverInstance.sendDevToolsCommand('Emulation.setCPUThrottlingRate', {
rate: 4 // throttle cpu 4x
}
Selenium 4 release will have a user friendly API for Chrome DevTools protocol.
I just finished implementing Network and Performance domains for the Selenium Java client.
https://github.com/SeleniumHQ/selenium/pull/7212
In addition, there is a generic API for all domains in Java client that was merged a while ago.
All those new features will be released probably in the next Alpha release.
Related
I have a scenario which is a series of rest api calls but in the middle is a section that executes a few steps within a chrome browser. The browser steps are common to another scenario so I tried to extract the browser steps into a separate feature that could then be called from multiple scenarios.
When the main scenario executes it executes the browser feature but fails to auto-close the browser after execution. I read in the documentation "Karate will close the browser automatically after a Scenario unless the driver instance was created before entering the Scenario" . The configure driver code is in the callable scenario.
I also tried caling quit() but this resulted in the error: "The forked VM terminated without properly saying goodbye. VM crash or System.exit called?"
Does anyone know how I can ensure the browser closes in this circumstance?
UPDATE: As suggested by #PeterThomas I started to craft a full example to replicate this when I discovered that to replicate is actually quite simple.
If the UI feature is called like this then the browser is closed after execution:
* call read('classpath:/ui/callable/GoogleSearch.feature')
If called like this then the browser remains open:
* def result = call read('classpath:/ui/callable/GoogleSearch.feature')
My UI scenario scrapes a value from a web page which I then stored within a '* def ticket' within the called feature. I was hoping to access it via result.ticket. As I am unable to do this I am successfully using the following:
* def extractedTicket = { value: '' }
* call read('classpath:/ui/callable/GoogleSearch.feature')
* def ticket = extractedTicket.value
And within the called feature:
* set extractedTicket.value = karate.extract(val, '.ticket=(.*?)&', 1)
First, I think you should provide a way to replicate this, so that we can investigate and fix this for everyone. Please follow this process: https://github.com/karatelabs/karate/wiki/How-to-Submit-an-Issue
That said, maybe for just getting Chrome to do a few steps you should just use the Java API - and you can call it from wherever you want, even within a feature file using Java interop: https://github.com/karatelabs/karate#java-api
Also see if this answer gives you any pointers: https://stackoverflow.com/a/60387907/143475
After performing a search using POST /session/{session id}/element, I get this from the Chrome webdriver:
{ sessionId: '3241e7da289f4feb19c1f55dfc87024b',
status: 0,
value: { ELEMENT: '0.12239552668870868-1' } }
Is this what the specs demand?
I am asking because I couldn't find anywhere a spot where it said clearly "ELEMENT" in capital letters. All I can find in the specs is that a key called value is set (which it is: it's set as { ELEMENT: '0.12239552668870868-1' }
Can I always always expect this response, from other browsers' webdrivers? That is, are status and sessionId always returned?
Is that { ELEMENT: '0.12239552668870868-1' } the way chromium makes up the object? Or is this true for any webdrivers? Of not, what do other webdrivers return?
The WebDriver-W3C Candidate Recommendation clearly mentions the following points:
The Find Element command is used to find an element in the current browsing context that can be used for future commands.
Let location strategy be the result of getting a property called "using".
Let selector be the result of getting a property called "value".
The result of getting a property with argument name is defined as being the same as the result of calling Object.GetOwnProperty(propertyName).
GetOwnProperty(propertyName) in ECMAScript® Language Specification is defined as :
String objects use a variation of the GetOwnProperty internal method used for other native ECMAScript objects. This special internal method provides access to named properties corresponding to the individual characters of String objects.
So invoking GetOwnProperty are internal method used for other native ECMAScript objects and are resolved within the internal scope of the Browser Drivers and Browser Clients.
Mozilla have well documented Object.getOwnPropertyNames() and getOwnPropertyDescriptors().
Browser Specific Implementation
I did a small test with all the info you have provided with Search Box of Google Home Page i.e. https://www.google.co.in with all the major variants of WebDrivers and here is the result :
ChromeDriver - OSS :
[[ChromeDriver: chrome on XP (0d24fd038bde751b1e411711271c3e69)] -> name: q]
[[ChromeDriver: chrome on XP (0d24fd038bde751b1e411711271c3e69)] -> name: q]
FirefoxDriver - W3C :
[[FirefoxDriver: firefox on XP (e7a56813-97c5-466e-9c35-24c9f89af6ed)] -> name: q]
[[FirefoxDriver: firefox on XP (e7a56813-97c5-466e-9c35-24c9f89af6ed)] -> name: q]
InternetExplorerDriver - W3C :
[[InternetExplorerDriver: internet explorer on WINDOWS (367257db-cdbc-4be7-aeac-805a21ad9d2d)] -> name: q]
[[InternetExplorerDriver: internet explorer on WINDOWS (367257db-cdbc-4be7-aeac-805a21ad9d2d)] -> name: q]
So as you can observe from the field details of the concerned value field returned is in similar pattern and till the WebDriver variant passes the correct reference to user it shouldn't be a obstacle.
Finally, it is worth to mention at this point of time that like FirefoxDriver and InternetExplorerDriver (both being W3C compliant), ChromeDriver is almost W3C compliant and may vary from a few functional aspects.
Update A
As per your question and update, you are pretty right about ChromeDriver and Chrome communication protocol. Getting more granular we can find some difference in the webdriver call as follows :
Firefox :
1516626575533 webdriver::server DEBUG <- 200 OK {"value":{"element-6066-11e4-a52e-4f735466cecf":"6e35faa4-233f-400c-a6c7-6a66b54a69e5"}}
So, Firefox Browser returns :
"value":{"element-6066-11e4-a52e-4f735466cecf":"6e35faa4-233f-400c-a6c7-6a66b54a69e5"}
Chrome :
[14.921][DEBUG]: DEVTOOLS RESPONSE Runtime.evaluate (id=25) {
"result": {
"type": "object",
"value": {
"status": 0,
"value": {
"ELEMENT": "0.7086986861512812-1"
}
}
}
}
So, Chrome Browser returns :
"value": {"ELEMENT": "0.7086986861512812-1"}
What matters most to we user is the value of the element returned by the browser object which is always referred by an user and correctly identified by the webdriver instance. All these inner logic becomes abstract to the end user.
Update B
Adding some significant bytes from #FlorentB. 's comments :
The earlier versions of Selenium i.e. Selenium v2.x used the keyword ELEMENT to store the reference of a DOM Tree element. This key was changed within the recent versions of Selenium i.e. Selenium v3.x to element-6066-11e4-a52e-4f735466ce. Most of the implementation of the current ChromeDriver is still from the Selenium 2.x specifications.
I have just encountered this same issue, and found the change was made around 3.5 of the Selenium server and related images.
I found this comment the most specific to understand the change and identify which version it changed in:
https://github.com/SeleniumHQ/selenium/issues/4773#issuecomment-333092149
I am using Docker images like selenium/node-firefox:3.4.0-actinium, and have found v3.4.0 returns the ELEMENT key from the older JSonWire spec, whereas v3.9 returns the format element-6066-11e4-a52e-4f735466cecf from the new WebDriver spec. (I haven't checked any other versions in between).
It's part of their gradual migration to WebDriver, but it is a bit confusing that they did this breaking change at 3.5 (or thereabouts) and not v3.0.0 which I think everyone would have been OK with.
Also there's a mix of implementations in the "native" drivers like Gecko which is produced by the Firefox team now, and Chrome, as they will have different development roadmaps.
Furthermore, I've found the client-side library I'm using hasn't even implemented the new response yet, so I'll have to hang back for a while (or patch and PR it myself). I've seen similar conversations in other clients (like the Java client 2 years ago).
You can see the differences between the two protocols' definitions of the Element response:
https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#webelement-json-object
https://www.w3.org/TR/webdriver/#elements
I maintain a complex Angular (1.5.x) application that is being E2E tested using Protractor (2.5.x). I am experiencing a problem with this approach, which presents primarily in the way the tests seem flaky. Tests that worked perfectly well in one pull request fail in another. This concerns simple locators, such as by.linkTest(...). I debugged the failing tests and the app is on the correct page, the links are present and accessible.
Has anyone else experienced these consistency problems? Knows of a cause or workaround?
Just Say No to More End-to-End Tests!
That said, here are the few things you can do to tackle our mutual merciless "flakiness" enemy:
update to the latest Protractor (currently 4.0.0) which also brings latest selenium and chromedriver with it
turn off Angular animations
use dragons browser.wait() with a set of built-in or custom Expected Conditions. This is probably by far the most reliable way to approach the problem. Unfortunately, this is use-case and problem specific, you would need to modify your actual tests in the problematic places. For example, if you need to click an element, wait for it to be clickable:
var EC = protractor.ExpectedConditions;
var elm = $("#myid");
browser.wait(EC.elementToBeClickable(elm), 5000);
elm.click();
maximize the browser window (to avoid random element not visible or not clickable errors). Put this to onPrepare():
browser.driver.manage().window().maximize();
increase the Protractor and Jasmine timeouts
slow Protractor down by tweaking the Control Flow (not sure if it works for 4.0.0, please test)
manually call browser.waitForAngular(); in problematic places. I am not sure why this helps but I've seen reports where it definitely helped to fix a flaky test.
use the jasmine done() callback in your specs. This may help to, for example, not to start the it() block until done is called in beforeEach()
return a promise from the onPrepare() function. This usually helps to make sure things are prepared for the test run
use protractor-flake package that would automatically re-run failed tests. More like a quick workaround to the problem
There are also other problem-specific "tricks" like slow typing into the text box, clicking via JavaScript etc.
Yes, I think all of us experienced such flakiness issue.
Actually, the flakiness is quite common issue with any browser automation tool. However, this is supposed to be less in case of Protractor as Protractor has built-in wait consideration which performs actions only after loading the dom properly. But, in few cases you might have to use some explicit waits if you see intermittent failures.
I prefer to use few intelligent wait methods like:
function waitForElementToClickable(locator) {
var domElement = element(by.css(locator)),
isClickable = protractor.ExpectedConditions.elementToBeClickable(domElement);
return browser.wait(isClickable, 2000)
.then(function () {
return domElement;
});
}
Where 2000 ms is used as timeout, you can make it configurable using a variable.Sometimes I also go with browser.sleep() when none of my intelligent wait works.
It's been my experience that some methods (eg. sendKeys()) do not always fire at the expected time, within the controlFlow() queue, and will cause tests to be flakey. I work around this by specifically adding them to the controlFlow(). Eg:
this.enterText = function(input, text) {
return browser.controlFlow().execute(function() {
input.sendKeys(text);
});
};
A workaround that my team has been using is to re-run only failed tests using the plugin protractor-errors. Using this tool, we can identify real failures versus flakey tests within 2-3 runs. To add the plugin, just add a require statement to the bottom of the Protractor config's onPrepare function:
exports.config = {
...
onPrepare: function() {
require('protractor-errors');
}
}
You will need to pass these additional parameters when to run your tests with the plugin:
protractor config.js --params.errorsPath 'jasmineReports' --params.currentTime (timestamp) --params.errorRun (true or false)
There is also a cli tool that will handle generating the currentTime if you don't have an easy way to pass in a timestamp.
I want to use webdriver in nodejs to control a website which use ajax very heavy, especially it always have http request to server.
When I use driver.executeScript , I found the promise returned almost never resovled.
I checked the website, found that it use a keep-alive http request loop to communicate with server. That means, it will always have at least one live connection to server for 30s, then another connection for another 30s, again and again. which cause document.readyState keep interactive instead of completed, and then driver.executeScript almost be blocked forever.
I had tried driver.manage().timeouts().pageLoadTimeout(PAGE_LOAD_TIMEOUT_MS) but it only throws a exception after timeout.
I also tried to stop the connections by press ESC on the browser window. after that, it seems driver.executeScript can run immediately. But I didn't find any function like window.stop() in WebDriver API.
so is there have a way to resolve this problem? Either should be ok like:
run executeScript right now, regardless of whether page is loaded;
a webdriver api like driver.browser.stop() could stop all live connections in page.
Nodejs code to re-produce this problem:
const WebDriver = require('selenium-webdriver')
const driver = new WebDriver.Builder()
.withCapabilities(
WebDriver.Capabilities.firefox()
.set('webdriver.load.strategy', 'unstable')
).build()
driver.get('https://wx.qq.com/')
driver.executeScript('return "99% will be blocked, 1% luck to return"')
.then(function (ret) {console.log(ret)})
thanks!
UPDATE:
I found maybe set webdriver.load.strategy to unstable in firefox will be help.
FirefoxProfile fp = new FirefoxProfile();
fp.setPreference("webdriver.load.strategy", "unstable"); WebDriver
driver = new FirefoxDriver(fp);
https://github.com/seleniumhq/selenium-google-code-issue-archive/issues/4993
https://w3c.github.io/webdriver/webdriver-spec.html#page-load-strategies-1
Links from others got same problem:
http://grokbase.com/t/gg/webdriver/1263dbyws6/how-to-click-stop-button-or-equivalent
I think that if you don't use Implicit time the driver won't wait till it's loaded so you'll be able to execute your script.
If this don't work you can try to use your own timer. Check if doc state is ready, if it's not ready after some timeout then stop it manually (by JS) and run your script.
By using Thread.sleep(secs);. For example, we want to load a page in 3 secs means we write thread.sleep(30000);.
I am having trouble getting started with the webdriver dart library.
I was hoping for some simple examples.
I do have the seleniumn server standalone running in the background.
I am very new to dart and very experienced with ruby and watir-webdriver.
I was expecting something similar to the code below
import 'package:webdriver/webdriver.dart';
main() {
var url = "http://google.com";
var driver = new WebDriver();
b = driver.newSession(browser:'firefox');
b.getUrl(url);
}
But the error I am getting is
Unhandled exception:
No constructor 'WebDriver' declared in class 'WebDriver'.
Looking at the source
class WebDriver extends WebDriverBase {
WebDriver(host, port, path) : super(host, port, path);
So it seems like the constructor is there; and the defaults are in the WebDriverBase to go to the remote server. What am I doing wrong? I have scoured the internet trying to find simple examples with no luck
Currently, there are known issues with local and session storage, script execution, and log access.
To use these bindings, the Selenium standalone server must be running. You can download it at http://code.google.com/p/selenium/downloads/list.
There are a number of commands that use ids to access page elements. These ids are not the HTML ids; they are opaque ids internal to WebDriver. To get the id for an element you would first need to do a search, get the results, and extract the WebDriver id from the returned Map using the 'ELEMENT' key. see http://commondatastorage.googleapis.com/dartlang-api-docs/13991/webdriver.html