kookaburra 0.14.4 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.rspec +1 -0
  2. data/Gemfile +3 -2
  3. data/Gemfile.lock +49 -9
  4. data/LICENSE.txt +1 -1
  5. data/README.markdown +142 -188
  6. data/Rakefile +12 -16
  7. data/VERSION +1 -1
  8. data/kookaburra.gemspec +29 -22
  9. data/lib/kookaburra/api_driver.rb +8 -48
  10. data/lib/kookaburra/dependency_accessor.rb +24 -0
  11. data/lib/kookaburra/exceptions.rb +12 -0
  12. data/lib/kookaburra/given_driver.rb +55 -8
  13. data/lib/kookaburra/json_api_driver.rb +76 -0
  14. data/lib/kookaburra/rack_driver.rb +65 -0
  15. data/lib/kookaburra/test_data.rb +70 -46
  16. data/lib/kookaburra/test_helpers.rb +101 -0
  17. data/lib/kookaburra/ui_driver/ui_component.rb +173 -90
  18. data/lib/kookaburra/ui_driver.rb +69 -17
  19. data/lib/kookaburra.rb +70 -151
  20. data/spec/kookaburra/json_api_driver_spec.rb +34 -0
  21. data/spec/kookaburra/rack_driver_spec.rb +36 -0
  22. data/spec/kookaburra/test_data_spec.rb +31 -0
  23. data/spec/kookaburra/test_helpers_spec.rb +48 -0
  24. data/spec/kookaburra/ui_driver/ui_component_spec.rb +178 -0
  25. data/spec/kookaburra/ui_driver_spec.rb +27 -0
  26. data/spec/kookaburra_integration_spec.rb +312 -0
  27. data/spec/kookaburra_spec.rb +64 -0
  28. data/spec/support/shared_examples/it_has_a_dependency_accessor.rb +26 -0
  29. metadata +49 -30
  30. data/lib/kookaburra/assertion.rb +0 -32
  31. data/lib/kookaburra/ui_driver/mixins/has_browser.rb +0 -30
  32. data/lib/kookaburra/ui_driver/mixins/has_strategies.rb +0 -52
  33. data/lib/kookaburra/ui_driver/mixins/has_ui_component.rb +0 -45
  34. data/test/helper.rb +0 -30
  35. data/test/kookaburra/assertion_test.rb +0 -68
  36. data/test/kookaburra/test_data_test.rb +0 -28
  37. data/test/kookaburra/ui_driver/mixins/has_browser_test.rb +0 -38
  38. data/test/kookaburra/ui_driver/ui_component_test.rb +0 -101
  39. data/test/kookaburra/ui_driver_test.rb +0 -54
  40. data/test/kookaburra_test.rb +0 -188
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile CHANGED
@@ -7,11 +7,12 @@ gem 'rack-test'
7
7
  # Add dependencies to develop your gem here.
8
8
  # Include everything needed to run rake, tests, features, etc.
9
9
  group :development do
10
- gem 'minitest'
10
+ gem 'rspec'
11
+ gem 'capybara'
11
12
  gem 'yard'
12
13
  gem 'redcarpet', '~> 1.0' # used to format documentation
13
- gem 'bundler'
14
14
  gem 'jeweler'
15
15
  gem 'rcov'
16
16
  gem 'reek'
17
+ gem 'sinatra'
17
18
  end
data/Gemfile.lock CHANGED
@@ -1,46 +1,86 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- activesupport (3.2.0)
4
+ activesupport (3.2.2)
5
5
  i18n (~> 0.6)
6
6
  multi_json (~> 1.0)
7
+ capybara (1.1.2)
8
+ mime-types (>= 1.16)
9
+ nokogiri (>= 1.3.3)
10
+ rack (>= 1.0.0)
11
+ rack-test (>= 0.5.4)
12
+ selenium-webdriver (~> 2.0)
13
+ xpath (~> 0.1.4)
14
+ childprocess (0.3.1)
15
+ ffi (~> 1.0.6)
16
+ diff-lcs (1.1.3)
17
+ ffi (1.0.11)
7
18
  git (1.2.5)
