hypercuke 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +142 -0
- data/Rakefile +11 -0
- data/bin/hcu +19 -0
- data/hypercuke.gemspec +28 -0
- data/lib/hypercuke.rb +43 -0
- data/lib/hypercuke/adapter_definition.rb +29 -0
- data/lib/hypercuke/cli.rb +53 -0
- data/lib/hypercuke/cli/builder.rb +68 -0
- data/lib/hypercuke/cli/parser.rb +76 -0
- data/lib/hypercuke/config.rb +20 -0
- data/lib/hypercuke/context.rb +36 -0
- data/lib/hypercuke/cucumber_integration.rb +15 -0
- data/lib/hypercuke/exceptions.rb +42 -0
- data/lib/hypercuke/mini_inflector.rb +9 -0
- data/lib/hypercuke/name_list.rb +38 -0
- data/lib/hypercuke/step_adapter.rb +12 -0
- data/lib/hypercuke/step_adapters.rb +126 -0
- data/lib/hypercuke/step_driver.rb +52 -0
- data/lib/hypercuke/version.rb +3 -0
- data/spec/cli_spec.rb +135 -0
- data/spec/context_spec.rb +46 -0
- data/spec/hypercuke_spec.rb +29 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/step_adapter_definition_spec.rb +131 -0
- data/spec/step_driver_spec.rb +99 -0
- metadata +154 -0
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
=begin
|
4
|
+
|
5
|
+
All commands should begin with "cucumber".
|
6
|
+
(Assume that the --require business is handled in cucumber.yml.)
|
7
|
+
|
8
|
+
Some sample HCu commands and their expected outputs:
|
9
|
+
$ hcu core # cucumber --tags @core_ok
|
10
|
+
$ hcu model # cucumber --tags @model_ok
|
11
|
+
$ hcu core wip # cucumber --tags @model_wip --profile wip
|
12
|
+
$ hcu core ok # cucumber --tags @core_ok
|
13
|
+
|
14
|
+
If the user specifies a --profile tag, assume they know what they're doing...
|
15
|
+
$ hcu core --profile emperor_penguin # cucumber --tags @core_ok --profile emperor_penguin
|
16
|
+
$ hcu core wip --profile emperor_penguin # cucumber --tags @core_wip --profile emperor_penguin
|
17
|
+
...even if they use the "-p" tag instead...
|
18
|
+
$ hcu core -p emperor_penguin # cucumber --tags @core_ok --profile emperor_penguin
|
19
|
+
$ hcu core wip -p emperor_penguin # cucumber --tags @core_wip --profile emperor_penguin
|
20
|
+
|
21
|
+
Everything else should just get passed through to Cucumber unmangled.
|
22
|
+
$ hcu core --wibble # cucumber --tags @core_ok --wibble
|
23
|
+
|
24
|
+
Also, the '-h' flag should display HCu help (TBD)
|
25
|
+
( TODO: WRITE THIS EXAMPLE? )
|
26
|
+
|
27
|
+
=end
|
28
|
+
|
29
|
+
describe Hypercuke::CLI do
|
30
|
+
|
31
|
+
def cli_for(hcu_command, *args)
|
32
|
+
described_class.new(hcu_command, *args)
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "cucumber command line generation" do
|
36
|
+
shared_examples_for "command line builder" do |shared_example_opts|
|
37
|
+
let(:cmd_base) { shared_example_opts[:cmd_base] }
|
38
|
+
|
39
|
+
def expect_command_line(hcu_command, expected_output)
|
40
|
+
argv = hcu_command.split(/\s+/)
|
41
|
+
actual_output = cli_for(argv).cucumber_command
|
42
|
+
|
43
|
+
expect( actual_output ).to eq( expected_output ), <<-EOF
|
44
|
+
Transforming command '#{hcu_command}':
|
45
|
+
expected: #{expected_output.inspect}
|
46
|
+
got: #{actual_output.inspect}
|
47
|
+
EOF
|
48
|
+
end
|
49
|
+
|
50
|
+
it "ignores the 0th 'hcu' argument in its various forms (does this even happen?)" do
|
51
|
+
expect_command_line 'hcu core', "#{cmd_base} --tags @core_ok"
|
52
|
+
expect_command_line 'bin/hcu core', "#{cmd_base} --tags @core_ok"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "treats the first argument as a layer name and adds the appropriate --tags flag" do
|
56
|
+
expect_command_line 'core', "#{cmd_base} --tags @core_ok"
|
57
|
+
expect_command_line 'model', "#{cmd_base} --tags @model_ok"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "barfs if the layer name is not given" do
|
61
|
+
expect{ cli_for('hcu').cucumber_command }.to raise_error( "Layer name is required" )
|
62
|
+
expect{ cli_for('').cucumber_command }.to raise_error( "Layer name is required" )
|
63
|
+
end
|
64
|
+
|
65
|
+
it "treats the second argument as a mode (assuming it doesn't start with a dash)" do
|
66
|
+
expect_command_line 'core ok', "#{cmd_base} --tags @core_ok"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "adds '--profile wip' when the mode is 'wip'" do
|
70
|
+
expect_command_line 'core wip', "#{cmd_base} --tags @core_wip --profile wip"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "ignores most other arguments and just hands them off to Cucumber" do
|
74
|
+
expect_command_line 'core --wibble', "#{cmd_base} --tags @core_ok --wibble"
|
75
|
+
expect_command_line 'core ok --wibble', "#{cmd_base} --tags @core_ok --wibble"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "doesn't override a profile if the user explicitly specifies one (using either -p or --profile)" do
|
79
|
+
expect_command_line 'core --dingbat --profile emperor_penguin', "#{cmd_base} --tags @core_ok --profile emperor_penguin --dingbat"
|
80
|
+
expect_command_line 'core --dingbat -p emperor_penguin', "#{cmd_base} --tags @core_ok --profile emperor_penguin --dingbat"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "doesn't override a user-specified profile, even in wip mode when it would normally use the wip profile" do
|
84
|
+
expect_command_line 'core wip --profile emperor_penguin', "#{cmd_base} --tags @core_wip --profile emperor_penguin"
|
85
|
+
expect_command_line 'core wip -p emperor_penguin', "#{cmd_base} --tags @core_wip --profile emperor_penguin"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when Bundler IS NOT present" do
|
90
|
+
before do
|
91
|
+
allow( described_class ).to receive(:bundler_present?).and_return(false)
|
92
|
+
end
|
93
|
+
|
94
|
+
it_behaves_like "command line builder", cmd_base: 'cucumber'
|
95
|
+
end
|
96
|
+
|
97
|
+
context "when Bundler IS present" do
|
98
|
+
before do
|
99
|
+
allow( described_class ).to receive(:bundler_present?).and_return(true)
|
100
|
+
end
|
101
|
+
|
102
|
+
it_behaves_like "command line builder", cmd_base: 'bundle exec cucumber'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "layer_name" do
|
107
|
+
it "matches the layer argument to 'hcu'" do
|
108
|
+
expect( cli_for('hcu core') .layer_name ).to eq( 'core' )
|
109
|
+
expect( cli_for('hcu model').layer_name ).to eq( 'model' )
|
110
|
+
expect( cli_for('hcu ui') .layer_name ).to eq( 'ui' )
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#run!" do
|
115
|
+
let(:cli) { cli_for('fudge_ripple', output, environment, kernel) }
|
116
|
+
let(:output) { double('output', puts: nil) }
|
117
|
+
let(:kernel) { double('Kernel', exec: nil) }
|
118
|
+
let(:environment) { { 'foo' => 'bar' } }
|
119
|
+
|
120
|
+
it "prints the generated Cucumber command to output" do
|
121
|
+
expect(output).to receive(:puts).with(cli.cucumber_command_for_display)
|
122
|
+
cli.run!
|
123
|
+
end
|
124
|
+
|
125
|
+
it "uses exec to run the Cucumber command, passing in the layer name to the environment" do
|
126
|
+
layer = Hypercuke::LAYER_NAME_ENV_VAR
|
127
|
+
expected_env = {
|
128
|
+
'foo' => 'bar',
|
129
|
+
layer => 'fudge_ripple'
|
130
|
+
}
|
131
|
+
expect(kernel).to receive(:exec).with(expected_env, cli.cucumber_command)
|
132
|
+
cli.run!
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hypercuke::Context do
|
4
|
+
subject { Hypercuke::Context.new }
|
5
|
+
|
6
|
+
it "allows square-bracket setting and getting, same as a Hash" do
|
7
|
+
expect( subject[:wibble] ).to be nil
|
8
|
+
subject[:wibble] = 'wibble'
|
9
|
+
expect( subject[:wibble] ).to eq( 'wibble' )
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:ransom_1960s) { "one MILLION dollars" }
|
13
|
+
let(:ransom_1990s) { "one hundred billion dollars" }
|
14
|
+
|
15
|
+
it "behaves like a Hash with regard to #fetch" do
|
16
|
+
subject[:demand] = ransom_1960s
|
17
|
+
expect( subject.fetch(:demand) ).to eq( ransom_1960s )
|
18
|
+
|
19
|
+
expect{ subject.fetch(:updated_demand) }
|
20
|
+
.to raise_error( KeyError )
|
21
|
+
result = subject.fetch(:updated_demand) { ransom_1990s }
|
22
|
+
expect( result ).to eq( ransom_1990s )
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#fetch_or_default" do
|
26
|
+
before do
|
27
|
+
subject[:demand] = ransom_1960s
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "when asked for a key that exists" do
|
31
|
+
it "returns the value without calling the block" do
|
32
|
+
result = subject.fetch_or_default(:demand) { fail "this block should not be called" }
|
33
|
+
expect( result ).to eq( ransom_1960s )
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "when asked for a key that does not exist" do
|
38
|
+
it "calls the block, sets the key, and returns the value" do
|
39
|
+
result = subject.fetch_or_default(:updated_demand) { ransom_1990s }
|
40
|
+
expect( result ).to eq( ransom_1990s )
|
41
|
+
result = subject.fetch_or_default(:updated_demand) { fail "this block should not be called" }
|
42
|
+
expect( result ).to eq( ransom_1990s )
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hypercuke do
|
4
|
+
around do |example|
|
5
|
+
layer = Hypercuke.current_layer
|
6
|
+
begin
|
7
|
+
example.run
|
8
|
+
ensure
|
9
|
+
Hypercuke.current_layer = layer
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "current layer" do
|
14
|
+
it "can be set" do
|
15
|
+
Hypercuke.current_layer = 'wibble'
|
16
|
+
expect( Hypercuke.current_layer ).to eq( :wibble )
|
17
|
+
end
|
18
|
+
|
19
|
+
it "defaults to an environment variable" do
|
20
|
+
expect( Hypercuke.current_layer ).to be nil
|
21
|
+
begin
|
22
|
+
ENV['HYPERCUKE_LAYER'] = 'flapjack_adjustment_station'
|
23
|
+
expect( Hypercuke.current_layer ).to eq( :flapjack_adjustment_station )
|
24
|
+
ensure
|
25
|
+
ENV['HYPERCUKE_LAYER'] = nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "step adapter definition API" do
|
4
|
+
subject { Hypercuke }
|
5
|
+
|
6
|
+
# NAMING THINGS
|
7
|
+
#
|
8
|
+
# Top-level object available as #driver: Step Driver
|
9
|
+
# Area of domain: Topic
|
10
|
+
# Layer of application: Layer
|
11
|
+
# Intersection of the two: StepAdapter
|
12
|
+
# Name of generated step adapter: Hypercuke::StepAdapters::<Topic>::<Layer>
|
13
|
+
#
|
14
|
+
# L T O P I C S
|
15
|
+
# A | | Cheese | Wine | Bread |
|
16
|
+
# Y | Core | SA* | SA* | SA* |
|
17
|
+
# E | Model | SA* | SA* | SA* |
|
18
|
+
# R | UI | SA* | SA* | SA* |
|
19
|
+
# S
|
20
|
+
# * SA = StepAdapter
|
21
|
+
|
22
|
+
context "when empty" do
|
23
|
+
it "has no layers when it starts" do
|
24
|
+
expect( subject.layers ).to be_empty
|
25
|
+
end
|
26
|
+
it "has no topics when it starts" do
|
27
|
+
expect( subject.topics ).to be_empty
|
28
|
+
end
|
29
|
+
it "has no step adapters when it starts" do
|
30
|
+
expect( subject::StepAdapters.constants ).to be_empty
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "with a single adapter in a single layer" do
|
35
|
+
before do
|
36
|
+
subject.topic :cheese do
|
37
|
+
layer :core do
|
38
|
+
def select_variety
|
39
|
+
"It's hard to go wrong with cheddar."
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
let(:sd_class) { subject.step_adapter_class( :cheese, :core ) }
|
46
|
+
|
47
|
+
it "defines a cheese topic" do
|
48
|
+
expect( subject.topic_names ).to eq( [:cheese] )
|
49
|
+
end
|
50
|
+
|
51
|
+
it "defines a core layer" do
|
52
|
+
expect( subject.layer_names ).to eq( [:core] )
|
53
|
+
end
|
54
|
+
|
55
|
+
it "defines a step adapter class for the cheese/core combo" do
|
56
|
+
expect( sd_class ).to_not be_nil
|
57
|
+
expect( sd_class.superclass ).to be( Hypercuke::StepAdapter )
|
58
|
+
expect( sd_class.instance_methods(false) ).to eq( [ :select_variety ] )
|
59
|
+
end
|
60
|
+
|
61
|
+
it "allows the step adapter to be reopened" do
|
62
|
+
subject.topic :cheese do
|
63
|
+
layer :core do
|
64
|
+
def pair_with(wine)
|
65
|
+
"I probably should pick an example I know more about"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
expect( sd_class.instance_methods(false).sort ).to eq( [ :select_variety, :pair_with ].sort )
|
71
|
+
end
|
72
|
+
|
73
|
+
it "gives the step adapter class a reasonable name" do
|
74
|
+
expect( sd_class.name ).to eq( "Hypercuke::StepAdapters::Cheese::Core" )
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "with two topics and three layers" do
|
79
|
+
before do
|
80
|
+
subject.topic :wibble do
|
81
|
+
layer :spam do ; def wibble ; "wibble spam" ; end ; end
|
82
|
+
layer :eggs do ; def wibble ; "wibble eggs" ; end ; end
|
83
|
+
layer :bacon do ; def wibble ; "wibble bacon" ; end ; end
|
84
|
+
end
|
85
|
+
subject.topic :yak do
|
86
|
+
layer :spam do ; def shave ; "s*MOO*th spam" ; end ; end
|
87
|
+
layer :eggs do ; def shave ; "s*MOO*th eggs" ; end ; end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it "defines topic names" do
|
92
|
+
expect( subject.topic_names ).to eq( [:wibble, :yak] )
|
93
|
+
end
|
94
|
+
|
95
|
+
it "defines layer names" do
|
96
|
+
expect( subject.layer_names ).to eq( [:spam, :eggs, :bacon] )
|
97
|
+
end
|
98
|
+
|
99
|
+
it "defines step adapter classes with reasonable class names" do
|
100
|
+
expect( Hypercuke::StepAdapters::Wibble::Spam .superclass ).to be( Hypercuke::StepAdapter )
|
101
|
+
expect( Hypercuke::StepAdapters::Wibble::Eggs .superclass ).to be( Hypercuke::StepAdapter )
|
102
|
+
expect( Hypercuke::StepAdapters::Wibble::Bacon.superclass ).to be( Hypercuke::StepAdapter )
|
103
|
+
|
104
|
+
expect( Hypercuke::StepAdapters::Yak::Spam .superclass ).to be( Hypercuke::StepAdapter )
|
105
|
+
expect( Hypercuke::StepAdapters::Yak::Eggs .superclass ).to be( Hypercuke::StepAdapter )
|
106
|
+
expect{ Hypercuke::StepAdapters::Yak::Bacon }.to raise_error(NameError)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "naming conflicts" do
|
111
|
+
it "works when a topic name resolves to something outside the Hypercuke namespace" do
|
112
|
+
Hypercuke.topic :array do
|
113
|
+
layer :core do
|
114
|
+
def metasyntactic_variables ; %w[ foo bar yak shed ] ; end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
expect( Hypercuke::StepAdapters::Array::Core .superclass ).to be( Hypercuke::StepAdapter )
|
119
|
+
end
|
120
|
+
|
121
|
+
it "works when a layer name resolves to something outside the Hypercuke namespace" do
|
122
|
+
Hypercuke.topic :absurdity do
|
123
|
+
layer :array do
|
124
|
+
def metasyntactic_variables ; %w[ foo bar yak shed ] ; end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
expect( Hypercuke::StepAdapters::Absurdity::Array .superclass ).to be( Hypercuke::StepAdapter )
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hypercuke::StepDriver do
|
4
|
+
before do
|
5
|
+
Hypercuke.topic :wibble do
|
6
|
+
layer :spam do ; def wibble ; "wibble spam" ; end ; end
|
7
|
+
layer :eggs do ; def wibble ; "wibble eggs" ; end ; end
|
8
|
+
layer :bacon do ; def wibble ; "wibble bacon" ; end ; end
|
9
|
+
end
|
10
|
+
Hypercuke.topic :yak do
|
11
|
+
layer :spam do ; def shave ; "s*MOO*th spam" ; end ; end
|
12
|
+
layer :eggs do ; def shave ; "s*MOO*th eggs" ; end ; end
|
13
|
+
# Is yak bacon even a thing? ...wait, don't answer that.
|
14
|
+
end
|
15
|
+
Hypercuke.current_layer = :spam
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
subject(:step_driver) { described_class.new }
|
20
|
+
|
21
|
+
describe "step adapter retrieval" do
|
22
|
+
|
23
|
+
it "asks Hypercuke for the current layer" do
|
24
|
+
Hypercuke.current_layer = :bacon
|
25
|
+
expect( step_driver.layer_name ).to eq( :bacon )
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can return a step adapter for each layer" do
|
29
|
+
expect( step_driver.wibble.wibble ).to eq( "wibble spam" )
|
30
|
+
expect( step_driver.yak.shave ).to eq( "s*MOO*th spam" )
|
31
|
+
end
|
32
|
+
|
33
|
+
it "explodes when asked for a topic that isn't defined" do
|
34
|
+
expect{ step_driver.heffalump }.to raise_error( Hypercuke::TopicNotDefinedError, "Topic not defined: heffalump" )
|
35
|
+
end
|
36
|
+
|
37
|
+
it "explodes when asked for a layer that isn't defined" do
|
38
|
+
Hypercuke.current_layer = :booOOoogus
|
39
|
+
expect{ step_driver.wibble }.to raise_error( Hypercuke::LayerNotDefinedError, "Layer not defined: booOOoogus" )
|
40
|
+
end
|
41
|
+
|
42
|
+
it "explodes when asked for a step adapter that isn't defined at the current layer" do
|
43
|
+
Hypercuke.current_layer = :bacon
|
44
|
+
expect( step_driver.wibble ).to be_kind_of( Hypercuke::StepAdapters::Wibble::Bacon )
|
45
|
+
expect{ step_driver.yak }.to raise_error( Hypercuke::StepAdapterNotDefinedError, "Step adapter not defined: 'Hypercuke::StepAdapters::Yak::Bacon'" )
|
46
|
+
end
|
47
|
+
|
48
|
+
it "can return a step adapter for each layer (even when the layer is changed)" do
|
49
|
+
[ :spam, :eggs, :bacon ].each do |layer|
|
50
|
+
Hypercuke.current_layer = layer
|
51
|
+
expect( step_driver.wibble.wibble ).to eq( "wibble #{layer}" )
|
52
|
+
expect( step_driver.yak.shave ).to eq( "s*MOO*th #{layer}" ) unless :bacon == layer
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "context management" do
|
59
|
+
let(:sd_context) { step_driver.send(:__context__) }
|
60
|
+
|
61
|
+
specify "step driver has a context" do
|
62
|
+
expect( sd_context ).to be_kind_of( Hypercuke::Context )
|
63
|
+
end
|
64
|
+
|
65
|
+
specify "step driver passes its context to any step adapter it creates" do
|
66
|
+
step_adapter_context = step_driver.wibble.__send__(:context)
|
67
|
+
expect( step_adapter_context ).to be( sd_context )
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "inter-adapter dependencies" do
|
72
|
+
before do
|
73
|
+
Hypercuke.reset!
|
74
|
+
Hypercuke.topic :bikeshed do
|
75
|
+
layer :distraction do
|
76
|
+
def color ; "blue" ; end
|
77
|
+
end
|
78
|
+
layer :planet_ruby do
|
79
|
+
def color ; "ruby red" ; end # HINT: the herring is red, too...
|
80
|
+
end
|
81
|
+
end
|
82
|
+
Hypercuke.topic :yak do
|
83
|
+
layer :distraction do
|
84
|
+
def bikeshed_color
|
85
|
+
step_driver.bikeshed.color
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
Hypercuke.current_layer = :distraction
|
90
|
+
end
|
91
|
+
|
92
|
+
let(:step_driver) { described_class.new }
|
93
|
+
|
94
|
+
specify "step adapters within the same layer may access one another through the step driver" do
|
95
|
+
expect( step_driver.yak.bikeshed_color ).to eq( "blue" )
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|