rutl 0.1.3 → 0.1.4

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
  SHA256:
3
- metadata.gz: 91f41b74e68fe36acb46dd525f7f375f18d3939d5a7fc15c1972ff443af6125e
4
- data.tar.gz: 1a2d184998b8f3f18532ece3f353059cbd616e8f7a419c4331c8dfc10fc40d59
3
+ metadata.gz: 44ba7a05dd473a09ac45ecafc02d9e11f0a74e26d0e8eb5909e315226066e0ca
4
+ data.tar.gz: 6a272e4fc1afb2d1111b90604ceb3443cc89fd49c703ff417ab56fd9f609573b
5
5
  SHA512:
6
- metadata.gz: 4a91637e9662ede93d7991261f44dde9299d0b04482ab25ce8d952fc2f16e4df210452c120642f816a373f4c565287ee16aa57033d0d3c609b23a07f5a9cd17f
7
- data.tar.gz: 93a535c70dbd92121f461bbcea341c3dea2c9d23ac33ae6f5bdcd2658efe1dacf3feb048012412577d6a5726ecf5ca3ce535b76524a48789f92dffebcdf6e8ac
6
+ metadata.gz: 58227d7b2a1dd426ced4d42a826081ad71b638b6f212a16c5905ef57d0fb28ea4689b0240233dbfbb8d42701460c3bf91354cb088414693047836dcf30fb56ce
7
+ data.tar.gz: c4acf6148162ef28b3f7704ea6885a6acd81b9bfb0dbff26fb87a6294f943fa5a1e6bcdeb3aaa8668bf597fa0d452620a90587aadf6cd71ea3b4670f5f3281a1
data/.gitignore CHANGED
@@ -1,9 +1,10 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
1
+ /.bundle/
2
+ /.yardoc
3
+ /debug.log
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
data/.rubocop.yml CHANGED
@@ -6,7 +6,7 @@ require: rubocop-rspec
6
6
  # There are also a bunch of warnigs spewed about the RSpec cops listed
7
7
  # in the todo file so it looks like we're not playing well with rubocop-rspec
8
8
  # either.
9
- # Check this in for now and TODO: figure this out tomorrow.
9
+ # Check this in for now and TODO: figure this out.
10
10
  require: drewcoo-cops
11
11
 
12
12
  AllCops:
data/.rubocop_todo.yml CHANGED
@@ -1,45 +1,20 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2018-05-26 12:34:26 -0700 using RuboCop version 0.56.0.
3
+ # on 2018-06-03 15:06:48 -0700 using RuboCop version 0.56.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 1
10
- Metrics/AbcSize:
11
- Max: 16
12
-
13
9
  # Offense count: 3
14
10
  # Configuration parameters: CountComments, ExcludedMethods.
15
11
  Metrics/BlockLength:
16
- Max: 48
12
+ Max: 74
17
13
 
18
- # Offense count: 4
14
+ # Offense count: 3
19
15
  # Configuration parameters: CountComments.
20
16
  Metrics/MethodLength:
21
- Max: 19
22
-
23
- # Offense count: 1
24
- Naming/AccessorMethodName:
25
- Exclude:
26
- - 'lib/rutl/interface/null_interface.rb'
27
-
28
- # Offense count: 1
29
- RSpec/ExampleLength:
30
- Exclude:
31
- - 'spec/chrome_interface_spec.rb'
32
-
33
- # Offense count: 2
34
- RSpec/ExpectActual:
35
- Exclude:
36
- - 'spec/null_interface_spec.rb'
37
- - 'spec/rutl_spec.rb'
38
-
39
- # Offense count: 1
40
- RSpec/MultipleExpectations:
41
- Exclude:
42
- - 'spec/chrome_interface_spec.rb'
17
+ Max: 13
43
18
 
44
19
  # Offense count: 4
45
20
  # Cop supports --auto-correct.
@@ -50,39 +25,34 @@ Style/BracesAroundHashParameters:
50
25
  - 'spec/pages/internet_login_page.rb'
51
26
  - 'spec/pages/page1.rb'
52
27
 
53
- # Offense count: 1
28
+ # Offense count: 3
54
29
  Style/ClassVars:
55
30
  Exclude:
56
31
  - 'lib/rutl/base_page.rb'
32
+ - 'lib/rutl/driver/null_driver_page_element.rb'
57
33
 
58
34
  # Offense count: 4
