testable 0.2.0 → 0.3.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: ca79301e385c9f1457040532815cfe04632a4977
4
- data.tar.gz: 315f37f4f0e4afe12e610e5d87d1b5e36194e80e
3
+ metadata.gz: 9fe925fea55efebca8a07a490fb639669355cecf
4
+ data.tar.gz: 750388f14ff7df442cba5c4abb60ea51125a70c6
5
5
  SHA512:
6
- metadata.gz: 2132bbcc51ddbc97d9416c1e80c72e6b4b245bfadd2dd0179e1fac1c75f794080fe917ec59dfff3e3e424b057d007c984aa41eda885df1fc0748910ed69b6e18
7
- data.tar.gz: 64f358af899249bed9b2eaa06936420090a0b209440d3ad63d81475903c02d7845792a3a1df33c983650e4f67c8dd48339d63ccf17d5924710e9bdcf0d2bde22
6
+ metadata.gz: 4ae956962245d4326c31028c59b96fb060d1e46765d8db56309eadfe8460bb308a79876bfcafaa57fdfde38ad9e2a9c676c83c10ce9f7eef8df231a3938d0c57
7
+ data.tar.gz: 2b986d69188ff6df56efe57e3dbb5724d83ed21bc6aefc0acb19198ad2e3f8b8da21dbf1c9aa31b7cdbbce156757528bed75d1f116c635c9f66b0cc736c19598
data/.gitignore CHANGED
@@ -11,6 +11,7 @@
11
11
  /doc/
12
12
  /pkg/
13
13
  /spec/reports/
14
+ /spec/examples.txt
14
15
  /tmp/
15
16
  *.log
16
17
  *.tmp
data/.hound.yml CHANGED
@@ -49,6 +49,12 @@ Style/SingleLineBlockParams:
49
49
  Style/DotPosition:
50
50
  EnforcedStyle: leading
51
51
 
52
+ # Allow methods with has_ for predicates.
53
+ Style/PredicateName:
54
+ NameWhitelist:
55
+ - has_correct_url?
56
+ - has_correct_title?
57
+
52
58
  # Stop nesting so hard.
53
59
  Metrics/BlockNesting:
54
60
  Max: 2
data/.rspec CHANGED
@@ -1,3 +1,3 @@
1
- --color
2
1
  --format documentation
2
+ --color
3
3
  --require spec_helper
data/README.md CHANGED
@@ -1,16 +1,15 @@
1
1
  # Testable
2
2
 
