kookaburra 0.20.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -30,7 +30,7 @@ server could be running on the same machine, it doesn't need to be), and it is
30
30
  the responsibility of the test implementation to ensure that the server is
31
31
  running. Take a look at Kookaburra's own integration specs for one example of
32
32
  how to achieve this for a [Rack-based] [Rack] application. (Note that you cannot
33
- easily start the application server in a seperate thread. Because Ruby uses
33
+ easily start the application server in a separate thread. Because Ruby uses
34
34
  green threads, the HTTP library used in the APIDriver will block while making
35
35
  its requests and prevent the application server thread from responding.)
36
36
 
@@ -284,6 +284,13 @@ called on the object. The `MentalModel::Collection` object behaves like a `Hash`
284
284
  for the most part, however it will raise a `Kookaburra::UnknownKeyError` if you
285
285
  try to access a key that has not yet been assigned a value.
286
286
 
287
+ Deletions (via `#delete` or `#delete_if`) will actually remove the key/value
288
+ pair from the collection, but add it to a subcollection (available at
289
+ `MentalModel::Collection#deleted`). This reflects the fact that the user's
290
+ mental model of the dataset would also include any intentional exceptions -
291
+ the user will, for example, want to verify that an item they deleted does
292
+ not appear to be available in the system.
293
+
287
294
  Here's a quick example of MentalModel behavor:
288
295
 
289
296
  mental_model = MentalModel.new
@@ -292,10 +299,20 @@ Here's a quick example of MentalModel behavor:
292
299
 
293
300
  mental_model.widgets[:widget_a]
294
301
  #=> {'name' => 'Widget A'}
295
-
302
+
296
303
  # this will raise a Kookaburra::UnknownKeyError
297
304
  mental_model.widgets[:widget_b]
298
305
 
306
+ mental_model.widgets.delete(:widget_a)
307
+ #=> {'name' => 'Widget A'}
308
+
309
+ # this will now also raise a Kookaburra::UnknownKeyError...
310
+ mental_model.widgets[:widget_a]
311
+
312
+ # ...but the pair is now available here:
313
+ mental_model.widgets.deleted[:widget_a]
314
+ #=> {'name' => 'Widget A'}
315
+
299
316
  #### Given Driver ####
300
317
 
301
318
  The `Kookaburra::GivenDriver` is used to create a particular "preexisting" state
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.20.0
1
+ 0.21.0
data/kookaburra.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "kookaburra"
8
- s.version = "0.20.0"
8
+ s.version = "0.21.0"
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"]
12
- s.date = "2012-03-22"
12
+ s.date = "2012-03-25"
13
13
  s.description = "Cucumber + Capybara = Kookaburra? It made sense at the time."
14
14
  s.email = "johnwilger@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -1,4 +1,5 @@
1
1
  require 'delegate'
2
+ require 'kookaburra/exceptions'
2
3
 
3
4
  class Kookaburra
4
5
  # Each instance of {Kookaburra} has its own instance of MentalModel. This object
@@ -81,6 +82,45 @@ class Kookaburra
81
82
  self[key]
82
83
  end
83
84
  end
85
+
86
+ # Deletes a key/value pair from the collection, and persists the deleted pair
87
+ # in a subcollection.
88
+ #
89
+ # Deleting a key/value pair from a collection on the MentalModel works just
90
+ # like `Hash#delete` but with a side effect - deleted members are added to
91
+ # a subcollection, accessible at `#deleted`.
92
+ #
93
+ # @param key the key to delete from the collection
94
+ #
95
+ # @return the value of the deleted key/value pair
96
+ #
97
+ # @raise [Kookaburra::UnknownKeyError] if the specified key has not been set
98
+ def delete(key, &block)
99
+ self[key] # simple fetch to possibly trigger UnknownKeyError
100
+ deleted[key] = super
101
+ end
102
+
103
+ # Finds or initializes, and returns, the subcollection of deleted items
104
+ #
105
+ # Key/value pairs `#delete`d from a collection on the MentalModel will be added
106
+ # to this subcollection.
107
+ #
108
+ # @return [Kookaburra::MentalModel::Collection] the deleted items subcollection
109
+ def deleted
110
+ @deleted ||= self.class.new("deleted")
111
+ end
112
+
113
+ # Deletes key/value pairs from the collection for which the given block evaluates
114
+ # to true, and persists all deleted pairs in a subcollection.
115
+ #
116
+ # Works just like `Hash#delete_if` but with a side effect - deleted members are
117
+ # added to a subcollection, accessible at `#deleted`.
118
+ #
119
+ # @return [Hash] the key/value pairs still remaining after the deletion
120
+ def delete_if(&block)
121
+ move = lambda { |k,v| deleted[k] = v; true }
122
+ super { |k,v| block.call(k,v) && move.call(k,v) }
123
+ end
84
124
  end