8
19
  i18n (0.6.0)
9
- jeweler (1.6.4)
20
+ jeweler (1.8.3)
10
21
  bundler (~> 1.0)
11
22
  git (>= 1.2.5)
12
23
  rake
13
- minitest (2.10.1)
14
- multi_json (1.0.4)
15
- rack (1.4.0)
24
+ rdoc
25
+ json (1.6.5)
26
+ mime-types (1.17.2)
27
+ multi_json (1.1.0)
28
+ nokogiri (1.5.0)
29
+ rack (1.4.1)
30
+ rack-protection (1.2.0)
31
+ rack
16
32
  rack-test (0.6.1)
17
33
  rack (>= 1.0)
18
34
  rake (0.9.2.2)
19
35
  rcov (1.0.0)
36
+ rdoc (3.12)
37
+ json (~> 1.4)
20
38
  redcarpet (1.17.2)
21
39
  reek (1.2.8)
22
40
  ruby2ruby (~> 1.2)
23
41
  ruby_parser (~> 2.0)
24
42
  sexp_processor (~> 3.0)
43
+ rspec (2.8.0)
44
+ rspec-core (~> 2.8.0)
45
+ rspec-expectations (~> 2.8.0)
46
+ rspec-mocks (~> 2.8.0)
47
+ rspec-core (2.8.0)
48
+ rspec-expectations (2.8.0)
49
+ diff-lcs (~> 1.1.2)
50
+ rspec-mocks (2.8.0)
25
51
  ruby2ruby (1.3.1)
26
52
  ruby_parser (~> 2.0)
27
53
  sexp_processor (~> 3.0)
28
54
  ruby_parser (2.3.1)
29
55
  sexp_processor (~> 3.0)
30
- sexp_processor (3.0.10)
31
- yard (0.7.4)
56
+ rubyzip (0.9.6.1)
57
+ selenium-webdriver (2.20.0)
58
+ childprocess (>= 0.2.5)
59
+ ffi (~> 1.0)
60
+ multi_json (~> 1.0)
61
+ rubyzip
62
+ sexp_processor (3.1.0)
63
+ sinatra (1.3.2)
64
+ rack (~> 1.3, >= 1.3.6)
65
+ rack-protection (~> 1.2)
66
+ tilt (~> 1.3, >= 1.3.3)
67
+ tilt (1.3.3)
68
+ xpath (0.1.4)
69
+ nokogiri (~> 1.3)
70
+ yard (0.7.5)
32
71
 
33
72
  PLATFORMS
34
73
  ruby
35
74
 
36
75
  DEPENDENCIES
37
76
  activesupport (>= 3.0)
38
- bundler
77
+ capybara
39
78
  i18n
40
79
  jeweler
41
- minitest
42
80
  rack-test
43
81
  rcov
44
82
  redcarpet (~> 1.0)
45
83
  reek
84
+ rspec
85
+ sinatra
46
86
  yard
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 Renewable Funding, LLC
1
+ Copyright (c) 2011 John Wilger <johnwilger@gmail.com>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.markdown CHANGED
@@ -3,6 +3,20 @@
3
3
  Kookaburra is a framework for implementing the [Window Driver] [Window Driver] pattern in
4
4
  order to keep acceptance tests maintainable.
5
5
 
6
+ ## WARNING: Significant Changes since 0.14.x ##
7
+
8
+ As of 0.15.0, Kookaburra has been rewritten from the ground up. The original
9
+ (up through 0.14.x) version was extracted from another project in which the
10
+ testing library was being used. Unfortunately, this meant that the code in
11
+ Kookaburra itself did not have very good test coverage, because it was being
12
+ tested indirectly by the fact of its usage in the other project. What we've
13
+ found is that a *lot* of complexity was sneaking into Kookaburra due to its
14
+ having been developed without much focused TDD.
15
+
16
+ Starting with 0.15.0, we are treating the previous versions as a spike. They
17
+ were really useful for learning about the approach, but the code has enough
18
+ design flaws that its best just to toss it.
19
+
6
20
  ## Installation ##
