kookaburra 0.18.2 → 0.18.3

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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.18.2
1
+ 0.18.3
data/kookaburra.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "kookaburra"
8
- s.version = "0.18.2"
8
+ s.version = "0.18.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["John Wilger", "Sam Livingston-Gray", "Ravi Gadad"]
@@ -41,6 +41,7 @@ Gem::Specification.new do |s|
41
41
  "lib/kookaburra/ui_driver.rb",
42
42
  "lib/kookaburra/ui_driver/ui_component.rb",
43
43
  "lib/kookaburra/utils/active_record_shared_connection.rb",
44
+ "spec/integration/test_a_rack_application_spec.rb",
44
45
  "spec/kookaburra/json_api_driver_spec.rb",
45
46
  "spec/kookaburra/null_browser_spec.rb",
46
47
  "spec/kookaburra/rack_driver_spec.rb",
@@ -48,7 +49,6 @@ Gem::Specification.new do |s|
48
49
  "spec/kookaburra/test_helpers_spec.rb",
49
50
  "spec/kookaburra/ui_driver/ui_component_spec.rb",
50
51
  "spec/kookaburra/ui_driver_spec.rb",
51
- "spec/kookaburra_integration_spec.rb",
52
52
  "spec/kookaburra_spec.rb",
53
53
  "spec/support/shared_examples/it_has_a_dependency_accessor.rb"
54
54
  ]
@@ -7,5 +7,10 @@ class Kookaburra
7
7
  #
8
8
  # @abstract Subclass and provide an API client implementation
9
9
  class APIDriver
10
+ # Returns a new APIDriver.
11
+ #
12
+ # @param args Not actually used, but takes any arguments
13
+ def initialize(*args)
14
+ end
10
15
  end
11
16
  end
