cucumber 2.99.0 → 3.0.0

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.
Files changed (395) hide show
  1. checksums.yaml +4 -4
  2. data/.cucumberproignore +6 -0
  3. data/.gitattributes +32 -0
  4. data/.github/ISSUE_TEMPLATE.md +1 -1
  5. data/.github/PULL_REQUEST_TEMPLATE.md +1 -0
  6. data/.rubocop.yml +13 -0
  7. data/.rubocop_todo.yml +991 -0
  8. data/.travis.yml +56 -10
  9. data/{History.md → CHANGELOG.md} +166 -32
  10. data/CONTRIBUTING.md +21 -1
  11. data/Gemfile +18 -5
  12. data/README.md +13 -4
  13. data/Rakefile +12 -2
  14. data/appveyor.yml +27 -0
  15. data/bin/cucumber +2 -1
  16. data/cucumber.gemspec +15 -10
  17. data/cucumber.yml +10 -11
  18. data/examples/i18n/Rakefile +1 -1
  19. data/examples/i18n/ar/features/step_definitions/calculator_steps.rb +8 -3
  20. data/examples/i18n/ar/lib/calculator.rb +2 -2
  21. data/examples/i18n/bg/features/step_definitions/calculator_steps.rb +6 -6
  22. data/examples/i18n/bg/features/support/env.rb +6 -1
  23. data/examples/i18n/bg/lib/calculator.rb +2 -1
  24. data/examples/i18n/ca/features/step_definitions/calculator_steps.rb +10 -5
  25. data/examples/i18n/ca/lib/calculadora.rb +2 -2
  26. data/examples/i18n/cs/features/step_definitions/calculator_steps.rb +10 -5
  27. data/examples/i18n/cs/lib/calculator.rb +3 -3
  28. data/examples/i18n/da/features/step_definitions/lommeregner_steps.rb +10 -5
  29. data/examples/i18n/da/lib/lommeregner.rb +2 -2
  30. data/examples/i18n/de/features/step_definitions/calculator_steps.rb +10 -5
  31. data/examples/i18n/de/lib/calculator.rb +2 -2
  32. data/examples/i18n/el/features/step_definitions/calculator_steps.rb +10 -5
  33. data/examples/i18n/el/lib/calculator.rb +2 -2
  34. data/examples/i18n/en/features/step_definitions/calculator_steps.rb +9 -4
  35. data/examples/i18n/en/lib/calculator.rb +2 -2
  36. data/examples/i18n/en-lol/features/step_definitions/cucumbrz_steps.rb +4 -4
  37. data/examples/i18n/en-lol/features/support/env.rb +6 -2
  38. data/examples/i18n/en-lol/lib/basket.rb +2 -2
  39. data/examples/i18n/en-lol/lib/belly.rb +2 -2
  40. data/examples/i18n/eo/features/step_definitions/calculator_steps.rb +9 -4
  41. data/examples/i18n/eo/lib/calculator.rb +2 -2
  42. data/examples/i18n/es/features/step_definitions/calculador_steps.rb +10 -5
  43. data/examples/i18n/es/lib/calculador.rb +2 -2
  44. data/examples/i18n/et/features/step_definitions/kalkulaator_steps.rb +10 -5
  45. data/examples/i18n/et/lib/kalkulaator.rb +3 -3
  46. data/examples/i18n/fi/features/step_definitions/laskin_steps.rb +9 -4
  47. data/examples/i18n/fi/lib/laskin.rb +1 -1
  48. data/examples/i18n/fr/features/step_definitions/calculatrice_steps.rb +8 -8
  49. data/examples/i18n/fr/features/support/env.rb +7 -2
  50. data/examples/i18n/fr/lib/calculatrice.rb +2 -2
  51. data/examples/i18n/he/features/step_definitions/calculator_steps.rb +10 -5
  52. data/examples/i18n/he/lib/calculator.rb +1 -1
  53. data/examples/i18n/hi/features/step_definitions/calculator_steps.rb +9 -4
  54. data/examples/i18n/hi/lib/calculator.rb +1 -1
  55. data/examples/i18n/ht/features/step_definitions/kalkilatris_steps.rb +9 -5
  56. data/examples/i18n/ht/lib/kalkilatris.rb +2 -2
  57. data/examples/i18n/hu/features/step_definitions/calculator_steps.rb +10 -5
  58. data/examples/i18n/hu/lib/calculator.rb +1 -1
  59. data/examples/i18n/id/features/step_definitions/calculator_steps.rb +10 -5
  60. data/examples/i18n/id/lib/calculator.rb +2 -2
  61. data/examples/i18n/it/features/step_definitions/calcolatrice_steps.rb +10 -5
  62. data/examples/i18n/it/lib/calcolatrice.rb +3 -3
  63. data/examples/i18n/ja/features/step_definitions/calculator_steps.rb +3 -3
  64. data/examples/i18n/ja/features/support/env.rb +7 -2
  65. data/examples/i18n/ja/lib/calculator.rb +2 -2
  66. data/examples/i18n/ko/features/step_definitions/calculator_steps.rb +10 -5
  67. data/examples/i18n/ko/lib/calculator.rb +2 -2
  68. data/examples/i18n/lt/features/step_definitions/calculator_steps.rb +10 -5
  69. data/examples/i18n/lt/lib/calculator.rb +2 -2
  70. data/examples/i18n/lv/features/step_definitions/calculator_steps.rb +10 -5
  71. data/examples/i18n/lv/lib/calculator.rb +2 -2
  72. data/examples/i18n/no/features/step_definitions/kalkulator_steps.rb +3 -3
  73. data/examples/i18n/no/features/support/env.rb +7 -2
  74. data/examples/i18n/no/lib/kalkulator.rb +2 -2
  75. data/examples/i18n/pl/features/step_definitions/calculator_steps.rb +10 -5
  76. data/examples/i18n/pl/features/support/env.rb +6 -1
  77. data/examples/i18n/pl/lib/calculator.rb +2 -2
  78. data/examples/i18n/pt/features/step_definitions/calculadora_steps.rb +3 -3
  79. data/examples/i18n/pt/features/support/env.rb +7 -2
  80. data/examples/i18n/pt/lib/calculadora.rb +1 -1
  81. data/examples/i18n/ro/features/step_definitions/calculator_steps.rb +11 -6
  82. data/examples/i18n/ro/lib/calculator.rb +1 -1
  83. data/examples/i18n/ru/features/step_definitions/calculator_steps.rb +5 -5
  84. data/examples/i18n/ru/features/support/env.rb +6 -1
  85. data/examples/i18n/ru/lib/calculator.rb +2 -1
  86. data/examples/i18n/sk/features/step_definitions/calculator_steps.rb +10 -5
  87. data/examples/i18n/sk/lib/calculator.rb +2 -2
  88. data/examples/i18n/sr-Cyrl/features/step_definitions/calculator_steps.rb +3 -3
  89. data/examples/i18n/sr-Cyrl/features/support/env.rb +7 -2
  90. data/examples/i18n/sr-Cyrl/lib/calculator.rb +2 -2
  91. data/examples/i18n/sr-Latn/features/step_definitions/calculator_steps.rb +10 -5
  92. data/examples/i18n/sr-Latn/lib/calculator.rb +2 -2
  93. data/examples/i18n/sv/features/step_definitions/kalkulator_steps.rb +9 -4
  94. data/examples/i18n/sv/lib/kalkulator.rb +2 -2
  95. data/examples/i18n/tr/features/step_definitions/hesap_makinesi_adimlari.rb +10 -5
  96. data/examples/i18n/tr/lib/hesap_makinesi.rb +2 -2
  97. data/examples/i18n/uk/features/step_definitions/calculator_steps.rb +4 -4
  98. data/examples/i18n/uk/features/support/env.rb +6 -1
  99. data/examples/i18n/uk/lib/calculator.rb +2 -1
  100. data/examples/i18n/uz/features/step_definitions/calculator_steps.rb +4 -4
  101. data/examples/i18n/uz/features/support/env.rb +6 -1
  102. data/examples/i18n/uz/lib/calculator.rb +2 -1
  103. data/examples/i18n/zh-CN/features/step_definitions/calculator_steps.rb +11 -6
  104. data/examples/i18n/zh-CN/lib/calculator.rb +2 -2
  105. data/examples/i18n/zh-TW/features/step_definitions/calculator_steps.rb +10 -5
  106. data/examples/i18n/zh-TW/lib/calculator.rb +2 -2
  107. data/examples/rspec_doubles/features/step_definitions/calvin_steps.rb +3 -3
  108. data/examples/sinatra/features/step_definitions/add_steps.rb +4 -4
  109. data/examples/sinatra/features/support/env.rb +6 -1
  110. data/examples/tcl/features/step_definitions/fib_steps.rb +2 -2
  111. data/examples/tcl/features/support/env.rb +2 -1
  112. data/examples/test_unit/features/step_definitions/test_unit_steps.rb +4 -4
  113. data/examples/watir/features/step_definitions/search_steps.rb +6 -5
  114. data/examples/watir/features/support/env.rb +5 -1
  115. data/examples/watir/features/support/screenshots.rb +5 -5
  116. data/features/docs/api/list_step_defs_as_json.feature +21 -7
  117. data/features/docs/api/listen_for_events.feature +9 -8
  118. data/features/docs/api/run_cli_main_with_existing_runtime.feature +1 -0
  119. data/features/docs/cli/dry_run.feature +3 -0
  120. data/features/docs/cli/execute_with_tag_filter.feature +8 -6
  121. data/features/docs/cli/fail_fast.feature +2 -0
  122. data/features/docs/cli/help.feature +8 -0
  123. data/features/docs/{gherkin/language_help.feature → cli/i18n.feature} +6 -6
  124. data/features/docs/cli/randomize.feature +2 -2
  125. data/features/docs/cli/retry_failing_tests.feature +75 -15
  126. data/features/docs/cli/run_scenarios_matching_name.feature +1 -0
  127. data/features/docs/cli/specifying_multiple_formatters.feature +3 -0
  128. data/features/docs/cli/strict_mode.feature +6 -0
  129. data/features/docs/defining_steps/ambiguous_steps.feature +89 -0
  130. data/features/docs/defining_steps/nested_steps.feature +5 -1
  131. data/features/docs/defining_steps/nested_steps_i18n.feature +3 -0
  132. data/features/docs/defining_steps/nested_steps_with_second_arg.feature +2 -0
  133. data/features/docs/defining_steps/printing_messages.feature +1 -2
  134. data/features/docs/defining_steps/skip_scenario.feature +1 -34
  135. data/features/docs/defining_steps/snippets.feature +18 -18
  136. data/features/docs/events/gherkin_source_read_event.feature +43 -0
  137. data/features/docs/events/step_activated_event.feature +36 -0
  138. data/features/docs/events/step_definition_registered_event.feature +29 -0
  139. data/features/docs/events/test_case_finished_event.feature +35 -0
  140. data/features/docs/events/test_case_started_event.feature +54 -0
  141. data/features/docs/events/test_run_finished_event.feature +40 -0
  142. data/features/docs/events/test_run_started_event.feature +41 -0
  143. data/features/docs/events/test_step_finished_event.feature +47 -0
  144. data/features/docs/events/test_step_started_event.feature +43 -0
  145. data/features/docs/exception_in_after_hook.feature +3 -2
  146. data/features/docs/exception_in_after_step_hook.feature +2 -0
  147. data/features/docs/exception_in_around_hook.feature +2 -0
  148. data/features/docs/exception_in_before_hook.feature +3 -1
  149. data/features/docs/extending_cucumber/custom_formatter.feature +12 -48
  150. data/features/docs/formatters/json_formatter.feature +4 -4
  151. data/features/docs/formatters/junit_formatter.feature +7 -6
  152. data/features/docs/formatters/pretty_formatter.feature +1 -0
  153. data/features/docs/formatters/progress_formatter.feature +1 -0
  154. data/features/docs/formatters/rerun_formatter.feature +2 -0
  155. data/features/docs/formatters/summary_formatter.feature +35 -0
  156. data/features/docs/formatters/usage_formatter.feature +3 -0
  157. data/features/docs/getting_started.feature +2 -1
  158. data/features/docs/gherkin/background.feature +5 -5
  159. data/features/docs/gherkin/doc_strings.feature +2 -0
  160. data/features/docs/gherkin/expand_option_for_outlines.feature +1 -0
  161. data/features/docs/gherkin/outlines.feature +6 -4
  162. data/features/docs/gherkin/unicode_table.feature +2 -1
  163. data/features/docs/gherkin/using_descriptions.feature +1 -0
  164. data/features/docs/gherkin/using_star_notation.feature +1 -1
  165. data/features/docs/post_configuration_hook.feature +1 -17
  166. data/features/docs/rake_task.feature +5 -0
  167. data/features/docs/raketask.feature +3 -1
  168. data/features/docs/work_in_progress.feature +5 -2
  169. data/features/docs/writing_support_code/after_step_hooks.feature +3 -1
  170. data/features/docs/writing_support_code/around_hooks.feature +6 -0
  171. data/features/docs/writing_support_code/before_hook.feature +1 -0
  172. data/features/docs/writing_support_code/hook_order.feature +3 -1
  173. data/features/docs/writing_support_code/parameter_types.feature +53 -0
  174. data/features/docs/writing_support_code/tagged_hooks.feature +1 -1
  175. data/features/docs/writing_support_code/world.feature +129 -0
  176. data/features/lib/step_definitions/aruba_steps.rb +5 -10
  177. data/features/lib/step_definitions/cli_steps.rb +4 -0
  178. data/features/lib/step_definitions/cucumber_steps.rb +9 -12
  179. data/features/lib/step_definitions/iso-8859-1_steps.rb +9 -5
  180. data/features/lib/step_definitions/json_steps.rb +2 -1
  181. data/features/lib/step_definitions/junit_steps.rb +2 -1
  182. data/features/lib/step_definitions/language_steps.rb +3 -2
  183. data/features/lib/step_definitions/profile_steps.rb +7 -6
  184. data/features/lib/step_definitions/retry_steps.rb +33 -13
  185. data/features/lib/step_definitions/ruby_steps.rb +2 -1
  186. data/features/lib/support/env.rb +3 -2
  187. data/features/lib/support/fake_wire_server.rb +17 -3
  188. data/features/lib/support/feature_factory.rb +2 -1
  189. data/features/lib/support/normalise_output.rb +14 -12
  190. data/features/lib/support/parameter_types.rb +5 -0
  191. data/gem_tasks/contributors.rake +10 -6
  192. data/gem_tasks/cov.rake +1 -0
  193. data/gem_tasks/downloads.rb +1 -0
  194. data/gem_tasks/environment.rake +2 -5
  195. data/gem_tasks/examples.rake +3 -2
  196. data/gem_tasks/fix_cr_lf.rake +19 -7
  197. data/gem_tasks/flog.rake +4 -3
  198. data/gem_tasks/rspec.rake +2 -1
  199. data/gem_tasks/sass.rake +2 -1
  200. data/lib/autotest/cucumber.rb +1 -0
  201. data/lib/autotest/cucumber_mixin.rb +17 -16
  202. data/lib/autotest/cucumber_rails.rb +1 -0
  203. data/lib/autotest/cucumber_rails_rspec.rb +1 -0
  204. data/lib/autotest/cucumber_rails_rspec2.rb +1 -0
  205. data/lib/autotest/cucumber_rspec.rb +1 -0
  206. data/lib/autotest/cucumber_rspec2.rb +1 -0
  207. data/lib/autotest/discover.rb +3 -2
  208. data/lib/cucumber/cli/configuration.rb +12 -21
  209. data/lib/cucumber/cli/main.rb +8 -7
  210. data/lib/cucumber/cli/options.rb +341 -217
  211. data/lib/cucumber/cli/profile_loader.rb +26 -21
  212. data/lib/cucumber/cli/rerun_file.rb +1 -0
  213. data/lib/cucumber/configuration.rb +54 -37
  214. data/lib/cucumber/constantize.rb +2 -1
  215. data/lib/cucumber/core_ext/string.rb +2 -1
  216. data/lib/cucumber/deprecate.rb +4 -3
  217. data/lib/cucumber/encoding.rb +1 -0
  218. data/lib/cucumber/errors.rb +3 -1
  219. data/lib/cucumber/events/gherkin_source_read.rb +17 -0
  220. data/lib/cucumber/events/{step_match.rb → step_activated.rb} +5 -2
  221. data/lib/cucumber/events/step_definition_registered.rb +24 -0
  222. data/lib/cucumber/events/test_case_finished.rb +18 -0
  223. data/lib/cucumber/events/test_case_started.rb +15 -0
  224. data/lib/cucumber/events/test_run_finished.rb +12 -0
  225. data/lib/cucumber/events/test_run_started.rb +16 -0
  226. data/lib/cucumber/events/test_step_finished.rb +20 -0
  227. data/lib/cucumber/events/test_step_started.rb +17 -0
  228. data/lib/cucumber/events.rb +23 -3
  229. data/lib/cucumber/file_specs.rb +1 -0
  230. data/lib/cucumber/filters/activate_steps.rb +8 -3
  231. data/lib/cucumber/filters/apply_after_hooks.rb +1 -0
  232. data/lib/cucumber/filters/apply_after_step_hooks.rb +1 -0
  233. data/lib/cucumber/filters/apply_around_hooks.rb +1 -0
  234. data/lib/cucumber/filters/apply_before_hooks.rb +1 -0
  235. data/lib/cucumber/filters/broadcast_test_run_started_event.rb +27 -0
  236. data/lib/cucumber/filters/gated_receiver.rb +1 -0
  237. data/lib/cucumber/filters/prepare_world.rb +1 -0
  238. data/lib/cucumber/filters/quit.rb +1 -0
  239. data/lib/cucumber/filters/randomizer.rb +1 -0
  240. data/lib/cucumber/filters/retry.rb +4 -4
  241. data/lib/cucumber/filters/tag_limits/test_case_index.rb +1 -0
  242. data/lib/cucumber/filters/tag_limits/verifier.rb +2 -1
  243. data/lib/cucumber/filters/tag_limits.rb +4 -3
  244. data/lib/cucumber/filters.rb +3 -0
  245. data/lib/cucumber/formatter/ansicolor.rb +9 -11
  246. data/lib/cucumber/formatter/backtrace_filter.rb +21 -8
  247. data/lib/cucumber/formatter/console.rb +72 -66
  248. data/lib/cucumber/formatter/console_counts.rb +44 -0
  249. data/lib/cucumber/formatter/console_issues.rb +58 -0
  250. data/lib/cucumber/formatter/duration.rb +2 -1
  251. data/lib/cucumber/formatter/duration_extractor.rb +1 -0
  252. data/lib/cucumber/formatter/fail_fast.rb +4 -2
  253. data/lib/cucumber/formatter/fanout.rb +1 -0
  254. data/lib/cucumber/formatter/hook_query_visitor.rb +1 -0
  255. data/lib/cucumber/formatter/html.rb +175 -249
  256. data/lib/cucumber/formatter/html_builder.rb +120 -0
  257. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -0
  258. data/lib/cucumber/formatter/inline-js.js +30 -0
  259. data/lib/cucumber/formatter/interceptor.rb +3 -2
  260. data/lib/cucumber/formatter/io.rb +2 -1
  261. data/lib/cucumber/formatter/json.rb +33 -30
  262. data/lib/cucumber/formatter/json_pretty.rb +1 -0
  263. data/lib/cucumber/formatter/junit.rb +26 -24
  264. data/lib/cucumber/formatter/legacy_api/adapter.rb +81 -69
  265. data/lib/cucumber/formatter/legacy_api/ast.rb +27 -22
  266. data/lib/cucumber/formatter/legacy_api/results.rb +7 -8
  267. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +1 -0
  268. data/lib/cucumber/formatter/pretty.rb +24 -18
  269. data/lib/cucumber/formatter/progress.rb +55 -13
  270. data/lib/cucumber/formatter/rerun.rb +15 -18
  271. data/lib/cucumber/formatter/stepdefs.rb +1 -0
  272. data/lib/cucumber/formatter/steps.rb +2 -1
  273. data/lib/cucumber/formatter/summary.rb +43 -20
  274. data/lib/cucumber/formatter/unicode.rb +2 -1
  275. data/lib/cucumber/formatter/usage.rb +33 -28
  276. data/lib/cucumber/gherkin/data_table_parser.rb +1 -0
  277. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +2 -1
  278. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -1
  279. data/lib/cucumber/gherkin/i18n.rb +1 -0
  280. data/lib/cucumber/gherkin/steps_parser.rb +1 -0
  281. data/lib/cucumber/{rb_support/rb_dsl.rb → glue/dsl.rb} +41 -30
  282. data/lib/cucumber/glue/hook.rb +43 -0
  283. data/lib/cucumber/glue/invoke_in_world.rb +70 -0
  284. data/lib/cucumber/glue/proto_world.rb +220 -0
  285. data/lib/cucumber/{rb_support/rb_language.rb → glue/registry_and_more.rb} +60 -90
  286. data/lib/cucumber/{rb_support → glue}/snippet.rb +64 -26
  287. data/lib/cucumber/glue/step_definition.rb +143 -0
  288. data/lib/cucumber/glue/world_factory.rb +23 -0
  289. data/lib/cucumber/hooks.rb +9 -8
  290. data/lib/cucumber/load_path.rb +1 -0
  291. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +158 -0
  292. data/lib/cucumber/multiline_argument/data_table.rb +47 -181
  293. data/lib/cucumber/multiline_argument/doc_string.rb +1 -0
  294. data/lib/cucumber/multiline_argument.rb +6 -15
  295. data/lib/cucumber/platform.rb +4 -5
  296. data/lib/cucumber/project_initializer.rb +3 -2
  297. data/lib/cucumber/rake/task.rb +19 -14
  298. data/lib/cucumber/rspec/disable_option_parser.rb +2 -1
  299. data/lib/cucumber/rspec/doubles.rb +2 -1
  300. data/lib/cucumber/running_test_case.rb +3 -27
  301. data/lib/cucumber/runtime/after_hooks.rb +2 -1
  302. data/lib/cucumber/runtime/before_hooks.rb +1 -0
  303. data/lib/cucumber/runtime/for_programming_languages.rb +2 -36
  304. data/lib/cucumber/runtime/step_hooks.rb +1 -0
  305. data/lib/cucumber/runtime/support_code.rb +21 -16
  306. data/lib/cucumber/runtime/user_interface.rb +10 -16
  307. data/lib/cucumber/runtime.rb +49 -32
  308. data/lib/cucumber/step_argument.rb +1 -2
  309. data/lib/cucumber/step_definition_light.rb +2 -0
  310. data/lib/cucumber/step_definitions.rb +1 -0
  311. data/lib/cucumber/step_match.rb +30 -15
  312. data/lib/cucumber/step_match_search.rb +3 -3
  313. data/lib/cucumber/term/ansicolor.rb +2 -1
  314. data/lib/cucumber/unit.rb +1 -0
  315. data/lib/cucumber/version +1 -1
  316. data/lib/cucumber.rb +1 -13
  317. data/lib/simplecov_setup.rb +2 -1
  318. data/scripts/invite-collaborator +40 -0
  319. data/scripts/update-changelog +86 -0
  320. data/spec/cucumber/cli/configuration_spec.rb +109 -98
  321. data/spec/cucumber/cli/main_spec.rb +8 -7
  322. data/spec/cucumber/cli/options_spec.rb +114 -79
  323. data/spec/cucumber/cli/profile_loader_spec.rb +25 -5
  324. data/spec/cucumber/cli/rerun_spec.rb +21 -20
  325. data/spec/cucumber/configuration_spec.rb +48 -47
  326. data/spec/cucumber/constantize_spec.rb +3 -2
  327. data/spec/cucumber/events_spec.rb +9 -0
  328. data/spec/cucumber/file_specs_spec.rb +26 -25
  329. data/spec/cucumber/filters/activate_steps_spec.rb +25 -22
  330. data/spec/cucumber/filters/gated_receiver_spec.rb +7 -6
  331. data/spec/cucumber/filters/retry_spec.rb +42 -23
  332. data/spec/cucumber/filters/tag_limits/test_case_index_spec.rb +12 -11
  333. data/spec/cucumber/filters/tag_limits/verifier_spec.rb +15 -14
  334. data/spec/cucumber/filters/tag_limits_spec.rb +9 -8
  335. data/spec/cucumber/formatter/ansicolor_spec.rb +10 -9
  336. data/spec/cucumber/formatter/backtrace_filter_spec.rb +32 -0
  337. data/spec/cucumber/formatter/console_counts_spec.rb +14 -0
  338. data/spec/cucumber/formatter/duration_spec.rb +4 -3
  339. data/spec/cucumber/formatter/fail_fast_spec.rb +27 -27
  340. data/spec/cucumber/formatter/html_spec.rb +65 -60
  341. data/spec/cucumber/formatter/interceptor_spec.rb +1 -0
  342. data/spec/cucumber/formatter/json_spec.rb +71 -97
  343. data/spec/cucumber/formatter/junit_spec.rb +46 -47
  344. data/spec/cucumber/formatter/legacy_api/adapter_spec.rb +134 -141
  345. data/spec/cucumber/formatter/pretty_spec.rb +105 -117
  346. data/spec/cucumber/formatter/progress_spec.rb +22 -21
  347. data/spec/cucumber/formatter/rerun_spec.rb +70 -64
  348. data/spec/cucumber/formatter/spec_helper.rb +14 -12
  349. data/spec/cucumber/{rb_support/rb_world_spec.rb → glue/proto_world_spec.rb} +22 -6
  350. data/spec/cucumber/glue/registry_and_more_spec.rb +213 -0
  351. data/spec/cucumber/{rb_support → glue}/snippet_spec.rb +62 -25
  352. data/spec/cucumber/glue/step_definition_spec.rb +207 -0
  353. data/spec/cucumber/hooks_spec.rb +5 -4
  354. data/spec/cucumber/multiline_argument/data_table_spec.rb +179 -129
  355. data/spec/cucumber/project_initializer_spec.rb +12 -11
  356. data/spec/cucumber/rake/forked_spec.rb +22 -17
  357. data/spec/cucumber/rake/task_spec.rb +21 -20
  358. data/spec/cucumber/running_test_case_spec.rb +36 -35
  359. data/spec/cucumber/runtime/for_programming_languages_spec.rb +2 -30
  360. data/spec/cucumber/runtime/support_code_spec.rb +1 -0
  361. data/spec/cucumber/runtime_spec.rb +3 -2
  362. data/spec/cucumber/step_argument_spec.rb +6 -5
  363. data/spec/cucumber/step_match_search_spec.rb +32 -41
  364. data/spec/cucumber/step_match_spec.rb +34 -33
  365. data/spec/cucumber/world/pending_spec.rb +12 -11
  366. data/spec/spec_helper.rb +1 -0
  367. data/spec/support/standard_step_actions.rb +2 -1
  368. metadata +132 -61
  369. data/features/docs/formatters/debug_formatter.feature +0 -47
  370. data/features/docs/formatters/formatter_step_file_colon_line.feature +0 -44
  371. data/features/docs/writing_support_code/transforms.feature +0 -98
  372. data/features/lib/step_definitions/wire_steps.rb +0 -58
  373. data/gem_tasks/cucumber.rake +0 -25
  374. data/lib/cucumber/ast.rb +0 -13
  375. data/lib/cucumber/core_ext/instance_exec.rb +0 -70
  376. data/lib/cucumber/events/after_test_case.rb +0 -25
  377. data/lib/cucumber/events/after_test_step.rb +0 -30
  378. data/lib/cucumber/events/before_test_case.rb +0 -18
  379. data/lib/cucumber/events/before_test_step.rb +0 -23
  380. data/lib/cucumber/events/bus.rb +0 -86
  381. data/lib/cucumber/events/finished_testing.rb +0 -9
  382. data/lib/cucumber/formatter/debug.rb +0 -35
  383. data/lib/cucumber/formatter/event_bus_report.rb +0 -38
  384. data/lib/cucumber/rb_support/rb_hook.rb +0 -19
  385. data/lib/cucumber/rb_support/rb_step_definition.rb +0 -131
  386. data/lib/cucumber/rb_support/rb_transform.rb +0 -59
  387. data/lib/cucumber/rb_support/rb_world.rb +0 -149
  388. data/spec/cucumber/core_ext/instance_exec_spec.rb +0 -4
  389. data/spec/cucumber/events/bus_spec.rb +0 -94
  390. data/spec/cucumber/formatter/debug_spec.rb +0 -64
  391. data/spec/cucumber/formatter/event_bus_report_spec.rb +0 -88
  392. data/spec/cucumber/rb_support/rb_language_spec.rb +0 -243
  393. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +0 -199
  394. data/spec/cucumber/rb_support/rb_transform_spec.rb +0 -46
  395. data/spec/cucumber_spec.rb +0 -39
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
1
2
  require 'cucumber/cli/profile_loader'
