isomorfeus-puppetmaster 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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