85
125
  end
86
126
  end
@@ -1,4 +1,5 @@
1
1
  require 'basic_object'
2
+ require 'kookaburra/exceptions'
2
3
 
3
4
  class Kookaburra
4
5
  # If you don't specify a browser in your {Kookaburra#configuration} but you
@@ -36,6 +36,25 @@ class Kookaburra
36
36
  # end
37
37
  # end
38
38
  # end
39
+ #
40
+ # With larger applications, it may be beneficial to break down your business
41
+ # actions into multiple classes. The top-level {UIDriver} can have sub-drivers
42
+ # associated with it (and those can have sub-drivers, too; but let's not get
43
+ # carried away, eh?):
44
+ #
45
+ # class AccountManagementDriver < Kookaburra::UIDriver
46
+ # ui_component :account_list, AccountList
47
+ # # ...
48
+ # end
49
+ #
50
+ # class MyUIDriver < Kookaburra::UIDriver
51
+ # ui_driver :account_management, AccountManagementDriver
52
+ # # ...
53
+ # end
54
+ #
55
+ # In your test implementation, you can then do (among other things):
56
+ #
57
+ # ui.account_management.account_list.should be_visible
39
58
  class UIDriver
40
59
  extend DependencyAccessor
41
60
 
@@ -48,8 +67,19 @@ class Kookaburra
48
67
  # this component.
49
68
  def ui_component(component_name, component_class)
50
69
  define_method(component_name) do
51
- component_class.new(:browser => @browser, :server_error_detection => @server_error_detection,
52
- :app_host => @app_host)
70
+ component_class.new(options.slice(:browser, :server_error_detection, :app_host))
71
+ end
72
+ end
73
+
74
+ # Tells the UIDriver about sub-drivers (other {UIDriver} subclasses).
75
+ #
76
+ # @param [Symbol] driver_name Will create an instance method of this
77
+ # name that returns an instance of the driver_class
78
+ # @param [Class] driver_class The {UIDriver} subclass that defines
79
+ # this driver.
80
+ def ui_driver(driver_name, driver_class)
81
+ define_method(driver_name) do
82
+ driver_class.new(options.slice(:browser, :server_error_detection, :app_host, :mental_model))
53
83
  end
54
84
  end
55
85
  end
@@ -66,14 +96,15 @@ class Kookaburra
66
96
  # `:browser` object and should return `true` if the page indicates a server
67
97
  # error has occured
68
98
  def initialize(options = {})
69
- @browser = options[:browser]
70
- @app_host = options[:app_host]
99
+ @options = options
71
100
  @mental_model = options[:mental_model]
72
- @server_error_detection = options[:server_error_detection]
73
101
  end
74
102
 
75
103
  protected
76
104
 
105
+ # Provides access to the options with which the object was initialized
106
+ attr_reader :options
107
+
77
108
  # @attribute [r] mental_model
78
109
  dependency_accessor :mental_model
79
110
  end
@@ -1,4 +1,5 @@
1
1
  require 'kookaburra/exceptions'
2
+ require 'active_support/core_ext/object/try'
2
3
 
3
4
  class Kookaburra
4
5
  class UIDriver
@@ -23,6 +23,60 @@ describe Kookaburra::MentalModel do
23
23
  end
24
24
  end
25
25
 
