tapestry 0.1.0 → 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/.gitignore +1 -0
- data/.hound.yml +8 -2
- data/.travis.yml +4 -0
- data/README.md +9 -0
- data/Rakefile +10 -0
- data/examples/tapestry-data-set.rb +24 -0
- data/examples/tapestry-events.rb +42 -0
- data/examples/tapestry-factory.rb +56 -0
- data/examples/tapestry-simple.rb +63 -5
- data/lib/tapestry.rb +40 -2
- data/lib/tapestry/attribute.rb +40 -0
- data/lib/tapestry/element.rb +30 -10
- data/lib/tapestry/errors.rb +16 -0
- data/lib/tapestry/extensions/data_setter.rb +106 -0
- data/lib/tapestry/extensions/dom_observer.js +78 -0
- data/lib/tapestry/extensions/dom_observer.rb +74 -0
- data/lib/tapestry/extensions/watir_elements.rb +16 -0
- data/lib/tapestry/factory.rb +92 -0
- data/lib/tapestry/interface.rb +203 -0
- data/lib/tapestry/ready.rb +93 -0
- data/lib/tapestry/situation.rb +77 -0
- data/lib/tapestry/version.rb +22 -1
- data/tapestry.gemspec +1 -0
- metadata +30 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c86a3e97f5b536ef792a2f328ab1c89730dba52
|
4
|
+
data.tar.gz: 05868d9a03e465878d552706e4fb1edf9d7b0c54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cde8c77355467aa19b00036ec2c5d7005a030bafc7c8fda922c038904bdbd360695ee00f2add1e79cbda1647bedae1ed189c02f258644ab3deaf0317df63cebf
|
7
|
+
data.tar.gz: c0acd68c6e51f6eea985c4add3c6a4b071bd8865fa9e6be67431a253478f00c1709f7b9ff8d9e33650e3cdfbcef68cbf60e0010cf75be48280da4d4292db2dfb
|
data/.gitignore
CHANGED
data/.hound.yml
CHANGED
@@ -5,8 +5,8 @@ AllCops:
|
|
5
5
|
- spec/**/*
|
6
6
|
|
7
7
|
# Removing need for frozen string literal comment.
|
8
|
-
|
9
|
-
|
8
|
+
Style/FrozenStringLiteralComment:
|
9
|
+
Enabled: false
|
10
10
|
|
11
11
|
# Removing the preference for string single quotes.
|
12
12
|
Style/StringLiterals:
|
@@ -60,3 +60,9 @@ Metrics/MethodLength:
|
|
60
60
|
# Encourage fewer parameters.
|
61
61
|
Metrics/ParameterLists:
|
62
62
|
Max: 4
|
63
|
+
|
64
|
+
# Allow methods with has_ for predicates.
|
65
|
+
Style/PredicateName:
|
66
|
+
NameWhitelist:
|
67
|
+
- has_correct_url?
|
68
|
+
- has_correct_title?
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Tapestry
|
2
2
|
|
3
|
+
[](https://travis-ci.org/jeffnyman/tapestry)
|
4
|
+
[](http://badge.fury.io/rb/tapestry)
|
5
|
+
[](https://github.com/jeffnyman/tapestry/blob/master/LICENSE.md)
|
6
|
+
|
7
|
+
[](https://gemnasium.com/jeffnyman/tapestry)
|
8
|
+
|
3
9
|
> _Nature uses only the longest threads to weave her patterns, so that each
|
4
10
|
> small piece of her fabric reveals the organization of the entire tapestry._
|
5
11
|
>
|
@@ -57,6 +63,9 @@ You can also install Tapestry just as you would any other gem:
|
|
57
63
|
Probably the best way to get a feel for the current state of the code is to look at the examples:
|
58
64
|
|
59
65
|
* [Simple script](https://github.com/jeffnyman/tapestry/blob/master/examples/tapestry-simple.rb)
|
66
|
+
* [Factory script](https://github.com/jeffnyman/tapestry/blob/master/examples/tapestry-factory.rb)
|
67
|
+
* [Data Set script](https://github.com/jeffnyman/tapestry/blob/master/examples/tapestry-data-set.rb)
|
68
|
+
* [Events script](https://github.com/jeffnyman/tapestry/blob/master/examples/tapestry-events.rb)
|
60
69
|
|
61
70
|
You'll see references to "Veilus" and a "localhost" in the script. I'm using my own [Veilus application](https://veilus.herokuapp.com/). As far as the localhost, you can use the [Veilus repo](https://github.com/jeffnyman/veilus) to get a local copy to play around with.
|
62
71
|
|
data/Rakefile
CHANGED
@@ -13,6 +13,16 @@ namespace :script do
|
|
13
13
|
task :simple do
|
14
14
|
system("ruby ./examples/tapestry-simple.rb")
|
15
15
|
end
|
16
|
+
|
17
|
+
desc "Run the Tapestry factory script"
|
18
|
+
task :factory do
|
19
|
+
system("ruby ./examples/tapestry-factory.rb")
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Run the Tapestry data setter script"
|
23
|
+
task :data_set do
|
24
|
+
system("ruby ./examples/tapestry-data-set.rb")
|
25
|
+
end
|
16
26
|
end
|
17
27
|
|
18
28
|
namespace :spec do
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << "./lib"
|
3
|
+
|
4
|
+
require "rspec"
|
5
|
+
include RSpec::Matchers
|
6
|
+
|
7
|
+
require "tapestry"
|
8
|
+
include Tapestry::Factory
|
9
|
+
|
10
|
+
class WarpTravel
|
11
|
+
include Tapestry
|
12
|
+
|
13
|
+
url_is "http://localhost:9292/warp"
|
14
|
+
|
15
|
+
text_field :warp_factor, id: 'warpInput'
|
16
|
+
text_field :velocity, id: 'velocityInput'
|
17
|
+
text_field :distance, id: 'distInput'
|
18
|
+
end
|
19
|
+
|
20
|
+
Tapestry.start_browser
|
21
|
+
|
22
|
+
on_view(WarpTravel).using_data("warp factor": 1, velocity: 1, distance: 4.3)
|
23
|
+
|
24
|
+
Tapestry.quit_browser
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << "./lib"
|
3
|
+
|
4
|
+
require "rspec"
|
5
|
+
include RSpec::Matchers
|
6
|
+
|
7
|
+
require "tapestry"
|
8
|
+
|
9
|
+
class Dynamic
|
10
|
+
include Tapestry
|
11
|
+
|
12
|
+
url_is "http://localhost:9292/practice/dynamic_events"
|
13
|
+
|
14
|
+
button :long, id: 'long'
|
15
|
+
button :quick, id: 'quick'
|
16
|
+
button :stale, id: 'stale'
|
17
|
+
button :fade, id: 'fade'
|
18
|
+
|
19
|
+
div :dom_events, id: 'container1'
|
20
|
+
div :stale_event, id: 'container2'
|
21
|
+
div :effect_events, id: 'container3'
|
22
|
+
end
|
23
|
+
|
24
|
+
Tapestry.start_browser
|
25
|
+
|
26
|
+
page = Dynamic.new
|
27
|
+
|
28
|
+
page.visit
|
29
|
+
|
30
|
+
expect(page.dom_events.dom_updated?).to be_truthy
|
31
|
+
expect(page.dom_events.wait_until(&:dom_updated?).spans.count).to eq(0)
|
32
|
+
|
33
|
+
page.long.click
|
34
|
+
|
35
|
+
expect(page.dom_events.dom_updated?).to be_falsey
|
36
|
+
expect(page.dom_events.wait_until(&:dom_updated?).spans.count).to eq(5)
|
37
|
+
|
38
|
+
page.quick.click
|
39
|
+
|
40
|
+
expect(page.dom_events.wait_until(&:dom_updated?).spans.count).to eq(25)
|
41
|
+
|
42
|
+
Tapestry.quit_browser
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << "./lib"
|
3
|
+
|
4
|
+
require "rspec"
|
5
|
+
include RSpec::Matchers
|
6
|
+
|
7
|
+
require "tapestry"
|
8
|
+
include Tapestry::Factory
|
9
|
+
|
10
|
+
puts Tapestry::VERSION
|
11
|
+
|
12
|
+
class Home
|
13
|
+
include Tapestry
|
14
|
+
|
15
|
+
url_is "http://localhost:9292"
|
16
|
+
|
17
|
+
p :login_form, id: "open", visible: true
|
18
|
+
text_field :username, id: "username"
|
19
|
+
text_field :password
|
20
|
+
button :login, id: "login-button"
|
21
|
+
div :message, class: 'notice'
|
22
|
+
|
23
|
+
#element :login_form, id: "open", visible: true
|
24
|
+
#element :username, id: "username"
|
25
|
+
#element :password
|
26
|
+
#element :login, id: "login-button"
|
27
|
+
end
|
28
|
+
|
29
|
+
class Navigation
|
30
|
+
include Tapestry
|
31
|
+
|
32
|
+
p :page_list, id: 'navlist'
|
33
|
+
link :planets, id: 'planets'
|
34
|
+
|
35
|
+
image :planet_logo, id: 'planet-logo'
|
36
|
+
end
|
37
|
+
|
38
|
+
Tapestry.set_browser :chrome
|
39
|
+
|
40
|
+
on_view(Home)
|
41
|
+
|
42
|
+
on(Home) do
|
43
|
+
@context.login_form.click
|
44
|
+
@context.username.set "admin"
|
45
|
+
@context.password(id: 'password').set "admin"
|
46
|
+
@context.login.click
|
47
|
+
expect(@context.message.text).to eq('You are now logged in as admin.')
|
48
|
+
end
|
49
|
+
|
50
|
+
on(Navigation) do
|
51
|
+
@context.page_list.wait_until(&:dom_updated?).click
|
52
|
+
@context.planets.click
|
53
|
+
expect(@context.planet_logo.exists?).to be true
|
54
|
+
end
|
55
|
+
|
56
|
+
Tapestry.quit_browser
|
data/examples/tapestry-simple.rb
CHANGED
@@ -7,28 +7,86 @@ include RSpec::Matchers
|
|
7
7
|
require "tapestry"
|
8
8
|
|
9
9
|
puts Tapestry::VERSION
|
10
|
-
|
11
|
-
|
10
|
+
puts Tapestry.version
|
11
|
+
puts Tapestry.dependencies
|
12
|
+
puts Tapestry.elements?
|
13
|
+
puts Tapestry.recognizes?("div")
|
12
14
|
|
13
15
|
class Home
|
14
16
|
include Tapestry
|
15
17
|
|
18
|
+
url_is "http://localhost:9292/"
|
19
|
+
url_matches /:\d{4}/
|
20
|
+
title_is "Veilus"
|
21
|
+
|
22
|
+
# Elements can be defined with HTML-style names as found in Watir.
|
16
23
|
p :login_form, id: "open", visible: true
|
17
24
|
text_field :username, id: "username"
|
18
25
|
text_field :password
|
19
26
|
button :login, id: "login-button"
|
27
|
+
div :message, class: 'notice'
|
20
28
|
|
29
|
+
# Elements can be defined with a generic name.
|
21
30
|
#element :login_form, id: "open", visible: true
|
22
31
|
#element :username, id: "username"
|
23
32
|
#element :password
|
24
33
|
#element :login, id: "login-button"
|
25
34
|
end
|
26
35
|
|
27
|
-
|
28
|
-
|
36
|
+
class Navigation
|
37
|
+
include Tapestry
|
38
|
+
|
39
|
+
p :page_list, id: 'navlist'
|
40
|
+
link :planets, id: 'planets'
|
41
|
+
|
42
|
+
image :planet_logo, id: 'planet-logo'
|
43
|
+
|
44
|
+
def begin_with
|
45
|
+
move_to(0, 0)
|
46
|
+
resize_to(screen_width, screen_height)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Tapestry.start_browser
|
51
|
+
|
52
|
+
page = Home.new
|
53
|
+
|
54
|
+
expect(Tapestry.browser).to be_an_instance_of(Watir::Browser)
|
55
|
+
expect(Tapestry.browser.driver).to be_an_instance_of(Selenium::WebDriver::Driver)
|
56
|
+
|
57
|
+
expect(page).to be_a_kind_of(Tapestry)
|
58
|
+
expect(page).to be_an_instance_of(Home)
|
59
|
+
|
60
|
+
# You can specify a URL to visit or you can rely on the provided
|
61
|
+
# url_is attribute on the page definition.
|
62
|
+
#page.visit("http://localhost:9292")
|
63
|
+
page.visit
|
64
|
+
|
65
|
+
expect(page.url).to eq(page.url_attribute)
|
66
|
+
expect(page.url).to match(page.url_match_attribute)
|
67
|
+
expect(page.title).to eq(page.title_attribute)
|
68
|
+
|
69
|
+
expect(page.secure?).to be_falsey
|
70
|
+
expect(page).not_to be_secure
|
71
|
+
|
72
|
+
expect(page.has_correct_url?).to be_truthy
|
73
|
+
expect(page).to have_correct_url
|
74
|
+
|
75
|
+
expect(page.displayed?).to be_truthy
|
76
|
+
expect(page).to be_displayed
|
77
|
+
|
78
|
+
expect(page.has_correct_title?).to be_truthy
|
79
|
+
expect(page).to have_correct_title
|
80
|
+
|
29
81
|
page.login_form.click
|
30
82
|
page.username.set "admin"
|
31
83
|
page.password(id: 'password').set "admin"
|
32
84
|
page.login.click
|
85
|
+
expect(page.message.text).to eq('You are now logged in as admin.')
|
86
|
+
|
87
|
+
page = Navigation.new
|
88
|
+
page.page_list.wait_until(&:dom_updated?).click
|
89
|
+
page.planets.click
|
90
|
+
expect(page.planet_logo.exists?).to be true
|
33
91
|
|
34
|
-
|
92
|
+
Tapestry.quit_browser
|
data/lib/tapestry.rb
CHANGED
@@ -1,15 +1,53 @@
|
|
1
1
|
require "tapestry/version"
|
2
2
|
require "tapestry/element"
|
3
|
+
require "tapestry/factory"
|
4
|
+
require "tapestry/interface"
|
5
|
+
require "tapestry/attribute"
|
6
|
+
require "tapestry/ready"
|
7
|
+
|
8
|
+
require "tapestry/extensions/dom_observer"
|
9
|
+
require "tapestry/extensions/data_setter"
|
10
|
+
require "tapestry/extensions/watir_elements"
|
3
11
|
|
4
12
|
require "watir"
|
5
13
|
|
6
14
|
module Tapestry
|
7
15
|
def self.included(caller)
|
8
16
|
caller.extend Tapestry::Element
|
17
|
+
caller.extend Tapestry::Interface::Page::Attribute
|
18
|
+
caller.__send__ :include, Tapestry::Ready
|
9
19
|
caller.__send__ :include, Tapestry::Locator
|
20
|
+
caller.__send__ :include, Tapestry::Interface::Page
|
21
|
+
caller.__send__ :include, Tapestry::DataSetter
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(browser = nil, &block)
|
25
|
+
@browser = Tapestry.browser unless Tapestry.browser.nil?
|
26
|
+
@browser = browser if Tapestry.browser.nil?
|
27
|
+
begin_with if respond_to?(:begin_with)
|
28
|
+
instance_eval(&block) if block
|
10
29
|
end
|
11
30
|
|
12
|
-
|
13
|
-
|
31
|
+
# This accessor is needed so that internal API calls, like `markup` or
|
32
|
+
# `text`, have access to the browser instance. This is an instance-level
|
33
|
+
# access to the browser.
|
34
|
+
attr_accessor :browser
|
35
|
+
|
36
|
+
class << self
|
37
|
+
# This accessor is needed so that Tapestry itself can provide a browser
|
38
|
+
# reference to indicate connection to WebDriver. This is a class-level
|
39
|
+
# access to the browser.
|
40
|
+
attr_accessor :browser
|
41
|
+
|
42
|
+
def set_browser(app = :chrome, *args)
|
43
|
+
@browser = Watir::Browser.new(app, *args)
|
44
|
+
Tapestry.browser = @browser
|
45
|
+
end
|
46
|
+
|
47
|
+
alias start_browser set_browser
|
48
|
+
|
49
|
+
def quit_browser
|
50
|
+
@browser.quit
|
51
|
+
end
|
14
52
|
end
|
15
53
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "tapestry/situation"
|
2
|
+
|
3
|
+
module Tapestry
|
4
|
+
module Interface
|
5
|
+
module Page
|
6
|
+
module Attribute
|
7
|
+
include Situation
|
8
|
+
|
9
|
+
def url_is(url = nil)
|
10
|
+
url_is_empty if url.nil? && url_attribute.nil?
|
11
|
+
url_is_empty if url.nil? || url.empty?
|
12
|
+
@url = url
|
13
|
+
end
|
14
|
+
|
15
|
+
def url_matches(pattern = nil)
|
16
|
+
url_match_is_empty if pattern.nil?
|
17
|
+
url_match_is_empty if pattern.is_a?(String) && pattern.empty?
|
18
|
+
@url_match = pattern
|
19
|
+
end
|
20
|
+
|
21
|
+
def title_is(title = nil)
|
22
|
+
title_is_empty if title.nil? || title.empty?
|
23
|
+
@title = title
|
24
|
+
end
|
25
|
+
|
26
|
+
def url_attribute
|
27
|
+
@url
|
28
|
+
end
|
29
|
+
|
30
|
+
def url_match_attribute
|
31
|
+
@url_match
|
32
|
+
end
|
33
|
+
|
34
|
+
def title_attribute
|
35
|
+
@title
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/tapestry/element.rb
CHANGED
@@ -5,6 +5,14 @@ module Tapestry
|
|
5
5
|
|
6
6
|
NATIVE_QUALIFIERS = %i(visible).freeze
|
7
7
|
|
8
|
+
def elements?
|
9
|
+
@elements
|
10
|
+
end
|
11
|
+
|
12
|
+
def recognizes?(method)
|
13
|
+
@elements.include? method.to_sym
|
14
|
+
end
|
15
|
+
|
8
16
|
def elements
|
9
17
|
@elements = Watir::Container.instance_methods unless @elements
|
10
18
|
end
|
@@ -16,11 +24,9 @@ module Tapestry
|
|
16
24
|
# element definition.
|
17
25
|
Tapestry.elements.each do |element|
|
18
26
|
define_method(element) do |*signature, &block|
|
19
|
-
puts "(1) signature: #{signature}"
|
20
27
|
identifier, signature = parse_signature(signature)
|
21
|
-
|
22
|
-
|
23
|
-
define_element_accessor(identifier, signature, element, &block)
|
28
|
+
context = context_from_signature(signature, &block)
|
29
|
+
define_element_accessor(identifier, signature, element, &context)
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
@@ -41,6 +47,26 @@ module Tapestry
|
|
41
47
|
[signature.shift, signature.shift]
|
42
48
|
end
|
43
49
|
|
50
|
+
# Returns the block or proc that serves as a context for an element
|
51
|
+
# definition. Consider the following element definitions:
|
52
|
+
#
|
53
|
+
# ul :facts, id: 'fact-list'
|
54
|
+
# span :fact, -> { facts.span(class: 'site-item')}
|
55
|
+
#
|
56
|
+
# Here the second element definition provides a proc that contains a
|
57
|
+
# context for another element definition. That leads to the following
|
58
|
+
# construction being sent to the browser:
|
59
|
+
#
|
60
|
+
# @browser.ul(id: 'fact-list').span(class: 'site-item')
|
61
|
+
def context_from_signature(*signature, &block)
|
62
|
+
if block_given?
|
63
|
+
block
|
64
|
+
else
|
65
|
+
context = signature.shift
|
66
|
+
context.is_a?(Proc) && signature.empty? ? context : nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
44
70
|
# This method provides the means to get the aspects of an accessor
|
45
71
|
# signature. The "aspects" refer to the locator information and any
|
46
72
|
# qualifier information that was provided along with the locator.
|
@@ -51,11 +77,9 @@ module Tapestry
|
|
51
77
|
# Note that "qualifiers" here refers to Watir boolean methods.
|
52
78
|
def accessor_aspects(element, *signature)
|
53
79
|
identifier = signature.shift
|
54
|
-
puts "(4) identifier: #{identifier}"
|
55
80
|
locator_args = {}
|
56
81
|
qualifier_args = {}
|
57
82
|
gather_aspects(identifier, element, locator_args, qualifier_args)
|
58
|
-
puts "(5) QUAL/LOC: #{[locator_args, qualifier_args]}"
|
59
83
|
[locator_args, qualifier_args]
|
60
84
|
end
|
61
85
|
|
@@ -147,15 +171,11 @@ module Tapestry
|
|
147
171
|
# like this: ["Drag and Drop"].
|
148
172
|
def define_element_accessor(identifier, *signature, element, &block)
|
149
173
|
locators, qualifiers = accessor_aspects(element, signature)
|
150
|
-
puts "(6) locators: #{locators}"
|
151
|
-
puts "(7) qualifiers: #{qualifiers}"
|
152
174
|
define_method(identifier.to_s) do |*values|
|
153
175
|
if block_given?
|
154
176
|
instance_exec(*values, &block)
|
155
177
|
else
|
156
|
-
puts "(8) values: #{values}"
|
157
178
|
locators = values[0] if locators.empty?
|
158
|
-
puts "(9) locators: #{locators} | empty? #{locators.empty?}"
|
159
179
|
access_element(element, locators, qualifiers)
|
160
180
|
end
|
161
181
|
end
|