cucumber 4.1.0 → 9.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +134 -21
- data/VERSION +1 -0
- data/lib/cucumber/cli/configuration.rb +27 -2
- data/lib/cucumber/cli/main.rb +5 -4
- data/lib/cucumber/cli/options.rb +102 -65
- data/lib/cucumber/cli/profile_loader.rb +6 -10
- data/lib/cucumber/cli/rerun_file.rb +1 -1
- data/lib/cucumber/configuration.rb +26 -13
- data/lib/cucumber/constantize.rb +1 -1
- data/lib/cucumber/deprecate.rb +6 -46
- data/lib/cucumber/errors.rb +4 -3
- data/lib/cucumber/events/envelope.rb +2 -0
- data/lib/cucumber/events/gherkin_source_parsed.rb +2 -0
- data/lib/cucumber/events/gherkin_source_read.rb +2 -0
- data/lib/cucumber/events/hook_test_step_created.rb +1 -2
- data/lib/cucumber/events/step_activated.rb +0 -6
- data/lib/cucumber/events/step_definition_registered.rb +0 -5
- data/lib/cucumber/events/test_case_created.rb +1 -2
- data/lib/cucumber/events/test_case_finished.rb +2 -0
- data/lib/cucumber/events/test_case_started.rb +2 -0
- data/lib/cucumber/events/test_run_finished.rb +2 -1
- data/lib/cucumber/events/test_step_created.rb +1 -2
- data/lib/cucumber/events/test_step_finished.rb +2 -0
- data/lib/cucumber/events/test_step_started.rb +2 -0
- data/lib/cucumber/events/undefined_parameter_type.rb +3 -2
- data/lib/cucumber/events.rb +2 -2
- data/lib/cucumber/file_specs.rb +2 -1
- data/lib/cucumber/filters/activate_steps.rb +1 -0
- data/lib/cucumber/filters/retry.rb +20 -1
- data/lib/cucumber/filters/tag_limits/verifier.rb +1 -3
- data/lib/cucumber/filters/tag_limits.rb +1 -3
- data/lib/cucumber/formatter/ansicolor.rb +70 -85
- data/lib/cucumber/formatter/ast_lookup.rb +16 -10
- data/lib/cucumber/formatter/backtrace_filter.rb +3 -1
- data/lib/cucumber/formatter/console.rb +34 -16
- data/lib/cucumber/formatter/console_counts.rb +3 -1
- data/lib/cucumber/formatter/console_issues.rb +10 -3
- data/lib/cucumber/formatter/curl_option_parser.rb +49 -0
- data/lib/cucumber/formatter/duration_extractor.rb +1 -0
- data/lib/cucumber/formatter/errors.rb +3 -0
- data/lib/cucumber/formatter/fail_fast.rb +1 -1
- data/lib/cucumber/formatter/fanout.rb +1 -1
- data/lib/cucumber/formatter/html.rb +3 -1
- data/lib/cucumber/formatter/http_io.rb +10 -136
- data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
- data/lib/cucumber/formatter/interceptor.rb +4 -3
- data/lib/cucumber/formatter/io.rb +50 -12
- data/lib/cucumber/formatter/io_http_buffer.rb +88 -0
- data/lib/cucumber/formatter/json.rb +36 -37
- data/lib/cucumber/formatter/junit.rb +29 -9
- data/lib/cucumber/formatter/message.rb +3 -2
- data/lib/cucumber/formatter/message_builder.rb +32 -16
- data/lib/cucumber/formatter/pretty.rb +44 -29
- data/lib/cucumber/formatter/progress.rb +2 -1
- data/lib/cucumber/formatter/publish_banner_printer.rb +75 -0
- data/lib/cucumber/formatter/query/hook_by_test_step.rb +3 -0
- data/lib/cucumber/formatter/query/pickle_by_test.rb +2 -0
- data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +2 -0
- data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +2 -0
- data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +4 -0
- data/lib/cucumber/formatter/rerun.rb +7 -5
- data/lib/cucumber/formatter/steps.rb +6 -3
- data/lib/cucumber/formatter/summary.rb +2 -1
- data/lib/cucumber/formatter/unicode.rb +7 -7
- data/lib/cucumber/formatter/url_reporter.rb +19 -0
- data/lib/cucumber/formatter/usage.rb +9 -7
- data/lib/cucumber/gherkin/data_table_parser.rb +2 -1
- data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +25 -27
- data/lib/cucumber/gherkin/steps_parser.rb +1 -1
- data/lib/cucumber/glue/dsl.rb +30 -16
- data/lib/cucumber/glue/hook.rb +6 -3
- data/lib/cucumber/glue/invoke_in_world.rb +5 -5
- data/lib/cucumber/glue/proto_world.rb +37 -57
- data/lib/cucumber/glue/registry_and_more.rb +31 -12
- data/lib/cucumber/glue/registry_wrapper.rb +31 -0
- data/lib/cucumber/glue/snippet.rb +4 -2
- data/lib/cucumber/glue/step_definition.rb +9 -7
- data/lib/cucumber/glue/world_factory.rb +2 -0
- data/lib/cucumber/hooks.rb +1 -0
- data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +5 -2
- data/lib/cucumber/multiline_argument/data_table.rb +65 -79
- data/lib/cucumber/platform.rb +11 -16
- data/lib/cucumber/rake/task.rb +22 -17
- data/lib/cucumber/rspec/disable_option_parser.rb +6 -3
- data/lib/cucumber/rspec/doubles.rb +3 -5
- data/lib/cucumber/running_test_case.rb +2 -1
- data/lib/cucumber/runtime/for_programming_languages.rb +1 -2
- data/lib/cucumber/runtime/meta_message_builder.rb +108 -0
- data/lib/cucumber/runtime/support_code.rb +3 -0
- data/lib/cucumber/runtime/user_interface.rb +7 -6
- data/lib/cucumber/runtime.rb +50 -24
- data/lib/cucumber/step_match.rb +7 -11
- data/lib/cucumber/step_match_search.rb +3 -2
- data/lib/cucumber/term/ansicolor.rb +74 -50
- data/lib/cucumber/term/banner.rb +59 -0
- data/lib/cucumber.rb +2 -1
- data/lib/simplecov_setup.rb +1 -1
- metadata +103 -229
- data/CHANGELOG.md +0 -2682
- data/CONTRIBUTING.md +0 -71
- data/lib/autotest/cucumber.rb +0 -8
- data/lib/autotest/cucumber_mixin.rb +0 -132
- data/lib/autotest/cucumber_rails.rb +0 -8
- data/lib/autotest/cucumber_rails_rspec.rb +0 -8
- data/lib/autotest/cucumber_rails_rspec2.rb +0 -8
- data/lib/autotest/cucumber_rspec.rb +0 -8
- data/lib/autotest/cucumber_rspec2.rb +0 -8
- data/lib/autotest/discover.rb +0 -13
- data/lib/cucumber/core_ext/string.rb +0 -11
- data/lib/cucumber/version +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c84d3e0ab90cabd1d67121371c8a95c0e5f91bfa5f3ec42a1024c1a85130bde
|
4
|
+
data.tar.gz: 1386d49afc93d04c9b616f38ab4466c8aa3092aa60a293c82557465ad8b4538a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afd28a66515dbb07168f7c906d1de7feb5c780437a22b0492f218db943555bc996e7dfc77750257033c9ae8c843e350ca12c389963b78e6ddf1c58dc3f1df371
|
7
|
+
data.tar.gz: bd21e8e340afc215e887be2e353fd9060a4a4ccf1975298403ed6487e263b4103ef01a73e069f16758cc286ba6d507ae0e2f4b9d72916fb976cbbe440af9d154
|
data/README.md
CHANGED
@@ -1,38 +1,151 @@
|
|
1
|
-
|
2
|
-
[![OpenCollective](https://opencollective.com/cucumber/sponsors/badge.svg)](https://opencollective.com/cucumber)
|
1
|
+
<img src="docs/img/cucumber-open-logo.png" alt="Cucumber Open - Supported by Smartbear" width="428" />
|
3
2
|
|
4
|
-
|
3
|
+
# Cucumber
|
5
4
|
|
5
|
+
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://vshymanskyy.github.io/StandWithUkraine)
|
6
|
+
[![OpenCollective](https://opencollective.com/cucumber/backers/badge.svg)](https://opencollective.com/cucumber)
|
7
|
+
[![OpenCollective](https://opencollective.com/cucumber/sponsors/badge.svg)](https://opencollective.com/cucumber)
|
8
|
+
[![Test cucumber](https://github.com/cucumber/cucumber-ruby/actions/workflows/test.yaml/badge.svg)](https://github.com/cucumber/cucumber-ruby/actions/workflows/test.yaml)
|
6
9
|
[![Code Climate](https://codeclimate.com/github/cucumber/cucumber-ruby.svg)](https://codeclimate.com/github/cucumber/cucumber-ruby)
|
7
|
-
[![Coverage Status](https://coveralls.io/repos/cucumber/cucumber-ruby/badge.svg?branch=
|
8
|
-
|
9
|
-
# Cucumber
|
10
|
+
[![Coverage Status](https://coveralls.io/repos/cucumber/cucumber-ruby/badge.svg?branch=main)](https://coveralls.io/r/cucumber/cucumber-ruby?branch=main)
|
10
11
|
|
11
12
|
Cucumber is a tool for running automated tests written in plain language. Because they're
|
12
13
|
written in plain language, they can be read by anyone on your team. Because they can be
|
13
14
|
read by anyone, you can use them to help improve communication, collaboration and trust on
|
14
15
|
your team.
|
15
16
|
|
16
|
-
|
17
|
+
<img src="docs/img/gherkin-example.png" alt="Cucumber Gherkin Example" width="728" />
|
18
|
+
|
19
|
+
This is the Ruby implementation of Cucumber. Cucumber is also available for [JavaScript](https://github.com/cucumber/cucumber-js),
|
20
|
+
[Java](https://github.com/cucumber/cucumber-jvm), and a lot of other languages. You can find a list of implementations here: https://cucumber.io/docs/installation/.
|
21
|
+
|
22
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for info on contributing to Cucumber (issues, PRs, etc.).
|
23
|
+
|
24
|
+
Everyone interacting in this codebase and issue tracker is expected to follow the
|
25
|
+
Cucumber [code of conduct](https://cucumber.io/conduct).
|
26
|
+
|
27
|
+
## Installation
|
28
|
+
|
29
|
+
Cucumber for Ruby is a Ruby gem. Install it as you would install any gem: add
|
30
|
+
`cucumber` to your Gemfile:
|
31
|
+
|
32
|
+
gem 'cucumber'
|
33
|
+
|
34
|
+
then install it:
|
35
|
+
|
36
|
+
$ bundle
|
37
|
+
|
38
|
+
or install the gem directly:
|
39
|
+
|
40
|
+
$ gem install cucumber
|
41
|
+
|
42
|
+
Later in this document, bundler is considered being used so all commands are using
|
43
|
+
`bundle exec`. If this is not the case for you, execute `cucumber` directly, without
|
44
|
+
`bundle exec`.
|
45
|
+
|
46
|
+
### Supported platforms
|
47
|
+
|
48
|
+
- Ruby 3.2
|
49
|
+
- Ruby 3.1
|
50
|
+
- Ruby 3.0
|
51
|
+
- Ruby 2.7
|
52
|
+
- TruffleRuby 22.0.0+
|
53
|
+
- JRuby 9.4+ (with [some limitations](https://github.com/cucumber/cucumber-ruby/blob/main/docs/jruby-limitations.md))
|
54
|
+
|
55
|
+
### Ruby on Rails
|
56
|
+
|
57
|
+
Using Ruby on Rails? You can use [cucumber-rails](https://github.com/cucumber/cucumber-rails) to bring Cucumber into your Rails project.
|
58
|
+
|
59
|
+
## Usage
|
60
|
+
|
61
|
+
### Initialization
|
62
|
+
|
63
|
+
If you need to, initialize your `features` directory with
|
64
|
+
|
65
|
+
$ bundle exec cucumber --init
|
66
|
+
|
67
|
+
This will create the following directories and files if they do not exist already:
|
68
|
+
|
69
|
+
features
|
70
|
+
├── step_definitions
|
71
|
+
└── support
|
72
|
+
└── env.rb
|
73
|
+
|
74
|
+
### Create your specification
|
75
|
+
|
76
|
+
Create a file named `rule.feature` in the `features` directory with:
|
77
|
+
|
78
|
+
```gherkin
|
79
|
+
# features/rule.feature
|
80
|
+
|
81
|
+
Feature: Rule Sample
|
82
|
+
|
83
|
+
Rule: This is a rule
|
84
|
+
|
85
|
+
Example: A passing example
|
86
|
+
Given this will pass
|
87
|
+
When I do an action
|
88
|
+
Then some results should be there
|
89
|
+
|
90
|
+
Example: A failing example
|
91
|
+
Given this will fail
|
92
|
+
When I do an action
|
93
|
+
Then some results should be there
|
94
|
+
|
95
|
+
```
|
96
|
+
|
97
|
+
### Automate your specification
|
98
|
+
|
99
|
+
And a file named `steps.rb` in `features/step_definitions` with:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
# features/step_definitions/steps.rb
|
103
|
+
|
104
|
+
Given('this will pass') do
|
105
|
+
@this_will_pass = true
|
106
|
+
end
|
107
|
+
|
108
|
+
Given('this will fail') do
|
109
|
+
@this_will_pass = false
|
110
|
+
end
|
111
|
+
|
112
|
+
When('I do an action') do
|
113
|
+
:no_op
|
114
|
+
end
|
115
|
+
|
116
|
+
Then("some results should be there") do
|
117
|
+
expect(@this_will_pass).to be true
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
### Run Cucumber
|
122
|
+
|
123
|
+
$ bundle exec cucumber
|
124
|
+
|
125
|
+
To execute a single feature file:
|
126
|
+
|
127
|
+
$ bundle exec cucumber features/rule.feature
|
128
|
+
|
129
|
+
To execute a single example, indicates the line of the name of the example:
|
130
|
+
|
131
|
+
$ bundle exec cucumber features/rule.feature:5
|
132
|
+
|
133
|
+
To summarize the results on the standard output, and generate a HTML report on disk:
|
134
|
+
|
135
|
+
$ bundle exec cucumber --format summary --format html --out report.html
|
17
136
|
|
18
|
-
|
19
|
-
* Documentation: https://cucumber.io/docs
|
20
|
-
* Ruby API Documentation: http://www.rubydoc.info/github/cucumber/cucumber-ruby/
|
21
|
-
* Support forum: https://groups.google.com/group/cukes
|
22
|
-
* Chat: ([Slack](https://cucumber.io/support#slack) and [Gitter](https://cucumber.io/support#gitter))
|
137
|
+
For more command line options
|
23
138
|
|
24
|
-
|
139
|
+
$ bundle exec cucumber --help
|
25
140
|
|
26
|
-
|
27
|
-
* Ruby 2.6
|
28
|
-
* Ruby 2.5
|
29
|
-
* Ruby 2.4
|
30
|
-
* Ruby 2.3
|
31
|
-
* JRuby 9.2 (with [some limitations](https://github.com/cucumber/cucumber-ruby/blob/master/docs/jruby-limitations.md))
|
141
|
+
You can also find documentation on the command line possibilities in [features/docs/cli](features/docs/cli).
|
32
142
|
|
33
|
-
##
|
143
|
+
## Documentation and support
|
34
144
|
|
35
|
-
|
145
|
+
- Getting started, writing features, step definitions, and more: https://cucumber.io/docs
|
146
|
+
- Ruby API Documentation: http://www.rubydoc.info/github/cucumber/cucumber-ruby/
|
147
|
+
- Community support forum: https://community.smartbear.com/t5/Cucumber-Open/bd-p/CucumberOS
|
148
|
+
- Slack: [register for an account](https://cucumberbdd-slack-invite.herokuapp.com/) then head over to [#intro](https://cucumberbdd.slack.com/messages/C5WD8SA21/)
|
36
149
|
|
37
150
|
## Copyright
|
38
151
|
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
9.2.0
|
@@ -9,7 +9,9 @@ require 'cucumber'
|
|
9
9
|
module Cucumber
|
10
10
|
module Cli
|
11
11
|
class YmlLoadError < StandardError; end
|
12
|
+
|
12
13
|
class ProfilesNotDefinedError < YmlLoadError; end
|
14
|
+
|
13
15
|
class ProfileNotFound < StandardError; end
|
14
16
|
|
15
17
|
class Configuration
|
@@ -17,7 +19,7 @@ module Cucumber
|
|
17
19
|
|
18
20
|
attr_reader :out_stream
|
19
21
|
|
20
|
-
def initialize(out_stream =
|
22
|
+
def initialize(out_stream = $stdout, error_stream = $stderr)
|
21
23
|
@out_stream = out_stream
|
22
24
|
@error_stream = error_stream
|
23
25
|
@options = Options.new(@out_stream, @error_stream, default_profile: 'default')
|
@@ -28,6 +30,7 @@ module Cucumber
|
|
28
30
|
@options.parse!(args)
|
29
31
|
arrange_formats
|
30
32
|
raise("You can't use both --strict and --wip") if strict.strict? && wip?
|
33
|
+
|
31
34
|
set_environment_variables
|
32
35
|
end
|
33
36
|
|
@@ -126,13 +129,35 @@ module Cucumber
|
|
126
129
|
end
|
127
130
|
|
128
131
|
def arrange_formats
|
129
|
-
|
132
|
+
add_default_formatter if needs_default_formatter?
|
133
|
+
|
130
134
|
@options[:formats] = @options[:formats].sort_by do |f|
|
131
135
|
f[2] == @out_stream ? -1 : 1
|
132
136
|
end
|
133
137
|
@options[:formats].uniq!
|
134
138
|
@options.check_formatter_stream_conflicts
|
135
139
|
end
|
140
|
+
|
141
|
+
def add_default_formatter
|
142
|
+
@options[:formats] << ['pretty', {}, @out_stream]
|
143
|
+
end
|
144
|
+
|
145
|
+
def needs_default_formatter?
|
146
|
+
formatter_missing? || publish_only?
|
147
|
+
end
|
148
|
+
|
149
|
+
def formatter_missing?
|
150
|
+
@options[:formats].empty?
|
151
|
+
end
|
152
|
+
|
153
|
+
def publish_only?
|
154
|
+
@options[:formats]
|
155
|
+
.uniq
|
156
|
+
.map { |formatter, _, stream| [formatter, stream] }
|
157
|
+
.uniq
|
158
|
+
.reject { |formatter, stream| formatter == 'message' && stream != @out_stream }
|
159
|
+
.empty?
|
160
|
+
end
|
136
161
|
end
|
137
162
|
end
|
138
163
|
end
|
data/lib/cucumber/cli/main.rb
CHANGED
@@ -14,7 +14,7 @@ module Cucumber
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
def initialize(args,
|
17
|
+
def initialize(args, out = $stdout, err = $stderr, kernel = Kernel)
|
18
18
|
@args = args
|
19
19
|
@out = out
|
20
20
|
@err = err
|
@@ -41,7 +41,7 @@ module Cucumber
|
|
41
41
|
@err.puts("Couldn't open #{e.path}")
|
42
42
|
exit_unable_to_finish
|
43
43
|
rescue FeatureFolderNotFoundException => e
|
44
|
-
@err.puts(e.message
|
44
|
+
@err.puts("#{e.message}. You can use `cucumber --init` to get started.")
|
45
45
|
exit_unable_to_finish
|
46
46
|
rescue ProfilesNotDefinedError, YmlLoadError, ProfileNotFound => e
|
47
47
|
@err.puts(e.message)
|
@@ -49,7 +49,7 @@ module Cucumber
|
|
49
49
|
rescue Errno::EACCES, Errno::ENOENT => e
|
50
50
|
@err.puts("#{e.message} (#{e.class})")
|
51
51
|
exit_unable_to_finish
|
52
|
-
rescue Exception => e
|
52
|
+
rescue Exception => e
|
53
53
|
@err.puts("#{e.message} (#{e.class})")
|
54
54
|
@err.puts(e.backtrace.join("\n"))
|
55
55
|
exit_unable_to_finish
|
@@ -85,13 +85,14 @@ module Cucumber
|
|
85
85
|
trap('INT') do
|
86
86
|
exit_unable_to_finish! if Cucumber.wants_to_quit
|
87
87
|
Cucumber.wants_to_quit = true
|
88
|
-
|
88
|
+
$stderr.puts "\nExiting... Interrupt again to exit immediately."
|
89
89
|
exit_unable_to_finish
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
93
|
def runtime(existing_runtime)
|
94
94
|
return Runtime.new(configuration) unless existing_runtime
|
95
|
+
|
95
96
|
existing_runtime.configure(configuration)
|
96
97
|
existing_runtime
|
97
98
|
end
|
data/lib/cucumber/cli/options.rb
CHANGED
@@ -9,23 +9,29 @@ require 'cucumber/core/test/result'
|
|
9
9
|
module Cucumber
|
10
10
|
module Cli
|
11
11
|
class Options
|
12
|
+
CUCUMBER_PUBLISH_URL = ENV['CUCUMBER_PUBLISH_URL'] || 'https://messages.cucumber.io/api/reports -X GET'
|
12
13
|
INDENT = ' ' * 53
|
13
14
|
BUILTIN_FORMATS = {
|
14
|
-
'pretty'
|
15
|
-
'progress'
|
16
|
-
'rerun'
|
17
|
-
'usage'
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
'stepdefs'
|
23
|
-
|
24
|
-
'junit'
|
25
|
-
|
26
|
-
'
|
27
|
-
|
28
|
-
|
15
|
+
'pretty' => ['Cucumber::Formatter::Pretty', 'Prints the feature as is - in colours.'],
|
16
|
+
'progress' => ['Cucumber::Formatter::Progress', 'Prints one character per scenario.'],
|
17
|
+
'rerun' => ['Cucumber::Formatter::Rerun', 'Prints failing files with line numbers.'],
|
18
|
+
'usage' => ['Cucumber::Formatter::Usage', "Prints where step definitions are used.\n" \
|
19
|
+
"#{INDENT}The slowest step definitions (with duration) are\n" \
|
20
|
+
"#{INDENT}listed first. If --dry-run is used the duration\n" \
|
21
|
+
"#{INDENT}is not shown, and step definitions are sorted by\n" \
|
22
|
+
"#{INDENT}filename instead."],
|
23
|
+
'stepdefs' => ['Cucumber::Formatter::Stepdefs', "Prints All step definitions with their locations. Same as\n" \
|
24
|
+
"#{INDENT}the usage formatter, except that steps are not printed."],
|
25
|
+
'junit' => ['Cucumber::Formatter::Junit', "Generates a report similar to Ant+JUnit. Use\n" \
|
26
|
+
"#{INDENT}junit,fileattribute=true to include a file attribute."],
|
27
|
+
'json' => ['Cucumber::Formatter::Json', "Prints the feature as JSON.\n" \
|
28
|
+
"#{INDENT}The JSON format is in maintenance mode.\n" \
|
29
|
+
"#{INDENT}Please consider using the message formatter\n"\
|
30
|
+
"#{INDENT}with the standalone json-formatter\n" \
|
31
|
+
"#{INDENT}(https://github.com/cucumber/cucumber/tree/master/json-formatter)."],
|
32
|
+
'message' => ['Cucumber::Formatter::Message', 'Prints each message in NDJSON form, which can then be consumed by other tools.'],
|
33
|
+
'html' => ['Cucumber::Formatter::HTML', 'Outputs HTML report'],
|
34
|
+
'summary' => ['Cucumber::Formatter::Summary', 'Summary output of feature and scenarios']
|
29
35
|
}.freeze
|
30
36
|
max = BUILTIN_FORMATS.keys.map(&:length).max
|
31
37
|
FORMAT_HELP_MSG = [
|
@@ -44,26 +50,27 @@ module Cucumber
|
|
44
50
|
FORMAT_HELP = (BUILTIN_FORMATS.keys.sort.map do |key|
|
45
51
|
" #{key}#{' ' * (max - key.length)} : #{BUILTIN_FORMATS[key][1]}"
|
46
52
|
end) + FORMAT_HELP_MSG
|
47
|
-
PROFILE_SHORT_FLAG = '-p'
|
48
|
-
NO_PROFILE_SHORT_FLAG = '-P'
|
49
|
-
PROFILE_LONG_FLAG = '--profile'
|
50
|
-
NO_PROFILE_LONG_FLAG = '--no-profile'
|
51
|
-
FAIL_FAST_FLAG = '--fail-fast'
|
52
|
-
RETRY_FLAG = '--retry'
|
53
|
+
PROFILE_SHORT_FLAG = '-p'
|
54
|
+
NO_PROFILE_SHORT_FLAG = '-P'
|
55
|
+
PROFILE_LONG_FLAG = '--profile'
|
56
|
+
NO_PROFILE_LONG_FLAG = '--no-profile'
|
57
|
+
FAIL_FAST_FLAG = '--fail-fast'
|
58
|
+
RETRY_FLAG = '--retry'
|
59
|
+
RETRY_TOTAL_FLAG = '--retry-total'
|
53
60
|
OPTIONS_WITH_ARGS = [
|
54
61
|
'-r', '--require', '--i18n-keywords', '-f', '--format', '-o',
|
55
62
|
'--out', '-t', '--tags', '-n', '--name', '-e', '--exclude',
|
56
|
-
PROFILE_SHORT_FLAG, PROFILE_LONG_FLAG, RETRY_FLAG,
|
57
|
-
'--lines', '--port', '-I', '--snippet-type'
|
63
|
+
PROFILE_SHORT_FLAG, PROFILE_LONG_FLAG, RETRY_FLAG, RETRY_TOTAL_FLAG,
|
64
|
+
'-l', '--lines', '--port', '-I', '--snippet-type'
|
58
65
|
].freeze
|
59
66
|
ORDER_TYPES = %w[defined random].freeze
|
60
|
-
TAG_LIMIT_MATCHER = /(?<tag_name
|
67
|
+
TAG_LIMIT_MATCHER = /(?<tag_name>@\w+):(?<limit>\d+)/x.freeze
|
61
68
|
|
62
69
|
def self.parse(args, out_stream, error_stream, options = {})
|
63
70
|
new(out_stream, error_stream, options).parse!(args)
|
64
71
|
end
|
65
72
|
|
66
|
-
def initialize(out_stream =
|
73
|
+
def initialize(out_stream = $stdout, error_stream = $stderr, options = {})
|
67
74
|
@out_stream = out_stream
|
68
75
|
@error_stream = error_stream
|
69
76
|
|
@@ -85,29 +92,34 @@ module Cucumber
|
|
85
92
|
@options[key] = value
|
86
93
|
end
|
87
94
|
|
88
|
-
def parse!(args)
|
95
|
+
def parse!(args)
|
89
96
|
@args = args
|
90
97
|
@expanded_args = @args.dup
|
91
98
|
|
92
99
|
@args.extend(::OptionParser::Arguable)
|
93
100
|
|
94
|
-
@args.options do |opts|
|
101
|
+
@args.options do |opts|
|
95
102
|
opts.banner = banner
|
103
|
+
opts.on('--publish', 'Publish a report to https://reports.cucumber.io') do
|
104
|
+
set_option :publish_enabled, true
|
105
|
+
end
|
106
|
+
opts.on('--publish-quiet', 'Don\'t print information banner about publishing reports') { set_option :publish_quiet }
|
96
107
|
opts.on('-r LIBRARY|DIR', '--require LIBRARY|DIR', *require_files_msg) { |lib| require_files(lib) }
|
97
108
|
|
98
109
|
opts.on('-j DIR', '--jars DIR', 'Load all the jars under DIR') { |jars| load_jars(jars) } if Cucumber::JRUBY
|
99
110
|
|
100
111
|
opts.on("#{RETRY_FLAG} ATTEMPTS", *retry_msg) { |v| set_option :retry, v.to_i }
|
112
|
+
opts.on("#{RETRY_TOTAL_FLAG} TESTS", *retry_total_msg) { |v| set_option :retry_total, v.to_i }
|
101
113
|
opts.on('--i18n-languages', *i18n_languages_msg) { list_languages_and_exit }
|
102
114
|
opts.on('--i18n-keywords LANG', *i18n_keywords_msg) { |lang| language lang }
|
103
115
|
opts.on(FAIL_FAST_FLAG, 'Exit immediately following the first failing scenario') { set_option :fail_fast }
|
104
116
|
opts.on('-f FORMAT', '--format FORMAT', *format_msg, *FORMAT_HELP) do |v|
|
105
117
|
add_option :formats, [*parse_formats(v), @out_stream]
|
106
118
|
end
|
107
|
-
opts.on('--init', *init_msg) {
|
119
|
+
opts.on('--init', *init_msg) { initialize_project }
|
108
120
|
opts.on('-o', '--out [FILE|DIR|URL]', *out_msg) { |v| out_stream v }
|
109
|
-
opts.on('-t TAG_EXPRESSION', '--tags TAG_EXPRESSION', *tags_msg) { |v| add_tag
|
110
|
-
opts.on('-n NAME', '--name NAME', *name_msg) { |v| add_option
|
121
|
+
opts.on('-t TAG_EXPRESSION', '--tags TAG_EXPRESSION', *tags_msg) { |v| add_tag(v) }
|
122
|
+
opts.on('-n NAME', '--name NAME', *name_msg) { |v| add_option(:name_regexps, /#{v}/) }
|
111
123
|
opts.on('-e', '--exclude PATTERN', *exclude_msg) { |v| add_option :excludes, Regexp.new(v) }
|
112
124
|
opts.on(PROFILE_SHORT_FLAG, "#{PROFILE_LONG_FLAG} PROFILE", *profile_short_flag_msg) { |v| add_profile v }
|
113
125
|
opts.on(NO_PROFILE_SHORT_FLAG, NO_PROFILE_LONG_FLAG, *no_profile_short_flag_msg) { |_v| disable_profile_loading }
|
@@ -117,7 +129,7 @@ module Cucumber
|
|
117
129
|
opts.on('-s', '--no-source', "Don't print the file and line of the step definition with the steps.") { set_option :source, false }
|
118
130
|
opts.on('-i', '--no-snippets', "Don't print snippets for pending steps.") { set_option :snippets, false }
|
119
131
|
opts.on('-I', '--snippet-type TYPE', *snippet_type_msg) { |v| set_option :snippet_type, v.to_sym }
|
120
|
-
opts.on('-q', '--quiet', 'Alias for --no-snippets --no-source.') { shut_up }
|
132
|
+
opts.on('-q', '--quiet', 'Alias for --no-snippets --no-source --no-duration --publish-quiet.') { shut_up }
|
121
133
|
opts.on('--no-duration', "Don't print the duration at the end of the summary") { set_option :duration, false }
|
122
134
|
opts.on('-b', '--backtrace', 'Show full backtrace for all errors.') { Cucumber.use_full_backtrace = true }
|
123
135
|
opts.on('-S', '--[no-]strict', *strict_msg) { |setting| set_strict(setting) }
|
@@ -131,11 +143,11 @@ module Cucumber
|
|
131
143
|
opts.on('-x', '--expand', 'Expand Scenario Outline Tables in output.') { set_option :expand }
|
132
144
|
|
133
145
|
opts.on('--order TYPE[:SEED]', 'Run examples in the specified order. Available types:',
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
Specify SEED to reproduce the shuffling from a previous run.
|
138
|
-
|
146
|
+
*<<~TEXT.split("\n")) do |order|
|
147
|
+
[defined] Run scenarios in the order they were defined (default).
|
148
|
+
[random] Shuffle scenarios before running.
|
149
|
+
Specify SEED to reproduce the shuffling from a previous run.
|
150
|
+
e.g. --order random:5738
|
139
151
|
TEXT
|
140
152
|
@options[:order], @options[:seed] = *order.split(':')
|
141
153
|
raise "'#{@options[:order]}' is not a recognised order type. Please use one of #{ORDER_TYPES.join(', ')}." unless ORDER_TYPES.include?(@options[:order])
|
@@ -145,6 +157,8 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
145
157
|
opts.on_tail('-h', '--help', "You're looking at it.") { exit_ok(opts.help) }
|
146
158
|
end.parse!
|
147
159
|
|
160
|
+
process_publish_options
|
161
|
+
|
148
162
|
@args.map! { |a| "#{a}:#{@options[:lines]}" } if @options[:lines]
|
149
163
|
|
150
164
|
extract_environment_variables
|
@@ -168,6 +182,7 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
168
182
|
def check_formatter_stream_conflicts
|
169
183
|
streams = @options[:formats].uniq.map { |(_, _, stream)| stream }
|
170
184
|
return if streams == streams.uniq
|
185
|
+
|
171
186
|
raise 'All but one formatter must use --out, only one can print to each stream (or STDOUT)'
|
172
187
|
end
|
173
188
|
|
@@ -182,6 +197,19 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
182
197
|
|
183
198
|
private
|
184
199
|
|
200
|
+
def process_publish_options
|
201
|
+
@options[:publish_enabled] = true if truthy_string?(ENV['CUCUMBER_PUBLISH_ENABLED']) || ENV['CUCUMBER_PUBLISH_TOKEN']
|
202
|
+
@options[:formats] << publisher if @options[:publish_enabled]
|
203
|
+
|
204
|
+
@options[:publish_quiet] = true if truthy_string?(ENV['CUCUMBER_PUBLISH_QUIET'])
|
205
|
+
end
|
206
|
+
|
207
|
+
def truthy_string?(str)
|
208
|
+
return false if str.nil?
|
209
|
+
|
210
|
+
str !~ /^(false|no|0)$/i
|
211
|
+
end
|
212
|
+
|
185
213
|
def color_msg
|
186
214
|
[
|
187
215
|
'Whether or not to use ANSI color in the output. Cucumber decides',
|
@@ -245,6 +273,13 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
245
273
|
['Specify the number of times to retry failing tests (default: 0)']
|
246
274
|
end
|
247
275
|
|
276
|
+
def retry_total_msg
|
277
|
+
[
|
278
|
+
'The total number of failing test after which retrying of tests is suspended.',
|
279
|
+
'Example: --retry-total 10 -> Will stop retrying tests after 10 failing tests.'
|
280
|
+
]
|
281
|
+
end
|
282
|
+
|
248
283
|
def name_msg
|
249
284
|
[
|
250
285
|
'Only execute the feature elements which match part of the given name.',
|
@@ -260,15 +295,15 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
260
295
|
]
|
261
296
|
end
|
262
297
|
|
263
|
-
def parse_formats(
|
264
|
-
formatter, *formatter_options =
|
265
|
-
options_hash = Hash[formatter_options.map { |
|
298
|
+
def parse_formats(value)
|
299
|
+
formatter, *formatter_options = value.split(',')
|
300
|
+
options_hash = Hash[formatter_options.map { |string| string.split('=') }]
|
266
301
|
[formatter, options_hash]
|
267
302
|
end
|
268
303
|
|
269
|
-
def out_stream(
|
304
|
+
def out_stream(value)
|
270
305
|
@options[:formats] << ['pretty', {}, nil] if @options[:formats].empty?
|
271
|
-
@options[:formats][-1][2] =
|
306
|
+
@options[:formats][-1][2] = value
|
272
307
|
end
|
273
308
|
|
274
309
|
def tags_msg
|
@@ -316,7 +351,7 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
316
351
|
'option is specified; all loading becomes explicit.',
|
317
352
|
'Files in directories named "support" are still always',
|
318
353
|
'loaded first when their parent directories are',
|
319
|
-
'required or if the "support"
|
354
|
+
'required or if the "support" directories themselves are',
|
320
355
|
'explicitly required.',
|
321
356
|
'This option can be specified multiple times.'
|
322
357
|
]
|
@@ -340,21 +375,29 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
340
375
|
].join("\n")
|
341
376
|
end
|
342
377
|
|
343
|
-
def require_files(
|
344
|
-
@options[:require] <<
|
345
|
-
return unless Cucumber::JRUBY && File.directory?(
|
378
|
+
def require_files(filenames)
|
379
|
+
@options[:require] << filenames
|
380
|
+
return unless Cucumber::JRUBY && File.directory?(filenames)
|
381
|
+
|
346
382
|
require 'java'
|
347
|
-
$CLASSPATH <<
|
383
|
+
$CLASSPATH << filenames
|
348
384
|
end
|
349
385
|
|
350
386
|
def require_jars(jars)
|
351
|
-
Dir["#{jars}/**/*.jar"].each { |jar| require jar }
|
387
|
+
Dir["#{jars}/**/*.jar"].sort.each { |jar| require jar }
|
388
|
+
end
|
389
|
+
|
390
|
+
def publisher
|
391
|
+
url = CUCUMBER_PUBLISH_URL
|
392
|
+
url += %( -H "Authorization: Bearer #{ENV['CUCUMBER_PUBLISH_TOKEN']}") if ENV['CUCUMBER_PUBLISH_TOKEN']
|
393
|
+
['message', {}, url]
|
352
394
|
end
|
353
395
|
|
354
396
|
def language(lang)
|
355
397
|
require 'gherkin/dialect'
|
356
398
|
|
357
399
|
return indicate_invalid_language_and_exit(lang) unless ::Gherkin::DIALECTS.key?(lang)
|
400
|
+
|
358
401
|
list_keywords_and_exit(lang)
|
359
402
|
end
|
360
403
|
|
@@ -373,6 +416,7 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
373
416
|
def add_tag(value)
|
374
417
|
raise("Found tags option '#{value}'. '~@tag' is no longer supported, use 'not @tag' instead.") if value.include?('~')
|
375
418
|
raise("Found tags option '#{value}'. '@tag1,@tag2' is no longer supported, use '@tag or @tag2' instead.") if value.include?(',')
|
419
|
+
|
376
420
|
@options[:tag_expressions] << value.gsub(/(@\w+)(:\d+)?/, '\1')
|
377
421
|
add_tag_limits(value)
|
378
422
|
end
|
@@ -385,6 +429,7 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
385
429
|
|
386
430
|
def add_tag_limit(tag_limits, tag_name, limit)
|
387
431
|
raise "Inconsistent tag limits for #{tag_name}: #{tag_limits[tag_name]} and #{limit}" if tag_limits[tag_name] && tag_limits[tag_name] != limit
|
432
|
+
|
388
433
|
tag_limits[tag_name] = limit
|
389
434
|
end
|
390
435
|
|
@@ -396,8 +441,8 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
396
441
|
ProjectInitializer.new.run && Kernel.exit(0)
|
397
442
|
end
|
398
443
|
|
399
|
-
def add_profile(
|
400
|
-
@profiles <<
|
444
|
+
def add_profile(profile)
|
445
|
+
@profiles << profile
|
401
446
|
end
|
402
447
|
|
403
448
|
def set_option(option, value = nil)
|
@@ -410,11 +455,12 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
410
455
|
end
|
411
456
|
|
412
457
|
def exit_ok(text)
|
413
|
-
@out_stream.puts
|
458
|
+
@out_stream.puts(text)
|
414
459
|
Kernel.exit(0)
|
415
460
|
end
|
416
461
|
|
417
462
|
def shut_up
|
463
|
+
@options[:publish_quiet] = true
|
418
464
|
@options[:snippets] = false
|
419
465
|
@options[:source] = false
|
420
466
|
@options[:duration] = false
|
@@ -437,22 +483,11 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
437
483
|
end
|
438
484
|
end
|
439
485
|
|
440
|
-
def disable_profile_loading?
|
441
|
-
@disable_profile_loading
|
442
|
-
end
|
443
|
-
|
444
486
|
def merge_profiles
|
445
|
-
if @disable_profile_loading
|
446
|
-
@out_stream.puts 'Disabling profiles...'
|
447
|
-
return
|
448
|
-
end
|
487
|
+
return @out_stream.puts 'Disabling profiles...' if @disable_profile_loading
|
449
488
|
|
450
489
|
@profiles << @default_profile if default_profile_should_be_used?
|
451
|
-
|
452
|
-
@profiles.each do |profile|
|
453
|
-
merge_with_profile(profile)
|
454
|
-
end
|
455
|
-
|
490
|
+
@profiles.each { |profile| merge_with_profile(profile) }
|
456
491
|
@options[:profiles] = @profiles
|
457
492
|
end
|
458
493
|
|
@@ -476,7 +511,7 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
476
511
|
@profile_loader ||= ProfileLoader.new
|
477
512
|
end
|
478
513
|
|
479
|
-
def reverse_merge(other_options)
|
514
|
+
def reverse_merge(other_options)
|
480
515
|
@options = other_options.options.merge(@options)
|
481
516
|
@options[:require] += other_options[:require]
|
482
517
|
@options[:excludes] += other_options[:excludes]
|
@@ -506,6 +541,7 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
506
541
|
end
|
507
542
|
|
508
543
|
@options[:retry] = other_options[:retry] if @options[:retry].zero?
|
544
|
+
@options[:retry_total] = other_options[:retry_total] if @options[:retry_total].infinite?
|
509
545
|
|
510
546
|
self
|
511
547
|
end
|
@@ -579,7 +615,8 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
579
615
|
snippets: true,
|
580
616
|
source: true,
|
581
617
|
duration: true,
|
582
|
-
retry: 0
|
618
|
+
retry: 0,
|
619
|
+
retry_total: Float::INFINITY
|
583
620
|
}
|
584
621
|
end
|
585
622
|
end
|