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 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