@@ -0,0 +1,311 @@
1
+ require 'kookaburra'
2
+ require 'capybara'
3
+ require 'sinatra/base'
4
+ require 'active_support/hash_with_indifferent_access'
5
+
6
+ describe "testing a Rack application with Kookaburra" do
7
+ describe "with an HTML interface" do
8
+ describe "with a JSON API" do
9
+ require 'kookaburra/json_api_driver'
10
+ class MyAPIDriver < Kookaburra::JsonApiDriver
11
+ def create_user(user_data)
12
+ post '/users', user_data
13
+ end
14
+
15
+ def create_widget(widget_data)
16
+ post '/widgets', widget_data
17
+ end
18
+ end
19
+
20
+ class MyGivenDriver < Kookaburra::GivenDriver
21
+ def a_user(name)
22
+ user = {'email' => 'bob@example.com', 'password' => '12345'}
23
+ result = api.create_user(user)
24
+ test_data.users[name] = result
25
+ end
26
+
27
+ def a_widget(name, attributes = {})
28
+ widget = {'name' => 'Foo'}.merge(attributes)
29
+ result = api.create_widget(widget)
30
+ test_data.widgets[name] = result
31
+ end
32
+ end
33
+
34
+ class SignInScreen < Kookaburra::UIDriver::UIComponent
35
+ def component_path
36
+ '/session/new'
37
+ end
38
+
39
+ def component_locator
40
+ '#sign_in_screen'
41
+ end
42
+
43
+ def sign_in(user_data)
44
+ fill_in 'Email:', :with => user_data['email']
45
+ fill_in 'Password:', :with => user_data['password']
46
+ click_button 'Sign In'
47
+ end
48
+ end
49
+
50
+ class WidgetList < Kookaburra::UIDriver::UIComponent
51
+ def component_path
52
+ '/widgets'
53
+ end
54
+
55
+ def component_locator
56
+ '#widget_list'
57
+ end
58
+
59
+ def widgets
60
+ all('.widget_summary').map do |el|
61
+ extract_widget_data(el)
62
+ end
63
+ end
64
+
65
+ def last_widget_created
66
+ element = find('.last_widget.created')
67
+ extract_widget_data(element)
68
+ end
69
+
70
+ def choose_to_create_new_widget
71
+ click_on 'New Widget'
72
+ end
73
+
74
+ def choose_to_delete_widget(widget_data)
75
+ find("#delete_#{widget_data['id']}").click_button('Delete')
76
+ end
77
+
78
+ private
79
+
80
+ def extract_widget_data(element)
81
+ {
82
+ 'id' => element.find('.id').text,
83
+ 'name' => element.find('.name').text
84
+ }
85
+ end
86
+ end
87
+
88
+ class WidgetForm < Kookaburra::UIDriver::UIComponent
89
+ def component_locator
90
+ '#widget_form'
91
+ end
92
+
93
+ def submit(widget_data)
94
+ fill_in 'Name:', :with => widget_data['name']
95
+ click_on 'Save'
96
+ end
97
+ end
98
+
99
+ class MyUIDriver < Kookaburra::UIDriver
100
+ ui_component :sign_in_screen, SignInScreen
101
+ ui_component :widget_list, WidgetList
102
+ ui_component :widget_form, WidgetForm
103
+
104
+ def sign_in(name)
105
+ sign_in_screen.show
106
+ sign_in_screen.sign_in(test_data.users[name])
107
+ end
108
+
109
+ def create_new_widget(name, attributes = {})
110
+ widget_list.show
111
+ widget_list.choose_to_create_new_widget
112
+ widget_form.submit('name' => 'My Widget')
113
+ test_data.widgets[name] = widget_list.last_widget_created
114
+ end
115
+
116
+ def delete_widget(name)
117
+ widget_list.show
118
+ widget_list.choose_to_delete_widget(test_data.widgets[name])
119
+ end
120
+ end
121
+
122
+ # This is the fixture Rack application against which the integration
123
+ # test will run. It uses class variables to persist data, because
124
+ # Sinatra will instantiate a new instance of TestRackApp for each
125
+ # request.
126
+ class TestRackApp < Sinatra::Base
127
+ enable :sessions
128
+
129
+ # we want error handling to behave as it would for a production
130
+ # deployment rather than development
131
+ set :raise_errors, false
132
+ set :show_exceptions, false
133
+
134
+ def parse_json_req_body
135
+ request.body.rewind
136
+ ActiveSupport::JSON.decode(request.body.read).symbolize_keys
137
+ end
138
+
139
+ post '/users' do
140
+ user_data = parse_json_req_body
141
+ @@users ||= {}
142
+ @@users[user_data[:email]] = user_data
143
+ status 201
144
+ headers 'Content-Type' => 'application/json'
145
+ body user_data.to_json
146
+ end
147
+
148
+ post '/session' do
149
+ user = @@users[params[:email]]
150
+ if user && user[:password] == params[:password]
151
+ session[:logged_in] = true
152
+ status 200
153
+ body 'You are logged in!'
154
+ else
155
+ session[:logged_in] = false
156
+ status 403
157
+ body 'Log in failed!'
158
+ end
159
+ end
160
+
161
+ get '/session/new' do
162
+ body <<-EOF
163
+ <html>
164
+ <head>
165
+ <title>Sign In</title>
166
+ </head>
167
+ <body>
168
+ <div id="sign_in_screen">
169
+ <form action="/session" method="POST">
170
+ <label for="email">Email:</label>
171
+ <input id="email" name="email" type="text" />
172
+
173
+ <label for="password">Password:</label>
174
+ <input id="password" name="password" type="password" />
175
+
176
+ <input type="submit" value="Sign In" />
177
+ </form>
178
+ </div>
179
+ </body>
180
+ </html>
181
+ EOF
182
+ end
183
+
184
+ post '/widgets/:widget_id' do
185
+ @@widgets.delete_if do |w|
186
+ w[:id] == params['widget_id']
187
+ end
188
+ redirect to('/widgets')
189
+ end
190
+
191
+ get '/widgets/new' do
192
+ body <<-EOF
193
+ <html>
194
+ <head>
195
+ <title>New Widget</title>
196
+ </head>
197
+ <body>
198
+ <div id="widget_form">
199
+ <form action="/widgets" method="POST">
200
+ <label for="name">Name:</label>
201
+ <input id="name" name="name" type="text" />
202
+
203
+ <input type="submit" value="Save" />
204
+ </form>
205
+ </div>
206
+ </body>
207
+ </html>
208
+ EOF
209
+ end
210
+
211
+ post '/widgets' do
212
+ @@widgets ||= []
213
+ widget_data = if request.media_type == 'application/json'
214
+ parse_json_req_body
215
+ else
216
+ params.symbolize_keys.slice(:name)
217
+ end
218
+ widget_data[:id] = `uuidgen`.strip
219
+ @@widgets << widget_data
220
+ @@last_widget_created = widget_data
221
+ if request.accept? 'text/html'
222
+ redirect to('/widgets')
223
+ elsif request.accept? 'application/json'
224
+ status 201
225
+ headers 'Content-Type' => 'application/json'
226
+ body widget_data.to_json
227
+ else
228
+ redirect to('/widgets')
229
+ end
230
+ end
231
+
232
+ get '/widgets' do
233
+ raise "Not logged in!" unless session[:logged_in]
234
+ @@widgets ||= []
235
+ last_widget_created, @@last_widget_created = @@last_widget_created, nil
236
+ content = ''
237
+ content << <<-EOF
238
+ <html>
239
+ <head>
240
+ <title>Widgets</title>
241
+ </head>
242
+ <body>
243
+ <div id="widget_list">
244
+ EOF
245
+ if last_widget_created
246
+ content << <<-EOF
247
+ <div class="last_widget created">
248
+ <span class="id">#{last_widget_created[:id]}</span>
249
+ <span class="name">#{last_widget_created[:name]}</span>
250
+ </div>
251
+ EOF
252
+ end
253
+ content << <<-EOF
254
+ <ul>
255
+ EOF
256
+ @@widgets.each do |w|
257
+ content << <<-EOF
258
+ <li class="widget_summary">
259
+ <span class="id">#{w[:id]}</span>
260
+ <span class="name">#{w[:name]}</span>
261
+ <form id="delete_#{w[:id]}" action="/widgets/#{w[:id]}" method="POST">
262
+ <button type="submit" value="Delete" />
263
+ </form>
264
+ </li>
265
+ EOF
266
+ end
267
+ content << <<-EOF
268
+ </ul>
269
+ <a href="/widgets/new">New Widget</a>
270
+ </div>
271
+ </body>
272
+ </html>
273
+ EOF
274
+ body content
275
+ end
276
+ end
277
+
278
+
279
+ it "runs the tests against the app" do
280
+ my_app = TestRackApp.new
281
+
282
+ server_error_detection = lambda { |browser|
283
+ browser.has_css?('h1', :text => 'Internal Server Error')
284
+ }
285
+
286
+ k = Kookaburra.new({
287
+ :ui_driver_class => MyUIDriver,
288
+ :given_driver_class => MyGivenDriver,
289
+ :api_driver_class => MyAPIDriver,
290
+ :browser => Capybara::Session.new(:rack_test, my_app),
291
+ :rack_app => my_app,
292
+ :server_error_detection => server_error_detection
293
+ })
294
+
295
+ k.given.a_user(:bob)
296
+ k.given.a_widget(:widget_a)
297
+ k.given.a_widget(:widget_b, :name => 'Foo')
298
+
299
+ k.ui.sign_in(:bob)
300
+ k.ui.widget_list.show
301
+ k.ui.widget_list.widgets.should == k.get_data(:widgets).slice(:widget_a, :widget_b)
302
+
303
+ k.ui.create_new_widget(:widget_c, :name => 'Bar')
304
+ k.ui.widget_list.widgets.should == k.get_data(:widgets).slice(:widget_a, :widget_b, :widget_c)
305
+
306
+ k.ui.delete_widget(:widget_b)
307
+ k.ui.widget_list.widgets.should == k.get_data(:widgets).slice(:widget_a, :widget_c)
308
+ end
309
+ end
310
+ end
311
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kookaburra
3
3
  version: !ruby/object:Gem::Version