2
3
  require 'cucumber/formatter/ansicolor'
3
- require 'cucumber/rb_support/rb_language'
4
+ require 'cucumber/glue/registry_and_more'
4
5
  require 'cucumber/project_initializer'
6
+ require 'cucumber/core/test/result'
5
7
 
6
8
  module Cucumber
7
9
  module Cli
@@ -23,34 +25,39 @@ module Cucumber
23
25
  'junit' => ['Cucumber::Formatter::Junit', 'Generates a report similar to Ant+JUnit.'],
24
26
  'json' => ['Cucumber::Formatter::Json', 'Prints the feature as JSON'],
25
27
  'json_pretty' => ['Cucumber::Formatter::JsonPretty', 'Prints the feature as prettified JSON'],
26
- 'debug' => ['Cucumber::Formatter::Debug', 'For developing formatters - prints the calls made to the listeners.']
28
+ 'summary' => ['Cucumber::Formatter::Summary', 'Summary output of feature and scenarios']
27
29
  }
28
30
  max = BUILTIN_FORMATS.keys.map{|s| s.length}.max
31
+ FORMAT_HELP_MSG = [
32
+ 'Use --format rerun --out rerun.txt to write out failing',
33
+ 'features. You can rerun them with cucumber @rerun.txt.',
34
+ 'FORMAT can also be the fully qualified class name of',
35
+ "your own custom formatter. If the class isn't loaded,",
36
+ 'Cucumber will attempt to require a file with a relative',
37
+ 'file name that is the underscore name of the class name.',
38
+ 'Example: --format Foo::BarZap -> Cucumber will look for',
39
+ 'foo/bar_zap.rb. You can place the file with this relative',
40
+ 'path underneath your features/support directory or anywhere',
41
+ "on Ruby's LOAD_PATH, for example in a Ruby gem."
42
+ ]
43
+
29
44
  FORMAT_HELP = (BUILTIN_FORMATS.keys.sort.map do |key|
30
45
  " #{key}#{' ' * (max - key.length)} : #{BUILTIN_FORMATS[key][1]}"
31
- end) + ["Use --format rerun --out rerun.txt to write out failing",
32
- "features. You can rerun them with cucumber @rerun.txt.",
33
- "FORMAT can also be the fully qualified class name of",
34
- "your own custom formatter. If the class isn't loaded,",
35
- "Cucumber will attempt to require a file with a relative",
36
- "file name that is the underscore name of the class name.",
37
- "Example: --format Foo::BarZap -> Cucumber will look for",
38
- "foo/bar_zap.rb. You can place the file with this relative",
39
- "path underneath your features/support directory or anywhere",
40
- "on Ruby's LOAD_PATH, for example in a Ruby gem."
41
- ]
46
+ end) + FORMAT_HELP_MSG
42
47
  PROFILE_SHORT_FLAG = '-p'
