testable 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.hound.yml +6 -0
- data/.rspec +1 -1
- data/README.md +12 -5
- data/Rakefile +7 -6
- data/circle.yml +3 -0
- data/lib/testable/data_setter.rb +51 -0
- data/lib/testable/dom_update.rb +19 -0
- data/lib/testable/element.rb +40 -39
- data/lib/testable/errors.rb +4 -0
- data/lib/testable/extensions/dom_observer.js +24 -0
- data/lib/testable/factory.rb +6 -2
- data/lib/testable/interface.rb +65 -24
- data/lib/testable/ready.rb +59 -0
- data/lib/testable/situation.rb +51 -8
- data/lib/testable/version.rb +11 -5
- data/lib/testable.rb +33 -7
- data/testable.gemspec +1 -2
- metadata +11 -27
- data/.travis.yml +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9fe925fea55efebca8a07a490fb639669355cecf
|
4
|
+
data.tar.gz: 750388f14ff7df442cba5c4abb60ea51125a70c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ae956962245d4326c31028c59b96fb060d1e46765d8db56309eadfe8460bb308a79876bfcafaa57fdfde38ad9e2a9c676c83c10ce9f7eef8df231a3938d0c57
|
7
|
+
data.tar.gz: 2b986d69188ff6df56efe57e3dbb5724d83ed21bc6aefc0acb19198ad2e3f8b8da21dbf1c9aa31b7cdbbce156757528bed75d1f116c635c9f66b0cc736c19598
|
data/.gitignore
CHANGED
data/.hound.yml
CHANGED
@@ -49,6 +49,12 @@ Style/SingleLineBlockParams:
|
|
49
49
|
Style/DotPosition:
|
50
50
|
EnforcedStyle: leading
|
51
51
|
|
52
|
+
# Allow methods with has_ for predicates.
|
53
|
+
Style/PredicateName:
|
54
|
+
NameWhitelist:
|
55
|
+
- has_correct_url?
|
56
|
+
- has_correct_title?
|
57
|
+
|
52
58
|
# Stop nesting so hard.
|
53
59
|
Metrics/BlockNesting:
|
54
60
|
Max: 2
|
data/.rspec
CHANGED
data/README.md
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
# Testable
|
2
2
|
|
3
|
+
[![Build Status](https://circleci.com/gh/jeffnyman/testable.svg?style=shield)](https://circleci.com/gh/jeffnyman/testable)
|
3
4
|
[![Gem Version](https://badge.fury.io/rb/testable.svg)](http://badge.fury.io/rb/testable)
|
4
5
|
[![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jeffnyman/testable/blob/master/LICENSE.txt)
|
5
6
|
|
6
7
|
[![Dependency Status](https://gemnasium.com/jeffnyman/testable.png)](https://gemnasium.com/jeffnyman/testable)
|
7
8
|
|
8
|
-
Testable
|
9
|
+
The Testable gem aims to be a micro-framework that will provide a semantic DSL to construct a fluent interface for test execution libraries.
|
9
10
|
|
10
11
|
This fluent interface promotes the idea of compressibility of your test logic, allowing for more factoring, more reuse, and less repetition. You can use Testable directly as an automated test library or you can use it with other tools such as RSpec, Cucumber, or anything else that allows you to delegate down to a different level of abstraction.
|
11
12
|
|
12
|
-
Unlike many of my other tooling experiments with Ruby, Testable relies on [Capybara](https://github.com/jnicklas/capybara) as its execution library.
|
13
|
-
|
14
13
|
## Installation
|
15
14
|
|
16
15
|
To get the latest stable release, add this line to your application's Gemfile:
|
@@ -36,14 +35,22 @@ You can also install Testable just as you would any other gem:
|
|
36
35
|
## Usage
|
37
36
|
|
38
37
|
Probably the best way to get a feel for the current state of the code is to look at the test scripts:
|
39
|
-
|
38
|
+
|
40
39
|
* [Standard test script](https://github.com/jeffnyman/testable/blob/master/test/testable-standard.rb)
|
41
40
|
* [Factory test script](https://github.com/jeffnyman/testable/blob/master/test/testable-factory.rb)
|
42
41
|
|
43
|
-
If you clone the repository, you can see this script in action by running the command `rake
|
42
|
+
If you clone the repository, you can see this script in action by running the command `rake script:standard` or `rake script:factory`.
|
43
|
+
|
44
|
+
You'll see references to "Veilus" and a localhost URL. I'm using my own [Veilus application](https://veilus.herokuapp.com/). The localhost refers to using the [Veilus repo](https://github.com/jeffnyman/veilus) locally.
|
44
45
|
|
45
46
|
More details will be forthcoming as the project evolves.
|
46
47
|
|
48
|
+
## Implementation Ideas
|
49
|
+
|
50
|
+
I want to hide driver details (i.e., Selenium) as much as possible but still allow access to the full API. Ideally I'd like to avoid the low-level Selenium and use a wrapper around it while not inventing my own. Since both watir-webdriver and capybara delegate down to Selenium, it seems wise to use one of those. (Or maybe allow for both as Symbiont currently does.) An open question is whether I should proxy the driver library to my own implementation or not. Symbiont currently does exactly that, which is a very different model than [page-object](https://github.com/cheezy/page-object), which has to add an incredible amount of wasted code rather than just relying on delegation and a proxy pattern.
|
51
|
+
|
52
|
+
I don't want to presume a particular modeling structure, such as the use of page objects. That being said, most such modeling structures are really just classes that are being utilized in a certain way, which means I really want to model the more generic notion of an interface. An interface could, theoretically, be an application (screens), a site (pages), or an API (services). An interface would be a Screen class, a Page class, or a Service class. These then become screen objects, page objects or service objects. An open question is how those distinctions are purely conceptual versus have specific implementations.
|
53
|
+
|
47
54
|
## Development
|
48
55
|
|
49
56
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake spec:all` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/Rakefile
CHANGED
@@ -1,26 +1,27 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
|
-
|
3
2
|
require "bundler/gem_tasks"
|
4
3
|
require "rdoc/task"
|
5
|
-
require "rubocop/rake_task"
|
6
4
|
require "rspec/core/rake_task"
|
5
|
+
require "rubocop/rake_task"
|
7
6
|
|
8
7
|
RuboCop::RakeTask.new
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
10
|
+
|
11
|
+
namespace :script do
|
12
|
+
desc "Run the Testable standard script"
|
12
13
|
task :standard do
|
13
14
|
system("ruby ./test/testable-standard.rb")
|
14
15
|
end
|
15
16
|
|
16
|
-
desc "Run the
|
17
|
+
desc "Run the Testable factory script"
|
17
18
|
task :factory do
|
18
19
|
system("ruby ./test/testable-factory.rb")
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
22
23
|
namespace :spec do
|
23
|
-
desc
|
24
|
+
desc 'Clean all generated reports'
|
24
25
|
task :clean do
|
25
26
|
system('rm -rf spec/reports')
|
26
27
|
end
|
data/circle.yml
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
class Object
|
2
|
+
def chain(method_chain, arg = nil)
|
3
|
+
return self if method_chain.empty?
|
4
|
+
method_chain.split('.').inject(self) do |o, m|
|
5
|
+
if arg.nil?
|
6
|
+
o.send(m.intern)
|
7
|
+
else
|
8
|
+
o.send(m.intern, arg)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Testable
|
15
|
+
module DataSetter
|
16
|
+
def using(data)
|
17
|
+
data.each do |key, value|
|
18
|
+
use_data_with(key, value) if object_enabled_for(key)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
alias using_data using
|
23
|
+
alias use_data using
|
24
|
+
alias using_values using
|
25
|
+
alias use_values using
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def use_data_with(key, value)
|
30
|
+
element = send(key.to_s)
|
31
|
+
set_and_select(key, element, value)
|
32
|
+
check_and_uncheck(key, element, value)
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_and_select(key, element, value)
|
36
|
+
chain("#{key}.set", value) if element.class == Watir::TextField
|
37
|
+
chain("#{key}.set") if element.class == Watir::Radio
|
38
|
+
chain("#{key}.select", value) if element.class == Watir::Select
|
39
|
+
end
|
40
|
+
|
41
|
+
def check_and_uncheck(key, element, value)
|
42
|
+
return chain("#{key}.check") if element.class == Watir::CheckBox && value
|
43
|
+
chain("#{key}.uncheck") if element.class == Watir::CheckBox
|
44
|
+
end
|
45
|
+
|
46
|
+
def object_enabled_for(key)
|
47
|
+
web_element = send(key.to_s)
|
48
|
+
web_element.enabled? && web_element.visible?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Watir
|
2
|
+
class Element
|
3
|
+
OBSERVER_FILE = "/extensions/dom_observer.js".freeze
|
4
|
+
DOM_OBSERVER = File.read("#{File.dirname(__FILE__)}#{OBSERVER_FILE}").freeze
|
5
|
+
|
6
|
+
def dom_updated?(delay: 1.1)
|
7
|
+
driver.manage.timeouts.script_timeout = delay + 1
|
8
|
+
driver.execute_async_script(DOM_OBSERVER, wd, delay)
|
9
|
+
ensure
|
10
|
+
driver.manage.timeouts.script_timeout = 1
|
11
|
+
end
|
12
|
+
|
13
|
+
alias dom_updated dom_updated?
|
14
|
+
alias dom_is_updated dom_updated?
|
15
|
+
alias dom_is_changed dom_updated?
|
16
|
+
alias when_dom_updated dom_updated?
|
17
|
+
alias when_dom_changed dom_updated?
|
18
|
+
end
|
19
|
+
end
|
data/lib/testable/element.rb
CHANGED
@@ -1,55 +1,56 @@
|
|
1
|
+
require "watir"
|
2
|
+
require "testable/situation"
|
3
|
+
|
1
4
|
module Testable
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
include Situation
|
6
|
+
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def elements
|
10
|
+
@elements = Watir::Container.instance_methods unless @elements
|
11
|
+
end
|
12
|
+
|
13
|
+
def elements?
|
14
|
+
@elements
|
15
|
+
end
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
def recognizes?(method)
|
18
|
+
@elements.include? method.to_sym
|
19
|
+
end
|
20
|
+
|
21
|
+
module Element
|
22
|
+
Testable.elements.each do |element|
|
23
|
+
# This is what allows Watir-based elements to be defined
|
24
|
+
# on an element definition.
|
25
|
+
define_method(element) do |*signature|
|
26
|
+
identifier, locator = parse_signature(signature)
|
27
|
+
define_element_accessor(identifier, locator, element)
|
18
28
|
end
|
19
29
|
end
|
20
30
|
|
21
31
|
private
|
22
32
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
33
|
+
def define_element_accessor(identifier, *locator, element)
|
34
|
+
# This is what allows each identifier to be a method.
|
35
|
+
define_method(identifier.to_s.to_sym) do |*values|
|
36
|
+
no_locator(self.class, identifier) if empty_locator(locator, values)
|
37
|
+
locator = values if locator[0].nil?
|
38
|
+
reference_element(element, locator)
|
39
|
+
end
|
26
40
|
end
|
27
41
|
|
28
|
-
def
|
29
|
-
|
30
|
-
nonexistence_check(name, locator)
|
42
|
+
def parse_signature(signature)
|
43
|
+
[signature.shift, signature.shift]
|
31
44
|
end
|
32
45
|
|
33
|
-
|
34
|
-
|
35
|
-
create_element_method(name, locator) do
|
36
|
-
define_method(method_name) do |*args|
|
37
|
-
element_exists?(locator, *args)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
46
|
+
module Locator
|
47
|
+
private
|
41
48
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
element_does_not_exist?(locator, *args)
|
47
|
-
end
|
49
|
+
def reference_element(element, locator)
|
50
|
+
@browser.__send__(element, *locator).to_subtype
|
51
|
+
rescue NoMethodError
|
52
|
+
@browser.__send__(element, *locator)
|
48
53
|
end
|
49
54
|
end
|
50
|
-
|
51
|
-
def create_element_method(_name, _locator)
|
52
|
-
yield
|
53
|
-
end
|
54
55
|
end
|
55
56
|
end
|
data/lib/testable/errors.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
module Testable
|
2
2
|
module Errors
|
3
3
|
NoUrlForDefinition = Class.new(StandardError)
|
4
|
+
NoUrlMatchForDefinition = Class.new(StandardError)
|
5
|
+
NoUrlMatchPossible = Class.new(StandardError)
|
4
6
|
NoTitleForDefinition = Class.new(StandardError)
|
5
7
|
NoLocatorForDefinition = Class.new(StandardError)
|
8
|
+
PageNotValidatedError = Class.new(StandardError)
|
9
|
+
NoBlockForWhenReady = Class.new(StandardError)
|
6
10
|
end
|
7
11
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
// WebDriver arguments
|
2
|
+
var element = arguments[0];
|
3
|
+
var delay = arguments[1] * 1000;
|
4
|
+
var callback = arguments[2];
|
5
|
+
|
6
|
+
var notStartedUpdating = function() {
|
7
|
+
return setTimeout(function() {
|
8
|
+
observer.disconnect();
|
9
|
+
callback(true);
|
10
|
+
}, 1000);
|
11
|
+
};
|
12
|
+
|
13
|
+
var startedUpdating = function() {
|
14
|
+
clearTimeout(notStartedUpdating);
|
15
|
+
observer.disconnect();
|
16
|
+
callback(false);
|
17
|
+
};
|
18
|
+
|
19
|
+
// Observer
|
20
|
+
var observer = new MutationObserver(startedUpdating);
|
21
|
+
var config = { attributes: true, childList: true, characterData: true, subtree: true };
|
22
|
+
observer.observe(element, config);
|
23
|
+
|
24
|
+
var notStartedUpdating = notStartedUpdating();
|
data/lib/testable/factory.rb
CHANGED
@@ -4,14 +4,18 @@ module Testable
|
|
4
4
|
# exists, that context will be re-used.
|
5
5
|
def on(definition, visit = false, &block)
|
6
6
|
unless @context.is_a?(definition)
|
7
|
-
@context = definition.new
|
8
|
-
@context.
|
7
|
+
@context = definition.new(@browser) if @browser
|
8
|
+
@context = definition.new unless @browser
|
9
|
+
@context.visit if visit
|
9
10
|
end
|
10
11
|
|
11
12
|
yield @context if block
|
12
13
|
@context
|
13
14
|
end
|
14
15
|
|
16
|
+
alias on_page on
|
17
|
+
alias while_on on
|
18
|
+
|
15
19
|
# Creates a definition context for actions and establishes the
|
16
20
|
# context for execution.
|
17
21
|
def on_view(definition, &block)
|
data/lib/testable/interface.rb
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
require "capybara/dsl"
|
2
1
|
require "testable/situation"
|
3
|
-
require "testable/element"
|
4
2
|
|
5
3
|
module Testable
|
6
|
-
include Situation
|
7
4
|
module Interface
|
8
5
|
module Page
|
9
|
-
include Capybara::DSL
|
10
|
-
|
11
6
|
module Attribute
|
12
7
|
include Situation
|
13
|
-
|
14
8
|
def url_is(url = nil)
|
15
9
|
url_is_empty if url.nil? || url.empty?
|
16
10
|
@url = url
|
17
11
|
end
|
18
12
|
|
19
|
-
def
|
13
|
+
def url_matches(pattern = nil)
|
14
|
+
url_match_is_empty if pattern.nil?
|
15
|
+
url_match_is_empty if pattern.is_a?(String) && pattern.empty?
|
16
|
+
@url_match = pattern
|
17
|
+
end
|
18
|
+
|
19
|
+
def title_is(title)
|
20
20
|
title_is_empty if title.nil? || title.empty?
|
21
21
|
@title = title
|
22
22
|
end
|
@@ -25,48 +25,89 @@ module Testable
|
|
25
25
|
@url
|
26
26
|
end
|
27
27
|
|
28
|
+
def url_match_attribute
|
29
|
+
@url_match || url_attribute
|
30
|
+
end
|
31
|
+
|
28
32
|
def title_attribute
|
29
33
|
@title
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
33
|
-
def
|
34
|
-
no_url_provided if
|
35
|
-
|
37
|
+
def visit(url = nil)
|
38
|
+
no_url_provided if url.nil? && url_attribute.nil?
|
39
|
+
@browser.goto(url) unless url.nil?
|
40
|
+
@browser.goto(url_attribute) if url.nil?
|
41
|
+
self
|
36
42
|
end
|
37
43
|
|
44
|
+
alias view visit
|
45
|
+
alias navigate_to visit
|
46
|
+
alias goto visit
|
47
|
+
alias perform visit
|
48
|
+
|
38
49
|
def url
|
39
|
-
|
50
|
+
@browser.url
|
40
51
|
end
|
41
52
|
|
42
|
-
|
43
|
-
|
53
|
+
alias page_url url
|
54
|
+
alias current_url url
|
55
|
+
|
56
|
+
def title
|
57
|
+
@browser.title
|
44
58
|
end
|
45
59
|
|
46
|
-
|
47
|
-
|
60
|
+
alias page_title title
|
61
|
+
|
62
|
+
def markup
|
63
|
+
browser.html
|
48
64
|
end
|
49
65
|
|
66
|
+
alias html markup
|
67
|
+
|
68
|
+
def text
|
69
|
+
browser.text
|
70
|
+
end
|
71
|
+
|
72
|
+
alias page_text text
|
73
|
+
|
74
|
+
def resize(width, height)
|
75
|
+
browser.window.resize_to(width, height)
|
76
|
+
end
|
77
|
+
|
78
|
+
alias resize_to resize
|
79
|
+
|
50
80
|
def secure?
|
51
81
|
!url.match(/^https/).nil?
|
52
82
|
end
|
53
83
|
|
54
|
-
|
84
|
+
def url_match_attribute
|
85
|
+
value = self.class.url_match_attribute
|
86
|
+
return if value.nil?
|
87
|
+
value = Regexp.new(value) unless value.is_a?(Regexp)
|
88
|
+
value
|
89
|
+
end
|
55
90
|
|
56
|
-
def
|
57
|
-
|
91
|
+
def has_correct_url?
|
92
|
+
if url_attribute.nil? && url_match_attribute.nil?
|
93
|
+
no_url_match_is_possible
|
94
|
+
end
|
95
|
+
!(url =~ url_match_attribute).nil?
|
58
96
|
end
|
59
97
|
|
60
|
-
|
61
|
-
|
98
|
+
alias displayed? has_correct_url?
|
99
|
+
|
100
|
+
def has_correct_title?
|
101
|
+
no_title_is_provided if title_attribute.nil?
|
102
|
+
!title.match(title_attribute).nil?
|
62
103
|
end
|
63
104
|
|
64
|
-
def
|
65
|
-
|
105
|
+
def url_attribute
|
106
|
+
self.class.url_attribute
|
66
107
|
end
|
67
108
|
|
68
|
-
def
|
69
|
-
|
109
|
+
def title_attribute
|
110
|
+
self.class.title_attribute
|
70
111
|
end
|
71
112
|
end
|
72
113
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "testable/situation"
|
2
|
+
|
3
|
+
module Testable
|
4
|
+
module Ready
|
5
|
+
include Situation
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def ready_validations
|
9
|
+
if superclass.respond_to?(:ready_validations)
|
10
|
+
superclass.ready_validations + _ready_validations
|
11
|
+
else
|
12
|
+
_ready_validations
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def page_ready(&block)
|
17
|
+
_ready_validations << block
|
18
|
+
end
|
19
|
+
|
20
|
+
alias page_ready_when page_ready
|
21
|
+
|
22
|
+
def _ready_validations
|
23
|
+
@_ready_validations ||= []
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_accessor :ready, :ready_error
|
28
|
+
|
29
|
+
def self.included(caller)
|
30
|
+
caller.extend(ClassMethods)
|
31
|
+
end
|
32
|
+
|
33
|
+
def when_ready(&_block)
|
34
|
+
already_marked_ready = ready
|
35
|
+
|
36
|
+
no_ready_check_possible unless block_given?
|
37
|
+
|
38
|
+
self.ready = ready?
|
39
|
+
not_ready_validation(ready_error || 'NO REASON PROVIDED') unless ready
|
40
|
+
yield self
|
41
|
+
ensure
|
42
|
+
self.ready = already_marked_ready
|
43
|
+
end
|
44
|
+
|
45
|
+
def ready?
|
46
|
+
self.ready_error = nil
|
47
|
+
return true if ready
|
48
|
+
ready_validations_pass?
|
49
|
+
end
|
50
|
+
|
51
|
+
def ready_validations_pass?
|
52
|
+
self.class.ready_validations.all? do |validation|
|
53
|
+
passed, message = instance_eval(&validation)
|
54
|
+
self.ready_error = message if message && !passed
|
55
|
+
passed
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/testable/situation.rb
CHANGED
@@ -11,6 +11,30 @@ module Testable
|
|
11
11
|
raise Testable::Errors::NoUrlForDefinition
|
12
12
|
end
|
13
13
|
|
14
|
+
def no_url_provided
|
15
|
+
puts "PROBLEM: no url provided.\n" \
|
16
|
+
"You called a '#{retrieve_method(caller)}' action but the " \
|
17
|
+
"definition '#{self.class}' does not have a url_is attribute.\n" \
|
18
|
+
"Either provide the url_is attribute or pass the url as an " \
|
19
|
+
"argument to the visit call.\n\n"
|
20
|
+
raise Testable::Errors::NoUrlForDefinition
|
21
|
+
end
|
22
|
+
|
23
|
+
def url_match_is_empty
|
24
|
+
puts "PROBLEM: url_matches attribute empty.\n" \
|
25
|
+
"The url_matches attribute is empty on the definition " \
|
26
|
+
"'#{retrieve_class(caller)}'.\n\n"
|
27
|
+
raise Testable::Errors::NoUrlMatchForDefinition
|
28
|
+
end
|
29
|
+
|
30
|
+
def no_url_match_is_possible
|
31
|
+
puts "PROBLEM: No url_is or url_matches attribute.\n" \
|
32
|
+
"You called a '#{retrieve_method(caller)}' action but the " \
|
33
|
+
"definition '#{self.class}' has no url_is attribute nor a " \
|
34
|
+
"url_matches attribute.\n\n"
|
35
|
+
raise Testable::Errors::NoUrlMatchPossible
|
36
|
+
end
|
37
|
+
|
14
38
|
def title_is_empty
|
15
39
|
puts "PROBLEM: title_is attribute empty.\n" \
|
16
40
|
"The title_is attribute is empty on the definition " \
|
@@ -18,22 +42,37 @@ module Testable
|
|
18
42
|
raise Testable::Errors::NoTitleForDefinition
|
19
43
|
end
|
20
44
|
|
21
|
-
def
|
22
|
-
puts "PROBLEM:
|
45
|
+
def no_title_is_provided
|
46
|
+
puts "PROBLEM: No title provided.\n" \
|
23
47
|
"You called a '#{retrieve_method(caller)}' action but the " \
|
24
|
-
"definition '#{self.class}' does not have a
|
25
|
-
|
26
|
-
"argument to the visit call.\n\n"
|
27
|
-
raise Testable::Errors::NoUrlForDefinition
|
48
|
+
"definition '#{self.class}' does not have a title_is attribute.\n\n"
|
49
|
+
raise Testable::Errors::NoTitleForDefinition
|
28
50
|
end
|
29
51
|
|
30
|
-
def no_locator(
|
52
|
+
def no_locator(definition, identifier)
|
31
53
|
puts "PROBLEM: No locator provided.\n" \
|
32
|
-
"You are using '#{
|
54
|
+
"You are using '#{identifier}' on '#{definition}'. " \
|
33
55
|
"But there is no locator provided with it.\n\n"
|
34
56
|
raise Testable::Errors::NoLocatorForDefinition
|
35
57
|
end
|
36
58
|
|
59
|
+
def not_ready_validation(message)
|
60
|
+
puts "PROBLEM: A ready validation error was encountered.\n" \
|
61
|
+
"A ready validation failed to validate. The ready check was " \
|
62
|
+
"on the '#{self.class}' definition. " \
|
63
|
+
"The reason provided was:\n" \
|
64
|
+
"#{message}.\n\n"
|
65
|
+
raise Testable::Errors::PageNotValidatedError, message
|
66
|
+
end
|
67
|
+
|
68
|
+
def no_ready_check_possible
|
69
|
+
puts "PROBLEM: A when ready call has no action.\n" \
|
70
|
+
"You called a when_ready on a definition but did not provide " \
|
71
|
+
"any action for it. Add a block with logic that should be " \
|
72
|
+
"executed if the ready check passes.\n\n"
|
73
|
+
raise Testable::Errors::NoBlockForWhenReady
|
74
|
+
end
|
75
|
+
|
37
76
|
def retrieve_class(caller)
|
38
77
|
caller[1][/`.*'/][8..-3]
|
39
78
|
end
|
@@ -41,5 +80,9 @@ module Testable
|
|
41
80
|
def retrieve_method(caller)
|
42
81
|
caller[0][/`.*'/][1..-2]
|
43
82
|
end
|
83
|
+
|
84
|
+
def empty_locator(locator, values)
|
85
|
+
locator[0].nil? && values.empty? && !values[0].is_a?(Hash)
|
86
|
+
end
|
44
87
|
end
|
45
88
|
end
|
data/lib/testable/version.rb
CHANGED
@@ -1,18 +1,24 @@
|
|
1
1
|
module Testable
|
2
|
-
VERSION = "0.2.0".freeze
|
3
|
-
|
4
2
|
module_function
|
5
3
|
|
4
|
+
VERSION = "0.3.0".freeze
|
5
|
+
|
6
6
|
def version
|
7
7
|
"""
|
8
8
|
Testable v#{Testable::VERSION}
|
9
|
-
|
10
|
-
selenium-webdriver: #{
|
9
|
+
watir: #{gem_version('watir')}
|
10
|
+
selenium-webdriver: #{gem_version('selenium-webdriver')}
|
11
11
|
"""
|
12
12
|
end
|
13
13
|
|
14
14
|
def dependencies
|
15
|
-
Gem.loaded_specs.values.map { |
|
15
|
+
Gem.loaded_specs.values.map { |spec| "#{spec.name} #{spec.version}\n" }
|
16
16
|
.uniq.sort.join(",").split(",")
|
17
17
|
end
|
18
|
+
|
19
|
+
def gem_version(name)
|
20
|
+
Gem.loaded_specs[name].version
|
21
|
+
rescue NoMethodError
|
22
|
+
puts "No gem loaded for #{name}."
|
23
|
+
end
|
18
24
|
end
|
data/lib/testable.rb
CHANGED
@@ -1,17 +1,43 @@
|
|
1
|
-
require "capybara"
|
2
|
-
require "selenium-webdriver"
|
3
|
-
|
4
1
|
require "testable/version"
|
5
|
-
|
2
|
+
|
3
|
+
require "testable/ready"
|
6
4
|
require "testable/factory"
|
5
|
+
require "testable/element"
|
7
6
|
require "testable/interface"
|
7
|
+
require "testable/dom_update"
|
8
|
+
require "testable/data_setter"
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
require "watir"
|
11
|
+
require "selenium-webdriver"
|
11
12
|
|
12
|
-
|
13
|
+
module Testable
|
14
|
+
def self.included(caller)
|
13
15
|
caller.extend Testable::Element
|
14
16
|
caller.extend Testable::Interface::Page::Attribute
|
17
|
+
caller.__send__ :include, Testable::Ready
|
18
|
+
caller.__send__ :include, Testable::DataSetter
|
15
19
|
caller.__send__ :include, Testable::Interface::Page
|
20
|
+
caller.__send__ :include, Testable::Element::Locator
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(browser = nil, &block)
|
24
|
+
@browser = Testable.browser unless Testable.browser.nil?
|
25
|
+
@browser = browser if Testable.browser.nil?
|
26
|
+
instance_eval(&block) if block
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_accessor :browser
|
30
|
+
|
31
|
+
class << self
|
32
|
+
attr_accessor :browser
|
33
|
+
|
34
|
+
def set_browser(app = :chrome, *args)
|
35
|
+
@browser = Watir::Browser.new(app, *args)
|
36
|
+
Testable.browser = @browser
|
37
|
+
end
|
38
|
+
|
39
|
+
def quit_browser
|
40
|
+
@browser.quit
|
41
|
+
end
|
16
42
|
end
|
17
43
|
end
|
data/testable.gemspec
CHANGED
@@ -27,8 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_development_dependency "rubocop"
|
28
28
|
spec.add_development_dependency "pry"
|
29
29
|
|
30
|
-
spec.add_runtime_dependency "
|
31
|
-
spec.add_runtime_dependency "selenium-webdriver", "~> 3.0"
|
30
|
+
spec.add_runtime_dependency "watir", "~> 6.0"
|
32
31
|
|
33
32
|
spec.post_install_message = %{
|
34
33
|
(::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: testable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Nyman
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -81,39 +81,19 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '2.10'
|
90
|
-
- - "<"
|
91
|
-
- !ruby/object:Gem::Version
|
92
|
-
version: '3.0'
|
93
|
-
type: :runtime
|
94
|
-
prerelease: false
|
95
|
-
version_requirements: !ruby/object:Gem::Requirement
|
96
|
-
requirements:
|
97
|
-
- - ">="
|
98
|
-
- !ruby/object:Gem::Version
|
99
|
-
version: '2.10'
|
100
|
-
- - "<"
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
version: '3.0'
|
103
|
-
- !ruby/object:Gem::Dependency
|
104
|
-
name: selenium-webdriver
|
84
|
+
name: watir
|
105
85
|
requirement: !ruby/object:Gem::Requirement
|
106
86
|
requirements:
|
107
87
|
- - "~>"
|
108
88
|
- !ruby/object:Gem::Version
|
109
|
-
version: '
|
89
|
+
version: '6.0'
|
110
90
|
type: :runtime
|
111
91
|
prerelease: false
|
112
92
|
version_requirements: !ruby/object:Gem::Requirement
|
113
93
|
requirements:
|
114
94
|
- - "~>"
|
115
95
|
- !ruby/object:Gem::Version
|
116
|
-
version: '
|
96
|
+
version: '6.0'
|
117
97
|
description: Provides a semantic DSL to construct fluent interfaces for test execution
|
118
98
|
logic.
|
119
99
|
email:
|
@@ -126,7 +106,6 @@ files:
|
|
126
106
|
- ".hound.yml"
|
127
107
|
- ".rspec"
|
128
108
|
- ".rubocop.yml"
|
129
|
-
- ".travis.yml"
|
130
109
|
- CODE_OF_CONDUCT.md
|
131
110
|
- Gemfile
|
132
111
|
- LICENSE.txt
|
@@ -134,11 +113,16 @@ files:
|
|
134
113
|
- Rakefile
|
135
114
|
- bin/console
|
136
115
|
- bin/setup
|
116
|
+
- circle.yml
|
137
117
|
- lib/testable.rb
|
118
|
+
- lib/testable/data_setter.rb
|
119
|
+
- lib/testable/dom_update.rb
|
138
120
|
- lib/testable/element.rb
|
139
121
|
- lib/testable/errors.rb
|
122
|
+
- lib/testable/extensions/dom_observer.js
|
140
123
|
- lib/testable/factory.rb
|
141
124
|
- lib/testable/interface.rb
|
125
|
+
- lib/testable/ready.rb
|
142
126
|
- lib/testable/situation.rb
|
143
127
|
- lib/testable/version.rb
|
144
128
|
- testable.gemspec
|
@@ -147,7 +131,7 @@ licenses:
|
|
147
131
|
- MIT
|
148
132
|
metadata: {}
|
149
133
|
post_install_message: "\n(::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)\n
|
150
|
-
\ Testable 0.
|
134
|
+
\ Testable 0.3.0 has been installed.\n(::) (::) (::) (::) (::) (::) (::) (::) (::)
|
151
135
|
(::) (::) (::)\n "
|
152
136
|
rdoc_options: []
|
153
137
|
require_paths:
|