gridium 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.idea/
11
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ before_install: gem install bundler -v 1.10.3
@@ -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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gridium.gemspec
4
+ gemspec
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
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
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
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
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
@@ -0,0 +1,11 @@
1
+ class DriverExtensions
2
+ def self.open_new_window(url)
3
+ Driver.execute_script_driver("window.open('#{url}')")
4
+ sleep 1
5
+ end
6
+
7
+ def self.close_window
8
+ Driver.execute_script_driver("window.close()")
9
+ sleep 1
10
+ end
11
+ 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
@@ -0,0 +1,3 @@
1
+ module Gridium
2
+ VERSION = "0.1.0"
3
+ 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: []