cukesalad 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/Examples/Calculator/Gemfile +5 -0
- data/Examples/Calculator/Gemfile.lock +66 -0
- data/Examples/Calculator/cucumber.yml +4 -0
- data/Examples/Calculator/features/LOOK_MA_NO_STEP_DEFS.txt +2 -0
- data/Examples/Calculator/features/a_place_to_start.feature +9 -0
- data/Examples/Calculator/features/addition.feature +19 -0
- data/Examples/Calculator/features/complex_calculations.feature +16 -0
- data/Examples/Calculator/features/lib/alternative/roles/calculating_web_user.rb +55 -0
- data/Examples/Calculator/features/lib/default/roles/calculating_individual.rb +32 -0
- data/Examples/Calculator/features/lib/default/tasks/add.rb +13 -0
- data/Examples/Calculator/features/lib/default/tasks/calculate.rb +4 -0
- data/Examples/Calculator/features/lib/default/tasks/calculations.rb +15 -0
- data/Examples/Calculator/features/lib/default/tasks/see_the_answer.rb +3 -0
- data/Examples/Calculator/features/lib/default/tasks/subtract.rb +4 -0
- data/Examples/Calculator/features/lib/default/tasks/switch_on_the_calculator.rb +9 -0
- data/Examples/Calculator/features/subtraction.feature +20 -0
- data/Examples/Calculator/features/support/env.rb +6 -0
- data/Examples/Calculator/features/typical_workflow.feature +18 -0
- data/Examples/Calculator/lib/calculator.rb +53 -0
- data/Examples/Calculator/lib/config.ru +4 -0
- data/Examples/Calculator/lib/web_calculator.rb +82 -0
- data/Examples/Calculator/spec/calculator_spec.rb +73 -0
- data/Examples/Calculator/spec/web_calculator_spec.rb +99 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +47 -0
- data/LICENSE +21 -0
- data/README.md +273 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/bin/cukesalad +28 -0
- data/cucumber.yml +4 -0
- data/cukesalad/cli.rb +68 -0
- data/examples/Calculator/features/lib/alternative/roles/calculating_web_user.rb +55 -0
- data/examples/Calculator/features/lib/default/roles/calculating_individual.rb +32 -0
- data/examples/Calculator/features/lib/default/tasks/add.rb +13 -0
- data/examples/Calculator/features/lib/default/tasks/calculate.rb +4 -0
- data/examples/Calculator/features/lib/default/tasks/calculations.rb +15 -0
- data/examples/Calculator/features/lib/default/tasks/see_the_answer.rb +3 -0
- data/examples/Calculator/features/lib/default/tasks/subtract.rb +4 -0
- data/examples/Calculator/features/lib/default/tasks/switch_on_the_calculator.rb +9 -0
- data/examples/Calculator/features/support/env.rb +6 -0
- data/examples/Calculator/lib/calculator.rb +53 -0
- data/examples/Calculator/lib/web_calculator.rb +82 -0
- data/examples/Calculator/spec/calculator_spec.rb +73 -0
- data/examples/Calculator/spec/web_calculator_spec.rb +99 -0
- data/features/a_new_cukesalad_project.feature +34 -0
- data/features/define_a_role.feature +31 -0
- data/features/define_a_task.feature +55 -0
- data/features/lib/roles/step_free_cuker.rb +5 -0
- data/features/lib/tasks/create_a_file.rb +3 -0
- data/features/lib/tasks/create_a_new_cukesalad_project.rb +11 -0
- data/features/lib/tasks/create_a_role.rb +4 -0
- data/features/lib/tasks/create_a_task.rb +4 -0
- data/features/lib/tasks/create_directories.rb +5 -0
- data/features/lib/tasks/not_create_a_role.rb +2 -0
- data/features/lib/tasks/not_create_a_task.rb +3 -0
- data/features/lib/tasks/run.rb +4 -0
- data/features/lib/tasks/run_a_scenario.rb +7 -0
- data/features/lib/tasks/see_a_reply.rb +3 -0
- data/features/lib/tasks/see_the_step_has.rb +4 -0
- data/features/support/env.rb +7 -0
- data/lib/actor.rb +32 -0
- data/lib/codify/const_name.rb +21 -0
- data/lib/cukesalad.rb +33 -0
- data/lib/director.rb +28 -0
- data/lib/specifics.rb +30 -0
- data/lib/task_author.rb +12 -0
- data/spec/actor_spec.rb +72 -0
- data/spec/codify/as_const_name_spec.rb +28 -0
- data/spec/cukesalad/cli_spec.rb +99 -0
- data/spec/director_spec.rb +34 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/specifics_spec.rb +36 -0
- data/spec/task_author_spec.rb +50 -0
- metadata +202 -0
data/Gemfile.lock
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
aruba (0.3.5)
|
5
|
+
childprocess (>= 0.1.7)
|
6
|
+
cucumber (>= 0.10.0)
|
7
|
+
rspec (>= 2.5.0)
|
8
|
+
builder (3.0.0)
|
9
|
+
childprocess (0.1.7)
|
10
|
+
ffi (~> 0.6.3)
|
11
|
+
cucumber (0.10.0)
|
12
|
+
builder (>= 2.1.2)
|
13
|
+
diff-lcs (~> 1.1.2)
|
14
|
+
gherkin (~> 2.3.2)
|
15
|
+
json (~> 1.4.6)
|
16
|
+
term-ansicolor (~> 1.0.5)
|
17
|
+
diff-lcs (1.1.2)
|
18
|
+
ffi (0.6.3)
|
19
|
+
rake (>= 0.8.7)
|
20
|
+
gherkin (2.3.3)
|
21
|
+
json (~> 1.4.6)
|
22
|
+
git (1.2.5)
|
23
|
+
jeweler (1.5.2)
|
24
|
+
bundler (~> 1.0.0)
|
25
|
+
git (>= 1.2.5)
|
26
|
+
rake
|
27
|
+
json (1.4.6)
|
28
|
+
rake (0.8.7)
|
29
|
+
rspec (2.5.0)
|
30
|
+
rspec-core (~> 2.5.0)
|
31
|
+
rspec-expectations (~> 2.5.0)
|
32
|
+
rspec-mocks (~> 2.5.0)
|
33
|
+
rspec-core (2.5.1)
|
34
|
+
rspec-expectations (2.5.0)
|
35
|
+
diff-lcs (~> 1.1.2)
|
36
|
+
rspec-mocks (2.5.0)
|
37
|
+
term-ansicolor (1.0.5)
|
38
|
+
|
39
|
+
PLATFORMS
|
40
|
+
ruby
|
41
|
+
|
42
|
+
DEPENDENCIES
|
43
|
+
aruba (= 0.3.5)
|
44
|
+
bundler (~> 1.0.0)
|
45
|
+
cucumber (= 0.10.0)
|
46
|
+
jeweler (~> 1.5.2)
|
47
|
+
rspec (= 2.5.0)
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2010 RiverGlide
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/README.md
ADDED
@@ -0,0 +1,273 @@
|
|
1
|
+
# Cuke Salad
|
2
|
+
|
3
|
+
_Cucumber, washed and ready to eat for Friction-free ATDD/BDD_
|
4
|
+
|
5
|
+
**This is a work in progress - feedback welcome**
|
6
|
+
e-mail feedback to <talktous@riverglide.com>
|
7
|
+
|
8
|
+
** ToDo: **
|
9
|
+
|
10
|
+
* Support more step structures - such as tables as input
|
11
|
+
* Move beyond current examples by documenting CukeSalad with Cucumber
|
12
|
+
* Remembering data between steps
|
13
|
+
* Multiple role/actor scenarios
|
14
|
+
|
15
|
+
## This project has step-free access!
|
16
|
+
|
17
|
+
CukeSalad allows you to focus on the task at hand - expressing examples, the roles involved in those examples and what those roles can do with the product under development.
|
18
|
+
|
19
|
+
With CukeSalad you don't need to write step-definitions.
|
20
|
+
|
21
|
+
Of course, you still have to write some code - but only the code that expresses:
|
22
|
+
|
23
|
+
* the roles and the actions they can perform
|
24
|
+
* the tasks and the actions involved in completing that task
|
25
|
+
|
26
|
+
## Goals->Tasks->Actions
|
27
|
+
The terms *"actions"* and *"tasks"* above come from Task Analysis, as used in User Centred Design (UCD) of Human Computer Interfaces (HCI) a.k.a. User Experience (UX):
|
28
|
+
|
29
|
+
* *Goal:* What we’re trying to achieve which has one or more…
|
30
|
+
* *Tasks:* The high-level work-item that we complete to fulfil the goal, each having one or more…
|
31
|
+
* *Actions:* The specific steps or interactions we execute to complete the task.
|
32
|
+
|
33
|
+
More information about Goals, Tasks and Actions can be found in this [blog post](http://antonymarcano.com/blog/2011/03/goals-tasks-action/)
|
34
|
+
|
35
|
+
Let's see how this works with a simple example...
|
36
|
+
|
37
|
+
## Install
|
38
|
+
|
39
|
+
gem install cukesalad
|
40
|
+
|
41
|
+
## Let's Get started
|
42
|
+
|
43
|
+
Create a new project Calculator:
|
44
|
+
|
45
|
+
mkdir Calculator
|
46
|
+
cd Calculator
|
47
|
+
mkdir features
|
48
|
+
mkdir features/support
|
49
|
+
mkdir features/lib
|
50
|
+
mkdir features/lib/tasks
|
51
|
+
mkdir features/lib/roles
|
52
|
+
|
53
|
+
In idiomatic Cucumber style, we use `features/support/env.rb` to require _CukeSalad_:
|
54
|
+
|
55
|
+
require 'cukesalad'
|
56
|
+
begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end
|
57
|
+
|
58
|
+
Cucumber will automatically find our project's _roles_ and _tasks_, as it loads
|
59
|
+
all .rb files beneath the project's `features/` directory.
|
60
|
+
|
61
|
+
## Write Features
|
62
|
+
|
63
|
+
In `features/`, let's create our first feature file - `a_place_to_start.feature`:
|
64
|
+
|
65
|
+
Feature: A Place To Start
|
66
|
+
As Callie, a calculating individual
|
67
|
+
I want to know when my calculator is on
|
68
|
+
So that I know when I can start calculating
|
69
|
+
|
70
|
+
Scenario: Let's Begin
|
71
|
+
Given I am a Calculating Individual
|
72
|
+
When I attempt to switch on the calculator
|
73
|
+
Then I should see the answer '0'
|
74
|
+
|
75
|
+
Let's take a moment to understand this scenario:
|
76
|
+
|
77
|
+
Scenario: Let's Begin
|
78
|
+
Given I am a <some role>
|
79
|
+
When I attempt to <do some task>
|
80
|
+
Then I should <ask some question> '<expected answer>'
|
81
|
+
|
82
|
+
To get this working, we don't need to write any steps.
|
83
|
+
Just explain how to do the _task_ using a class...
|
84
|
+
|
85
|
+
## Create Tasks
|
86
|
+
|
87
|
+
Explaining how to do a _task_ is easy:
|
88
|
+
Create a new file, `features/lib/tasks/switch_on_the_calculator.rb`
|
89
|
+
|
90
|
+
Remember the step `When I attempt to switch on the calculator`
|
91
|
+
|
92
|
+
in_order_to "SwitchOnTheCalculator" do
|
93
|
+
@calc = switch_on_the_calculator
|
94
|
+
end
|
95
|
+
|
96
|
+
Remember the step `Then I should see the answer '0'`
|
97
|
+
Now we need `task/see_the_answer.rb`
|
98
|
+
|
99
|
+
in_order_to "SeeTheAnswer" do
|
100
|
+
look_at_the_display
|
101
|
+
end
|
102
|
+
|
103
|
+
Now we've explained the _tasks_, we need to define the _role_ that performs them. In
|
104
|
+
this example, we need to explain how the `CalculatingIndividual` _role_ works...
|
105
|
+
|
106
|
+
## Create Roles
|
107
|
+
|
108
|
+
Remember the step `Given I am a Calculating Individual`?
|
109
|
+
|
110
|
+
We explain a _role_ by creating a new file
|
111
|
+
called `features/lib/roles/calculating_individual.rb`
|
112
|
+
|
113
|
+
module CalculatingIndividual
|
114
|
+
|
115
|
+
def switch_on_the_calculator
|
116
|
+
@calculator = Calculator.new
|
117
|
+
end
|
118
|
+
|
119
|
+
def look_at_the_display
|
120
|
+
@calculator.display
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
You'll need a class called Calculator on the load path of course, but that's it.
|
125
|
+
|
126
|
+
From your project folder, run (_note: '%' is our command prompt_)
|
127
|
+
|
128
|
+
% cucumber
|
129
|
+
|
130
|
+
We now have our first passing Feature, without creating a single step definition!
|
131
|
+
|
132
|
+
## Wash, rinse, repeat
|
133
|
+
|
134
|
+
Let's try another scenario...
|
135
|
+
|
136
|
+
Scenario Outline: Find the sum of two numbers
|
137
|
+
Given I am a Calculating Individual
|
138
|
+
And I was able to switch on the calculator
|
139
|
+
When I attempt to add: the number '10' to the number '10'
|
140
|
+
Then I should see the answer '20'
|
141
|
+
|
142
|
+
Notice that we've reused 'switch on the calculator'.
|
143
|
+
|
144
|
+
The new _When_ step has a slightly different layout.
|
145
|
+
Let's examine that for a second... Notice the ':' (colon) after <do something> and the name-value pairs:
|
146
|
+
|
147
|
+
When I attempt to <do something>: <name> '<value>' <name> '<value>'
|
148
|
+
|
149
|
+
You can have as many name-value pairs as you like.
|
150
|
+
|
151
|
+
So, we need a task called `tasks/add.rb` that explains the individual actions required to complete the task:
|
152
|
+
|
153
|
+
in_order_to "Add" do
|
154
|
+
enter @value_of(:the_number)
|
155
|
+
press :plus
|
156
|
+
enter @value_of(:to_the_number)
|
157
|
+
press :equals
|
158
|
+
end
|
159
|
+
|
160
|
+
Notice how the `value_of` lines use symbols that correspond to the wording `'the number '10' to the number '10'` in the "When" step.
|
161
|
+
|
162
|
+
There is some 'syntactic sugar' that we can use to dress this up a little and make it read nicer... a simple attribute mapping:
|
163
|
+
|
164
|
+
in_order_to "Add", the_number: :first_number, to_the_number: :second_number do
|
165
|
+
enter the :first_number
|
166
|
+
press :plus
|
167
|
+
enter the :second_number
|
168
|
+
press :equals
|
169
|
+
end
|
170
|
+
|
171
|
+
All we've done is mapped "the_number" to "first_number". There is a special method called "the" that allows you to reference the mapped values rather than the symbols derived from the scenario.
|
172
|
+
|
173
|
+
Now all we need to do is modify our `calculating_individual.rb` to receive those calls...
|
174
|
+
|
175
|
+
module CalculatingIndividual
|
176
|
+
|
177
|
+
def switch_on_the_calculator
|
178
|
+
@calculator = Calculator.new
|
179
|
+
@operate_with = {
|
180
|
+
plus: :+,
|
181
|
+
minus: :-
|
182
|
+
}
|
183
|
+
end
|
184
|
+
|
185
|
+
def enter value
|
186
|
+
@calculator.enter value.to_i
|
187
|
+
end
|
188
|
+
|
189
|
+
def press next_operator
|
190
|
+
if next_operator == :equals
|
191
|
+
equals
|
192
|
+
else
|
193
|
+
@calculator.get_ready_to @operate_with[next_operator]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def equals
|
198
|
+
@calculator.equals
|
199
|
+
end
|
200
|
+
|
201
|
+
def look_at_the_display
|
202
|
+
@calculator.display
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
Now, you can run cucumber again:
|
207
|
+
|
208
|
+
% cucumber
|
209
|
+
|
210
|
+
There's no need to write `step_definitions`...
|
211
|
+
Simply express the _roles_ and the _tasks_ in clear,
|
212
|
+
concise, easy to read classes.
|
213
|
+
|
214
|
+
After adding some more scenarios and tasks and an alternative "Calculating Individual" (see below for why), our finished Calculator directory structure looks like this...
|
215
|
+
|
216
|
+
├── cucumber.yml
|
217
|
+
├── features
|
218
|
+
│ ├── A_PlaceToStart.feature
|
219
|
+
│ ├── Addition.feature
|
220
|
+
│ ├── Complex_calculations.feature
|
221
|
+
│ ├── LOOK_MA_NO_STEP_DEFS.txt
|
222
|
+
│ ├── Subtraction.feature
|
223
|
+
│ ├── Typical_workflow.feature
|
224
|
+
│ ├── lib
|
225
|
+
│ │ ├── alternative
|
226
|
+
│ │ │ ├── roles
|
227
|
+
│ │ │ │ └── calculating_web_user.rb
|
228
|
+
│ │ │ └── tasks
|
229
|
+
│ │ └── default
|
230
|
+
│ │ ├── roles
|
231
|
+
│ │ │ └── calculating_individual.rb
|
232
|
+
│ │ └── tasks
|
233
|
+
│ │ ├── add.rb
|
234
|
+
│ │ ├── perform.rb
|
235
|
+
│ │ ├── see_the_answer.rb
|
236
|
+
│ │ ├── subtract.rb
|
237
|
+
│ │ └── switch_on_the_calculator.rb
|
238
|
+
│ └── support
|
239
|
+
│ └── env.rb
|
240
|
+
├── lib
|
241
|
+
│ ├── calculator.rb
|
242
|
+
│ └── web_calculator.rb
|
243
|
+
└── spec
|
244
|
+
├── calculator_spec.rb
|
245
|
+
└── web_calculator_spec.rb
|
246
|
+
|
247
|
+
Take a look around the examples and the code to see how it all works. We hope you enjoy reading as much as we enjoyed writing it.
|
248
|
+
|
249
|
+
## Alternative Roles
|
250
|
+
|
251
|
+
As our features _describe the value of a calculator application and not its
|
252
|
+
implementation_, we have the opportunity to reuse them.
|
253
|
+
|
254
|
+
In the Calculator example, we create a new _role_ in
|
255
|
+
`./features/lib/alternative/roles/calculating_web_user.rb`, which we can swap
|
256
|
+
into our tests using a Cucumber profile defined in `features/cucumber.yml`:
|
257
|
+
|
258
|
+
default --exclude features/lib/alternative/
|
259
|
+
alternative -r features/lib/alternative/ -r features/support/env.rb -r features/lib/default/tasks/
|
260
|
+
|
261
|
+
We can run our alternative configuration like so:
|
262
|
+
|
263
|
+
`%cucumber --profile alternative`
|
264
|
+
|
265
|
+
The Calculating Web User masquerades as the Calculating Individual from our
|
266
|
+
previous example, and provides the same API, allowing us to reuse all of our
|
267
|
+
existing features and _tasks_.
|
268
|
+
|
269
|
+
The alternative, `./lib/web_calculator.rb`, implementation is a simple [Sinatra](http://www.sinatrarb.com) application,
|
270
|
+
which we drive with the [Capybara](http://github.com/jnicklas/capybara) web testing framework.
|
271
|
+
|
272
|
+
By writing a single new _role_ class we're able to reuse all of our existing features,
|
273
|
+
_tasks_ and even the `Calculator` itself, which the web calculator delegates to in order to do its calculations.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "cukesalad"
|
16
|
+
gem.homepage = "https://github.com/RiverGlide/CukeSalad"
|
17
|
+
gem.platform = Gem::Platform::RUBY
|
18
|
+
gem.license = "MIT"
|
19
|
+
gem.summary = %Q{Friction Free BDD/ATDD with cucumber}
|
20
|
+
gem.description = %Q{CukeSalad allows you to focus on the tasks at hand - expressing examples, the roles involved in those examples and the tasks that those roles need to perform with the product under development.}
|
21
|
+
gem.email = "talktous@riverglide.com"
|
22
|
+
gem.authors = ["RiverGlide"]
|
23
|
+
|
24
|
+
# The following two lines need to be commented out in order to gain access to the version rake tasks
|
25
|
+
gem_version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
26
|
+
gem.version = gem_version
|
27
|
+
gem.executables = ["cukesalad"]
|
28
|
+
gem.files.include 'lib/cukesalad/cli.rb'
|
29
|
+
end
|
30
|
+
|
31
|
+
Jeweler::RubygemsDotOrgTasks.new
|
32
|
+
|
33
|
+
require 'rspec/core'
|
34
|
+
require 'rspec/core/rake_task'
|
35
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
36
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
37
|
+
spec.rspec_opts = "-cfd"
|
38
|
+
end
|
39
|
+
|
40
|
+
require 'cucumber/rake/task'
|
41
|
+
Cucumber::Rake::Task.new(:cucumber)
|
42
|
+
|
43
|
+
task :default => :spec
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "CukeSalad #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/bin/cukesalad
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'cukesalad/cli'
|
5
|
+
|
6
|
+
OptionParser.new do |opts|
|
7
|
+
opts.banner = "Usage: cukesalad [new | configure] project_name"
|
8
|
+
|
9
|
+
begin
|
10
|
+
opts.parse!(ARGV)
|
11
|
+
rescue OptionParser::ParseError => e
|
12
|
+
warn e.message
|
13
|
+
puts opts
|
14
|
+
exit 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
if ARGV.empty?
|
19
|
+
abort "Usage: cukesalad new <project name>\nOr: cukesalad configure"
|
20
|
+
elsif ARGV.first == 'new'
|
21
|
+
project = ARGV[1]
|
22
|
+
puts "Creating project #{project}..."
|
23
|
+
CukeSalad::CLI.create_new_project project
|
24
|
+
puts "Done!"
|
25
|
+
elsif ARGV.first == 'configure'
|
26
|
+
CukeSalad::CLI.configure_existing_project
|
27
|
+
puts "Done!"
|
28
|
+
end
|
data/cucumber.yml
ADDED
data/cukesalad/cli.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'aruba/api'
|
2
|
+
module CukeSalad
|
3
|
+
class CLI
|
4
|
+
|
5
|
+
def self.create_new_project project
|
6
|
+
structure = Structure.new
|
7
|
+
structure.setup project
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.configure_existing_project project=nil
|
11
|
+
`cd #{project}` if project
|
12
|
+
structure = Structure.new
|
13
|
+
structure.setup_cucumber_with_cukesalad
|
14
|
+
end
|
15
|
+
|
16
|
+
class Structure
|
17
|
+
include Aruba::Api
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
set_aruba_path_to_current
|
21
|
+
end
|
22
|
+
|
23
|
+
def setup project
|
24
|
+
create_and_navigate_to project
|
25
|
+
setup_cucumber_with_cukesalad
|
26
|
+
end
|
27
|
+
|
28
|
+
def setup_cucumber_with_cukesalad
|
29
|
+
create_dir_structure
|
30
|
+
configure
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def set_aruba_path_to_current
|
35
|
+
@dirs = ["./"]
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_dir_structure
|
39
|
+
create_cucumber_structure
|
40
|
+
create_cukesalad_structure
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_cucumber_structure
|
44
|
+
create_and_navigate_to "features"
|
45
|
+
create_dir "support"
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_cukesalad_structure
|
49
|
+
create_and_navigate_to "lib"
|
50
|
+
create_and_navigate_to "default"
|
51
|
+
create_dir "roles"
|
52
|
+
create_dir "tasks"
|
53
|
+
cd "../../../"
|
54
|
+
end
|
55
|
+
|
56
|
+
def configure
|
57
|
+
cd "features/support"
|
58
|
+
content = "require 'cukesalad'\n begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end"
|
59
|
+
append_to_file "env.rb",content
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_and_navigate_to directory
|
63
|
+
create_dir directory
|
64
|
+
cd directory
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# To run with this version of the Calculating Individual:
|
2
|
+
# cucumber --profile alternative
|
3
|
+
|
4
|
+
require 'web_calculator'
|
5
|
+
require 'capybara'
|
6
|
+
require 'capybara/dsl'
|
7
|
+
|
8
|
+
Capybara.app = WebCalculator
|
9
|
+
Capybara.default_driver = :rack_test
|
10
|
+
|
11
|
+
module CalculatingIndividual
|
12
|
+
|
13
|
+
# Uses a Browser to perform its tasks
|
14
|
+
include Capybara
|
15
|
+
# This is not a page object. It is a WebUser, specialised to our application
|
16
|
+
# See the comments in the 'enter' and 'press' method for an explanation of why
|
17
|
+
|
18
|
+
def switch_on_the_calculator
|
19
|
+
visit '/'
|
20
|
+
end
|
21
|
+
|
22
|
+
def enter value
|
23
|
+
fill_in 'number', :with => value
|
24
|
+
# Because the calculator is a simple web_app with only
|
25
|
+
# one page we can put the id for the field here. In a more complex web-app
|
26
|
+
# we would pass in the id of the thing to enter
|
27
|
+
# The task would express the action as: enter( :the_number, "10")
|
28
|
+
# Because we can change the id used in our application,
|
29
|
+
# we would just change the id in the application to be 'the_number'.
|
30
|
+
# If we were on a project where we could not change the id
|
31
|
+
# in the application we would write an 'ApplicationExpert'
|
32
|
+
# to map :the_number to the id for the field 'number'. Our CalculatingIndividual
|
33
|
+
# would ask the ApplicationExpert for the id of the symbol :the_number
|
34
|
+
# See 'press' below for am explanation of how an ApplicationExpert
|
35
|
+
# would work
|
36
|
+
end
|
37
|
+
|
38
|
+
def press operator
|
39
|
+
click_button operator.to_s
|
40
|
+
# Here, our 'operator' happens to also match the id for the button.
|
41
|
+
# If we were on a project where we could not choose what the id
|
42
|
+
# was for buttons, we would write an ApplicationExpert to map
|
43
|
+
# the symbol used in the task to the symbol for that button.
|
44
|
+
# The ApplicationExpert would only need to know what page it
|
45
|
+
# was on to find a mappings class (or yml file) for the fields on that page.
|
46
|
+
end
|
47
|
+
|
48
|
+
def look_at_the_display
|
49
|
+
find_field('display').value.to_i
|
50
|
+
end
|
51
|
+
|
52
|
+
def is_the_calculator_ready?
|
53
|
+
page.body.should =~ /Calculator is Ready!/
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'calculator'
|
2
|
+
|
3
|
+
module CalculatingIndividual
|
4
|
+
|
5
|
+
def switch_on_the_calculator
|
6
|
+
@calculator = Calculator.new
|
7
|
+
@operate_with = {
|
8
|
+
plus: :+,
|
9
|
+
minus: :-
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def enter value
|
14
|
+
@calculator.enter value.to_i
|
15
|
+
end
|
16
|
+
|
17
|
+
def press next_operator
|
18
|
+
if next_operator == :equals
|
19
|
+
equals
|
20
|
+
else
|
21
|
+
@calculator.get_ready_to @operate_with[next_operator]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def equals
|
26
|
+
@calculator.equals
|
27
|
+
end
|
28
|
+
|
29
|
+
def look_at_the_display
|
30
|
+
@calculator.display
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# This demonstrates how to use an attribute mapping
|
2
|
+
# to make the interactions within the task read more naturally
|
3
|
+
# In the scenario it might say "add: the number '5', to the number '10'
|
4
|
+
# The form illustrated below simply maps the elements the_number and to_the_number
|
5
|
+
# to alternative symbols that make the interactions read more like real instructions
|
6
|
+
# See subtract.rb for a way of writing tasks that reuses the common interactions
|
7
|
+
# required to perform calculations.
|
8
|
+
in_order_to 'add', the_number: :first_number, to_the_number: :second_number do
|
9
|
+
enter the :first_number
|
10
|
+
press :plus
|
11
|
+
enter the :second_number
|
12
|
+
press :equals
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Calculations
|
2
|
+
|
3
|
+
def follow_the_steps_for sum
|
4
|
+
enter_numbers_and_operators_for sum
|
5
|
+
end
|
6
|
+
|
7
|
+
def enter_numbers_and_operators_for sum
|
8
|
+
operator = {"+" => :plus, "-" => :minus, "=" => :equals}
|
9
|
+
sum.each do | token |
|
10
|
+
enter token.to_i if token =~ /\d+/
|
11
|
+
press operator[token] if operator.include? token
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,6 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../../lib') #where to find the calculator implementation
|
2
|
+
$:.unshift(File.dirname(__FILE__) + '/../../../../lib') #where to find CukeSalad
|
3
|
+
|
4
|
+
require 'cukesalad'
|
5
|
+
|
6
|
+
begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end
|