59
- Style/MethodMissingSuper:
60
- Exclude:
61
- - 'lib/rutl/base_page.rb'
62
- - 'lib/rutl/browser.rb'
63
- - 'lib/rutl/interface/base_interface.rb'
64
- - 'spec/spec_helper.rb'
65
-
66
- # Offense count: 1
67
- # Cop supports --auto-correct.
68
- Style/PerlBackrefs:
35
+ # Configuration parameters: AllowedVariables.
36
+ Style/GlobalVars:
69
37
  Exclude:
70
38
  - 'lib/rutl/browser.rb'
39
+ - 'lib/rutl/interface/elements/base_element.rb'
40
+ - 'lib/rutl/interface/elements/element_context.rb'
41
+ - 'lib/rutl/interface/null_interface.rb'
71
42
 
72
43
  # Offense count: 1
73
44
  # Cop supports --auto-correct.
74
- # Configuration parameters: EnforcedStyle.
75
- # SupportedStyles: implicit, explicit
76
- Style/RescueStandardError:
45
+ Style/IfUnlessModifier:
77
46
  Exclude:
78
- - 'lib/rutl/utilities.rb'
47
+ - 'lib/rutl/interface/elements/element_context.rb'
79
48
 
80
- # Offense count: 3
81
- # Cop supports --auto-correct.
82
- # Configuration parameters: AllowIfMethodIsEmpty.
83
- Style/SingleLineMethods:
49
+ # Offense count: 4
50
+ Style/MethodMissingSuper:
84
51
  Exclude:
85
52
  - 'lib/rutl/base_page.rb'
53
+ - 'lib/rutl/browser.rb'
54
+ - 'lib/rutl/interface/base_interface.rb'
55
+ - 'spec/spec_helper.rb'
86
56
 
87
57
  # Offense count: 1
88
58
  # Cop supports --auto-correct.
@@ -91,3 +61,9 @@ Style/SingleLineMethods:
91
61
  Style/TrivialAccessors:
92
62
  Exclude:
93
63
  - 'lib/rutl/base_page.rb'