7
21
 
8
22
  Kookaburra is available as a Rubygem and [published on Rubygems.org] [Kookaburra
@@ -19,58 +33,58 @@ following:
19
33
 
20
34
  ## Setup ##
21
35
 
22
- Kookaburra itself abstracts some common patterns for implementing the Window
23
- Driver pattern for tests of Ruby web applications built on [Rack] [Rack]. You will need
36
+ Kookaburra abstracts some common patterns for implementing the Window Driver
37
+ pattern for tests of Ruby web applications built on [Rack] [Rack]. You will need
24
38
  to tell Kookaburra which classes contain the specific Domain Driver
25
39
  implementations for your application as well as which driver to use for running
26
- the tests (currently only tested with [Capybara] [Capybara]). The details of setting up your
27
- Domain Driver layer are discussed below, but in general you will need the
28
- following in a locations such as `lib/my_application/kookaburra.rb` (replace
29
- `MyApplication` with a module name suitable to your actual application:
30
-
31
- module MyApplication
32
- module Kookaburra
33
- ::Kookaburra.adapter = Capybara
34
-
35
- # Note: the following assigned classes are defined under your
36
- # application's namespace, e.g. MyApplication::Kookaburra::APIDriver
37
- ::Kookaburra.api_driver = APIDriver
38
- ::Kookaburra.given_driver = GivenDriver
39
- ::Kookaburra.ui_driver = UIDriver
40
-
41
- ::Kookaburra.test_data_setup do
42
- provide_collection :accounts
43
- # See section on Test Data for more examples of what can go here.
44
- end
45
- end
46
- end
40
+ the tests (currently only tested with [Capybara] [Capybara]).
47
41
 
48
42
  ### RSpec ###
49
43
 
50
44
  For [RSpec] [RSpec] integration tests, just add the following to
51
45
  `spec/support/kookaburra_setup.rb`:
52
46
 
53
- require 'my_application/kookaburra'
47
+ require 'kookaburra/test_helpers'
48
+ require 'my_app/kookaburra/api_driver'
49
+ require 'my_app/kookaburra/given_driver'
50
+ require 'my_app/kookaburra/ui_driver'
51
+
52
+ Kookaburra.configuration = {
53
+ :api_driver_class => MyApp::Kookaburra::APIDriver,
54
+ :given_driver_class => MyApp::Kookaburra::GivenDriver,
55
+ :ui_driver_class => MyApp::Kookaburra::UIDriver,
56
+ :browser => Capybara,
57
+ :server_error_detection => { |browser|
58
+ browser.has_css?('head title', :text => 'Internal Server Error')
59
+ }
60
+ }
54
61
 
55
62
  RSpec.configure do |c|
56
- c.include(Kookaburra, :type => :request)
63
+ c.include(Kookaburra::TestHelpers, :type => :request)
57
64
  end
58
65
 
59
66
  ### Cucumber ###
60
67
 
61
68
  For [Cucumber] [Cucumber], add the following to `features/support/kookaburra_setup.rb`:
62
69
 
63
- require 'my_application/kookaburra'
70
+ require 'kookaburra/test_helpers'
71
+ require 'my_app/kookaburra/api_driver'
72
+ require 'my_app/kookaburra/given_driver'
73
+ require 'my_app/kookaburra/ui_driver'
64
74
 
65
- Kookaburra.adapter = Capybara
66
- World(Kookaburra)
75
+ Kookaburra.configuration = {
76
+ :api_driver_class => MyApp::Kookaburra::APIDriver,
77
+ :given_driver_class => MyApp::Kookaburra::GivenDriver,
78
+ :ui_driver_class => MyApp::Kookaburra::UIDriver,
79
+ :browser => Capybara,
80
+ :server_error_detection => { |browser|
81
+ browser.has_css?('head title', :text => 'Internal Server Error')
82
+ }
83
+ }
67
84
 
68
- Before do
69
- # Ensure that there isn't state-leakage between scenarios
70
- kookaburra_reset!
71
- end
85
+ World(Kookaburra::TestHelpers)
72
86
 
73
- This will cause the #api, #given and #ui methods will be available in your
87
+ This will cause the #k, #given and #ui methods will be available in your
74
88
  Cucumber step definitions.
75
89
 
76
90
  ## Defining Your Testing DSL ##
@@ -86,7 +100,7 @@ Kookaburra has the following layers:
86
100
  etc.)
87
101
  3. The **Domain Driver** (Kookaburra::GivenDriver and Kookaburra::UIDriver)
88
102
  4. The **Window Driver** (Kookaburra::UIDriver::UIComponent)
89
- 5. The **Application Driver** (Capybara and Rack::Test)
103
+ 5. The **Application Driver** (Capybara and Kookaburra::RackDriver)
90
104
 
91
105
  ### The Business Specification Language ###
92
106
 
@@ -138,14 +152,10 @@ anywhere outside of your test implementation (such as within `UIDriver` or
138
152
  `UIComponent` subclasses.) Doing so will tightly couple your Domain Driver
139
153
  and/or Window Driver implementation to a specific testing library.
140
154
 
141
- If you must make some type of assertion within the Domain Driver layer, a better
142
- approach is to simply raise an exception with an informative error message when
143
- some desired condition is not met. Kookaburra provides its own `#assert` method
144
- for this purpose. You may use it directly or build your own custom assertions
145
- using it as a base. However, this method should be used only for the purpose
146
- of short-circuiting your Domain Driver with an informative error message, not
147
- to test the results of your operations as you would at the test implementation
148
- layer.
155
+ `Kookaburra::UIDriver::UIComponent` does provide an `#assert` method for use
156
+ inside your own UIComponents. This method exists to verify preconditions and
157
+ provide more informative error messages; it is not intended to be used to make
158
+ test verifications.
149
159
 
150
160
  Given the Cucumber scenario above, here is how the test implementation layer
151
161
  might look:
@@ -177,15 +187,15 @@ might look:
177
187
  end
178
188
 
179
189
  Then "I see my order summary" do
180
- ui.should be_displaying_order_summary
190
+ ui.order_summary.should be_visible
181
191
  end
182
192
 
183
193
  Then "I see that my default payment options will be used" do
184
- ui.should be_displaying_account_default_payment_options_in_order_summary
194
+ ui.order_summary.payment_options.should == k.get_data(:default_payment_options)[:my_account]
185
195
  end
186
196
 
187
197
  Then "I see that my default shipping options will be used" do
188
- ui.should be_displaying_account_default_shipping_options_in_order_summary
198
+ ui.order_summary.shipping_options.should == k.get_data(:default_shipping_options)[:my_account]
189
199
  end
190
200
 
191
201
  The step definitions contain neither explicitly shared state (instance
@@ -199,11 +209,11 @@ scenario, it's not a big deal to have the following in your step definitions (as
199
209
  long as the author of the spec confirms that they really mean the same thing):
200
210
 
201
211
  Then "I see my order summary" do
202
- ui.should be_displaying_order_summary
212
+ ui.order_summary.should be_visible
203
213
  end
204
214
 
205
215
  Then "I see a summary of my order" do
206
- ui.should be_displaying_order_summary
216
+ ui.order_summary.should be_visible
207
217
  end
208
218
 
209
219
  The step definitions are nothing more than a natural language reference to an
@@ -234,131 +244,116 @@ Using RSpec, the test implementation would be as follows:
234
244
  ui.choose_to_check_out
235
245
 
236
246
  ui.order_summary.should be_visible
237
- ui.should be_displaying_account_default_payment_options_in_order_summary
238
- ui.should be_displaying_account_default_shipping_options_in_order_summary
247
+ ui.order_summary.payment_options.should == k.get_data(:default_payment_options)[:my_account]
248
+ ui.order_summary.shipping_options.should == k.get_data(:default_shipping_options)[:my_account]
239
249
  end
240
250
  end
241
251
 
242
252
  ### The Domain Driver ###
243
253
 
244
254
  The Domain Driver layer is where you build up an internal DSL that describes the
245
- business concepts of your application at a fairly high level. It consists of
246
- three top-level drivers: the `APIDriver` (available via `#api`) for interacting
247
- with your application's external API, the `GivenDriver` (available via `#given`)
248
- which really just wraps the `APIDriver` and is used to set up state for your
249
- tests, and the UIDriver (available via `#ui`) for describing the tasks that a
250
- user can accomplish with the application.
255
+ business concepts of your application at a fairly high level. It consists of two
256
+ top-level drivers: the `GivenDriver` (available via `#given`) used to set up
257
+ state for your tests and the UIDriver (available via `#ui`) for describing the
258
+ tasks that a user can accomplish with the application.
251
259
 
252
260
  #### Test Data ####
253
261
 
254
262
  `Kookaburra::TestData` is the component via which the `GivenDriver` and the
255
263
  `UIDriver` share information. For instance, if you create a user account via the
256
264
  `GivenDriver`, you would store the login credentials for that account in the
257
- `TestData` instance, so the UIDriver knows what to use when you tell it to
265
+ `TestData` instance, so the `UIDriver` knows what to use when you tell it to
258
266
  `#sign_in`. This is what allows the Cucumber step definitions to remain free
259
267
  from explicitly shared state.
260
268
 
261
- The `TestData` class can be configured to contain both collections of test data
262
- as well as default data that can be used as a starting point for creating new
263
- resources in the application. To configure `TestData`, call
264
- `Kookaburra.test_data_setup` with a block (usually in your
265
- `lib/my_application/kookaburra.rb` file):
266
-
267
- module MyApplication
268
- module Kookaburra
269
- # ...
270
- ::Kookaburra.test_data_setup do
271
- provide_collection :animals
272
- set_default :animal,
273
- :name => 'horse'
274
- :size => 'large',
275
- :number_of_legs => 4
276
- end
277
- end
278
- end
269
+ Kookaburra automatically configures your `GivenDriver` and your `UIDriver` to share
270
+ a `TestData` instance, which is available to both of them via their `#test_data`
271
+ method.
279
272
 
280
- Then, in any context where you have an instance of `TestData` (such as in
281
- `GivenDriver` or `UIDriver`), you can add/retrieve items to/from collections and
282
- access default data:
273
+ The `TestData` instance will return a `TestData::Collection` for any method
274
+ called on the object. The `TestData::Collection` object behaves like a `Hash`
275
+ for the most part, however it will raise a `Kookaburra::UnknownKeyError` if you
276
+ try to access a key that has not yet been assigned a value.
283
277
 
284
- class MyApplication::Kookaburra::GivenDriver < Kookaburra::GivenDriver
285
- def existing_account(nickname)
286
- default_account_data = test_data.default(:account)
287
- # do something to create account in application
288
- # ...
289
- # make the details of the new account available to the rest of the test
290
- test_data.set_accounts(nickname, account)
291
- end
292
- end
278
+ Here's a quick example of TestData behavor:
293
279
 
294
- class MyApplication::Kookaburra::UIDriver < Kookaburra::UIDriver
295
- def sign_in(account_nickname)
296
- # pull stored account details from TestData
297
- account_info = test_data.fetch_accounts(account_nickname)
280
+ test_data = TestData.new
298
281
 
299
- # do something to log in using that account_info
300
- end
301
- end
282
+ test_data.widgets[:widget_a] = {:name => 'Widget A'}
283
+
284
+ test_data.widgets[:widget_a]
285
+ #=> {:name => 'Widget A'}
286
+
287
+ # this will raise a Kookaburra::UnknownKeyError
288
+ test_data.widgets[:widget_b]
302
289
 
303
290
  #### API Driver ####
304
291
 
305
292
  The `Kookaburra::APIDriver` is used to interact with an application's external
306
293
  web services API. You tell Kookaburra about your API by creating a subclass of
307
- `Kookaburra::APIDriver` for your application:
294
+ `Kookaburra::APIDriver` for your application. Because different applications may
295
+ implement different types of APIs, Kookaburra will provide more than one base
296
+ APIDriver class. At the moment, only a JSON API is supported via
297
+ `Kookaburra::JsonApiDriver`:
308
298
 
309
- # lib/my_application/kookaburra/api_driver.rb
299
+ # lib/my_app/kookaburra/api_driver.rb
310
300
 
311
- class MyApplication::Kookaburra::APIDriver < Kookaburra::APIDriver
301
+ class MyApp::Kookaburra::APIDriver < Kookaburra::JsonApiDriver
312
302
  def create_account(account_data)
313
- post_as_json 'Account', 'api/v1/accounts', :account => account_data
314
- hash_from_response_json[:account]
303
+ post '/api/v1/accounts', account_data
315
304
  end
316
305
  end
317
306
 
307
+ Regardless of the type of APIDriver subclass, the contents of your application's
308
+ APIDriver should consist mainly of mappings between discrete actions and HTTP
309
+ requests to the specified URL paths. Each driver will implement `#post`, `#get`,
310
+ `#put`, `#head`, and `#delete` in such a way that any Ruby data structure
311
+ provided as parameters will be appropriately translated to the API's required
312
+ data format, and any response body from the API request will be translated into
313
+ a Ruby data structure and returned.
314
+
318
315
  #### Given Driver ####
319
316
 
320
- The `Kookaburra::GivenDriver` is used to create a particular "preexisting"
321
- state within your application's data and ensure you have a handle to that data
322
- (when needed) prior to interacting with the UI. Like the `APIDriver`, you will
323
- create a subclass of `Kookaburra::GivenDriver` in which you will create part of
324
- the Domain Driver DSL for your application:
317
+ The `Kookaburra::GivenDriver` is used to create a particular "preexisting" state
318
+ within your application's data and ensure you have a handle to that data (when
319
+ needed) prior to interacting with the UI. You will create a subclass of
320
+ `Kookaburra::GivenDriver` in which you will create part of the Domain Driver DSL
321
+ for your application:
325
322
 