26
+ describe '#delete' do
27
+ it 'deletes and returns the item matching the specified key' do
28
+ collection[:baz] = 'baz'
29
+ collection.delete(:baz).should == 'baz'
30
+ lambda { collection[:baz] }.should raise_error(Kookaburra::UnknownKeyError)
31
+ end
32
+
33
+ it 'persists the deleted key/value pair to the #deleted subcollection' do
34
+ collection[:baz] = 'baz'
35
+ collection.delete(:baz)
36
+ collection.deleted[:baz].should == 'baz'
37
+ end
38
+
39
+ it 'raises a Kookaburra::UnknownKeyError exception if trying to delete a missing key' do
40
+ lambda { collection.delete(:snerf) }.should \
41
+ raise_error(Kookaburra::UnknownKeyError, "Can't find mental_model.widgets[:snerf]. Did you forget to set it?")
42
+ end
43
+ end
44
+
45
+ describe '#delete_if' do
46
+ before(:each) do
47
+ collection[:foo] = 'foo'
48
+ collection[:bar] = 'spoon'
49
+ collection[:baz] = 'baz'
50
+ end
51
+
52
+ it 'deletes all members of collection for whom given block evaluates to false' do
53
+ collection.delete_if { |k,v| k.to_s != v }
54
+ collection.keys.should =~ [:foo, :baz]
55
+ end
56
+
57
+ it 'adds deleted members of collection to #deleted subcollection' do
58
+ collection.delete_if { |k,v| k.to_s != v }
59
+ collection.deleted.keys.should == [:bar]
60
+ end
61
+
62
+ it 'returns hash of items not deleted' do
63
+ collection.delete_if { |k,v| k.to_s != v }.should == { :foo => 'foo', :baz => 'baz' }
64
+ end
65
+ end
66
+
67
+ describe '#deleted' do
68
+ it 'generates a new subcollection if none exists' do
69
+ initialized_collection = collection
70
+ Kookaburra::MentalModel::Collection.should_receive(:new).with("deleted")
71
+ initialized_collection.deleted
72
+ end
73
+
74
+ it 'returns the deleted subcollection if already initialized' do
75
+ deleted_collection = collection.deleted
76
+ collection.deleted.should === deleted_collection
77
+ end
78
+ end
79
+
26
80
  it 'raises a Kookaburra::UnknownKeyError exception for #[] with a missing key' do
27
81
  lambda { collection[:foo] }.should \
28
82
  raise_error(Kookaburra::UnknownKeyError, "Can't find mental_model.widgets[:foo]. Did you forget to set it?")
@@ -20,6 +20,24 @@ describe Kookaburra::UIDriver do
20
20
  end
21
21
  end
22
22
 
23
+ describe '.ui_driver' do
24
+ it 'adds an accessor method for the named driver that defaults to an instance of the specified class' do
25
+ foo_driver_class = mock(Class)
26
+ foo_driver_class.should_receive(:new) \
27
+ .with(:browser => :a_browser, :server_error_detection => :server_error_detection,
28
+ :app_host => :a_url, :mental_model => :a_mental_model) \
29
+ .and_return(:a_foo_driver)
30
+
31
+ ui_driver_class = Class.new(Kookaburra::UIDriver) do
32
+ ui_driver :foo, foo_driver_class
33
+ end
34
+
35
+ ui = ui_driver_class.new(:browser => :a_browser, :server_error_detection => :server_error_detection,
36
+ :app_host => :a_url, :mental_model => :a_mental_model)
37
+ ui.foo.should == :a_foo_driver
38
+ end
39
+ end
40
+
23
41
  describe 'dependency accessors' do
24
42
  let(:subject_class) { Kookaburra::UIDriver }
25
43
 
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: 79
4
+ hash: 75
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 20
8
+ - 21
9
9
  - 0
10
- version: 0.20.0
10
+ version: 0.21.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - John Wilger
@@ -17,7 +17,7 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2012-03-22 00:00:00 Z
20
+ date: 2012-03-25 00:00:00 Z
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
23
23
  version_requirements: &id001 !ruby/object:Gem::Requirement