Throttle CPU in chromedriver with selenium - ruby

So I'm trying to add the CPUThrottlingRate in my selenium chromedriver setup below.
require 'webdrivers/chromedriver'
module ChromeBrowser
class << self
def headless(throttling)
#browser ||= headless_chrome_browser(throttling || 'regular')
end
private
def headless_chrome_browser(throttling)
options = Selenium::WebDriver::Chrome::Options.new
# -> https://peter.sh/experiments/chromium-command-line-switches/#profiler-timing
options.add_argument '--ignore-certificate-errors'
# options.add_argument "--user-agent=#{random_user_agent}" # set Random User Agent
options.add_argument '--allow-insecure-localhost'
options.add_argument '--window-size=1400x1400'
options.add_argument '--disable-gpu'
options.add_argument '--disable-dev-shm-usage'
options.add_argument '--noerrdialogs' # Suppresses all error dialogs when present
options.add_argument '--profiler-timing=0' # whether chrome will contain timing information
options.add_argument '--disable-infobars' # prevent infobars from appearing
options.add_argument '--headless' if Rails.env.production? # headless on chrome
# options.add_argument '--blink-settings=imagesEnabled=false' # disable images
options.add_argument '--no-referrers' # don't send HTTP-Referer headers
options.add_argument '--disable-breakpad' # disables the crash reporting
options.add_argument '--disable-demo-model' # disables the chrome OS demo
options.add_argument '--disable-translate' # disable google translate
options.add_argument '--dns-prefetch-disable' # disable DNS prefetching
options.add_argument '--no-pings' # no hyperlink auditing pings
browser = Selenium::WebDriver.for :chrome, options: options
case throttling
when 'slow'
browser.network_conditions = {
offline: false,
latency: 50,
download_throughput: 51200,
upload_throughput: 51200
}
when 'regular'
browser.network_conditions = {
offline: false,
latency: 30,
download_throughput: 51200,
upload_throughput: 512000
}
when 'fast'
browser.network_conditions = {
offline: false,
latency: 20,
download_throughput: 1024000,
upload_throughput: 1024000
}
end
return browser
end
end
end
I'm looking for this solution here but in ruby.
## rate 1 is no throttle, 2 is 2x slower, etc.
driver.execute_cdp_cmd("Emulation.setCPUThrottlingRate", {'rate': 10})
developertools is installed
running on chromedriver v92
I'm trying to throttle both network speed and CPU to simulate ~kinda~ real-life requests.
Can't seem to figure it out, what am I missing? Useful links:
https://www.selenium.dev/documentation/support_packages/chrome_devtools/
CPU throttling in chrome via python selenium
https://github.com/SeleniumHQ/selenium/wiki/Ruby-Bindings
I have tried:
...
browser = Selenium::WebDriver.for :chrome, options: options
devToolsSession = browser.devtools
devToolsSession.send_cmd('Emulation.setCPUThrottlingRate', {rate: 10})
Which produces
ArgumentError: wrong number of arguments (given 2, expected 1)
from /Users/minijohn/.asdf/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/selenium-webdriver-4.0.3/lib/selenium/webdriver/devtools.rb:55:in `send_cmd'
and with one param
Selenium::WebDriver::Error::WebDriverError: -32602: Invalid parameters: Failed to deserialize params.rate - BINDINGS: mandatory field missing at position 7
from /Users/minijohn/.asdf/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/selenium-webdriver-4.0.3/lib/selenium/webdriver/devtools.rb:69:in `send_cmd'
Also tried this
browser = Selenium::WebDriver.for :chrome, options: options
browser.send_cmd('Emulation.setCPUThrottlingRate', {rate: 10})
which produces
NoMethodError: undefined method `send_cmd' for #<Selenium::WebDriver::Chrome::Driver:0x2738339e1fe77884 browser=:chrome>
from (pry):14:in `headless_chrome_browser'

