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,19 @@
1
+ Isomorfeus::Puppetmaster.register_driver(:chromium) do |app|
2
+ Isomorfeus::Puppetmaster::Driver::Puppeteer.new(browser_type: :chromium, headless: true, app: app)
3
+ end
4
+
5
+ Isomorfeus::Puppetmaster.register_driver(:chromium_debug) do |app|
6
+ Isomorfeus::Puppetmaster::Driver::Puppeteer.new(browser_type: :chromium, headless: false, devtools: true, app: app)
7
+ end
8
+
9
+ Isomorfeus::Puppetmaster.register_driver(:firefox) do |app|
10
+ Isomorfeus::Puppetmaster::Driver::Puppeteer.new(browser_type: :firefox, headless: true, app: app)
11
+ end
12
+
13
+ Isomorfeus::Puppetmaster.register_driver(:firefox_debug) do |app|
14
+ Isomorfeus::Puppetmaster::Driver::Puppeteer.new(browser_type: :firefox, headless: false, devtools: true, app: app)
15
+ end
16
+
17
+ Isomorfeus::Puppetmaster.register_driver(:jsdom) do |app|
18
+ Isomorfeus::Puppetmaster::Driver::Jsdom.new(app: app)
19
+ end
@@ -0,0 +1,40 @@
1
+ module Isomorfeus
2
+ module Puppetmaster
3
+ module DSL
4
+ def goto(uri)
5
+ puppetmaster_session.document_goto(puppetmaster_default_document, uri)
6
+ @puppetmaster_default_document
7
+ end
8
+
9
+ alias_method :visit, :goto
10
+
11
+ def open_new_document(uri = nil)
12
+ puppetmaster_session.document_open_new_document(uri)
13
+ end
14
+
15
+ def reset_session!
16
+ @@puppetmaster_session = nil
17
+ end
18
+
19
+ private
20
+
21
+ def get_default_document
22
+ doc = puppetmaster_session.default_document
23
+ return doc if doc
24
+ puppetmaster_session.document_open_new_document('about:blank')
25
+ end
26
+
27
+ def puppetmaster_default_document
28
+ @puppetmaster_default_document ||= get_default_document
29
+ end
30
+
31
+ def puppetmaster_session
32
+ @@puppetmaster_session ||= new_session(Isomorfeus::Puppetmaster.served_app)
33
+ end
34
+
35
+ def new_session(app = nil, driver = nil)
36
+ Isomorfeus::Puppetmaster.drivers[driver ? driver : Isomorfeus::Puppetmaster.driver].call(app ? app : Isomorfeus::Puppetmaster.served_app)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Isomorfeus
4
+ module Puppetmaster
5
+ class Error < StandardError; end
6
+ class KeyError < Error; end
7
+ class JavaScriptError < Error; end
8
+ class ExecutionContextError < Error; end
9
+ class CertificateError < Error; end
10
+ class DNSError < Error; end
11
+ class NoModalError < Error; end
12
+ class FrozenInTime < Error; end
13
+ class NotSupported < Error; end
14
+ class DOMException < Error; end
15
+
16
+ class ElementNotFound < Error
17
+ def initialize(selector_query)
18
+ @selector_query = selector_query
19
+ super "Unable to find \"#{@selector_query}\""
20
+ end
21
+ end
22
+
23
+ class ClientError < Error
24
+ attr_reader :response
25
+
26
+ def initialize(response)
27
+ @response = response
28
+ end
29
+ end
30
+
31
+ class FrameNotFound < ClientError
32
+ def name
33
+ response["args"].first
34
+ end
35
+
36
+ def message
37
+ "The frame "#{name}" was not found."
38
+ end
39
+ end
40
+
41
+ class InvalidSelector < ClientError
42
+ def initialize(response, method, selector)
43
+ super(response)
44
+ @method, @selector = method, selector
45
+ end
46
+
47
+ def message
48
+ "Browser raised error trying to find #{@method}: #{@selector.inspect}"
49
+ end
50
+ end
51
+
52
+ class MouseEventFailed < ClientError
53
+ attr_reader :name, :selector, :position
54
+
55
+ def initialize(*)
56
+ super
57
+ data = /\A\w+: (\w+), (.+?), ([\d\.-]+), ([\d\.-]+)/.match(@response)
58
+ @name, @selector = data.values_at(1, 2)
59
+ @position = data.values_at(3, 4).map(&:to_f)
60
+ end
61
+
62
+
63
+ def message
64
+ "Firing a #{name} at coordinates [#{position.join(", ")}] failed. Cuprite detected " \
65
+ "another element with CSS selector \"#{selector}\" at this position. " \
66
+ "It may be overlapping the element you are trying to interact with. " \
67
+ "If you don't care about overlapping elements, try using node.trigger(\"#{name}\")."
68
+ end
69
+ end
70
+
71
+ class NodeError < ClientError
72
+ attr_reader :node
73
+
74
+ def initialize(node, response)
75
+ @node = node
76
+ super(response)
77
+ end
78
+ end
79
+
80
+ class ObsoleteNode < NodeError
81
+ def message
82
+ "The element you are trying to interact with is either not part of the DOM, or is " \
83
+ "not currently visible on the page (perhaps display: none is set). " \
84
+ "It is possible the element has been replaced by another element and you meant to interact with " \
85
+ "the new element. If so you need to do a new find in order to get a reference to the " \
86
+ "new element."
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,17 @@
1
+ module Isomorfeus
2
+ module Puppetmaster
3
+ class Iframe < Isomorfeus::Puppetmaster::Node
4
+
5
+ frame_forward %i[
6
+ body
7
+ focus
8
+ head
9
+ html
10
+ title
11
+ url
12
+ visible_text
13
+ ]
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,241 @@
1
+ module Isomorfeus
2
+ module Puppetmaster
3
+ class Node
4
+ SUPPORTED_HTML_ELEMENTS = %w[
5
+ a abbr address area article aside audio
6
+ b base bdi bdo blockquote body br button
7
+ canvas caption cite code col colgroup
8
+ data datalist dd del details dfn dialog div dl dt
9
+ em embed
10
+ fieldset figcaption figure footer form
11
+ h1 h2 h3 h4 h5 h6 head header hr html
12
+ i iframe img input ins
13
+ kbd
14
+ label legend li link
15
+ main map mark meta meter
16
+ nav noscript
17
+ object ol optgroup option output
18
+ p param picture pre progress
19
+ q
20
+ rp rt rtc ruby
21
+ s samp script section select small source span strong style sub summary sup
22
+ table tbody td template textarea tfoot th thead time title tr track
23
+ u ul
24
+ var video
25
+ wbr
26
+ ].freeze
27
+
28
+ # https://www.w3.org/TR/SVG11/eltindex.html
29
+ # elements listed above not mentioned a second time
30
+ SUPPORTED_SVG_ELEMENTS = %w[
31
+ altGlyph altGlyphDef altGlyphItem animate animateColor animateMotion animateTransform
32
+ circle clipPath color-profile cursor
33
+ defs desc
34
+ ellipse
35
+ feBlend feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting
36
+ feDisplacementMap feDistantLight feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur
37
+ feImage feMerge feMergeNode feMorphology feOffset fePointLight feSpecularLighting
38
+ feSpotLight feTile feTurbulence
39
+ filter font font-face font-face-format font-face-name font-face-src font-face-uri foreignObject
40
+ g glyph glyphRef
41
+ hkern
42
+ image
43
+ line linearGradient
44
+ marker mask metadata missing-glyph mpath
45
+ path pattern polygon polyline
46
+ radialGradient rect
47
+ script set stop style svg switch symbol
48
+ text textPath tref tspan
49
+ use
50
+ view vkern
51
+ ].freeze
52
+ SUPPORTED_HTML_AND_SVG_ELEMENTS = (SUPPORTED_HTML_ELEMENTS + SUPPORTED_SVG_ELEMENTS).freeze
53
+
54
+ extend Isomorfeus::Puppetmaster::SelfForwardable
55
+
56
+ node_forward %i[
57
+ all_text
58
+ click
59
+ disabled?
60
+ dispatch_event
61
+ double_click
62
+ drag_to
63
+ evaluate_script
64
+ execute_script
65
+ find
66
+ find_all
67
+ find_all_xpath
68
+ find_xpath
69
+ focus
70
+ hover
71
+ in_viewport?
72
+ render_base64
73
+ right_click
74
+ save_screenshot
75
+ scroll_by
76
+ scroll_into_view
77
+ scroll_to
78
+ style
79
+ source
80
+ type_keys
81
+ visible_text
82
+ visible?
83
+ wait_for
84
+ wait_for_xpath
85
+ ]
86
+
87
+ attr_reader :document, :handle, :css_selector, :tag, :xpath_query
88
+
89
+ SUPPORTED_HTML_ELEMENTS.each do |element|
90
+ is_name = element == 'a' ? :is_link? : "is_#{element}?".to_sym
91
+ define_method(is_name) do
92
+ @tag == element
93
+ end
94
+ end
95
+
96
+ def self.new_by_tag(driver, document, node_data)
97
+ tag = node_data[:tag] || node_data['tag']
98
+ case tag
99
+ when 'iframe' then Isomorfeus::Puppetmaster::Iframe.new(driver, document, node_data)
100
+ when 'input'
101
+ type = node_data[:type] || node_data['type']
102
+ case type
103
+ when 'checkbox' then Isomorfeus::Puppetmaster::Checkbox.new(driver, document, node_data)
104
+ when 'filechooser' then Isomorfeus::Puppetmaster::Filechooser.new(driver, document, node_data)
105
+ when 'radiobutton' then Isomorfeus::Puppetmaster::Radiobutton.new(driver, document, node_data)
106
+ when 'select' then Isomorfeus::Puppetmaster::Select.new(driver, document, node_data)
107
+ else
108
+ Isomorfeus::Puppetmaster::Input.new(driver, document, node_data)
109
+ end
110
+ when 'textarea' then Isomorfeus::Puppetmaster::Textarea.new(driver, document, node_data)
111
+ else
112
+ content_editable = node_data[:content_editable] || node_data['content_editable']
113
+ if content_editable
114
+ Isomorfeus::Puppetmaster::ContentEditable.new(driver, document, node_data)
115
+ else
116
+ Isomorfeus::Puppetmaster::Node.new(driver, document, node_data)
117
+ end
118
+ end
119
+ end
120
+
121
+ def initialize(driver, document, node_data)
122
+ @css_selector = node_data[:css_selector] || node_data['css_selector']
123
+ @document = document
124
+ @driver = driver
125
+ @handle = node_data[:handle] || node_data['handle']
126
+ @tag = node_data[:tag] || node_data['tag']
127
+ @type = node_data[:type] || node_data['type']
128
+ @xpath_query = node_data[:xpath_query] || node_data['xpath_query']
129
+ ObjectSpace.define_finalizer(self, @driver.class.node_handle_disposer(@driver, @element_handle))
130
+ end
131
+
132
+ def [](attribute)
133
+ get_attribute(attribute)
134
+ end
135
+
136
+ def ==(other)
137
+ @driver.node_equal(self, other)
138
+ end
139
+
140
+ def get_attribute(attribute)
141
+ attribute = attribute.to_s
142
+ if !(attribute.start_with?('aria-') || attribute.start_with?('data-'))
143
+ attribute = attribute.camelize(:lower)
144
+ end
145
+ @driver.node_get_attribute(self, attribute)
146
+ end
147
+
148
+ def get_property(property)
149
+ property = property.to_s.camelize(:lower)
150
+ @driver.node_get_property(self, property)
151
+ end
152
+
153
+ def has_content?(content, **options)
154
+ visible_text.include?(content)
155
+ end
156
+
157
+ def has_css?(selector, **options)
158
+ res = find_all(selector)
159
+ return false unless res
160
+ return false if options.has_key?(:count) && options[:count] != res.size
161
+ return true
162
+ end
163
+
164
+ def has_text?(text, **options)
165
+ count = visible_text.scan(/#{text}/).size
166
+ return false if options.has_key?(:count) && options[:count] != count
167
+ count > 0
168
+ end
169
+
170
+ def has_xpath?(query, **options)
171
+ res = find_all_xpath(query)
172
+ return false unless res
173
+ return false if options.has_key?(:count) && options[:count] != res.size
174
+ return true
175
+ end
176
+
177
+ def html
178
+ get_property(:outerHTML)
179
+ end
180
+
181
+ def inner_html
182
+ get_property(:innerHTML)
183
+ end
184
+
185
+ def method_missing(name, *args)
186
+ method_name = name.to_s
187
+ if method_name.start_with?('find_by_')
188
+ what = method_name[8..-1]
189
+ return find("[#{what}=\"#{args.first}\"]") if %w[name type value].include?(what)
190
+ return find_xpath("//*[text()=\"#{args.first}\"]") if what == 'content'
191
+ # elsif method_name.start_with?('has_')
192
+ # :has_checked_field?, #
193
+ # :has_content?,
194
+ # :has_css?,
195
+ # :has_field?,
196
+ # :has_link?,
197
+ # :has_select?,
198
+ # :has_selector?,
199
+ # :has_table?,
200
+ # :has_text?,
201
+ # :has_unchecked_field?,
202
+ # :has_xpath?,
203
+ # :has_button?, # method_missing
204
+ end
205
+ super(name, *args)
206
+ end
207
+
208
+ def open_document_by(&block)
209
+ open_documents = @driver.document_handles
210
+ block.call
211
+ new_documents = @driver.document_handles - open_documents
212
+ raise 'Multiple documents opened' if new_documents.size > 1
213
+ Isomorfeus::Puppetmaster::Document.new(@driver, new_documents.first, Isomorfeus::Puppetmaster::Response.new)
214
+ end
215
+
216
+ def parents
217
+ find_all_xpath('./ancestor::*').reverse
218
+ end
219
+
220
+ def respond_to?(name, include_private = false)
221
+ return true if %i[find_by_content find_by_name find_by_type find_by_value].include?(name)
222
+ super(name, include_private)
223
+ end
224
+
225
+ def within(&block)
226
+ instance_exec(&block)
227
+ end
228
+
229
+ # assertions
230
+ # # probably can keep
231
+ # :assert_all_of_selectors,
232
+ # :assert_any_of_selectors,
233
+ # :assert_selector,
234
+ # :assert_text,
235
+ # :assert_no_selector,
236
+ # :assert_none_of_selectors,
237
+ # :assert_no_text,
238
+ # :refute_selector
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,17 @@
1
+ module Isomorfeus
2
+ module Puppetmaster
3
+ class Checkbox < Isomorfeus::Puppetmaster::Input
4
+ def check
5
+ @driver.check(self)
6
+ end
7
+
8
+ def checked?
9
+ !!self[:checked]
10
+ end
11
+
12
+ def uncheck
13
+ @driver.uncheck(self)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module Isomorfeus
2
+ module Puppetmaster
3
+ class ContentEditable < Isomorfeus::Puppetmaster::Node
4
+ node_forward %i[
5
+ disabled?
6
+ value
7
+ value=
8
+ ]
9
+
10
+ def readonly?
11
+ !!self[:readOnly]
12
+ end
13
+
14
+ alias_method :text, :value
15
+ alias_method :text=, :value=
16
+ end
17
+ end
18
+ end