testable 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.hound.yml +10 -0
- data/README.md +23 -7
- data/Rakefile +32 -0
- data/examples/testable-capybara-context.rb +64 -0
- data/examples/testable-capybara-rspec.rb +70 -0
- data/examples/testable-capybara.rb +46 -0
- data/examples/testable-watir-context.rb +5 -5
- data/lib/testable/capybara/dsl.rb +82 -0
- data/lib/testable/capybara/node.rb +30 -0
- data/lib/testable/capybara/page.rb +29 -0
- data/lib/testable/context.rb +6 -5
- data/lib/testable/extensions/core_ruby.rb +13 -0
- data/lib/testable/version.rb +1 -1
- data/lib/testable.rb +3 -0
- metadata +10 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c59c802e12f855543871b0917b7043a6dca602714c79cf5624d0033b86f8ce68
|
4
|
+
data.tar.gz: 3011ecba1297734eff0491ce54f4b8192bc5bdafe61b2736598af3649b790455
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9b98f008221c0bd278f690f6aee6fe32c5ea84ffe3c55a977d015ef25100846291bf9a753080caffec7e13d8fcc81f9f3e757cdf7e9f980ab4ce0abf8289ae0
|
7
|
+
data.tar.gz: b977fd589e01f32b48af6ed449087c2a3db1d05ed0aa0f87ee2b74d876259695b61ec0d117f1691f426b77f285ed7809876324243fd0e1e2d42fdee5436df903
|
data/.hound.yml
CHANGED
@@ -2,6 +2,7 @@ AllCops:
|
|
2
2
|
Exclude:
|
3
3
|
- testable.gemspec
|
4
4
|
- spec/**/*
|
5
|
+
- examples/**/*
|
5
6
|
|
6
7
|
# Removing need for frozen string literal comment.
|
7
8
|
Style/FrozenStringLiteralComment:
|
@@ -75,3 +76,12 @@ Naming/PredicateName:
|
|
75
76
|
NameWhitelist:
|
76
77
|
- has_correct_title?
|
77
78
|
- has_correct_url?
|
79
|
+
|
80
|
+
# Sometimes this seems like a better way to do things.
|
81
|
+
Style/DoubleNegation:
|
82
|
+
Enabled: false
|
83
|
+
|
84
|
+
# This is entirely for the addition of the match? method to String
|
85
|
+
# and that's only needed if you are using an outdated Ruby.
|
86
|
+
Style/MultilineIfModifier:
|
87
|
+
Enabled: False
|
data/README.md
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
# Testable
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
> **Testable /ˈtestəb(ə)l/**
|
4
|
+
>
|
5
|
+
> _adjective_
|
6
|
+
>
|
7
|
+
> _able to be tested or tried._
|
6
8
|
|
7
|
-
|
9
|
+
----
|
8
10
|
|
9
|
-
Testable is
|
11
|
+
Testable is an automated test micro-framework that provides a thin wrapper around [Watir](http://watir.com/) and [Capybara](http://teamcapybara.github.io/capybara/). Testable is based on many ideas from tools like [SitePrism](https://github.com/natritmeyer/site_prism) and [Watirsome](https://github.com/p0deje/watirsome), while also being a logical evolution of my own tool, [Tapestry](https://github.com/jeffnyman/tapestry).
|
10
12
|
|
11
|
-
|
13
|
+
One of the core goals of Testable is to be a mediating influence between higher-level tests (acceptance criteria) and lower-level implementations of those tests. You can see some of the design principles for more details on what guided construction.
|
12
14
|
|
13
15
|
## Installation
|
14
16
|
|
@@ -34,7 +36,21 @@ You can also install Testable just as you would any other gem:
|
|
34
36
|
|
35
37
|
## Usage
|
36
38
|
|
37
|
-
Instructions coming soon.
|
39
|
+
Instructions will be coming soon. However, there are a series of scripts in the `examples` directory and a series of commands in the `Rakefile` that will let you execute those scripts.
|
40
|
+
|
41
|
+
## Design Principles
|
42
|
+
|
43
|
+
An automated test framework provides a machine-executable abstraction around testing and encodes a set of guiding principles and heuristics for writing tests-as-code.
|
44
|
+
|
45
|
+
One of the obstacles to covering the gap between principles of testing and the practice of testing is the mechanics of writing tests. These mechanics are focused on abstractions. A lot of the practice of testing comes down to that: finding the right abstractions.
|
46
|
+
|
47
|
+
An automated test framework should be capable of consuming your preferred abstractions because ultimately the automation is simply a tool that supports testing, which means how the framework encourages tests to be expressed should have high fidelity with how human tests would be expressed.
|
48
|
+
|
49
|
+
Testable is built around the the idea that automation should largely be small-footprint, low-fiction, and high-yield.
|
50
|
+
|
51
|
+
The code that a test-supporting micro-framework allows should be modular, promoting both high cohesion and low coupling, as well as promoting a single level of abstraction. These concepts together lead to lightweight design as well as support traits that make change affordable for tests.
|
52
|
+
|
53
|
+
That makes the automation code less expensive to maintain and easier to change. That, ultimately, has a positive impact on the cost of change but, more importantly, allows Testable to be fit within a cost of mistake model, where the goal is to get feedback as quickly as possible regarding when mistakes are made.
|
38
54
|
|
39
55
|
## Development
|
40
56
|
|
data/Rakefile
CHANGED
@@ -16,6 +16,18 @@ namespace :script_testable do
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
namespace :script_capybara do
|
20
|
+
desc "Run the Testable Capybara script"
|
21
|
+
task :capybara do
|
22
|
+
system("ruby ./examples/testable-capybara.rb")
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Run the Testable Capybara context script"
|
26
|
+
task :context do
|
27
|
+
system("ruby ./examples/testable-capybara-context.rb")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
19
31
|
namespace :script_watir do
|
20
32
|
desc "Run the Testable Watir script"
|
21
33
|
task :watir do
|
@@ -26,6 +38,26 @@ namespace :script_watir do
|
|
26
38
|
task :test do
|
27
39
|
system("ruby ./examples/testable-watir-test.rb")
|
28
40
|
end
|
41
|
+
|
42
|
+
desc "Run the Testable Watir context script"
|
43
|
+
task :context do
|
44
|
+
system("ruby ./examples/testable-watir-context.rb")
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "Run the Testable Watir events script"
|
48
|
+
task :events do
|
49
|
+
system("ruby ./examples/testable-watir-events.rb")
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "Run the Testable Watir ready script"
|
53
|
+
task :ready do
|
54
|
+
system("ruby ./examples/testable-watir-ready.rb")
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "Run the Testable Watir data setter script"
|
58
|
+
task :dataset do
|
59
|
+
system("ruby ./examples/testable-watir-datasetter.rb")
|
60
|
+
end
|
29
61
|
end
|
30
62
|
|
31
63
|
namespace :spec do
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH << "./lib"
|
3
|
+
|
4
|
+
require "rspec"
|
5
|
+
include RSpec::Matchers
|
6
|
+
|
7
|
+
require "testable"
|
8
|
+
include Testable::Context
|
9
|
+
|
10
|
+
Capybara.configure do |config|
|
11
|
+
config.run_server = false
|
12
|
+
config.default_driver = :selenium
|
13
|
+
config.app_host = "https://veilus.herokuapp.com"
|
14
|
+
end
|
15
|
+
|
16
|
+
class HomePage < Testable::Page
|
17
|
+
element :login_form, "#open"
|
18
|
+
element :username, "#username"
|
19
|
+
element :password, "#password"
|
20
|
+
element :login, "#login-button"
|
21
|
+
|
22
|
+
def path
|
23
|
+
"/"
|
24
|
+
end
|
25
|
+
|
26
|
+
def login_as_admin
|
27
|
+
login_form.click
|
28
|
+
username.set "admin"
|
29
|
+
password.set "admin"
|
30
|
+
login.click
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Navigation < Testable::Node
|
35
|
+
elements :items, "a"
|
36
|
+
|
37
|
+
element :page_list, "#navlist"
|
38
|
+
element :overlord, "#overlord"
|
39
|
+
element :planets, "#planets"
|
40
|
+
element :warp, "#warp"
|
41
|
+
element :stardate, "#stardate"
|
42
|
+
end
|
43
|
+
|
44
|
+
class MenuItem < Testable::Node
|
45
|
+
components :items, Navigation, "#areas"
|
46
|
+
end
|
47
|
+
|
48
|
+
class LandingPage < Testable::Page
|
49
|
+
component :navigation, Navigation, "#areas"
|
50
|
+
element :logo, "#site-image"
|
51
|
+
end
|
52
|
+
|
53
|
+
on_visit(HomePage).login_as_admin
|
54
|
+
|
55
|
+
on(LandingPage) do |action|
|
56
|
+
action.navigation.page_list.click
|
57
|
+
puts action.navigation.overlord.text
|
58
|
+
puts action.navigation.items
|
59
|
+
puts action.navigation.items[0].text
|
60
|
+
expect(action.navigation).to have_items
|
61
|
+
expect(action.navigation.items[0].text).to eq("Home")
|
62
|
+
expect(action.navigation.items.count).to be(8)
|
63
|
+
action.navigation.overlord.click
|
64
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH << "./lib"
|
3
|
+
|
4
|
+
# Run this with:
|
5
|
+
# rspec ./examples/testable-capybara-rspec.rb
|
6
|
+
|
7
|
+
require "rspec"
|
8
|
+
include RSpec::Matchers
|
9
|
+
|
10
|
+
require "testable"
|
11
|
+
include Testable::Context
|
12
|
+
|
13
|
+
require "capybara/rspec"
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.expose_dsl_globally = true
|
17
|
+
end
|
18
|
+
|
19
|
+
Capybara.configure do |config|
|
20
|
+
config.run_server = false
|
21
|
+
config.default_driver = :selenium
|
22
|
+
config.app_host = "https://veilus.herokuapp.com"
|
23
|
+
end
|
24
|
+
|
25
|
+
class HomePage < Testable::Page
|
26
|
+
element :login_form, "#open"
|
27
|
+
element :username, "#username"
|
28
|
+
element :password, "#password"
|
29
|
+
element :login, "#login-button"
|
30
|
+
|
31
|
+
def path
|
32
|
+
"/"
|
33
|
+
end
|
34
|
+
|
35
|
+
def login_as_admin
|
36
|
+
login_form.click
|
37
|
+
username.set "admin"
|
38
|
+
password.set "admin"
|
39
|
+
login.click
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Navigation < Testable::Node
|
44
|
+
elements :items, "a"
|
45
|
+
|
46
|
+
element :page_list, "#navlist"
|
47
|
+
element :overlord, "#overlord"
|
48
|
+
element :planets, "#planets"
|
49
|
+
element :warp, "#warp"
|
50
|
+
element :stardate, "#stardate"
|
51
|
+
end
|
52
|
+
|
53
|
+
class LandingPage < Testable::Page
|
54
|
+
component :navigation, Navigation, "#areas"
|
55
|
+
|
56
|
+
def go_to_overlord
|
57
|
+
navigation.page_list.click
|
58
|
+
navigation.overlord.click
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
feature "navigation" do
|
63
|
+
background do
|
64
|
+
on_visit(HomePage).login_as_admin
|
65
|
+
end
|
66
|
+
|
67
|
+
scenario "navigates to overlord" do
|
68
|
+
on(LandingPage).go_to_overlord
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH << "./lib"
|
3
|
+
|
4
|
+
require "rspec"
|
5
|
+
include RSpec::Matchers
|
6
|
+
|
7
|
+
require "testable"
|
8
|
+
|
9
|
+
Capybara.configure do |config|
|
10
|
+
config.run_server = false
|
11
|
+
config.default_driver = :selenium
|
12
|
+
config.app_host = "https://veilus.herokuapp.com"
|
13
|
+
end
|
14
|
+
|
15
|
+
class HomePage < Testable::Page
|
16
|
+
element :login_form, "#open"
|
17
|
+
element :username, "#username"
|
18
|
+
element :password, "#password"
|
19
|
+
element :login, "#login-button"
|
20
|
+
|
21
|
+
def path
|
22
|
+
"/"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
home_page = HomePage.visit
|
27
|
+
|
28
|
+
puts home_page.current?
|
29
|
+
expect(home_page).to be_current
|
30
|
+
|
31
|
+
puts home_page.find("article").text
|
32
|
+
|
33
|
+
puts home_page.login_form.inspect
|
34
|
+
|
35
|
+
puts home_page.has_login_form?
|
36
|
+
puts home_page.has_no_login_form?
|
37
|
+
|
38
|
+
expect(home_page).to have_login_form
|
39
|
+
|
40
|
+
# The next statement would (correctly) fail.
|
41
|
+
# expect(home_page).to have_no_login_form
|
42
|
+
|
43
|
+
home_page.login_form.click
|
44
|
+
home_page.username.set "admin"
|
45
|
+
home_page.password.set "admin"
|
46
|
+
home_page.login.click
|
@@ -50,11 +50,11 @@ end
|
|
50
50
|
Testable.start_browser :firefox
|
51
51
|
|
52
52
|
on_visit(Home) do
|
53
|
-
@
|
54
|
-
@
|
55
|
-
@
|
56
|
-
@
|
57
|
-
expect(@
|
53
|
+
@context.login_form.click
|
54
|
+
@context.username.set "admin"
|
55
|
+
@context.password(id: 'password').set "admin"
|
56
|
+
@context.login.click
|
57
|
+
expect(@context.message.text).to eq('You are now logged in as admin.')
|
58
58
|
end
|
59
59
|
|
60
60
|
on(Navigation) do |page|
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Testable
|
2
|
+
module DSL
|
3
|
+
# The DSL module is mixed into the Node class to provide the DSL for
|
4
|
+
# defining elements and components.
|
5
|
+
def self.included(caller)
|
6
|
+
caller.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
# The ClassMethods provide a set of macro-like methods for wrapping
|
11
|
+
# HTML fragments in Node objects.
|
12
|
+
|
13
|
+
# Defines an element that wraps an HTML fragment.
|
14
|
+
def element(name, selector, options = {})
|
15
|
+
define_method(name.to_s) do
|
16
|
+
Node.new(node: @node.find(selector, options))
|
17
|
+
end
|
18
|
+
|
19
|
+
define_helpers(name, selector)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Defines a collection of elements that wrap HTML fragments.
|
23
|
+
def elements(name, selector, options = {})
|
24
|
+
options = { minimum: 1 }.merge(options)
|
25
|
+
|
26
|
+
define_method(name.to_s) do
|
27
|
+
@node.all(selector, options).map do |node|
|
28
|
+
Node.new(node: node)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
define_helpers(name, selector)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Defines a component that wraps an HTML fragment.
|
36
|
+
def component(name, klass, selector, options = {})
|
37
|
+
unless klass < Node
|
38
|
+
raise ArgumentError, 'Must be given a subclass of Node'
|
39
|
+
end
|
40
|
+
|
41
|
+
define_method(name.to_s) do
|
42
|
+
klass.new(node: @node.find(selector, options))
|
43
|
+
end
|
44
|
+
|
45
|
+
define_helpers(name, selector)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Defines a collection of components that wrap HTML fragments.
|
49
|
+
def components(name, klass, selector, options = {})
|
50
|
+
unless klass < Node
|
51
|
+
raise ArgumentError, 'Must be given a subclass of Node'
|
52
|
+
end
|
53
|
+
|
54
|
+
options = { minimum: 1 }.merge(options)
|
55
|
+
|
56
|
+
define_method(name.to_s) do
|
57
|
+
@node.all(selector, options).map do |node|
|
58
|
+
klass.new(node: node)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
define_helpers(name, selector)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def define_helpers(name, selector)
|
68
|
+
define_existence_predicates(name, selector)
|
69
|
+
end
|
70
|
+
|
71
|
+
def define_existence_predicates(name, selector)
|
72
|
+
define_method("has_#{name}?") do
|
73
|
+
@node.has_selector?(selector)
|
74
|
+
end
|
75
|
+
|
76
|
+
define_method("has_no_#{name}?") do
|
77
|
+
@node.has_no_selector?(selector)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "testable/capybara/dsl"
|
2
|
+
|
3
|
+
module Testable
|
4
|
+
class Node
|
5
|
+
# The Node class represents a wrapped HTML page or fragment. It exposes all
|
6
|
+
# methods of the Cogent DSL, making sure that any Capybara API methods
|
7
|
+
# are passed to the node instance.
|
8
|
+
include DSL
|
9
|
+
|
10
|
+
attr_reader :node
|
11
|
+
|
12
|
+
# A Capybara node is being wrapped in a node instance.
|
13
|
+
def initialize(node:)
|
14
|
+
@node = node
|
15
|
+
end
|
16
|
+
|
17
|
+
# Any Capybara API calls will be sent to the node object.
|
18
|
+
def method_missing(name, *args, &block)
|
19
|
+
if @node.respond_to?(name)
|
20
|
+
@node.send(name, *args, &block)
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def respond_to_missing?(name, include_private = false)
|
27
|
+
@node.respond_to?(name) || super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "capybara"
|
2
|
+
require "testable/capybara/node"
|
3
|
+
|
4
|
+
module Testable
|
5
|
+
class Page < Node
|
6
|
+
# The `Page` class wraps an HTML page with an application-specific API.
|
7
|
+
# This can be extended to define an API for manipulating the pages of
|
8
|
+
# the web application.
|
9
|
+
attr_reader :path
|
10
|
+
|
11
|
+
def self.visit
|
12
|
+
new.visit
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(node: Capybara.current_session, path: nil)
|
16
|
+
@node = node
|
17
|
+
@path = path
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit
|
21
|
+
@node.visit(path)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def current?
|
26
|
+
@node.current_path == path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/testable/context.rb
CHANGED
@@ -14,8 +14,8 @@ module Testable
|
|
14
14
|
# on_visit(TestPage)
|
15
15
|
def on_visit(definition, &block)
|
16
16
|
create_active(definition)
|
17
|
-
@
|
18
|
-
verify_page(@
|
17
|
+
@context.visit
|
18
|
+
verify_page(@context)
|
19
19
|
call_block(&block)
|
20
20
|
end
|
21
21
|
|
@@ -54,6 +54,7 @@ module Testable
|
|
54
54
|
# whether a given page has been reached would make the context definition
|
55
55
|
# look sloppy.
|
56
56
|
def verify_page(context)
|
57
|
+
return unless defined?(context.url_match_attribute)
|
57
58
|
return if context.url_match_attribute.nil?
|
58
59
|
return if context.has_correct_url?
|
59
60
|
|
@@ -61,12 +62,12 @@ module Testable
|
|
61
62
|
end
|
62
63
|
|
63
64
|
def create_active(definition)
|
64
|
-
@
|
65
|
+
@context = definition.new unless @context.is_a?(definition)
|
65
66
|
end
|
66
67
|
|
67
68
|
def call_block(&block)
|
68
|
-
yield @
|
69
|
-
@
|
69
|
+
yield @context if block
|
70
|
+
@context
|
70
71
|
end
|
71
72
|
end
|
72
73
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class String
|
2
|
+
# This is only required if using a version of Ruby before 2.4. A match?
|
3
|
+
# method for String was added in version 2.4.
|
4
|
+
def match?(string, pos = 0)
|
5
|
+
!!match(string, pos)
|
6
|
+
end unless //.respond_to?(:match?)
|
7
|
+
end
|
8
|
+
|
9
|
+
class FalseClass
|
10
|
+
def exists?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
end
|
data/lib/testable/version.rb
CHANGED
data/lib/testable.rb
CHANGED
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.5.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: 2019-08-
|
11
|
+
date: 2019-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -156,6 +156,9 @@ files:
|
|
156
156
|
- Rakefile
|
157
157
|
- bin/console
|
158
158
|
- bin/setup
|
159
|
+
- examples/testable-capybara-context.rb
|
160
|
+
- examples/testable-capybara-rspec.rb
|
161
|
+
- examples/testable-capybara.rb
|
159
162
|
- examples/testable-info.rb
|
160
163
|
- examples/testable-watir-context.rb
|
161
164
|
- examples/testable-watir-datasetter.rb
|
@@ -165,9 +168,13 @@ files:
|
|
165
168
|
- examples/testable-watir.rb
|
166
169
|
- lib/testable.rb
|
167
170
|
- lib/testable/attribute.rb
|
171
|
+
- lib/testable/capybara/dsl.rb
|
172
|
+
- lib/testable/capybara/node.rb
|
173
|
+
- lib/testable/capybara/page.rb
|
168
174
|
- lib/testable/context.rb
|
169
175
|
- lib/testable/element.rb
|
170
176
|
- lib/testable/errors.rb
|
177
|
+
- lib/testable/extensions/core_ruby.rb
|
171
178
|
- lib/testable/extensions/data_setter.rb
|
172
179
|
- lib/testable/extensions/dom_observer.js
|
173
180
|
- lib/testable/extensions/dom_observer.rb
|
@@ -185,7 +192,7 @@ metadata:
|
|
185
192
|
source_code_uri: https://github.com/jeffnyman/testable
|
186
193
|
changelog_uri: https://github.com/jeffnyman/testable/blob/master/CHANGELOG.md
|
187
194
|
post_install_message: "\n(::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)\n
|
188
|
-
\ Testable 0.
|
195
|
+
\ Testable 0.5.0 has been installed.\n(::) (::) (::) (::) (::) (::) (::) (::) (::)
|
189
196
|
(::) (::) (::)\n "
|
190
197
|
rdoc_options: []
|
191
198
|
require_paths:
|