4
- hash: 83
4
+ hash: 81
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 18
9
- - 2
10
- version: 0.18.2
9
+ - 3
10
+ version: 0.18.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - John Wilger
@@ -223,6 +223,7 @@ files:
223
223
  - lib/kookaburra/ui_driver.rb
224
224
  - lib/kookaburra/ui_driver/ui_component.rb
225
225
  - lib/kookaburra/utils/active_record_shared_connection.rb
226
+ - spec/integration/test_a_rack_application_spec.rb
226
227
  - spec/kookaburra/json_api_driver_spec.rb
227
228
  - spec/kookaburra/null_browser_spec.rb
228
229
  - spec/kookaburra/rack_driver_spec.rb
@@ -230,7 +231,6 @@ files:
230
231
  - spec/kookaburra/test_helpers_spec.rb
231
232
  - spec/kookaburra/ui_driver/ui_component_spec.rb
232
233
  - spec/kookaburra/ui_driver_spec.rb
233
- - spec/kookaburra_integration_spec.rb
234
234
  - spec/kookaburra_spec.rb
235
235
  - spec/support/shared_examples/it_has_a_dependency_accessor.rb
236
236
  homepage: http://github.com/projectdx/kookaburra
@@ -1,348 +0,0 @@
1
- require 'kookaburra'
2
- require 'capybara'
3
- require 'sinatra/base'
4
- require 'active_support/hash_with_indifferent_access'
5
-
6
- describe 'Kookaburra Integration' do
7
- describe "testing a Rack application" do
8
- describe "with an HTML interface" do
9
- describe "with a JSON API" do
10
- require 'kookaburra/json_api_driver'
11
- class MyAPIDriver < Kookaburra::JsonApiDriver
12
- def create_user(user_data)
13
- post '/users', user_data
14
- end
15
-
16
- def create_widget(widget_data)
17
- post '/widgets', widget_data
18
- end
19
- end
20
-
21
- class MyGivenDriver < Kookaburra::GivenDriver
22
- def a_user(name)
23
- user = {'email' => 'bob@example.com', 'password' => '12345'}
24
- result = api.create_user(user)
25
- test_data.users[name] = result
26
- end
27
-
28
- def a_widget(name, attributes = {})
29
- widget = {'name' => 'Foo'}.merge(attributes)
30
- result = api.create_widget(widget)
31
- test_data.widgets[name] = result
32
- end
33
- end
34
-
35
- class SignInScreen < Kookaburra::UIDriver::UIComponent
36
- def component_path
37
- '/session/new'
38
- end
39
-
40
- def component_locator
41
- '#sign_in_screen'
42
- end
43
-
44
- def sign_in(user_data)
45
- fill_in 'Email:', :with => user_data['email']
46
- fill_in 'Password:', :with => user_data['password']
47
- click_button 'Sign In'
48
- end
49
- end
50
-
51
- class WidgetList < Kookaburra::UIDriver::UIComponent
52
- def component_path
53
- '/widgets'
54
- end
55
-
56
- def component_locator
57
- '#widget_list'
58
- end
59
-
60
- def widgets
61
- all('.widget_summary').map do |el|
62
- extract_widget_data(el)
63
- end
64
- end
65
-
66
- def last_widget_created
67
- element = find('.last_widget.created')
68
- extract_widget_data(element)
69
- end
70
-
71
- def choose_to_create_new_widget
72
- click_on 'New Widget'
73
- end
74
-
75
- def choose_to_delete_widget(widget_data)
76
- find("#delete_#{widget_data['id']}").click_button('Delete')
77
- end
78
-
79
- private
80
-
81
- def extract_widget_data(element)
82
- {
83
- 'id' => element.find('.id').text,
84
- 'name' => element.find('.name').text
85
- }
86
- end
87
- end
88
-
89
- class WidgetForm < Kookaburra::UIDriver::UIComponent
90
- def component_locator
91
- '#widget_form'
92
- end
93
-
94
- def submit(widget_data)
95
- fill_in 'Name:', :with => widget_data['name']
96
- click_on 'Save'
97
- end
98
- end
99
-
100
- class MyUIDriver < Kookaburra::UIDriver
101
- ui_component :sign_in_screen, SignInScreen
102
- ui_component :widget_list, WidgetList
103
- ui_component :widget_form, WidgetForm
104
-
105
- def sign_in(name)
106
- sign_in_screen.show
107
- sign_in_screen.sign_in(test_data.users[name])
108
- end
109
-
110
- def create_new_widget(name, attributes = {})
111
- widget_list.show
112
- widget_list.choose_to_create_new_widget
113
- widget_form.submit('name' => 'My Widget')
114
- test_data.widgets[name] = widget_list.last_widget_created
115
- end
116
-
117
- def delete_widget(name)
118
- widget_list.show
119
- widget_list.choose_to_delete_widget(test_data.widgets[name])
120
- end
121
- end
122
-
123
- # This is the fixture Rack application against which the integration
124
- # test will run. It uses class variables to persist data, because
125
- # Sinatra will instantiate a new instance of TestRackApp for each
126
- # request.
127
- class TestRackApp < Sinatra::Base
128
- enable :sessions
129
-
130
- # we want error handling to behave as it would for a production
131
- # deployment rather than development
132
- set :raise_errors, false
133
- set :show_exceptions, false
134
-
135
- def parse_json_req_body
136
- request.body.rewind
137
- ActiveSupport::JSON.decode(request.body.read).symbolize_keys
138
- end
139
-
140
- post '/users' do
141
- user_data = parse_json_req_body
142
- @@users ||= {}
143
- @@users[user_data[:email]] = user_data
144
- status 201
145
- headers 'Content-Type' => 'application/json'
146
- body user_data.to_json
147
- end
148
-
149
- post '/session' do
150
- user = @@users[params[:email]]
151
- if user && user[:password] == params[:password]
152
- session[:logged_in] = true
153
- status 200
154
- body 'You are logged in!'
155
- else
156
- session[:logged_in] = false
157
- status 403
158
- body 'Log in failed!'
159
- end
160
- end
161
-
162
- get '/session/new' do
163
- body <<-EOF
164
- <html>
165
- <head>
166
- <title>Sign In</title>
167
- </head>
168
- <body>
169
- <div id="sign_in_screen">
170
- <form action="/session" method="POST">
171
- <label for="email">Email:</label>
172
- <input id="email" name="email" type="text" />
173
-
174
- <label for="password">Password:</label>
175
- <input id="password" name="password" type="password" />
176
-
177
- <input type="submit" value="Sign In" />
178
- </form>
179
- </div>
180
- </body>
181
- </html>
182
- EOF
183
- end
184
-
185
- post '/widgets/:widget_id' do
186
- @@widgets.delete_if do |w|
187
- w[:id] == params['widget_id']
188
- end
189
- redirect to('/widgets')
190
- end
191
-
192
- get '/widgets/new' do
193
- body <<-EOF
194
- <html>
195
- <head>
196
- <title>New Widget</title>
197
- </head>
198
- <body>
199
- <div id="widget_form">
200
- <form action="/widgets" method="POST">
201
- <label for="name">Name:</label>
202
- <input id="name" name="name" type="text" />
203
-
204
- <input type="submit" value="Save" />
205
- </form>
206
- </div>
207
- </body>
208
- </html>
209
- EOF
210
- end
211
-
212
- post '/widgets' do
213
- @@widgets ||= []
214
- widget_data = if request.media_type == 'application/json'
215
- parse_json_req_body
216
- else
217
- params.symbolize_keys.slice(:name)
218
- end
219
- widget_data[:id] = `uuidgen`.strip
220
- @@widgets << widget_data
221
- @@last_widget_created = widget_data
222
- if request.accept? 'text/html'
223
- redirect to('/widgets')
224
- elsif request.accept? 'application/json'
225
- status 201
226
- headers 'Content-Type' => 'application/json'
227
- body widget_data.to_json
228
- else
229
- redirect to('/widgets')
230
- end
231
- end
232
-
233
- get '/widgets' do
234
- raise "Not logged in!" unless session[:logged_in]
235
- @@widgets ||= []
236
- last_widget_created, @@last_widget_created = @@last_widget_created, nil
237
- content = ''
238
- content << <<-EOF
239
- <html>
240
- <head>
241
- <title>Widgets</title>
242
- </head>
243
- <body>
244
- <div id="widget_list">
245
- EOF
246
- if last_widget_created
247
- content << <<-EOF
248
- <div class="last_widget created">
249
- <span class="id">#{last_widget_created[:id]}</span>
250
- <span class="name">#{last_widget_created[:name]}</span>
251
- </div>
252
- EOF
253
- end
254
- content << <<-EOF
255
- <ul>
256
- EOF
257
- @@widgets.each do |w|
258
- content << <<-EOF
259
- <li class="widget_summary">
260
- <span class="id">#{w[:id]}</span>
261
- <span class="name">#{w[:name]}</span>
262
- <form id="delete_#{w[:id]}" action="/widgets/#{w[:id]}" method="POST">
263
- <button type="submit" value="Delete" />
264
- </form>
265
- </li>
266
- EOF
267
- end
268
- content << <<-EOF
269
- </ul>
270
- <a href="/widgets/new">New Widget</a>
271
- </div>
272
- </body>
273
- </html>
274
- EOF
275
- body content
276
- end
277
- end
278
-
279
-
280
- it "runs the tests against the app" do
281
- my_app = TestRackApp.new
282
-
283
- server_error_detection = lambda { |browser|
284
- browser.has_css?('h1', :text => 'Internal Server Error')
285
- }
286
-
287
- k = Kookaburra.new({
288
- :ui_driver_class => MyUIDriver,
289
- :given_driver_class => MyGivenDriver,
290
- :api_driver_class => MyAPIDriver,
291
- :browser => Capybara::Session.new(:rack_test, my_app),
292
- :rack_app => my_app,
293
- :server_error_detection => server_error_detection
294
- })
295
-
296
- k.given.a_user(:bob)
297
- k.given.a_widget(:widget_a)
298
- k.given.a_widget(:widget_b, :name => 'Foo')
299
-
300
- k.ui.sign_in(:bob)
301
- k.ui.widget_list.show
302
- k.ui.widget_list.widgets.should == k.get_data(:widgets).slice(:widget_a, :widget_b)
303
-
304
- k.ui.create_new_widget(:widget_c, :name => 'Bar')
305
- k.ui.widget_list.widgets.should == k.get_data(:widgets).slice(:widget_a, :widget_b, :widget_c)
306
-
307
- k.ui.delete_widget(:widget_b)
308
- k.ui.widget_list.widgets.should == k.get_data(:widgets).slice(:widget_a, :widget_c)
309
- end
310
- end
311
- end
312
- end
313
-
314
- describe "testing a Ruby API" do
315
- class MyUIDriver < Kookaburra::UIDriver
316
- end
317
-
318
- class MyGivenDriver < Kookaburra::GivenDriver
319
- end
320
-
321
- class APIDriver < Kookaburra::APIDriver
322
- end
323
-
324
- it "runs the tests" do
325
- k = Kookaburra.new({
326
- :api_driver_class => MyAPIDriver,
327
- :given_driver_class => MyGivenDriver,
328
- :ui_driver_class => MyUIDriver,
329
- })
330
-
331
- pending 'WIP' do
332
- k.given.a_user(:bob)
333
- k.given.a_widget(:widget_a)
334
- k.given.a_widget(:widget_b, :name => 'Foo')
335
-
336
- k.ui.sign_in(:bob)
337
- k.ui.widget_list.show
338
- k.ui.widget_list.widgets.should == k.get_data(:widgets).slice(:widget_a, :widget_b)
339
-
340
- k.ui.create_new_widget(:widget_c, :name => 'Bar')
341
- k.ui.widget_list.widgets.should == k.get_data(:widgets).slice(:widget_a, :widget_b, :widget_c)
342
-
343
- k.ui.delete_widget(:widget_b)
344
- k.ui.widget_list.widgets.should == k.get_data(:widgets).slice(:widget_a, :widget_c)
345
- end
346
- end
347
- end
348
- end