rutl 0.3.0 → 0.4.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 +4 -4
- data/.circleci/config.yml +2 -2
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +10 -4
- data/.travis.yml +1 -0
- data/README.md +1 -2
- data/lib/rspec/rutl_matchers.rb +4 -1
- data/lib/rutl.rb +3 -3
- data/lib/rutl/browser.rb +56 -51
- data/lib/rutl/camera.rb +74 -0
- data/lib/rutl/interface/base_interface.rb +75 -62
- data/lib/rutl/interface/chrome_interface.rb +27 -25
- data/lib/rutl/interface/firefox_interface.rb +21 -19
- data/lib/rutl/interface/null_interface.rb +26 -24
- data/lib/rutl/null_driver/null_driver.rb +42 -0
- data/lib/rutl/null_driver/null_element.rb +56 -0
- data/lib/rutl/page.rb +119 -0
- data/lib/rutl/version.rb +1 -1
- data/lib/{rutl/utilities.rb → utilities.rb} +2 -6
- metadata +7 -16
- data/lib/rutl/base_page.rb +0 -111
- data/lib/rutl/driver/null_driver.rb +0 -38
- data/lib/rutl/driver/null_driver_page_element.rb +0 -51
- data/lib/rutl/interface/elements.rb +0 -5
- data/lib/rutl/interface/elements/base_element.rb +0 -24
- data/lib/rutl/interface/elements/button.rb +0 -10
- data/lib/rutl/interface/elements/checkbox.rb +0 -8
- data/lib/rutl/interface/elements/click_to_change_state_mixin.rb +0 -18
- data/lib/rutl/interface/elements/element_context.rb +0 -29
- data/lib/rutl/interface/elements/link.rb +0 -11
- data/lib/rutl/interface/elements/string_reader_writer_mixin.rb +0 -66
- data/lib/rutl/interface/elements/text.rb +0 -10
- data/lib/rutl/screencam.rb +0 -71
@@ -1,32 +1,34 @@
|
|
1
1
|
require 'selenium-webdriver'
|
2
2
|
require 'rutl/interface/base_interface'
|
3
3
|
|
4
|
-
|
5
|
-
#
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
4
|
+
module RUTL
|
5
|
+
#
|
6
|
+
# Small interface for Chrome browser.
|
7
|
+
#
|
8
|
+
class ChromeInterface < BaseInterface
|
9
|
+
# rubocop:disable Metrics/MethodLength
|
10
|
+
def initialize
|
11
|
+
@logged_in = true
|
12
|
+
options = Selenium::WebDriver::Chrome::Options.new
|
13
|
+
options.add_argument('--ignore-certificate-errors')
|
14
|
+
options.add_argument('--disable-popup-blocking')
|
15
|
+
options.add_argument('--disable-translate')
|
16
|
+
# Run headless on TravisCI
|
17
|
+
if ENV['TRAVIS'] == 'true'
|
18
|
+
options.add_argument('--disable-gpu')
|
19
|
+
options.add_argument('--headless ')
|
20
|
+
options.add_argument('--no-sandbox')
|
21
|
+
end
|
22
|
+
@driver = Selenium::WebDriver.for :chrome, options: options
|
23
|
+
super
|
20
24
|
end
|
21
|
-
|
22
|
-
super
|
23
|
-
end
|
24
|
-
# rubocop:enable Metrics/MethodLength
|
25
|
+
# rubocop:enable Metrics/MethodLength
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
def current_page
|
28
|
+
url = @driver.current_url
|
29
|
+
page = find_page(url)
|
30
|
+
raise "PAGE NOT FOUND: #{url}, PAGES: #{@pages}" unless page
|
31
|
+
page
|
32
|
+
end
|
31
33
|
end
|
32
34
|
end
|
@@ -1,25 +1,27 @@
|
|
1
1
|
require 'selenium-webdriver'
|
2
2
|
require 'rutl/interface/base_interface'
|
3
3
|
|
4
|
-
|
5
|
-
#
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
4
|
+
module RUTL
|
5
|
+
#
|
6
|
+
# Small interface for Chrome browser.
|
7
|
+
#
|
8
|
+
class FirefoxInterface < BaseInterface
|
9
|
+
def initialize
|
10
|
+
@logged_in = true
|
11
|
+
options = Selenium::WebDriver::Firefox::Options.new
|
12
|
+
options.add_argument('--ignore-certificate-errors')
|
13
|
+
options.add_argument('--disable-popup-blocking')
|
14
|
+
options.add_argument('--disable-translate')
|
15
|
+
options.add_argument('--headless') if ENV['TRAVIS'] == 'true'
|
16
|
+
@driver = Selenium::WebDriver.for :firefox, options: options
|
17
|
+
super
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
def current_page
|
21
|
+
url = @driver.current_url
|
22
|
+
page = find_page(url)
|
23
|
+
raise "PAGE NOT FOUND: #{url}, PAGES: #{@pages}" unless page
|
24
|
+
page
|
25
|
+
end
|
24
26
|
end
|
25
27
|
end
|
@@ -1,31 +1,33 @@
|
|
1
1
|
require 'rutl/interface/base_interface'
|
2
2
|
|
3
|
-
|
4
|
-
#
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
module RUTL
|
4
|
+
#
|
5
|
+
# Interface-level code for fake browser.
|
6
|
+
#
|
7
|
+
class NullInterface < BaseInterface
|
8
|
+
def initialize
|
9
|
+
context = RUTL::Element::ElementContext.new(destinations: nil,
|
10
|
+
interface: self,
|
11
|
+
selectors: [])
|
12
|
+
@driver = NullDriver.new(context)
|
13
|
+
super
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
# The null driver needs to talk to the null interface.
|
17
|
+
# Other driver/interface relations are not like this.
|
18
|
+
attr_writer :current_page
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
def current_page
|
21
|
+
# Default to @pages.first if not set?
|
22
|
+
# A browser can always check its current URL but the null driver can't.
|
23
|
+
@current_page ||= @pages.first
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
def wait_for_transition(destinations)
|
27
|
+
# TODO: Setting @current page didn't do it beacause that set
|
28
|
+
# context.interface.current_page and we wanted this in the browser.
|
29
|
+
@current_page = destinations.first.new(self)
|
30
|
+
$browser.current_page = @current_page
|
31
|
+
end
|
30
32
|
end
|
31
33
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rutl/null_driver/null_element'
|
2
|
+
|
3
|
+
module RUTL
|
4
|
+
#
|
5
|
+
# This is at a peer level to the webdrivers but it's for a fake brwoser.
|
6
|
+
#
|
7
|
+
class NullDriver
|
8
|
+
attr_accessor :context
|
9
|
+
|
10
|
+
def initialize(context)
|
11
|
+
raise 'no context' unless context.is_a?(RUTL::Element::ElementContext)
|
12
|
+
@context = context
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return a new one of these fake things so that it can be clicked
|
16
|
+
# ar written to or whatever.
|
17
|
+
def find_element(type, location)
|
18
|
+
context = RUTL::Element::ElementContext.new(interface: @context.interface)
|
19
|
+
RUTL::Element::NullElement.new(context, type, location)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Cheap way to handle browser.navigate.to(url)
|
23
|
+
# TODO: Until I care about the url and then I should ????
|
24
|
+
def navigate
|
25
|
+
context = RUTL::Element::ElementContext.new(interface: @context.interface)
|
26
|
+
NullDriver.new(context)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Cheap second part to naviate.to(url) calls to look like real drivers.
|
30
|
+
def to(url)
|
31
|
+
result = @context.interface.find_page(url)
|
32
|
+
@context.interface.current_page = result
|
33
|
+
result.url
|
34
|
+
end
|
35
|
+
|
36
|
+
# Clean out the @@variables from NullElement.
|
37
|
+
# Other than this, this is a placeholder to match real drivers.
|
38
|
+
def quit
|
39
|
+
RUTL::Element::NullElement.clear_variables
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rutl/element/element_context'
|
2
|
+
|
3
|
+
module RUTL
|
4
|
+
module Element
|
5
|
+
#
|
6
|
+
# This fakes all page elements when used with the null driver.
|
7
|
+
# It's a dirty way to avoid modeling all of what a driver talks to.
|
8
|
+
#
|
9
|
+
class NullElement
|
10
|
+
attr_accessor :context
|
11
|
+
|
12
|
+
def self.clear_variables
|
13
|
+
@@variables = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(context, _type, location)
|
17
|
+
@@variables ||= {}
|
18
|
+
@context = context
|
19
|
+
@location = location
|
20
|
+
end
|
21
|
+
|
22
|
+
# @@string is a class variable because this framework creates new
|
23
|
+
# instances of each element every time it accesses them. This is good
|
24
|
+
# behavior by default because pages could change underneath us.
|
25
|
+
# For text fields in the null browser, though, we want to preserve the
|
26
|
+
# values across calls, letting us write and then read.
|
27
|
+
def send_keys(string)
|
28
|
+
init = @@variables[@location] || ''
|
29
|
+
@@variables[@location] = init + string
|
30
|
+
end
|
31
|
+
|
32
|
+
def attribute(attr)
|
33
|
+
case attr.to_sym
|
34
|
+
when :value
|
35
|
+
@@variables[@location] || ''
|
36
|
+
else
|
37
|
+
raise ArgumentError, "Attribute unknown: #{attr}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def clear
|
42
|
+
@@variables[@location] = ''
|
43
|
+
end
|
44
|
+
|
45
|
+
def this_css
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
# Placeholder - NOP
|
50
|
+
# Called by RUTL::Element::ClickToChangeStateMixin like
|
51
|
+
# Selenium driver.click
|
52
|
+
def click
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/rutl/page.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'rutl/element'
|
2
|
+
require 'rutl/null_driver/null_driver'
|
3
|
+
|
4
|
+
module RUTL
|
5
|
+
#
|
6
|
+
# Base page class. It's used to call the magical method_messing
|
7
|
+
# stuff to parse the page object files into actual objects.
|
8
|
+
#
|
9
|
+
class Page
|
10
|
+
# BUGBUG: Kludgy. What do I really want to do here?
|
11
|
+
# Make it easy to define a page's default url and
|
12
|
+
# also matchers for page urls for pages with variable urls?
|
13
|
+
# rubocop:disable Style/TrivialAccessors
|
14
|
+
def self.url
|
15
|
+
@url
|
16
|
+
end
|
17
|
+
|
18
|
+
def url
|
19
|
+
self.class.url
|
20
|
+
end
|
21
|
+
# rubocop:enable Style/TrivialAccessors
|
22
|
+
|
23
|
+
# Intentially use a class variable to hald pages. Once they're all loaded
|
24
|
+
# they are all loaded for everyone.
|
25
|
+
# rubocop:disable Style/ClassVars
|
26
|
+
@@loaded_pages = []
|
27
|
+
# rubocop:enable Style/ClassVars
|
28
|
+
|
29
|
+
def initialize(interface)
|
30
|
+
@interface = interface
|
31
|
+
# Dirty trick because we're loading all of page classes from files and
|
32
|
+
# then initializing them, calling their layout methods to do magic.
|
33
|
+
# The Page class knows what pages are loaded.
|
34
|
+
return if @@loaded_pages.include?(self.class)
|
35
|
+
layout
|
36
|
+
@@loaded_pages << self.class
|
37
|
+
end
|
38
|
+
|
39
|
+
def go_to_here
|
40
|
+
# Ovveride this in base page to have something more
|
41
|
+
# complicated than this.
|
42
|
+
@interface.driver.navigate.to(url)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Written by Browser and only used internally.
|
46
|
+
attr_writer :interface
|
47
|
+
|
48
|
+
def loaded?
|
49
|
+
url == @interface.driver.current_url
|
50
|
+
end
|
51
|
+
|
52
|
+
# Dynamically add a method, :<name> (or :<name>= if setter)
|
53
|
+
# to the current class where that method creates an instance
|
54
|
+
# of klass.
|
55
|
+
# context is a RUTL::Element::ElementContext
|
56
|
+
#
|
57
|
+
# As it is, this seems silly to break into pieces for Rubocop.
|
58
|
+
# rubocop:disable Metrics/MethodLength
|
59
|
+
def add_method(context:, klass:, name:, setter: false)
|
60
|
+
name = "#{name}_#{klass.downcase}"
|
61
|
+
constant = Module.const_get("RUTL::Element::#{klass.capitalize}")
|
62
|
+
self.class.class_exec do
|
63
|
+
if setter
|
64
|
+
define_method("#{name}=") do |value|
|
65
|
+
constant.new(context, value)
|
66
|
+
end
|
67
|
+
else
|
68
|
+
define_method(name) do
|
69
|
+
constant.new(context)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
private :add_method
|
75
|
+
# rubocop:enable Metrics/MethodLength
|
76
|
+
|
77
|
+
# This creates a new element instance whenever it's called.
|
78
|
+
# Because of that we can't keep state in any element objects.
|
79
|
+
# That seems like a good thing, actually.
|
80
|
+
# Called by layout method on pages.
|
81
|
+
#
|
82
|
+
# Hard to make shorter.
|
83
|
+
# rubocop:disable Metrics/MethodLength
|
84
|
+
def method_missing(element, *args, &_block)
|
85
|
+
name, selectors, rest = args
|
86
|
+
context = RUTL::Element::ElementContext.new(destinations: rest,
|
87
|
+
interface: @interface,
|
88
|
+
selectors: selectors)
|
89
|
+
case element
|
90
|
+
when /button/, /checkbox/, /element/, /link/
|
91
|
+
add_method(name: name, context: context, klass: element)
|
92
|
+
when /text/
|
93
|
+
add_method(name: name, context: context, klass: element)
|
94
|
+
add_method(name: name, context: context, klass: element, setter: true)
|
95
|
+
else
|
96
|
+
# TODO: replace with a super call. This is useful for debugging for now.
|
97
|
+
raise "#{element} NOT FOUND WITH ARGS #{args}!!!"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
# rubocop:enable Metrics/MethodLength
|
101
|
+
|
102
|
+
def respond_to_missing?(*args)
|
103
|
+
# Is this right at all???
|
104
|
+
case args[0].to_s
|
105
|
+
when /button/, /checkbox/, /element/, /link/, /text/,
|
106
|
+
'driver', 'url', 'children', 'loaded?'
|
107
|
+
true
|
108
|
+
when 'ok_link'
|
109
|
+
raise 'OK LINK WAY DOWN HERE IN BASE PAGE!!!'
|
110
|
+
else
|
111
|
+
# I think it's good to raise but change the message.
|
112
|
+
raise 'TODO: BETTER ERROR MESSAGE, PLEASE. I AM SHOUTING!!!\n' \
|
113
|
+
'Drew, you hit this most often when checking current page ' \
|
114
|
+
"rather than current page class:\n\n #{args}"
|
115
|
+
# I think I want to raise instead of returningn false.
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/lib/rutl/version.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
module Utilities
|
5
5
|
require 'timeout'
|
6
6
|
|
7
|
-
POLL_SLEEP_TIME = 0.
|
7
|
+
POLL_SLEEP_TIME = 0.01
|
8
8
|
DEFAULT_TIMEOUT = 5
|
9
9
|
|
10
10
|
# The lambda passed to await should return false if thing not found
|
@@ -33,13 +33,9 @@ module Utilities
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def page?(checkme)
|
36
|
-
checkme.ancestors.include?(
|
36
|
+
checkme.ancestors.include?(RUTL::Page)
|
37
37
|
rescue NoMethodError
|
38
38
|
# This isn't a even a class. It's no page!
|
39
39
|
false
|
40
40
|
end
|
41
|
-
|
42
|
-
def raise_if_not_page(page)
|
43
|
-
raise "NOT A PAGE: #{page}. Ancestors: #{page.ancestors}" unless page?(page)
|
44
|
-
end
|
45
41
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rutl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Drew Cooper
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-06-
|
11
|
+
date: 2018-06-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: selenium-webdriver
|
@@ -187,26 +187,17 @@ files:
|
|
187
187
|
- lib/rspec/default_rspec_to_browser.rb
|
188
188
|
- lib/rspec/rutl_matchers.rb
|
189
189
|
- lib/rutl.rb
|
190
|
-
- lib/rutl/base_page.rb
|
191
190
|
- lib/rutl/browser.rb
|
192
|
-
- lib/rutl/
|
193
|
-
- lib/rutl/driver/null_driver_page_element.rb
|
191
|
+
- lib/rutl/camera.rb
|
194
192
|
- lib/rutl/interface/base_interface.rb
|
195
193
|
- lib/rutl/interface/chrome_interface.rb
|
196
|
-
- lib/rutl/interface/elements.rb
|
197
|
-
- lib/rutl/interface/elements/base_element.rb
|
198
|
-
- lib/rutl/interface/elements/button.rb
|
199
|
-
- lib/rutl/interface/elements/checkbox.rb
|
200
|
-
- lib/rutl/interface/elements/click_to_change_state_mixin.rb
|
201
|
-
- lib/rutl/interface/elements/element_context.rb
|
202
|
-
- lib/rutl/interface/elements/link.rb
|
203
|
-
- lib/rutl/interface/elements/string_reader_writer_mixin.rb
|
204
|
-
- lib/rutl/interface/elements/text.rb
|
205
194
|
- lib/rutl/interface/firefox_interface.rb
|
206
195
|
- lib/rutl/interface/null_interface.rb
|
207
|
-
- lib/rutl/
|
208
|
-
- lib/rutl/
|
196
|
+
- lib/rutl/null_driver/null_driver.rb
|
197
|
+
- lib/rutl/null_driver/null_element.rb
|
198
|
+
- lib/rutl/page.rb
|
209
199
|
- lib/rutl/version.rb
|
200
|
+
- lib/utilities.rb
|
210
201
|
- rutl.gemspec
|
211
202
|
homepage: https://github.com/drewcoo/rutl
|
212
203
|
licenses:
|