The goal for Selenium is to define the common commands that the browser vendors will support in the WebDriver BiDi specification, and support them via a straightforward API, which will be accessible via Driver#devtools method.
In the meantime, any Chrome DevTools command can be executed via Driver#execute_cdp.
In this case it will look like:
SL-1495:code titusfortner$ irb
irb(main):001:0> require 'selenium-webdriver'
=> true
irb(main):002:0> driver = Selenium::WebDriver.for :chrome
=> #<Selenium::WebDriver::Chrome::Driver:0x..fc42365c4d30079b0 browser=:chrome>
irb(main):003:0> driver.execute_cdp('Emulation.setCPUThrottlingRate', rate: 10)
=> {}
irb(main):004:0>
The key here is that in Ruby we're using keywords for this instead of a Hash, and in Ruby 3, those are now handled differently.

Related

Issues updating desired_capabilities: Selenium WebDriver - Ruby

Super beginner here. Trying to update this test with Selenium WebDriver using Ruby the course I was going through uses the below which is deprecated.
driver = Selenium::WebDriver.for :remote, desired_capabilities: :firefox
The error I'm getting in cmd when I try to run the tests is
"WARN Selenium [DEPRECATION] [:desired_capabilities] :desired_capabilities as a parameter for driver initialization is deprecated. Use :capabilities with an Array value of capabilities/options if necessary instead."
So I've tried to find examples of how to suppress the error mentioned in This Link, but I'm having trouble finding examples of how to implement that.
I've also tried to look up several ways to just use capabilities: as suggested, but I'm having trouble finding resources for that also, so I've just messed around and tried different combinations to no avail.
Curious if anyone knows of anything that would help me find the answer for this?
Also looked at these sources
https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities
https://www.lambdatest.com/blog/desired-capabilities-in-selenium-testing/
Selenium include firefox profile into DesiredCapabilities for Remote Driver
https://www.selenium.dev/documentation/webdriver/remote_webdriver/
Based off the last link, I believe the below should work? But I'm sure I'm just missing something with syntax.
driver = Selenium::WebDriver.for :Remote::Capabilities.firefox
Selenium capabilities are not where they should be in Ruby. You want to avoid using Capabilities entirely now.
Here's an example in the Selenium documentation with a before/after for how to use Options properly: https://www.selenium.dev/documentation/webdriver/getting_started/upgrade_to_selenium_4/#before
It does not match how the other Selenium languages do things, so I'm planning to change the deprecations in Selenium 4.3 to get them to match.
# spec/rails_helper.rb
Capybara.server = :puma, { Silent: true }
Capybara.server_port = 9887
Capybara.register_driver :headless_chrome do |app|
options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
opts.args << '--headless'
opts.args << '--disable-site-isolation-trials'
opts.args << '--no-sandbox'
end
options.add_preference(:download, prompt_for_download: false, default_directory: Rails.root.join('tmp/capybara/downloads'))
options.add_preference(:browser, set_download_behavior: { behavior: 'allow' })
service_options = ::Selenium::WebDriver::Service.chrome(
args: {
port: 9515,
read_timeout: 120
}
)
remote_caps = Selenium::WebDriver::Remote::Capabilities.chrome(
'goog:loggingPrefs': {
browser: ENV['JS_LOG'].to_s == 'true' ? 'ALL' : nil
}.compact
)
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
capabilities: [remote_caps, options],
service: service_options,
timeout: 120
)
end
Capybara::Screenshot.register_driver(:headless_chrome) do |driver, path|
driver.browser.save_screenshot(path)
end
Capybara.javascript_driver = :headless_chrome
I'd hope this be helpfull for you.

Selenium Webdriver - set preferred browser language DE