64
+
65
+ # Offense count: 15
66
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
67
+ # URISchemes: http, https
68
+ Metrics/LineLength:
69
+ Max: 223
data/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![CircleCI](https://circleci.com/gh/drewcoo/rutl.svg?style=shield)](https://circleci.com/gh/drewcoo/rutl)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/drewcoo/rutl/badge.svg?branch=master)](https://coveralls.io/github/drewcoo/rutl?branch=master)
6
6
  [![Gem Version](https://badge.fury.io/rb/rutl.svg)](https://badge.fury.io/rb/rutl)
7
+ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f95d69f6bdd149e697cf63e842f71600)](https://www.codacy.com/app/drewcoo/rutl?utm_source=github.com&utm_medium=referral&utm_content=drewcoo/rutl&utm_campaign=Badge_Grade)
7
8
 
8
9
  This is the Ruby Ui Test Library, or RUTL. Not to be confused with [The Rutles](https://www.rutles.org/).
9
10
 
data/Rakefile CHANGED
@@ -1,6 +1,17 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
3
4
 
4
- RSpec::Core::RakeTask.new(:spec)
5
+ RuboCop::RakeTask.new
5
6
 
6
- task default: :spec
7
+ task :fast_first do
8
+ Rake::Task['spec'].invoke('fast')
9
+ Rake::Task['spec'].reenable
10
+ Rake::Task['spec'].invoke('slow')
11
+ end
12
+
13
+ RSpec::Core::RakeTask.new(:spec, :tag) do |t, args|
14
+ t.rspec_opts = ["--tag #{args[:tag]}"]
15
+ end
16
+
17
+ task default: :fast_first
@@ -9,73 +9,68 @@ class BasePage
9
9
  # BUGBUG: Kludgy. What do I really want to do here?
10
10
  # Make it easy to define a page's default url and
11
11
  # also matchers for page urls for pages with variable urls?
12
- def self.url; @url; end
13
-
14
- def url; self.class.url; end
12
+ def self.url
13
+ @url
14
+ end
15
15
 
16
- @@children = []
17
- def self.children; @@children; end
16
+ def url
17
+ self.class.url
18
+ end
18
19
 
19
- attr_accessor :driver
20
+ @@loaded_pages = []
20
21
 
21
- # TODO: DO I REALLY WANT TO PASS IN DRIVER LIKE THAT?
22
- def initialize
23
- # Call a class's layout method the first time it's loaded
24
- # and put the class name in a list of children, which is a
25
- # list of all actual page objects in this case.
26
- return if @@children.include?(self.class)
22
+ def initialize(interface)
23
+ @interface = interface
24
+ # Dirty trick because we're loading all of page classes from files and then
25
+ # initializing them, calling their layout methods to do magic.
26
+ # The base_page class knows what pages are loaded.
27
+ return if @@loaded_pages.include?(self.class)
27
28
  layout
28
- @@children << self.class
29
+ @@loaded_pages << self.class
29
30
  end
30
31
 
31
- def loaded?
32
- @url = @driver.current_url
32
+ # Written by Browser and only used internally.
33
+ attr_writer :interface
34
+
35
+ def loaded?(driver)
36
+ url == driver.current_url
33
37
  end
34
38
 
35
- # BUGBUG: This creates a new element instance whenever it's called.
36
- # Because of that we can't keep state in any element objects.
37
- # Is that actually a good thing, maybe?
39
+ # Dynamically add a method, :<name> (or :<name>= if setter)
40
+ # to the current class where that method creates an instance
41
+ # of klass.
42
+ # context is an ElementContext
43
+ def add_method(context:, klass:, name:, setter: false)
44
+ name = "#{name}_#{klass.downcase}"
45
+ constant = Module.const_get(klass.capitalize)
46
+ self.class.class_exec do
47
+ if setter
48
+ define_method("#{name}=") do |value|
49
+ constant.new(context, value)
50
+ end
51
+ else
52
+ define_method(name) do
53
+ constant.new(context)
54
+ end
55
+ end
56
+ end
57
+ end
38
58
 
59
+ # This creates a new element instance whenever it's called.
60
+ # Because of that we can't keep state in any element objects.
61
+ # That seems like a good thing, actually.
39
62
  # Called by layout method on pages.
40
63
  def method_missing(element, *args, &_block)
41
- name, selector, rest = args
42
- name = "#{name}_#{element.downcase}"
43
-
64
+ name, selectors, rest = args
65
+ context = ElementContext.new(destinations: rest,
66
+ interface: @interface,
67
+ selectors: selectors)
44
68
  case element
45
69
  when /button/, /checkbox/, /link/
46
- # self.class.class_exec do
47
- # define_method(name) do
48
- # Module.const_get(element.capitalize).new(selector, rest)
49
- # end
50
- # end
51
- self.class.class_exec do
52
- foo = Module.const_get(element.capitalize).new(selector, rest)
53
- define_method(name) do
54
- foo
55
- end
56
- end
70
+ add_method(name: name, context: context, klass: element)
57
71
  when /text/
58
- self.class.class_exec do
59
- # foo = Module.const_get(element.capitalize).new(selector, rest)
60
- # define_method("_#{name}") do
61
- # foo.get
62
- # end
63
- define_method(name) do
64
- Module.const_get(element.capitalize).new(selector, rest)
65
- end
66
- # define_method(name.to_s) do
67
- # foo.get
68
- # end
69
- # define_method((name + '=').to_s) do
70
- # foo.set
71
- # end
72
- # foo.define_method(:get) do
73
- # foo.get
74
- # end
75
- # foo.define_method(:set) do
76
- # foo.set
77
- # end
78
- end
72
+ add_method(name: name, context: context, klass: element)
73
+ add_method(name: name, context: context, klass: element, setter: true)
79
74
  else
80
75
  # TODO: replace with a super call. This is useful for debugging for now.
81
76
  raise "#{element} NOT FOUND WITH ARGS #{args}!!!"
@@ -93,7 +88,7 @@ class BasePage
93
88
  else
94
89
  # I think it's good to raise but change the message.
95
90
  raise 'Drew, you hit this most often when checking current page ' \
96
- 'rather than current page class'
91
+ "rather than current page class:\n\n #{args}"
97
92
  # I think I want to raise instead of returningn false.
98
93
  end
99
94
  end
data/lib/rutl/browser.rb CHANGED
@@ -9,26 +9,30 @@ require 'rutl/base_page'
9
9
  class Browser
10
10
  include Utilities
11
11
 
12
- def initialize(interface_type: :none, page_object_dir: 'spec/pages')
13
- @pages = load_pages(page_object_dir: page_object_dir)
14
- load_interface(type: interface_type)
12
+ attr_reader :interface
13
+
14
+ def initialize(interface_type:, page_object_dir: 'spec/pages')
15
+ # This is kind of evil. Figure out how to ditch the $ variable.
16
+ $browser = self
17
+ @interface = nil
18
+ @interface = load_interface(interface_type)
19
+ @interface.pages = load_pages(dir: page_object_dir)
15
20
  end
16
21
 
17
- def load_interface(type: :none)
18
- pages = @pages
22
+ def load_interface(type)
19
23
  require "rutl/interface/#{type}_interface"
20
24
  klass = "#{type.to_s.capitalize}Interface"
21
- @interface = Object.const_get(klass).new(pages: pages)
22
- @interface.interface = @interface
25
+ Object.const_get(klass).new
23
26
  end
24
27
 
25
28
  # Ugly. Requires files for page objects. Returns array of class names to load.
26
- def require_pages(page_object_dir: 'spec/pages')
29
+ def require_pages(dir: 'spec/pages')
27
30
  names = []
28
- Dir["#{page_object_dir}/*"].each do |file|
31
+ Dir["#{dir}/*"].each do |file|
29
32
  require "rutl/../../#{file}"
30
33
  File.open(file).each do |line|
31
- names << $1 if line =~ /class (.*) < BasePage/
34
+ bingo = line.match(/class (.*) < BasePage/)
35
+ names << bingo[1] if bingo && bingo[1]
32
36
  end
33
37
  end
34
38
  names
@@ -36,26 +40,19 @@ class Browser
36
40
 
37
41
  def load_pages(*)
38
42
  pages = []
39
- names = require_pages
40
- names.each do |klass|
43
+ require_pages.each do |klass|
41
44
  # Don't have @interface set yet.
42
45
  # That would have been the param to new, :interface.
43
- pages << Object.const_get(klass).new
46
+ pages << Object.const_get(klass).new(@interface)
44
47
  end
45
48
  pages
46
49
  end
47
50
 
48
51
  def method_missing(method, *args, &block)
49
- result = if args.empty?
50
- @interface.send(method)
51
- else
52
- @interface.send(method, *args, &block)
53
- end
54
- if result.class == Array && (result[0].class.ancestors.include?(BasePage) ||
55
- result[0].class == Exception)
56
- wait_for_transition(result)
52
+ if args.empty?
53
+ @interface.send(method)
57
54
  else
58
- result
55
+ @interface.send(method, *args, &block)
59
56
  end
60
57
  end
61
58
 
@@ -4,31 +4,35 @@ require 'rutl/driver/null_driver_page_element'
4
4
  # This is at a peer level to the webdrivers but it's for a fake brwoser.
5
5
  #
6
6
  class NullDriver
7
- attr_accessor :interface
7
+ attr_accessor :context
8
+
9
+ def initialize(context)
10
+ raise 'no context' unless context.is_a?(ElementContext)
11
+ @context = context
12
+ end
8
13
 
9
14
  def find_element(type, location)
10
15
  # Return a new one of these so that it can be clicked ar written
11
16
  # to or whatever.
12
- element = NullDriverPageElement.new(type, location)
13
- element.interface = @interface
14
- element
17
+ context = ElementContext.new(interface: @context.interface)
18
+ NullDriverPageElement.new(context, type, location)
15
19
  end
16
20
 
17
21
  # Cheap way to handle browser.navigate.to(url)
18
22
  # TODO: Until I care about the url and then I should ????
19
23
  def navigate
20
- result = NullDriver.new
21
- result.interface = @interface
22
- result
24
+ context = ElementContext.new(interface: @context.interface)
25
+ NullDriver.new(context)
23
26
  end
24
27
 
25
28
  def to(url)
26
- result = @interface.find_page(url)
27
- @interface.set_current_page result
29
+ result = @context.interface.find_page(url)
30
+ @context.interface.current_page = result
28
31
  result.url
29
32
  end
30
33
 
31
34
  def quit
32
- 'quit'
35
+ # Clean out the @@variables.
36
+ NullDriverPageElement.clear_variables
33
37
  end
34
38
  end
@@ -1,40 +1,51 @@
1
+ require 'rutl/interface/elements/element_context'
2
+
1
3
  #
2
4
  # This fakes all page elements when used with the null driver.
3
5
  # It's a dirty way to avoid modeling all of what a driver talks to.
4
6
  #
5
7
  class NullDriverPageElement
6
- attr_accessor :string
7
- attr_reader :selector_type, :selector
8
+ attr_accessor :context
8
9
 
9
- attr_accessor :interface
10
- attr_accessor :destinations
10
+ def self.clear_variables
11
+ @@variables = {}
12
+ end
11
13
 
12
- def initialize(selector_type, selector)
13
- # :css, selector
14
- @selector_type = selector_type
15
- @selector = selector
14
+ def initialize(context, _type, location)
15
+ @@variables ||= {}
16
+ @context = context
17
+ @location = location
16
18
  end
17
19
 
20
+ # @@string is a class variable because this framework creates new instances
21
+ # of each element every time it accesses them. This is good behavior by
22
+ # default because pages could change underneath us.
23
+ # For text fields in the null browser, though, we want to preserve the values
24
+ # across calls, letting us write and then read.
18
25
  def send_keys(string)
19
- @string = string
26
+ init = @@variables[@location] || ''
27
+ @@variables[@location] = init + string
20
28
  end
21
29
 
22
30
  def attribute(attr)
23
31
  case attr.to_sym
24
32
  when :value
25
- @string
33
+ @@variables[@location]
26
34
  else
27
35
  raise ArgumentError, "Attribute unknown: #{attr}"
28
36
  end
29
37
  end
30
38
 
31
- # Return simple strings for checks against the NullDriver instead of
32
- # having to use some heavyweight UI.
33
39
  def clear
34
- 'clear'
40
+ @@variables[@location] = ''
41
+ end
42
+
43
+ def this_css
44
+ self
35
45
  end
36
46
 
37
47
  def click
38
- 'click'
48
+ # nop
49
+ # Called by ClickToChangeStateMixin like Selenium driver.click
39
50
  end
40
51
  end
@@ -7,46 +7,27 @@ require 'rutl/utilities'
7
7
  class BaseInterface
8
8
  include Utilities
9
9
 
10
- def current_url
11
- current_page.url
12
- end
13
-
14
- attr_reader :pages
15
10
  attr_accessor :driver
16
- attr_accessor :interface
11
+ attr_accessor :pages
17
12
 
18
- def initialize(pages:)
19
- @pages = pages
20
- raise 'Child classes must implement @driver.' unless defined? @driver
21
- @pages.each { |p| p.driver = @driver }
13
+ def initialize
14
+ raise 'Child interface class must set @driver.' if @driver.nil?
22
15
  end
23
16
 
24
- def goto(input)
25
- # TODO: KLUDGY. Fix. Modifier if usage bombs here. *shrug*
26
- @driver.interface = @interface if @interface.class.to_s == 'NullInterface'
27
- input = find_page(input) unless input.methods.include?(:url)
28
-
29
- @driver.navigate.to input.url
17
+ def goto(page)
18
+ @driver.navigate.to find_page(page).url
30
19
  end
31
20
 
32
21
  def current_page
33
- raise 'OVERRIDE IN CHILDREN'
22
+ raise 'define in child classes'
34
23
  end
35
24
 
36
25
  def method_missing(method, *args, &block)
37
- result = if args.empty?
38
- current_page.send(method)
39
- else
40
- current_page.send(method, *args, &block)
41
- end
42
- raise interface.to_s if interface.nil?
43
- raise result.to_s unless defined? result # result.interface
44
- begin
45
- result.interface = interface
46
- rescue NoMethodError
47
- raise NoMethodError, "METHOD NOT FOUND: #{method}"
26
+ if args.empty?
27
+ current_page.send(method)
28
+ else
29
+ current_page.send(method, *args, &block)
48
30
  end
49
- result
50
31
  end
51
32
 
52
33
  # TODO: Is this needed? I not only find the page but also make sure the
@@ -54,21 +35,18 @@ class BaseInterface
54
35
  def find_state(target_states)
55
36
  target_states.each do |state|
56
37
  next unless state.url == current_page.url
57
- page = find_page(state, true)
58
- return page if page.loaded?
38
+ page = find_page(state)
39
+ return page if page.loaded?(@driver)
59
40
  end
60
- raise current_page.to_s if current_page.class == InternetLoggedInPage
61
41
  false
62
42
  end
63
43
 
64
- def find_page(page, raise_on_fail = false)
44
+ def find_page(page)
65
45
  @pages.each do |p|
66
- # page is a Page class
67
46
  return p if page?(page) && p.class == page
68
- # or a String, possibly URL
69
47
  return p if String == page.class && page == p.url
70
48
  end
71
- raise "Page \"#{page}\" not found in pages #{@pages}" if raise_on_fail
49
+ raise "Page \"#{page}\" not found in pages #{@pages}"
72
50
  end
73
51
 
74
52
  def wait_for_transition(target_states)
@@ -86,6 +64,7 @@ class BaseInterface
86
64
 
87
65
  def quit
88
66
  @driver.quit
89
- @pages = []
67
+ # Maybe I'm reusing pages from @pages?
68
+ # @pages = []
90
69
  end
91
70
  end
@@ -4,10 +4,8 @@ require 'rutl/interface/base_interface'
4
4
  #
5
5
  # Small interface for Chrome browser.
6
6
  #
7
- # TODO: Probably the current_page() implementation should move up to base.
8
- #
9
7
  class ChromeInterface < BaseInterface
10
- def initialize(pages:)
8
+ def initialize
11
9
  @logged_in = true
12
10
  options = Selenium::WebDriver::Chrome::Options.new
13
11
  options.add_argument('--ignore-certificate-errors')
@@ -25,7 +23,7 @@ class ChromeInterface < BaseInterface
25
23
 
26
24
  def current_page
27
25
  url = @driver.current_url
28
- page = find_page(url, true)
26
+ page = find_page(url)
29
27
  raise "PAGE NOT FOUND: #{url}, PAGES: #{@pages}" unless page
30
28
  page
31
29
  end
@@ -2,27 +2,23 @@
2
2
  # Page elements. Base class.
3
3
  #
4
4
  class BaseElement
5
- attr_accessor :interface
6
- def driver
7
- @interface.driver
8
- end
9
- attr_accessor :selectors
10
- attr_accessor :destinations
11
-
12
- def initialize(selectors = {}, destinations = [])
13
- @selectors = selectors
14
- @destinations = destinations
15
- end
5
+ attr_accessor :context
16
6
 
17
- def find_css(selectors)
18
- result = driver.find_element(:css, selectors[:css])
19
- return result unless NullDriver == result.class
20
- result.destinations = @destinations
21
- result.interface = @interface
22
- result
7
+ def initialize(element_context)
8
+ raise element_context.to_s unless element_context.is_a? ElementContext
9
+ @context = element_context
10
+ # Not sure why, but I'm seeing Chrome fail becase the context interface
11
+ # passed in isn't the same as the browser's interface.
12
+ # This only happens with click test cases, before the click, and
13
+ # only if that case isn't run first.
14
+ # The context we're passed is also an instance from as ChromeInterface,
15
+ # but a different instance.
16
+ #
17
+ # Here's the kludge workaround line:
18
+ @context.interface = $browser.interface
23
19
  end
24
20
 
25
21
  def this_css
26
- find_css(@selectors)
22
+ @context.find_element(:css)
27
23
  end
28
24
  end
@@ -6,8 +6,9 @@
6
6
  module ClickToChangeStateMixin
7
7
  def click
8
8
  this_css.click
9
- interface.wait_for_transition(@destinations)
10
- # TODO: Return what?
11
- @destinations
9
+
10
+ # TODO: Is this part of the instance-stamping problem???
11
+ # returns the page it found
12
+ @context.interface.wait_for_transition(@context.destinations)
12
13
  end
13
14
  end
@@ -0,0 +1,29 @@
1
+ #
2
+ # The context passed around to all elements.
3
+ # What they need to know outside of themselves to function.
4
+ #
5
+ class ElementContext
6
+ attr_accessor :destinations
7
+ attr_accessor :interface
8
+ attr_accessor :selectors
9
+
10
+ def initialize(destinations: nil, interface: nil, selectors: [])
11
+ unless destinations.nil? || destinations.class == Array
12
+ # Should check each destination to make sure it's a Page or a _____, too.
13
+ raise 'destination must be an Array of destinations or nil.'
14
+ end
15
+ @destinations = destinations || []
16
+ unless interface.nil? || interface.class.ancestors.include?(BaseInterface)
17
+ raise "#{interface.class}: #{interface} must be a *Interface class."
18
+ end
19
+ @interface = interface
20
+ @selectors = selectors
21
+ end
22
+
23
+ def find_element(type)
24
+ # @interface.driver.find_element(type, @selectors[type])
25
+ # Should be this, but apparently @interface.driver is being overwritten
26
+ # (or not written to) and it doesn't work. Using $browser does. :-(
27
+ $browser.interface.driver.find_element(type, @selectors[type])
28
+ end
29
+ end
@@ -0,0 +1,66 @@
1
+ #
2
+ # Implement String stuff in a mixin.
3
+ # TODO: Not finished yet. Must be able to
4
+ #
5
+ module StringReaderWriterMixin
6
+ # Override BaseElement's normal initialize method.
7
+ def initialize(element_context, input_value = nil)
8
+ raise element_context.to_s unless element_context.is_a? ElementContext
9
+ @context = element_context
10
+ set input_value unless input_value.nil?
11
+ end
12
+
13
+ # I could cut set() and only foo_text= if I change this.
14
+ # The problem I'm running into is not having the driver in
15
+ # base element to do this_css calls. So I have to change the way
16
+ # drivers are passed into everything or initially have them everywhere,
17
+ # which means rewriting chosen drivers or changing page load.
18
+ # Ick.
19
+ def set(string)
20
+ clear
21
+ this_css.send_keys(string)
22
+ end
23
+ alias text= set
24
+ alias value= set
25
+
26
+ def get
27
+ this_css.attribute(:value)
28
+ end
29
+ alias text get
30
+ alias value get
31
+ alias to_s get
32
+
33
+ def clear
34
+ this_css.clear
35
+ get
36
+ end
37
+
38
+ def eql?(other)
39
+ other == get
40
+ end
41
+ alias == eql?
42
+
43
+ def send_keys(string)
44
+ this_css.send_keys(string)
45
+ get
46
+ end
47
+
48
+ def method_missing(method, *args, &block)
49
+ # RuboCop complains unless I fall back to super here
50
+ # even though that's pretty meaningless. Oh, well, it's harmless.
51
+ super unless get.respond_to?(method)
52
+ if args.empty?
53
+ get.send(method)
54
+ else
55
+ get.send(method, *args, &block)
56
+ end
57
+ end
58
+
59
+ def respond_to_missing?(method, flag)
60
+ get.respond_to?(method, flag)
61
+ end
62
+
63
+ #
64
+ # TODO: Fall through to String methods?
65
+ #
66
+ end
@@ -1,30 +1,10 @@
1
1
  require 'rutl/interface/elements/base_element'
2
+ require 'rutl/interface/elements/string_reader_writer_mixin.rb'
2
3
 
3
4
  #
4
5
  # I'm using the text element for all text-like things. Passowrds, too.
6
+ # TODO: Also have a reader only class with StringReaderMixin for labels?
5
7
  #
6
8
  class Text < BaseElement
7
- def initialize(selectors = {}, destinations = [])
8
- super
9
- end
10
-
11
- def clear
12
- this_css.clear
13
- end
14
-
15
- def text
16
- get
17
- end
18
-
19
- def get
20
- this_css.attribute(:value)
21
- end
22
-
23
- def text=(string)
24
- set(string)
25
- end
26
-
27
- def set(string)
28
- this_css.send_keys(string)
29
- end
9
+ include StringReaderWriterMixin
30
10
  end
@@ -2,3 +2,4 @@ require 'rutl/interface/elements/button'
2
2
  require 'rutl/interface/elements/checkbox'
3
3
  require 'rutl/interface/elements/link'
4
4
  require 'rutl/interface/elements/text'
5
+ require 'rutl/interface/elements/element_context'
@@ -4,10 +4,8 @@ require 'rutl/interface/base_interface'
4
4
  #
5
5
  # Small interface for Chrome browser.
6
6
  #
7
- # TODO: Probably the current_page() implementation should move up to base.
8
- #
9
7
  class FirefoxInterface < BaseInterface
10
- def initialize(pages:)
8
+ def initialize
11
9
  @logged_in = true
12
10
  options = Selenium::WebDriver::Firefox::Options.new
13
11
  options.add_argument('--ignore-certificate-errors')
@@ -20,7 +18,7 @@ class FirefoxInterface < BaseInterface
20
18
 
21
19
  def current_page
22
20
  url = @driver.current_url
23
- page = find_page(url, true)
21
+ page = find_page(url)
24
22
  raise "PAGE NOT FOUND: #{url}, PAGES: #{@pages}" unless page
25
23
  page
26
24
  end
@@ -4,26 +4,28 @@ require 'rutl/interface/base_interface'
4
4
  # Interface-level code for fake browser.
5
5
  #
6
6
  class NullInterface < BaseInterface
7
- @variables = []
8
- attr_accessor :variables
9
-
10
- def initialize(pages:)
11
- @driver = NullDriver.new
12
- @driver.interface = @interface
7
+ def initialize
8
+ context = ElementContext.new(destinations: nil,
9
+ interface: self,
10
+ selectors: [])
11
+ @driver = NullDriver.new(context)
13
12
  super
14
13
  end
15
14
 
16
- def set_current_page(page)
17
- @current_page = page
18
- end
15
+ # The null driver needs to talk to the null interface.
16
+ # Other driver/interface relations are not like this.
17
+ attr_writer :current_page
19
18
 
20
19
  def current_page
21
20
  # Default to @pages.first if not set?
22
- # A browser can alwasy check its current URL but the null driver can't.
23
- @current_page || @pages.first
21
+ # A browser can always check its current URL but the null driver can't.
22
+ @current_page ||= @pages.first
24
23
  end
25
24
 
26
25
  def wait_for_transition(destinations)
27
- @current_page = find_page(destinations.first)
26
+ # TODO: Setting @current page didn't do it beacause that set
27
+ # context.interface.current_page and we wanted this in the browser.
28
+ @current_page = destinations.first.new(self)
29
+ $browser.current_page = @current_page
28
30
  end
29
31
  end
@@ -34,7 +34,8 @@ module Utilities
34
34
 
35
35
  def page?(checkme)
36
36
  checkme.ancestors.include?(BasePage)
37
- rescue # BUGBUG: Didn't have time to find all the things to rescue yet.
37
+ rescue NoMethodError
38
+ # This isn't a even a class. It's no page!
38
39
  false
39
40
  end
40
41
 
data/lib/rutl/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RUTL
2
- VERSION = '0.1.3'.freeze
2
+ VERSION = '0.1.4'.freeze
3
3
  end
data/rutl.gemspec CHANGED
@@ -30,14 +30,14 @@ Gem::Specification.new do |spec|
30
30
  spec.require_paths = ['lib']
31
31
 
32
32
  spec.add_development_dependency 'bundler', '~> 1.15'
33
- spec.add_development_dependency 'coveralls'
34
- spec.add_development_dependency 'drewcoo-cops'
35
- spec.add_development_dependency 'gem-release'
33
+ spec.add_development_dependency 'coveralls', '~> 0.8'
34
+ spec.add_development_dependency 'drewcoo-cops', '~> 0.1'
35
+ spec.add_development_dependency 'gem-release', '~> 1.0'
36
36
  spec.add_development_dependency 'rake', '~> 12.3'
37
37
  spec.add_development_dependency 'rspec', '~> 3.0'
38
38
  spec.add_development_dependency 'rspec_junit_formatter', '~> 0.3'
39
39
  spec.add_development_dependency 'rubocop', '~> 0.55'
40
- spec.add_development_dependency 'rubocop-rspec'
40
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.25'
41
41
  spec.add_development_dependency 'selenium-webdriver', '~> 3.12'
42
42
  spec.add_development_dependency 'webdrivers', '~> 3.0'
43
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rutl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Drew Cooper
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-05-27 00:00:00.000000000 Z
11
+ date: 2018-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -28,44 +28,44 @@ dependencies:
28
28
  name: coveralls
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '0.8'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '0.8'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: drewcoo-cops
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '0.1'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '0.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: gem-release
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '1.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '1.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -126,16 +126,16 @@ dependencies:
126
126
  name: rubocop-rspec
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - ">="
129
+ - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: '0'
131
+ version: '1.25'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - ">="
136
+ - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '0'
138
+ version: '1.25'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: selenium-webdriver
141
141
  requirement: !ruby/object:Gem::Requirement
@@ -197,7 +197,9 @@ files:
197
197
  - lib/rutl/interface/elements/button.rb
198
198
  - lib/rutl/interface/elements/checkbox.rb
199
199
  - lib/rutl/interface/elements/click_to_change_state_mixin.rb
200
+ - lib/rutl/interface/elements/element_context.rb
200
201
  - lib/rutl/interface/elements/link.rb
202
+ - lib/rutl/interface/elements/string_reader_writer_mixin.rb
201
203
  - lib/rutl/interface/elements/text.rb
202
204
  - lib/rutl/interface/firefox_interface.rb
203
205
  - lib/rutl/interface/null_interface.rb