metallize 0.0.1
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.
- 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
|