I have a problem setting the preferred (accepted language) within headless Chrome using Selenium Webdriver and Ruby. I use the following WebDriver settings:
Selenium::WebDriver::Chrome.driver_path = #config[<path to the Chrome Driver>]
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument('--headless')
options.add_argument('--disable-translate')
options.add_argument("--lang=de")
The driver is then initialized with:
#selenium_driver = Selenium::WebDriver.for :chrome, options: options
Everything works fine but at some pages Chrome returns English content even when I navigate to the German page URL (e.g. page.de). In these cases the Chrome driver returns the English content due to an internal forwarding to page.de/en. I do not specify the en path in my queried URL.
I have tried to set the language using the Webdriver preference:
options.add_preference('accept_languages', 'de')
instead of the add_argument but it doesn't change anything of the behavior.
Does anyone have an idea how to force a headless Chrome controlled by Selenium Webdriver within Ruby to request page content in a defined language or - not optimal but it might help as a workaround - to stop the forwarding?
Any help greatly appreciated
Best
Krid
I found a solution that works for me. As in many cases the problem was sitting in front of the screen and simply doesn't work precisely enough ;-)
Instead of using
options.add_argument("--lang=de")
you have to use
options.add_argument("--lang=de-DE")
When I use an IETF language tag the code I initially posted works as expected.
I'am using this in my test_helper.rb Works fine for me.
Capybara.register_driver :selenium do |app|
Chromedriver.set_version "2.36"
desired_capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
'chromeOptions' => {
'prefs' => {
'intl.accept_languages' => 'en-US'
},
args: ['disable-gpu', 'headless']
}
)
Capybara::Selenium::Driver.new(app, { browser: :chrome, desired_capabilities: desired_capabilities })
end
Capybara.javascript_driver = :chrome
Capybara.default_driver = :selenium
This prefs hash inside an options hash did the trick for me. It's at the end of the driven_by :selenium line.
(Inside test/application_syste_test_case.rb)
# frozen_string_literal: true
require 'test_helper'
require 'capybara/rails'
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :chrome, screen_size: [1400, 1400], options: { prefs: { 'intl.accept_languages' => 'de,de-DE;q=0.9,en;q=0.1' } }
# ...
2021-06-14 UPDATE:
The previous example produces this deprecation warning:
WARN Selenium [DEPRECATION] :prefs is deprecated. Use Selenium::WebDriver::Chrome::Options#add_preference instead.
IMO, the solution below is uglier, but I'm posting it for when it's fully deprecated and the original stops working.
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by(:selenium,
using: :chrome,
screen_size: [1400, 1400],
options: {
options: Selenium::WebDriver::Chrome::Options.new(
prefs: { 'intl.accept_languages' => 'de,de-DE;q=0.9,en;q=0.1' }
)
},
)
You should be able to solve your problem by adding an experimental option:
options.add_option('prefs', {'intl.accept_languages': 'en,en_US'})
I'm sure it works with Python, but I've not tried with Ruby: this approach is the correct one, not sure about the implementation.
You can find in this repository the code which handles your problem in Python code, and in this Q&A how to implement experimental_options in Ruby
For me works:
options = Selenium::WebDriver::Firefox::Options.new
options.add_preference("intl.accept_languages", 'de-DE')
Capybara::Selenium::Driver.new(app, browser: :firefox, options: options)

Debug in headless Chrome

After switching from capybara_webkit to headless_chrome, I'm trying to find a way to replicate
Capybara::Webkit.configure do |config|
config.debug = true
end
or
Capybara.javascript_driver = :webkit_debug
with the new driver.
The goal is to be able to see the log of everything happening when running rspec my_spec.rb: for example, all GET requests.
Is there a way to achieve that?
There is no option when using Selenium like the capybara_webkit debug option that outputs the debug info in realtime, however you can get access to the Chrome logs and output those at the end of each test (or you could write a helper to output them whenever you call it of course).
First you'd need to configure your selenium driver for logging
Capybara.register_driver :logging_chrome do |app|
caps = Selenium::WebDriver::Remote::Capabilities.chrome(
# customize this based on which and what level logs your prefer
loggingPrefs: {
browser: 'ALL',
driver: 'ALL',
performance: 'ALL'
}
)
browser_options = ::Selenium::WebDriver::Chrome::Options.new()
browser_options.headless!
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
desired_capabilities: caps,
options: browser_options
)
end
Then you'd set up to use that driver
Capybara.javascript_driver = :logging_chrome # possibly default_driver = depending on your config
and then add an after block that gets the logs and displays them
after(:each) do
# customize based on which type of logs you want displayed
log_types = page.driver.browser.manage.logs.available_types
log_types.each do |t|
puts t.to_s + ": " + page.driver.browser.manage.logs.get(t).join("\n")
end
end

