metallize 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/metallize.rb +47 -0
- data/lib/metallize/element_matcher.rb +65 -0
- data/lib/metallize/form.rb +284 -0
- data/lib/metallize/form/button.rb +2 -0
- data/lib/metallize/form/field.rb +19 -0
- data/lib/metallize/form/hidden.rb +2 -0
- data/lib/metallize/form/multi_select_list.rb +53 -0
- data/lib/metallize/form/option.rb +48 -0
- data/lib/metallize/form/select_list.rb +7 -0
- data/lib/metallize/form/submit.rb +2 -0
- data/lib/metallize/form/text.rb +2 -0
- data/lib/metallize/page.rb +62 -0
- data/lib/metallize/page/link.rb +39 -0
- data/lib/metallize/version.rb +3 -0
- data/metallize.gemspec +23 -0
- data/test/htdocs/select.html +11 -0
- data/test/spec/select_spec.rb +43 -0
- data/test/spec/spec_helper.rb +24 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7c73d9917bd4adae0a41a4ed1c2a152af4861deb
|
4
|
+
data.tar.gz: c782498d465db2481a33683a749782b9f9e95996
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0891c321113a414dfe07fe7d3d3ecaf97e2723d78246e539a28d08d45f2e4bed556634d89c8233f985e4141412c08c28d85f0b51716fdc2dd2770af1db9bd5d7
|
7
|
+
data.tar.gz: 4045961d464c82d642eb303dc46a7fc3bea3b75a777c3f34874c970b28c444297a491e223d8a7c6a1c712ec16a8d2ebf2039b414918e40c6d79c9a3495cb84fd
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Sam Treweek
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Metallize
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'metallize'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install metallize
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( http://github.com/<my-github-username>/metallize/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/metallize.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'metallize/version'
|
2
|
+
require 'selenium-webdriver'
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
require 'metallize/page'
|
6
|
+
require 'metallize/element_matcher'
|
7
|
+
|
8
|
+
require 'metallize/form'
|
9
|
+
require 'metallize/form/field'
|
10
|
+
require 'metallize/form/button'
|
11
|
+
require 'metallize/form/hidden'
|
12
|
+
require 'metallize/form/submit'
|
13
|
+
require 'metallize/form/text'
|
14
|
+
require 'metallize/form/multi_select_list'
|
15
|
+
require 'metallize/form/select_list'
|
16
|
+
require 'metallize/form/option'
|
17
|
+
|
18
|
+
require 'metallize/page'
|
19
|
+
require 'metallize/page/link'
|
20
|
+
|
21
|
+
class Metallize
|
22
|
+
|
23
|
+
attr_reader :driver
|
24
|
+
|
25
|
+
def initialize(browser)
|
26
|
+
@driver = Selenium::WebDriver.for browser
|
27
|
+
end
|
28
|
+
|
29
|
+
def get(uri)
|
30
|
+
driver.get(uri)
|
31
|
+
Page.new(driver)
|
32
|
+
end
|
33
|
+
|
34
|
+
def quit
|
35
|
+
driver.quit
|
36
|
+
end
|
37
|
+
|
38
|
+
alias :close :quit
|
39
|
+
|
40
|
+
def method_missing(sym)
|
41
|
+
driver.send sym
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.inspect
|
45
|
+
'Metallize; Mechanize API using Selenium-WebDriver'
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Metallize::ElementMatcher
|
2
|
+
|
3
|
+
def elements_with singular, plural = "#{singular}s"
|
4
|
+
class_eval <<-CODE
|
5
|
+
def #{plural}_with criteria = {}
|
6
|
+
selector = method = nil
|
7
|
+
if String === criteria then
|
8
|
+
criteria = {:name => criteria}
|
9
|
+
else
|
10
|
+
criteria = criteria.each_with_object({}) { |(k, v), h|
|
11
|
+
case k = k.to_sym
|
12
|
+
when :id
|
13
|
+
h[:dom_id] = v
|
14
|
+
when :class
|
15
|
+
h[:dom_class] = v
|
16
|
+
when :search, :xpath, :css
|
17
|
+
if v
|
18
|
+
selector = v
|
19
|
+
method = k
|
20
|
+
end
|
21
|
+
else
|
22
|
+
h[k] = v
|
23
|
+
end
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
f = select_#{plural}(selector, method).find_all do |thing|
|
28
|
+
criteria.all? do |k,v|
|
29
|
+
v === thing.send(k)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
yield f if block_given?
|
33
|
+
f
|
34
|
+
end
|
35
|
+
|
36
|
+
def #{singular}_with criteria = {}
|
37
|
+
f = #{plural}_with(criteria).first
|
38
|
+
yield f if block_given?
|
39
|
+
f
|
40
|
+
end
|
41
|
+
|
42
|
+
def #{singular}_with! criteria = {}
|
43
|
+
f = #{singular}_with(criteria)
|
44
|
+
raise Mechanize::ElementNotFoundError.new(self, :#{singular}, criteria) if f.nil?
|
45
|
+
yield f if block_given?
|
46
|
+
f
|
47
|
+
end
|
48
|
+
|
49
|
+
def select_#{plural} selector, method = :search
|
50
|
+
if selector.nil? then
|
51
|
+
#{plural}
|
52
|
+
else
|
53
|
+
nodes = __send__(method, selector)
|
54
|
+
#{plural}.find_all do |element|
|
55
|
+
nodes.include?(element.node)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
alias :#{singular} :#{singular}_with
|
61
|
+
CODE
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
@@ -0,0 +1,284 @@
|
|
1
|
+
require_relative 'element_matcher'
|
2
|
+
|
3
|
+
class Metallize::Form
|
4
|
+
|
5
|
+
extend Metallize::ElementMatcher
|
6
|
+
|
7
|
+
attr_accessor :method, :action, :name
|
8
|
+
|
9
|
+
attr_reader :fields, :buttons, :file_uploads, :radiobuttons, :checkboxes
|
10
|
+
|
11
|
+
# Content-Type for form data (i.e. application/x-www-form-urlencoded)
|
12
|
+
attr_accessor :enctype
|
13
|
+
|
14
|
+
# Character encoding of form data (i.e. UTF-8)
|
15
|
+
attr_accessor :encoding
|
16
|
+
|
17
|
+
# When true, character encoding errors will never be never raised on form
|
18
|
+
# submission. Default is false
|
19
|
+
attr_accessor :ignore_encoding_error
|
20
|
+
|
21
|
+
alias :elements :fields
|
22
|
+
|
23
|
+
attr_reader :form_node
|
24
|
+
attr_reader :page
|
25
|
+
|
26
|
+
attr_accessor :method, :action
|
27
|
+
|
28
|
+
attr_reader :fields, :buttons
|
29
|
+
|
30
|
+
alias :elements :fields
|
31
|
+
|
32
|
+
def initialize(driver, form)
|
33
|
+
@driver = driver
|
34
|
+
@form = form
|
35
|
+
@method = (@form.attribute('method') || 'GET').upcase
|
36
|
+
@action = @form.attribute('action')
|
37
|
+
parse
|
38
|
+
end
|
39
|
+
|
40
|
+
def name
|
41
|
+
@form.attribute('name')
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse
|
45
|
+
@fields = []
|
46
|
+
@buttons = []
|
47
|
+
|
48
|
+
form_node = @driver.find_elements(:tag_name, 'input')
|
49
|
+
form_node.each do |node|
|
50
|
+
type = (node.attribute('type') || 'text').downcase
|
51
|
+
name = node.attribute('name')
|
52
|
+
next if name.nil? && !%w[submit button image].include?(type)
|
53
|
+
case type
|
54
|
+
when 'submit'
|
55
|
+
@buttons << Submit.new(node)
|
56
|
+
when 'button'
|
57
|
+
@buttons << Button.new(node)
|
58
|
+
when 'hidden'
|
59
|
+
@fields << Hidden.new(node, node.attribute('value') || '')
|
60
|
+
when 'text'
|
61
|
+
@fields << Text.new(node, node.attribute('value') || '')
|
62
|
+
else
|
63
|
+
@fields << Field.new(node, node.attribute('value') || '')
|
64
|
+
end
|
65
|
+
|
66
|
+
form_node = @driver.find_elements(:tag_name, 'select')
|
67
|
+
form_node.each do |node|
|
68
|
+
next unless node['name']
|
69
|
+
@fields << SelectList.new(node)
|
70
|
+
end
|
71
|
+
|
72
|
+
form_node = @driver.find_elements(:tag_name, 'button')
|
73
|
+
form_node.each do |node|
|
74
|
+
@buttons << Button.new(node)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def has_value?(value)
|
79
|
+
fields.find { |f| f.value == value }
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns all field names (keys) for this form
|
83
|
+
def keys
|
84
|
+
fields.map { |f| f.name }
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns all field values for this form
|
88
|
+
def values
|
89
|
+
fields.map { |f| f.value }
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns all buttons of type Submit
|
93
|
+
def submits
|
94
|
+
@submits ||= buttons.select { |f| f.class == Submit }
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns all buttons of type Reset
|
98
|
+
def resets
|
99
|
+
@resets ||= buttons.select { |f| f.class == Reset }
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns all fields of type Text
|
103
|
+
def texts
|
104
|
+
@texts ||= fields.select { |f| f.class == Text }
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns all fields of type Hidden
|
108
|
+
def hiddens
|
109
|
+
@hiddens ||= fields.select { |f| f.class == Hidden }
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns all fields of type Textarea
|
113
|
+
def textareas
|
114
|
+
@textareas ||= fields.select { |f| f.class == Textarea }
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns all fields of type Keygen
|
118
|
+
def keygens
|
119
|
+
@keygens ||= fields.select { |f| f.class == Keygen }
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns whether or not the form contains a Submit button named +button_name+
|
123
|
+
def submit_button?(button_name)
|
124
|
+
submits.find { |f| f.name == button_name }
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns whether or not the form contains a Reset button named +button_name+
|
128
|
+
def reset_button?(button_name)
|
129
|
+
resets.find { |f| f.name == button_name }
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns whether or not the form contains a Text field named +field_name+
|
133
|
+
def text_field?(field_name)
|
134
|
+
texts.find { |f| f.name == field_name }
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns whether or not the form contains a Hidden field named +field_name+
|
138
|
+
def hidden_field?(field_name)
|
139
|
+
hiddens.find { |f| f.name == field_name }
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns whether or not the form contains a Textarea named +field_name+
|
143
|
+
def textarea_field?(field_name)
|
144
|
+
textareas.find { |f| f.name == field_name }
|
145
|
+
end
|
146
|
+
|
147
|
+
# This method is a shortcut to get form's DOM id.
|
148
|
+
# Common usage:
|
149
|
+
# page.form_with(:dom_id => "foorm")
|
150
|
+
# Note that you can also use +:id+ to get to this method:
|
151
|
+
# page.form_with(:id => "foorm")
|
152
|
+
def dom_id
|
153
|
+
form_node['id']
|
154
|
+
end
|
155
|
+
|
156
|
+
# This method is a shortcut to get form's DOM class.
|
157
|
+
# Common usage:
|
158
|
+
# page.form_with(:dom_class => "foorm")
|
159
|
+
# Note that you can also use +:class+ to get to this method:
|
160
|
+
# page.form_with(:class => "foorm")
|
161
|
+
def dom_class
|
162
|
+
form_node['class']
|
163
|
+
end
|
164
|
+
|
165
|
+
# Add a field with +field_name+ and +value+
|
166
|
+
def add_field!(field_name, value = nil)
|
167
|
+
fields << Field.new({'name' => field_name}, value)
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# This method sets multiple fields on the form. It takes a list of +fields+
|
172
|
+
# which are name, value pairs.
|
173
|
+
#
|
174
|
+
# If there is more than one field found with the same name, this method will
|
175
|
+
# set the first one found. If you want to set the value of a duplicate
|
176
|
+
# field, use a value which is a Hash with the key as the index in to the
|
177
|
+
# form. The index is zero based.
|
178
|
+
#
|
179
|
+
# For example, to set the second field named 'foo', you could do the
|
180
|
+
# following:
|
181
|
+
#
|
182
|
+
# form.set_fields :foo => { 1 => 'bar' }
|
183
|
+
def set_fields fields = {}
|
184
|
+
fields.each do |name, v|
|
185
|
+
case v
|
186
|
+
when Hash
|
187
|
+
v.each do |index, value|
|
188
|
+
self.fields_with(:name => name.to_s)[index].value = value
|
189
|
+
end
|
190
|
+
else
|
191
|
+
value = nil
|
192
|
+
index = 0
|
193
|
+
|
194
|
+
[v].flatten.each do |val|
|
195
|
+
index = val.to_i if value
|
196
|
+
value = val unless value
|
197
|
+
end
|
198
|
+
|
199
|
+
self.fields_with(:name => name.to_s)[index].value = value
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Fetch the value of the first input field with the name passed in. Example:
|
205
|
+
# puts form['name']
|
206
|
+
def [](field_name)
|
207
|
+
f = field(field_name)
|
208
|
+
f && f.value
|
209
|
+
end
|
210
|
+
|
211
|
+
# Set the value of the first input field with the name passed in. Example:
|
212
|
+
# form['name'] = 'Aaron'
|
213
|
+
def []=(field_name, value)
|
214
|
+
f = field(field_name)
|
215
|
+
if f
|
216
|
+
f.value = value
|
217
|
+
else
|
218
|
+
add_field!(field_name, value)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
def pretty_print(q) # :nodoc:
|
225
|
+
q.object_group(self) {
|
226
|
+
q.breakable; q.group(1, '{name', '}') { q.breakable; q.pp name }
|
227
|
+
q.breakable; q.group(1, '{method', '}') { q.breakable; q.pp method }
|
228
|
+
q.breakable; q.group(1, '{action', '}') { q.breakable; q.pp action }
|
229
|
+
q.breakable; q.group(1, '{fields', '}') {
|
230
|
+
fields.each do |field|
|
231
|
+
q.breakable
|
232
|
+
q.pp field
|
233
|
+
end
|
234
|
+
}
|
235
|
+
# q.breakable; q.group(1, '{radiobuttons', '}') {
|
236
|
+
# radiobuttons.each { |b| q.breakable; q.pp b }
|
237
|
+
# }
|
238
|
+
# q.breakable; q.group(1, '{checkboxes', '}') {
|
239
|
+
# checkboxes.each { |b| q.breakable; q.pp b }
|
240
|
+
# }
|
241
|
+
# q.breakable; q.group(1, '{file_uploads', '}') {
|
242
|
+
# file_uploads.each { |b| q.breakable; q.pp b }
|
243
|
+
# }
|
244
|
+
q.breakable; q.group(1, '{buttons', '}') {
|
245
|
+
buttons.each { |b| q.breakable; q.pp b }
|
246
|
+
}
|
247
|
+
}
|
248
|
+
end
|
249
|
+
|
250
|
+
elements_with :field
|
251
|
+
|
252
|
+
def submit
|
253
|
+
# 1. Loop through the non hidden fields and if they're active and displayed enter the value
|
254
|
+
@fields.each do |field|
|
255
|
+
unless field.kind_of?(Metallize::Form::Hidden)
|
256
|
+
|
257
|
+
element = @driver.find_element(name: field.name)
|
258
|
+
if element.displayed? and !field.value.empty?
|
259
|
+
element.send_keys field.value
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# 2. Submit Form
|
266
|
+
@buttons.first.node.click
|
267
|
+
|
268
|
+
wait_for_page(@driver)
|
269
|
+
|
270
|
+
# 4. Return new Page
|
271
|
+
Metallize::Page.new(@driver)
|
272
|
+
|
273
|
+
end
|
274
|
+
|
275
|
+
def wait_for_page(driver)
|
276
|
+
# 3. Wait for the Page State to Return
|
277
|
+
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
|
278
|
+
wait.until {
|
279
|
+
driver.execute_script("return document.readyState;") == "complete"
|
280
|
+
}
|
281
|
+
end
|
282
|
+
|
283
|
+
|
284
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Metallize::Form::Field
|
2
|
+
|
3
|
+
attr_accessor :name, :value, :node, :type
|
4
|
+
|
5
|
+
def initialize(node, value = node.attribute('value'))
|
6
|
+
@node = node
|
7
|
+
@name = node.attribute('name')
|
8
|
+
@type = node.attribute('type')
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def inspect # :nodoc:
|
13
|
+
"[%s:0x%x type: %s name: %s value: %s]" % [
|
14
|
+
self.class.name.sub(/Metallize::Form::/, '').downcase,
|
15
|
+
object_id, type, name, value
|
16
|
+
]
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class Metallize::Form::MultiSelectList < Metallize::Form::Field
|
2
|
+
|
3
|
+
extend Metallize::ElementMatcher
|
4
|
+
|
5
|
+
attr_accessor :options
|
6
|
+
|
7
|
+
def initialize node
|
8
|
+
value = []
|
9
|
+
@options = []
|
10
|
+
|
11
|
+
# parse
|
12
|
+
node.find_elements(:tag_name, 'option').each do |n|
|
13
|
+
@options << Metallize::Form::Option.new(n, self)
|
14
|
+
end
|
15
|
+
|
16
|
+
super node, value
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
elements_with :option
|
21
|
+
|
22
|
+
# Select no options
|
23
|
+
def select_none
|
24
|
+
@value = []
|
25
|
+
options.each { |o| o.untick }
|
26
|
+
end
|
27
|
+
|
28
|
+
def value=(values)
|
29
|
+
select_none
|
30
|
+
[values].flatten.each do |value|
|
31
|
+
option = options.find { |o| o.value == value }
|
32
|
+
if option.nil?
|
33
|
+
@value.push(value)
|
34
|
+
else
|
35
|
+
option.node.click
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get a list of all selected options
|
41
|
+
def selected_options
|
42
|
+
@options.find_all { |o| o.selected? }
|
43
|
+
end
|
44
|
+
|
45
|
+
def value
|
46
|
+
value = []
|
47
|
+
value.concat @value
|
48
|
+
value.concat selected_options.map { |o| o.value }
|
49
|
+
value.first
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Metallize::Form::Option
|
2
|
+
|
3
|
+
attr_reader :value, :selected, :text, :select_list, :node
|
4
|
+
|
5
|
+
alias :selected? :selected
|
6
|
+
|
7
|
+
def has_attribute?(node, attr)
|
8
|
+
if node.attribute(attr)
|
9
|
+
true
|
10
|
+
else
|
11
|
+
false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(node, select_list)
|
16
|
+
@node = node
|
17
|
+
@text = node.attribute('innerText')
|
18
|
+
@value = node.attribute('value') || node.attribute('innerText')
|
19
|
+
@selected = has_attribute? node, 'selected'
|
20
|
+
@select_list = select_list # The select list this option belongs to
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
# Select this option
|
25
|
+
def select
|
26
|
+
unselect_peers
|
27
|
+
@selected = true
|
28
|
+
# option = Selenium::WebDriver::Support::Select.new(node)
|
29
|
+
# option
|
30
|
+
end
|
31
|
+
|
32
|
+
# Unselect this option
|
33
|
+
def unselect
|
34
|
+
@selected = false
|
35
|
+
end
|
36
|
+
|
37
|
+
alias :tick :select
|
38
|
+
alias :untick :unselect
|
39
|
+
|
40
|
+
|
41
|
+
private
|
42
|
+
def unselect_peers
|
43
|
+
# return unless Mechanize::Form::SelectList === @select_list
|
44
|
+
|
45
|
+
@select_list.select_none
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative 'element_matcher'
|
2
|
+
|
3
|
+
class Metallize::Page
|
4
|
+
|
5
|
+
extend Metallize::ElementMatcher
|
6
|
+
|
7
|
+
attr_reader :driver
|
8
|
+
|
9
|
+
def initialize(driver)
|
10
|
+
@driver = driver
|
11
|
+
end
|
12
|
+
|
13
|
+
def title
|
14
|
+
driver.title
|
15
|
+
end
|
16
|
+
|
17
|
+
def uri
|
18
|
+
driver.current_url
|
19
|
+
end
|
20
|
+
|
21
|
+
def links
|
22
|
+
links = driver.find_elements(:tag_name, 'a')
|
23
|
+
links.map {|link| Link.new(driver, link)}
|
24
|
+
end
|
25
|
+
|
26
|
+
def forms
|
27
|
+
forms = driver.find_elements(:tag_name, 'form')
|
28
|
+
forms.map {|form| Metallize::Form.new(driver, form)}
|
29
|
+
end
|
30
|
+
|
31
|
+
def pretty_print(q)
|
32
|
+
q.object_group(self) {
|
33
|
+
q.breakable
|
34
|
+
q.group(1, '{url', '}') {q.breakable; q.pp uri }
|
35
|
+
q.breakable
|
36
|
+
# q.group(1, '{meta_refresh', '}') {
|
37
|
+
# meta_refresh.each { |link| q.breakable; q.pp link }
|
38
|
+
# }
|
39
|
+
# q.breakable
|
40
|
+
q.group(1, '{title', '}') { q.breakable; q.pp title }
|
41
|
+
q.breakable
|
42
|
+
# q.group(1, '{iframes', '}') {
|
43
|
+
# iframes.each { |link| q.breakable; q.pp link }
|
44
|
+
# }
|
45
|
+
# q.breakable
|
46
|
+
# q.group(1, '{frames', '}') {
|
47
|
+
# frames.each { |link| q.breakable; q.pp link }
|
48
|
+
# }
|
49
|
+
# q.breakable
|
50
|
+
q.group(1, '{links', '}') {
|
51
|
+
links.each { |link| q.breakable; q.pp link }
|
52
|
+
}
|
53
|
+
# q.breakable
|
54
|
+
q.group(1, '{forms', '}') {
|
55
|
+
forms.each { |form| q.breakable; q.pp form }
|
56
|
+
}
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
elements_with :link
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Metallize::Page::Link
|
2
|
+
|
3
|
+
attr_reader :driver, :link
|
4
|
+
|
5
|
+
def initialize(driver, link)
|
6
|
+
@driver = driver
|
7
|
+
@link = link
|
8
|
+
end
|
9
|
+
|
10
|
+
def text
|
11
|
+
link.text
|
12
|
+
end
|
13
|
+
|
14
|
+
def href
|
15
|
+
link_attribute = link.attribute('href')
|
16
|
+
URI(link_attribute).path
|
17
|
+
end
|
18
|
+
|
19
|
+
def click
|
20
|
+
link.click
|
21
|
+
|
22
|
+
# 1. Wait for the Page State to Return
|
23
|
+
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
|
24
|
+
wait.until {
|
25
|
+
driver.execute_script("return document.readyState;") == "complete"
|
26
|
+
}
|
27
|
+
|
28
|
+
# 2. Return new Page
|
29
|
+
Metallize::Page.new(driver)
|
30
|
+
end
|
31
|
+
|
32
|
+
def pretty_print(q) # :nodoc:
|
33
|
+
q.object_group(self) {
|
34
|
+
q.breakable; q.pp text
|
35
|
+
q.breakable; q.pp href
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/metallize.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'metallize/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "metallize"
|
8
|
+
spec.version = Metallize::VERSION
|
9
|
+
spec.authors = ["Sam Treweek"]
|
10
|
+
spec.email = ["samtreweek@uksa.eu"]
|
11
|
+
spec.summary = %q{Mechanize plus Selenium}
|
12
|
+
spec.description = %q{Mechanize API with Selenium-WebDriver Library}
|
13
|
+
spec.homepage = "http://uksa.eu"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency 'rake', '~> 0'
|
23
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<form action="/numbers" method="POST" name="post_numbers" id="post_numbers">
|
2
|
+
<select id="numbers" name="numbers" class="numbers" required="">
|
3
|
+
<option id="number_select_1" value="1">1</option>
|
4
|
+
<option id="number_select_2" value="2">2</option>
|
5
|
+
<option id="number_select_3" value="3">3</option>
|
6
|
+
<option id="number_select_4" value="4" selected="">4</option>
|
7
|
+
<option id="number_select_5" value="5">5</option>
|
8
|
+
<option id="number_select_7" value="7">7</option>
|
9
|
+
</select>
|
10
|
+
<input type="submit" class="btn-submit" id="submit" value="Submit">
|
11
|
+
</form>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe '#HTML Element Select' do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
file = File.join(File.dirname(__FILE__),"../","htdocs/select.html")
|
7
|
+
@page = @metz.get "file://#{file}"
|
8
|
+
@form = @page.forms.first
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should return list of Options' do
|
12
|
+
options_value = @form.field_with(name: 'numbers').options
|
13
|
+
expect(options_value.count).to eq 6
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should return a list of Option Values' do
|
17
|
+
options_value = @form.field_with(name: 'numbers').options.map(&:value)
|
18
|
+
expect(options_value.count).to eq 6
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should return current value of option' do
|
22
|
+
option_value = @form.field_with(name: 'numbers').value
|
23
|
+
expect(option_value).to eq "4"
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should change when a new value is selected' do
|
27
|
+
@form.field_with(name: 'numbers').value = '3'
|
28
|
+
page = @page.forms.first
|
29
|
+
option_value = page.field_with(name: 'numbers').value
|
30
|
+
expect(option_value).to eq '3'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should return the selected option' do
|
34
|
+
option_value = @form.field_with(name: 'numbers').selected_options
|
35
|
+
expect(option_value.first.selected).to eq true
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should inspect the option' do
|
39
|
+
inspect = @form.field_with(name: 'numbers').inspect
|
40
|
+
expect(inspect).to include('value: 4')
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'metallize'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'webrick'
|
4
|
+
|
5
|
+
include WEBrick
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
# Run specs in random order to surface order dependencies. If you find an
|
9
|
+
# order dependency and want to debug it, you can fix the order by providing
|
10
|
+
# the seed, which is printed after each run.
|
11
|
+
# --seed 1234
|
12
|
+
config.order = 'random'
|
13
|
+
|
14
|
+
config.before(:all) do
|
15
|
+
browser = [:chrome, :firefox].sample
|
16
|
+
puts "Used Browser: #{browser}"
|
17
|
+
@metz = Metallize.new(browser)
|
18
|
+
end
|
19
|
+
|
20
|
+
config.after(:all) do
|
21
|
+
@metz.quit
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: metallize
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sam Treweek
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMRMwEQYDVQQDDApzYW10
|
14
|
+
cmV3ZWVrMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZFgNj
|
15
|
+
b20wHhcNMTMxMDEzMjAxMjE5WhcNMTQxMDEzMjAxMjE5WjBBMRMwEQYDVQQDDApz
|
16
|
+
YW10cmV3ZWVrMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
|
17
|
+
FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDt5a6qQLOOsPv7
|
18
|
+
cdGaWtu8g622mMvgLpP5DPDc3D6BMOib9wwSXhZJJBcddtO+QN52lRb0tBR8klBE
|
19
|
+
uqAbBkv2UuBCKu2cKFldCVMQ5KKwTxj25My8cG3UVX60o7uT7HmWOmsGXqojGw9f
|
20
|
+
DFdBzURwFwI5ZtKl3sblwl25AH4qmn+DbENMgrvlYDDGs+JRBGBeMU6fN30C0PvP
|
21
|
+
YVUAdt3GAOG4KxPvA/8e5FMwohiAV6OfVjdIz7XV3c95omrZtafVVj8jQsaPOCO4
|
22
|
+
YsBd9zrZAqglVp2yo5fEzAozKS6B6FE5n+wpBR9dhjXMhfCmKKuGPjD9zvnnaE6Y
|
23
|
+
KnCyK0j9AgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
|
24
|
+
BBSoLCC1wDQu43FFE55O778Ox77OmjAfBgNVHREEGDAWgRRzYW10cmV3ZWVrQGdt
|
25
|
+
YWlsLmNvbTAfBgNVHRIEGDAWgRRzYW10cmV3ZWVrQGdtYWlsLmNvbTANBgkqhkiG
|
26
|
+
9w0BAQUFAAOCAQEAJ3swCy1wmG6tQ9oer7PpxUhZzXPYZ8JBMFaZWbkirFeoI9Yn
|
27
|
+
c4qh0FyHnaM2pqde0pqOfbKxESUHSLtb8x2vhwpTvvsjRRixdMpIBP/Vtxp6p9Oa
|
28
|
+
Ayq5ITS201gMgktPhDO8MkaSpaxMXSCcMQSWuJmdY4uDv+mAY2ansK6mpwYLLi60
|
29
|
+
ovWjYMUykCDhWQH8l8SsWRkIKcVlnjgs73ZAKCSlehzPWCFBmMgpAZeMNFHw3a5k
|
30
|
+
qzoH/QN65IPJvffqtIusKUZBsHUKy9n60vrCEgK4ZTj+VZAjvxmPRB5UYDeP6xeh
|
31
|
+
SC+V9I2l+qX545yXG4raXDZqTyh9bsMrlE+5+A==
|
32
|
+
-----END CERTIFICATE-----
|
33
|
+
date: 2015-11-23 00:00:00.000000000 Z
|
34
|
+
dependencies:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: bundler
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '1.5'
|
42
|
+
type: :development
|
43
|
+
prerelease: false
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '1.5'
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rake
|
51
|
+
requirement: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
description: Mechanize API with Selenium-WebDriver Library
|
64
|
+
email:
|
65
|
+
- samtreweek@uksa.eu
|
66
|
+
executables: []
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- ".gitignore"
|
71
|
+
- Gemfile
|
72
|
+
- LICENSE.txt
|
73
|
+
- README.md
|
74
|
+
- Rakefile
|
75
|
+
- lib/metallize.rb
|
76
|
+
- lib/metallize/element_matcher.rb
|
77
|
+
- lib/metallize/form.rb
|
78
|
+
- lib/metallize/form/button.rb
|
79
|
+
- lib/metallize/form/field.rb
|
80
|
+
- lib/metallize/form/hidden.rb
|
81
|
+
- lib/metallize/form/multi_select_list.rb
|
82
|
+
- lib/metallize/form/option.rb
|
83
|
+
- lib/metallize/form/select_list.rb
|
84
|
+
- lib/metallize/form/submit.rb
|
85
|
+
- lib/metallize/form/text.rb
|
86
|
+
- lib/metallize/page.rb
|
87
|
+
- lib/metallize/page/link.rb
|
88
|
+
- lib/metallize/version.rb
|
89
|
+
- metallize.gemspec
|
90
|
+
- test/htdocs/select.html
|
91
|
+
- test/spec/select_spec.rb
|
92
|
+
- test/spec/spec_helper.rb
|
93
|
+
homepage: http://uksa.eu
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
metadata: {}
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 2.2.2
|
114
|
+
signing_key:
|
115
|
+
specification_version: 4
|
116
|
+
summary: Mechanize plus Selenium
|
117
|
+
test_files:
|
118
|
+
- test/htdocs/select.html
|
119
|
+
- test/spec/select_spec.rb
|
120
|
+
- test/spec/spec_helper.rb
|