Skip to content

Commit c2e9087

Browse files
authored
Refactor: #attach methodology (#1797)
* A file input is either a path or a base64 IO.read object, either way we handle it by passing it to super * Handle errors in raw byte attach (Plus potentially file open attach) * Fix up some test inconsistencies and remove duplicate tst * Add in new specs for diff variants including note to fix input type * Fix order of detection of file and mime type * Fix whitespace rubocop * Enhance stdout response from process to be formatted in a variety of ways * Refactor 2 steps to be more optimal using new formatter for stdout Write a new cuke that will recurse to find the desired message and then check its internal contents for more optimal processing * Use new steps * Improve message formatter scenarios by using the ndjson tests and fix up attachment steps styling * Rubocop fixes * Add changelog
1 parent 93dceea commit c2e9087

File tree

7 files changed

+126
-85
lines changed

7 files changed

+126
-85
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
99
Please visit [cucumber/CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CONTRIBUTING.md) for more info on how to contribute to Cucumber.
1010

1111
## [Unreleased]
12+
### Changed
13+
- Internal refactors to testing code to better check that message responses are as expected
14+
- Simplify `#attach` by better checking the different use cases (base64 vs file path)
15+
1216
### Fixed
1317
- Prevent messages (And any consuming formatters), from not handling unknown (base64), media types ([#1796](https://github.com/cucumber/cucumber-ruby/pull/1796) [luke-hill](https://github.com/luke-hill))
1418

features/docs/formatters/message.feature

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ Feature: Message output formatter
2929
gherkinDocument
3030
pickle
3131
pickle
32-
testCase
33-
testCase
3432
testRunStarted
33+
testCase
3534
testCaseStarted
3635
testStepStarted
3736
testStepFinished
3837
testCaseFinished
38+
testCase
3939
testCaseStarted
4040
testStepStarted
4141
testStepFinished
@@ -51,10 +51,8 @@ Feature: Message output formatter
5151
"""
5252
When I run `cucumber features/my_feature.feature --format message`
5353
Then output should be valid NDJSON
54-
And the output should contain:
55-
"""
56-
{"testRunFinished":{"success":false
57-
"""
54+
And the output should contain NDJSON with key "testRunFinished"
55+
And the output should contain NDJSON "testRunFinished" message with key "success" and boolean value false
5856

5957
Scenario: it sets "testRunFinished"."success" to true if nothing failed
6058
Given a file named "features/my_feature.feature" with:
@@ -70,7 +68,5 @@ Feature: Message output formatter
7068
"""
7169
When I run `cucumber features/my_feature.feature --format message`
7270
Then output should be valid NDJSON
73-
And the output should contain:
74-
"""
75-
{"testRunFinished":{"success":true
76-
"""
71+
And the output should contain NDJSON with key "testRunFinished"
72+
And the output should contain NDJSON "testRunFinished" message with key "success" and boolean value true

features/docs/writing_support_code/attachments.feature

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,43 +12,43 @@ Feature: Attachments
1212
"""
1313
Feature: A screenshot feature
1414
Scenario:
15-
Given I attach a screenshot with media type
15+
Given I attach a screenshot with a media type
1616
"""
1717
Given a file named "features/attaching_screenshot_without_mediatype.feature" with:
1818
"""
1919
Feature: A file feature
2020
Scenario:
21-
Given I attach a screenshot without media type
21+
Given I attach a screenshot without a media type
2222
"""
2323
And a file named "features/screenshot.png" with:
2424
"""
2525
foo
2626
"""
2727
And a file named "features/step_definitions/attaching_screenshot_steps.rb" with:
2828
"""
29-
Given /^I attach a screenshot with media type/ do
30-
attach "features/screenshot.png", "image/png"
29+
Given('I attach a screenshot with a media type') do
30+
attach('features/screenshot.png', 'image/png')
3131
end
3232
33-
Given /^I attach a screenshot without media type/ do
34-
attach "features/screenshot.png"
33+
Given('I attach a screenshot without a media type') do
34+
attach('features/screenshot.png')
3535
end
3636
"""
3737

3838
Scenario: Files can be attached given their path
3939
When I run `cucumber --format message features/attaching_screenshot_with_mediatype.feature`
4040
Then output should be valid NDJSON
4141
And the output should contain NDJSON with key "attachment"
42-
And the output should contain NDJSON with key "body" and value "Zm9v"
43-
And the output should contain NDJSON with key "mediaType" and value "image/png"
42+
And the output should contain NDJSON "attachment" message with key "body" and value "Zm9v"
43+
And the output should contain NDJSON "attachment" message with key "mediaType" and value "image/png"
4444

4545
Scenario: Media type is inferred from the given file
4646
When I run `cucumber --format message features/attaching_screenshot_without_mediatype.feature`
4747
Then output should be valid NDJSON
4848
And the output should contain NDJSON with key "attachment"
49-
And the output should contain NDJSON with key "body" and value "Zm9v"
50-
And the output should contain NDJSON with key "mediaType" and value "image/png"
51-
49+
And the output should contain NDJSON "attachment" message with key "body" and value "Zm9v"
50+
And the output should contain NDJSON "attachment" message with key "mediaType" and value "image/png"
51+
5252
Scenario: With json formatter, files can be attached given their path
5353
When I run `cucumber --format json features/attaching_screenshot_with_mediatype.feature`
5454
Then the output should contain "embeddings\":"
Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,32 @@
11
# frozen_string_literal: true
22

33
Then('messages types should be:') do |expected_types|
4-
parsed_json = command_line.stdout.split("\n").map { |line| JSON.parse(line) }
5-
message_types = parsed_json.map(&:keys).flatten.compact
4+
message_types = command_line.stdout(format: :messages).map(&:keys).flatten.compact
65

7-
expect(expected_types.split("\n").map(&:strip)).to contain_exactly(*message_types)
6+
expect(expected_types.split("\n")).to eq(message_types)
87
end
98

109
Then('output should be valid NDJSON') do
11-
command_line.stdout.split("\n").map do |line|
12-
expect { JSON.parse(line) }.not_to raise_exception
13-
end
10+
expect { command_line.stdout(format: :messages) }.not_to raise_error
1411
end
1512

1613
Then('the output should contain NDJSON with key {string}') do |key|
17-
expect(command_line.stdout).to match(/"#{key}":/)
14+
expect(command_line.stdout(format: :messages)).to include(have_key(key))
1815
end
1916

2017
Then('the output should contain NDJSON with key {string} and value {string}') do |key, value|
2118
expect(command_line.stdout).to match(/"#{key}": ?"#{value}"/)
2219
end
20+
21+
Then('the output should contain NDJSON {string} message with key {string} and value {string}') do |message_name, key, value|
22+
message_contents = command_line.stdout(format: :messages).detect { |msg| msg.keys == [message_name] }[message_name]
23+
24+
expect(message_contents).to include(key => value)
25+
end
26+
27+
Then('the output should contain NDJSON {string} message with key {string} and boolean value {word}') do |message_name, key, value|
28+
boolean = value == 'true'
29+
message_contents = command_line.stdout(format: :messages).detect { |msg| msg.keys == [message_name] }[message_name]
30+
31+
expect(message_contents).to include(key => boolean)
32+
end

features/lib/support/command_line.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ def stderr
3030
@stderr.string
3131
end
3232

33-
def stdout
34-
@stdout.string
33+
def stdout(format: :string)
34+
case format
35+
when :lines then @stdout.string.split("\n")
36+
when :messages then @stdout.string.split("\n").map { |line| JSON.parse(line) }
37+
else @stdout.string
38+
end
3539
end
3640

3741
def all_output

lib/cucumber/glue/proto_world.rb

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,11 @@ def log(*messages)
9090
# This is only needed in situations where you want to rename a PDF download e.t.c. - In most situations
9191
# you should not need to pass a filename
9292
def attach(file, media_type = nil, filename = nil)
93-
return super unless File.file?(file)
94-
95-
content = File.read(file, mode: 'rb')
96-
media_type = MiniMime.lookup_by_filename(file)&.content_type if media_type.nil?
97-
98-
super(content, media_type.to_s, filename)
93+
if File.file?(file)
94+
media_type = MiniMime.lookup_by_filename(file)&.content_type if media_type.nil?
95+
file = File.read(file, mode: 'rb')
96+
end
97+
super
9998
rescue StandardError
10099
super
101100
end

spec/cucumber/glue/proto_world_spec.rb

Lines changed: 77 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require 'spec_helper'
44
require 'cucumber/formatter/spec_helper'
55
require 'cucumber/formatter/pretty'
6+
require 'cucumber/formatter/message'
67

78
module Cucumber
89
module Glue
@@ -32,38 +33,38 @@ module Glue
3233
run_defined_feature
3334
end
3435

35-
describe 'when modifying the printed variable after the call to log' do
36-
define_feature <<-FEATURE
37-
Feature: Banana party
36+
describe 'when modifying the printed variable after the call to #log' do
37+
define_feature <<~FEATURE
38+
Feature: Banana party
3839
39-
Scenario: Monkey eats banana
40-
When log is called twice for the same variable
40+
Scenario: Monkey eats banana
41+
When log is called twice for the same variable
4142
FEATURE
4243

4344
define_steps do
44-
When(/^log is called twice for the same variable$/) do
45+
When('log is called twice for the same variable') do
4546
foo = String.new('a')
4647
log foo
4748
foo.upcase!
4849
log foo
4950
end
5051
end
5152

52-
it 'prints the variable value at the time puts was called' do
53-
expect(@out.string).to include <<OUTPUT
54-
When log is called twice for the same variable
55-
a
56-
A
57-
OUTPUT
53+
it 'prints the variable value at the time `#log` was called' do
54+
expect(@out.string).to include <<~OUTPUT
55+
When log is called twice for the same variable
56+
a
57+
A
58+
OUTPUT
5859
end
5960
end
6061

6162
describe 'when logging an object' do
62-
define_feature <<-FEATURE
63-
Feature: Banana party
63+
define_feature <<~FEATURE
64+
Feature: Banana party
6465
65-
Scenario: Monkey eats banana
66-
When an object is logged
66+
Scenario: Monkey eats banana
67+
When an object is logged
6768
FEATURE
6869

6970
define_steps do
@@ -82,11 +83,11 @@ def object.to_s
8283
end
8384

8485
describe 'when logging multiple items on one call' do
85-
define_feature <<-FEATURE
86-
Feature: Logging multiple entries
86+
define_feature <<~FEATURE
87+
Feature: Logging multiple entries
8788
88-
Scenario: Logging multiple entries
89-
When logging multiple entries
89+
Scenario: Logging multiple entries
90+
When logging multiple entries
9091
FEATURE
9192

9293
define_steps do
@@ -103,32 +104,6 @@ def object.to_s
103104
].join("\n"))
104105
end
105106
end
106-
107-
describe 'when modifying the printed variable after the call to log' do
108-
define_feature <<-FEATURE
109-
Feature: Banana party
110-
111-
Scenario: Monkey eats banana
112-
When puts is called twice for the same variable
113-
FEATURE
114-
115-
define_steps do
116-
When(/^puts is called twice for the same variable$/) do
117-
foo = String.new('a')
118-
log foo
119-
foo.upcase!
120-
log foo
121-
end
122-
end
123-
124-
it 'prints the variable value at the time puts was called' do
125-
expect(@out.string).to include <<OUTPUT
126-
When puts is called twice for the same variable
127-
a
128-
A
129-
OUTPUT
130-
end
131-
end
132107
end
133108

134109
describe 'Handling attachments in step definitions' do
@@ -142,7 +117,7 @@ def object.to_s
142117
run_defined_feature
143118
end
144119

145-
describe 'when attaching data with null byte' do
120+
context 'when attaching data with null byte' do
146121
define_feature <<~FEATURE
147122
Feature: Banana party
148123
@@ -152,7 +127,7 @@ def object.to_s
152127

153128
define_steps do
154129
When('some data is attached') do
155-
attach("'\x00'attachement", 'text/x.cucumber.log+plain')
130+
attach("'\x00'attachment", 'text/x.cucumber.log+plain')
156131
end
157132
end
158133

@@ -161,7 +136,60 @@ def object.to_s
161136
end
162137

163138
it 'properly attaches the data' do
164-
expect(@out.string).to include("'\x00'attachement")
139+
expect(@out.string).to include("'\x00'attachment")
140+
end
141+
end
142+
143+
context 'when attaching a image using a file path' do
144+
define_feature <<~FEATURE
145+
Feature: Banana party
146+
147+
Scenario: Monkey eats banana
148+
When some data is attached
149+
FEATURE
150+
151+
define_steps do
152+
When('some data is attached') do
153+
path = "#{Dir.pwd}/docs/img/cucumber-open-logo.png"
154+
attach(path, 'image/png')
155+
end
156+
end
157+
158+
it 'does not report an error' do
159+
expect(@out.string).not_to include('Error')
160+
end
161+
162+
it 'properly attaches the image' do
163+
pending 'This is correct currently with the pretty implementation'
164+
165+
expect(@out.string).to include("'\x00'attachment")
166+
end
167+
end
168+
169+
context 'when attaching a image using the input-read data' do
170+
define_feature <<~FEATURE
171+
Feature: Banana party
172+
173+
Scenario: Monkey eats banana
174+
When some data is attached
175+
FEATURE
176+
177+
define_steps do
178+
When('some data is attached') do
179+
path = "#{Dir.pwd}/docs/img/cucumber-open-logo.png"
180+
image_data = File.read(path, mode: 'rb')
181+
attach(image_data, 'base64;image/png')
182+
end
183+
end
184+
185+
it 'does not report an error' do
186+
expect(@out.string).not_to include('Error')
187+
end
188+
189+
it 'properly attaches the image' do
190+
pending 'This is correct currently with the pretty implementation'
191+
192+
expect(@out.string).to include("'\x00'attachment")
165193
end
166194
end
167195
end

0 commit comments

Comments
 (0)