Automated testing with Cucumber on BrowserStack of site behind VPN

I am trying to get my Cucumber tests to work on BrowserStack. The problem is that our test environments are all behind a VPN. BrowserStack's docs say, just set up local testing and it will just work!. It doesn't. The tests start up, but they are not being rerouted through my local machine to pick up my vpn credentials.
I downloaded the binary as instructed by BrowserStack. I started it up with the command
~ ./BrowserStackLocal <my BS key> -forcelocal
Then I run my test with (in a different terminal window):
bundle exec cucumber CURRENT_BROWSER=browserstack features/01_login.feature
my env.rb looks like this:
require 'cucumber/rails'
Capybara.default_selector = :css
cb = ENV['CURRENT_BROWSER']
testbrowser = cb ? cb.downcase.to_sym : :firefox
puts "-------------- current browser: #{testbrowser}........."
Capybara.register_driver :selenium do |app|
if RbConfig::CONFIG['host_os'][/linux/] && testbrowser.to_s.eql?("CHROME".downcase)
Capybara::Selenium::Driver.new(app, {:browser => :remote, :url => "http://127.0.0.1:9515"})
else
if testbrowser.eql?(:chrome)
prefs = {
:download => {
:prompt_for_download => false,
:default_directory => DownloadHelpers::PATH.to_s
}
}
Capybara::Selenium::Driver.new(app, :browser => :chrome, :prefs => prefs, :switches => %w[--test-type])
elsif testbrowser.eql?(:browserstack)
stackToUse = ENV['BS_STACK'] || 'ie_9'
json = JSON.load(open(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'browsers.json'))))
config = json[stackToUse]
unless config
puts "invalid BS_STACK specified. Was '#{stackToUse}'"
return
end
# Add default config
config['name'] = "#{config['os']} #{config['os_version']} - #{Time.now.strftime '%Y-%m-%d %H:%M'}"
config['acceptSslCert'] = true
config['browserstack.debug'] = true
Capybara::Selenium::Driver.new(app, :browser => :remote, :desired_capabilities => config, :url => "http://<BS_USERNAME>:<BS_PASSKEY>#hub.browserstack.com/wd/hub")
elsif testbrowser.eql?(:internetexplorer)
Capybara::Selenium::Driver.new(app, :browser => :internetexplorer, :switches => %w[--test-type])
else
profile = Selenium::WebDriver::Firefox::Profile.new
profile
profile["browser.download.dir"] = DownloadHelpers::PATH.to_s
profile["browser.download.folderList"] = 2 # 2 - save to user defined location
profile["browser.download.manager.alertOnEXEOpen"] = false
profile["browser.helperApps.neverAsk.saveToDisk"] = "application/msword, application/csv, application/ris, text/csv, image/png, application/pdf, text/html, text/plain, application/zip, application/x-zip, application/x-zip-compressed, application/download, application/octet-stream, data:application/csv"
profile["browser.helperApps.alwaysAsk.force"] = false
profile["browser.download.manager.showWhenStarting"] = false
profile["browser.download.manager.focusWhenStarting"] = false
profile["browser.download.useDownloadDir"] = true
profile["browser.download.manager.alertOnEXEOpen"] = false
profile["browser.download.manager.closeWhenDone"] = true
profile["browser.download.manager.showAlertOnComplete"] = false
profile["browser.download.manager.useWindow"] = false
profile["services.sync.prefs.sync.browser.download.manager.showWhenStarting"] = false
profile["pdfjs.disabled"] = true
Capybara::Selenium::Driver.new(app, :browser => testbrowser, :profile => profile)
end
end
end
ActionController::Base.allow_rescue = false
# Remove/comment out the lines below if your app doesn't have a database.
# For some databases (like MongoDB and CouchDB) you may need to use :truncation instead.
begin
DatabaseCleaner.strategy = :transaction
rescue NameError
raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it."
end
# You may also want to configure DatabaseCleaner to use different strategies for certain features and scenarios.
# See the DatabaseCleaner documentation for details. Example:
#
# Before('#no-txn,#selenium,#culerity,#celerity,#javascript') do
# # { :except => [:widgets] } may not do what you expect here
# # as tCucumber::Rails::Database.javascript_strategy overrides
# # this setting.
# DatabaseCleaner.strategy = :truncation
# end
#
# Before('~#no-txn', '~#selenium', '~#culerity', '~#celerity', '~#javascript') do
# DatabaseCleaner.strategy = :transaction
# end
#
# Possible values are :truncation and :transaction
# The :transaction strategy is faster, but might give you threading problems.
# See https://github.com/cucumber/cucumber-rails/blob/master/features/choose_javascript_database_strategy.feature
Cucumber::Rails::Database.javascript_strategy = :truncation
Other people use these same tests and we have written dozens, so making sure that I can still run these tests locally is paramount.
When I do run the tests, it begins to run on browserstack, but it can't reach any of the sites I have tried telling it to reach. Including http://localhost:3000/login I have contacted BrowserStack support and they asked if I had set up local testing.
The only error I am getting is that the test can't find the CSS element to login. When I watched the automated test through browserstack I can see that it isn't reaching the page. It just says "Oops! This link appears to be broken." Any suggestions would be most appreciated.
The support at BrowserStack got back to me. I had to had another config property like so to the env.rb:
config['browserstack.local'] = true
Now my only problem is that none of the features that we wrote seem to work on IE. So I can test chrome or firefox on browserstack, but I already had that capability set up.