326
- # lib/my_application/kookaburra/given_driver.rb
323
+ # lib/my_app/kookaburra/given_driver.rb
327
324
 
328
- class MyApplication::Kookaburra::GivenDriver < Kookaburra::GivenDriver
325
+ class MyApp::Kookaburra::GivenDriver < Kookaburra::GivenDriver
329
326
  def existing_account(nickname)
330
- # grab the default account details and add a unique username and
331
- # password
332
- account_data = test_data.default(:account)
327
+ account_data = {:display_name => 'John Doe', :password => 'a password'}
333
328
  account_data[:username] = "test-user-#{`uuidgen`.strip}"
334
- account_data[:password] = account_data[:username] + "-password"
335
329
 
336
330
  # use the API to create the account in the application
337
- account_details = api.create_account(account_data)
331
+ result = api.create_account(account_data)
338
332
 
339
333
  # merge in the password (since API doesn't return it) and store details
340
334
  # in the TestData instance
341
- account_details.merge(:password => account_data[:password])
342
- test_data.set_accounts(nickname, account_details)
335
+ result.merge!(:password => account_data[:password])
336
+ test_data.accounts[nickname] = account_details
343
337
  end
344
338
  end
345
339
 
346
340
  Although there is nothing that actually *prevents* you from either interacting
