cucumber-compatibility-kit 13.0.0 → 13.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/features/attachments/attachments.feature.rb +13 -24
- data/features/hooks/hooks.feature.rb +1 -6
- data/features/skipped/skipped.feature +8 -8
- data/features/skipped/skipped.feature.ndjson +8 -8
- data/features/skipped/skipped.feature.rb +5 -5
- data/features/stack-traces/stack-traces.feature +4 -6
- data/features/stack-traces/stack-traces.feature.ndjson +3 -3
- data/features/stack-traces/stack-traces.feature.rb +1 -1
- data/features/undefined/undefined.feature +7 -8
- data/features/undefined/undefined.feature.ndjson +6 -6
- data/features/undefined/undefined.feature.rb +1 -1
- data/features/unknown-parameter-type/unknown-parameter-type.feature +1 -1
- data/features/unknown-parameter-type/unknown-parameter-type.feature.ndjson +1 -1
- data/features/unknown-parameter-type/unknown-parameter-type.feature.rb +2 -2
- data/lib/cck/helpers.rb +11 -0
- data/lib/cck/keys_checker.rb +64 -0
- data/lib/cck/messages_comparator.rb +97 -0
- data/lib/cucumber/cucumber-compatibility-kit.rb +57 -0
- data/lib/cucumber-compatibility-kit.rb +1 -57
- data/lib/keys_checker.rb +1 -64
- data/lib/messages_comparator.rb +1 -88
- data/lib/shared_examples.rb +7 -30
- data/spec/cck/keys_checker_spec.rb +67 -0
- data/spec/cck/messages_comparator_spec.rb +29 -0
- data/spec/{cucumber-compatibility-kit_spec.rb → cucumber/cucumber-compatibility-kit_spec.rb} +10 -19
- metadata +13 -11
- data/spec/capture_warnings.rb +0 -75
- data/spec/keys_checker_spec.rb +0 -95
- data/spec/messages_comparator_spec.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 571624730f6e04d4737391f34e4a8e04001a98d8ea71972b300393d8a3dc24a9
|
4
|
+
data.tar.gz: 98687934c3fe1a37a637696b8f362b34882479e4b6cad8630cb04309ffb61f8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6faeb789cf47ac895fcd460fad7d2fff99652e3235b5d45a8d15c283acbd3bcdc35676f51ea2d414804c5822f97c17329653fb398b492b9afbb360731bd72932
|
7
|
+
data.tar.gz: fe388e4219711ecf17eefe17eda1085f8b742b48dc194208704880169f8490b25f7031b2e97f660c5dd7df55cf8174df65835cf62e9e35446c6f90461934304f
|
@@ -3,55 +3,44 @@
|
|
3
3
|
require 'stringio'
|
4
4
|
|
5
5
|
# Cucumber-JVM needs to use a Before hook in order to create attachments
|
6
|
-
Before
|
7
|
-
# no-op
|
8
|
-
end
|
9
|
-
|
10
|
-
def attach_or_embed(world, data, media_type, filename = nil)
|
11
|
-
# Backward compatibility as the steps are also used by cucumber-ruby 3 which does not support `attach`
|
12
|
-
world.respond_to?(:attach) ? attach(data, media_type, filename) : embed(data, media_type)
|
13
|
-
end
|
6
|
+
Before { nil }
|
14
7
|
|
15
8
|
When('the string {string} is attached as {string}') do |text, media_type|
|
16
|
-
|
9
|
+
attach(text, media_type)
|
17
10
|
end
|
18
11
|
|
19
12
|
When('the string {string} is logged') do |text|
|
20
|
-
|
21
|
-
self.respond_to?(:log) ? log(text) : puts(text)
|
22
|
-
end
|
23
|
-
|
24
|
-
When('the following string is attached as {string}:') do |media_type, doc_string|
|
25
|
-
attach_or_embed(self, doc_string, media_type)
|
13
|
+
log(text)
|
26
14
|
end
|
27
15
|
|
28
16
|
When('text with ANSI escapes is logged') do
|
29
|
-
|
17
|
+
log("This displays a \x1b[31mr\x1b[0m\x1b[91ma\x1b[0m\x1b[33mi\x1b[0m\x1b[32mn\x1b[0m\x1b[34mb\x1b[0m\x1b[95mo\x1b[0m\x1b[35mw\x1b[0m")
|
18
|
+
end
|
30
19
|
|
31
|
-
|
20
|
+
When('the following string is attached as {string}:') do |media_type, doc_string|
|
21
|
+
attach(doc_string, media_type)
|
32
22
|
end
|
33
23
|
|
34
24
|
When('an array with {int} bytes is attached as {string}') do |size, media_type|
|
35
|
-
data = (0..size-1).map {|i| [i].pack('C') }.join
|
36
|
-
|
25
|
+
data = (0..size-1).map { |i| [i].pack('C') }.join
|
26
|
+
attach(data, media_type)
|
37
27
|
end
|
38
28
|
|
39
29
|
When('a stream with {int} bytes are attached as {string}') do |size, media_type|
|
40
30
|
stream = StringIO.new
|
41
31
|
stream.puts (0..size).map(&:to_s).join('')
|
42
32
|
stream.seek(0)
|
43
|
-
|
44
|
-
attach_or_embed(self, stream, media_type)
|
33
|
+
attach(stream, media_type)
|
45
34
|
end
|
46
35
|
|
47
36
|
When('a JPEG image is attached') do
|
48
|
-
|
37
|
+
attach(File.open("#{__dir__}/cucumber.jpeg"), 'image/jpeg')
|
49
38
|
end
|
50
39
|
|
51
40
|
When('the {word} png is attached') do |filename|
|
52
|
-
|
41
|
+
attach(File.open("#{__dir__}/#{filename}"), 'image/png')
|
53
42
|
end
|
54
43
|
|
55
44
|
When('a PDF document is attached and renamed') do
|
56
|
-
|
45
|
+
attach(File.open("#{__dir__}/document.pdf"), 'document/pdf', 'renamed.pdf')
|
57
46
|
end
|
@@ -1,10 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
def attach_or_embed(world, data, media_type)
|
4
|
-
# Backward compatibility as the steps are also used by cucumber-ruby 3 which does not support `attach`
|
5
|
-
world.respond_to?(:attach) ? attach(data, media_type) : embed(data, media_type)
|
6
|
-
end
|
7
|
-
|
8
3
|
Before do
|
9
4
|
# no-op
|
10
5
|
end
|
@@ -30,5 +25,5 @@ After('@some-tag or @some-other-tag') do
|
|
30
25
|
end
|
31
26
|
|
32
27
|
After('@with-attachment') do
|
33
|
-
|
28
|
+
attach(File.open("#{__dir__}/cucumber.svg"), 'image/svg+xml')
|
34
29
|
end
|
@@ -1,19 +1,19 @@
|
|
1
1
|
Feature: Skipping scenarios
|
2
2
|
|
3
3
|
Hooks and step definitions are able to signal at runtime that the scenario should
|
4
|
-
be skipped by
|
4
|
+
be skipped by raising a particular kind of exception status (For example PENDING or SKIPPED).
|
5
5
|
|
6
|
-
This can be useful
|
7
|
-
for running
|
6
|
+
This can be useful in certain situations e.g. the current environment doesn't have
|
7
|
+
the right conditions for running a particular scenario.
|
8
8
|
|
9
9
|
@skip
|
10
10
|
Scenario: Skipping from a Before hook
|
11
|
-
Given a step that
|
11
|
+
Given a step that is skipped
|
12
12
|
|
13
13
|
Scenario: Skipping from a step doesn't affect the previous steps
|
14
|
-
Given
|
15
|
-
|
14
|
+
Given a step that does not skip
|
15
|
+
And I skip a step
|
16
16
|
|
17
17
|
Scenario: Skipping from a step causes the rest of the scenario to be skipped
|
18
|
-
Given a step
|
19
|
-
|
18
|
+
Given I skip a step
|
19
|
+
And a step that is skipped
|
@@ -1,12 +1,12 @@
|
|
1
1
|
{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"linux","version":"5.15.0-84-generic"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"18.18.0"}}}
|
2
|
-
{"source":{"data":"Feature: Skipping scenarios\n\n Hooks and step definitions are able to signal at runtime that the scenario should\n be skipped by
|
3
|
-
{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"6","keyword":"Scenario","location":{"column":3,"line":10},"name":"Skipping from a Before hook","steps":[{"id":"4","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":11},"text":"a step that
|
4
|
-
{"pickle":{"astNodeIds":["6"],"id":"14","language":"en","name":"Skipping from a Before hook","steps":[{"astNodeIds":["4"],"id":"13","text":"a step that
|
5
|
-
{"pickle":{"astNodeIds":["9"],"id":"17","language":"en","name":"Skipping from a step doesn't affect the previous steps","steps":[{"astNodeIds":["7"],"id":"15","text":"
|
6
|
-
{"pickle":{"astNodeIds":["12"],"id":"20","language":"en","name":"Skipping from a step causes the rest of the scenario to be skipped","steps":[{"astNodeIds":["10"],"id":"18","text":"a step
|
7
|
-
{"stepDefinition":{"id":"1","pattern":{"source":"
|
8
|
-
{"stepDefinition":{"id":"2","pattern":{"source":"a step that
|
9
|
-
{"stepDefinition":{"id":"3","pattern":{"source":"a step
|
2
|
+
{"source":{"data":"Feature: Skipping scenarios\n\n Hooks and step definitions are able to signal at runtime that the scenario should\n be skipped by raising a particular kind of exception status (For example PENDING or SKIPPED).\n\n This can be useful in certain situations e.g. the current environment doesn't have\n the right conditions for running a particular scenario.\n\n @skip\n Scenario: Skipping from a Before hook\n Given a step that is skipped\n\n Scenario: Skipping from a step doesn't affect the previous steps\n Given a step that does not skip\n And I skip a step\n\n Scenario: Skipping from a step causes the rest of the scenario to be skipped\n Given I skip a step\n And a step that is skipped\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/skipped/skipped.feature"}}
|
3
|
+
{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"6","keyword":"Scenario","location":{"column":3,"line":10},"name":"Skipping from a Before hook","steps":[{"id":"4","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":11},"text":"a step that is skipped"}],"tags":[{"id":"5","location":{"column":3,"line":9},"name":"@skip"}]}},{"scenario":{"description":"","examples":[],"id":"9","keyword":"Scenario","location":{"column":3,"line":13},"name":"Skipping from a step doesn't affect the previous steps","steps":[{"id":"7","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":14},"text":"a step that does not skip"},{"id":"8","keyword":"And ","keywordType":"Conjunction","location":{"column":5,"line":15},"text":"I skip a step"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"12","keyword":"Scenario","location":{"column":3,"line":17},"name":"Skipping from a step causes the rest of the scenario to be skipped","steps":[{"id":"10","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":18},"text":"I skip a step"},{"id":"11","keyword":"And ","keywordType":"Conjunction","location":{"column":5,"line":19},"text":"a step that is skipped"}],"tags":[]}}],"description":" Hooks and step definitions are able to signal at runtime that the scenario should\n be skipped by raising a particular kind of exception status (For example PENDING or SKIPPED).\n\n This can be useful in certain situations e.g. the current environment doesn't have\n the right conditions for running a particular scenario.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Skipping scenarios","tags":[]},"uri":"samples/skipped/skipped.feature"}}
|
4
|
+
{"pickle":{"astNodeIds":["6"],"id":"14","language":"en","name":"Skipping from a Before hook","steps":[{"astNodeIds":["4"],"id":"13","text":"a step that is skipped","type":"Context"}],"tags":[{"astNodeId":"5","name":"@skip"}],"uri":"samples/skipped/skipped.feature"}}
|
5
|
+
{"pickle":{"astNodeIds":["9"],"id":"17","language":"en","name":"Skipping from a step doesn't affect the previous steps","steps":[{"astNodeIds":["7"],"id":"15","text":"a step that does not skip","type":"Context"},{"astNodeIds":["8"],"id":"16","text":"I skip a step","type":"Context"}],"tags":[],"uri":"samples/skipped/skipped.feature"}}
|
6
|
+
{"pickle":{"astNodeIds":["12"],"id":"20","language":"en","name":"Skipping from a step causes the rest of the scenario to be skipped","steps":[{"astNodeIds":["10"],"id":"18","text":"I skip a step","type":"Context"},{"astNodeIds":["11"],"id":"19","text":"a step that is skipped","type":"Context"}],"tags":[],"uri":"samples/skipped/skipped.feature"}}
|
7
|
+
{"stepDefinition":{"id":"1","pattern":{"source":"a step that does not skip","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":7},"uri":"samples/skipped/skipped.feature.ts"}}}
|
8
|
+
{"stepDefinition":{"id":"2","pattern":{"source":"a step that is skipped","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":11},"uri":"samples/skipped/skipped.feature.ts"}}}
|
9
|
+
{"stepDefinition":{"id":"3","pattern":{"source":"I skip a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":15},"uri":"samples/skipped/skipped.feature.ts"}}}
|
10
10
|
{"hook":{"id":"0","sourceReference":{"location":{"line":3},"uri":"samples/skipped/skipped.feature.ts"},"tagExpression":"@skip"}}
|
11
11
|
{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}}
|
12
12
|
{"testCase":{"id":"23","pickleId":"14","testSteps":[{"hookId":"0","id":"21"},{"id":"22","pickleStepId":"13","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}}
|
@@ -1,17 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Before('@skip') do
|
4
|
-
'
|
4
|
+
skip_this_scenario('')
|
5
5
|
end
|
6
6
|
|
7
|
-
Given('
|
7
|
+
Given('a step that does not skip') do
|
8
8
|
# no-op
|
9
9
|
end
|
10
10
|
|
11
|
-
Given('a step that
|
11
|
+
Given('a step that is skipped') do
|
12
12
|
# no-op
|
13
13
|
end
|
14
14
|
|
15
|
-
Given('a step
|
16
|
-
'
|
15
|
+
Given('I skip a step') do
|
16
|
+
skip_this_scenario('')
|
17
17
|
end
|
@@ -1,11 +1,9 @@
|
|
1
1
|
Feature: Stack traces
|
2
|
-
|
3
|
-
Cucumber provides helpful stack traces that
|
4
|
-
|
5
|
-
- Include a stack frame from the Gherkin document
|
6
|
-
- Remove uninteresting frames by default
|
2
|
+
Stack traces can help you diagnose the source of a bug.
|
3
|
+
Cucumber provides helpful stack traces that includes the stack frames from the
|
4
|
+
Gherkin document and remove uninteresting frames by default
|
7
5
|
|
8
|
-
The first line of the stack trace
|
6
|
+
The first line of the stack trace will contain a reference to the feature file.
|
9
7
|
|
10
8
|
Scenario: A failing step
|
11
9
|
When a step throws an exception
|
@@ -1,12 +1,12 @@
|
|
1
1
|
{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"linux","version":"5.15.0-84-generic"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"18.18.0"}}}
|
2
|
-
{"source":{"data":"Feature: Stack traces\n
|
3
|
-
{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"2","keyword":"Scenario","location":{"column":3,"line":
|
2
|
+
{"source":{"data":"Feature: Stack traces\n Stack traces can help you diagnose the source of a bug.\n Cucumber provides helpful stack traces that includes the stack frames from the\n Gherkin document and remove uninteresting frames by default\n\n The first line of the stack trace will contain a reference to the feature file.\n\n Scenario: A failing step\n When a step throws an exception\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/stack-traces/stack-traces.feature"}}
|
3
|
+
{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"2","keyword":"Scenario","location":{"column":3,"line":8},"name":"A failing step","steps":[{"id":"1","keyword":"When ","keywordType":"Action","location":{"column":5,"line":9},"text":"a step throws an exception"}],"tags":[]}}],"description":" Stack traces can help you diagnose the source of a bug.\n Cucumber provides helpful stack traces that includes the stack frames from the\n Gherkin document and remove uninteresting frames by default\n\n The first line of the stack trace will contain a reference to the feature file.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Stack traces","tags":[]},"uri":"samples/stack-traces/stack-traces.feature"}}
|
4
4
|
{"pickle":{"astNodeIds":["2"],"id":"4","language":"en","name":"A failing step","steps":[{"astNodeIds":["1"],"id":"3","text":"a step throws an exception","type":"Action"}],"tags":[],"uri":"samples/stack-traces/stack-traces.feature"}}
|
5
5
|
{"stepDefinition":{"id":"0","pattern":{"source":"a step throws an exception","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":3},"uri":"samples/stack-traces/stack-traces.feature.ts"}}}
|
6
6
|
{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}}
|
7
7
|
{"testCase":{"id":"6","pickleId":"4","testSteps":[{"id":"5","pickleStepId":"3","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}}
|
8
8
|
{"testCaseStarted":{"attempt":0,"id":"7","testCaseId":"6","timestamp":{"nanos":1000000,"seconds":0}}}
|
9
9
|
{"testStepStarted":{"testCaseStartedId":"7","testStepId":"5","timestamp":{"nanos":2000000,"seconds":0}}}
|
10
|
-
{"testStepFinished":{"testCaseStartedId":"7","testStepId":"5","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"BOOM","type":"Error"},"message":"BOOM\nsamples/stack-traces/stack-traces.feature:
|
10
|
+
{"testStepFinished":{"testCaseStartedId":"7","testStepId":"5","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"exception":{"message":"BOOM","type":"Error"},"message":"BOOM\nsamples/stack-traces/stack-traces.feature:9","status":"FAILED"},"timestamp":{"nanos":3000000,"seconds":0}}}
|
11
11
|
{"testCaseFinished":{"testCaseStartedId":"7","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}}
|
12
12
|
{"testRunFinished":{"success":false,"timestamp":{"nanos":5000000,"seconds":0}}}
|
@@ -1,17 +1,16 @@
|
|
1
1
|
Feature: Undefined steps
|
2
2
|
|
3
3
|
At runtime, Cucumber may encounter a step in a scenario that it cannot match to a
|
4
|
-
step definition. In these cases, the scenario
|
5
|
-
will be UNDEFINED, with subsequent steps being
|
6
|
-
as a failure.
|
4
|
+
step definition. In these cases, the scenario is not able to run and so the step status
|
5
|
+
will be UNDEFINED, with subsequent steps being SKIPPED and the overall result will be FAILURE
|
7
6
|
|
8
|
-
Scenario:
|
9
|
-
Given a step that
|
7
|
+
Scenario: An undefined step causes a failure
|
8
|
+
Given a step that is yet to be defined
|
10
9
|
|
11
10
|
Scenario: Steps before undefined steps are executed
|
12
11
|
Given an implemented step
|
13
|
-
|
12
|
+
And a step that is yet to be defined
|
14
13
|
|
15
14
|
Scenario: Steps after undefined steps are skipped
|
16
|
-
Given a step that
|
17
|
-
|
15
|
+
Given a step that is yet to be defined
|
16
|
+
And a step that will be skipped
|
@@ -1,11 +1,11 @@
|
|
1
1
|
{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"linux","version":"5.15.0-84-generic"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"18.18.0"}}}
|
2
|
-
{"source":{"data":"Feature: Undefined steps\n\n At runtime, Cucumber may encounter a step in a scenario that it cannot match to a\n step definition. In these cases, the scenario
|
3
|
-
{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"3","keyword":"Scenario","location":{"column":3,"line":
|
4
|
-
{"pickle":{"astNodeIds":["3"],"id":"11","language":"en","name":"
|
5
|
-
{"pickle":{"astNodeIds":["6"],"id":"14","language":"en","name":"Steps before undefined steps are executed","steps":[{"astNodeIds":["4"],"id":"12","text":"an implemented step","type":"Context"},{"astNodeIds":["5"],"id":"13","text":"a step that
|
6
|
-
{"pickle":{"astNodeIds":["9"],"id":"17","language":"en","name":"Steps after undefined steps are skipped","steps":[{"astNodeIds":["7"],"id":"15","text":"a step that
|
2
|
+
{"source":{"data":"Feature: Undefined steps\n\n At runtime, Cucumber may encounter a step in a scenario that it cannot match to a\n step definition. In these cases, the scenario is not able to run and so the step status\n will be UNDEFINED, with subsequent steps being SKIPPED and the overall result will be FAILURE\n\n Scenario: An undefined step causes a failure\n Given a step that is yet to be defined\n\n Scenario: Steps before undefined steps are executed\n Given an implemented step\n And a step that is yet to be defined\n\n Scenario: Steps after undefined steps are skipped\n Given a step that is yet to be defined\n And a step that will be skipped\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/undefined/undefined.feature"}}
|
3
|
+
{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"3","keyword":"Scenario","location":{"column":3,"line":7},"name":"An undefined step causes a failure","steps":[{"id":"2","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":8},"text":"a step that is yet to be defined"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"6","keyword":"Scenario","location":{"column":3,"line":10},"name":"Steps before undefined steps are executed","steps":[{"id":"4","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":11},"text":"an implemented step"},{"id":"5","keyword":"And ","keywordType":"Conjunction","location":{"column":5,"line":12},"text":"a step that is yet to be defined"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"9","keyword":"Scenario","location":{"column":3,"line":14},"name":"Steps after undefined steps are skipped","steps":[{"id":"7","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":15},"text":"a step that is yet to be defined"},{"id":"8","keyword":"And ","keywordType":"Conjunction","location":{"column":5,"line":16},"text":"a step that will be skipped"}],"tags":[]}}],"description":" At runtime, Cucumber may encounter a step in a scenario that it cannot match to a\n step definition. In these cases, the scenario is not able to run and so the step status\n will be UNDEFINED, with subsequent steps being SKIPPED and the overall result will be FAILURE","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Undefined steps","tags":[]},"uri":"samples/undefined/undefined.feature"}}
|
4
|
+
{"pickle":{"astNodeIds":["3"],"id":"11","language":"en","name":"An undefined step causes a failure","steps":[{"astNodeIds":["2"],"id":"10","text":"a step that is yet to be defined","type":"Context"}],"tags":[],"uri":"samples/undefined/undefined.feature"}}
|
5
|
+
{"pickle":{"astNodeIds":["6"],"id":"14","language":"en","name":"Steps before undefined steps are executed","steps":[{"astNodeIds":["4"],"id":"12","text":"an implemented step","type":"Context"},{"astNodeIds":["5"],"id":"13","text":"a step that is yet to be defined","type":"Context"}],"tags":[],"uri":"samples/undefined/undefined.feature"}}
|
6
|
+
{"pickle":{"astNodeIds":["9"],"id":"17","language":"en","name":"Steps after undefined steps are skipped","steps":[{"astNodeIds":["7"],"id":"15","text":"a step that is yet to be defined","type":"Context"},{"astNodeIds":["8"],"id":"16","text":"a step that will be skipped","type":"Context"}],"tags":[],"uri":"samples/undefined/undefined.feature"}}
|
7
7
|
{"stepDefinition":{"id":"0","pattern":{"source":"an implemented step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":3},"uri":"samples/undefined/undefined.feature.ts"}}}
|
8
|
-
{"stepDefinition":{"id":"1","pattern":{"source":"a step that
|
8
|
+
{"stepDefinition":{"id":"1","pattern":{"source":"a step that will be skipped","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":7},"uri":"samples/undefined/undefined.feature.ts"}}}
|
9
9
|
{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}}
|
10
10
|
{"testCase":{"id":"19","pickleId":"11","testSteps":[{"id":"18","pickleStepId":"10","stepDefinitionIds":[],"stepMatchArgumentsLists":[]}]}}
|
11
11
|
{"testCase":{"id":"22","pickleId":"14","testSteps":[{"id":"20","pickleStepId":"12","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"21","pickleStepId":"13","stepDefinitionIds":[],"stepMatchArgumentsLists":[]}]}}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.3.0"},"os":{"name":"linux","version":"5.15.0-84-generic"},"protocolVersion":"22.0.0","runtime":{"name":"node.js","version":"18.18.0"}}}
|
2
|
-
{"source":{"data":"Feature: Parameter Types\n Cucumber will generate an error message if a step definition registers\n an unknown parameter type, but the suite will run.\n\n Scenario: undefined parameter type\n Given CDG is closed because of a strike","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/unknown-parameter-type/unknown-parameter-type.feature"}}
|
2
|
+
{"source":{"data":"Feature: Parameter Types\n Cucumber will generate an error message if a step definition registers\n an unknown parameter type, but the suite will run.\n\n Scenario: undefined parameter type\n Given CDG is closed because of a strike\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/unknown-parameter-type/unknown-parameter-type.feature"}}
|
3
3
|
{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"1","keyword":"Scenario","location":{"column":3,"line":5},"name":"undefined parameter type","steps":[{"id":"0","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":6},"text":"CDG is closed because of a strike"}],"tags":[]}}],"description":" Cucumber will generate an error message if a step definition registers\n an unknown parameter type, but the suite will run.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Parameter Types","tags":[]},"uri":"samples/unknown-parameter-type/unknown-parameter-type.feature"}}
|
4
4
|
{"pickle":{"astNodeIds":["1"],"id":"3","language":"en","name":"undefined parameter type","steps":[{"astNodeIds":["0"],"id":"2","text":"CDG is closed because of a strike","type":"Context"}],"tags":[],"uri":"samples/unknown-parameter-type/unknown-parameter-type.feature"}}
|
5
5
|
{"undefinedParameterType":{"expression":"{airport} is closed because of a strike","name":"airport"}}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
Given('{airport} is closed because of a strike') do |
|
4
|
-
raise
|
3
|
+
Given('{airport} is closed because of a strike') do |_airport|
|
4
|
+
raise 'Should not be called because airport parameter type has not been defined'
|
5
5
|
end
|
data/lib/cck/helpers.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CCK
|
4
|
+
class KeysChecker
|
5
|
+
def self.compare(detected, expected)
|
6
|
+
new(detected, expected).compare
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :detected, :expected
|
10
|
+
|
11
|
+
def initialize(detected, expected)
|
12
|
+
@detected = detected
|
13
|
+
@expected = expected
|
14
|
+
end
|
15
|
+
|
16
|
+
def compare
|
17
|
+
return [] if identical_keys?
|
18
|
+
|
19
|
+
errors << "Detected extra keys in message #{message_name}: #{extra_keys}" if extra_keys.any?
|
20
|
+
errors << "Missing keys in message #{message_name}: #{missing_keys}" if missing_keys.any?
|
21
|
+
errors
|
22
|
+
rescue StandardError => e
|
23
|
+
["Unexpected error: #{e.message}"]
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def detected_keys
|
29
|
+
@detected_keys ||= ordered_uniq_hash_keys(detected)
|
30
|
+
end
|
31
|
+
|
32
|
+
def expected_keys
|
33
|
+
@expected_keys ||= ordered_uniq_hash_keys(expected)
|
34
|
+
end
|
35
|
+
|
36
|
+
def identical_keys?
|
37
|
+
detected_keys == expected_keys
|
38
|
+
end
|
39
|
+
|
40
|
+
def missing_keys
|
41
|
+
(expected_keys - detected_keys).reject { |key| meta_message? && key == :ci }
|
42
|
+
end
|
43
|
+
|
44
|
+
def extra_keys
|
45
|
+
(detected_keys - expected_keys).reject { |key| meta_message? && key == :ci }
|
46
|
+
end
|
47
|
+
|
48
|
+
def meta_message?
|
49
|
+
detected.instance_of?(Cucumber::Messages::Meta)
|
50
|
+
end
|
51
|
+
|
52
|
+
def message_name
|
53
|
+
detected.class.name
|
54
|
+
end
|
55
|
+
|
56
|
+
def ordered_uniq_hash_keys(object)
|
57
|
+
object.to_h(reject_nil_values: true).keys.sort
|
58
|
+
end
|
59
|
+
|
60
|
+
def errors
|
61
|
+
@errors ||= []
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'keys_checker'
|
4
|
+
require_relative 'helpers'
|
5
|
+
|
6
|
+
module CCK
|
7
|
+
class MessagesComparator
|
8
|
+
include Helpers
|
9
|
+
|
10
|
+
def initialize(validator, detected, expected)
|
11
|
+
@all_errors = []
|
12
|
+
@compared = []
|
13
|
+
@validator = validator
|
14
|
+
|
15
|
+
compare(detected, expected)
|
16
|
+
end
|
17
|
+
|
18
|
+
def errors
|
19
|
+
@all_errors.flatten
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def compare(detected, expected)
|
25
|
+
detected_by_type = messages_by_type(detected)
|
26
|
+
expected_by_type = messages_by_type(expected)
|
27
|
+
|
28
|
+
detected_by_type.keys.each do |type|
|
29
|
+
compare_list(detected_by_type[type], expected_by_type[type])
|
30
|
+
rescue StandardError => e
|
31
|
+
@all_errors << "Error while comparing #{type}: #{e.message}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def messages_by_type(messages)
|
36
|
+
by_type = Hash.new { |h, k| h[k] = [] }
|
37
|
+
messages.each do |msg|
|
38
|
+
by_type[message_type(msg)] << remove_envelope(msg)
|
39
|
+
end
|
40
|
+
by_type
|
41
|
+
end
|
42
|
+
|
43
|
+
def remove_envelope(message)
|
44
|
+
message.send(message_type(message))
|
45
|
+
end
|
46
|
+
|
47
|
+
def compare_list(detected, expected)
|
48
|
+
detected.each_with_index do |message, index|
|
49
|
+
compare_message(message, expected[index])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def compare_message(detected, expected)
|
54
|
+
return if not_message?(detected)
|
55
|
+
return if ignorable?(detected)
|
56
|
+
return if incomparable?(detected)
|
57
|
+
|
58
|
+
@all_errors << @validator.compare(detected, expected)
|
59
|
+
@compared << detected.class.name
|
60
|
+
compare_sub_messages(detected, expected)
|
61
|
+
end
|
62
|
+
|
63
|
+
def not_message?(detected)
|
64
|
+
!detected.is_a?(Cucumber::Messages::Message)
|
65
|
+
end
|
66
|
+
|
67
|
+
# These messages we need to ignore because they are too large or they feature timestamps which always vary
|
68
|
+
def ignorable?(detected)
|
69
|
+
too_large_message?(detected) || time_message?(detected)
|
70
|
+
end
|
71
|
+
|
72
|
+
def too_large_message?(detected)
|
73
|
+
detected.is_a?(Cucumber::Messages::GherkinDocument) || detected.is_a?(Cucumber::Messages::Pickle)
|
74
|
+
end
|
75
|
+
|
76
|
+
def time_message?(detected)
|
77
|
+
detected.is_a?(Cucumber::Messages::Timestamp) || detected.is_a?(Cucumber::Messages::Duration)
|
78
|
+
end
|
79
|
+
|
80
|
+
# These messages we need to ignore because they are often not of identical shape/value
|
81
|
+
def incomparable?(detected)
|
82
|
+
detected.is_a?(Cucumber::Messages::Ci) || detected.is_a?(Cucumber::Messages::Git)
|
83
|
+
end
|
84
|
+
|
85
|
+
def compare_sub_messages(detected, expected)
|
86
|
+
return unless expected.respond_to? :to_h
|
87
|
+
expected.to_h.keys.each do |key|
|
88
|
+
value = expected.send(key)
|
89
|
+
if value.is_a?(Array)
|
90
|
+
compare_list(detected.send(key), value)
|
91
|
+
else
|
92
|
+
compare_message(detected.send(key), value)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'shared_examples'
|
4
|
+
|
5
|
+
module Cucumber::CompatibilityKit
|
6
|
+
class << self
|
7
|
+
def all_examples
|
8
|
+
gherkin_examples + markdown_examples
|
9
|
+
end
|
10
|
+
|
11
|
+
def gherkin_examples
|
12
|
+
Dir
|
13
|
+
.entries(examples_path)
|
14
|
+
.select do |file|
|
15
|
+
folder = File.join(examples_path, file)
|
16
|
+
|
17
|
+
file != '.' && file != '..' &&
|
18
|
+
File.directory?(folder) &&
|
19
|
+
gherkin_example?(folder)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def markdown_examples
|
24
|
+
Dir
|
25
|
+
.entries(examples_path)
|
26
|
+
.select do |file|
|
27
|
+
folder = File.join(examples_path, file)
|
28
|
+
|
29
|
+
file != '.' && file != '..' &&
|
30
|
+
File.directory?(folder) &&
|
31
|
+
markdown_example?(folder)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def examples_path
|
36
|
+
File.expand_path("#{File.dirname(__FILE__)}/../../features/")
|
37
|
+
end
|
38
|
+
|
39
|
+
def example_path(example_name)
|
40
|
+
path = File.join(examples_path, example_name)
|
41
|
+
|
42
|
+
return path if File.directory?(path)
|
43
|
+
|
44
|
+
raise ArgumentError.new
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def gherkin_example?(example_folder)
|
50
|
+
Dir.entries(example_folder).select { |file| File.extname(file) == '.feature' }.count > 0
|
51
|
+
end
|
52
|
+
|
53
|
+
def markdown_example?(example_folder)
|
54
|
+
Dir.entries(example_folder).select { |file| File.extname(file) == '.md' }.count > 0
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|