vapir-common 1.7.2 → 1.8.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/History.txt +0 -5
- data/lib/vapir-common.rb +16 -4
- data/lib/vapir-common/browser.rb +189 -144
- data/lib/vapir-common/browsers.rb +21 -9
- data/lib/vapir-common/config.rb +341 -0
- data/lib/vapir-common/container.rb +160 -30
- data/lib/vapir-common/element.rb +65 -555
- data/lib/vapir-common/element_class_and_module.rb +378 -0
- data/lib/vapir-common/element_collection.rb +108 -20
- data/lib/vapir-common/elements/elements.rb +243 -67
- data/lib/vapir-common/external/core_extensions.rb +62 -0
- data/lib/vapir-common/handle_options.rb +1 -1
- data/lib/vapir-common/keycodes.rb +135 -0
- data/lib/vapir-common/options.rb +5 -38
- data/lib/vapir-common/page_container.rb +26 -21
- data/lib/vapir-common/specifier.rb +2 -2
- data/lib/vapir-common/version.rb +5 -0
- data/lib/vapir-common/waiter.rb +44 -90
- data/lib/vapir.rb +7 -0
- metadata +12 -27
- data/lib/vapir-common/testcase.rb +0 -89
- data/lib/vapir-common/win_window.rb +0 -1227
@@ -0,0 +1,62 @@
|
|
1
|
+
class Module
|
2
|
+
def alias_deprecated(to, from)
|
3
|
+
define_method to do |*args|
|
4
|
+
if !respond_to?(:config) || config.warn_deprecated
|
5
|
+
Kernel.warn_with_caller "DEPRECATION WARNING: #{self.class.name}\##{to} is deprecated. Please use #{self.class.name}\##{from}"
|
6
|
+
end
|
7
|
+
send(from, *args)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class String
|
13
|
+
if method_defined?(:ord)
|
14
|
+
alias vapir_ord ord
|
15
|
+
else
|
16
|
+
def vapir_ord
|
17
|
+
unpack("U*")[0] # assume it's unicode
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
unless :to_proc.respond_to?(:to_proc)
|
23
|
+
class Symbol
|
24
|
+
# Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:
|
25
|
+
#
|
26
|
+
# # The same as people.collect { |p| p.name }
|
27
|
+
# people.collect(&:name)
|
28
|
+
#
|
29
|
+
# # The same as people.select { |p| p.manager? }.collect { |p| p.salary }
|
30
|
+
# people.select(&:manager?).collect(&:salary)
|
31
|
+
def to_proc
|
32
|
+
Proc.new { |*args| args[0].__send__(self, *args[1..-1]) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module Kernel
|
38
|
+
# this is the Y-combinator, which allows anonymous recursive functions. for a simple example,
|
39
|
+
# to define a recursive function to return the length of an array:
|
40
|
+
#
|
41
|
+
# length = ycomb do |len|
|
42
|
+
# proc{|list| list == [] ? 0 : len.call(list[1..-1]) }
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# see https://secure.wikimedia.org/wikipedia/en/wiki/Fixed_point_combinator#Y_combinator
|
46
|
+
# and chapter 9 of the little schemer, available as the sample chapter at http://www.ccs.neu.edu/home/matthias/BTLS/
|
47
|
+
def ycomb
|
48
|
+
proc{|f| f.call(f) }.call(proc{|f| yield proc{|*x| f.call(f).call(*x) } })
|
49
|
+
end
|
50
|
+
module_function :ycomb
|
51
|
+
|
52
|
+
def warn_with_caller(message)
|
53
|
+
Kernel.warn "#{message}\ncalled from: #{caller[1..-1].map{|c|"\n\t"+c}}"
|
54
|
+
end
|
55
|
+
module_function :warn_with_caller
|
56
|
+
end
|
57
|
+
|
58
|
+
require 'enumerator'
|
59
|
+
module Vapir
|
60
|
+
Enumerator = Object.const_defined?('Enumerator') ? ::Enumerator : Enumerable::Enumerator
|
61
|
+
end
|
62
|
+
|
@@ -6,7 +6,7 @@
|
|
6
6
|
def handle_options(given_options, default_options, other_allowed_keys=[])
|
7
7
|
given_options=given_options.dup
|
8
8
|
unless (unknown_keys=(given_options.keys-default_options.keys-other_allowed_keys)).empty?
|
9
|
-
raise ArgumentError, "Unknown options: #{
|
9
|
+
raise ArgumentError, "Unknown options: #{unknown_keys.map(&:inspect).join(', ')}. Known options are #{(default_options.keys+other_allowed_keys).uniq.map(&:inspect).join(', ')}"
|
10
10
|
end
|
11
11
|
(default_options.keys-given_options.keys).each do |key|
|
12
12
|
given_options[key]=default_options[key]
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'vapir-common/external/core_extensions'
|
2
|
+
module Vapir
|
3
|
+
KeyCodes = (('0'..'9').to_a+('a'..'z').to_a).inject({}){|hash, char| hash.merge(char.to_sym => char.vapir_ord) }.merge(
|
4
|
+
{
|
5
|
+
:cancel => 3,
|
6
|
+
:help => 6,
|
7
|
+
:back_space => 8,
|
8
|
+
:tab => 9,
|
9
|
+
:clear => 12,
|
10
|
+
:return => 13,
|
11
|
+
:enter => 14,
|
12
|
+
:shift => 16,
|
13
|
+
:control => 17,
|
14
|
+
:alt => 18,
|
15
|
+
:pause => 19,
|
16
|
+
:caps_lock => 20,
|
17
|
+
:escape => 27,
|
18
|
+
:space => 32,
|
19
|
+
:page_up => 33,
|
20
|
+
:page_down => 34,
|
21
|
+
:end => 35,
|
22
|
+
:home => 36,
|
23
|
+
:left => 37,
|
24
|
+
:up => 38,
|
25
|
+
:right => 39,
|
26
|
+
:down => 40,
|
27
|
+
:printscreen => 44,
|
28
|
+
:insert => 45,
|
29
|
+
:delete => 46,
|
30
|
+
:semicolon => 59,
|
31
|
+
:equals => 61,
|
32
|
+
:context_menu => 93,
|
33
|
+
:numpad0 => 96,
|
34
|
+
:numpad1 => 97,
|
35
|
+
:numpad2 => 98,
|
36
|
+
:numpad3 => 99,
|
37
|
+
:numpad4 => 100,
|
38
|
+
:numpad5 => 101,
|
39
|
+
:numpad6 => 102,
|
40
|
+
:numpad7 => 103,
|
41
|
+
:numpad8 => 104,
|
42
|
+
:numpad9 => 105,
|
43
|
+
:multiply => 106,
|
44
|
+
:add => 107,
|
45
|
+
:separator => 108,
|
46
|
+
:subtract => 109,
|
47
|
+
:decimal => 110,
|
48
|
+
:divide => 111,
|
49
|
+
:f1 => 112,
|
50
|
+
:f2 => 113,
|
51
|
+
:f3 => 114,
|
52
|
+
:f4 => 115,
|
53
|
+
:f5 => 116,
|
54
|
+
:f6 => 117,
|
55
|
+
:f7 => 118,
|
56
|
+
:f8 => 119,
|
57
|
+
:f9 => 120,
|
58
|
+
:f10 => 121,
|
59
|
+
:f11 => 122,
|
60
|
+
:f12 => 123,
|
61
|
+
:f13 => 124,
|
62
|
+
:f14 => 125,
|
63
|
+
:f15 => 126,
|
64
|
+
:f16 => 127,
|
65
|
+
:f17 => 128,
|
66
|
+
:f18 => 129,
|
67
|
+
:f19 => 130,
|
68
|
+
:f20 => 131,
|
69
|
+
:f21 => 132,
|
70
|
+
:f22 => 133,
|
71
|
+
:f23 => 134,
|
72
|
+
:f24 => 135,
|
73
|
+
:num_lock => 144,
|
74
|
+
:scroll_lock => 145,
|
75
|
+
:comma => 188,
|
76
|
+
:dash => 189,
|
77
|
+
:period => 190,
|
78
|
+
:slash => 191,
|
79
|
+
:back_quote => 192,
|
80
|
+
:open_bracket => 219,
|
81
|
+
:back_slash => 220,
|
82
|
+
:close_bracket => 221,
|
83
|
+
:quote => 222,
|
84
|
+
:meta => 224,
|
85
|
+
})
|
86
|
+
PrintKeyCodes = (('0'..'9').to_a+('a'..'z').to_a).inject({}){|hash, char| hash.merge(char => char.to_sym) }.merge(
|
87
|
+
{
|
88
|
+
"\t" => :tab,
|
89
|
+
#"\n" => :return,
|
90
|
+
#"\n" => :enter,
|
91
|
+
' ' => :space,
|
92
|
+
';' => :semicolon,
|
93
|
+
'=' => :equals,
|
94
|
+
',' => :comma,
|
95
|
+
'.' => :period,
|
96
|
+
'/' => :slash,
|
97
|
+
"`" => :back_quote,
|
98
|
+
'[' => :open_bracket,
|
99
|
+
"\\" => :back_slash,
|
100
|
+
']' => :close_bracket,
|
101
|
+
"'" => :quote,
|
102
|
+
}).inject({}){|hash, (key, key_codes_key)| hash.merge(key => KeyCodes[key_codes_key]) }
|
103
|
+
ShiftPrintKeyCodes = ('A'..'Z').to_a.inject({}){|hash, char| hash.merge(char => char.downcase.to_sym) }.merge(
|
104
|
+
{
|
105
|
+
')' => :'0',
|
106
|
+
'!' => :'1',
|
107
|
+
'@' => :'2',
|
108
|
+
'#' => :'3',
|
109
|
+
'$' => :'4',
|
110
|
+
'%' => :'5',
|
111
|
+
'^' => :'6',
|
112
|
+
'&' => :'7',
|
113
|
+
'*' => :'8',
|
114
|
+
'(' => :'9',
|
115
|
+
':' => :semicolon,
|
116
|
+
'+' => :equals,
|
117
|
+
'<' => :comma,
|
118
|
+
'>' => :period,
|
119
|
+
'?' => :slash,
|
120
|
+
'~' => :back_quote,
|
121
|
+
'{' => :open_bracket,
|
122
|
+
'|' => :back_slash,
|
123
|
+
'}' => :close_bracket,
|
124
|
+
'"' => :quote,
|
125
|
+
}).inject({}){|hash, (key, key_codes_key)| hash.merge(key => KeyCodes[key_codes_key]) }
|
126
|
+
NumpadKeyCodes = ('0'..'9').inject({}){|hash, char| hash.merge(char => "numpad#{char}".to_sym) }.merge(
|
127
|
+
{
|
128
|
+
'*' => :multiply,
|
129
|
+
'+' => :add,
|
130
|
+
#? => :separator,
|
131
|
+
'-' => :subtract,
|
132
|
+
'.' => :decimal,
|
133
|
+
'/' => :divide,
|
134
|
+
}).inject({}){|hash, (key, key_codes_key)| hash.merge(key => KeyCodes[key_codes_key]) }
|
135
|
+
end
|
data/lib/vapir-common/options.rb
CHANGED
@@ -1,49 +1,16 @@
|
|
1
|
-
require 'user-choices'
|
2
|
-
|
3
1
|
module Vapir
|
4
|
-
@@options_file = nil
|
5
|
-
@@options = nil
|
6
2
|
class << self
|
7
|
-
|
8
|
-
|
9
|
-
def options_file= file
|
10
|
-
@@options_file = file
|
3
|
+
def options_file=(file) # :nodoc:
|
4
|
+
raise NotImplementedError, "this method of specifying options is deprecated and gone. see documentation for Vapir.config"
|
11
5
|
end
|
12
6
|
def options_file
|
13
|
-
|
7
|
+
raise NotImplementedError, "this method of specifying options is deprecated and gone. see documentation for Vapir.config"
|
14
8
|
end
|
15
9
|
def options= x
|
16
|
-
|
10
|
+
raise NotImplementedError, "this method of specifying options is deprecated and gone. see documentation for Vapir.config"
|
17
11
|
end
|
18
|
-
# Return the Vapir options, as a hash. If they haven't been parsed yet,
|
19
|
-
# they will be now.
|
20
12
|
def options
|
21
|
-
|
13
|
+
raise NotImplementedError, "this method of specifying options is deprecated and gone. see documentation for Vapir.config"
|
22
14
|
end
|
23
15
|
end
|
24
|
-
|
25
|
-
class Options < UserChoices::Command
|
26
|
-
include UserChoices
|
27
|
-
def add_sources builder
|
28
|
-
builder.add_source EnvironmentSource, :with_prefix, 'watir_'
|
29
|
-
if Vapir.options_file
|
30
|
-
builder.add_source YamlConfigFileSource, :from_complete_path,
|
31
|
-
Vapir.options_file
|
32
|
-
end
|
33
|
-
end
|
34
|
-
def add_choices builder
|
35
|
-
builder.add_choice :browser,
|
36
|
-
:type => Vapir::Browser.browser_names,
|
37
|
-
:default => Vapir::Browser.default
|
38
|
-
builder.add_choice :speed,
|
39
|
-
:type => ['slow', 'fast', 'zippy'],
|
40
|
-
:default => 'fast'
|
41
|
-
builder.add_choice :visible,
|
42
|
-
:type => :boolean
|
43
|
-
end
|
44
|
-
def execute
|
45
|
-
@user_choices[:speed] = @user_choices[:speed].to_sym
|
46
|
-
@user_choices
|
47
|
-
end
|
48
|
-
end
|
49
16
|
end
|
@@ -1,21 +1,26 @@
|
|
1
|
-
module Vapir
|
2
|
-
module PageContainer
|
3
|
-
include Vapir::Container
|
4
|
-
def containing_object
|
5
|
-
document_object
|
6
|
-
end
|
7
|
-
def
|
8
|
-
document_object.documentElement || raise(Exception::ExistenceFailureException, "document_object.documentElement was nil")
|
9
|
-
end
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
1
|
+
module Vapir
|
2
|
+
module PageContainer
|
3
|
+
include Vapir::Container
|
4
|
+
def containing_object
|
5
|
+
document_object
|
6
|
+
end
|
7
|
+
def document_element_object
|
8
|
+
document_object.documentElement || raise(Exception::ExistenceFailureException, "document_object.documentElement was nil")
|
9
|
+
end
|
10
|
+
alias document_element document_element_object
|
11
|
+
|
12
|
+
def title
|
13
|
+
document_object.title
|
14
|
+
end
|
15
|
+
# The url of the page object.
|
16
|
+
def url
|
17
|
+
document_object.location.href
|
18
|
+
end
|
19
|
+
def page_container
|
20
|
+
self
|
21
|
+
end
|
22
|
+
def active_element
|
23
|
+
base_element_class.new(nil, nil, extra_for_contained.merge(:candidates => proc{|container| [container.document_object.activeElement] })).to_subtype
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -50,7 +50,7 @@ module Vapir
|
|
50
50
|
|
51
51
|
# this is a list of what users can specify (there are additional possible hows that may be given
|
52
52
|
# to the Element constructor, but not generally for use by users, such as :element_object or :label
|
53
|
-
HowList=[:attributes, :xpath, :custom, :element_object, :label]
|
53
|
+
HowList=[:attributes, :xpath, :css, :custom, :element_object, :label]
|
54
54
|
|
55
55
|
# returns an Enumerable of element objects that _may_ match (note, not do match, necessarily)
|
56
56
|
# the given specifiers on the given container. these are obtained from the container's containing_object
|
@@ -141,7 +141,7 @@ module Vapir
|
|
141
141
|
unless specifiers_list.is_a?(Enumerable) && specifiers_list.all?{|spec| spec.is_a?(Hash)}
|
142
142
|
raise ArgumentError, "specifiers_list should be a list of Hashes!"
|
143
143
|
end
|
144
|
-
if
|
144
|
+
if Object.const_defined?('JsshObject') && (candidates.is_a?(JsshObject) || (candidates.length != 0 && candidates.all?{|c| c.is_a?(JsshObject)}))
|
145
145
|
# optimize for JSSH by moving code to the other side of the socket, rather than talking across it a whole lot
|
146
146
|
# this javascript should be exactly the same as the ruby in the else (minus WIN32OLE optimization) -
|
147
147
|
# just written in javascript instead of ruby.
|
data/lib/vapir-common/waiter.rb
CHANGED
@@ -1,100 +1,53 @@
|
|
1
1
|
require 'vapir-common/exceptions'
|
2
2
|
|
3
3
|
module Vapir
|
4
|
+
class Waiter # :nodoc:all
|
5
|
+
# How long to wait between each iteration through the wait_until
|
6
|
+
# loop. In seconds.
|
7
|
+
attr_accessor :polling_interval
|
4
8
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
attr_reader :sleep_time
|
11
|
-
def initialize
|
12
|
-
@sleep_time = 0.0
|
13
|
-
end
|
14
|
-
def sleep seconds
|
15
|
-
@sleep_time += Kernel.sleep seconds
|
16
|
-
end
|
17
|
-
def now
|
18
|
-
Time.now
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class Waiter
|
23
|
-
# This is an interface to a TimeKeeper which proxies
|
24
|
-
# calls to "sleep" and "Time.now".
|
25
|
-
# Useful for unit testing Waiter.
|
26
|
-
attr_accessor :timer
|
27
|
-
|
28
|
-
# How long to wait between each iteration through the wait_until
|
29
|
-
# loop. In seconds.
|
30
|
-
attr_accessor :polling_interval
|
31
|
-
|
32
|
-
# Timeout for wait_until.
|
33
|
-
attr_accessor :timeout
|
9
|
+
# Timeout for wait_until.
|
10
|
+
attr_accessor :timeout
|
11
|
+
|
12
|
+
@@default_polling_interval = 0.5
|
13
|
+
@@default_timeout = 60.0
|
34
14
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
# Vapir::TimeOutException.
|
55
|
-
def wait_until # block
|
56
|
-
start_time = now
|
57
|
-
until yield do
|
58
|
-
if (duration = now - start_time) > @timeout
|
59
|
-
raise Vapir::Exception::TimeOutException.new(duration, @timeout),
|
60
|
-
"Timed out after #{duration} seconds."
|
15
|
+
def initialize(timeout=@@default_timeout, polling_interval=@@default_polling_interval)
|
16
|
+
@timeout = timeout
|
17
|
+
@polling_interval = polling_interval
|
18
|
+
end
|
19
|
+
|
20
|
+
module WaitUntil
|
21
|
+
public
|
22
|
+
# Execute the provided block until either (1) it returns true, or
|
23
|
+
# (2) the timeout (in seconds) has been reached. If the timeout is reached,
|
24
|
+
# a TimeOutException will be raised. The block will always
|
25
|
+
# execute at least once.
|
26
|
+
#
|
27
|
+
# waiter = Waiter.new(5)
|
28
|
+
# waiter.wait_until {puts 'hello'}
|
29
|
+
#
|
30
|
+
# This code will print out "hello" for five seconds, and then raise a
|
31
|
+
# Vapir::TimeOutException.
|
32
|
+
def wait_until(timeout=::Vapir::Waiter.send(:class_variable_get, '@@default_timeout'), polling_interval=::Vapir::Waiter.send(:class_variable_get, '@@default_polling_interval'), &block)
|
33
|
+
::Waiter.try_for(timeout, :interval => polling_interval, &block)
|
61
34
|
end
|
62
|
-
sleep @polling_interval
|
63
35
|
end
|
36
|
+
include ::Vapir::Waiter::WaitUntil
|
37
|
+
extend ::Vapir::Waiter::WaitUntil
|
64
38
|
end
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
#
|
71
|
-
# Waiter.wait_until(5) {puts 'hello'}
|
72
|
-
#
|
73
|
-
# This code will print out "hello" for five seconds, and then raise a
|
74
|
-
# Vapir::TimeOutException.
|
75
|
-
|
76
|
-
# IDEA: wait_until: remove defaults from Waiter.wait_until
|
77
|
-
def self.wait_until(timeout=@@default_timeout,
|
78
|
-
polling_interval=@@default_polling_interval)
|
79
|
-
waiter = new(timeout, polling_interval)
|
80
|
-
waiter.wait_until { yield }
|
81
|
-
end
|
82
|
-
|
83
|
-
private
|
84
|
-
def sleep seconds
|
85
|
-
@timer.sleep seconds
|
86
|
-
end
|
87
|
-
def now
|
88
|
-
@timer.now
|
39
|
+
include ::Vapir::Waiter::WaitUntil
|
40
|
+
extend ::Vapir::Waiter::WaitUntil
|
41
|
+
class Browser
|
42
|
+
include ::Vapir::Waiter::WaitUntil
|
43
|
+
extend ::Vapir::Waiter::WaitUntil
|
89
44
|
end
|
90
|
-
end
|
91
|
-
|
92
45
|
end # module
|
93
46
|
|
94
47
|
require 'vapir-common/handle_options'
|
95
48
|
|
96
49
|
class WaiterError < StandardError; end
|
97
|
-
|
50
|
+
module Waiter
|
98
51
|
# Tries for +time+ seconds to get the desired result from the given block. Stops when either:
|
99
52
|
# 1. The :condition option (which should be a proc) returns true (that is, not false or nil)
|
100
53
|
# 2. The block returns true (that is, anything but false or nil) if no :condition option is given
|
@@ -104,17 +57,18 @@ class Waiter
|
|
104
57
|
#
|
105
58
|
# Returns the value of the block, which can be handy for things that return nil on failure and some
|
106
59
|
# other object on success, like Enumerable#detect for example:
|
107
|
-
#
|
60
|
+
# found_thing=Waiter.try_for(30){ all_things().detect{|thing| thing.name=="Bill" } }
|
108
61
|
#
|
109
62
|
# Examples:
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
63
|
+
# Waiter.try_for(30) do
|
64
|
+
# Time.now.year == 2015
|
65
|
+
# end
|
113
66
|
# Raises a WaiterError unless it is called between the last 30 seconds of December 31, 2014 and the end of 2015
|
114
67
|
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
68
|
+
# Waiter.try_for(365*24*60*60, :interval => 0.1, :exception => nil, :condition => proc{ 2+2==5 }) do
|
69
|
+
# STDERR.puts "any decisecond now ..."
|
70
|
+
# end
|
71
|
+
#
|
118
72
|
# Complains to STDERR for one year, every tenth of a second, as long as 2+2 does not equal 5. Does not
|
119
73
|
# raise an exception if 2+2 does not become equal to 5.
|
120
74
|
def self.try_for(time, options={})
|