347
341
  with the UI or directly manipulating your application via calls into the model
348
- from the `GivenDriver`, both things should be avoided. In the first case, the
342
+ from the `GivenDriver`, both should be avoided. In the first case, the
349
343
  `GivenDriver`'s purpose is to describe state that exists *before* the user
350
344
  interaction that is being tested. Although this state may be the result of a
351
- previous user interaction, your tests will generally be much, much faster if you
352
- are able to create this state via API calls rather than driving a web browser.
345
+ previous user interaction, your tests will be much, much faster if you create
346
+ this state via API calls rather than driving a web browser.
353
347
 
354
- In the second case, by avoiding manipulating your applications's state at the
348
+ In the second case, by avoiding the manipulation of your applications's state at the
355
349
  code level and instead doing so via an external API, it is much less likely that
356
- you will be creating a state that your application can't actually get into in a
350
+ you will create a state that your application can't actually get into in a
357
351
  production environment. Additionally, this opens up the possibility of running
358
352
  your tests against a "remote" server where you would not have access to the
359
353
  application internals. ("Remote" in the sense that it is not in the same Ruby
360
354
  process as your running tests, although it may or may not be on the same
361
- machine.)
355
+ machine. Note that this is not currently possible with Kookaburra due to our
356
+ current reliance on Rack::Test.)
362
357
 
