webpilot 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/Gemfile +6 -0
- data/Rakefile +7 -0
- data/lib/webpilot.rb +133 -0
- data/spec/basic_spec.rb +19 -0
- data/spec/browsers_spec.rb +62 -0
- data/spec/spec_helper.rb +5 -0
- metadata +57 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Gemfile.lock
|
data/Gemfile
ADDED
data/Rakefile
ADDED
data/lib/webpilot.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'headless'
|
3
|
+
require 'log4r'
|
4
|
+
require 'selenium-webdriver'
|
5
|
+
|
6
|
+
$LOAD_PATH << File.expand_path(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
class WebPilot
|
9
|
+
include Log4r
|
10
|
+
attr_reader :driver, :logger
|
11
|
+
|
12
|
+
extend Forwardable
|
13
|
+
def_delegators :@driver, :close, :current_url, :execute_script, :navigate,
|
14
|
+
:page_source, :quit, :switch_to, :window_handle, :window_handles
|
15
|
+
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
16
|
+
|
17
|
+
def initialize(options={})
|
18
|
+
@browser = options[:browser] || :firefox
|
19
|
+
@is_headless = options[:is_headless]
|
20
|
+
@pause_time = options[:pause_time] || 0.3
|
21
|
+
@screenshot_dir = options[:screenshot_dir]
|
22
|
+
@timeout = options[:timeout] || 8
|
23
|
+
|
24
|
+
case
|
25
|
+
when options[:logger].nil?
|
26
|
+
@logger = Logger.new 'debug_log'
|
27
|
+
null_outputter = IOOutputter.new 'null', File.open('/dev/null', 'w')
|
28
|
+
when options[:logger].is_a?(Logger)
|
29
|
+
@logger = options[:logger]
|
30
|
+
when options[:logger] == 'STDOUT'
|
31
|
+
@logger = Logger.new 'debug_log'
|
32
|
+
@logger.outputters = Outputter.stdout
|
33
|
+
@logger.level = DEBUG
|
34
|
+
when options[:logger] == 'STDERR'
|
35
|
+
@logger = Logger.new 'debug_log'
|
36
|
+
@logger.outputters = Outputter.stderr
|
37
|
+
@logger.level = DEBUG
|
38
|
+
end
|
39
|
+
|
40
|
+
if @is_headless
|
41
|
+
@headless = Headless.new
|
42
|
+
@headless.start
|
43
|
+
end
|
44
|
+
|
45
|
+
@driver = Selenium::WebDriver.for @browser
|
46
|
+
end
|
47
|
+
|
48
|
+
# Close all windows that have a current url of "about:blank". Must follow this with a call to
|
49
|
+
# `#switch_to`, so that you know what window you're on.
|
50
|
+
def close_blank_windows
|
51
|
+
@driver.window_handles.each do |handle|
|
52
|
+
@driver.switch_to.window(handle)
|
53
|
+
@driver.close if @driver.current_url == 'about:blank'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Enlargens the text of an element, using `method` and `locator`, by changing the `font-size`
|
58
|
+
# in the style to be `3em`. It uses the following Javascript:
|
59
|
+
#
|
60
|
+
# hlt = function(c) { c.style.fontSize='3em'; }; return hlt(arguments[0]);
|
61
|
+
def enlargen(method, locator)
|
62
|
+
@logger.debug " enlargen: Waiting up to #{@timeout} seconds to find_element(#{method}, #{locator})..."
|
63
|
+
wait = Selenium::WebDriver::Wait.new(:timeout => @timeout)
|
64
|
+
wait.until { find_element(method, locator) }
|
65
|
+
element = find_element(method, locator)
|
66
|
+
execute_script("hlt = function(c) { c.style.fontSize='3em'; }; return hlt(arguments[0]);", element)
|
67
|
+
end
|
68
|
+
|
69
|
+
def find_element(method, selector, options={})
|
70
|
+
retries = 4
|
71
|
+
sleep 0.1 # based on http://groups.google.com/group/ruby-capybara/browse_thread/thread/5e182835a8293def fixes "NS_ERROR_ILLEGAL_VALUE"
|
72
|
+
begin
|
73
|
+
@driver.find_element(method, selector)
|
74
|
+
rescue Selenium::WebDriver::Error::NoSuchElementError, Selenium::WebDriver::Error::TimeOutError => e
|
75
|
+
return nil if options[:no_raise]
|
76
|
+
raise e
|
77
|
+
rescue Selenium::WebDriver::Error::InvalidSelectorError => e
|
78
|
+
raise e if retries == 0
|
79
|
+
@logger.info "Caught a Selenium::WebDriver::Error::InvalidSelectorError: #{e}"
|
80
|
+
@logger.info "Retrying..."
|
81
|
+
pause 2
|
82
|
+
retries -= 1
|
83
|
+
retry
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def highlight(method, locator, ancestors=0)
|
88
|
+
wait = Selenium::WebDriver::Wait.new(:timeout => @timeout)
|
89
|
+
wait.until { find_element(method, locator) }
|
90
|
+
element = find_element(method, locator)
|
91
|
+
execute_script("hlt = function(c) { c.style.border='solid 1px rgb(255, 16, 16)'; }; return hlt(arguments[0]);", element)
|
92
|
+
parents = ""
|
93
|
+
red = 255
|
94
|
+
|
95
|
+
ancestors.times do
|
96
|
+
parents << ".parentNode"
|
97
|
+
red -= (12*8 / ancestors)
|
98
|
+
execute_script("hlt = function(c) { c#{parents}.style.border='solid 1px rgb(#{red}, 0, 0)'; }; return hlt(arguments[0]);", element)
|
99
|
+
end
|
100
|
+
pause
|
101
|
+
end
|
102
|
+
|
103
|
+
def pause(time = nil)
|
104
|
+
@logger.debug " breathing..."
|
105
|
+
sleep (time || @pause_time)
|
106
|
+
end
|
107
|
+
|
108
|
+
def quit
|
109
|
+
@driver.quit
|
110
|
+
@headless.destroy if @is_headless
|
111
|
+
end
|
112
|
+
|
113
|
+
# Deselect all `<option>s` within a `<select>`, suppressing any `UnsupportedOperationError`
|
114
|
+
# that Selenium may throw
|
115
|
+
def safe_deselect_all(el)
|
116
|
+
el.deselect_all
|
117
|
+
rescue Selenium::WebDriver::Error::UnsupportedOperationError
|
118
|
+
end
|
119
|
+
|
120
|
+
# Select a frame by its `id`
|
121
|
+
def select_frame(id)
|
122
|
+
@driver.switch_to().frame(id)
|
123
|
+
pause
|
124
|
+
end
|
125
|
+
|
126
|
+
# Create and execute a `Selenium::WebDriver::Wait` for finding an element by `method` and `selector`
|
127
|
+
def wait_for(method, locator)
|
128
|
+
@logger.debug " wait_for: Waiting up to #{@timeout} seconds to find_element(#{method}, #{locator})..."
|
129
|
+
wait = Selenium::WebDriver::Wait.new(:timeout => @timeout)
|
130
|
+
sleep 0.1 # based on http://groups.google.com/group/ruby-capybara/browse_thread/thread/5e182835a8293def fixes "NS_ERROR_ILLEGAL_VALUE"
|
131
|
+
wait.until { driver.find_element(method, locator) }
|
132
|
+
end
|
133
|
+
end
|
data/spec/basic_spec.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
|
2
|
+
|
3
|
+
describe "WebPilot" do
|
4
|
+
before(:each) do
|
5
|
+
@driver = WebPilot.new(:is_headless => true)
|
6
|
+
end
|
7
|
+
|
8
|
+
after(:each) do
|
9
|
+
@driver.quit
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should do some basics" do
|
13
|
+
@driver.navigate.to "http://www.google.com"
|
14
|
+
@driver.find_element(:css, '#gbqfq').send_keys('mwrc ruby')
|
15
|
+
@driver.find_element(:css, '#gbqfb').click
|
16
|
+
@driver.wait_for(:xpath, "//ol[@id='rso']/li//a")
|
17
|
+
@driver.find_element(:xpath, "//ol[@id='rso']/li//a").click
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
|
2
|
+
|
3
|
+
describe "WebPilot" do
|
4
|
+
context "default" do
|
5
|
+
before(:each) do
|
6
|
+
@driver = WebPilot.new(:is_headless => true)
|
7
|
+
end
|
8
|
+
|
9
|
+
after(:each) do
|
10
|
+
@driver.quit
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should launch firefox by default" do
|
14
|
+
@driver.navigate.to "http://www.google.com"
|
15
|
+
ps_firefox = `ps -ef | grep "[f]irefox"`
|
16
|
+
ps_firefox['foreground'].should_not == nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "firefox" do
|
21
|
+
before(:each) do
|
22
|
+
@driver = WebPilot.new(:browser => :firefox, :is_headless => true)
|
23
|
+
end
|
24
|
+
|
25
|
+
after(:each) do
|
26
|
+
@driver.quit
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should launch firefox when specified" do
|
30
|
+
@driver.navigate.to "http://www.google.com"
|
31
|
+
ps_firefox = `ps -ef | grep "[f]irefox"`
|
32
|
+
ps_firefox['foreground'].should_not == nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "chrome" do
|
37
|
+
before(:each) do
|
38
|
+
begin
|
39
|
+
@driver = WebPilot.new(:browser => :chrome, :is_headless => true)
|
40
|
+
rescue Selenium::WebDriver::Error::WebDriverError => e
|
41
|
+
@driver = nil
|
42
|
+
@pending = true
|
43
|
+
@exception = e
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
after(:each) do
|
48
|
+
@driver.quit unless @driver.nil?
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should launch chrome when specified" do
|
52
|
+
if @driver.nil? and @pending
|
53
|
+
pending(@exception)
|
54
|
+
else
|
55
|
+
@driver.navigate.to "http://www.google.com"
|
56
|
+
sleep 5
|
57
|
+
ps_chrome = `ps -ef | grep "[c]hrom"`
|
58
|
+
ps_chrome['foreground'].should_not == nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: webpilot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sam Rawlins
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-26 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: WebPilot is a wrapper for Selenium::WebDriver that makes the library
|
15
|
+
more ruby-esque. Rather than inherit from Selenium::WebDriver, an instance of WebPilot
|
16
|
+
has a Selenium::WebDriver as an attribute. It serves other functions rather than
|
17
|
+
just extending Selenium::WebDriver, so it also wraps a logger, saves screenshots,
|
18
|
+
etc.
|
19
|
+
email:
|
20
|
+
- katt-core@listserv.arizona.edu
|
21
|
+
executables: []
|
22
|
+
extensions: []
|
23
|
+
extra_rdoc_files: []
|
24
|
+
files:
|
25
|
+
- .gitignore
|
26
|
+
- Gemfile
|
27
|
+
- Rakefile
|
28
|
+
- lib/webpilot.rb
|
29
|
+
- spec/basic_spec.rb
|
30
|
+
- spec/browsers_spec.rb
|
31
|
+
- spec/spec_helper.rb
|
32
|
+
homepage:
|
33
|
+
licenses: []
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ! '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
requirements: []
|
51
|
+
rubyforge_project:
|
52
|
+
rubygems_version: 1.8.17
|
53
|
+
signing_key:
|
54
|
+
specification_version: 3
|
55
|
+
summary: A facade for Selenium::WebDriver to make the library more ruby-esque.
|
56
|
+
test_files: []
|
57
|
+
has_rdoc:
|