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,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4b481e8e7cbdaf5306f466442b34746e75b94ae25f0b8c0df4f0f9f1f697ae9d
4
+ data.tar.gz: '008940290200527c011ec2a629282582943dfb447e0731dfe91c28839844d250'
5
+ SHA512:
6
+ metadata.gz: 9991f261a20cfc93bed0f58be805beaa0ffd5c6bba33509eac520a334f0ea4efe008005a1b98ef444636785036ecf13ba0bf60a2b2f1282885cdc24b1f3f9e58
7
+ data.tar.gz: cc1cec085b651ed9611f3dadc3094143d3b73081e7449e00942954bee77f5d43ae9ec5f5cc3baeba16fe61e048c3d91f6066972cfbc85106739404e25644cfd9
@@ -0,0 +1,191 @@
1
+ # isomorfeus-puppetmaster
2
+
3
+ A framework for acceptance tests or simply running tests in a headless browser.
4
+ Comes with drivers for chromium headless, firefox and jsdom.
5
+
6
+ ## Chat
7
+ At our [Gitter Isomorfeus Lobby](http://gitter.im/isomorfeus/Lobby)
8
+
9
+ ## Running on:
10
+ - [CircleCI](https://circleci.com): [![CircleCI](https://circleci.com/gh/isomorfeus/isomorfeus-puppetmaster/tree/master.svg?style=svg)](https://circleci.com/gh/isomorfeus/isomorfeus-puppetmaster/tree/master)
11
+ - [SemaphoreCI 2.0](https://semaphoreci.com): (no badge yet, and i dont know how to link to public 2.0 repos)
12
+ - [TravisCI](https://travis-ci.org): [![Build Status](https://travis-ci.org/isomorfeus/isomorfeus-puppetmaster.svg?branch=master)](https://travis-ci.org/isomorfeus/isomorfeus-puppetmaster)
13
+
14
+ ## Installation
15
+
16
+ In Gemfile:
17
+ `gem 'isomorfeus-puppetmaster'`, then `bundle install`
18
+
19
+ Also requires the following npm modules with recommended versions:
20
+
21
+ - puppeteer 1.13.0 - for the chromium driver
22
+ - puppeteer-firefox 0.5.0 - for the firefox driver
23
+ - jsdom 14.0.0 - for the jsdom driver
24
+ - canvas 2.4.1 - for the jsdom driver
25
+
26
+ Simply install them in your projects root. Puppetmaster also depends on isomorfeus-speednode, which will be installed automatically.
27
+ Speednode will pickup the node modules then from the projects root node_modules directory.
28
+
29
+ ## Configuration
30
+
31
+ Puppetmaster provides these drivers:
32
+ - chromium - a real browser, headless, fast
33
+ - chromium_debug - opens a chromium browser window with devtools enabled, useful for debugging tests
34
+ - firefox - real firefox, running headless, not so fast
35
+ - firefox_debug - opens a firefox browser window with devtools enabled, useful for debugging tests
36
+ - jsdom - provides a dom implementation in javascript, can execute javascript in the document, super fast, headless, has certain limitations,
37
+ especially because its not rendering anything (no element bounding box, etc.)
38
+
39
+ Selecting a driver, for example jsdom:
40
+ ```ruby
41
+ Isomorfeus::Puppetmaster.driver = :jsdom
42
+ ```
43
+ (chromium is the default driver)
44
+
45
+
46
+ Getting the app ready and running:
47
+ 1. Assign a rack app
48
+ 2. Boot the app
49
+
50
+ For Example:
51
+ ````ruby
52
+ Isomorfeus::Puppetmaster.app = TestApp
53
+ Isomorfeus::Puppetmaster.boot_app
54
+ ````
55
+
56
+ Include the Puppetmaster DSL in the spec_helper.rb:
57
+ ```ruby
58
+ RSpec.configure do |config|
59
+ config.include Isomorfeus::Puppetmaster::DSL
60
+ end
61
+ ```
62
+ Ready to play!
63
+
64
+ ## Terminology
65
+
66
+ There are Browsers which may have windows or tabs, which translate to targets or pages which may contain documents or frames which consist of elements or nodes.
67
+ In Puppetmaster, which is focusing on headless testing, the main "thing" is **just a document**. Its possible to open many documents at once, but what exactly a document is contained in,
68
+ a window or tab or browser or target or whatever, is not of interest.
69
+ A document consists of nodes. Simply working with just documents and nodes makes testing a lot simpler.
70
+ ```
71
+ document
72
+ |____head (is a node)
73
+ | |
74
+ | |___more nodes
75
+ | |___...
76
+ |
77
+ |____body (is a node)
78
+ |
79
+ |___more nodes
80
+ |___...
81
+ ```
82
+
83
+ ## Basic usage pattern
84
+
85
+ 1. Find something
86
+ 2. Interact with it
87
+
88
+ ## Documents
89
+ The drivers open a empty default document, to use that and go to the apps root page:
90
+ ```ruby
91
+ doc = visit('/')
92
+ ```
93
+ This provides the document `doc` which can further be used to interact with.
94
+ To go to another location, call visit or goto on that doc:
95
+ ```ruby
96
+ doc.visit('/login') # or
97
+ doc.goto('/login')
98
+ ```
99
+
100
+ To open another document:
101
+ ```ruby
102
+ doc2 = open_new_document('/play')
103
+ ```
104
+
105
+ Both open documents can then be independently interacted with:
106
+ ```ruby
107
+ doc.visit('/go')
108
+ doc2.goto('/location')
109
+ ```
110
+
111
+ ### Documents and Javascript
112
+
113
+ #### Executing Javascript
114
+
115
+ TODO
116
+
117
+ #### DOM changes by Javascript, Timing
118
+
119
+ TODO
120
+
121
+ ## Nodes
122
+
123
+ ### Finding Nodes ...
124
+
125
+ Nodes can be found by CSS selectors or XPath queries.
126
+ Node can be found from the document or or other nodes as root.
127
+
128
+ #### ... with CSS selectors
129
+
130
+ Documents and nodes provide methods for finding single nodes or a group of nodes with CSS selectors. Examples:
131
+ Find a single node in a document:
132
+ ```ruby
133
+ node1 = doc.find('div') # finds the first div in the document
134
+ node2 = doc.find('#super') # find node with id="super"
135
+ ```
136
+ Find a single from another node:
137
+ ```ruby
138
+ node3 = node1.find('div') # find first div within the node
139
+ ```
140
+
141
+ Find multiple nodes in a document or from a node:
142
+ ```ruby
143
+ node_array1 = doc.find_all('div') # finds all div's in a document
144
+ node_array2 = node2.find_all('div') # find all div's within node2
145
+ ```
146
+
147
+ #### ... with XPath queries
148
+
149
+ Documents and nodes provide methods for finding single nodes or a group of nodes with Xpath queries. Examples:
150
+ Find a single node in a document:
151
+ ```ruby
152
+ node1 = doc.find_xpath('//div') # finds the first div in the document
153
+ node2 = doc.find_xpath("//div[contains(@id,'super')]") # find div with id="super"
154
+ ```
155
+ Find a single from another node:
156
+ ```ruby
157
+ node3 = node1.find_xpath('//div') # find first div within the node
158
+ ```
159
+
160
+ Find multiple nodes in a document or from a node:
161
+ ```ruby
162
+ node_array1 = doc.find_all_xpath('//div') # finds all div's in a document
163
+ node_array2 = node2.find_all_xpath('//div') # find all div's within node2
164
+ ```
165
+
166
+ ### Interacting with nodes
167
+
168
+ Puppetmaster provides methods for emulating user interactions with nodes or documents.
169
+
170
+ ### Mouse
171
+ ```ruby
172
+ node1.click
173
+ ```
174
+ TODO
175
+
176
+ ### Keyboard
177
+ ```ruby
178
+ node4 = doc.find('input')
179
+ node4.type_keys('Message')
180
+ ```
181
+ TODO
182
+
183
+ ### Fingers
184
+ TODO
185
+
186
+
187
+ ### Tests
188
+ To run tests:
189
+ - clone repo
190
+ - `bundle install`
191
+ - `bundle exec rake`
@@ -0,0 +1,43 @@
1
+ require 'active_support/core_ext/string'
2
+ require 'uri'
3
+ require 'net/http'
4
+ require 'rack'
5
+
6
+ require 'isomorfeus-speednode'
7
+
8
+ # use execjs speednode for sure, unless something else has been specified
9
+ unless ENV["EXECJS_RUNTIME"]
10
+ ExecJS.runtime = ExecJS::Runtimes::Speednode
11
+ end
12
+
13
+ require 'isomorfeus/puppetmaster'
14
+ require 'isomorfeus/puppetmaster/self_forwardable'
15
+ require 'isomorfeus/puppetmaster/errors'
16
+ require 'isomorfeus/puppetmaster/cookie'
17
+ require 'isomorfeus/puppetmaster/console_message'
18
+ require 'isomorfeus/puppetmaster/request'
19
+ require 'isomorfeus/puppetmaster/response'
20
+ require 'isomorfeus/puppetmaster/node'
21
+ require 'isomorfeus/puppetmaster/node/content_editable'
22
+ require 'isomorfeus/puppetmaster/node/input'
23
+ require 'isomorfeus/puppetmaster/node/checkbox'
24
+ require 'isomorfeus/puppetmaster/node/filechooser'
25
+ require 'isomorfeus/puppetmaster/node/radiobutton'
26
+ require 'isomorfeus/puppetmaster/node/select'
27
+ require 'isomorfeus/puppetmaster/node/textarea'
28
+ require 'isomorfeus/puppetmaster/document'
29
+ require 'isomorfeus/puppetmaster/iframe'
30
+ require 'isomorfeus/puppetmaster/driver/puppeteer_document'
31
+ require 'isomorfeus/puppetmaster/driver/puppeteer_node'
32
+ require 'isomorfeus/puppetmaster/driver/puppeteer'
33
+ require 'isomorfeus/puppetmaster/driver/jsdom_document'
34
+ require 'isomorfeus/puppetmaster/driver/jsdom_node'
35
+ require 'isomorfeus/puppetmaster/driver/jsdom'
36
+ require 'isomorfeus/puppetmaster/driver_registration'
37
+
38
+ require 'isomorfeus/puppetmaster/server/middleware'
39
+ require 'isomorfeus/puppetmaster/server/checker'
40
+ require 'isomorfeus/puppetmaster/server'
41
+ require 'isomorfeus/puppetmaster/server_registration'
42
+
43
+ require 'isomorfeus/puppetmaster/dsl'
@@ -0,0 +1,62 @@
1
+ module Isomorfeus
2
+ module Puppetmaster
3
+ class << self
4
+ attr_accessor :app, :server_port, :session
5
+ attr_writer :save_path, :server_host, :server_scheme
6
+ attr_reader :served_app, :server
7
+
8
+ def boot_app
9
+ @served_app = Isomorfeus::Puppetmaster::Server.new(app, port: server_port, host: server_host).boot
10
+ end
11
+
12
+ def driver
13
+ @driver ||= :chromium
14
+ end
15
+
16
+ def driver=(drvr)
17
+ raise "#{drvr}: no such driver registered! Available drivers: #{drivers.keys.join(', ')}." unless drivers.has_key?(drvr)
18
+ @driver = drvr
19
+ end
20
+
21
+ def drivers
22
+ @drivers ||= {}
23
+ end
24
+
25
+ def register_driver(name, &block)
26
+ drivers[name.to_sym] = block
27
+ end
28
+
29
+ def register_server(name, &block)
30
+ servers[name.to_sym] = block
31
+ end
32
+
33
+ def save_path
34
+ @save_path ||= Dir.pwd
35
+ end
36
+
37
+ def server=(name)
38
+ raise "#{name}: no such server registered! Available drivers: #{servers.keys.join(', ')}." unless servers.has_key?(name)
39
+ name, options = *name if name.is_a? Array
40
+ @server = if name.respond_to? :call
41
+ name
42
+ elsif options
43
+ proc { |app, port, host| servers[name.to_sym].call(app, port, host, options) }
44
+ else
45
+ servers[name.to_sym]
46
+ end
47
+ end
48
+
49
+ def server_host
50
+ @server_host ||= '127.0.0.1'
51
+ end
52
+
53
+ def server_scheme
54
+ @server_scheme ||= 'http'
55
+ end
56
+
57
+ def servers
58
+ @servers ||= {}
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,19 @@
1
+ module Isomorfeus
2
+ module Puppetmaster
3
+ class ConsoleMessage
4
+ def initialize(message_hash = {})
5
+ @message_hash = message_hash ? message_hash : {}
6
+ end
7
+
8
+ def method_missing(name, *args)
9
+ if %i[column_number level location line_number text].include?(name)
10
+ @message_hash[name.to_s]
11
+ elsif %i[location url].include?(name)
12
+ @message_hash['location'] ? @message_hash['location']['url'] : ''
13
+ else
14
+ super(name, *args)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,46 @@
1
+ module Isomorfeus
2
+ module Puppetmaster
3
+ class Cookie
4
+ def initialize(attributes)
5
+ @attributes = attributes
6
+ end
7
+
8
+ def ==(other)
9
+ return super unless other.is_a? String
10
+ value == other
11
+ end
12
+
13
+ def domain
14
+ @attributes['domain']
15
+ end
16
+
17
+ def expires
18
+ Time.at @attributes['expires'] unless [nil, 0, -1].include? @attributes['expires']
19
+ end
20
+
21
+ def http_only?
22
+ !!@attributes['httpOnly']
23
+ end
24
+
25
+ def name
26
+ @attributes['name']
27
+ end
28
+
29
+ def value
30
+ @attributes['value']
31
+ end
32
+
33
+ def path
34
+ @attributes['path']
35
+ end
36
+
37
+ def secure?
38
+ !!@attributes['secure']
39
+ end
40
+
41
+ def same_site
42
+ @attributes['sameSite']
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,160 @@
1
+ module Isomorfeus
2
+ module Puppetmaster
3
+ class Document
4
+ extend Isomorfeus::Puppetmaster::SelfForwardable
5
+
6
+ document_forward %i[
7
+ all_text
8
+ accept_alert
9
+ accept_confirm
10
+ accept_leave_page
11
+ accept_prompt
12
+ body
13
+ bring_to_front
14
+ clear_authentication_credentials
15
+ clear_cookies
16
+ clear_extra_headers
17
+ clear_url_blacklist
18
+ click
19
+ close
20
+ cookies
21
+ console
22
+ dismiss_confirm
23
+ dismiss_leave_page
24
+ dismiss_prompt
25
+ dispatch_event
26
+ double_click
27
+ evaluate_script
28
+ execute_script
29
+ find
30
+ find_all
31
+ find_all_xpath
32
+ find_xpath
33
+ head
34
+ html
35
+ open_new_document
36
+ remove_cookie
37
+ render_base64
38
+ reset_user_agent
39
+ right_click
40
+ save_pdf
41
+ save_screenshot
42
+ scroll_by
43
+ scroll_to
44
+ set_authentication_credentials
45
+ set_cookie
46
+ set_extra_headers
47
+ set_url_blacklist
48
+ set_user_agent
49
+ title
50
+ url
51
+ user_agent
52
+ viewport_maximize
53
+ viewport_resize
54
+ viewport_size
55
+ wait_for
56
+ wait_for_xpath
57
+ ]
58
+
59
+ attr_reader :handle, :response
60
+
61
+ def initialize(driver, handle, response)
62
+ @driver = driver
63
+ @handle = handle
64
+ @response = response
65
+ ObjectSpace.define_finalizer(self, @driver.class.document_handle_disposer(@driver, @handle))
66
+ end
67
+
68
+ def browser
69
+ @driver.browser
70
+ end
71
+
72
+ def go_back
73
+ @response = @driver.document_go_back(self)
74
+ self
75
+ end
76
+
77
+ def go_forward
78
+ @response = @driver.document_go_forward(self)
79
+ self
80
+ end
81
+
82
+ def goto(uri)
83
+ @response = @driver.document_goto(self, uri)
84
+ self
85
+ end
86
+ alias_method :visit, :goto
87
+
88
+ def has_content?(content, **options)
89
+ body.has_content?(content, options)
90
+ end
91
+
92
+ def has_css?(selector, **options)
93
+ body.has_css?(selector, options)
94
+ end
95
+
96
+ def has_current_path?(other_path)
97
+ path == other_path
98
+ end
99
+
100
+ def has_text?(text, **options)
101
+ body.has_text?(text, options)
102
+ end
103
+
104
+ def has_xpath?(query, **options)
105
+ body.has_xpath?(query, options)
106
+ end
107
+
108
+ def method_missing(name, *args)
109
+ method_name = name.to_s
110
+ if method_name.start_with?('find_by_')
111
+ what = method_name[8..-1]
112
+ return find("[#{what}=\"#{args.first}\"]") if %w[name type value].include?(what)
113
+ return find_xpath("//*[text()=\"#{args.first}\"]") if what == 'content'
114
+ elsif method_name.start_with?('has_')
115
+ # :has_checked_field?, #
116
+ # :has_content?,
117
+ # :has_css?,
118
+ # :has_field?,
119
+ # :has_link?,
120
+ # :has_select?,
121
+ # :has_selector?,
122
+ # :has_table?,
123
+ # :has_text?,
124
+ # :has_unchecked_field?,
125
+ # :has_xpath?,
126
+ # :has_button?, # method_missing
127
+ end
128
+ super(name, *args)
129
+ end
130
+
131
+ def open_document_by(&block)
132
+ open_documents = @driver.document_handles
133
+ block.call
134
+ new_documents = @driver.document_handles - open_documents
135
+ raise 'Multiple documents opened' if new_documents.size > 1
136
+ raise 'No window opened' if new_documents.size < 1
137
+ Isomorfeus::Puppetmaster::Document.new(@driver, new_documents.first, Isomorfeus::Puppetmaster::Response.new)
138
+ end
139
+
140
+ def path
141
+ URI.parse(url).path
142
+ end
143
+
144
+ def reload
145
+ @response = @driver.reload(self)
146
+ end
147
+
148
+ def respond_to?(name, include_private = false)
149
+ return true if %i[find_by_content find_by_name find_by_type find_by_value].include?(name)
150
+ super(name, include_private)
151
+ end
152
+
153
+ # assertions
154
+ # :assert_current_path,
155
+ # :assert_no_current_path
156
+ # assert_title
157
+ # assert_no_title
158
+ end
159
+ end
160
+ end