363
358
  #### UI Driver ####
364
359
 
@@ -367,26 +362,20 @@ application's user interface using the Window Driver pattern. You will subclass
367
362
  `Kookaburra::UIDriver` for your application and implement your testing DSL
368
363
  within your subclass:
369
364
 
370
- # lib/my_application/kookaburra/ui_driver.rb
365
+ # lib/my_app/kookaburra/ui_driver.rb
371
366
 
372
- class MyApplication::Kookaburra::UIDriver < Kookaburra::UIDriver
373
- # makes an instance of MyApplication::Kookaburra::UIDriver::SignInScreen
374
- # available via the private instance method #sign_in_screen
375
- ui_component :sign_in_screen
367
+ class MyApp::Kookaburra::UIDriver < Kookaburra::UIDriver
368
+ # makes an instance of MyApp::Kookaburra::UIDriver::SignInScreen
369
+ # available via the instance method #sign_in_screen
370
+ ui_component :sign_in_screen, SignInScreen
376
371
 
377
372
  def sign_in(account_nickname)
378
- account = test_data.fetch_accounts(account_nickname)
379
- navigate_to :sign_in_screen
373
+ account = test_data.accounts[account_nickname]
374
+ sign_in_screen.show
380
375
  sign_in_screen.submit_login(account[:username], account[:password])
