isomorfeus-puppetmaster 0.1.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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +191 -0
  3. data/lib/isomorfeus-puppetmaster.rb +43 -0
  4. data/lib/isomorfeus/puppetmaster.rb +62 -0
  5. data/lib/isomorfeus/puppetmaster/console_message.rb +19 -0
  6. data/lib/isomorfeus/puppetmaster/cookie.rb +46 -0
  7. data/lib/isomorfeus/puppetmaster/document.rb +160 -0
  8. data/lib/isomorfeus/puppetmaster/driver/jsdom.rb +370 -0
  9. data/lib/isomorfeus/puppetmaster/driver/jsdom_document.rb +908 -0
  10. data/lib/isomorfeus/puppetmaster/driver/jsdom_node.rb +836 -0
  11. data/lib/isomorfeus/puppetmaster/driver/puppeteer.rb +401 -0
  12. data/lib/isomorfeus/puppetmaster/driver/puppeteer_document.rb +944 -0
  13. data/lib/isomorfeus/puppetmaster/driver/puppeteer_node.rb +866 -0
  14. data/lib/isomorfeus/puppetmaster/driver_registration.rb +19 -0
  15. data/lib/isomorfeus/puppetmaster/dsl.rb +40 -0
  16. data/lib/isomorfeus/puppetmaster/errors.rb +90 -0
  17. data/lib/isomorfeus/puppetmaster/iframe.rb +17 -0
  18. data/lib/isomorfeus/puppetmaster/node.rb +241 -0
  19. data/lib/isomorfeus/puppetmaster/node/checkbox.rb +17 -0
  20. data/lib/isomorfeus/puppetmaster/node/content_editable.rb +18 -0
  21. data/lib/isomorfeus/puppetmaster/node/filechooser.rb +9 -0
  22. data/lib/isomorfeus/puppetmaster/node/input.rb +21 -0
  23. data/lib/isomorfeus/puppetmaster/node/radiobutton.rb +13 -0
  24. data/lib/isomorfeus/puppetmaster/node/select.rb +36 -0
  25. data/lib/isomorfeus/puppetmaster/node/textarea.rb +7 -0
  26. data/lib/isomorfeus/puppetmaster/request.rb +17 -0
  27. data/lib/isomorfeus/puppetmaster/response.rb +26 -0
  28. data/lib/isomorfeus/puppetmaster/rspec/features.rb +23 -0
  29. data/lib/isomorfeus/puppetmaster/rspec/matcher_proxies.rb +80 -0
  30. data/lib/isomorfeus/puppetmaster/rspec/matchers.rb +164 -0
  31. data/lib/isomorfeus/puppetmaster/rspec/matchers/base.rb +98 -0
  32. data/lib/isomorfeus/puppetmaster/rspec/matchers/become_closed.rb +33 -0
  33. data/lib/isomorfeus/puppetmaster/rspec/matchers/compound.rb +88 -0
  34. data/lib/isomorfeus/puppetmaster/rspec/matchers/have_current_path.rb +29 -0
  35. data/lib/isomorfeus/puppetmaster/rspec/matchers/have_selector.rb +69 -0
  36. data/lib/isomorfeus/puppetmaster/rspec/matchers/have_text.rb +33 -0
  37. data/lib/isomorfeus/puppetmaster/rspec/matchers/have_title.rb +29 -0
  38. data/lib/isomorfeus/puppetmaster/rspec/matchers/match_selector.rb +27 -0
  39. data/lib/isomorfeus/puppetmaster/rspec/matchers/match_style.rb +38 -0
  40. data/lib/isomorfeus/puppetmaster/self_forwardable.rb +31 -0
  41. data/lib/isomorfeus/puppetmaster/server.rb +128 -0
  42. data/lib/isomorfeus/puppetmaster/server/checker.rb +40 -0
  43. data/lib/isomorfeus/puppetmaster/server/middleware.rb +60 -0
  44. data/lib/isomorfeus/puppetmaster/server_registration.rb +37 -0
  45. data/lib/isomorfeus/puppetmaster/version.rb +3 -0
  46. metadata +282 -0
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ module RSpecMatchers
5
+ module Matchers
6
+ class BecomeClosed
7
+ def initialize(options)
8
+ @options = options
9
+ end
10
+
11
+ def matches?(window)
12
+ @window = window
13
+ @wait_time = Capybara::Queries::BaseQuery.wait(@options, window.session.config.default_max_wait_time)
14
+ timer = Capybara::Helpers.timer(expire_in: @wait_time)
15
+ while window.exists?
16
+ return false if timer.expired?
17
+
18
+ sleep 0.01
19
+ end
20
+ true
21
+ end
22
+
23
+ def failure_message
24
+ "expected #{@window.inspect} to become closed after #{@wait_time} seconds"
25
+ end
26
+
27
+ def failure_message_when_negated
28
+ "expected #{@window.inspect} not to become closed after #{@wait_time} seconds"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(::RSpec::Expectations::Version)
4
+ module Capybara
5
+ module RSpecMatchers
6
+ module Matchers
7
+ module Compound
8
+ include ::RSpec::Matchers::Composable
9
+
10
+ def and(matcher)
11
+ And.new(self, matcher)
12
+ end
13
+
14
+ def and_then(matcher)
15
+ ::RSpec::Matchers::BuiltIn::Compound::And.new(self, matcher)
16
+ end
17
+
18
+ def or(matcher)
19
+ Or.new(self, matcher)
20
+ end
21
+
22
+ class CapybaraEvaluator
23
+ def initialize(actual)
24
+ @actual = actual
25
+ @match_results = Hash.new { |hsh, matcher| hsh[matcher] = matcher.matches?(@actual) }
26
+ end
27
+
28
+ def matcher_matches?(matcher)
29
+ @match_results[matcher]
30
+ end
31
+
32
+ def reset
33
+ @match_results.clear
34
+ end
35
+ end
36
+
37
+ # @api private
38
+ module Synchronizer
39
+ def match(_expected, actual)
40
+ @evaluator = CapybaraEvaluator.new(actual)
41
+ syncer = sync_element(actual)
42
+ begin
43
+ syncer.synchronize do
44
+ @evaluator.reset
45
+ raise ::Capybara::ElementNotFound unless synchronized_match?
46
+
47
+ true
48
+ end
49
+ rescue StandardError
50
+ false
51
+ end
52
+ end
53
+
54
+ def sync_element(el)
55
+ if el.respond_to? :synchronize
56
+ el
57
+ elsif el.respond_to? :current_scope
58
+ el.current_scope
59
+ else
60
+ Capybara.string(el)
61
+ end
62
+ end
63
+ end
64
+
65
+ class And < ::RSpec::Matchers::BuiltIn::Compound::And
66
+ include Synchronizer
67
+
68
+ private
69
+
70
+ def synchronized_match?
71
+ [matcher_1_matches?, matcher_2_matches?].all?
72
+ end
73
+ end
74
+
75
+ class Or < ::RSpec::Matchers::BuiltIn::Compound::Or
76
+ include Synchronizer
77
+
78
+ private
79
+
80
+ def synchronized_match?
81
+ [matcher_1_matches?, matcher_2_matches?].any?
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/rspec/matchers/base'
4
+
5
+ module Capybara
6
+ module RSpecMatchers
7
+ module Matchers
8
+ class HaveCurrentPath < WrappedElementMatcher
9
+ def element_matches?(el)
10
+ el.assert_current_path(*@args)
11
+ end
12
+
13
+ def element_does_not_match?(el)
14
+ el.assert_no_current_path(*@args)
15
+ end
16
+
17
+ def description
18
+ "have current path #{current_path.inspect}"
19
+ end
20
+
21
+ private
22
+
23
+ def current_path
24
+ @args.first
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/rspec/matchers/base'
4
+
5
+ module Capybara
6
+ module RSpecMatchers
7
+ module Matchers
8
+ class HaveSelector < WrappedElementMatcher
9
+ def element_matches?(el)
10
+ el.assert_selector(*@args, &@filter_block)
11
+ end
12
+
13
+ def element_does_not_match?(el)
14
+ el.assert_no_selector(*@args, &@filter_block)
15
+ end
16
+
17
+ def description
18
+ "have #{query.description}"
19
+ end
20
+
21
+ def query
22
+ @query ||= Capybara::Queries::SelectorQuery.new(*session_query_args, &@filter_block)
23
+ end
24
+ end
25
+
26
+ class HaveAllSelectors < WrappedElementMatcher
27
+ def element_matches?(el)
28
+ el.assert_all_of_selectors(*@args, &@filter_block)
29
+ end
30
+
31
+ def does_not_match?(_actual)
32
+ raise ArgumentError, 'The have_all_selectors matcher does not support use with not_to/should_not'
33
+ end
34
+
35
+ def description
36
+ 'have all selectors'
37
+ end
38
+ end
39
+
40
+ class HaveNoSelectors < WrappedElementMatcher
41
+ def element_matches?(el)
42
+ el.assert_none_of_selectors(*@args, &@filter_block)
43
+ end
44
+
45
+ def does_not_match?(_actual)
46
+ raise ArgumentError, 'The have_none_of_selectors matcher does not support use with not_to/should_not'
47
+ end
48
+
49
+ def description
50
+ 'have no selectors'
51
+ end
52
+ end
53
+
54
+ class HaveAnySelectors < WrappedElementMatcher
55
+ def element_matches?(el)
56
+ el.assert_any_of_selectors(*@args, &@filter_block)
57
+ end
58
+
59
+ def does_not_match?(_actual)
60
+ el.assert_none_of_selectors(*@args, &@filter_block)
61
+ end
62
+
63
+ def description
64
+ 'have any selectors'
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/rspec/matchers/base'
4
+
5
+ module Capybara
6
+ module RSpecMatchers
7
+ module Matchers
8
+ class HaveText < WrappedElementMatcher
9
+ def element_matches?(el)
10
+ el.assert_text(*@args)
11
+ end
12
+
13
+ def element_does_not_match?(el)
14
+ el.assert_no_text(*@args)
15
+ end
16
+
17
+ def description
18
+ "text #{format(text)}"
19
+ end
20
+
21
+ def format(content)
22
+ content.inspect
23
+ end
24
+
25
+ private
26
+
27
+ def text
28
+ @args[0].is_a?(Symbol) ? @args[1] : @args[0]
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/rspec/matchers/base'
4
+
5
+ module Capybara
6
+ module RSpecMatchers
7
+ module Matchers
8
+ class HaveTitle < WrappedElementMatcher
9
+ def element_matches?(el)
10
+ el.assert_title(*@args)
11
+ end
12
+
13
+ def element_does_not_match?(el)
14
+ el.assert_no_title(*@args)
15
+ end
16
+
17
+ def description
18
+ "have title #{title.inspect}"
19
+ end
20
+
21
+ private
22
+
23
+ def title
24
+ @args.first
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/rspec/matchers/have_selector'
4
+
5
+ module Capybara
6
+ module RSpecMatchers
7
+ module Matchers
8
+ class MatchSelector < HaveSelector
9
+ def element_matches?(el)
10
+ el.assert_matches_selector(*@args, &@filter_block)
11
+ end
12
+
13
+ def element_does_not_match?(el)
14
+ el.assert_not_matches_selector(*@args, &@filter_block)
15
+ end
16
+
17
+ def description
18
+ "match #{query.description}"
19
+ end
20
+
21
+ def query
22
+ @query ||= Capybara::Queries::MatchQuery.new(*session_query_args, &@filter_block)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/rspec/matchers/base'
4
+
5
+ module Capybara
6
+ module RSpecMatchers
7
+ module Matchers
8
+ class MatchStyle < WrappedElementMatcher
9
+ def element_matches?(el)
10
+ el.assert_matches_style(*@args)
11
+ end
12
+
13
+ def does_not_match?(_actual)
14
+ raise ArgumentError, 'The match_style matcher does not support use with not_to/should_not'
15
+ end
16
+
17
+ def description
18
+ 'match style'
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ module Capybara
26
+ module RSpecMatchers
27
+ module Matchers
28
+ ##
29
+ # @deprecated
30
+ class HaveStyle < MatchStyle
31
+ def initialize(*args, &filter_block)
32
+ warn 'HaveStyle matcher is deprecated, please use the MatchStyle matcher instead'
33
+ super
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ module Isomorfeus
2
+ module Puppetmaster
3
+ module SelfForwardable
4
+ def self.extended(base)
5
+ base.define_singleton_method(:document_forward) do |methods|
6
+ methods.each do |method|
7
+ define_method(method) do |*args, &block|
8
+ @driver.send("document_#{method}", self, *args, &block)
9
+ end
10
+ end
11
+ end
12
+
13
+ base.define_singleton_method(:frame_forward) do |methods|
14
+ methods.each do |method|
15
+ define_method(method) do |*args|
16
+ @driver.send("frame_#{method}", self, *args)
17
+ end
18
+ end
19
+ end
20
+
21
+ base.define_singleton_method(:node_forward) do |methods|
22
+ methods.each do |method|
23
+ define_method(method) do |*args|
24
+ @driver.send("node_#{method}", self, *args)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,128 @@
1
+ module Isomorfeus
2
+ module Puppetmaster
3
+ class Server
4
+ class Timer
5
+ def initialize(expire_in:)
6
+ @start = current
7
+ @expire_in = expire_in
8
+ end
9
+
10
+ def expired?
11
+ raise Isomorfeus::Puppetmaster::FrozenInTime, 'Time appears to be frozen. Puppetmaster does not work with libraries which freeze time, consider using time travelling instead' if stalled?
12
+
13
+ current - @start >= @expire_in
14
+ end
15
+
16
+ def stalled?
17
+ @start == current
18
+ end
19
+
20
+ private
21
+
22
+ if defined?(Process::CLOCK_MONOTONIC)
23
+ def current; Process.clock_gettime Process::CLOCK_MONOTONIC; end
24
+ else
25
+ def current; Time.now.to_f; end
26
+ end
27
+ end
28
+
29
+ class << self
30
+ def ports
31
+ @ports ||= {}
32
+ end
33
+ end
34
+
35
+ attr_reader :app, :port, :host
36
+
37
+ def initialize(app,
38
+ port: Isomorfeus::Puppetmaster.server_port,
39
+ host: Isomorfeus::Puppetmaster.server_host,
40
+ extra_middleware: [])
41
+ @app = app
42
+ @extra_middleware = extra_middleware
43
+ @server_thread = nil # suppress warnings
44
+ @host = host
45
+ @port = port
46
+ @port ||= Isomorfeus::Puppetmaster::Server.ports[port_key]
47
+ @port ||= find_available_port(host)
48
+ @checker = Isomorfeus::Puppetmaster::Server::Checker.new(@host, @port)
49
+ end
50
+
51
+ def reset_error!
52
+ middleware.clear_error
53
+ end
54
+
55
+ def error
56
+ middleware.error
57
+ end
58
+
59
+ def using_ssl?
60
+ @checker.ssl?
61
+ end
62
+
63
+ def scheme
64
+ using_ssl? ? 'https' : 'http'
65
+ end
66
+
67
+ def responsive?
68
+ return false if @server_thread&.join(0)
69
+
70
+ res = @checker.request { |http| http.get('/__identify__') }
71
+
72
+ return res.body == app.object_id.to_s if res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPRedirection)
73
+ rescue SystemCallError, Net::ReadTimeout, OpenSSL::SSL::SSLError
74
+ false
75
+ end
76
+
77
+ def wait_for_pending_requests
78
+ timer = Isomorfeus::Puppetmaster::Server::Timer.new(expire_in: 60)
79
+ while pending_requests?
80
+ raise 'Requests did not finish in 60 seconds' if timer.expired?
81
+
82
+ sleep 0.01
83
+ end
84
+ end
85
+
86
+ def boot
87
+ unless responsive?
88
+ Isomorfeus::Puppetmaster::Server.ports[port_key] = port
89
+
90
+ @server_thread = Thread.new do
91
+ Isomorfeus::Puppetmaster.server.call(middleware, port, host)
92
+ end
93
+
94
+ timer = Isomorfeus::Puppetmaster::Server::Timer.new(expire_in: 60)
95
+ until responsive?
96
+ raise 'Rack application timed out during boot' if timer.expired?
97
+
98
+ @server_thread.join(0.1)
99
+ end
100
+ end
101
+
102
+ self
103
+ end
104
+
105
+ private
106
+
107
+ def middleware
108
+ @middleware ||= ::Isomorfeus::Puppetmaster::Server::Middleware.new(app, @extra_middleware)
109
+ end
110
+
111
+ def port_key
112
+ app.object_id
113
+ end
114
+
115
+ def pending_requests?
116
+ middleware.pending_requests?
117
+ end
118
+
119
+ def find_available_port(host)
120
+ server = TCPServer.new(host, 0)
121
+ server.addr[1]
122
+ ensure
123
+ server&.close
124
+ end
125
+ end
126
+ end
127
+ end
128
+