rutl 0.1.4 → 0.2.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/.rubocop.yml +28 -0
- data/.rubocop_todo.yml +2 -48
- data/README.md +136 -11
- data/lib/rspec/default_rspec_to_browser.rb +22 -0
- data/lib/rspec/rutl_matchers.rb +8 -0
- data/lib/rutl/base_page.rb +16 -0
- data/lib/rutl/browser.rb +6 -3
- data/lib/rutl/driver/null_driver_page_element.rb +1 -1
- data/lib/rutl/interface/base_interface.rb +2 -1
- data/lib/rutl/interface/chrome_interface.rb +2 -0
- data/lib/rutl/version.rb +1 -1
- data/lib/rutl.rb +4 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 100c19d4e865873162c8b2d6fef35433cfad509517fe33947f3620b8ec3e1501
|
4
|
+
data.tar.gz: c89348d198df34fb5574dff6ca684df5d7b9509726a6aa6524990ebe86549b8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8bc3dcf1eb98a2f2359fd631b31550f722704b975a13eeab7bd348fe4f691f0433e12ccd446967aaf8bc77138252c005d025f6c59bafcf3a3ae66dbabf12277f
|
7
|
+
data.tar.gz: 95c3525a9dfdf0981584e6d7040c1a781229f824d7581b935c0fa8a56e9704151140a5be9aa101d0c6f720021ce0ce476e340ad158cde17495d8cd42cfd649e8
|
data/.rubocop.yml
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# The exclusions here and in source code are intentional decisions to not
|
2
|
+
# follow Rubocop's advice. Anything that should be fiex but isn't is in
|
3
|
+
# .rubocop_todo.yml
|
1
4
|
inherit_from: .rubocop_todo.yml
|
2
5
|
|
3
6
|
require: rubocop-rspec
|
@@ -14,3 +17,28 @@ AllCops:
|
|
14
17
|
DisplayCopNames: true
|
15
18
|
DisplayStyleGuide: true
|
16
19
|
ExtraDetails: true
|
20
|
+
|
21
|
+
# Metrics/BlockLength, Metrics/MethodLength
|
22
|
+
# don't apply to the spec files. They naturally have long swaths of code.
|
23
|
+
# And the gemspec is special.
|
24
|
+
Metrics/BlockLength:
|
25
|
+
Exclude:
|
26
|
+
- 'rutl.gemspec'
|
27
|
+
- 'spec/*_spec.rb'
|
28
|
+
Metrics/MethodLength:
|
29
|
+
Exclude:
|
30
|
+
- 'spec/*_spec.rb'
|
31
|
+
|
32
|
+
# TODO: Change this setting? Ideally, I should force braces on the *Page files.
|
33
|
+
# But should think about this first.
|
34
|
+
Style/BracesAroundHashParameters:
|
35
|
+
Exclude:
|
36
|
+
- 'spec/pages/**/*'
|
37
|
+
|
38
|
+
# NullDriverPageElement plays dirty tricks with @@variables to simulate
|
39
|
+
# longer-lived data sources.
|
40
|
+
# And BasePage does @@loaded_pages.
|
41
|
+
Style/ClassVars:
|
42
|
+
Exclude:
|
43
|
+
- 'lib/rutl/driver/null_driver_page_element.rb'
|
44
|
+
- 'lib/rutl/base_page.rb'
|
data/.rubocop_todo.yml
CHANGED
@@ -1,36 +1,11 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2018-06-
|
3
|
+
# on 2018-06-04 00:16:13 -0700 using RuboCop version 0.56.0.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
8
8
|
|
9
|
-
# Offense count: 3
|
10
|
-
# Configuration parameters: CountComments, ExcludedMethods.
|
11
|
-
Metrics/BlockLength:
|
12
|
-
Max: 74
|
13
|
-
|
14
|
-
# Offense count: 3
|
15
|
-
# Configuration parameters: CountComments.
|
16
|
-
Metrics/MethodLength:
|
17
|
-
Max: 13
|
18
|
-
|
19
|
-
# Offense count: 4
|
20
|
-
# Cop supports --auto-correct.
|
21
|
-
# Configuration parameters: EnforcedStyle.
|
22
|
-
# SupportedStyles: braces, no_braces, context_dependent
|
23
|
-
Style/BracesAroundHashParameters:
|
24
|
-
Exclude:
|
25
|
-
- 'spec/pages/internet_login_page.rb'
|
26
|
-
- 'spec/pages/page1.rb'
|
27
|
-
|
28
|
-
# Offense count: 3
|
29
|
-
Style/ClassVars:
|
30
|
-
Exclude:
|
31
|
-
- 'lib/rutl/base_page.rb'
|
32
|
-
- 'lib/rutl/driver/null_driver_page_element.rb'
|
33
|
-
|
34
9
|
# Offense count: 4
|
35
10
|
# Configuration parameters: AllowedVariables.
|
36
11
|
Style/GlobalVars:
|
@@ -40,30 +15,9 @@ Style/GlobalVars:
|
|
40
15
|
- 'lib/rutl/interface/elements/element_context.rb'
|
41
16
|
- 'lib/rutl/interface/null_interface.rb'
|
42
17
|
|
43
|
-
# Offense count:
|
44
|
-
# Cop supports --auto-correct.
|
45
|
-
Style/IfUnlessModifier:
|
46
|
-
Exclude:
|
47
|
-
- 'lib/rutl/interface/elements/element_context.rb'
|
48
|
-
|
49
|
-
# Offense count: 4
|
18
|
+
# Offense count: 3
|
50
19
|
Style/MethodMissingSuper:
|
51
20
|
Exclude:
|
52
21
|
- 'lib/rutl/base_page.rb'
|
53
22
|
- 'lib/rutl/browser.rb'
|
54
23
|
- 'lib/rutl/interface/base_interface.rb'
|
55
|
-
- 'spec/spec_helper.rb'
|
56
|
-
|
57
|
-
# Offense count: 1
|
58
|
-
# Cop supports --auto-correct.
|
59
|
-
# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist.
|
60
|
-
# Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
|
61
|
-
Style/TrivialAccessors:
|
62
|
-
Exclude:
|
63
|
-
- 'lib/rutl/base_page.rb'
|
64
|
-
|
65
|
-
# Offense count: 15
|
66
|
-
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
67
|
-
# URISchemes: http, https
|
68
|
-
Metrics/LineLength:
|
69
|
-
Max: 223
|
data/README.md
CHANGED
@@ -17,13 +17,12 @@ Framework goals:
|
|
17
17
|
* Secondary-ish goal: Make fake browser to test the framework faster.
|
18
18
|
* Tertiary-ish: Stop calling browser "fake" because I'm sick of that word. Null!
|
19
19
|
|
20
|
+
|
20
21
|
## Installation
|
21
22
|
|
22
23
|
Add this line to your application's Gemfile:
|
23
24
|
|
24
|
-
|
25
|
-
gem 'rutl'
|
26
|
-
```
|
25
|
+
$ gem 'rutl'
|
27
26
|
|
28
27
|
And then execute:
|
29
28
|
|
@@ -33,36 +32,159 @@ Or install it yourself as:
|
|
33
32
|
|
34
33
|
$ gem install rutl
|
35
34
|
|
35
|
+
|
36
36
|
## Usage
|
37
37
|
|
38
|
-
|
38
|
+
### Page Objects
|
39
|
+
Page objects are a common paradigm in browser testing. This framework uses the
|
40
|
+
following convention for page classes:
|
41
|
+
* must inherit from Rutl::BasePage (require rutl/base_page)
|
42
|
+
* by default, the class should follow the naming convention ending with "Page" (optional?)
|
43
|
+
* must have @url defined per page
|
44
|
+
* must have a layout method such that
|
45
|
+
* field types are defined by methods button, checkbox, link, and text (more tbd?)
|
46
|
+
* field type is followed by name as a symbol (add support for string? tbd)
|
47
|
+
* then a comma
|
48
|
+
* hash of selectors
|
49
|
+
* key is selector type as symbol (currently only :css)
|
50
|
+
* value is string path
|
51
|
+
* optional comma if there are destinations or error conditions
|
52
|
+
* optional array of destination page or error condition classes if clicking the element causes transition
|
53
|
+
* loaded? method returning boolean to determine when page is loaded
|
54
|
+
* defaults to just checking url; overide as needed
|
55
|
+
* go_to_here (better name?) method to navigate to the page if we can't just go to the url
|
56
|
+
* your own methods because it's a plain ol' Ruby class
|
57
|
+
|
58
|
+
|
59
|
+
Example:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
require 'rutl/base_page'
|
63
|
+
|
64
|
+
class MyPage < BasePage
|
65
|
+
@url = 'https://url.string.com/page.html'
|
66
|
+
|
67
|
+
def layout
|
68
|
+
text :username, { css: 'some_css_input#username' }
|
69
|
+
text :password, { css: 'css#to_password_field' }
|
70
|
+
button :log_me_in, { css: 'button#login' }, [SomeOtherPage, LoginFailurePage]
|
71
|
+
link :refresh, { css: 'link_css_to_refresh_page' }, [MyPage]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
And here's some example RSpec:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
require 'spec_helper'
|
80
|
+
|
81
|
+
RSpec.describe MyTest do
|
82
|
+
let!(:browser) do
|
83
|
+
Browser.new(type: :firefox)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'logs in' do
|
87
|
+
goto(MyPage)
|
88
|
+
username_text = 'drew'
|
89
|
+
password_text = 's3cr3T!'
|
90
|
+
log_me_in_button.click
|
91
|
+
expect(current_page).to be_page(SomeOtherPage)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
The framework loads and manages all the pages. You just have to interact with
|
97
|
+
what you can see on whatever page you're on. Let's walk through this.
|
98
|
+
* TBD: Does RUTL come with browser drivers? Browsers? What needs to be added?
|
99
|
+
* We're using let! because:
|
100
|
+
* it forces instantiation of "browser" every time
|
101
|
+
* we include DefaultRspecToBrowser, defaulting missing methods to "browser"
|
102
|
+
* thus the terse lines that follow
|
103
|
+
* We didn't pass named param rutl_pages: to Browser so we must have done one of:
|
104
|
+
* setting environment variable RUTL_PAGES
|
105
|
+
* setting RUTL::PAGES
|
106
|
+
* Browser's type: parameter currently supports :chrome, :firefox, and :null.
|
107
|
+
* The first call to the browser is goto because it wasn't on a page.
|
108
|
+
* Auto-created fields are named "#{friendly_name}_#{field_type}".
|
109
|
+
* Getting and setting text fields is as easy as calling a String.
|
110
|
+
* When we call click, the framework polls for a next state.
|
111
|
+
* We verify that the current page is an instance of the intended page.
|
112
|
+
* Also note here that we have a matcher be_page which matches a page class.
|
113
|
+
|
114
|
+
### RSpec Goodies
|
115
|
+
|
116
|
+
The tests here are in RSpec and use some conventions that may be common if your tests are also RSpec.
|
117
|
+
|
118
|
+
#### DefaultRspecToBrowser
|
119
|
+
This is a module that allows us to skip writing `browser.` in front of everything.
|
120
|
+
1. We assume that `browser` is defined.
|
121
|
+
2. On method_missing, we try to send the method to `browser`.
|
122
|
+
|
123
|
+
It lets us turn this:
|
124
|
+
```
|
125
|
+
browser.field1_text = 'foo'
|
126
|
+
browser.ok_button.click
|
127
|
+
expect(browser.current_page).to eq(NextPage)
|
128
|
+
```
|
129
|
+
into this:
|
130
|
+
```
|
131
|
+
field1_text = 'foo'
|
132
|
+
ok_button.click
|
133
|
+
expect(current_page).to eq(NextPage)
|
134
|
+
```
|
135
|
+
which means less boilerplate and it's easier to follow.
|
136
|
+
|
137
|
+
To use it:
|
138
|
+
```
|
139
|
+
require 'rutl/rspec/default_rspec_to_browser'
|
140
|
+
```
|
141
|
+
|
142
|
+
#### RSpec Matcher
|
143
|
+
|
144
|
+
Currently the only has the `be_page` matcher.
|
145
|
+
|
146
|
+
It lets us turn this:
|
147
|
+
```
|
148
|
+
expect(browser.current_page).to be_instance_of(MyPage)
|
149
|
+
```
|
150
|
+
into this:
|
151
|
+
```
|
152
|
+
expect(browser.current_page).to be_page(MyPage)
|
153
|
+
```
|
154
|
+
Both are acceptable but the second is more readable.
|
155
|
+
|
156
|
+
To use it:
|
157
|
+
```
|
158
|
+
require 'rutl/rspec/rutl_matchers'
|
159
|
+
```
|
160
|
+
|
39
161
|
|
40
162
|
## Roadmap
|
41
163
|
Coming up soon in almost no order:
|
42
|
-
* A test framework should have better tests.
|
43
|
-
* Flesh out null interface/driver. Make them do what a real browser does.
|
44
|
-
* Restructure tests to handle fake browsers and real browsers. Same structure?
|
45
|
-
* Make this work with pages files in some other location so we can use it as a gem.
|
164
|
+
* A test framework should have better tests.
|
46
165
|
* Put more info in this readme.
|
47
166
|
* Take screenshots.
|
48
167
|
* Diff screenshots. Make this smart so we don't have to be experts.
|
168
|
+
* Move bugs and would-be features to Github Issues instead of this readme and scattered through the code.
|
169
|
+
* Make the framework make it easier to spot bugs in pages. Focus on exception-handling?
|
49
170
|
* The webdriver gem should already include InternetExplorerDriver. Maybe run tests on AppVeyor.
|
50
171
|
* Other browser drivers? Look at https://github.com/fnando/browser
|
51
172
|
* Get this working with Appium:
|
52
173
|
* Make TK app to test on desktops and test it.
|
53
|
-
* Can Ruby TK create accesible apps?
|
174
|
+
* Can Ruby TK create accesible apps? Not in the simple demos.
|
54
175
|
* Make Android example app and get this to work.
|
55
176
|
* Corboba?
|
56
177
|
* Same with iPhone.
|
57
178
|
* Same Cordoba test app?
|
58
179
|
* Others?
|
59
|
-
* Spidering page object maker.
|
180
|
+
* Spidering page object maker. Or selector checker/fixer?
|
60
181
|
* Possibly pair the null browser with auto-generated pages for ______?
|
61
182
|
* Call rutl.rb properly.
|
62
183
|
* Optional install of test resources based on machine type.
|
63
184
|
* Instructions about machine installs to help people using gem.
|
64
185
|
* Pair with some kind of VM, Docker container, AMI, or something.
|
65
186
|
|
187
|
+
|
66
188
|
## Development
|
67
189
|
|
68
190
|
Set everything up:
|
@@ -75,10 +197,13 @@ Set everything up:
|
|
75
197
|
Great! You've checked out the code, installed everything and run the tests.
|
76
198
|
|
77
199
|
Rubocop. I still have to tweak what I want it to complain about.
|
78
|
-
|
200
|
+
```ruby
|
201
|
+
bundle exec rubocop
|
202
|
+
```
|
79
203
|
|
80
204
|
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).
|
81
205
|
|
206
|
+
|
82
207
|
## Contributing
|
83
208
|
|
84
209
|
Bug reports and pull requests are welcome on GitHub at https://github.com/drewcoo/rutl.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#
|
2
|
+
# Currently not in use.
|
3
|
+
# Should go in its own file, too.
|
4
|
+
#
|
5
|
+
module DefaultRspecToBrowser
|
6
|
+
# rubocop:disable Style/MethodMissingSuper
|
7
|
+
def method_missing(method, *args, &block)
|
8
|
+
if args.empty?
|
9
|
+
browser.send(method)
|
10
|
+
else
|
11
|
+
browser.send(method, *args, &block)
|
12
|
+
end
|
13
|
+
rescue ArgumentError
|
14
|
+
browser.send(method)
|
15
|
+
end
|
16
|
+
# rubocop:enable Style/MethodMissingSuper
|
17
|
+
|
18
|
+
def respond_to_missing?(method, _include_private = false)
|
19
|
+
return false if method =~ /browser/
|
20
|
+
browser.respond_to?(method)
|
21
|
+
end
|
22
|
+
end
|
data/lib/rutl/base_page.rb
CHANGED
@@ -9,6 +9,7 @@ class BasePage
|
|
9
9
|
# BUGBUG: Kludgy. What do I really want to do here?
|
10
10
|
# Make it easy to define a page's default url and
|
11
11
|
# also matchers for page urls for pages with variable urls?
|
12
|
+
# rubocop:disable Style/TrivialAccessors
|
12
13
|
def self.url
|
13
14
|
@url
|
14
15
|
end
|
@@ -16,6 +17,7 @@ class BasePage
|
|
16
17
|
def url
|
17
18
|
self.class.url
|
18
19
|
end
|
20
|
+
# rubocop:enable Style/TrivialAccessors
|
19
21
|
|
20
22
|
@@loaded_pages = []
|
21
23
|
|
@@ -29,6 +31,12 @@ class BasePage
|
|
29
31
|
@@loaded_pages << self.class
|
30
32
|
end
|
31
33
|
|
34
|
+
def go_to_here
|
35
|
+
# Ovveride this in base page to have something more
|
36
|
+
# complicated than this.
|
37
|
+
@interface.driver.navigate.to(url)
|
38
|
+
end
|
39
|
+
|
32
40
|
# Written by Browser and only used internally.
|
33
41
|
attr_writer :interface
|
34
42
|
|
@@ -40,6 +48,9 @@ class BasePage
|
|
40
48
|
# to the current class where that method creates an instance
|
41
49
|
# of klass.
|
42
50
|
# context is an ElementContext
|
51
|
+
#
|
52
|
+
# As it is, this seems silly to break into pieces for Rubocop.
|
53
|
+
# rubocop:disable Metrics/MethodLength
|
43
54
|
def add_method(context:, klass:, name:, setter: false)
|
44
55
|
name = "#{name}_#{klass.downcase}"
|
45
56
|
constant = Module.const_get(klass.capitalize)
|
@@ -55,11 +66,15 @@ class BasePage
|
|
55
66
|
end
|
56
67
|
end
|
57
68
|
end
|
69
|
+
# rubocop:enable Metrics/MethodLength
|
58
70
|
|
59
71
|
# This creates a new element instance whenever it's called.
|
60
72
|
# Because of that we can't keep state in any element objects.
|
61
73
|
# That seems like a good thing, actually.
|
62
74
|
# Called by layout method on pages.
|
75
|
+
#
|
76
|
+
# Hard to make shorter.
|
77
|
+
# rubocop:disable Metrics/MethodLength
|
63
78
|
def method_missing(element, *args, &_block)
|
64
79
|
name, selectors, rest = args
|
65
80
|
context = ElementContext.new(destinations: rest,
|
@@ -76,6 +91,7 @@ class BasePage
|
|
76
91
|
raise "#{element} NOT FOUND WITH ARGS #{args}!!!"
|
77
92
|
end
|
78
93
|
end
|
94
|
+
# rubocop:enable Metrics/MethodLength
|
79
95
|
|
80
96
|
def respond_to_missing?(*args)
|
81
97
|
# Is this right at all???
|
data/lib/rutl/browser.rb
CHANGED
@@ -11,12 +11,15 @@ class Browser
|
|
11
11
|
|
12
12
|
attr_reader :interface
|
13
13
|
|
14
|
-
def initialize(
|
14
|
+
def initialize(type:, rutl_pages: RUTL::PAGES || ENV['RUTL_PAGES'])
|
15
|
+
if rutl_pages.nil? || rutl_pages.empty?
|
16
|
+
raise "Set RUTL::PAGES or ENV['RUTL_PAGES'] or pass dir as rutl_pages:"
|
17
|
+
end
|
15
18
|
# This is kind of evil. Figure out how to ditch the $ variable.
|
16
19
|
$browser = self
|
17
20
|
@interface = nil
|
18
|
-
@interface = load_interface(
|
19
|
-
@interface.pages = load_pages(dir:
|
21
|
+
@interface = load_interface(type)
|
22
|
+
@interface.pages = load_pages(dir: rutl_pages)
|
20
23
|
end
|
21
24
|
|
22
25
|
def load_interface(type)
|
@@ -5,6 +5,7 @@ require 'rutl/interface/base_interface'
|
|
5
5
|
# Small interface for Chrome browser.
|
6
6
|
#
|
7
7
|
class ChromeInterface < BaseInterface
|
8
|
+
# rubocop:disable Metrics/MethodLength
|
8
9
|
def initialize
|
9
10
|
@logged_in = true
|
10
11
|
options = Selenium::WebDriver::Chrome::Options.new
|
@@ -20,6 +21,7 @@ class ChromeInterface < BaseInterface
|
|
20
21
|
@driver = Selenium::WebDriver.for :chrome, options: options
|
21
22
|
super
|
22
23
|
end
|
24
|
+
# rubocop:enable Metrics/MethodLength
|
23
25
|
|
24
26
|
def current_page
|
25
27
|
url = @driver.current_url
|
data/lib/rutl/version.rb
CHANGED
data/lib/rutl.rb
CHANGED
@@ -6,5 +6,8 @@ require 'rutl/version'
|
|
6
6
|
# desktop UI testing, turning the UI into an API via its DSL.
|
7
7
|
#
|
8
8
|
module RUTL
|
9
|
-
#
|
9
|
+
# Should define RUTL::PAGES directory for your code
|
10
|
+
# or set ENV['RUTL_PAGES']
|
11
|
+
# or Browser intialize will raise.
|
12
|
+
PAGES = nil
|
10
13
|
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.2.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-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -185,6 +185,8 @@ files:
|
|
185
185
|
- Rakefile
|
186
186
|
- bin/console
|
187
187
|
- bin/setup
|
188
|
+
- lib/rspec/default_rspec_to_browser.rb
|
189
|
+
- lib/rspec/rutl_matchers.rb
|
188
190
|
- lib/rutl.rb
|
189
191
|
- lib/rutl/base_page.rb
|
190
192
|
- lib/rutl/browser.rb
|