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,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