gridium 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +42 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/gridium.gemspec +37 -0
- data/lib/driver.rb +214 -0
- data/lib/driver_extensions.rb +11 -0
- data/lib/element.rb +243 -0
- data/lib/element_extensions.rb +30 -0
- data/lib/element_verification.rb +161 -0
- data/lib/gridium.rb +39 -0
- data/lib/gridium/version.rb +3 -0
- data/lib/log.rb +142 -0
- data/lib/page.rb +106 -0
- data/lib/spec_data.rb +63 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 55207517ef6c73206e73da6b350b30dc4289c6b8
|
4
|
+
data.tar.gz: c0658a076cbf44b733c0472a4159cbd7e898e739
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2750d1c19ee74b7d3da5ace0245cb48dcb09a85ec7f89eee4c1b60b14f134b2ecd13121f182efc4a313c2005e5b2129bec8048e614871cf06cae5ac81505dfc8
|
7
|
+
data.tar.gz: 46138b07ca14d1290f7f8e10919940722a44d6ca4f351dc3bffe8c37a8dd9023a03807e391e0e9fe2f1f34270a5c920090ae208ea1b026b7bdea57309fed584c
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Seth Urban
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# Gridium
|
2
|
+
|
3
|
+
Welcome to Gridium! Gridium helps you build better automated tests using only Selenium and your advanced knowledge of page objects. This is a replacement gem for Capybara. I found it more difficult to use Capybara with Firefox and Selenium, and wanted to take advantage of everything that Selenium offered and Capybara lacked.
|
4
|
+
|
5
|
+
Before you get started you should understand how page objects work. A primer on Page Objects:
|
6
|
+
[Template Design Pattern](http://www.electricsheepdreams.com/blog/2014/12/4/template-design-pattern-the-first-avenger)
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'gridium'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install gridium
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
Comming Soon!
|
27
|
+
|
28
|
+
## Development
|
29
|
+
|
30
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
31
|
+
|
32
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
33
|
+
|
34
|
+
## Contributing
|
35
|
+
|
36
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sethuster/gridium. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
37
|
+
|
38
|
+
|
39
|
+
## License
|
40
|
+
|
41
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
42
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "gridium"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/gridium.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gridium/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "gridium"
|
8
|
+
spec.version = Gridium::VERSION
|
9
|
+
spec.authors = ["Seth Urban"]
|
10
|
+
spec.email = ["sethuster@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{This Gem is used to make building Selenium Tests without Capybara Easier.}
|
13
|
+
spec.description = %q{Capybara is a great tool to start making automated tests for your web application. However, many automation engineers find it difficult to use effectively for UI tests. Capybara works better when webkit, and not so well with mozilla. This makes Selenium integration difficult, this gem remedies that.}
|
14
|
+
spec.homepage = "http://github.com/sethuster/gridium"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
18
|
+
# delete this section to allow pushing this gem to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
21
|
+
else
|
22
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
23
|
+
end
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
spec.bindir = "exe"
|
27
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
31
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
32
|
+
spec.add_development_dependency "rspec"
|
33
|
+
|
34
|
+
spec.add_runtime_dependency "selenium-webdriver", ">=2.45.0"
|
35
|
+
spec.add_runtime_dependency "oily_png"
|
36
|
+
|
37
|
+
end
|
data/lib/driver.rb
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'selenium-webdriver'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
|
5
|
+
class Driver
|
6
|
+
@@driver = nil
|
7
|
+
|
8
|
+
def self.reset
|
9
|
+
driver.manage.delete_all_cookies
|
10
|
+
driver.manage.timeouts.page_load = Gridium.config.page_load_timeout
|
11
|
+
driver.manage.timeouts.implicit_wait = Gridium.config.element_timeout
|
12
|
+
|
13
|
+
# Ensure the browser is maximized to maximize visibility of element
|
14
|
+
# Currently doesn't work with chromedriver, but the following workaround does:
|
15
|
+
if @browser_type.eql?(:chrome)
|
16
|
+
width = driver.execute_script("return screen.width;")
|
17
|
+
height = driver.execute_script("return screen.height;")
|
18
|
+
driver.manage.window.move_to(0, 0)
|
19
|
+
driver.manage.window.resize_to(width, height)
|
20
|
+
else
|
21
|
+
driver.manage.window.maximize
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.driver
|
26
|
+
begin
|
27
|
+
unless @@driver
|
28
|
+
@browser_type = Gridium.config.browser
|
29
|
+
@@driver = Selenium::WebDriver.for(Gridium.config.browser)
|
30
|
+
reset
|
31
|
+
end
|
32
|
+
@@driver
|
33
|
+
rescue Exception => e
|
34
|
+
Log.debug(e.backtrace.inspect)
|
35
|
+
Log.info("Driver did not load within (#{Gridium.config.page_load_timeout}) seconds. [#{e.message}]")
|
36
|
+
$fail_test_instantly = true
|
37
|
+
Kernel.fail(e.message)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.driver= driver
|
42
|
+
@@driver.quit if @@driver
|
43
|
+
@@driver = driver
|
44
|
+
end
|
45
|
+
|
46
|
+
# =============== #
|
47
|
+
# Driver Commands #
|
48
|
+
# =============== #
|
49
|
+
|
50
|
+
def self.visit(path)
|
51
|
+
begin
|
52
|
+
Log.debug("Navigating to url: (#{path}).")
|
53
|
+
driver
|
54
|
+
time_start = Time.now
|
55
|
+
driver.navigate.to(path)
|
56
|
+
time_end = Time.new
|
57
|
+
page_load = (time_end - time_start)
|
58
|
+
Log.debug("Page loaded in (#{page_load}) seconds.")
|
59
|
+
$verification_passes += 1
|
60
|
+
rescue Exception => e
|
61
|
+
Log.debug(e.backtrace.inspect)
|
62
|
+
Log.error("#{e.message} - Also be sure to check the url formatting. http:// is required for proper test execution (www is optional).")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.nav(path)
|
67
|
+
visit(Gridium.config.url + path)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.quit
|
71
|
+
if @@driver
|
72
|
+
Log.debug('Shutting down web driver...')
|
73
|
+
@@driver.quit
|
74
|
+
@@driver = nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.go_back
|
79
|
+
driver.navigate.back
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.go_forward
|
83
|
+
driver.navigate.forward
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.html
|
87
|
+
driver.page_source
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.title
|
91
|
+
driver.title
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.current_url
|
95
|
+
driver.current_url
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.refresh
|
99
|
+
driver.navigate.refresh
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def self.current_domain
|
104
|
+
site_url = driver.current_url.to_s
|
105
|
+
# domain = site_url.match(/(https?:\/\/)?(\S*\.)?([\w\d]*\.\w+)\/?/i)[3]
|
106
|
+
domain = URI.parse(site_url)
|
107
|
+
host = domain.host
|
108
|
+
if (!host.nil?)
|
109
|
+
Log.debug("Current domain is: (#{host}).")
|
110
|
+
return host
|
111
|
+
else
|
112
|
+
Log.error("Unable to parse URL.")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.verify_url(given_url)
|
117
|
+
Log.debug('Verifying URL...')
|
118
|
+
current_url = self.current_url.to_s
|
119
|
+
current_domain = self.current_domain.to_s
|
120
|
+
if current_domain.include?(given_url)
|
121
|
+
Log.debug("Confirmed. (#{current_url}) includes (#{given_url}).")
|
122
|
+
$verification_passes += 1
|
123
|
+
else
|
124
|
+
Log.error("(#{current_url}) does not include (#{given_url}).")
|
125
|
+
Kernel.fail
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Execute Javascript on the element
|
131
|
+
#
|
132
|
+
# @param [String] script - Javascript source to execute
|
133
|
+
# @param [Element] element
|
134
|
+
#
|
135
|
+
# @return The value returned from the script
|
136
|
+
#
|
137
|
+
def self.execute_script(script, element)
|
138
|
+
driver.execute_script(script, element)
|
139
|
+
end
|
140
|
+
|
141
|
+
#
|
142
|
+
# Execute Javascript on the page
|
143
|
+
#
|
144
|
+
# @param [String] script - Javascript source to execute
|
145
|
+
#
|
146
|
+
# @return The value returned from the script
|
147
|
+
#
|
148
|
+
def self.execute_script_driver(script)
|
149
|
+
driver.execute_script(script)
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.evaluate_script(script)
|
153
|
+
driver.execute_script "return #{script}"
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.save_screenshot(type = 'saved')
|
157
|
+
Log.debug ("Capturing screenshot of browser...")
|
158
|
+
timestamp = Time.now.strftime("%Y_%m_%d__%H_%M_%S")
|
159
|
+
screenshot_path = File.join($current_run_dir, "screenshot__#{timestamp}__#{type}.png")
|
160
|
+
driver.save_screenshot(screenshot_path)
|
161
|
+
$screenshots_captured.push("screenshot__#{timestamp}__#{type}.png") # used by custom_formatter.rb for embedding in report
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.list_open_windows
|
165
|
+
handles = driver.window_handles
|
166
|
+
Log.debug("List of active windows:")
|
167
|
+
handles.each do |handle|
|
168
|
+
driver.switch_to.window(handle)
|
169
|
+
Log.debug("| Window with title: (#{driver.title}) and handle: #{handle} is currently open.")
|
170
|
+
end
|
171
|
+
driver.switch_to.window(driver.window_handles.first)
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.open_new_window(url)
|
175
|
+
Log.debug("Opening new window and loading url (#{url})...")
|
176
|
+
DriverExtensions.open_new_window(url)
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.close_window
|
180
|
+
Log.debug("Closing window (#{driver.title})...")
|
181
|
+
DriverExtensions.close_window
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.switch_to_window(title)
|
185
|
+
current_title = driver.title
|
186
|
+
Log.debug("Current window is: (#{current_title}). Switching to next window (#{title})...")
|
187
|
+
handles = driver.window_handles
|
188
|
+
driver.switch_to.window(handles.first)
|
189
|
+
handles.each do |handle|
|
190
|
+
driver.switch_to.window(handle)
|
191
|
+
if driver.title == title
|
192
|
+
Log.debug("Window (#{driver.title}) is now the active window.")
|
193
|
+
return
|
194
|
+
end
|
195
|
+
end
|
196
|
+
list_open_windows
|
197
|
+
Log.error("Unable to switch to window with title (#{title}).")
|
198
|
+
end
|
199
|
+
|
200
|
+
def self.switch_to_next_window
|
201
|
+
current_title = driver.title
|
202
|
+
Log.debug("Current window is: (#{current_title}). Switching to next window...")
|
203
|
+
driver.switch_to.window(driver.window_handles.last)
|
204
|
+
Log.debug("Window (#{driver.title}) is now the active window.")
|
205
|
+
end
|
206
|
+
|
207
|
+
def self.switch_to_main_window
|
208
|
+
current_title = driver.title
|
209
|
+
Log.debug("Current window is: (#{current_title}). Switching to main window...")
|
210
|
+
driver.switch_to.window(driver.window_handles.first)
|
211
|
+
Log.debug("Window (#{driver.title}) is now the active window.")
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
data/lib/element.rb
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
require 'selenium-webdriver'
|
2
|
+
require 'oily_png'
|
3
|
+
|
4
|
+
class Element
|
5
|
+
attr_reader :name, :by, :locator
|
6
|
+
|
7
|
+
def initialize(name, by, locator)
|
8
|
+
@name = name
|
9
|
+
@by = by
|
10
|
+
@locator = locator
|
11
|
+
|
12
|
+
# wrapped driver
|
13
|
+
@driver = Driver.driver
|
14
|
+
|
15
|
+
# selenium web element
|
16
|
+
@element = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"'#{@name}' (By:#{@by} => '#{@locator}')"
|
21
|
+
end
|
22
|
+
|
23
|
+
def element
|
24
|
+
if @element.nil? or is_stale?
|
25
|
+
wait = Selenium::WebDriver::Wait.new :timeout => Gridium.config.element_timeout, :interval => 1
|
26
|
+
if Gridium.config.visible_elements_only
|
27
|
+
wait.until {
|
28
|
+
elements = @driver.find_elements(@by, @locator)
|
29
|
+
elements.each do |element|
|
30
|
+
if element.displayed?
|
31
|
+
@element = element;
|
32
|
+
end
|
33
|
+
end
|
34
|
+
@element.enabled?
|
35
|
+
}
|
36
|
+
else
|
37
|
+
wait.until { @element = @driver.find_element(@by, @locator); Log.debug("Finding element #{self}..."); @element.enabled? }
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
@element
|
42
|
+
end
|
43
|
+
|
44
|
+
def element= e
|
45
|
+
@element = e
|
46
|
+
end
|
47
|
+
|
48
|
+
# ================ #
|
49
|
+
# Element Commands #
|
50
|
+
# ================ #
|
51
|
+
|
52
|
+
# soft failure, will not kill test immediately
|
53
|
+
def verify(timeout=nil)
|
54
|
+
Log.debug('Verifying new element...')
|
55
|
+
timeout = Gridium.config.element_timeout if timeout.nil?
|
56
|
+
ElementVerification.new(self, timeout)
|
57
|
+
end
|
58
|
+
|
59
|
+
# hard failure, will kill test immediately
|
60
|
+
def wait_until(timeout=nil)
|
61
|
+
Log.debug('Waiting for new element...')
|
62
|
+
timeout = Gridium.config.element_timeout if timeout.nil?
|
63
|
+
ElementVerification.new(self, timeout, fail_test=true)
|
64
|
+
end
|
65
|
+
|
66
|
+
def attribute(name)
|
67
|
+
element.attribute(name)
|
68
|
+
end
|
69
|
+
|
70
|
+
def present?
|
71
|
+
begin
|
72
|
+
return element.enabled?
|
73
|
+
rescue Exception => e
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def displayed?
|
79
|
+
begin
|
80
|
+
return element.displayed?
|
81
|
+
rescue Exception => e
|
82
|
+
return false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def enabled?
|
87
|
+
return element.enabled?
|
88
|
+
end
|
89
|
+
|
90
|
+
def clear
|
91
|
+
element.clear
|
92
|
+
end
|
93
|
+
|
94
|
+
def click
|
95
|
+
Log.debug("Clicking on #{self}")
|
96
|
+
if element.enabled?
|
97
|
+
ElementExtensions.highlight(self) if Gridium.config.highlight_verifications
|
98
|
+
$verification_passes += 1
|
99
|
+
element.click
|
100
|
+
else
|
101
|
+
Log.error('Cannot click on element. Element is not present.')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def send_keys(*args)
|
106
|
+
Log.debug("Typing: #{args} into element: (#{self}).")
|
107
|
+
if element.enabled?
|
108
|
+
ElementExtensions.highlight(self) if Gridium.config.highlight_verifications
|
109
|
+
$verification_passes += 1
|
110
|
+
element.send_keys *args
|
111
|
+
else
|
112
|
+
Log.error('Cannot type into element. Element is not present.')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def location
|
117
|
+
element.location
|
118
|
+
end
|
119
|
+
|
120
|
+
def hover_over
|
121
|
+
Log.debug("Hovering over element (#{self.to_s})...")
|
122
|
+
# @driver.mouse.move_to(element) # Note: Doesn't work with Selenium 2.42 bindings for Firefox v31
|
123
|
+
# @driver.action.move_to(element).perform
|
124
|
+
# @driver.mouse_over(@locator)
|
125
|
+
if element.enabled?
|
126
|
+
$verification_passes += 1
|
127
|
+
ElementExtensions.hover_over(self) # Javascript workaround to above issue
|
128
|
+
else
|
129
|
+
Log.error('Cannot hover over element. Element is not present.')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def hover_away
|
134
|
+
Log.debug("Hovering away from element (#{self.to_s})...")
|
135
|
+
if element.enabled?
|
136
|
+
$verification_passes += 1
|
137
|
+
ElementExtensions.hover_away(self) # Javascript workaround to above issue
|
138
|
+
else
|
139
|
+
Log.error('Cannot hover away from element. Element is not present.')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def scroll_into_view
|
144
|
+
if element.enabled?
|
145
|
+
$verification_passes += 1
|
146
|
+
ElementExtensions.scroll_to(self)
|
147
|
+
else
|
148
|
+
Log.error('Cannot scroll element into view. Element is not present.')
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def size
|
153
|
+
element.size
|
154
|
+
end
|
155
|
+
|
156
|
+
def selected?
|
157
|
+
element.selected?
|
158
|
+
end
|
159
|
+
|
160
|
+
def tag_name
|
161
|
+
element.tag_name
|
162
|
+
end
|
163
|
+
|
164
|
+
def submit
|
165
|
+
element.submit
|
166
|
+
end
|
167
|
+
|
168
|
+
def text
|
169
|
+
element.text
|
170
|
+
end
|
171
|
+
|
172
|
+
def text= text
|
173
|
+
element.send_keys text
|
174
|
+
end
|
175
|
+
|
176
|
+
#
|
177
|
+
# Search for an element within this element
|
178
|
+
#
|
179
|
+
# @param [Symbol] by (:css or :xpath)
|
180
|
+
# @param [String] locator
|
181
|
+
#
|
182
|
+
# @return [Element] element
|
183
|
+
#
|
184
|
+
def find_element(by, locator)
|
185
|
+
Log.debug('Finding element...')
|
186
|
+
element.find_element(by, locator)
|
187
|
+
end
|
188
|
+
|
189
|
+
#
|
190
|
+
# Search for an elements within this element
|
191
|
+
#
|
192
|
+
# @param [Symbol] by (:css or :xpath)
|
193
|
+
# @param [String] locator
|
194
|
+
#
|
195
|
+
# @return [Array] elements
|
196
|
+
#
|
197
|
+
def find_elements(by, locator)
|
198
|
+
element.find_elements(by, locator)
|
199
|
+
end
|
200
|
+
|
201
|
+
def save_element_screenshot
|
202
|
+
Log.debug ("Capturing screenshot of element...")
|
203
|
+
self.scroll_into_view
|
204
|
+
|
205
|
+
timestamp = Time.now.strftime("%Y_%m_%d__%H_%M_%S")
|
206
|
+
name = self.name.gsub(' ', '_')
|
207
|
+
screenshot_path = File.join($current_run_dir, "#{name}__#{timestamp}.png")
|
208
|
+
@driver.save_screenshot(screenshot_path)
|
209
|
+
|
210
|
+
location_x = self.location.x
|
211
|
+
location_y = self.location.y
|
212
|
+
element_width = self.size.width
|
213
|
+
element_height = self.size.height
|
214
|
+
|
215
|
+
# ChunkyPNG commands tap into oily_png (performance-enhanced version of chunky_png)
|
216
|
+
image = ChunkyPNG::Image.from_file(screenshot_path.to_s)
|
217
|
+
image1 = image.crop(location_x, location_y, element_width, element_height)
|
218
|
+
image2 = image1.to_image
|
219
|
+
element_screenshot_path = File.join($current_run_dir, "#{name}__#{timestamp}.png")
|
220
|
+
image2.save(element_screenshot_path)
|
221
|
+
$screenshots_captured.push("#{name}__#{timestamp}.png")
|
222
|
+
end
|
223
|
+
|
224
|
+
def method_missing(method_sym, *arguments, &block)
|
225
|
+
Log.debug("called #{method_sym} on element #{@locator} by #{@by_type}")
|
226
|
+
if @element.respond_to?(method_sym)
|
227
|
+
@element.method(method_sym).call(*arguments, &block)
|
228
|
+
else
|
229
|
+
super
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
private
|
234
|
+
|
235
|
+
def is_stale?
|
236
|
+
begin
|
237
|
+
@element.enabled?
|
238
|
+
return false
|
239
|
+
rescue Exception => e
|
240
|
+
return true
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
include Gridium
|
2
|
+
|
3
|
+
class Gridium::ElementExtensions
|
4
|
+
def self.highlight(element)
|
5
|
+
Log.debug("Highlighting element...")
|
6
|
+
original_border = Driver.execute_script("return arguments[0].style.border", element.element)
|
7
|
+
original_background = Driver.execute_script("return arguments[0].style.backgroundColor", element.element)
|
8
|
+
Driver.execute_script("arguments[0].style.border='3px solid lime'; return;", element.element)
|
9
|
+
Driver.execute_script("arguments[0].style.backgroundColor='lime'; return;", element.element)
|
10
|
+
sleep (Gridium.config.highlight_duration)
|
11
|
+
Driver.execute_script("arguments[0].style.border='" + original_border + "'; return;", element.element)
|
12
|
+
Driver.execute_script("arguments[0].style.backgroundColor='" + original_background + "'; return;", element.element)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.scroll_to(element)
|
16
|
+
Log.debug("Scrolling element into view...")
|
17
|
+
Driver.execute_script("arguments[0].scrollIntoView(); return;", element.element)
|
18
|
+
sleep 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.hover_over(element)
|
22
|
+
Driver.execute_script("var evObj = document.createEvent('MouseEvents'); evObj.initMouseEvent(\"mouseover\",true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); arguments[0].dispatchEvent(evObj);", element.element)
|
23
|
+
sleep 2
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.hover_away(element)
|
27
|
+
Driver.execute_script("var evObj = document.createEvent('MouseEvents'); evObj.initMouseEvent(\"mouseout\",true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); arguments[0].dispatchEvent(evObj);", element.element)
|
28
|
+
sleep 1
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
include Gridium
|
2
|
+
|
3
|
+
class Gridium::ElementVerification
|
4
|
+
#
|
5
|
+
# @param [Element] element
|
6
|
+
# @param [Integer] timeout
|
7
|
+
# @param [Boolean] not_verification - Whether or not we are 'NOT' verifying something about the element
|
8
|
+
#
|
9
|
+
def initialize(element, timeout, fail_test=false, element_should_exist=true)
|
10
|
+
@element = element # Selement
|
11
|
+
@timeout = timeout
|
12
|
+
@should_exist = element_should_exist
|
13
|
+
@fail_test = fail_test
|
14
|
+
end
|
15
|
+
|
16
|
+
def not
|
17
|
+
ElementVerification.new(@element, @timeout, @fail_test, element_should_exist=false)
|
18
|
+
end
|
19
|
+
|
20
|
+
def text(text)
|
21
|
+
fail_message = nil
|
22
|
+
pass_message = nil
|
23
|
+
should_have_text = @should_exist
|
24
|
+
element_text = @element.text
|
25
|
+
if @element.present?
|
26
|
+
$verification_passes += 1
|
27
|
+
else
|
28
|
+
Log.error("Cannot determine element text. Element is not present.")
|
29
|
+
end
|
30
|
+
|
31
|
+
if should_have_text
|
32
|
+
fail_message = "Element should contain text (#{text}), but does not."
|
33
|
+
pass_message = "contains text (#{text})."
|
34
|
+
else
|
35
|
+
fail_message = "Element should not contain text (#{text}), but does."
|
36
|
+
pass_message = "does not contain text (#{text}). Actual text is: (#{element_text})."
|
37
|
+
end
|
38
|
+
|
39
|
+
wait = Selenium::WebDriver::Wait.new :timeout => @timeout, :interval => 1
|
40
|
+
begin
|
41
|
+
wait.until do
|
42
|
+
element_contains_text = element_text.eql?(text)
|
43
|
+
if should_have_text && element_contains_text
|
44
|
+
Log.debug("Confirming text (#{text}) is within element...")
|
45
|
+
ElementExtensions.highlight(@element) if Gridium.config.highlight_verifications
|
46
|
+
log_success(pass_message)
|
47
|
+
elsif !should_have_text && !element_contains_text
|
48
|
+
Log.debug("Confirming text (#{text}) is NOT within element...")
|
49
|
+
ElementExtensions.highlight(@element) if Gridium.config.highlight_verifications
|
50
|
+
log_success(pass_message)
|
51
|
+
else
|
52
|
+
log_issue("#{fail_message} Element's text is: (#{element_text}).")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
@element
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def visible
|
60
|
+
fail_message = nil
|
61
|
+
pass_message = nil
|
62
|
+
should_be_visible = @should_exist
|
63
|
+
|
64
|
+
if should_be_visible
|
65
|
+
fail_message = "Element should be visible."
|
66
|
+
pass_message = "Element is visible."
|
67
|
+
else
|
68
|
+
fail_message = "Element should not be visible."
|
69
|
+
pass_message = "Element is not visible."
|
70
|
+
end
|
71
|
+
|
72
|
+
wait = Selenium::WebDriver::Wait.new :timeout => @timeout, :interval => 1
|
73
|
+
begin
|
74
|
+
wait.until do
|
75
|
+
element_is_displayed = @element.displayed?
|
76
|
+
if element_is_displayed && should_be_visible
|
77
|
+
ElementExtensions.highlight(@element) if Gridium.config.highlight_verifications
|
78
|
+
log_success(pass_message)
|
79
|
+
return @element
|
80
|
+
elsif !element_is_displayed && !should_be_visible
|
81
|
+
Log.debug("Confirming element is NOT visible...")
|
82
|
+
log_success(pass_message)
|
83
|
+
else
|
84
|
+
log_issue(fail_message)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
@element
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def present
|
92
|
+
fail_message = nil
|
93
|
+
pass_message = nil
|
94
|
+
should_be_present = @should_exist
|
95
|
+
|
96
|
+
if should_be_present
|
97
|
+
fail_message = "Element should be present."
|
98
|
+
pass_message = "is present."
|
99
|
+
else
|
100
|
+
fail_message = "Element should NOT be present."
|
101
|
+
pass_message = "is not present."
|
102
|
+
end
|
103
|
+
|
104
|
+
wait = Selenium::WebDriver::Wait.new :timeout => @timeout, :interval => 1
|
105
|
+
begin
|
106
|
+
wait.until do
|
107
|
+
element_is_present = @element.present?
|
108
|
+
if element_is_present && should_be_present
|
109
|
+
ElementExtensions.highlight(@element) if Gridium.config.highlight_verifications
|
110
|
+
log_success(pass_message)
|
111
|
+
return @element
|
112
|
+
elsif !element_is_present && !should_be_present
|
113
|
+
Log.debug("Confirming element is NOT present...")
|
114
|
+
log_success(pass_message)
|
115
|
+
else
|
116
|
+
log_issue(fail_message)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
@element
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# TODO:
|
124
|
+
def value(value)
|
125
|
+
raise NotImplementedError
|
126
|
+
end
|
127
|
+
|
128
|
+
# TODO:
|
129
|
+
def selected
|
130
|
+
raise NotImplementedError
|
131
|
+
end
|
132
|
+
|
133
|
+
# TODO:
|
134
|
+
def attribute(attribute, value)
|
135
|
+
raise NotImplementedError
|
136
|
+
end
|
137
|
+
|
138
|
+
# TODO:
|
139
|
+
def css(attribute, value)
|
140
|
+
raise NotImplementedError
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def log_issue(message)
|
147
|
+
if @fail_test
|
148
|
+
Log.error("#{message} ['#{@element.name}' (By:(#{@element.by} => '#{@element.locator}'))].")
|
149
|
+
$fail_test_instantly = true
|
150
|
+
Kernel.fail(message)
|
151
|
+
else
|
152
|
+
Log.error("#{message} ['#{@element.name}' (By:(#{@element.by} => '#{@element.locator}'))].")
|
153
|
+
$fail_test_at_end = true
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def log_success(pass_message)
|
158
|
+
$verification_passes += 1
|
159
|
+
Log.debug("Verified: '#{@element.name}' (By:(#{@element.by} => '#{@element.locator}')) #{pass_message}")
|
160
|
+
end
|
161
|
+
end
|
data/lib/gridium.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "gridium/version"
|
2
|
+
require "log"
|
3
|
+
require "spec_data"
|
4
|
+
require "driver"
|
5
|
+
require "driver_extensions"
|
6
|
+
require "element"
|
7
|
+
require "element_extensions"
|
8
|
+
require "element_verification"
|
9
|
+
require "page"
|
10
|
+
|
11
|
+
module Gridium
|
12
|
+
class << self
|
13
|
+
attr_accessor :config
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configure
|
17
|
+
self.config ||= Config.new
|
18
|
+
yield config
|
19
|
+
end
|
20
|
+
|
21
|
+
class Config
|
22
|
+
attr_accessor :report_dir, :target_environment, :browser, :url, :page_load_timeout, :element_timeout, :visible_elements_only, :log_level
|
23
|
+
attr_accessor :highlight_verifications, :highlight_duration, :screenshot_on_failure
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@report_dir = Dir.home
|
27
|
+
@target_environment = "localhost"
|
28
|
+
@browser = :firefox
|
29
|
+
@url = "about:blank"
|
30
|
+
@page_load_timeout = 15
|
31
|
+
@element_timeout = 15 #This needs to be changed to only look for an element after a page is done loading
|
32
|
+
@visible_elements_only = true
|
33
|
+
@log_level = :fatal
|
34
|
+
@highlight_verifications = false
|
35
|
+
@highlight_duration = 0.100
|
36
|
+
@screenshot_on_failure = false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/log.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# Logger class wraps around ruby 'logger' gem to provide diagnostic and workflow information
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
# Add in multiple device logging directly into Logger class
|
6
|
+
class Logger
|
7
|
+
# Creates or opens a secondary log file.
|
8
|
+
def attach(name)
|
9
|
+
@logdev.attach(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Closes a secondary log file.
|
13
|
+
def detach(name)
|
14
|
+
@logdev.detach(name)
|
15
|
+
end
|
16
|
+
|
17
|
+
class LogDevice # :nodoc:
|
18
|
+
attr_reader :devs
|
19
|
+
|
20
|
+
def attach(log)
|
21
|
+
@devs ||= {}
|
22
|
+
@devs[log] = open_logfile(log)
|
23
|
+
end
|
24
|
+
|
25
|
+
def detach(log)
|
26
|
+
@devs ||= {}
|
27
|
+
@devs[log].close
|
28
|
+
@devs.delete(log)
|
29
|
+
end
|
30
|
+
|
31
|
+
alias_method :old_write, :write
|
32
|
+
|
33
|
+
def write(message)
|
34
|
+
old_write(message)
|
35
|
+
|
36
|
+
@devs ||= {}
|
37
|
+
@devs.each do |log, dev|
|
38
|
+
dev.write(message)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end # class logger
|
43
|
+
|
44
|
+
|
45
|
+
# Singleton Logger class
|
46
|
+
module Gridium
|
47
|
+
class Log
|
48
|
+
# make this class static
|
49
|
+
class << self
|
50
|
+
|
51
|
+
#
|
52
|
+
# more generic than INFO, useful for debugging issues
|
53
|
+
# DEBUG = 0
|
54
|
+
# generic, useful information about system operation
|
55
|
+
# INFO = 1
|
56
|
+
# a warning
|
57
|
+
# WARN = 2
|
58
|
+
# a handleable error condition
|
59
|
+
# ERROR = 3
|
60
|
+
# an unhandleable error that results in a program crash
|
61
|
+
# FATAL = 4
|
62
|
+
# an unknown message that should always be logged
|
63
|
+
# UNKNOWN = 5
|
64
|
+
|
65
|
+
def debug(msg)
|
66
|
+
log.debug(msg)
|
67
|
+
end
|
68
|
+
|
69
|
+
def info(msg)
|
70
|
+
log.info(msg)
|
71
|
+
end
|
72
|
+
|
73
|
+
def warn(msg)
|
74
|
+
log.warn(msg)
|
75
|
+
Driver.save_screenshot('warning') if Gridium.config.screenshot_on_failure
|
76
|
+
$execution_warnings << msg
|
77
|
+
end
|
78
|
+
|
79
|
+
def error(msg)
|
80
|
+
log.error(msg)
|
81
|
+
Driver.save_screenshot('error') if Gridium.config.screenshot_on_failure
|
82
|
+
$verification_errors << msg
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_device device
|
86
|
+
@@devices ||= []
|
87
|
+
log.attach(device)
|
88
|
+
@@devices << device
|
89
|
+
end
|
90
|
+
|
91
|
+
def close
|
92
|
+
@@devices.each { |dev| @@logger.detach(dev) }
|
93
|
+
@@devices.clear
|
94
|
+
log.close if log
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def log
|
101
|
+
@@logger ||= initialize_logger
|
102
|
+
end
|
103
|
+
|
104
|
+
def initialize_logger
|
105
|
+
# log to STDOUT and file
|
106
|
+
logger ||= Logger.new(STDOUT)
|
107
|
+
|
108
|
+
# messages that have the set level or higher will be logged
|
109
|
+
case Gridium.config.log_level
|
110
|
+
when :debug then
|
111
|
+
level = Logger::DEBUG
|
112
|
+
when :info then
|
113
|
+
level = Logger::INFO
|
114
|
+
when :warn then
|
115
|
+
level = Logger::WARN
|
116
|
+
when :error then
|
117
|
+
level = Logger::ERROR
|
118
|
+
when :fatal then
|
119
|
+
level = Logger::FATAL
|
120
|
+
end
|
121
|
+
|
122
|
+
logger.level = level
|
123
|
+
|
124
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
125
|
+
base_msg = "[#{datetime.strftime('%Y-%m-%d %H:%M:%S')}][#{severity}]"
|
126
|
+
sev = severity.to_s
|
127
|
+
if sev.eql?("DEBUG")
|
128
|
+
"#{base_msg} #{msg}\n"
|
129
|
+
elsif sev.eql?("INFO")
|
130
|
+
"#{base_msg} > #{msg}\n"
|
131
|
+
elsif sev.eql?("WARN")
|
132
|
+
"#{base_msg} X #{msg}\n"
|
133
|
+
else
|
134
|
+
"#{base_msg} X #{msg}\n"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
logger
|
139
|
+
end # initialize_logger
|
140
|
+
end # class << self
|
141
|
+
end # Log class
|
142
|
+
end
|
data/lib/page.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
module Gridium
|
2
|
+
class Page
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.switch_to_frame(frame)
|
9
|
+
Driver.driver.switch_to.frame(frame)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.switch_to_default
|
13
|
+
Driver.driver.switch_to.default_content
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.assert_selector(by, locator)
|
17
|
+
asserted_element = Element.new("Asserted Element", by, locator)
|
18
|
+
unless asserted_element.enabled?
|
19
|
+
fail("Could not find element on page with locator #{locator} using #{by}")
|
20
|
+
else
|
21
|
+
Log.info("Asserted Element present with locator #{locator} using #{by}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.has_css?(css, options={})
|
26
|
+
begin
|
27
|
+
Driver.driver.find_element(:css, css).enabled?
|
28
|
+
rescue Exception => e
|
29
|
+
return false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.has_xpath?(xpath, options={})
|
34
|
+
begin
|
35
|
+
Driver.driver.find_element(:xpath, xpath).enabled?
|
36
|
+
rescue Exception => e
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.has_link?(linktext)
|
43
|
+
begin
|
44
|
+
Driver.driver.find_element(:link_text, linktext).enabled?
|
45
|
+
rescue Exception => e
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.has_text?(text)
|
51
|
+
unless Driver.html.include? text
|
52
|
+
Log.warn("Could not find expected text: #{text} on page.")
|
53
|
+
return false
|
54
|
+
else
|
55
|
+
return true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.scroll_to_bottom
|
60
|
+
Driver.execute_script('window.scrollTo(0,100000)')
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.scroll_to_top
|
64
|
+
#TODO Verify this
|
65
|
+
Driver.execute_script('window.scrollTo(100000,0)')
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.execute_script(script)
|
69
|
+
Driver.execute_script(script)
|
70
|
+
end
|
71
|
+
|
72
|
+
def all(by, locator)
|
73
|
+
Driver.driver.find_elements(by, locator)
|
74
|
+
end
|
75
|
+
|
76
|
+
def find(by, locator)
|
77
|
+
Element.new("Page Find Element", by, locator)
|
78
|
+
end
|
79
|
+
|
80
|
+
def first(by, locator)
|
81
|
+
Driver.driver.find_elements(by, locator).first
|
82
|
+
end
|
83
|
+
|
84
|
+
def click_link(linktext)
|
85
|
+
link = Driver.driver.find_element(:link, linktext)
|
86
|
+
link.click
|
87
|
+
end
|
88
|
+
|
89
|
+
def click_button(button_name)
|
90
|
+
#The button maybe a link that looks like a button - we want to handle both
|
91
|
+
button = Element.new("A #{button_name} button", :xpath, "//button[contains(text(), '#{button_name}')]")
|
92
|
+
begin
|
93
|
+
button.click
|
94
|
+
rescue Exception => e
|
95
|
+
Log.debug("Button not found - Attempting Link - speed up test by using click_link method if this works...")
|
96
|
+
link = Element.new("A #{button_name} link", :xpath, "//a[contains(text(), '#{button_name}')]")
|
97
|
+
link.click
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def check(id) #checks a checkbox
|
102
|
+
Driver.driver.find_element(:id, id).click
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
data/lib/spec_data.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
class Spec_data
|
2
|
+
def self.load_suite_state
|
3
|
+
$screenshots_message = Array.new
|
4
|
+
$screenshots_captured = Array.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.load_spec_state
|
8
|
+
$execution_warnings = Array.new
|
9
|
+
$verification_errors = Array.new
|
10
|
+
$verification_passes = 0
|
11
|
+
$fail_test_instantly = false
|
12
|
+
$fail_test_at_end = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.clear_spec_state
|
16
|
+
$execution_warnings.clear
|
17
|
+
$verification_errors.clear
|
18
|
+
$verification_passes = 0
|
19
|
+
$fail_test_instantly = false
|
20
|
+
$fail_test_at_end = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.reset_captured_screenshots
|
24
|
+
$screenshots_message.clear
|
25
|
+
$screenshots_captured.clear
|
26
|
+
$screenshots_data = {}
|
27
|
+
$fail_screenshot = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.determine_spec_result
|
31
|
+
if $execution_warnings.empty?
|
32
|
+
Log.info("No warnings detected during test run.")
|
33
|
+
else
|
34
|
+
Log.info("Warnings detected during test run: (#{$execution_warnings.length} total).")
|
35
|
+
msg = "Warning detected during test execution:"
|
36
|
+
$execution_warnings.each { |error_message| msg << "\n\t" + error_message }
|
37
|
+
end
|
38
|
+
|
39
|
+
if $verification_errors.empty?
|
40
|
+
Log.info("No errors detected during test run.")
|
41
|
+
else
|
42
|
+
Log.info("Errors detected during test run: (#{$verification_errors.length} total).")
|
43
|
+
msg = "TEST FAILURE: Errors detected during test execution:"
|
44
|
+
$verification_errors.each { |error_message| msg << "\n\t" + error_message }
|
45
|
+
end
|
46
|
+
|
47
|
+
if $fail_test_instantly
|
48
|
+
Log.info("TEST FAILED - CRITICAL ERROR DETECTED")
|
49
|
+
Kernel.fail("TEST FAILED - CRITICAL ERROR DETECTED\n")
|
50
|
+
elsif $fail_test_at_end
|
51
|
+
Log.info("TEST FAILED - VERIFICATION ERRORS DETECTED")
|
52
|
+
Kernel.fail("TEST FAILED - VERIFICATION ERRORS DETECTED\n")
|
53
|
+
else
|
54
|
+
Log.info("TEST PASSED\n")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.add_spec_stats_to_suite_stats
|
59
|
+
$verifications_total += $verification_passes
|
60
|
+
$warnings_total += $execution_warnings.length
|
61
|
+
$errors_total += $verification_errors.length
|
62
|
+
end
|
63
|
+
end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gridium
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Seth Urban
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: selenium-webdriver
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.45.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.45.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: oily_png
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Capybara is a great tool to start making automated tests for your web
|
84
|
+
application. However, many automation engineers find it difficult to use effectively
|
85
|
+
for UI tests. Capybara works better when webkit, and not so well with mozilla. This
|
86
|
+
makes Selenium integration difficult, this gem remedies that.
|
87
|
+
email:
|
88
|
+
- sethuster@gmail.com
|
89
|
+
executables: []
|
90
|
+
extensions: []
|
91
|
+
extra_rdoc_files: []
|
92
|
+
files:
|
93
|
+
- ".gitignore"
|
94
|
+
- ".rspec"
|
95
|
+
- ".travis.yml"
|
96
|
+
- CODE_OF_CONDUCT.md
|
97
|
+
- Gemfile
|
98
|
+
- LICENSE.txt
|
99
|
+
- README.md
|
100
|
+
- Rakefile
|
101
|
+
- bin/console
|
102
|
+
- bin/setup
|
103
|
+
- gridium.gemspec
|
104
|
+
- lib/driver.rb
|
105
|
+
- lib/driver_extensions.rb
|
106
|
+
- lib/element.rb
|
107
|
+
- lib/element_extensions.rb
|
108
|
+
- lib/element_verification.rb
|
109
|
+
- lib/gridium.rb
|
110
|
+
- lib/gridium/version.rb
|
111
|
+
- lib/log.rb
|
112
|
+
- lib/page.rb
|
113
|
+
- lib/spec_data.rb
|
114
|
+
homepage: http://github.com/sethuster/gridium
|
115
|
+
licenses:
|
116
|
+
- MIT
|
117
|
+
metadata:
|
118
|
+
allowed_push_host: https://rubygems.org
|
119
|
+
post_install_message:
|
120
|
+
rdoc_options: []
|
121
|
+
require_paths:
|
122
|
+
- lib
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
requirements: []
|
134
|
+
rubyforge_project:
|
135
|
+
rubygems_version: 2.2.2
|
136
|
+
signing_key:
|
137
|
+
specification_version: 4
|
138
|
+
summary: This Gem is used to make building Selenium Tests without Capybara Easier.
|
139
|
+
test_files: []
|