tapestry 0.1.0 → 0.2.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 +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
|
+
[![Build Status](https://travis-ci.org/jeffnyman/tapestry.svg)](https://travis-ci.org/jeffnyman/tapestry)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/tapestry.svg)](http://badge.fury.io/rb/tapestry)
|
5
|
+
[![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jeffnyman/tapestry/blob/master/LICENSE.md)
|
6
|
+
|
7
|
+
[![Dependency Status](https://gemnasium.com/jeffnyman/tapestry.png)](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
|