381
376
  end
382
377
  end
383
378
 
384
- The call to `Kookaburra::UIDriver.ui_component` defines the UIComponent accessor
385
- as a private method in order to discourage accessing your UIComponent objects
386
- directly in the test implementation layer. Instead, you should build out your
387
- testing DSL in the `UIDriver` subclass as was done with the `#sign_in` method
388
- above.
389
-
390
379
  ### The Window Driver Layer ###
391
380
 
392
381
  While your `GivenDriver` and `UIDriver` provide a DSL that represents actions
@@ -395,81 +384,48 @@ layer describes the individual user interface components that the user interacts
395
384
  with to perform these tasks. By describing each interface component using an OOP
396
385
  approach, it is much easier to maintain your acceptance/integration tests,
397
386
  because the implementation details of each component are captured in a single
398
- place. If/when that implementation changes, you can---for example---fix every
399
- single test that needs to log a user into the system just by updating the
400
- SignInScreen class.
387
+ place. For example, if/when the implementation of your application's sign in
388
+ screen changes, you can fix every single test that needs to log a user into the
389
+ system just by updating the `SignInScreen` class.
401
390
 
402
391
  You describe the various user interface components by sub-classing
403
392
  `Kookaburra::UIDriver::UIComponent`:
404
393
 
405
- # lib/my_application/ui_driver/sign_in_screen.rb
394
+ # lib/my_app/ui_driver/sign_in_screen.rb
406
395
 
407
- class MyApplication::Kookaburra::UIDriver::SignInScreen < Kookaburra::UIDriver::UIComponent
408
- component_locator '#new_user_session'
409
- component_path '/session/new'
396
+ class MyApp::Kookaburra::UIDriver::SignInScreen < Kookaburra::UIDriver::UIComponent
397
+ def component_locator
398
+ '#new_user_session'
399
+ end
400
+
401
+ def component_path
402
+ '/session/new'
403
+ end
410
404
 