43
48
  NO_PROFILE_SHORT_FLAG = '-P'
44
49
  PROFILE_LONG_FLAG = '--profile'
45
50
  NO_PROFILE_LONG_FLAG = '--no-profile'
46
51
  FAIL_FAST_FLAG = '--fail-fast'
47
52
  RETRY_FLAG = '--retry'
48
- OPTIONS_WITH_ARGS = ['-r', '--require', '--i18n', '-f', '--format', '-o', '--out',
49
- '-t', '--tags', '-n', '--name', '-e', '--exclude',
50
- PROFILE_SHORT_FLAG, PROFILE_LONG_FLAG, RETRY_FLAG,
51
- '-l', '--lines', '--port',
52
- '-I', '--snippet-type']
53
+ OPTIONS_WITH_ARGS = [
54
+ '-r', '--require', '--i18n-keywords', '-f', '--format', '-o',
55
+ '--out', '-t', '--tags', '-n', '--name', '-e', '--exclude',
56
+ PROFILE_SHORT_FLAG, PROFILE_LONG_FLAG, RETRY_FLAG, '-l',
57
+ '--lines', '--port', '-I', '--snippet-type'
58
+ ]
53
59
  ORDER_TYPES = %w{defined random}
60
+ TAG_LIMIT_MATCHER = /(?<tag_name>\@\w+):(?<limit>\d+)/x
54
61
 
