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 +19 -2
- data/VERSION +1 -1
- data/kookaburra.gemspec +2 -2
- data/lib/kookaburra/mental_model.rb +40 -0
- data/lib/kookaburra/null_browser.rb +1 -0
- data/lib/kookaburra/ui_driver.rb +36 -5
- data/lib/kookaburra/ui_driver/ui_component.rb +1 -0
- data/spec/kookaburra/mental_model_spec.rb +54 -0
- data/spec/kookaburra/ui_driver_spec.rb +18 -0
- metadata +4 -4
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
|
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.
|
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.
|
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-
|
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
|
data/lib/kookaburra/ui_driver.rb
CHANGED
@@ -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
|
52
|
-
|
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
|
-
@
|
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
|
@@ -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:
|
4
|
+
hash: 75
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 21
|
9
9
|
- 0
|
10
|
-
version: 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-
|
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
|