mirage 0.1.2
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.
- data/.rvmrc +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +50 -0
- data/README.md +93 -0
- data/bin/mirage +54 -0
- data/features/checking_for_requests.feature +74 -0
- data/features/clearing_requests_and_responses.feature +81 -0
- data/features/client/checking_for_requests.feature +20 -0
- data/features/client/clearing_responses.feature +75 -0
- data/features/client/getting_responses.feature +30 -0
- data/features/client/mirage_client.feature +36 -0
- data/features/client/peeking.feature +32 -0
- data/features/client/setting_responses.feature +91 -0
- data/features/client/snapshotting.feature +34 -0
- data/features/command_line_iterface.feature +39 -0
- data/features/default_responses.feature +91 -0
- data/features/file_hosting.feature +8 -0
- data/features/logging.feature +7 -0
- data/features/peeking_at_response.feature +24 -0
- data/features/resources/test.zip +0 -0
- data/features/response_templates.feature +45 -0
- data/features/root_responses.feature +47 -0
- data/features/setting_responses.feature +40 -0
- data/features/setting_responses_with_a_delay.feature +10 -0
- data/features/setting_responses_with_pattern_matching.feature +72 -0
- data/features/snapshotting.feature +25 -0
- data/features/step_definitions/my_steps.rb +127 -0
- data/features/support/env.rb +89 -0
- data/features/web_user_interface.feature +39 -0
- data/full_build.sh +100 -0
- data/lib/config.ru +5 -0
- data/lib/mirage.rb +14 -0
- data/lib/mirage/client.rb +140 -0
- data/lib/mirage/core.rb +206 -0
- data/lib/mirage/util.rb +40 -0
- data/lib/mirage/web.rb +65 -0
- data/lib/start_mirage.rb +15 -0
- data/lib/view/mirage/index.xhtml +23 -0
- data/mirage.gemspec +40 -0
- data/rakefile +50 -0
- metadata +199 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
Feature: Mirage can respond with a 'root' response when a when the response requested at a sub url is not found.
|
2
|
+
I.e.
|
3
|
+
if a response is held for 'level1' and request comes in for 'level1/level2' the response for 'level1'
|
4
|
+
can be returned if nothing is held for 'level1/level2'
|
5
|
+
|
6
|
+
If a request is made and there is more than one response that could be appropriate then the closet is chosen.
|
7
|
+
|
8
|
+
E.g.
|
9
|
+
responses exist for: 'level1' and 'level1/level2'. If a response for 'level1/level2/level3 is made, then the response for
|
10
|
+
'level1/level2' will be returned as it is the most specific match out of the two.
|
11
|
+
|
12
|
+
Root responses can cause unexpected behaviour and so in order to qualify as a root reponse a client must knowingly mark it as one.
|
13
|
+
|
14
|
+
Scenario: A root response is returned
|
15
|
+
Given I hit 'http://localhost:7001/mirage/set/level0/level1' with parameters:
|
16
|
+
| response | another level |
|
17
|
+
And I hit 'http://localhost:7001/mirage/set/level1' with parameters:
|
18
|
+
| response | level 1 |
|
19
|
+
| root_response | true |
|
20
|
+
|
21
|
+
When I hit 'http://localhost:7001/mirage/get/level1/level2'
|
22
|
+
Then 'level 1' should be returned
|
23
|
+
|
24
|
+
|
25
|
+
Scenario: More than one potential root response exists
|
26
|
+
Given I hit 'http://localhost:7001/mirage/set/level1' with parameters:
|
27
|
+
| response | level 1 |
|
28
|
+
| root_response | true |
|
29
|
+
And I hit 'http://localhost:7001/mirage/set/level1/level2' with parameters:
|
30
|
+
| response | level 2 |
|
31
|
+
| root_response | true |
|
32
|
+
And I hit 'http://localhost:7001/mirage/set/level1/level2/level3' with parameters:
|
33
|
+
| response | level 3 |
|
34
|
+
| root_response | false |
|
35
|
+
And I hit 'http://localhost:7001/mirage/set/level1/level2/level3/level4/level5' with parameters:
|
36
|
+
| response | level 5 |
|
37
|
+
| root_response | true |
|
38
|
+
|
39
|
+
When I hit 'http://localhost:7001/mirage/get/level1/level2/level3/level4'
|
40
|
+
Then 'level 2' should be returned
|
41
|
+
|
42
|
+
|
43
|
+
Scenario: There isnt a root response
|
44
|
+
Given I hit 'http://localhost:7001/mirage/set/level1' with parameters:
|
45
|
+
| response | level 1 |
|
46
|
+
When I hit 'http://localhost:7001/mirage/get/level1/level2'
|
47
|
+
Then a 404 should be returned
|
@@ -0,0 +1,40 @@
|
|
1
|
+
Feature: Mirage can be configured with endpoints that when request returned defined responses.
|
2
|
+
On setting a response, a unique id is retuned. This is a key that can be used to manage the response. E.g. clearing or peek at it.
|
3
|
+
|
4
|
+
Usage:
|
5
|
+
${mirage_url}/set/your/end/point?response=your_response
|
6
|
+
|
7
|
+
|
8
|
+
Scenario: Setting a response without any selection criteria
|
9
|
+
Given I hit 'http://localhost:7001/mirage/set/greeting' with parameters:
|
10
|
+
| response | Hello, how are you? |
|
11
|
+
|
12
|
+
When I hit 'http://localhost:7001/mirage/get/greeting'
|
13
|
+
Then 'Hello, how are you?' should be returned
|
14
|
+
|
15
|
+
Scenario: A response hosted on a longer url
|
16
|
+
Given I hit 'http://localhost:7001/mirage/set/say/hello/to/me' with parameters:
|
17
|
+
| response | Hello to me |
|
18
|
+
|
19
|
+
When I hit 'http://localhost:7001/mirage/get/say/hello/to/me'
|
20
|
+
Then 'Hello to me' should be returned
|
21
|
+
|
22
|
+
|
23
|
+
Scenario: The same endpoint is set more than once
|
24
|
+
Given I hit 'http://localhost:7001/mirage/set/greeting' with parameters:
|
25
|
+
| response | Hello |
|
26
|
+
Then '1' should be returned
|
27
|
+
|
28
|
+
Given I hit 'http://localhost:7001/mirage/set/greeting' with parameters:
|
29
|
+
| response | Hi |
|
30
|
+
Then '1' should be returned
|
31
|
+
|
32
|
+
|
33
|
+
Scenario: A response is not supplied
|
34
|
+
Given I hit 'http://localhost:7001/mirage/set/greeting'
|
35
|
+
Then a 500 should be returned
|
36
|
+
|
37
|
+
|
38
|
+
Scenario: Getting a response that does not exist
|
39
|
+
When I hit 'http://localhost:7001/mirage/get/response_that_does_not_exist'
|
40
|
+
Then a 404 should be returned
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Feature: Its possible introduce a delay before responding to a client with a particular response. This lets you simulate real world
|
2
|
+
conditions by making your application wait before receiving a response.
|
3
|
+
|
4
|
+
Scenario: Response with a delay
|
5
|
+
Given I hit 'http://localhost:7001/mirage/set/an_appology' with parameters:
|
6
|
+
| response | Sorry it took me so long! |
|
7
|
+
| delay | 4 |
|
8
|
+
|
9
|
+
When I hit 'http://localhost:7001/mirage/get/an_appology'
|
10
|
+
Then it should take at least '4' seconds
|
@@ -0,0 +1,72 @@
|
|
1
|
+
Feature: Mirage can be configured to return particular responses conditionally based on if a prescribed pattern is found in
|
2
|
+
querystring or the body of a request.
|
3
|
+
|
4
|
+
Patterns can be either plain text or a regular expression
|
5
|
+
|
6
|
+
Background: There is already a default response for 'greeting'
|
7
|
+
Given I hit 'http://localhost:7001/mirage/set/greeting' with parameters:
|
8
|
+
| response | Hello Stranger. |
|
9
|
+
|
10
|
+
|
11
|
+
Scenario: A plain text pattern found in the request body
|
12
|
+
Given I hit 'http://localhost:7001/mirage/set/greeting' with parameters:
|
13
|
+
| response | Hello Leon, how are you? |
|
14
|
+
| pattern | <name>leon</name> |
|
15
|
+
When I hit 'http://localhost:7001/mirage/get/greeting' with request body:
|
16
|
+
"""
|
17
|
+
<greetingRequest>
|
18
|
+
<name>leon</name>
|
19
|
+
</greetingRequest>
|
20
|
+
"""
|
21
|
+
Then 'Hello Leon, how are you?' should be returned
|
22
|
+
|
23
|
+
|
24
|
+
Scenario: A regex based pattern found in the request body
|
25
|
+
Given I hit 'http://localhost:7001/mirage/set/greeting' with parameters:
|
26
|
+
| response | Hello Leon, how are you? |
|
27
|
+
| pattern | .*?leon<\/name> |
|
28
|
+
|
29
|
+
When I hit 'http://localhost:7001/mirage/get/greeting' with request body:
|
30
|
+
"""
|
31
|
+
<greetingRequest>
|
32
|
+
<name>leon</name>
|
33
|
+
</greetingRequest>
|
34
|
+
"""
|
35
|
+
Then 'Hello Leon, how are you?' should be returned
|
36
|
+
|
37
|
+
|
38
|
+
Scenario: A plain text pattern found in the query string
|
39
|
+
Given I hit 'http://localhost:7001/mirage/set/greeting' with parameters:
|
40
|
+
| response | Hello Leon, how are you? |
|
41
|
+
| pattern | leon |
|
42
|
+
|
43
|
+
When I hit 'http://localhost:7001/mirage/get/greeting' with parameters:
|
44
|
+
| name | leon |
|
45
|
+
|
46
|
+
Then 'Hello Leon, how are you?' should be returned
|
47
|
+
|
48
|
+
|
49
|
+
Scenario: A regex based pattern found in the query string
|
50
|
+
Given I hit 'http://localhost:7001/mirage/set/greeting' with parameters:
|
51
|
+
| response | Hello Leon, how are you? |
|
52
|
+
| pattern | name=[L\|l]eon |
|
53
|
+
|
54
|
+
When I hit 'http://localhost:7001/mirage/get/greeting' with parameters:
|
55
|
+
| name | leon |
|
56
|
+
|
57
|
+
Then 'Hello Leon, how are you?' should be returned
|
58
|
+
|
59
|
+
|
60
|
+
Scenario: The pattern is not matched
|
61
|
+
Given I hit 'http://localhost:7001/mirage/set/greeting' with parameters:
|
62
|
+
| response | Hello Leon, how are you? |
|
63
|
+
| pattern | .*?leon<\/name> |
|
64
|
+
|
65
|
+
When I hit 'http://localhost:7001/mirage/get/greeting' with request body:
|
66
|
+
"""
|
67
|
+
<greetingRequest>
|
68
|
+
<name>jim</name>
|
69
|
+
</greetingRequest>
|
70
|
+
"""
|
71
|
+
|
72
|
+
Then 'Hello Stranger.' should be returned
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Feature: Having set up the Mirage with a number of defaults, your tests may continue to change its state.
|
2
|
+
Clearing and resetting all of your responses, potentially hundreds of times, can be time expensive.
|
3
|
+
|
4
|
+
Mirage provides the ability to take a snapshot of its current state and to roll it back to that state.
|
5
|
+
|
6
|
+
Background: The MockServer has been setup with some default responses
|
7
|
+
Given I hit 'http://localhost:7001/mirage/set/greeting' with parameters:
|
8
|
+
| response | The default greeting |
|
9
|
+
|
10
|
+
|
11
|
+
Scenario: Taking a snapshot and rolling it back
|
12
|
+
Given I hit 'http://localhost:7001/mirage/snapshot'
|
13
|
+
And I hit 'http://localhost:7001/mirage/set/leaving' with parameters:
|
14
|
+
| response | Goodye |
|
15
|
+
|
16
|
+
And I hit 'http://localhost:7001/mirage/set/greeting' with parameters:
|
17
|
+
| response | Changed |
|
18
|
+
|
19
|
+
And I hit 'http://localhost:7001/mirage/rollback'
|
20
|
+
|
21
|
+
When I hit 'http://localhost:7001/mirage/get/leaving'
|
22
|
+
Then a 404 should be returned
|
23
|
+
|
24
|
+
When I hit 'http://localhost:7001/mirage/get/greeting'
|
25
|
+
Then 'The default greeting' should be returned
|
@@ -0,0 +1,127 @@
|
|
1
|
+
Before('@command_line') do
|
2
|
+
stop_mirage
|
3
|
+
end
|
4
|
+
|
5
|
+
After('@command_line') do
|
6
|
+
stop_mirage
|
7
|
+
end
|
8
|
+
|
9
|
+
Then /^'(.*?)' should be returned$/ do |expected_response|
|
10
|
+
response_text = @response.body
|
11
|
+
if ["1.8.6", "1.8.7"].include?(RUBY_VERSION) && response_text != expected_response
|
12
|
+
expected_response.length.should == response_text.length
|
13
|
+
expected_response.split('&').each { |param_value_pair| response_text.should =~ /#{param_value_pair}/ }
|
14
|
+
else
|
15
|
+
response_text.should == expected_response
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Then /^a (404|500) should be returned$/ do |error_code|
|
20
|
+
@response.code.should == error_code.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
Then /^it should take at least '(.*)' seconds$/ do |time|
|
24
|
+
(@response_time).should >= time.to_f
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
Then /^the response should be a file the same as '([^']*)'$/ do |file_path|
|
29
|
+
download_path = "#{SCRATCH}/temp.download"
|
30
|
+
@response.save_as(download_path)
|
31
|
+
FileUtils.cmp(download_path, file_path).should == true
|
32
|
+
end
|
33
|
+
|
34
|
+
Then /^mirage should be running on '(.*)'$/ do |url|
|
35
|
+
get(url).code.to_i.should == 200
|
36
|
+
end
|
37
|
+
|
38
|
+
Given /^I run '(.*)'$/ do |command|
|
39
|
+
path = ENV['mode'] == 'regression' ? '' : "../bin/"
|
40
|
+
@commandline_output = normalise(IO.popen("export RUBYOPT='' && cd #{SCRATCH} && #{path}#{command}").read)
|
41
|
+
end
|
42
|
+
|
43
|
+
Given /^Mirage (is|is not) running$/ do |running|
|
44
|
+
if running == 'is'
|
45
|
+
start_mirage unless $mirage.running?
|
46
|
+
else
|
47
|
+
stop_mirage if $mirage.running?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Then /^Connection should be refused to '(.*)'$/ do |url|
|
52
|
+
|
53
|
+
begin
|
54
|
+
get(url)
|
55
|
+
fail "Mirage is still running"
|
56
|
+
rescue Errno::ECONNREFUSED
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
Given /^the file '(.*)' contains:$/ do |file_path, content|
|
62
|
+
file_path = "#{SCRATCH}/#{file_path}" unless file_path =~ /^\//
|
63
|
+
|
64
|
+
FileUtils.rm_rf(file_path) if File.exists?(file_path)
|
65
|
+
directory = File.dirname(file_path)
|
66
|
+
FileUtils.mkdir_p(directory)
|
67
|
+
file = File.new("#{directory}/#{File.basename(file_path)}", 'w')
|
68
|
+
file.write(content)
|
69
|
+
file.close
|
70
|
+
end
|
71
|
+
|
72
|
+
Then /^the usage information should be displayed$/ do
|
73
|
+
@usage.each { |line| @commandline_output.should =~ /#{line}/ }
|
74
|
+
end
|
75
|
+
Given /^usage information:$/ do |table|
|
76
|
+
@usage = table.raw.flatten.collect { |line| normalise(line) }
|
77
|
+
end
|
78
|
+
|
79
|
+
Then /^I run$/ do |text|
|
80
|
+
text.gsub!("\"", "\\\\\"")
|
81
|
+
raise "run failed" unless system "#{RUBY_CMD} -e \"#{@code_snippet}\n#{text}\""
|
82
|
+
end
|
83
|
+
|
84
|
+
Given /^the following gems are required to run the Mirage client test code:$/ do |text|
|
85
|
+
@code_snippet = text.gsub("\"", "\\\\\"")
|
86
|
+
end
|
87
|
+
|
88
|
+
When /^I hit '(http:\/\/localhost:7001\/mirage\/(.*?))'$/ do |url, response_id|
|
89
|
+
@response = hit_mirage(url)
|
90
|
+
end
|
91
|
+
|
92
|
+
When /^I (hit|get|post to) '(http:\/\/localhost:7001\/mirage\/(.*?))' with parameters:$/ do |http_method, url, endpoint, table|
|
93
|
+
|
94
|
+
parameters = {}
|
95
|
+
table.raw.each do |row|
|
96
|
+
parameter, value = row[0].to_sym, row[1]
|
97
|
+
value = (parameter == :file ? File.open(value) : value)
|
98
|
+
parameters[parameter.to_sym]=value
|
99
|
+
end
|
100
|
+
|
101
|
+
@response = hit_mirage(url, parameters)
|
102
|
+
end
|
103
|
+
|
104
|
+
When /^I hit '(http:\/\/localhost:7001\/mirage\/(.*?))' with request body:$/ do |url, endpoint, request_body|
|
105
|
+
@response = hit_mirage(url, {:body => request_body})
|
106
|
+
end
|
107
|
+
|
108
|
+
Then /^I should see '(.*?)' on the command line$/ do |content|
|
109
|
+
@commandline_output.should =~/#{content}/
|
110
|
+
end
|
111
|
+
|
112
|
+
Then /^'(.*)' should exist$/ do |path|
|
113
|
+
File.exists?("#{SCRATCH}/#{path}").should == true
|
114
|
+
end
|
115
|
+
|
116
|
+
Then /^'(.*)' should contain '(.*)'$/ do |file, content|
|
117
|
+
fail("#{content} not found in: #{File.read(file)}") unless File.read("#{SCRATCH}/#{file}").index(content)
|
118
|
+
end
|
119
|
+
Given /^I goto '(.*)'$/ do |url|
|
120
|
+
@page = Mechanize.new.get url
|
121
|
+
end
|
122
|
+
Then /^I should see '(.*)'$/ do |text|
|
123
|
+
@page.body.index(text).should_not == nil
|
124
|
+
end
|
125
|
+
When /^I click '(.*)'$/ do |thing|
|
126
|
+
@page = @page.links.find{|link| link.attributes['id'] == thing}.click
|
127
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../../lib")
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler/setup'
|
4
|
+
Bundler.setup(:test)
|
5
|
+
require 'mirage'
|
6
|
+
require 'cucumber'
|
7
|
+
require 'rspec'
|
8
|
+
require 'mechanize'
|
9
|
+
|
10
|
+
SCRATCH = './scratch'
|
11
|
+
RUBY_CMD = RUBY_PLATFORM == 'JAVA' ? 'jruby' : 'ruby'
|
12
|
+
|
13
|
+
|
14
|
+
module Web
|
15
|
+
include Mirage::Web
|
16
|
+
|
17
|
+
def get(url)
|
18
|
+
browser = Mechanize.new
|
19
|
+
browser.keep_alive= false
|
20
|
+
browser.get(url)
|
21
|
+
end
|
22
|
+
|
23
|
+
def hit_mirage(url, parameters={})
|
24
|
+
start_time = Time.now
|
25
|
+
response = (parameters.include?(:file) ? http_post(url, parameters) : http_get(url, parameters))
|
26
|
+
@response_time = Time.now - start_time
|
27
|
+
response
|
28
|
+
end
|
29
|
+
|
30
|
+
def normalise text
|
31
|
+
text.gsub(/[\n]/, ' ').gsub(/\s+/, ' ')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
module Regression
|
37
|
+
def stop_mirage
|
38
|
+
`export RUBYOPT='' && cd #{SCRATCH} && mirage stop`
|
39
|
+
end
|
40
|
+
|
41
|
+
def start_mirage
|
42
|
+
`truncate mirage.log --size 0`
|
43
|
+
`export RUBYOPT='' && cd #{SCRATCH} && mirage start`
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module IntelliJ
|
48
|
+
include Mirage::Util
|
49
|
+
|
50
|
+
def stop_mirage
|
51
|
+
system "cd #{SCRATCH} && ../bin/mirage stop"
|
52
|
+
wait_until do
|
53
|
+
!$mirage.running?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def start_mirage
|
58
|
+
puts "starting mirage"
|
59
|
+
`truncate mirage.log --size 0`
|
60
|
+
system "cd #{SCRATCH} && ../bin/mirage start"
|
61
|
+
|
62
|
+
wait_until do
|
63
|
+
$mirage.running?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
'regression' == ENV['mode'] ? World(Regression) : World(IntelliJ)
|
69
|
+
'regression' == ENV['mode'] ? include(Regression) : include(IntelliJ)
|
70
|
+
|
71
|
+
World(Web)
|
72
|
+
|
73
|
+
Before do
|
74
|
+
FileUtils.mkdir_p(SCRATCH)
|
75
|
+
$mirage = Mirage::Client.new
|
76
|
+
|
77
|
+
if $mirage.running?
|
78
|
+
$mirage.clear
|
79
|
+
else
|
80
|
+
start_mirage
|
81
|
+
end
|
82
|
+
|
83
|
+
`ls #{SCRATCH}/ | grep -v mirage.log | rm -rf`
|
84
|
+
`truncate -s 0 #{SCRATCH}/mirage.log`
|
85
|
+
end
|
86
|
+
|
87
|
+
at_exit do
|
88
|
+
stop_mirage
|
89
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
Feature: Mirage's home page allows you to see what response are currently being hosted.
|
2
|
+
From this page you can:
|
3
|
+
- Peek at a responses content
|
4
|
+
- Check the response to see if a request has been made to it
|
5
|
+
|
6
|
+
Background: There are already a couple of responses hosted on he Mirage server
|
7
|
+
Given I hit 'http://localhost:7001/mirage/set/greeting' with parameters:
|
8
|
+
| response | hello |
|
9
|
+
| root_response | true |
|
10
|
+
And I hit 'http://localhost:7001/mirage/set/leaving' with parameters:
|
11
|
+
| response | goodbye |
|
12
|
+
|
13
|
+
Scenario: Using the home page to see what response are being hosted
|
14
|
+
Given I goto 'http://localhost:7001/mirage'
|
15
|
+
Then I should see 'greeting/*'
|
16
|
+
Then I should see 'leaving'
|
17
|
+
|
18
|
+
Scenario: Using the home page to peek at a response
|
19
|
+
Given I goto 'http://localhost:7001/mirage'
|
20
|
+
When I click 'peek_response_1'
|
21
|
+
Then I should see 'hello'
|
22
|
+
|
23
|
+
Scenario: Using the home page to check if a request has been made
|
24
|
+
Given I hit 'http://localhost:7001/mirage/get/greeting' with request body:
|
25
|
+
"""
|
26
|
+
Yo!
|
27
|
+
"""
|
28
|
+
Given I goto 'http://localhost:7001/mirage'
|
29
|
+
When I click 'check_response_1'
|
30
|
+
Then I should see 'Yo!'
|
31
|
+
|
32
|
+
Scenario: Using the home page to check if a request has been made
|
33
|
+
Given I hit 'http://localhost:7001/mirage/get/greeting' with request body:
|
34
|
+
"""
|
35
|
+
Yo!
|
36
|
+
"""
|
37
|
+
Given I goto 'http://localhost:7001/mirage'
|
38
|
+
When I click 'check_response_1'
|
39
|
+
Then I should see 'Yo!'
|