In https://github.com/GoogleChrome/puppeteer
There is one option to add a delay in every action
Slow it down - the slowMo option slows down Puppeteer operations by
the specified amount of milliseconds. It's another way to help see
what's going on.
I am trying to find a similar way in Selenium WebDriver (I am using the ruby library).
Issue: Right now when I run Selenium script it finishes very quickly I want it to be run like human behavior. Is there any way to achieve this in Selenium??
This is the magic method, it is more effective as we can put random sleep between every command to make simulation like human
module Selenium::WebDriver::Remote
class Bridge
def execute(command, opts = {}, command_hash = nil)
verb, path = commands(command) || raise(ArgumentError, "unknown command: #{command.inspect}")
path = path.dup
path[':session_id'] = session_id if path.include?(':session_id')
begin
opts.each { |key, value| path[key.inspect] = escaper.escape(value.to_s) }
rescue IndexError
raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
end
Selenium::WebDriver.logger.info("-> #{verb.to_s.upcase} #{path}")
res = http.call(verb, path, command_hash)
sleep rand(0.1..0.8).round(2) # <--- Add your sleep here.
res
end
end
end
Related
I am learning ruby and trying to write a unit test with rspec for the following method:
def get()
options = {}
OptionParser.new do |opt|
opt.banner = 'Usage: validate-gitlab-ci [options]'
opt.on('-f', '--yaml YAML-PATH', 'Path to .gitlab-ci.yml') { |o| options[:yamlFile] = o }
opt.on('-l', '--base-url GitLab url', 'GitLab API url') { |o| options[:baseUrl] = o + API_PATH }
opt.on('-t', '--timeout[TIMEOUT]', Integer, 'Api timeout in seconds') { |o| options[:timeout] = o || 10 }
opt.on('-v', '--version', 'Program version') { |o| options[:version] = o }
end.parse!
validateUrl!(options[:baseUrl])
validateYamlFile!(options[:yamlFile])
#baseUrl = options[:baseUrl]
#pathToYamlFile = options[:yamlFile]
end
The code for my unit test so far is:
RSpec.describe Gitlab::Lint::Client::Args do
describe "#get" do
context "when arguments are valid" do
it "sets baseUrl and pathToYamlFile" do
io = StringIO.new
io.puts "glab-lint --base-url=https://example.com --yaml=valid.ym\n"
io.rewind
$stdin = io
args = Gitlab::Lint::Client::Args.new
args.get()
expect(args.baseUrl).to.eq("https://example.com")
end
end
end
end
I am trying to mock STDIN for OptionParser. However, upon executing the test the following error is displayed:
OptionParser::InvalidOption:
invalid option: --pattern
This is raised by the end.parse! line in the get() method
Has anyone managed to test OptionsParser with stdin mocked?
Update
I think what is happening is that some RSpec options, e.g. --pattern?? are being captured in STDIN and passed to script??? Or .... RSpec is consuming the stdin options??
Reading this post seems to suggest that the desired functionality is not possible with RSpec....if this is indeed true then I will migrate over to using alternative test frameworks in future for CLI projects that use ARGV. There is a workaround suggested here but that suggests using environment variables for capturing commmand line arguments. In this case that would require further refactoring of the software under test, purely to suit the capabilities of the RSpec test framework!!
If I add a puts statement to display the contents of ARGV in the test script it confirms this is the case, with this output:
--pattern
spec/**{,/*/**}/*_spec.rb
[--base-url=https://gitlab.com --yaml=valid.ym]
So.....as a complete newbie to RSpec.....my options are:
Update the signature of the get method to accept an args array:
def get(args)
options = {}
OptionParser.new do |opt|
...
end.parse!(args)
end
This delays the issue with testing the code that reads from ARGV further up the call hierarchy
Modify ARGV shifting the first two arguments out of the array and then after the test has completed restore ARGV to original state. Looks like something similar has already been tried here without success.
Some other configuration that I am not aware of as a newbie to RSpec
Investigate alternative options, e.g. minitest, that maybe do not modify the ARGV array??
Further information regarding options 3 and 4 appreciated....
You can use RSpec to mock STDIN. For example:
STDIN.should_receive(:read).and_return("glab-lint --base-url=https://example.com --yaml=valid.yml")
Alternatively, you can invoke your actual command line program using backticks or system, and assert on the response.
I have a program, which calculate many things. While I run the code by ruby code.rb everything is okay. The problem starts, when I want to run it by command line with additional option: ruby code.rb --time 201712121100.
The piece of problematic code is below:
include Options #here I have some options to choose, like --time
def calculate_p(time, mode)
if mode
calculator = calc1
else
calculator = calc2
end
calculate_t(time, calculator)
end
def calculate_t(time, calculator)
date_ymd = time.strftime("%Y%m%d")
time_hm = time.strftime("%H%M")
calculator
.with(date_ymd, time_hm)
.run do |result|
if result.ok?
result.stdout.pop.split.first
else
msgw("Program returned with errors.", :error)
msgw("stdout: %s; stderr: %s" % [result.stdout, result.stderr], :error)
false
end
end
end
time = Options.get('--time')
.andand do |time_op|
msgw('Taking time from command line arguments') do
time_op.pop.andand
end
end || msgw('Calculating time for now.') do
Time.now.utc
end || abort
calc=calculate_p(time, mode)
msgw is just define to print messages.
mode takes true or false values.
I received an error:
"calculate_t: undefined method strftime for "201712121100":String (NoMethodError)"
What am I doing wrong? Why using Time.now.utc is working while giving a specific time is not?
I also checked the solutions from here Rails undefined method `strftime' for "2013-03-06":String
and Date.parse() gives the same error.
The issue is here:
time_op.pop.andand
time_op taken from the command line is a string, and you need a Time instance. The get it, use DateTime#strptime:
DateTime.strptime(time_op.pop, "%Y%m%d%H%M").to_time.andand
I have a ruby console app that you run with an argument, then once running outputs some text to the screen, asks for some more user input and then outputs some more text to the screen. I want to do an end to end test on this app and I don't know how. If I were writing an end to end test for an REST API, I would just hit the public endpoint, follow the links and then have an expect statement on the output. Easy. But on a console app I have no idea how to do the same thing. Are there any gems for stepping through a console app in the context of a test? I've been looking all day but can't find anything.
ANY help appreciated.
Inspired by this gem which has a fairly simple implementation, I wrote a method which captures console input & output and can, therefore, be used in tests:
require 'stringio'
module Kernel
def emulate_console(console_input)
$stdin = StringIO.new(console_input)
out = StringIO.new
$stdout = out
yield
return out
ensure
$stdout = STDOUT
$stdin = STDIN
end
end
This method captures console output, and also provides as input the string value which you specify in the console_input parameter.
Basic usage
Here's a simple usage of the emulate_console method:
out = emulate_console("abc\n") do
input = gets.chomp
puts "You entered: #{input}!"
end
The return value out is a StringIO object. To access its value, use the #string method:
out.string
=> "You entered: abc!\n"
Note that the input contains a newline character (\n) to simulate pressing the ENTER key.
Testing
Now, let's assume that you want to test this method, that uses both stdin and stdout:
def console_add_numbers
x = Integer(gets)
y = Integer(gets)
puts x + y
end
The following RSpec test tests the happy path of this code:
require 'rspec/autorun'
RSpec.describe '#console_add_numbers' do
it 'computes correct result' do
input = <<-EOS
2
3
EOS
output = emulate_console(input) { console_add_numbers }
expect(output.string.chomp).to eql '5'
end
end
While running automated test scripts I will oftentimes get the following warning messages and others:
QFont::setPixelSize: Pixel size <= 0 (0)
QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.
I've searched around and these outputs have nothing to do with my test scripts. They don't impact the results in any way. Thus, I don't wish to see them.
While looking for a way to solve this, I found code which is supposed to redirect stderr to a class which will filter out specific messages. However, when I try to use this code, none of my scripts work.
The class that suppresses the warnings:
class WarningSuppressor
# array to hold warnings to suppress
SUPPRESS_THESE_WARNINGS = [
'QFont::setPixelSize: Pixel size <= 0 (0)',
'QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.'
]
class << self
def write(message)
if suppress_warning? message
0
end
end
def suppress_warning? message
SUPPRESS_THESE_WARNINGS.any? { |suppressable_warning| message.chomp.include? suppressable_warning }
end
end
end
The configuration code that is supposed to redirect stderr:
Capybara.configure do |config|
config.default_driver = :webkit
config.javascript_driver = :webkit
config.run_server = false # prevents Capybara from booting up a rack application automatically
config.app_host = 'http://local.xxxxxxxx.com'
# Sends output to a custom warning supressor
config.register_driver :webkit do |app|
Capybara::Driver::Webkit.new(app, stderr: WarningSuppressor)
end
# 10 second wait for ajax to finish
config.default_wait_time = 10
end
If I insert a binding.pry in the block statement, app is nil but Capybara::Driver::Webkit exists.
Does anyone have a better way/way to fix this; a method of hiding certain warnings from being displayed while running my automated scripts?
I am trying to write a crawler that crawls all links from loaded page and logs all request and response headers along with response body in some file say XML or txt. I am opening all links from first loaded page in new browser window so I wont get this error:
Element not found in the cache - perhaps the page has changed since it was looked up
I want to know what could be the alternate way to make requests and receive response from all links and then locate input elements and submit buttons form all opened windows.
I am able to do above to some extent except when opened window has common site searh box like one on this http://www.testfire.net in the upper right corner.
What I want to do is I want to omit such common boxes so that I can fill other inputs with values using i.send_keys "value" method of webdriver and dont get this error
ERROR: Element not found in the cache - perhaps the page has changed since it was looked up.
What is the way to detect and distinguish input tags from each opened window so that value does not get filled repeatably in common input tags that appear on most pages of website.
My code is following:
require 'rubygems'
require 'selenium-webdriver'
require 'timeout'
class Clicker
def open_new_window(url)
#driver = Selenium::WebDriver.for :firefox
#url = #driver.get " http://test.acunetix.com "
#link = Array.new(#driver.find_elements(:tag_name, "a"))
#windows = Array.new(#driver.window_handles())
#link.each do |a|
a = #driver.execute_script("var d=document,a=d.createElement('a');a.target='_blank';a.href=arguments[0];a.innerHTML='.';d.body.appendChild(a);return a", a)
a.click
end
i = #driver.window_handles
i[0..i.length].each do |handle|
#driver.switch_to().window(handle)
puts #driver.current_url()
inputs = Array.new(#driver.find_elements(:tag_name, 'input'))
forms = Array.new(#driver.find_elements(:tag_name, 'form'))
inputs.each do |i|
begin
i.send_keys "value"
puts i.class
i.submit
rescue Timeout::Error => exc
puts "ERROR: #{exc.message}"
rescue Errno::ETIMEDOUT => exc
puts "ERROR: #{exc.message}"
rescue Exception => exc
puts "ERROR: #{exc.message}"
end
end
forms.each do |j|
begin
j.send_keys "value"
j.submit
rescue Timeout::Error => exc
puts "ERROR: #{exc.message}"
rescue Errno::ETIMEDOUT => exc
puts "ERROR: #{exc.message}"
rescue Exception => exc
puts "ERROR: #{exc.message}"
end
end
end
#Switch back to the original window
#driver.switch_to().window(i[0])
end
end
ol = Clicker.new
url = ""
ol.open_new_window(url)
Guide me how can I get all requeat and response headers with response body using Selenium Webdriver or using http.set_debug_output of ruby's net/http ?
Selenium is not one of the best options to use to attempt to build a "web-crawler". It can be too flakey at times, especially when it comes across unexpected scenarios. Selenium WebDriver is a great tool for automating and testing expectancies and user interactions.
Instead, good old fashioned curl would probably be a better option for web-crawling. Also, I am pretty sure there are some ruby gems that might help you web-crawl, just Google search it!
But To answer the actual question if you were to use Selenium WebDriver:
I'd work out a filtering algorithm where you can add the HTML of an element that you interact with to an variable array. Then, when you go on to the next window/tab/link, it would check against the variable array and skip the element if it finds a matching HTML value.
Unfortunately, SWD does not support getting request headers and responses with its API. The common work-around is to use a third party proxy to intercept the requests.
============
Now I'd like to address a few issues with your code.
I'd suggest before iterating over the links, add a #default_current_window = #driver.window_handle. This will allow you to always return back to the correct window at the end of your script when you call #driver.switch_to.window(#default_current_window).
In your #links iterator, instead of iterating over all the possible windows that could be displayed, use #driver.switch_to.window(#driver.window_handles.last). This will switch to the most recently displayed new window (and it only needs to happen once per link click!).
You can DRY up your inputs and form code by doing something like this:
inputs = []
inputs << #driver.find_elements(:tag_name => "input")
inputs << #driver.find_elements(:tag_name => "form")
inputs.flatten
inputs.each do |i|
begin
i.send_keys "value"
i.submit
rescue e
puts "ERROR: #{e.message}"
end
end
Please note how I just added all of the elements you wanted SWD to find into a single array variable that you iterate over. Then, when something bad happens, a single rescue is needed (I assume you don't want to automatically quit from there, which is why you just want to print the message to the screen).
Learning to DRY up your code and use external gems will help you achieve a lot of what you are trying to do, and at a faster pace.