55
62
  def self.parse(args, out_stream, error_stream, options = {})
56
63
  new(out_stream, error_stream, options).parse!(args)
@@ -78,196 +85,68 @@ module Cucumber
78
85
  @options[key] = value
79
86
  end
80
87
 
81
- def parse!(args)
88
+ def parse!(args) # rubocop:disable Metrics/AbcSize
82
89
  @args = args
83
90
  @expanded_args = @args.dup
84
91
 
85
92
  @args.extend(::OptionParser::Arguable)
86
93
 
87
94
  @args.options do |opts|
88
- opts.banner = ["Usage: cucumber [options] [ [FILE|DIR|URL][:LINE[:LINE]*] ]+", "",
89
- "Examples:",
90
- "cucumber examples/i18n/en/features",
91
- "cucumber @rerun.txt (See --format rerun)",
92
- "cucumber examples/i18n/it/features/somma.feature:6:98:113",
93
- "cucumber -s -i http://rubyurl.com/eeCl", "", "",
94
- ].join("\n")
95
- opts.on("-r LIBRARY|DIR", "--require LIBRARY|DIR",
96
- "Require files before executing the features. If this",
97
- "option is not specified, all *.rb files that are",
98
- "siblings or below the features will be loaded auto-",
99
- "matically. Automatic loading is disabled when this",
100
- "option is specified, and all loading becomes explicit.",
101
- "Files under directories named \"support\" are always",
102
- "loaded first.",
103
- "This option can be specified multiple times.") do |v|
104
- @options[:require] << v
105
- if(Cucumber::JRUBY && File.directory?(v))
106
- require 'java'
107
- $CLASSPATH << v
108
- end
109
- end
95
+ opts.banner = banner
96
+ opts.on('-r LIBRARY|DIR', '--require LIBRARY|DIR', *require_files_msg) {|lib| require_files(lib) }
110
97
 