3
+ [![Build Status](https://circleci.com/gh/jeffnyman/testable.svg?style=shield)](https://circleci.com/gh/jeffnyman/testable)
3
4
  [![Gem Version](https://badge.fury.io/rb/testable.svg)](http://badge.fury.io/rb/testable)
4
5
  [![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jeffnyman/testable/blob/master/LICENSE.txt)
5
6
 
6
7
  [![Dependency Status](https://gemnasium.com/jeffnyman/testable.png)](https://gemnasium.com/jeffnyman/testable)
7
8
 
8
- Testable provides a semantic DSL to construct a fluent interface for test execution libraries.
9
+ The Testable gem aims to be a micro-framework that will provide a semantic DSL to construct a fluent interface for test execution libraries.
9
10
 
10
11
  This fluent interface promotes the idea of compressibility of your test logic, allowing for more factoring, more reuse, and less repetition. You can use Testable directly as an automated test library or you can use it with other tools such as RSpec, Cucumber, or anything else that allows you to delegate down to a different level of abstraction.
11
12
 
12
- Unlike many of my other tooling experiments with Ruby, Testable relies on [Capybara](https://github.com/jnicklas/capybara) as its execution library.
13
-
14
13
  ## Installation
15
14
 
16
15
  To get the latest stable release, add this line to your application's Gemfile:
@@ -36,14 +35,22 @@ You can also install Testable just as you would any other gem:
36
35
  ## Usage
37
36
 
38
37
  Probably the best way to get a feel for the current state of the code is to look at the test scripts:
39
-
38
+
40
39
  * [Standard test script](https://github.com/jeffnyman/testable/blob/master/test/testable-standard.rb)
41
40
  * [Factory test script](https://github.com/jeffnyman/testable/blob/master/test/testable-factory.rb)
42
41
 
43
- If you clone the repository, you can see this script in action by running the command `rake scripts:standard` or `rake scripts:factory`.
42
+ If you clone the repository, you can see this script in action by running the command `rake script:standard` or `rake script:factory`.
43
+
44
+ You'll see references to "Veilus" and a localhost URL. I'm using my own [Veilus application](https://veilus.herokuapp.com/). The localhost refers to using the [Veilus repo](https://github.com/jeffnyman/veilus) locally.
44
45
 
45
46
  More details will be forthcoming as the project evolves.
46
47
 
48
+ ## Implementation Ideas
49
+
50
+ I want to hide driver details (i.e., Selenium) as much as possible but still allow access to the full API. Ideally I'd like to avoid the low-level Selenium and use a wrapper around it while not inventing my own. Since both watir-webdriver and capybara delegate down to Selenium, it seems wise to use one of those. (Or maybe allow for both as Symbiont currently does.) An open question is whether I should proxy the driver library to my own implementation or not. Symbiont currently does exactly that, which is a very different model than [page-object](https://github.com/cheezy/page-object), which has to add an incredible amount of wasted code rather than just relying on delegation and a proxy pattern.
51
+
52
+ I don't want to presume a particular modeling structure, such as the use of page objects. That being said, most such modeling structures are really just classes that are being utilized in a certain way, which means I really want to model the more generic notion of an interface. An interface could, theoretically, be an application (screens), a site (pages), or an API (services). An interface would be a Screen class, a Page class, or a Service class. These then become screen objects, page objects or service objects. An open question is how those distinctions are purely conceptual versus have specific implementations.
53
+
47
54
  ## Development
48
55
 
49
56
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake spec:all` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/Rakefile CHANGED
@@ -1,26 +1,27 @@
1
1
  #!/usr/bin/env rake
2
-
3
2
  require "bundler/gem_tasks"
4
3
  require "rdoc/task"
5
- require "rubocop/rake_task"
6
4
  require "rspec/core/rake_task"
5
+ require "rubocop/rake_task"
7
6
 
8
7
  RuboCop::RakeTask.new
9
8
 
10
- namespace :scripts do
11
- desc "Run the standard script."
9
+ RSpec::Core::RakeTask.new(:spec)
10
+
11
+ namespace :script do
12
+ desc "Run the Testable standard script"
12
13
  task :standard do
13
14
  system("ruby ./test/testable-standard.rb")
14
15
  end
15
16
 
16
- desc "Run the script with a factory."
17
+ desc "Run the Testable factory script"
17
18
  task :factory do
18
19
  system("ruby ./test/testable-factory.rb")
19
20
  end
20
21
  end
21
22
 
22
23
  namespace :spec do
23
- desc "Clean all generated reports."
24
+ desc 'Clean all generated reports'
24
25
  task :clean do
25
26
  system('rm -rf spec/reports')
26
27
  end
data/circle.yml ADDED
@@ -0,0 +1,3 @@
1
+ checkout:
2
+ post:
3
+ - mkdir -p spec/reports
@@ -0,0 +1,51 @@
1
+ class Object
2
+ def chain(method_chain, arg = nil)
3
+ return self if method_chain.empty?
4
+ method_chain.split('.').inject(self) do |o, m|
5
+ if arg.nil?
6
+ o.send(m.intern)
7
+ else
8
+ o.send(m.intern, arg)
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ module Testable
15
+ module DataSetter
16
+ def using(data)
17
+ data.each do |key, value|
18
+ use_data_with(key, value) if object_enabled_for(key)
19
+ end
20
+ end
21
+
22
+ alias using_data using
23
+ alias use_data using
24
+ alias using_values using
25
+ alias use_values using
26
+
27
+ private
28
+
29
+ def use_data_with(key, value)
30
+ element = send(key.to_s)
31
+ set_and_select(key, element, value)
32
+ check_and_uncheck(key, element, value)
33
+ end
34
+
35
+ def set_and_select(key, element, value)
36
+ chain("#{key}.set", value) if element.class == Watir::TextField
37
+ chain("#{key}.set") if element.class == Watir::Radio
38
+ chain("#{key}.select", value) if element.class == Watir::Select
39
+ end
40
+
41
+ def check_and_uncheck(key, element, value)
42
+ return chain("#{key}.check") if element.class == Watir::CheckBox && value
43
+ chain("#{key}.uncheck") if element.class == Watir::CheckBox
44
+ end
45
+
46
+ def object_enabled_for(key)
47
+ web_element = send(key.to_s)
48
+ web_element.enabled? && web_element.visible?
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,19 @@
1
+ module Watir
2
+ class Element
3
+ OBSERVER_FILE = "/extensions/dom_observer.js".freeze
4
+ DOM_OBSERVER = File.read("#{File.dirname(__FILE__)}#{OBSERVER_FILE}").freeze
5
+
6
+ def dom_updated?(delay: 1.1)
7
+ driver.manage.timeouts.script_timeout = delay + 1
8
+ driver.execute_async_script(DOM_OBSERVER, wd, delay)
9
+ ensure
10
+ driver.manage.timeouts.script_timeout = 1
11
+ end
12
+
13
+ alias dom_updated dom_updated?
14
+ alias dom_is_updated dom_updated?
15
+ alias dom_is_changed dom_updated?
16
+ alias when_dom_updated dom_updated?
17
+ alias when_dom_changed dom_updated?
18
+ end
19
+ end
@@ -1,55 +1,56 @@
1
+ require "watir"
2
+ require "testable/situation"
3
+
1
4
  module Testable
2
- module Element
3
- def element(name, locator = nil)
4
- no_locator(name) if locator.nil?
5
- build(name, locator) do
6
- define_method(name.to_s) do |*options|
7
- find_first(locator, *options)
8
- end
9
- end
10
- end
5
+ include Situation
6
+
7
+ module_function
8
+
9
+ def elements
10
+ @elements = Watir::Container.instance_methods unless @elements
11
+ end
12
+
13
+ def elements?
14
+ @elements
15
+ end
11
16
 
12
- def elements(name, locator = nil)
13
- no_locator(name) if locator.nil?
14
- build(name, locator) do
15
- define_method(name.to_s) do |*options|
16
- find_all(locator, *options)
17
- end
17
+ def recognizes?(method)
18
+ @elements.include? method.to_sym
19
+ end
20
+
21
+ module Element
22
+ Testable.elements.each do |element|
23
+ # This is what allows Watir-based elements to be defined
24
+ # on an element definition.
25
+ define_method(element) do |*signature|
26
+ identifier, locator = parse_signature(signature)
27
+ define_element_accessor(identifier, locator, element)
18
28
  end
19
29
  end
20
30
 
21
31
  private
22
32
 
23
- def build(name, locator)
24
- yield
25
- add_element_methods(name, locator)
33
+ def define_element_accessor(identifier, *locator, element)
34
+ # This is what allows each identifier to be a method.
35
+ define_method(identifier.to_s.to_sym) do |*values|
36
+ no_locator(self.class, identifier) if empty_locator(locator, values)
37
+ locator = values if locator[0].nil?
38
+ reference_element(element, locator)
39
+ end
26
40
  end
27
41
 
28
- def add_element_methods(name, locator)
29
- existence_check(name, locator)
30
- nonexistence_check(name, locator)
42
+ def parse_signature(signature)
43
+ [signature.shift, signature.shift]
31
44
  end
32
45
 
33
- def existence_check(name, locator)
34
- method_name = "has_#{name}?"
35
- create_element_method(name, locator) do
36
- define_method(method_name) do |*args|
37
- element_exists?(locator, *args)
38
- end
39
- end
40
- end
46
+ module Locator
47
+ private
41
48
 
42
- def nonexistence_check(name, locator)
43
- method_name = "has_no_#{name}?"
44
- create_element_method(name, locator) do
45
- define_method(method_name) do |*args|
46
- element_does_not_exist?(locator, *args)
47
- end
49
+ def reference_element(element, locator)
50
+ @browser.__send__(element, *locator).to_subtype
51
+ rescue NoMethodError
52
+ @browser.__send__(element, *locator)
48
53
  end
49
54
  end
50
-
51
- def create_element_method(_name, _locator)
52
- yield
53
- end
54
55
  end
55
56
  end
@@ -1,7 +1,11 @@
1
1
  module Testable
2
2
  module Errors
3
3
  NoUrlForDefinition = Class.new(StandardError)
4
+ NoUrlMatchForDefinition = Class.new(StandardError)
5
+ NoUrlMatchPossible = Class.new(StandardError)
4
6
  NoTitleForDefinition = Class.new(StandardError)
5
7
  NoLocatorForDefinition = Class.new(StandardError)
8
+ PageNotValidatedError = Class.new(StandardError)
9
+ NoBlockForWhenReady = Class.new(StandardError)
6
10
  end
7
11
  end
@@ -0,0 +1,24 @@
1
+ // WebDriver arguments
2
+ var element = arguments[0];
3
+ var delay = arguments[1] * 1000;
4
+ var callback = arguments[2];
5
+
6
+ var notStartedUpdating = function() {
7
+ return setTimeout(function() {
8
+ observer.disconnect();
9
+ callback(true);
10
+ }, 1000);
11
+ };
12
+
13
+ var startedUpdating = function() {
14
+ clearTimeout(notStartedUpdating);
15
+ observer.disconnect();
16
+ callback(false);
17
+ };
18
+
19
+ // Observer
20
+ var observer = new MutationObserver(startedUpdating);
21
+ var config = { attributes: true, childList: true, characterData: true, subtree: true };
22
+ observer.observe(element, config);
23
+
24
+ var notStartedUpdating = notStartedUpdating();
@@ -4,14 +4,18 @@ module Testable
4
4
  # exists, that context will be re-used.
5
5
  def on(definition, visit = false, &block)
6
6
  unless @context.is_a?(definition)
7
- @context = definition.new
8
- @context.view if visit
7
+ @context = definition.new(@browser) if @browser
8
+ @context = definition.new unless @browser
9
+ @context.visit if visit
9
10
  end
10
11
 
11
12
  yield @context if block
12
13
  @context
13
14
  end
14
15
 
16
+ alias on_page on
17
+ alias while_on on
18
+
15
19
  # Creates a definition context for actions and establishes the
16
20
  # context for execution.
17
21
  def on_view(definition, &block)
@@ -1,22 +1,22 @@
1
- require "capybara/dsl"
2
1
  require "testable/situation"
3
- require "testable/element"
4
2
 
5
3
  module Testable
6
- include Situation
7
4
  module Interface
8
5
  module Page
9
- include Capybara::DSL
10
-
11
6
  module Attribute
12
7
  include Situation
13
-
14
8
  def url_is(url = nil)
15
9
  url_is_empty if url.nil? || url.empty?
16
10
  @url = url
17
11
  end
18
12
 
19
- def title_is(title = nil)
13
+ def url_matches(pattern = nil)
14
+ url_match_is_empty if pattern.nil?
15
+ url_match_is_empty if pattern.is_a?(String) && pattern.empty?
16
+ @url_match = pattern
17
+ end
18
+
19
+ def title_is(title)
20
20
  title_is_empty if title.nil? || title.empty?
21
21
  @title = title
22
22
  end
@@ -25,48 +25,89 @@ module Testable
25
25
  @url
26
26
  end
27
27
 
28
+ def url_match_attribute
29
+ @url_match || url_attribute
30
+ end
31
+
28
32
  def title_attribute
29
33
  @title
30
34
  end
31
35
  end
32
36
 
33
- def view
34
- no_url_provided if (url.nil? || url == 'data:,') && url_attribute.nil?
35
- visit(url_attribute)
37
+ def visit(url = nil)
38
+ no_url_provided if url.nil? && url_attribute.nil?
39
+ @browser.goto(url) unless url.nil?
40
+ @browser.goto(url_attribute) if url.nil?
41
+ self
36
42
  end
37
43
 
44
+ alias view visit
45
+ alias navigate_to visit
46
+ alias goto visit
47
+ alias perform visit
48
+
38
49
  def url
39
- current_url
50
+ @browser.url
40
51
  end
41
52
 
42
- def url_attribute
43
- self.class.url_attribute
53
+ alias page_url url
54
+ alias current_url url
55
+
56
+ def title
57
+ @browser.title
44
58
  end
45
59
 
46
- def title_attribute
47
- self.class.title_attribute
60
+ alias page_title title
61
+
62
+ def markup
63
+ browser.html
48
64
  end
49
65
 
66
+ alias html markup
67
+
68
+ def text
69
+ browser.text
70
+ end
71
+
72
+ alias page_text text
73
+
74
+ def resize(width, height)
75
+ browser.window.resize_to(width, height)
76
+ end
77
+
78
+ alias resize_to resize
79
+
50
80
  def secure?
51
81
  !url.match(/^https/).nil?
52
82
  end
53
83
 
54
- private
84
+ def url_match_attribute
85
+ value = self.class.url_match_attribute
86
+ return if value.nil?
87
+ value = Regexp.new(value) unless value.is_a?(Regexp)
88
+ value
89
+ end
55
90
 
56
- def find_first(*locator)
57
- find(*locator)
91
+ def has_correct_url?
92
+ if url_attribute.nil? && url_match_attribute.nil?
93
+ no_url_match_is_possible
94
+ end
95
+ !(url =~ url_match_attribute).nil?
58
96
  end
59
97
 
60
- def find_all(*locator)
61
- all(*locator)
98
+ alias displayed? has_correct_url?
99
+
100
+ def has_correct_title?
101
+ no_title_is_provided if title_attribute.nil?
102
+ !title.match(title_attribute).nil?
62
103
  end
63
104
 
64
- def element_exists?(*locator)
65
- has_selector?(*locator)
105
+ def url_attribute
106
+ self.class.url_attribute
66
107
  end
67
108
 
68
- def element_does_not_exist?(*locator)
69
- has_no_selector?(*locator)
109
+ def title_attribute
110
+ self.class.title_attribute
70
111
  end
71
112
  end
72
113
  end
@@ -0,0 +1,59 @@
1
+ require "testable/situation"
2
+
3
+ module Testable
4
+ module Ready
5
+ include Situation
6
+
7
+ module ClassMethods
8
+ def ready_validations
9
+ if superclass.respond_to?(:ready_validations)
10
+ superclass.ready_validations + _ready_validations
11
+ else
12
+ _ready_validations
13
+ end
14
+ end
15
+
16
+ def page_ready(&block)
17
+ _ready_validations << block
18
+ end
19
+
20
+ alias page_ready_when page_ready
21
+
22
+ def _ready_validations
23
+ @_ready_validations ||= []
24
+ end
25
+ end
26
+
27
+ attr_accessor :ready, :ready_error
28
+
29
+ def self.included(caller)
30
+ caller.extend(ClassMethods)
31
+ end
32
+
33
+ def when_ready(&_block)
34
+ already_marked_ready = ready
35
+
36
+ no_ready_check_possible unless block_given?
37
+
38
+ self.ready = ready?
39
+ not_ready_validation(ready_error || 'NO REASON PROVIDED') unless ready
40
+ yield self
41
+ ensure
42
+ self.ready = already_marked_ready
43
+ end
44
+
45
+ def ready?
46
+ self.ready_error = nil
47
+ return true if ready
48
+ ready_validations_pass?
49
+ end
50
+
51
+ def ready_validations_pass?
52
+ self.class.ready_validations.all? do |validation|
53
+ passed, message = instance_eval(&validation)
54
+ self.ready_error = message if message && !passed
55
+ passed
56
+ end
57
+ end
58
+ end
59
+ end
@@ -11,6 +11,30 @@ module Testable
11
11
  raise Testable::Errors::NoUrlForDefinition
12
12
  end
13
13
 
14
+ def no_url_provided
15
+ puts "PROBLEM: no url provided.\n" \
16
+ "You called a '#{retrieve_method(caller)}' action but the " \
17
+ "definition '#{self.class}' does not have a url_is attribute.\n" \
18
+ "Either provide the url_is attribute or pass the url as an " \
19
+ "argument to the visit call.\n\n"
20
+ raise Testable::Errors::NoUrlForDefinition
21
+ end
22
+
23
+ def url_match_is_empty
24
+ puts "PROBLEM: url_matches attribute empty.\n" \
25
+ "The url_matches attribute is empty on the definition " \
26
+ "'#{retrieve_class(caller)}'.\n\n"
27
+ raise Testable::Errors::NoUrlMatchForDefinition
28
+ end
29
+
30
+ def no_url_match_is_possible
31
+ puts "PROBLEM: No url_is or url_matches attribute.\n" \
32
+ "You called a '#{retrieve_method(caller)}' action but the " \
33
+ "definition '#{self.class}' has no url_is attribute nor a " \
34
+ "url_matches attribute.\n\n"
35
+ raise Testable::Errors::NoUrlMatchPossible
36
+ end
37
+
14
38
  def title_is_empty
15
39
  puts "PROBLEM: title_is attribute empty.\n" \
16
40
  "The title_is attribute is empty on the definition " \
@@ -18,22 +42,37 @@ module Testable
18
42
  raise Testable::Errors::NoTitleForDefinition
19
43
  end
20
44
 
21
- def no_url_provided
22
- puts "PROBLEM: no url provided.\n" \
45
+ def no_title_is_provided
46
+ puts "PROBLEM: No title provided.\n" \
23
47
  "You called a '#{retrieve_method(caller)}' action but the " \
24
- "definition '#{self.class}' does not have a url_is attribute.\n" \
25
- "Either provide the url_is attribute or pass the url as an " \
26
- "argument to the visit call.\n\n"
27
- raise Testable::Errors::NoUrlForDefinition
48
+ "definition '#{self.class}' does not have a title_is attribute.\n\n"
49
+ raise Testable::Errors::NoTitleForDefinition
28
50
  end
29
51
 
30
- def no_locator(name)
52
+ def no_locator(definition, identifier)
31
53
  puts "PROBLEM: No locator provided.\n" \
32
- "You are using '#{name}' on '#{retrieve_class(caller)}'. " \
54
+ "You are using '#{identifier}' on '#{definition}'. " \
33
55
  "But there is no locator provided with it.\n\n"
34
56
  raise Testable::Errors::NoLocatorForDefinition
35
57
  end
36
58
 
59
+ def not_ready_validation(message)
60
+ puts "PROBLEM: A ready validation error was encountered.\n" \
61
+ "A ready validation failed to validate. The ready check was " \
62
+ "on the '#{self.class}' definition. " \
63
+ "The reason provided was:\n" \
64
+ "#{message}.\n\n"
65
+ raise Testable::Errors::PageNotValidatedError, message
66
+ end
67
+
68
+ def no_ready_check_possible
69
+ puts "PROBLEM: A when ready call has no action.\n" \
70
+ "You called a when_ready on a definition but did not provide " \
71
+ "any action for it. Add a block with logic that should be " \
72
+ "executed if the ready check passes.\n\n"
73
+ raise Testable::Errors::NoBlockForWhenReady
74
+ end
75
+
37
76
  def retrieve_class(caller)
38
77
  caller[1][/`.*'/][8..-3]
39
78
  end
@@ -41,5 +80,9 @@ module Testable
41
80
  def retrieve_method(caller)
42
81
  caller[0][/`.*'/][1..-2]
43
82
  end
83
+
84
+ def empty_locator(locator, values)
85
+ locator[0].nil? && values.empty? && !values[0].is_a?(Hash)
86
+ end
44
87
  end
45
88
  end
@@ -1,18 +1,24 @@
1
1
  module Testable
2
- VERSION = "0.2.0".freeze
3
-
4
2
  module_function
5
3
 
4
+ VERSION = "0.3.0".freeze
5
+
6
6
  def version
7
7
  """
8
8
  Testable v#{Testable::VERSION}
9
- Capybara: #{Gem.loaded_specs['capybara'].version}
10
- selenium-webdriver: #{Gem.loaded_specs['selenium-webdriver'].version}
9
+ watir: #{gem_version('watir')}
10
+ selenium-webdriver: #{gem_version('selenium-webdriver')}
11
11
  """
12
12
  end
13
13
 
14
14
  def dependencies
15
- Gem.loaded_specs.values.map { |x| "#{x.name} #{x.version}\n" }
15
+ Gem.loaded_specs.values.map { |spec| "#{spec.name} #{spec.version}\n" }
16
16
  .uniq.sort.join(",").split(",")
17
17
  end
18
+
19
+ def gem_version(name)
20
+ Gem.loaded_specs[name].version
21
+ rescue NoMethodError
22
+ puts "No gem loaded for #{name}."
23
+ end
18
24
  end
data/lib/testable.rb CHANGED
@@ -1,17 +1,43 @@
1
- require "capybara"
2
- require "selenium-webdriver"
3
-
4
1
  require "testable/version"
5
- require "testable/element"
2
+
3
+ require "testable/ready"
6
4
  require "testable/factory"
5
+ require "testable/element"
7
6
  require "testable/interface"
7
+ require "testable/dom_update"
8
+ require "testable/data_setter"
8
9
 
9
- module Testable
10
- module_function
10
+ require "watir"
11
+ require "selenium-webdriver"
11
12
 
12
- def included(caller)
13
+ module Testable
14
+ def self.included(caller)
13
15
  caller.extend Testable::Element
14
16
  caller.extend Testable::Interface::Page::Attribute
17
+ caller.__send__ :include, Testable::Ready
18
+ caller.__send__ :include, Testable::DataSetter
15
19
  caller.__send__ :include, Testable::Interface::Page
20
+ caller.__send__ :include, Testable::Element::Locator
21
+ end
22
+
23
+ def initialize(browser = nil, &block)
24
+ @browser = Testable.browser unless Testable.browser.nil?
25
+ @browser = browser if Testable.browser.nil?
26
+ instance_eval(&block) if block
27
+ end
28
+
29
+ attr_accessor :browser
30
+
31
+ class << self
32
+ attr_accessor :browser
33
+
34
+ def set_browser(app = :chrome, *args)
35
+ @browser = Watir::Browser.new(app, *args)
36
+ Testable.browser = @browser
37
+ end
38
+
39
+ def quit_browser
40
+ @browser.quit
41
+ end
16
42
  end
17
43
  end
data/testable.gemspec CHANGED
@@ -27,8 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency "rubocop"
28
28
  spec.add_development_dependency "pry"
29
29
 
30
- spec.add_runtime_dependency "capybara", [">= 2.10", "< 3.0"]
31
- spec.add_runtime_dependency "selenium-webdriver", "~> 3.0"
30
+ spec.add_runtime_dependency "watir", "~> 6.0"
32
31
 
33
32
  spec.post_install_message = %{
34
33
  (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)
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.2.0
4
+ version: 0.3.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: 2016-11-12 00:00:00.000000000 Z
11
+ date: 2016-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -81,39 +81,19 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: capybara
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '2.10'
90
- - - "<"
91
- - !ruby/object:Gem::Version
92
- version: '3.0'
93
- type: :runtime
94
- prerelease: false
95
- version_requirements: !ruby/object:Gem::Requirement
96
- requirements:
97
- - - ">="
98
- - !ruby/object:Gem::Version
99
- version: '2.10'
100
- - - "<"
101
- - !ruby/object:Gem::Version
102
- version: '3.0'
103
- - !ruby/object:Gem::Dependency
104
- name: selenium-webdriver
84
+ name: watir
105
85
  requirement: !ruby/object:Gem::Requirement
106
86
  requirements:
107
87
  - - "~>"
108
88
  - !ruby/object:Gem::Version
109
- version: '3.0'
89
+ version: '6.0'
110
90
  type: :runtime
111
91
  prerelease: false
112
92
  version_requirements: !ruby/object:Gem::Requirement
113
93
  requirements:
114
94
  - - "~>"
115
95
  - !ruby/object:Gem::Version
116
- version: '3.0'
96
+ version: '6.0'
117
97
  description: Provides a semantic DSL to construct fluent interfaces for test execution
118
98
  logic.
119
99
  email:
@@ -126,7 +106,6 @@ files:
126
106
  - ".hound.yml"
127
107
  - ".rspec"
128
108
  - ".rubocop.yml"
129
- - ".travis.yml"
130
109
  - CODE_OF_CONDUCT.md
131
110
  - Gemfile
132
111
  - LICENSE.txt
@@ -134,11 +113,16 @@ files:
134
113
  - Rakefile
135
114
  - bin/console
136
115
  - bin/setup
116
+ - circle.yml
137
117
  - lib/testable.rb
118
+ - lib/testable/data_setter.rb
119
+ - lib/testable/dom_update.rb
138
120
  - lib/testable/element.rb
139
121
  - lib/testable/errors.rb
122
+ - lib/testable/extensions/dom_observer.js
140
123
  - lib/testable/factory.rb
141
124
  - lib/testable/interface.rb
125
+ - lib/testable/ready.rb
142
126
  - lib/testable/situation.rb
143
127
  - lib/testable/version.rb
144
128
  - testable.gemspec
@@ -147,7 +131,7 @@ licenses:
147
131
  - MIT
148
132
  metadata: {}
149
133
  post_install_message: "\n(::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)\n
150
- \ Testable 0.2.0 has been installed.\n(::) (::) (::) (::) (::) (::) (::) (::) (::)
134
+ \ Testable 0.3.0 has been installed.\n(::) (::) (::) (::) (::) (::) (::) (::) (::)
151
135
  (::) (::) (::)\n "
152
136
  rdoc_options: []
153
137
  require_paths:
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.3.1
5
- before_install: gem install bundler -v 1.13.6