vapir-common 1.7.2 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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={})
|