111
98
  if(Cucumber::JRUBY)
112
- opts.on("-j DIR", "--jars DIR",
113
- "Load all the jars under DIR") do |jars|
114
- Dir["#{jars}/**/*.jar"].each {|jar| require jar}
115
- end
116
- end
117
-
118
- opts.on("--i18n LANG",
119
- "List keywords for in a particular language",
120
- %{Run with "--i18n help" to see all languages}) do |lang|
121
- require 'gherkin/dialect'
122
-
123
- if lang == 'help'
124
- list_languages_and_exit
125
- elsif !::Gherkin::DIALECTS.keys.include? lang
126
- indicate_invalid_language_and_exit(lang)
127
- else
128
- list_keywords_and_exit(lang)
129
- end
130
- end
131
- opts.on(FAIL_FAST_FLAG, "Exit immediately following the first failing scenario") do |v|
132
- options[:fail_fast] = true
133
- end
134
- opts.on("-f FORMAT", "--format FORMAT",
135
- "How to format features (Default: pretty). Available formats:",
136
- *FORMAT_HELP) do |v|
137
- @options[:formats] << [v, @out_stream]
138
- end
139
- opts.on('--init',
140
- 'Initializes folder structure and generates conventional files for',
141
- 'a Cucumber project.') do |v|
142
- ProjectInitializer.new.run
143
- Kernel.exit(0)
144
- end
145
- opts.on("-o", "--out [FILE|DIR]",
146
- "Write output to a file/directory instead of STDOUT. This option",
147
- "applies to the previously specified --format, or the",
148
- "default format if no format is specified. Check the specific",
149
- "formatter's docs to see whether to pass a file or a dir.") do |v|
150
- @options[:formats] << ['pretty', nil] if @options[:formats].empty?
151
- @options[:formats][-1][1] = v
152
- end
153
- opts.on("-t TAG_EXPRESSION", "--tags TAG_EXPRESSION",
154
- "Only execute the features or scenarios with tags matching TAG_EXPRESSION.",
155
- "Scenarios inherit tags declared on the Feature level. The simplest",
156
- "TAG_EXPRESSION is simply a tag. Example: --tags @dev. When a tag in a tag",
157
- "expression starts with a ~, this represents boolean NOT. Example: --tags ~@dev.",
158
- "A tag expression can have several tags separated by a comma, which represents",
159
- "logical OR. Example: --tags @dev,@wip. The --tags option can be specified",
160
- "several times, and this represents logical AND. Example: --tags @foo,~@bar --tags @zap.",
161
- "This represents the boolean expression (@foo || !@bar) && @zap.",
162
- "\n",
163
- "Beware that if you want to use several negative tags to exclude several tags",
164
- "you have to use logical AND: --tags ~@fixme --tags ~@buggy.",
165
- "\n",
166
- "Positive tags can be given a threshold to limit the number of occurrences.",
167
- "Example: --tags @qa:3 will fail if there are more than 3 occurrences of the @qa tag.",
168
- "This can be practical if you are practicing Kanban or CONWIP.") do |v|
169
- @options[:tag_expressions] << v
170
- end
171
- opts.on("-n NAME", "--name NAME",
172
- "Only execute the feature elements which match part of the given name.",
173
- "If this option is given more than once, it will match against all the",
174
- "given names.") do |v|
175
- @options[:name_regexps] << /#{v}/
176
- end
177
- opts.on("-e", "--exclude PATTERN", "Don't run feature files or require ruby files matching PATTERN") do |v|
178
- @options[:excludes] << Regexp.new(v)
179
- end
180
- opts.on(PROFILE_SHORT_FLAG, "#{PROFILE_LONG_FLAG} PROFILE",
181
- "Pull commandline arguments from cucumber.yml which can be defined as",
182
- "strings or arrays. When a 'default' profile is defined and no profile",
183
- "is specified it is always used. (Unless disabled, see -P below.)",
184
- "When feature files are defined in a profile and on the command line",
185
- "then only the ones from the command line are used.") do |v|
186
- @profiles << v
187
- end
188
- opts.on(NO_PROFILE_SHORT_FLAG, NO_PROFILE_LONG_FLAG,
189
- "Disables all profile loading to avoid using the 'default' profile.") do |v|
190
- @disable_profile_loading = true
191
- end
192
- opts.on("#{RETRY_FLAG} ATTEMPTS", "Specify the number of times to retry failing tests (default: 0)") do |v|
193
- @options[:retry] = v.to_i
194
- end
195
- opts.on("-c", "--[no-]color",
196
- "Whether or not to use ANSI color in the output. Cucumber decides",
197
- "based on your platform and the output destination if not specified.") do |v|
198
- Cucumber::Term::ANSIColor.coloring = v
199
- end
200
- opts.on("-d", "--dry-run", "Invokes formatters without executing the steps.",
201
- "This also omits the loading of your support/env.rb file if it exists.") do
202
- @options[:dry_run] = true
203
- @options[:duration] = false
204
- end
205
- opts.on("-m", "--no-multiline",
206
- "Don't print multiline strings and tables under steps.") do
207
- @options[:no_multiline] = true
208
- end
209
- opts.on("-s", "--no-source",
210
- "Don't print the file and line of the step definition with the steps.") do
211
- @options[:source] = false
212
- end
213
- opts.on("-i", "--no-snippets", "Don't print snippets for pending steps.") do
214
- @options[:snippets] = false
215
- end
216
- opts.on("-I", "--snippet-type TYPE",
217
- "Use different snippet type (Default: regexp). Available types:",
218
- *Cucumber::RbSupport::RbLanguage.cli_snippet_type_options) do |v|
219
- @options[:snippet_type] = v.to_sym
220
- end
221
-
222
- opts.on("-q", "--quiet", "Alias for --no-snippets --no-source.") do
223
- @options[:snippets] = false
224
- @options[:source] = false
225
- @options[:duration] = false
226
- end
227
- opts.on("--no-duration", "Don't print the duration at the end of the summary") do
228
- @options[:duration] = false
229
- end
230
- opts.on("-b", "--backtrace", "Show full backtrace for all errors.") do
231
- Cucumber.use_full_backtrace = true
232
- end
233
- opts.on("-S", "--strict", "Fail if there are any undefined or pending steps.") do
234
- @options[:strict] = true
235
- end
236
- opts.on("-w", "--wip", "Fail if there are any passing scenarios.") do
237
- @options[:wip] = true
238
- end
239
- opts.on("-v", "--verbose", "Show the files and features loaded.") do
240
- @options[:verbose] = true
241
- end
242
- opts.on("-g", "--guess", "Guess best match for Ambiguous steps.") do
243
- @options[:guess] = true
244
- end
245
- opts.on("-l", "--lines LINES", "Run given line numbers. Equivalent to FILE:LINE syntax") do |lines|
246
- @options[:lines] = lines
247
- end
248
- opts.on("-x", "--expand", "Expand Scenario Outline Tables in output.") do
249
- @options[:expand] = true
250
- end
251
- opts.on("--order TYPE[:SEED]", "Run examples in the specified order. Available types:",
99
+ opts.on('-j DIR', '--jars DIR', 'Load all the jars under DIR') {|jars| load_jars(jars) }
100
+ end
101
+
102
+ opts.on("#{RETRY_FLAG} ATTEMPTS", *retry_msg) {|v| set_option :retry, v.to_i }
103
+ opts.on('--i18n-languages', *i18n_languages_msg) { list_languages_and_exit }
104
+ opts.on('--i18n-keywords LANG', *i18n_keywords_msg) {|lang| set_language lang }
105
+ opts.on(FAIL_FAST_FLAG, 'Exit immediately following the first failing scenario') { set_option :fail_fast }
106
+ opts.on('-f FORMAT', '--format FORMAT', *format_msg, *FORMAT_HELP) do |v|
107
+ add_option :formats, [*parse_formats(v), @out_stream]
108
+ end
109
+ opts.on('--init', *init_msg) {|v| initialize_project }
110
+ opts.on('-o', '--out [FILE|DIR]', *out_msg) {|v| set_out_stream v }
111
+ opts.on('-t TAG_EXPRESSION', '--tags TAG_EXPRESSION', *tags_msg) {|v| add_tag v }
112
+ opts.on('-n NAME', '--name NAME', *name_msg) {|v| add_option :name_regexps, /#{v}/ }
113
+ opts.on('-e', '--exclude PATTERN', *exclude_msg) {|v| add_option :excludes, Regexp.new(v) }
114
+ opts.on(PROFILE_SHORT_FLAG, "#{PROFILE_LONG_FLAG} PROFILE", *profile_short_flag_msg) {|v| add_profile v }
115
+ opts.on(NO_PROFILE_SHORT_FLAG, NO_PROFILE_LONG_FLAG, *no_profile_short_flag_msg) {|v| disable_profile_loading }
116
+ opts.on('-c', '--[no-]color', *color_msg) {|v| set_color v }
117
+ opts.on('-d', '--dry-run', *dry_run_msg) { set_dry_run_and_duration }
118
+ opts.on('-m', '--no-multiline', "Don't print multiline strings and tables under steps.") { set_option :no_multiline }
119
+ opts.on('-s', '--no-source', "Don't print the file and line of the step definition with the steps.") { set_option :source, false }
120
+ opts.on('-i', '--no-snippets', "Don't print snippets for pending steps.") { set_option :snippets, false }
121
+ opts.on('-I', '--snippet-type TYPE', *snippet_type_msg) {|v| set_option :snippet_type, v.to_sym }
122
+ opts.on('-q', '--quiet', 'Alias for --no-snippets --no-source.') { shut_up }
123
+ opts.on('--no-duration', "Don't print the duration at the end of the summary") { set_option :duration, false }
124
+ opts.on('-b', '--backtrace', 'Show full backtrace for all errors.') { Cucumber.use_full_backtrace = true }
125
+ opts.on('-S', '--[no-]strict', *strict_msg) { |setting| set_strict(setting) }
126
+ opts.on('--[no-]strict-undefined', 'Fail if there are any undefined results.') { |setting| set_strict(setting, :undefined) }
127
+ opts.on('--[no-]strict-pending', 'Fail if there are any pending results.') { |setting| set_strict(setting, :pending) }
128
+ opts.on('--[no-]strict-flaky', 'Fail if there are any flaky results.') { |setting| set_strict(setting, :flaky) }
129
+ opts.on('-w', '--wip', 'Fail if there are any passing scenarios.') { set_option :wip }
130
+ opts.on('-v', '--verbose', 'Show the files and features loaded.') { set_option :verbose }
131
+ opts.on('-g', '--guess', 'Guess best match for Ambiguous steps.') { set_option :guess }
132
+ opts.on('-l', '--lines LINES', *lines_msg) {|lines| set_option :lines, lines }
133
+ opts.on('-x', '--expand', 'Expand Scenario Outline Tables in output.') { set_option :expand }
134
+
135
+ opts.on('--order TYPE[:SEED]', 'Run examples in the specified order. Available types:',
252
136
  *<<-TEXT.split("\n")) do |order|
253
137
  [defined] Run scenarios in the order they were defined (default).
254
138
  [random] Shuffle scenarios before running.
255
139
  Specify SEED to reproduce the shuffling from a previous run.
256
140
  e.g. --order random:5738
257
141
  TEXT
258
- @options[:order], @options[:seed] = *order.split(":")
142
+ @options[:order], @options[:seed] = *order.split(':')
259
143
  unless ORDER_TYPES.include?(@options[:order])
260
144
  fail "'#{@options[:order]}' is not a recognised order type. Please use one of #{ORDER_TYPES.join(", ")}."
261
145
  end
262
146
  end
263
- opts.on_tail("--version", "Show version.") do
264
- @out_stream.puts Cucumber::VERSION
265
- Kernel.exit(0)
266
- end
267
- opts.on_tail("-h", "--help", "You're looking at it.") do
268
- @out_stream.puts opts.help
269
- Kernel.exit(0)
270
- end
147
+
148
+ opts.on_tail('--version', 'Show version.') { exit_ok(Cucumber::VERSION) }
149
+ opts.on_tail('-h', '--help', "You're looking at it.") { exit_ok(opts.help) }
271
150
  end.parse!
272
151
 
273
152
  @args.map! { |a| "#{a}:#{@options[:lines]}" } if @options[:lines]
@@ -291,29 +170,265 @@ TEXT
291
170
  end
292
171
 
293
172
  def check_formatter_stream_conflicts()
294
- streams = @options[:formats].uniq.map { |(_, stream)| stream }
295
- if streams != streams.uniq
296
- raise "All but one formatter must use --out, only one can print to each stream (or STDOUT)"
297
- end
173
+ streams = @options[:formats].uniq.map { |(_, _, stream)| stream }
174
+ return if streams == streams.uniq
175
+ raise 'All but one formatter must use --out, only one can print to each stream (or STDOUT)'
298
176
  end
299
177
 
300
178
  def to_hash
301
- Cucumber::Hash(@options)
179
+ Hash(@options)
302
180
  end
303
181
 
304
- protected
182
+ protected
305
183
 
306
184
  attr_reader :options, :profiles, :expanded_args
307
185
  protected :options, :profiles, :expanded_args
308
186
 
309
- private
187
+ private
188
+
189
+ def color_msg
190
+ [
191
+ 'Whether or not to use ANSI color in the output. Cucumber decides',
192
+ 'based on your platform and the output destination if not specified.'
193
+ ]
194
+ end
195
+
196
+ def dry_run_msg
197
+ [
198
+ 'Invokes formatters without executing the steps.',
199
+ 'This also omits the loading of your support/env.rb file if it exists.'
200
+ ]
201
+ end
202
+
203
+ def exclude_msg
204
+ [ "Don't run feature files or require ruby files matching PATTERN" ]
205
+ end
206
+
207
+ def format_msg
208
+ [ 'How to format features (Default: pretty). Available formats:' ]
209
+ end
210
+
211
+ def i18n_languages_msg
212
+ [
213
+ 'List all available languages'
214
+ ]
215
+ end
216
+
217
+ def i18n_keywords_msg
218
+ [
219
+ 'List keywords for in a particular language',
220
+ %{Run with "--i18n help" to see all languages}
221
+ ]
222
+ end
223
+
224
+ def init_msg
225
+ [
226
+ 'Initializes folder structure and generates conventional files for',
227
+ 'a Cucumber project.'
228
+ ]
229
+ end
230
+
231
+ def lines_msg
232
+ [ 'Run given line numbers. Equivalent to FILE:LINE syntax' ]
233
+ end
234
+
235
+ def no_profile_short_flag_msg
236
+ [
237
+ "Disables all profile loading to avoid using the 'default' profile."
238
+ ]
239
+ end
240
+
241
+ def profile_short_flag_msg
242
+ [
243
+ 'Pull commandline arguments from cucumber.yml which can be defined as',
244
+ "strings or arrays. When a 'default' profile is defined and no profile",
245
+ 'is specified it is always used. (Unless disabled, see -P below.)',
246
+ 'When feature files are defined in a profile and on the command line',
247
+ 'then only the ones from the command line are used.'
248
+ ]
249
+ end
250
+
251
+ def retry_msg
252
+ [ 'Specify the number of times to retry failing tests (default: 0)' ]
253
+ end
254
+
255
+ def name_msg
256
+ [
257
+ 'Only execute the feature elements which match part of the given name.',
258
+ 'If this option is given more than once, it will match against all the',
259
+ 'given names.'
260
+ ]
261
+ end
262
+
263
+ def strict_msg
264
+ [
265
+ 'Fail if there are any strict affected results ',
266
+ '(that is undefined, pending or flaky results).'
267
+ ]
268
+ end
269
+
270
+ def parse_formats(v)
271
+ formatter, *formatter_options = v.split(',')
272
+ options_hash = Hash[formatter_options.map { |s| s.split('=') }]
273
+ [formatter, options_hash]
274
+ end
275
+
276
+ def set_out_stream(v)
277
+ @options[:formats] << ['pretty', {}, nil] if @options[:formats].empty?
278
+ @options[:formats][-1][2] = v
279
+ end
280
+
281
+ def tags_msg
282
+ [
283
+ 'Only execute the features or scenarios with tags matching TAG_EXPRESSION.',
284
+ 'Scenarios inherit tags declared on the Feature level. The simplest',
285
+ 'TAG_EXPRESSION is simply a tag. Example: --tags @dev. To represent',
286
+ "boolean NOT preceed the tag with 'not '. Example: --tags 'not @dev'.",
287
+ 'A tag expression can have several tags separated by an or which represents',
288
+ "logical OR. Example: --tags '@dev or @wip'. The --tags option can be specified",
289
+ 'A tag expression can have several tags separated by an and which represents',
290
+ "logical AND. Example: --tags '@dev and @wip'. The --tags option can be specified",
291
+ 'several times, and this also represents logical AND.',
292
+ "Example: --tags '@foo or not @bar' --tags @zap. This represents the boolean",
293
+ 'expression (@foo || !@bar) && @zap.',
294
+ "\n",
295
+ 'Beware that if you want to use several negative tags to exclude several tags',
296
+ "you have to use logical AND: --tags 'not @fixme and not @buggy'.",
297
+ "\n",
298
+ 'Tags can be given a threshold to limit the number of occurrences.',
299
+ 'Example: --tags @qa:3 will fail if there are more than 3 occurrences of the @qa tag.',
300
+ 'This can be practical if you are practicing Kanban or CONWIP.'
301
+ ]
302
+ end
303
+
304
+ def out_msg
305
+ [
306
+ 'Write output to a file/directory instead of STDOUT. This option',
307
+ 'applies to the previously specified --format, or the',
308
+ 'default format if no format is specified. Check the specific',
309
+ "formatter's docs to see whether to pass a file or a dir."
310
+ ]
311
+ end
312
+
313
+ def require_files_msg
314
+ [
315
+ 'Require files before executing the features. If this',
316
+ 'option is not specified, all *.rb files that are',
317
+ 'siblings or below the features will be loaded auto-',
318
+ 'matically. Automatic loading is disabled when this',
319
+ 'option is specified, and all loading becomes explicit.',
320
+ 'Files under directories named "support" are always',
321
+ 'loaded first.',
322
+ 'This option can be specified multiple times.'
323
+ ]
324
+ end
325
+
326
+ def snippet_type_msg
327
+ [
328
+ 'Use different snippet type (Default: cucumber_expression). Available types:',
329
+ Cucumber::Glue::RegistryAndMore.cli_snippet_type_options
330
+ ].flatten
331
+ end
332
+
333
+ def banner
334
+ [
335
+ 'Usage: cucumber [options] [ [FILE|DIR|URL][:LINE[:LINE]*] ]+', '',
336
+ 'Examples:',
337
+ 'cucumber examples/i18n/en/features',
338
+ 'cucumber @rerun.txt (See --format rerun)',
339
+ 'cucumber examples/i18n/it/features/somma.feature:6:98:113',
340
+ 'cucumber -s -i http://rubyurl.com/eeCl', '', ''
341
+ ].join("\n")
342
+ end
343
+
344
+ def require_files(v)
345
+ @options[:require] << v
346
+ return unless Cucumber::JRUBY && File.directory?(v)
347
+ require 'java'
348
+ $CLASSPATH << v
349
+ end
350
+
351
+ def require_jars(jars)
352
+ Dir["#{jars}/**/*.jar"].each {|jar| require jar}
353
+ end
354
+
355
+ def set_language(lang)
356
+ require 'gherkin/dialect'
357
+
358
+ return indicate_invalid_language_and_exit(lang) unless ::Gherkin::DIALECTS.keys.include? lang
359
+ list_keywords_and_exit(lang)
360
+ end
361
+
362
+ def disable_profile_loading
363
+ @disable_profile_loading = true
364
+ end
310
365
 
311
366
  def non_stdout_formats
312
- @options[:formats].select {|format, output| output != @out_stream }
367
+ @options[:formats].select { |_, _, output| output != @out_stream }
368
+ end
369
+
370
+ def add_option(option, value)
371
+ @options[option] << value
372
+ end
373
+
374
+ def add_tag(value)
375
+ warn("Deprecated: Found tags option '#{value}'. Support for '~@tag' will be removed from the next release of Cucumber. Please use 'not @tag' instead.") if value.include?('~')
376
+ warn("Deprecated: Found tags option '#{value}'. Support for '@tag1,@tag2' will be removed from the next release of Cucumber. Please use '@tag or @tag2' instead.") if value.include?(',')
377
+ @options[:tag_expressions] << value.gsub(/(@\w+)(:\d+)?/, '\1')
378
+ add_tag_limits(value)
379
+ end
380
+
381
+ def add_tag_limits(value)
382
+ value.split(/[, ]/).map { |part| TAG_LIMIT_MATCHER.match(part) }.compact.each do |matchdata|
383
+ add_tag_limit(@options[:tag_limits], matchdata[:tag_name], matchdata[:limit].to_i)
384
+ end
385
+ end
386
+
387
+ def add_tag_limit(tag_limits, tag_name, limit)
388
+ if tag_limits[tag_name] && tag_limits[tag_name] != limit
389
+ raise "Inconsistent tag limits for #{tag_name}: #{tag_limits[tag_name]} and #{limit}"
390
+ end
391
+ tag_limits[tag_name] = limit
392
+ end
393
+
394
+ def set_color(color)
395
+ Cucumber::Term::ANSIColor.coloring = color
396
+ end
397
+
398
+ def initialize_project
399
+ ProjectInitializer.new.run && Kernel.exit(0)
400
+ end
401
+
402
+ def add_profile(p)
403
+ @profiles << p
404
+ end
405
+
406
+ def set_option(option, value=nil)
407
+ @options[option] = value.nil? ? true : value
408
+ end
409
+
410
+ def set_dry_run_and_duration
411
+ @options[:dry_run] = true
412
+ @options[:duration] = false
413
+ end
414
+
415
+ def exit_ok(text)
416
+ @out_stream.puts text
417
+ Kernel.exit(0)
418
+ end
419
+
420
+ def shut_up
421
+ @options[:snippets] = false
422
+ @options[:source] = false
423
+ @options[:duration] = false
424
+ end
425
+
426
+ def set_strict(setting, type = nil)
427
+ @options[:strict].set_strict(setting, type)
313
428
  end
314
429
 
315
430
  def stdout_formats
316
- @options[:formats].select {|format, output| output == @out_stream }
431
+ @options[:formats].select { |_, _, output| output == @out_stream }
317
432
  end
318
433
 
319
434
  def extract_environment_variables
@@ -331,7 +446,7 @@ TEXT
331
446
 
332
447
  def merge_profiles
333
448
  if @disable_profile_loading
334
- @out_stream.puts "Disabling profiles..."
449
+ @out_stream.puts 'Disabling profiles...'
335
450
  return
336
451
  end
337
452
 
@@ -370,6 +485,7 @@ TEXT
370
485
  @options[:excludes] += other_options[:excludes]
371
486
  @options[:name_regexps] += other_options[:name_regexps]
372
487
  @options[:tag_expressions] += other_options[:tag_expressions]
488
+ merge_tag_limits(@options[:tag_limits], other_options[:tag_limits])
373
489
  @options[:env_vars] = other_options[:env_vars].merge(@options[:env_vars])
374
490
  if @options[:paths].empty?
375
491
  @options[:paths] = other_options[:paths]
@@ -379,7 +495,7 @@ TEXT
379
495
  @options[:source] &= other_options[:source]
380
496
  @options[:snippets] &= other_options[:snippets]
381
497
  @options[:duration] &= other_options[:duration]
382
- @options[:strict] |= other_options[:strict]
498
+ @options[:strict] = other_options[:strict].merge!(@options[:strict])
383
499
  @options[:dry_run] |= other_options[:dry_run]
384
500
 
385
501
  @profiles += other_options.profiles
@@ -395,6 +511,10 @@ TEXT
395
511
  self
396
512
  end
397
513
 
514
+ def merge_tag_limits(option_limits, other_limits)
515
+ other_limits.each { |key, value| add_tag_limit(option_limits, key, value) }
516
+ end
517
+
398
518
  def indicate_invalid_language_and_exit(lang)
399
519
  @out_stream.write("Invalid language '#{lang}'. Available languages are:\n")
400
520
  list_languages_and_exit
@@ -404,21 +524,24 @@ TEXT
404
524
  require 'gherkin/dialect'
405
525
  language = ::Gherkin::Dialect.for(lang)
406
526
  data = Cucumber::MultilineArgument::DataTable.from(
407
- [["feature", to_keywords_string(language.feature_keywords)],
408
- ["background", to_keywords_string(language.background_keywords)],
409
- ["scenario", to_keywords_string(language.scenario_keywords)],
410
- ["scenario_outline", to_keywords_string(language.scenario_outline_keywords)],
411
- ["examples", to_keywords_string(language.examples_keywords)],
412
- ["given", to_keywords_string(language.given_keywords)],
413
- ["when", to_keywords_string(language.when_keywords)],
414
- ["then", to_keywords_string(language.then_keywords)],
415
- ["and", to_keywords_string(language.and_keywords)],
416
- ["but", to_keywords_string(language.but_keywords)],
417
- ["given (code)", to_code_keywords_string(language.given_keywords)],
418
- ["when (code)", to_code_keywords_string(language.when_keywords)],
419
- ["then (code)", to_code_keywords_string(language.then_keywords)],
420
- ["and (code)", to_code_keywords_string(language.and_keywords)],
421
- ["but (code)", to_code_keywords_string(language.but_keywords)]])
527
+ [
528
+ ['feature', to_keywords_string(language.feature_keywords)],
529
+ ['background', to_keywords_string(language.background_keywords)],
530
+ ['scenario', to_keywords_string(language.scenario_keywords)],
531
+ ['scenario_outline', to_keywords_string(language.scenario_outline_keywords)],
532
+ ['examples', to_keywords_string(language.examples_keywords)],
533
+ ['given', to_keywords_string(language.given_keywords)],
534
+ ['when', to_keywords_string(language.when_keywords)],
535
+ ['then', to_keywords_string(language.then_keywords)],
536
+ ['and', to_keywords_string(language.and_keywords)],
537
+ ['but', to_keywords_string(language.but_keywords)],
538
+ ['given (code)', to_code_keywords_string(language.given_keywords)],
539
+ ['when (code)', to_code_keywords_string(language.when_keywords)],
540
+ ['then (code)', to_code_keywords_string(language.then_keywords)],
541
+ ['and (code)', to_code_keywords_string(language.and_keywords)],
542
+ ['but (code)', to_code_keywords_string(language.but_keywords)]
543
+ ]
544
+ )
422
545
  @out_stream.write(data.to_s({ color: false, prefixes: Hash.new('') }))
423
546
  Kernel.exit(0)
424
547
  end
@@ -443,12 +566,13 @@ TEXT
443
566
 
444
567
  def default_options
445
568
  {
446
- :strict => false,
569
+ :strict => Cucumber::Core::Test::Result::StrictConfiguration.new,
447
570
  :require => [],
448
571
  :dry_run => false,
449
572
  :formats => [],
450
573
  :excludes => [],
451
574
  :tag_expressions => [],
575
+ :tag_limits => {},
452
576
  :name_regexps => [],
453
577
  :env_vars => {},
454
578
  :diff_enabled => true,