cukesalad 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/.rspec +1 -0
  2. data/Examples/Calculator/Gemfile +5 -0
  3. data/Examples/Calculator/Gemfile.lock +66 -0
  4. data/Examples/Calculator/cucumber.yml +4 -0
  5. data/Examples/Calculator/features/LOOK_MA_NO_STEP_DEFS.txt +2 -0
  6. data/Examples/Calculator/features/a_place_to_start.feature +9 -0
  7. data/Examples/Calculator/features/addition.feature +19 -0
  8. data/Examples/Calculator/features/complex_calculations.feature +16 -0
  9. data/Examples/Calculator/features/lib/alternative/roles/calculating_web_user.rb +55 -0
  10. data/Examples/Calculator/features/lib/default/roles/calculating_individual.rb +32 -0
  11. data/Examples/Calculator/features/lib/default/tasks/add.rb +13 -0
  12. data/Examples/Calculator/features/lib/default/tasks/calculate.rb +4 -0
  13. data/Examples/Calculator/features/lib/default/tasks/calculations.rb +15 -0
  14. data/Examples/Calculator/features/lib/default/tasks/see_the_answer.rb +3 -0
  15. data/Examples/Calculator/features/lib/default/tasks/subtract.rb +4 -0
  16. data/Examples/Calculator/features/lib/default/tasks/switch_on_the_calculator.rb +9 -0
  17. data/Examples/Calculator/features/subtraction.feature +20 -0
  18. data/Examples/Calculator/features/support/env.rb +6 -0
  19. data/Examples/Calculator/features/typical_workflow.feature +18 -0
  20. data/Examples/Calculator/lib/calculator.rb +53 -0
  21. data/Examples/Calculator/lib/config.ru +4 -0
  22. data/Examples/Calculator/lib/web_calculator.rb +82 -0
  23. data/Examples/Calculator/spec/calculator_spec.rb +73 -0
  24. data/Examples/Calculator/spec/web_calculator_spec.rb +99 -0
  25. data/Gemfile +9 -0
  26. data/Gemfile.lock +47 -0
  27. data/LICENSE +21 -0
  28. data/README.md +273 -0
  29. data/Rakefile +53 -0
  30. data/VERSION +1 -0
  31. data/bin/cukesalad +28 -0
  32. data/cucumber.yml +4 -0
  33. data/cukesalad/cli.rb +68 -0
  34. data/examples/Calculator/features/lib/alternative/roles/calculating_web_user.rb +55 -0
  35. data/examples/Calculator/features/lib/default/roles/calculating_individual.rb +32 -0
  36. data/examples/Calculator/features/lib/default/tasks/add.rb +13 -0
  37. data/examples/Calculator/features/lib/default/tasks/calculate.rb +4 -0
  38. data/examples/Calculator/features/lib/default/tasks/calculations.rb +15 -0
  39. data/examples/Calculator/features/lib/default/tasks/see_the_answer.rb +3 -0
  40. data/examples/Calculator/features/lib/default/tasks/subtract.rb +4 -0
  41. data/examples/Calculator/features/lib/default/tasks/switch_on_the_calculator.rb +9 -0
  42. data/examples/Calculator/features/support/env.rb +6 -0
  43. data/examples/Calculator/lib/calculator.rb +53 -0
  44. data/examples/Calculator/lib/web_calculator.rb +82 -0
  45. data/examples/Calculator/spec/calculator_spec.rb +73 -0
  46. data/examples/Calculator/spec/web_calculator_spec.rb +99 -0
  47. data/features/a_new_cukesalad_project.feature +34 -0
  48. data/features/define_a_role.feature +31 -0
  49. data/features/define_a_task.feature +55 -0
  50. data/features/lib/roles/step_free_cuker.rb +5 -0
  51. data/features/lib/tasks/create_a_file.rb +3 -0
  52. data/features/lib/tasks/create_a_new_cukesalad_project.rb +11 -0
  53. data/features/lib/tasks/create_a_role.rb +4 -0
  54. data/features/lib/tasks/create_a_task.rb +4 -0
  55. data/features/lib/tasks/create_directories.rb +5 -0
  56. data/features/lib/tasks/not_create_a_role.rb +2 -0
  57. data/features/lib/tasks/not_create_a_task.rb +3 -0
  58. data/features/lib/tasks/run.rb +4 -0
  59. data/features/lib/tasks/run_a_scenario.rb +7 -0
  60. data/features/lib/tasks/see_a_reply.rb +3 -0
  61. data/features/lib/tasks/see_the_step_has.rb +4 -0
  62. data/features/support/env.rb +7 -0
  63. data/lib/actor.rb +32 -0
  64. data/lib/codify/const_name.rb +21 -0
  65. data/lib/cukesalad.rb +33 -0
  66. data/lib/director.rb +28 -0
  67. data/lib/specifics.rb +30 -0
  68. data/lib/task_author.rb +12 -0
  69. data/spec/actor_spec.rb +72 -0
  70. data/spec/codify/as_const_name_spec.rb +28 -0
  71. data/spec/cukesalad/cli_spec.rb +99 -0
  72. data/spec/director_spec.rb +34 -0
  73. data/spec/spec_helper.rb +8 -0
  74. data/spec/specifics_spec.rb +36 -0
  75. data/spec/task_author_spec.rb +50 -0
  76. 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
@@ -0,0 +1,4 @@
1
+ default: --tags ~@draft
2
+ all: features
3
+ autotest: --color
4
+ autotest-all: --color
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,4 @@
1
+ in_order_to "Calculate", with_the_following: :calculation do
2
+ see_how_to_do Calculations
3
+ follow_the_steps_for the( :calculation ).split(" ")
4
+ 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,3 @@
1
+ in_order_to "see the answer" do
2
+ look_at_the_display
3
+ end
@@ -0,0 +1,4 @@
1
+ in_order_to "Subtract", the_number: :subtractor, from_the_number: :subtractee do
2
+ see_how_to_do Calculations
3
+ follow_the_steps_for [the(:subtractee), "-", the(:subtractor), "="]
4
+ end
@@ -0,0 +1,9 @@
1
+ in_order_to "switch on the calculator" do
2
+ require 'rspec/expectations'
3
+ extend RSpec::Matchers
4
+
5
+ @calc = switch_on_the_calculator
6
+ self.should respond_to :enter
7
+ self.should respond_to :press
8
+ self.should respond_to :look_at_the_display
9
+ end
@@ -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