How to run multiple firefox browser parallely having different proxy in Watir

The code is given Below , where it is launching three firefox browser
, all browser has different proxy settings. Using watir how launch all three browser same time using tread in watir???
require 'selenium-webdriver'
require 'rubygems'
require 'watir'
require 'rautomation'
require './CLReport.class'
require 'win32ole'
# TO INITIATE FIRST FIREFOX BROWSER
# THE PROXY DATA CAN BE parameterized from Excel sheet
profile = Selenium::WebDriver::Firefox::Profile.new
profile.proxy = Selenium::WebDriver::Proxy.new :http => 'myproxy.com:8080', :ssl => 'myproxy.com:8080'
$b1 = Watir::Browser.new :firefox, :profile => profile
$b1.goto("https://google.com")
# TO INITIATE SECOND FIREFOX BROWSER
# THE PROXY DATA CAN BE parameterized from Excel sheet
profile = Selenium::WebDriver::Firefox::Profile.new
profile.proxy = Selenium::WebDriver::Proxy.new :http => 'myproxy.com:8081', :ssl => 'myproxy.com:8081'
$b2 = Watir::Browser.new :firefox, :profile => profile
$b2.goto("https://google.com")
# TO INITIATE THORD FIREFOX BROWSER
# THE PROXY DATA CAN BE parameterized from Excel sheet
profile = Selenium::WebDriver::Firefox::Profile.new
profile.proxy = Selenium::WebDriver::Proxy.new :http => 'myproxy.com:8082', :ssl => 'myproxy.com:8082'
$b3 = Watir::Browser.new :firefox, :profile => profile
$b3.goto("https://google.com")
Now my question is how to join $b1,$b2,$b3 in a single browser using thread so that
only $browser.link(:text, "form application")click should work for all three browser parallely insted of writing
$b1.link(:text, "form application").click
$b2.link(:text, "form application").click
$b3.link(:text, "form application").click
i.e single line of code work work in three firefox browser same time parallely.
It is not possible because $b1,$b2,$b3 are instances of different browser,You can not make them equal.What are you doing is right. Or You can do some thing like that.
array = [$b1,$b2,$b3]
array.each { |browser|
browser.link(:text, "form application").click
}

Resources