411
405
  def username
412
- in_component { browser.find('#session_username').value }
406
+ find('#session_username').value
413
407
  end
414
408
 
415
409
  def username=(new_value)
416
- fill_in('#session_username', :with => new_value)
410
+ fill_in '#session_username', :with => new_value
417
411
  end
418
412
 
419
413
  def password
420
- in_component { browser.find('#session_password').value }
414
+ find('#session_password').value
421
415
  end
422
416
 
423
417
  def password=(new_value)
424
- fill_in('#session_password', :with => new_value)
418
+ fill_in '#session_password', :with => new_value
425
419
  end
426
420
 
427
- def submit!
421
+ def submit
428
422
  click_on('Sign In')
429
- no_500_error!
430
423
  end
431
424
 
432
425
  def submit_login(username, password)
433
426
  self.username = username
434
427
  self.password = password
435
- submit!
436
- end
437
- end
438
-
439
- A `UIComponent` subclass can also contain nested components of its own. For
440
- instance:
441
-
442
- class MyApplication::Kookaburra::UIDriver::UserList::NewUserForm < Kookaburra::UIDriver::UIComponent
443
- component_locator '#new_user_form'
444
- end
445
-
446
- class MyApplication::Kookaburra::UIDriver::UserList < Kookaburra::UIDriver::UIComponent
447
- component_locator '#user_list'
448
-
449
- ui_component :new_user_form
450
- end
451
-
452
- In this case, `UserList#new_user_form` is still defined as a private method. In
453
- order to manipulate it from your domain driver, you can define either explicit
454
- methods or delegators on `UserList`:
455
-
456
- class MyApplication::Kookaburra::UIDriver::UserList < Kookaburra::UIDriver::UIComponent
457
- def fill_in_new_user_form(username, password, full_name)
458
- new_user_form.username = username
459
- new_user_form.password = password
460
- new_user_form.full_name = full_name
461
- end
462
-
463
- delegate :submit!, :to => :new_user_form, :prefix => true
464
- end
465
-
466
- class MyApplication::Kookaburra::UIDriver < Kookaburra::UIDriver
467
- ui_component :user_list
468
-
469
- def create_new_user_with_valid_data
470
- user = default_user_data # factory method defined elsewhere
471
- user_list.fill_in_new_user_form(user[:username], user[:password], user[:full_name])
472
- user_list.new_user_form_submit!
428
+ submit
473
429
  end
474
430
  end
475
431
 
@@ -478,11 +434,9 @@ methods or delegators on `UserList`:
478
434
  `Kookaburra::APIDriver`, `Kookaburra::UIDriver` and
479
435
  `Kookaburra::UIDriver::UIComponent` rely on the Application Driver layer to
480
436
  interact with your application. In the case of the `APIDriver`, Kookaburra uses
481
- `Rack::Test` to send HTTP requests to your application. The `UIDriver` and
482
- `UIComponent` rely on whatever is configured as `Kookaburra.adapter`. Presently,
483
- we have only used Capybara as the application driver for Kookaburra:
484
-
485
- Kookaburra.adapter = Capybara
437
+ `Kookaburra::RackDriver` to send HTTP requests to your application. The `UIDriver` and
438
+ `UIComponent` rely on whatever is passed to `Kookaburra.new` as the `:browser`
439
+ option. Presently, we have only used Capybara as the application driver for Kookaburra.
486
440
 
487
441
  It's possible that something other than Capybara could be passed in, as long as
488
442
  that something presented the same API. In reality, using something other than
@@ -508,7 +462,7 @@ and send us a [GitHub pull request] [Pull Request] with your changes.
508
462
 
509
463
  ## Copyright ##
510
464
 
511
- Copyright &copy; 2011 Renewable Funding, LLC. See LICENSE.txt for
465
+ Copyright &copy; 2011 John Wilger. See LICENSE.txt for
512
466
  further details.
513
467
 
514
468
  [Window Driver]: http://martinfowler.com/eaaDev/WindowDriver.html "Window Driver - Martin Fowler"