kookaburra 0.18.2 → 0.18.3

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