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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 736bda0dd5708d733bcf8462852fc6cfb85f83d7
4
- data.tar.gz: 05c9cddcef3a79a481218722289f8b17a76cbb2f
3
+ metadata.gz: 2c86a3e97f5b536ef792a2f328ab1c89730dba52
4
+ data.tar.gz: 05868d9a03e465878d552706e4fb1edf9d7b0c54
5
5
  SHA512:
6
- metadata.gz: 8b15fa2ecd4c7062d16bd774fdfabf228fe0baa5de65e402c2f1b55bdc7e312422014f8d739560a79c73eee8713f205f725f0578cd91fdd89240ac6ba2f81afa
7
- data.tar.gz: 39c0bd51b96fb07e6ff01f5e6483203a65051910457cff7a93fbd25fbd99f036722344dc055a6682157badd394ff346d2bd3c498f6f49707e4704b5ee4b7ed5c
6
+ metadata.gz: cde8c77355467aa19b00036ec2c5d7005a030bafc7c8fda922c038904bdbd360695ee00f2add1e79cbda1647bedae1ed189c02f258644ab3deaf0317df63cebf
7
+ data.tar.gz: c0acd68c6e51f6eea985c4add3c6a4b071bd8865fa9e6be67431a253478f00c1709f7b9ff8d9e33650e3cdfbcef68cbf60e0010cf75be48280da4d4292db2dfb
data/.gitignore CHANGED
@@ -7,6 +7,7 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ /.ruby-version
10
11
 
11
12
  # rspec failure tracking
12
13
  .rspec_status
data/.hound.yml CHANGED
@@ -5,8 +5,8 @@ AllCops:
5
5
  - spec/**/*
6
6
 
7
7
  # Removing need for frozen string literal comment.
8
- #Style/FrozenStringLiteralComment:
9
- # Enabled: false
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
@@ -3,3 +3,7 @@ language: ruby
3
3
  rvm:
4
4
  - 2.3.1
5
5
  before_install: gem install bundler -v 1.14.3
6
+
7
+ branches:
8
+ only:
9
+ - master
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
@@ -7,28 +7,86 @@ include RSpec::Matchers
7
7
  require "tapestry"
8
8
 
9
9
  puts Tapestry::VERSION
10
-
11
- browser = Watir::Browser.new
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
- browser.goto("http://localhost:9292")
28
- page = Home.new(browser)
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
- browser.quit()
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
- def initialize(browser)
13
- @browser = browser
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
@@ -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
- puts "(2) identifier: #{identifier}"
22
- puts "(3) signature: #{signature}"
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