mobiusloop 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (516) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.travis.yml +22 -0
  6. data/.yardopts +1 -0
  7. data/CONTRIBUTING.md +68 -0
  8. data/Gemfile +5 -0
  9. data/History.md +2280 -0
  10. data/LICENSE +22 -0
  11. data/README.md +373 -0
  12. data/Rakefile +22 -0
  13. data/bin/mobiusloop +8 -0
  14. data/cucumber.gemspec +47 -0
  15. data/cucumber.yml +20 -0
  16. data/examples/i18n/README.textile +3 -0
  17. data/examples/i18n/Rakefile +33 -0
  18. data/examples/i18n/ar/Rakefile +6 -0
  19. data/examples/i18n/ar/features/addition.feature +17 -0
  20. data/examples/i18n/ar/features/step_definitions/calculator_steps.rb +24 -0
  21. data/examples/i18n/ar/lib/calculator.rb +11 -0
  22. data/examples/i18n/bg/Rakefile +6 -0
  23. data/examples/i18n/bg/features/addition.feature +12 -0
  24. data/examples/i18n/bg/features/consecutive_calculations.feature +19 -0
  25. data/examples/i18n/bg/features/division.feature +17 -0
  26. data/examples/i18n/bg/features/step_definitions/calculator_steps.rb +24 -0
  27. data/examples/i18n/bg/features/support/env.rb +5 -0
  28. data/examples/i18n/bg/features/support/world.rb +8 -0
  29. data/examples/i18n/bg/lib/calculator.rb +24 -0
  30. data/examples/i18n/ca/Rakefile +6 -0
  31. data/examples/i18n/ca/features/step_definitions/calculator_steps.rb +21 -0
  32. data/examples/i18n/ca/features/suma.feature +17 -0
  33. data/examples/i18n/ca/lib/calculadora.rb +16 -0
  34. data/examples/i18n/cs/Rakefile +6 -0
  35. data/examples/i18n/cs/features/addition.feature +17 -0
  36. data/examples/i18n/cs/features/division.feature +11 -0
  37. data/examples/i18n/cs/features/step_definitions/calculator_steps.rb +24 -0
  38. data/examples/i18n/cs/lib/calculator.rb +14 -0
  39. data/examples/i18n/da/Rakefile +6 -0
  40. data/examples/i18n/da/features/sammenlaegning.feature +18 -0
  41. data/examples/i18n/da/features/step_definitions/lommeregner_steps.rb +24 -0
  42. data/examples/i18n/da/lib/lommeregner.rb +11 -0
  43. data/examples/i18n/de/Rakefile +6 -0
  44. data/examples/i18n/de/features/addition.feature +17 -0
  45. data/examples/i18n/de/features/division.feature +10 -0
  46. data/examples/i18n/de/features/step_definitions/calculator_steps.rb +24 -0
  47. data/examples/i18n/de/lib/calculator.rb +14 -0
  48. data/examples/i18n/el/Rakefile +6 -0
  49. data/examples/i18n/el/features/addition.feature +17 -0
  50. data/examples/i18n/el/features/division.feature +10 -0
  51. data/examples/i18n/el/features/step_definitions/calculator_steps.rb +24 -0
  52. data/examples/i18n/el/lib/calculator.rb +14 -0
  53. data/examples/i18n/en-lol/Rakefile +4 -0
  54. data/examples/i18n/en-lol/features/step_definitions/cucumbrz_steps.rb +16 -0
  55. data/examples/i18n/en-lol/features/stuffing.feature +8 -0
  56. data/examples/i18n/en-lol/features/support/env.rb +7 -0
  57. data/examples/i18n/en-lol/lib/basket.rb +12 -0
  58. data/examples/i18n/en-lol/lib/belly.rb +11 -0
  59. data/examples/i18n/en/Rakefile +6 -0
  60. data/examples/i18n/en/features/addition.feature +17 -0
  61. data/examples/i18n/en/features/division.feature +10 -0
  62. data/examples/i18n/en/features/step_definitions/calculator_steps.rb +24 -0
  63. data/examples/i18n/en/lib/calculator.rb +14 -0
  64. data/examples/i18n/eo/Rakefile +6 -0
  65. data/examples/i18n/eo/features/adicio.feature +17 -0
  66. data/examples/i18n/eo/features/divido.feature +10 -0
  67. data/examples/i18n/eo/features/step_definitions/calculator_steps.rb +24 -0
  68. data/examples/i18n/eo/lib/calculator.rb +14 -0
  69. data/examples/i18n/es/Rakefile +6 -0
  70. data/examples/i18n/es/features/adicion.feature +17 -0
  71. data/examples/i18n/es/features/step_definitions/calculador_steps.rb +21 -0
  72. data/examples/i18n/es/lib/calculador.rb +14 -0
  73. data/examples/i18n/et/Rakefile +6 -0
  74. data/examples/i18n/et/features/jagamine.feature +10 -0
  75. data/examples/i18n/et/features/liitmine.feature +17 -0
  76. data/examples/i18n/et/features/step_definitions/kalkulaator_steps.rb +24 -0
  77. data/examples/i18n/et/lib/kalkulaator.rb +14 -0
  78. data/examples/i18n/fi/Rakefile +6 -0
  79. data/examples/i18n/fi/features/jakolasku.feature +10 -0
  80. data/examples/i18n/fi/features/step_definitions/laskin_steps.rb +24 -0
  81. data/examples/i18n/fi/features/yhteenlasku.feature +17 -0
  82. data/examples/i18n/fi/lib/laskin.rb +14 -0
  83. data/examples/i18n/fr/Rakefile +8 -0
  84. data/examples/i18n/fr/features/addition.feature +18 -0
  85. data/examples/i18n/fr/features/addition2.feature +17 -0
  86. data/examples/i18n/fr/features/step_definitions/calculatrice_steps.rb +32 -0
  87. data/examples/i18n/fr/features/support/env.rb +5 -0
  88. data/examples/i18n/fr/lib/calculatrice.rb +10 -0
  89. data/examples/i18n/he/Rakefile +6 -0
  90. data/examples/i18n/he/features/addition.feature +17 -0
  91. data/examples/i18n/he/features/division.feature +10 -0
  92. data/examples/i18n/he/features/step_definitions/calculator_steps.rb +24 -0
  93. data/examples/i18n/he/lib/calculator.rb +15 -0
  94. data/examples/i18n/hi/Rakefile +6 -0
  95. data/examples/i18n/hi/features/addition.feature +16 -0
  96. data/examples/i18n/hi/features/division.feature +10 -0
  97. data/examples/i18n/hi/features/step_definitions/calculator_steps.rb +24 -0
  98. data/examples/i18n/hi/lib/calculator.rb +15 -0
  99. data/examples/i18n/ht/Rakefile +6 -0
  100. data/examples/i18n/ht/features/adisyon.feature +17 -0
  101. data/examples/i18n/ht/features/divizyon.feature +10 -0
  102. data/examples/i18n/ht/features/step_definitions/kalkilatris_steps.rb +25 -0
  103. data/examples/i18n/ht/lib/kalkilatris.rb +14 -0
  104. data/examples/i18n/hu/Rakefile +6 -0
  105. data/examples/i18n/hu/features/osszeadas.feature +17 -0
  106. data/examples/i18n/hu/features/osztas.feature +10 -0
  107. data/examples/i18n/hu/features/step_definitions/calculator_steps.rb +25 -0
  108. data/examples/i18n/hu/lib/calculator.rb +14 -0
  109. data/examples/i18n/id/Rakefile +6 -0
  110. data/examples/i18n/id/features/addition.feature +17 -0
  111. data/examples/i18n/id/features/division.feature +10 -0
  112. data/examples/i18n/id/features/step_definitions/calculator_steps.rb +24 -0
  113. data/examples/i18n/id/lib/calculator.rb +14 -0
  114. data/examples/i18n/it/Rakefile +6 -0
  115. data/examples/i18n/it/features/somma.feature +11 -0
  116. data/examples/i18n/it/features/step_definitions/calcolatrice_steps.rb +24 -0
  117. data/examples/i18n/it/lib/calcolatrice.rb +11 -0
  118. data/examples/i18n/ja/Rakefile +6 -0
  119. data/examples/i18n/ja/features/addition.feature +17 -0
  120. data/examples/i18n/ja/features/division.feature +10 -0
  121. data/examples/i18n/ja/features/step_definitions/calculator_steps.rb +19 -0
  122. data/examples/i18n/ja/features/support/env.rb +5 -0
  123. data/examples/i18n/ja/lib/calculator.rb +14 -0
  124. data/examples/i18n/ko/Rakefile +6 -0
  125. data/examples/i18n/ko/features/addition.feature +17 -0
  126. data/examples/i18n/ko/features/division.feature +11 -0
  127. data/examples/i18n/ko/features/step_definitions/calculator_steps.rb +24 -0
  128. data/examples/i18n/ko/lib/calculator.rb +14 -0
  129. data/examples/i18n/lt/Rakefile +6 -0
  130. data/examples/i18n/lt/features/addition.feature +17 -0
  131. data/examples/i18n/lt/features/division.feature +10 -0
  132. data/examples/i18n/lt/features/step_definitions/calculator_steps.rb +24 -0
  133. data/examples/i18n/lt/lib/calculator.rb +14 -0
  134. data/examples/i18n/lv/Rakefile +6 -0
  135. data/examples/i18n/lv/features/addition.feature +17 -0
  136. data/examples/i18n/lv/features/division.feature +10 -0
  137. data/examples/i18n/lv/features/step_definitions/calculator_steps.rb +24 -0
  138. data/examples/i18n/lv/lib/calculator.rb +14 -0
  139. data/examples/i18n/no/Rakefile +6 -0
  140. data/examples/i18n/no/features/step_definitions/kalkulator_steps.rb +17 -0
  141. data/examples/i18n/no/features/summering.feature +19 -0
  142. data/examples/i18n/no/features/support/env.rb +6 -0
  143. data/examples/i18n/no/lib/kalkulator.rb +11 -0
  144. data/examples/i18n/pl/Rakefile +6 -0
  145. data/examples/i18n/pl/features/addition.feature +17 -0
  146. data/examples/i18n/pl/features/division.feature +10 -0
  147. data/examples/i18n/pl/features/step_definitions/calculator_steps.rb +24 -0
  148. data/examples/i18n/pl/features/support/env.rb +5 -0
  149. data/examples/i18n/pl/lib/calculator.rb +14 -0
  150. data/examples/i18n/pt/Rakefile +6 -0
  151. data/examples/i18n/pt/features/adicao.feature +11 -0
  152. data/examples/i18n/pt/features/step_definitions/calculadora_steps.rb +20 -0
  153. data/examples/i18n/pt/features/support/env.rb +5 -0
  154. data/examples/i18n/pt/lib/calculadora.rb +10 -0
  155. data/examples/i18n/ro/Rakefile +6 -0
  156. data/examples/i18n/ro/features/adunare.feature +12 -0
  157. data/examples/i18n/ro/features/step_definitions/calculator_steps.rb +21 -0
  158. data/examples/i18n/ro/lib/calculator.rb +11 -0
  159. data/examples/i18n/ru/Rakefile +6 -0
  160. data/examples/i18n/ru/features/addition.feature +11 -0
  161. data/examples/i18n/ru/features/consecutive_calculations.feature +17 -0
  162. data/examples/i18n/ru/features/division.feature +16 -0
  163. data/examples/i18n/ru/features/step_definitions/calculator_steps.rb +19 -0
  164. data/examples/i18n/ru/features/support/env.rb +5 -0
  165. data/examples/i18n/ru/features/support/world.rb +8 -0
  166. data/examples/i18n/ru/lib/calculator.rb +24 -0
  167. data/examples/i18n/sk/Rakefile +6 -0
  168. data/examples/i18n/sk/features/addition.feature +17 -0
  169. data/examples/i18n/sk/features/division.feature +10 -0
  170. data/examples/i18n/sk/features/step_definitions/calculator_steps.rb +24 -0
  171. data/examples/i18n/sk/lib/calculator.rb +14 -0
  172. data/examples/i18n/sr-Cyrl/Rakefile +6 -0
  173. data/examples/i18n/sr-Cyrl/features/sabiranje.feature +18 -0
  174. data/examples/i18n/sr-Cyrl/features/step_definitions/calculator_steps.rb +20 -0
  175. data/examples/i18n/sr-Cyrl/features/support/env.rb +5 -0
  176. data/examples/i18n/sr-Cyrl/lib/calculator.rb +12 -0
  177. data/examples/i18n/sr-Latn/Rakefile +6 -0
  178. data/examples/i18n/sr-Latn/features/sabiranje.feature +18 -0
  179. data/examples/i18n/sr-Latn/features/step_definitions/calculator_steps.rb +24 -0
  180. data/examples/i18n/sr-Latn/lib/calculator.rb +12 -0
  181. data/examples/i18n/sv/Rakefile +6 -0
  182. data/examples/i18n/sv/features/step_definitions/kalkulator_steps.rb +24 -0
  183. data/examples/i18n/sv/features/summering.feature +18 -0
  184. data/examples/i18n/sv/lib/kalkulator.rb +11 -0
  185. data/examples/i18n/tr/Rakefile +6 -0
  186. data/examples/i18n/tr/features/bolme.feature +10 -0
  187. data/examples/i18n/tr/features/step_definitions/hesap_makinesi_adimlari.rb +24 -0
  188. data/examples/i18n/tr/features/toplama.feature +18 -0
  189. data/examples/i18n/tr/lib/hesap_makinesi.rb +15 -0
  190. data/examples/i18n/uk/Rakefile +6 -0
  191. data/examples/i18n/uk/features/addition.feature +11 -0
  192. data/examples/i18n/uk/features/consecutive_calculations.feature +17 -0
  193. data/examples/i18n/uk/features/division.feature +16 -0
  194. data/examples/i18n/uk/features/step_definitions/calculator_steps.rb +19 -0
  195. data/examples/i18n/uk/features/support/env.rb +5 -0
  196. data/examples/i18n/uk/features/support/world.rb +8 -0
  197. data/examples/i18n/uk/lib/calculator.rb +24 -0
  198. data/examples/i18n/uz/Rakefile +6 -0
  199. data/examples/i18n/uz/features/addition.feature +10 -0
  200. data/examples/i18n/uz/features/consecutive_calculations.feature +17 -0
  201. data/examples/i18n/uz/features/division.feature +17 -0
  202. data/examples/i18n/uz/features/step_definitions/calculator_steps.rb +19 -0
  203. data/examples/i18n/uz/features/support/env.rb +5 -0
  204. data/examples/i18n/uz/features/support/world.rb +8 -0
  205. data/examples/i18n/uz/lib/calculator.rb +24 -0
  206. data/examples/i18n/zh-CN/Rakefile +4 -0
  207. data/examples/i18n/zh-CN/features/addition.feature +18 -0
  208. data/examples/i18n/zh-CN/features/step_definitions/calculator_steps.rb +26 -0
  209. data/examples/i18n/zh-CN/lib/calculator.rb +10 -0
  210. data/examples/i18n/zh-TW/Rakefile +4 -0
  211. data/examples/i18n/zh-TW/features/addition.feature +17 -0
  212. data/examples/i18n/zh-TW/features/division.feature +11 -0
  213. data/examples/i18n/zh-TW/features/step_definitions/calculator_steps.rb +24 -0
  214. data/examples/i18n/zh-TW/lib/calculator.rb +14 -0
  215. data/examples/mobiusloop/increase_readers.goal +13 -0
  216. data/examples/mobiusloop/scale_page_response.rb +20 -0
  217. data/examples/mobiusloop/total_articles_scale.rb +10 -0
  218. data/examples/mobiusloop/total_readers_scale.rb +10 -0
  219. data/examples/rspec_doubles/Rakefile +4 -0
  220. data/examples/rspec_doubles/features/mocking.feature +9 -0
  221. data/examples/rspec_doubles/features/step_definitions/calvin_steps.rb +19 -0
  222. data/examples/rspec_doubles/features/support/env.rb +12 -0
  223. data/examples/sinatra/README.textile +13 -0
  224. data/examples/sinatra/Rakefile +6 -0
  225. data/examples/sinatra/app.rb +14 -0
  226. data/examples/sinatra/features/add.feature +11 -0
  227. data/examples/sinatra/features/step_definitions/add_steps.rb +15 -0
  228. data/examples/sinatra/features/support/env.rb +10 -0
  229. data/examples/sinatra/views/add.erb +7 -0
  230. data/examples/sinatra/views/layout.erb +8 -0
  231. data/examples/tcl/README.textile +11 -0
  232. data/examples/tcl/Rakefile +6 -0
  233. data/examples/tcl/features/fibonnacci.feature +17 -0
  234. data/examples/tcl/features/step_definitions/fib_steps.rb +7 -0
  235. data/examples/tcl/features/support/env.rb +6 -0
  236. data/examples/tcl/src/fib.tcl +3 -0
  237. data/examples/test_unit/Gemfile +4 -0
  238. data/examples/test_unit/Rakefile +6 -0
  239. data/examples/test_unit/features/step_definitions/test_unit_steps.rb +20 -0
  240. data/examples/test_unit/features/test_unit.feature +9 -0
  241. data/examples/watir/README.textile +16 -0
  242. data/examples/watir/Rakefile +12 -0
  243. data/examples/watir/cucumber.yml +1 -0
  244. data/examples/watir/features/search.feature +12 -0
  245. data/examples/watir/features/step_definitions/search_steps.rb +26 -0
  246. data/examples/watir/features/support/env.rb +35 -0
  247. data/examples/watir/features/support/screenshots.rb +47 -0
  248. data/features/docs/api/list_step_defs_as_json.feature +50 -0
  249. data/features/docs/api/listen_for_events.feature +58 -0
  250. data/features/docs/api/run_cli_main_with_existing_runtime.feature +26 -0
  251. data/features/docs/cli/backtraces.feature +36 -0
  252. data/features/docs/cli/dry_run.feature +70 -0
  253. data/features/docs/cli/exclude_files.feature +18 -0
  254. data/features/docs/cli/execute_with_tag_filter.feature +117 -0
  255. data/features/docs/cli/fail_fast.feature +46 -0
  256. data/features/docs/cli/finding_steps.feature +28 -0
  257. data/features/docs/cli/randomize.feature +140 -0
  258. data/features/docs/cli/require.feature +27 -0
  259. data/features/docs/cli/retry_failing_tests.feature +32 -0
  260. data/features/docs/cli/run_scenarios_matching_name.feature +104 -0
  261. data/features/docs/cli/run_specific_scenarios.feature +77 -0
  262. data/features/docs/cli/showing_differences.feature +43 -0
  263. data/features/docs/cli/specifying_multiple_formatters.feature +65 -0
  264. data/features/docs/cli/strict_mode.feature +64 -0
  265. data/features/docs/defining_steps/nested_steps.feature +177 -0
  266. data/features/docs/defining_steps/nested_steps_i18n.feature +34 -0
  267. data/features/docs/defining_steps/nested_steps_with_second_arg.feature +54 -0
  268. data/features/docs/defining_steps/one_line_step_definitions.feature +65 -0
  269. data/features/docs/defining_steps/printing_messages.feature +148 -0
  270. data/features/docs/defining_steps/skip_scenario.feature +64 -0
  271. data/features/docs/defining_steps/snippets.feature +56 -0
  272. data/features/docs/defining_steps/table_diffing.feature +50 -0
  273. data/features/docs/exception_in_after_hook.feature +126 -0
  274. data/features/docs/exception_in_after_step_hook.feature +102 -0
  275. data/features/docs/exception_in_around_hook.feature +78 -0
  276. data/features/docs/exception_in_before_hook.feature +96 -0
  277. data/features/docs/extending_cucumber/custom_filter.feature +29 -0
  278. data/features/docs/extending_cucumber/custom_formatter.feature +130 -0
  279. data/features/docs/formatters/api_methods.feature +37 -0
  280. data/features/docs/formatters/debug_formatter.feature +47 -0
  281. data/features/docs/formatters/formatter_step_file_colon_line.feature +44 -0
  282. data/features/docs/formatters/html_formatter.feature +91 -0
  283. data/features/docs/formatters/json_formatter.feature +767 -0
  284. data/features/docs/formatters/junit_formatter.feature +454 -0
  285. data/features/docs/formatters/pretty_formatter.feature +73 -0
  286. data/features/docs/formatters/progress_formatter.feature +31 -0
  287. data/features/docs/formatters/rerun_formatter.feature +188 -0
  288. data/features/docs/formatters/usage_formatter.feature +98 -0
  289. data/features/docs/getting_started.feature +27 -0
  290. data/features/docs/gherkin/background.feature +548 -0
  291. data/features/docs/gherkin/doc_strings.feature +74 -0
  292. data/features/docs/gherkin/expand_option_for_outlines.feature +46 -0
  293. data/features/docs/gherkin/language_from_header.feature +32 -0
  294. data/features/docs/gherkin/language_help.feature +42 -0
  295. data/features/docs/gherkin/outlines.feature +156 -0
  296. data/features/docs/gherkin/unicode_table.feature +32 -0
  297. data/features/docs/gherkin/using_descriptions.feature +82 -0
  298. data/features/docs/gherkin/using_star_notation.feature +37 -0
  299. data/features/docs/iso-8859-1.feature +6 -0
  300. data/features/docs/post_configuration_hook.feature +49 -0
  301. data/features/docs/profiles.feature +120 -0
  302. data/features/docs/rake_task.feature +141 -0
  303. data/features/docs/raketask.feature +44 -0
  304. data/features/docs/work_in_progress.feature +154 -0
  305. data/features/docs/writing_support_code/after_hooks.feature +102 -0
  306. data/features/docs/writing_support_code/after_step_hooks.feature +53 -0
  307. data/features/docs/writing_support_code/around_hooks.feature +260 -0
  308. data/features/docs/writing_support_code/before_hook.feature +65 -0
  309. data/features/docs/writing_support_code/hook_order.feature +61 -0
  310. data/features/docs/writing_support_code/load_path.feature +17 -0
  311. data/features/docs/writing_support_code/state.feature +32 -0
  312. data/features/docs/writing_support_code/tagged_hooks.feature +95 -0
  313. data/features/docs/writing_support_code/transforms.feature +98 -0
  314. data/features/lib/step_definitions/aruba_steps.rb +26 -0
  315. data/features/lib/step_definitions/cucumber_steps.rb +78 -0
  316. data/features/lib/step_definitions/iso-8859-1_steps.rb +12 -0
  317. data/features/lib/step_definitions/json_steps.rb +7 -0
  318. data/features/lib/step_definitions/junit_steps.rb +13 -0
  319. data/features/lib/step_definitions/language_steps.rb +8 -0
  320. data/features/lib/step_definitions/profile_steps.rb +15 -0
  321. data/features/lib/step_definitions/retry_steps.rb +35 -0
  322. data/features/lib/step_definitions/ruby_steps.rb +3 -0
  323. data/features/lib/step_definitions/wire_steps.rb +58 -0
  324. data/features/lib/support/env.rb +21 -0
  325. data/features/lib/support/fake_wire_server.rb +80 -0
  326. data/features/lib/support/feature_factory.rb +67 -0
  327. data/features/lib/support/normalise_output.rb +50 -0
  328. data/gem_tasks/contributors.rake +15 -0
  329. data/gem_tasks/cov.rake +5 -0
  330. data/gem_tasks/cucumber.rake +25 -0
  331. data/gem_tasks/downloads.rb +7 -0
  332. data/gem_tasks/environment.rake +7 -0
  333. data/gem_tasks/examples.rake +11 -0
  334. data/gem_tasks/fix_cr_lf.rake +10 -0
  335. data/gem_tasks/flog.rake +4 -0
  336. data/gem_tasks/rspec.rake +6 -0
  337. data/gem_tasks/sass.rake +4 -0
  338. data/gem_tasks/stats +16 -0
  339. data/gem_tasks/versions.txt +74 -0
  340. data/gherkin-languages.json +2972 -0
  341. data/lib/autotest/cucumber.rb +6 -0
  342. data/lib/autotest/cucumber_mixin.rb +135 -0
  343. data/lib/autotest/cucumber_rails.rb +6 -0
  344. data/lib/autotest/cucumber_rails_rspec.rb +6 -0
  345. data/lib/autotest/cucumber_rails_rspec2.rb +6 -0
  346. data/lib/autotest/cucumber_rspec.rb +6 -0
  347. data/lib/autotest/cucumber_rspec2.rb +6 -0
  348. data/lib/autotest/discover.rb +11 -0
  349. data/lib/cucumber.rb +37 -0
  350. data/lib/cucumber/ast.rb +13 -0
  351. data/lib/cucumber/cli/configuration.rb +146 -0
  352. data/lib/cucumber/cli/main.rb +99 -0
  353. data/lib/cucumber/cli/options.rb +465 -0
  354. data/lib/cucumber/cli/profile_loader.rb +91 -0
  355. data/lib/cucumber/cli/rerun_file.rb +29 -0
  356. data/lib/cucumber/configuration.rb +268 -0
  357. data/lib/cucumber/constantize.rb +45 -0
  358. data/lib/cucumber/core_ext/instance_exec.rb +70 -0
  359. data/lib/cucumber/core_ext/string.rb +9 -0
  360. data/lib/cucumber/deprecate.rb +29 -0
  361. data/lib/cucumber/encoding.rb +5 -0
  362. data/lib/cucumber/errors.rb +51 -0
  363. data/lib/cucumber/events.rb +20 -0
  364. data/lib/cucumber/events/after_test_case.rb +25 -0
  365. data/lib/cucumber/events/after_test_step.rb +30 -0
  366. data/lib/cucumber/events/before_test_case.rb +18 -0
  367. data/lib/cucumber/events/before_test_step.rb +23 -0
  368. data/lib/cucumber/events/bus.rb +86 -0
  369. data/lib/cucumber/events/finished_testing.rb +9 -0
  370. data/lib/cucumber/events/step_match.rb +23 -0
  371. data/lib/cucumber/file_specs.rb +37 -0
  372. data/lib/cucumber/filters.rb +9 -0
  373. data/lib/cucumber/filters/activate_steps.rb +67 -0
  374. data/lib/cucumber/filters/apply_after_hooks.rb +9 -0
  375. data/lib/cucumber/filters/apply_after_step_hooks.rb +12 -0
  376. data/lib/cucumber/filters/apply_around_hooks.rb +12 -0
  377. data/lib/cucumber/filters/apply_before_hooks.rb +9 -0
  378. data/lib/cucumber/filters/gated_receiver.rb +25 -0
  379. data/lib/cucumber/filters/prepare_world.rb +45 -0
  380. data/lib/cucumber/filters/quit.rb +28 -0
  381. data/lib/cucumber/filters/randomizer.rb +45 -0
  382. data/lib/cucumber/filters/retry.rb +32 -0
  383. data/lib/cucumber/filters/tag_limits.rb +45 -0
  384. data/lib/cucumber/filters/tag_limits/test_case_index.rb +31 -0
  385. data/lib/cucumber/filters/tag_limits/verifier.rb +59 -0
  386. data/lib/cucumber/formatter/ansicolor.rb +160 -0
  387. data/lib/cucumber/formatter/backtrace_filter.rb +41 -0
  388. data/lib/cucumber/formatter/console.rb +252 -0
  389. data/lib/cucumber/formatter/cucumber.css +286 -0
  390. data/lib/cucumber/formatter/cucumber.sass +247 -0
  391. data/lib/cucumber/formatter/debug.rb +35 -0
  392. data/lib/cucumber/formatter/duration.rb +13 -0
  393. data/lib/cucumber/formatter/duration_extractor.rb +28 -0
  394. data/lib/cucumber/formatter/event_bus_report.rb +38 -0
  395. data/lib/cucumber/formatter/fail_fast.rb +18 -0
  396. data/lib/cucumber/formatter/fanout.rb +27 -0
  397. data/lib/cucumber/formatter/hook_query_visitor.rb +40 -0
  398. data/lib/cucumber/formatter/html.rb +689 -0
  399. data/lib/cucumber/formatter/ignore_missing_messages.rb +20 -0
  400. data/lib/cucumber/formatter/interceptor.rb +80 -0
  401. data/lib/cucumber/formatter/io.rb +34 -0
  402. data/lib/cucumber/formatter/jquery-min.js +154 -0
  403. data/lib/cucumber/formatter/json.rb +334 -0
  404. data/lib/cucumber/formatter/json_pretty.rb +10 -0
  405. data/lib/cucumber/formatter/junit.rb +238 -0
  406. data/lib/cucumber/formatter/legacy_api/adapter.rb +1016 -0
  407. data/lib/cucumber/formatter/legacy_api/ast.rb +389 -0
  408. data/lib/cucumber/formatter/legacy_api/results.rb +51 -0
  409. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +31 -0
  410. data/lib/cucumber/formatter/pretty.rb +247 -0
  411. data/lib/cucumber/formatter/progress.rb +73 -0
  412. data/lib/cucumber/formatter/rerun.rb +35 -0
  413. data/lib/cucumber/formatter/stepdefs.rb +14 -0
  414. data/lib/cucumber/formatter/steps.rb +49 -0
  415. data/lib/cucumber/formatter/summary.rb +35 -0
  416. data/lib/cucumber/formatter/unicode.rb +53 -0
  417. data/lib/cucumber/formatter/usage.rb +136 -0
  418. data/lib/cucumber/gherkin/data_table_parser.rb +31 -0
  419. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +99 -0
  420. data/lib/cucumber/gherkin/formatter/escaping.rb +17 -0
  421. data/lib/cucumber/gherkin/i18n.rb +15 -0
  422. data/lib/cucumber/gherkin/steps_parser.rb +31 -0
  423. data/lib/cucumber/hooks.rb +99 -0
  424. data/lib/cucumber/load_path.rb +13 -0
  425. data/lib/cucumber/multiline_argument.rb +71 -0
  426. data/lib/cucumber/multiline_argument/data_table.rb +744 -0
  427. data/lib/cucumber/multiline_argument/doc_string.rb +9 -0
  428. data/lib/cucumber/platform.rb +27 -0
  429. data/lib/cucumber/project_initializer.rb +82 -0
  430. data/lib/cucumber/rake/task.rb +168 -0
  431. data/lib/cucumber/rb_support/rb_dsl.rb +119 -0
  432. data/lib/cucumber/rb_support/rb_hook.rb +19 -0
  433. data/lib/cucumber/rb_support/rb_language.rb +216 -0
  434. data/lib/cucumber/rb_support/rb_step_definition.rb +131 -0
  435. data/lib/cucumber/rb_support/rb_transform.rb +59 -0
  436. data/lib/cucumber/rb_support/rb_world.rb +149 -0
  437. data/lib/cucumber/rb_support/snippet.rb +170 -0
  438. data/lib/cucumber/rspec/disable_option_parser.rb +23 -0
  439. data/lib/cucumber/rspec/doubles.rb +19 -0
  440. data/lib/cucumber/running_test_case.rb +134 -0
  441. data/lib/cucumber/runtime.rb +266 -0
  442. data/lib/cucumber/runtime/after_hooks.rb +25 -0
  443. data/lib/cucumber/runtime/before_hooks.rb +27 -0
  444. data/lib/cucumber/runtime/for_programming_languages.rb +66 -0
  445. data/lib/cucumber/runtime/step_hooks.rb +23 -0
  446. data/lib/cucumber/runtime/support_code.rb +151 -0
  447. data/lib/cucumber/runtime/user_interface.rb +85 -0
  448. data/lib/cucumber/step_argument.rb +25 -0
  449. data/lib/cucumber/step_definition_light.rb +20 -0
  450. data/lib/cucumber/step_definitions.rb +13 -0
  451. data/lib/cucumber/step_match.rb +141 -0
  452. data/lib/cucumber/step_match_search.rb +67 -0
  453. data/lib/cucumber/term/ansicolor.rb +110 -0
  454. data/lib/cucumber/unit.rb +11 -0
  455. data/lib/cucumber/version +1 -0
  456. data/lib/mobiusloop/measure.rb +19 -0
  457. data/lib/mobiusloop/mobius_steps.rb +36 -0
  458. data/lib/mobiusloop/objective.rb +10 -0
  459. data/lib/mobiusloop/outcome.rb +99 -0
  460. data/lib/mobiusloop/scale.rb +32 -0
  461. data/lib/simplecov_setup.rb +18 -0
  462. data/major_changes_to_cucumber.md +45 -0
  463. data/mobiusloop.gemspec +47 -0
  464. data/spec/cucumber/cli/configuration_spec.rb +435 -0
  465. data/spec/cucumber/cli/main_spec.rb +83 -0
  466. data/spec/cucumber/cli/options_spec.rb +410 -0
  467. data/spec/cucumber/cli/profile_loader_spec.rb +46 -0
  468. data/spec/cucumber/cli/rerun_spec.rb +85 -0
  469. data/spec/cucumber/configuration_spec.rb +147 -0
  470. data/spec/cucumber/constantize_spec.rb +19 -0
  471. data/spec/cucumber/core_ext/instance_exec_spec.rb +4 -0
  472. data/spec/cucumber/events/bus_spec.rb +94 -0
  473. data/spec/cucumber/file_specs_spec.rb +60 -0
  474. data/spec/cucumber/filters/activate_steps_spec.rb +149 -0
  475. data/spec/cucumber/filters/gated_receiver_spec.rb +47 -0
  476. data/spec/cucumber/filters/retry_spec.rb +79 -0
  477. data/spec/cucumber/filters/tag_limits/test_case_index_spec.rb +38 -0
  478. data/spec/cucumber/filters/tag_limits/verifier_spec.rb +57 -0
  479. data/spec/cucumber/filters/tag_limits_spec.rb +59 -0
  480. data/spec/cucumber/formatter/ansicolor_spec.rb +32 -0
  481. data/spec/cucumber/formatter/debug_spec.rb +64 -0
  482. data/spec/cucumber/formatter/duration_spec.rb +22 -0
  483. data/spec/cucumber/formatter/event_bus_report_spec.rb +88 -0
  484. data/spec/cucumber/formatter/fail_fast_spec.rb +88 -0
  485. data/spec/cucumber/formatter/html_spec.rb +538 -0
  486. data/spec/cucumber/formatter/interceptor_spec.rb +136 -0
  487. data/spec/cucumber/formatter/json_spec.rb +841 -0
  488. data/spec/cucumber/formatter/junit_spec.rb +252 -0
  489. data/spec/cucumber/formatter/legacy_api/adapter_spec.rb +2182 -0
  490. data/spec/cucumber/formatter/pretty_spec.rb +919 -0
  491. data/spec/cucumber/formatter/progress_spec.rb +169 -0
  492. data/spec/cucumber/formatter/rerun_spec.rb +91 -0
  493. data/spec/cucumber/formatter/spec_helper.rb +82 -0
  494. data/spec/cucumber/hooks_spec.rb +30 -0
  495. data/spec/cucumber/multiline_argument/data_table_spec.rb +603 -0
  496. data/spec/cucumber/project_initializer_spec.rb +87 -0
  497. data/spec/cucumber/rake/forked_spec.rb +53 -0
  498. data/spec/cucumber/rake/task_spec.rb +85 -0
  499. data/spec/cucumber/rb_support/rb_language_spec.rb +243 -0
  500. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +199 -0
  501. data/spec/cucumber/rb_support/rb_transform_spec.rb +46 -0
  502. data/spec/cucumber/rb_support/rb_world_spec.rb +47 -0
  503. data/spec/cucumber/rb_support/snippet_spec.rb +137 -0
  504. data/spec/cucumber/running_test_case_spec.rb +139 -0
  505. data/spec/cucumber/runtime/for_programming_languages_spec.rb +36 -0
  506. data/spec/cucumber/runtime/support_code_spec.rb +16 -0
  507. data/spec/cucumber/runtime_spec.rb +20 -0
  508. data/spec/cucumber/sell_cucumbers.feature +19 -0
  509. data/spec/cucumber/step_argument_spec.rb +18 -0
  510. data/spec/cucumber/step_match_search_spec.rb +122 -0
  511. data/spec/cucumber/step_match_spec.rb +85 -0
  512. data/spec/cucumber/world/pending_spec.rb +47 -0
  513. data/spec/cucumber_spec.rb +39 -0
  514. data/spec/spec_helper.rb +29 -0
  515. data/spec/support/standard_step_actions.rb +18 -0
  516. metadata +1007 -0
@@ -0,0 +1,13 @@
1
+ module Cucumber
2
+ module LoadPath
3
+ def add_dirs(*dirs)
4
+ dirs.each do |dir|
5
+ $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir)
6
+ end
7
+ end
8
+
9
+ module_function :add_dirs
10
+ end
11
+ end
12
+
13
+ Cucumber::LoadPath.add_dirs('lib')
@@ -0,0 +1,71 @@
1
+ require 'delegate'
2
+ require 'cucumber/multiline_argument/data_table'
3
+ require 'cucumber/multiline_argument/doc_string'
4
+
5
+ module Cucumber
6
+ module MultilineArgument
7
+
8
+ class << self
9
+ def from_core(node)
10
+ builder.wrap(node)
11
+ end
12
+
13
+ def from(argument, location=nil)
14
+ location ||= Core::Ast::Location.of_caller
15
+ case argument
16
+ when String
17
+ doc_string(argument, 'text/plain', location)
18
+ when Array
19
+ location = location.on_line(argument.first.line..argument.last.line)
20
+ data_table(argument.map{ |row| row.cells }, location)
21
+ when DataTable, DocString, None
22
+ argument
23
+ when nil
24
+ None.new
25
+ else
26
+ raise ArgumentError, "Don't know how to convert #{argument.class} #{argument.inspect} into a MultilineArgument"
27
+ end
28
+ end
29
+
30
+ def doc_string(argument, content_type, location)
31
+ builder.doc_string(Core::Ast::DocString.new(argument, content_type, location))
32
+ end
33
+
34
+ def data_table(data, location)
35
+ builder.data_table(Core::Ast::DataTable.new(data, location))
36
+ end
37
+
38
+ private
39
+
40
+ def builder
41
+ @builder ||= Builder.new
42
+ end
43
+
44
+ class Builder
45
+ def wrap(node)
46
+ @result = None.new
47
+ node.describe_to(self)
48
+ @result
49
+ end
50
+
51
+ def doc_string(node, *args)
52
+ @result = DocString.new(node)
53
+ end
54
+
55
+ def data_table(node, *args)
56
+ @result = DataTable.new(node)
57
+ end
58
+ end
59
+ end
60
+
61
+ class None
62
+ def append_to(array)
63
+ end
64
+
65
+ def describe_to(visitor)
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ require 'cucumber/ast'
@@ -0,0 +1,744 @@
1
+ require 'forwardable'
2
+ require 'cucumber/gherkin/data_table_parser'
3
+ require 'cucumber/gherkin/formatter/escaping'
4
+ require 'cucumber/core/ast/describes_itself'
5
+
6
+ module Cucumber
7
+ module MultilineArgument
8
+ # Step Definitions that match a plain text Step with a multiline argument table
9
+ # will receive it as an instance of Table. A Table object holds the data of a
10
+ # table parsed from a feature file and lets you access and manipulate the data
11
+ # in different ways.
12
+ #
13
+ # For example:
14
+ #
15
+ # Given I have:
16
+ # | a | b |
17
+ # | c | d |
18
+ #
19
+ # And a matching StepDefinition:
20
+ #
21
+ # Given /I have:/ do |table|
22
+ # data = table.raw
23
+ # end
24
+ #
25
+ # This will store <tt>[['a', 'b'], ['c', 'd']]</tt> in the <tt>data</tt> variable.
26
+ #
27
+ class DataTable
28
+ class Different < StandardError
29
+ def initialize(table)
30
+ super("Tables were not identical:\n#{table}")
31
+ end
32
+ end
33
+
34
+ class Builder
35
+ attr_reader :rows
36
+
37
+ def initialize
38
+ @rows = []
39
+ end
40
+
41
+ def row(row)
42
+ @rows << row
43
+ end
44
+
45
+ def eof
46
+ end
47
+ end
48
+
49
+ include Enumerable
50
+ include Core::Ast::DescribesItself
51
+ extend Forwardable
52
+
53
+ NULL_CONVERSIONS = Hash.new({ :strict => false, :proc => lambda{ |cell_value| cell_value } }).freeze
54
+
55
+ attr_accessor :file
56
+ def_delegator :@ast_table, :location
57
+
58
+ def self.default_arg_name #:nodoc:
59
+ "table"
60
+ end
61
+
62
+ class << self
63
+ def from(data, location = Core::Ast::Location.of_caller)
64
+ case data
65
+ when Array
66
+ from_array(data, location)
67
+ when String
68
+ parse(data, location)
69
+ else
70
+ raise ArgumentError, "expected data to be a String or an Array."
71
+ end
72
+ end
73
+
74
+ private
75
+ def parse(text, location = Core::Ast::Location.of_caller)
76
+ builder = Builder.new
77
+ parser = Cucumber::Gherkin::DataTableParser.new(builder)
78
+ parser.parse(text)
79
+ from_array(builder.rows, location)
80
+ end
81
+
82
+ def from_array(data, location = Core::Ast::Location.of_caller)
83
+ new Core::Ast::DataTable.new(data, location)
84
+ end
85
+ end
86
+
87
+
88
+ # @param data [Core::Ast::DataTable] the data for the table
89
+ # @param conversion_procs [Hash] see map_columns!
90
+ # @param header_mappings [Hash] see map_headers!
91
+ # @param header_conversion_proc [Proc] see map_headers!
92
+ def initialize(data, conversion_procs = NULL_CONVERSIONS.dup, header_mappings = {}, header_conversion_proc = nil)
93
+ raise ArgumentError, "data must be a Core::Ast::DataTable" unless data.kind_of? Core::Ast::DataTable
94
+ ast_table = data
95
+ # Verify that it's square
96
+ ast_table.transpose
97
+ @cell_matrix = create_cell_matrix(ast_table)
98
+ @conversion_procs = conversion_procs
99
+ @header_mappings = header_mappings
100
+ @header_conversion_proc = header_conversion_proc
101
+ @ast_table = ast_table
102
+ end
103
+
104
+ def append_to(array)
105
+ array << self
106
+ end
107
+
108
+ def to_step_definition_arg
109
+ dup
110
+ end
111
+
112
+ # Creates a copy of this table, inheriting any column and header mappings
113
+ # registered with #map_column! and #map_headers!.
114
+ #
115
+ def dup
116
+ self.class.new(Core::Ast::DataTable.new(raw, location), @conversion_procs.dup, @header_mappings.dup, @header_conversion_proc)
117
+ end
118
+
119
+ # Returns a new, transposed table. Example:
120
+ #
121
+ # | a | 7 | 4 |
122
+ # | b | 9 | 2 |
123
+ #
124
+ # Gets converted into the following:
125
+ #
126
+ # | a | b |
127
+ # | 7 | 9 |
128
+ # | 4 | 2 |
129
+ #
130
+ def transpose
131
+ self.class.new(Core::Ast::DataTable.new(raw.transpose, location), @conversion_procs.dup, @header_mappings.dup, @header_conversion_proc)
132
+ end
133
+
134
+ # Converts this table into an Array of Hash where the keys of each
135
+ # Hash are the headers in the table. For example, a Table built from
136
+ # the following plain text:
137
+ #
138
+ # | a | b | sum |
139
+ # | 2 | 3 | 5 |
140
+ # | 7 | 9 | 16 |
141
+ #
142
+ # Gets converted into the following:
143
+ #
144
+ # [{'a' => '2', 'b' => '3', 'sum' => '5'}, {'a' => '7', 'b' => '9', 'sum' => '16'}]
145
+ #
146
+ # Use #map_column! to specify how values in a column are converted.
147
+ #
148
+ def hashes
149
+ @hashes ||= build_hashes
150
+ end
151
+
152
+ # Converts this table into an Array of Hashes where the keys are symbols.
153
+ # For example, a Table built from the following plain text:
154
+ #
155
+ # | foo | Bar | Foo Bar |
156
+ # | 2 | 3 | 5 |
157
+ # | 7 | 9 | 16 |
158
+ #
159
+ # Gets converted into the following:
160
+ #
161
+ # [{:foo => '2', :bar => '3', :foo_bar => '5'}, {:foo => '7', :bar => '9', :foo_bar => '16'}]
162
+ #
163
+ def symbolic_hashes
164
+ @header_conversion_proc = lambda { |h| symbolize_key(h) }
165
+ @symbolic_hashes ||= build_hashes
166
+ end
167
+
168
+ # Converts this table into a Hash where the first column is
169
+ # used as keys and the second column is used as values
170
+ #
171
+ # | a | 2 |
172
+ # | b | 3 |
173
+ #
174
+ # Gets converted into the following:
175
+ #
176
+ # {'a' => '2', 'b' => '3'}
177
+ #
178
+ # The table must be exactly two columns wide
179
+ #
180
+ def rows_hash
181
+ return @rows_hash if @rows_hash
182
+ verify_table_width(2)
183
+ @rows_hash = self.transpose.hashes[0]
184
+ end
185
+
186
+ # Gets the raw data of this table. For example, a Table built from
187
+ # the following plain text:
188
+ #
189
+ # | a | b |
190
+ # | c | d |
191
+ #
192
+ # gets converted into the following:
193
+ #
194
+ # [['a', 'b'], ['c', 'd']]
195
+ #
196
+ def raw
197
+ cell_matrix.map do |row|
198
+ row.map do |cell|
199
+ cell.value
200
+ end
201
+ end
202
+ end
203
+
204
+ def column_names #:nodoc:
205
+ @col_names ||= cell_matrix[0].map { |cell| cell.value }
206
+ end
207
+
208
+ def rows
209
+ hashes.map do |hash|
210
+ hash.values_at *headers
211
+ end
212
+ end
213
+
214
+ def each_cells_row(&proc) #:nodoc:
215
+ cells_rows.each(&proc)
216
+ end
217
+
218
+ # Matches +pattern+ against the header row of the table.
219
+ # This is used especially for argument transforms.
220
+ #
221
+ # Example:
222
+ # | column_1_name | column_2_name |
223
+ # | x | y |
224
+ #
225
+ # table.match(/table:column_1_name,column_2_name/) #=> non-nil
226
+ #
227
+ # Note: must use 'table:' prefix on match
228
+ def match(pattern)
229
+ header_to_match = "table:#{headers.join(',')}"
230
+ pattern.match(header_to_match)
231
+ end
232
+
233
+ # Redefines the table headers. This makes it possible to use
234
+ # prettier and more flexible header names in the features. The
235
+ # keys of +mappings+ are Strings or regular expressions
236
+ # (anything that responds to #=== will work) that may match
237
+ # column headings in the table. The values of +mappings+ are
238
+ # desired names for the columns.
239
+ #
240
+ # Example:
241
+ #
242
+ # | Phone Number | Address |
243
+ # | 123456 | xyz |
244
+ # | 345678 | abc |
245
+ #
246
+ # A StepDefinition receiving this table can then map the columns
247
+ # with both Regexp and String:
248
+ #
249
+ # table.map_headers!(/phone( number)?/i => :phone, 'Address' => :address)
250
+ # table.hashes
251
+ # # => [{:phone => '123456', :address => 'xyz'}, {:phone => '345678', :address => 'abc'}]
252
+ #
253
+ # You may also pass in a block if you wish to convert all of the headers:
254
+ #
255
+ # table.map_headers! { |header| header.downcase }
256
+ # table.hashes.keys
257
+ # # => ['phone number', 'address']
258
+ #
259
+ # When a block is passed in along with a hash then the mappings in the hash take precendence:
260
+ #
261
+ # table.map_headers!('Address' => 'ADDRESS') { |header| header.downcase }
262
+ # table.hashes.keys
263
+ # # => ['phone number', 'ADDRESS']
264
+ #
265
+ def map_headers!(mappings={}, &block)
266
+ # TODO: Remove this method for 2.0
267
+ clear_cache!
268
+ @header_mappings = mappings
269
+ @header_conversion_proc = block
270
+ end
271
+
272
+ # Returns a new Table where the headers are redefined. See #map_headers!
273
+ def map_headers(mappings={}, &block)
274
+ self.class.new(Core::Ast::DataTable.new(raw, location), @conversion_procs.dup, mappings, block)
275
+ end
276
+
277
+ # Change how #hashes converts column values. The +column_name+ argument identifies the column
278
+ # and +conversion_proc+ performs the conversion for each cell in that column. If +strict+ is
279
+ # true, an error will be raised if the column named +column_name+ is not found. If +strict+
280
+ # is false, no error will be raised. Example:
281
+ #
282
+ # Given /^an expense report for (.*) with the following posts:$/ do |table|
283
+ # posts_table.map_column!('amount') { |a| a.to_i }
284
+ # posts_table.hashes.each do |post|
285
+ # # post['amount'] is a Fixnum, rather than a String
286
+ # end
287
+ # end
288
+ #
289
+ def map_column!(column_name, strict=true, &conversion_proc)
290
+ # TODO: Remove this method for 2.0
291
+ @conversion_procs[column_name.to_s] = { :strict => strict, :proc => conversion_proc }
292
+ self
293
+ end
294
+
295
+ # Returns a new Table with an additional column mapping. See #map_column!
296
+ def map_column(column_name, strict=true, &conversion_proc)
297
+ conversion_procs = @conversion_procs.dup
298
+ conversion_procs[column_name.to_s] = { :strict => strict, :proc => conversion_proc }
299
+ self.class.new(Core::Ast::DataTable.new(raw, location), conversion_procs, @header_mappings.dup, @header_conversion_proc)
300
+ end
301
+
302
+ # Compares +other_table+ to self. If +other_table+ contains columns
303
+ # and/or rows that are not in self, new columns/rows are added at the
304
+ # relevant positions, marking the cells in those rows/columns as
305
+ # <tt>surplus</tt>. Likewise, if +other_table+ lacks columns and/or
306
+ # rows that are present in self, these are marked as <tt>missing</tt>.
307
+ #
308
+ # <tt>surplus</tt> and <tt>missing</tt> cells are recognised by formatters
309
+ # and displayed so that it's easy to read the differences.
310
+ #
311
+ # Cells that are different, but <em>look</em> identical (for example the
312
+ # boolean true and the string "true") are converted to their Object#inspect
313
+ # representation and preceded with (i) - to make it easier to identify
314
+ # where the difference actually is.
315
+ #
316
+ # Since all tables that are passed to StepDefinitions always have String
317
+ # objects in their cells, you may want to use #map_column! before calling
318
+ # #diff!. You can use #map_column! on either of the tables.
319
+ #
320
+ # A Different error is raised if there are missing rows or columns, or
321
+ # surplus rows. An error is <em>not</em> raised for surplus columns. An
322
+ # error is <em>not</em> raised for misplaced (out of sequence) columns.
323
+ # Whether to raise or not raise can be changed by setting values in
324
+ # +options+ to true or false:
325
+ #
326
+ # * <tt>missing_row</tt> : Raise on missing rows (defaults to true)
327
+ # * <tt>surplus_row</tt> : Raise on surplus rows (defaults to true)
328
+ # * <tt>missing_col</tt> : Raise on missing columns (defaults to true)
329
+ # * <tt>surplus_col</tt> : Raise on surplus columns (defaults to false)
330
+ # * <tt>misplaced_col</tt> : Raise on misplaced columns (defaults to false)
331
+ #
332
+ # The +other_table+ argument can be another Table, an Array of Array or
333
+ # an Array of Hash (similar to the structure returned by #hashes).
334
+ #
335
+ # Calling this method is particularly useful in <tt>Then</tt> steps that take
336
+ # a Table argument, if you want to compare that table to some actual values.
337
+ #
338
+ def diff!(other_table, options={})
339
+ options = {
340
+ :missing_row => true,
341
+ :surplus_row => true,
342
+ :missing_col => true,
343
+ :surplus_col => false,
344
+ :misplaced_col => false
345
+ }.merge(options)
346
+
347
+ other_table = ensure_table(other_table)
348
+ other_table.convert_headers!
349
+ other_table.convert_columns!
350
+ ensure_green!
351
+
352
+ convert_headers!
353
+ convert_columns!
354
+
355
+ original_width = cell_matrix[0].length
356
+ @cell_matrix, other_table_cell_matrix = pad_and_match(@cell_matrix, other_table.cell_matrix)
357
+ padded_width = cell_matrix[0].length
358
+
359
+ missing_col = cell_matrix[0].detect{|cell| cell.status == :undefined}
360
+ surplus_col = padded_width > original_width
361
+ misplaced_col = cell_matrix[0] != other_table.cell_matrix[0]
362
+
363
+ require_diff_lcs
364
+ cell_matrix.extend(Diff::LCS)
365
+ changes = cell_matrix.diff(other_table_cell_matrix).flatten
366
+
367
+ inserted = 0
368
+ missing = 0
369
+
370
+ row_indices = Array.new(other_table_cell_matrix.length) {|n| n}
371
+
372
+ last_change = nil
373
+ missing_row_pos = nil
374
+ insert_row_pos = nil
375
+
376
+ changes.each do |change|
377
+ if(change.action == '-')
378
+ missing_row_pos = change.position + inserted
379
+ cell_matrix[missing_row_pos].each{|cell| cell.status = :undefined}
380
+ row_indices.insert(missing_row_pos, nil)
381
+ missing += 1
382
+ else # '+'
383
+ insert_row_pos = change.position + missing
384
+ inserted_row = change.element
385
+ inserted_row.each{|cell| cell.status = :comment}
386
+ cell_matrix.insert(insert_row_pos, inserted_row)
387
+ row_indices[insert_row_pos] = nil
388
+ inspect_rows(cell_matrix[missing_row_pos], inserted_row) if last_change && last_change.action == '-'
389
+ inserted += 1
390
+ end
391
+ last_change = change
392
+ end
393
+
394
+ other_table_cell_matrix.each_with_index do |other_row, i|
395
+ row_index = row_indices.index(i)
396
+ row = cell_matrix[row_index] if row_index
397
+ if row
398
+ (original_width..padded_width).each do |col_index|
399
+ surplus_cell = other_row[col_index]
400
+ row[col_index].value = surplus_cell.value if row[col_index]
401
+ end
402
+ end
403
+ end
404
+
405
+ clear_cache!
406
+ should_raise =
407
+ missing_row_pos && options[:missing_row] ||
408
+ insert_row_pos && options[:surplus_row] ||
409
+ missing_col && options[:missing_col] ||
410
+ surplus_col && options[:surplus_col] ||
411
+ misplaced_col && options[:misplaced_col]
412
+ raise Different.new(self) if should_raise
413
+ end
414
+
415
+ def to_hash(cells) #:nodoc:
416
+ hash = Hash.new do |hash, key|
417
+ hash[key.to_s] if key.is_a?(Symbol)
418
+ end
419
+ column_names.each_with_index do |column_name, column_index|
420
+ hash[column_name] = cells.value(column_index)
421
+ end
422
+ hash
423
+ end
424
+
425
+ def index(cells) #:nodoc:
426
+ cells_rows.index(cells)
427
+ end
428
+
429
+ def verify_column(column_name) #:nodoc:
430
+ raise %{The column named "#{column_name}" does not exist} unless raw[0].include?(column_name)
431
+ end
432
+
433
+ def verify_table_width(width) #:nodoc:
434
+ raise %{The table must have exactly #{width} columns} unless raw[0].size == width
435
+ end
436
+
437
+ def has_text?(text) #:nodoc:
438
+ raw.flatten.compact.detect{|cell_value| cell_value.index(text)}
439
+ end
440
+
441
+ def cells_rows #:nodoc:
442
+ @rows ||= cell_matrix.map do |cell_row|
443
+ Cells.new(self, cell_row)
444
+ end
445
+ end
446
+
447
+ def headers #:nodoc:
448
+ raw.first
449
+ end
450
+
451
+ def header_cell(col) #:nodoc:
452
+ cells_rows[0][col]
453
+ end
454
+
455
+ def cell_matrix #:nodoc:
456
+ @cell_matrix
457
+ end
458
+
459
+ def col_width(col) #:nodoc:
460
+ columns[col].__send__(:width)
461
+ end
462
+
463
+ def to_s(options = {}) #:nodoc:
464
+ # TODO: factor out this code so we don't depend on such a big lump of old cruft
465
+ require 'cucumber/formatter/pretty'
466
+ require 'cucumber/formatter/legacy_api/adapter'
467
+ options = {:color => true, :indent => 2, :prefixes => TO_S_PREFIXES}.merge(options)
468
+ io = StringIO.new
469
+
470
+ c = Cucumber::Term::ANSIColor.coloring?
471
+ Cucumber::Term::ANSIColor.coloring = options[:color]
472
+ formatter = Formatter::Pretty.new(nil, io, options)
473
+ formatter.instance_variable_set('@indent', options[:indent])
474
+ Formatter::LegacyApi::Ast::MultilineArg.for(self).accept(Formatter::Fanout.new([formatter]))
475
+ Cucumber::Term::ANSIColor.coloring = c
476
+ io.rewind
477
+ s = "\n" + io.read + (" " * (options[:indent] - 2))
478
+ s
479
+ end
480
+
481
+ def description_for_visitors
482
+ :legacy_table
483
+ end
484
+
485
+ def columns #:nodoc:
486
+ @columns ||= cell_matrix.transpose.map do |cell_row|
487
+ Cells.new(self, cell_row)
488
+ end
489
+ end
490
+
491
+ def to_json(*args)
492
+ raw.to_json(*args)
493
+ end
494
+
495
+ private
496
+
497
+ TO_S_PREFIXES = Hash.new(' ')
498
+ TO_S_PREFIXES[:comment] = '(+) '
499
+ TO_S_PREFIXES[:undefined] = '(-) '
500
+
501
+ protected
502
+
503
+ def build_hashes
504
+ convert_headers!
505
+ convert_columns!
506
+ cells_rows[1..-1].map do |row|
507
+ row.to_hash
508
+ end
509
+ end
510
+
511
+ def inspect_rows(missing_row, inserted_row) #:nodoc:
512
+ missing_row.each_with_index do |missing_cell, col|
513
+ inserted_cell = inserted_row[col]
514
+ if(missing_cell.value != inserted_cell.value && (missing_cell.value.to_s == inserted_cell.value.to_s))
515
+ missing_cell.inspect!
516
+ inserted_cell.inspect!
517
+ end
518
+ end
519
+ end
520
+
521
+ def create_cell_matrix(ast_table) #:nodoc:
522
+ ast_table.raw.map do |raw_row|
523
+ line = raw_row.line rescue -1
524
+ raw_row.map do |raw_cell|
525
+ Cell.new(raw_cell, self, line)
526
+ end
527
+ end
528
+ end
529
+
530
+ def convert_columns! #:nodoc:
531
+ @conversion_procs.each do |column_name, conversion_proc|
532
+ verify_column(column_name) if conversion_proc[:strict]
533
+ end
534
+
535
+ cell_matrix.transpose.each do |col|
536
+ column_name = col[0].value
537
+ conversion_proc = @conversion_procs[column_name][:proc]
538
+ col[1..-1].each do |cell|
539
+ cell.value = conversion_proc.call(cell.value)
540
+ end
541
+ end
542
+ end
543
+
544
+ def convert_headers! #:nodoc:
545
+ header_cells = cell_matrix[0]
546
+
547
+ if @header_conversion_proc
548
+ header_values = header_cells.map { |cell| cell.value } - @header_mappings.keys
549
+ @header_mappings = @header_mappings.merge(Hash[*header_values.zip(header_values.map(&@header_conversion_proc)).flatten])
550
+ end
551
+
552
+ @header_mappings.each_pair do |pre, post|
553
+ mapped_cells = header_cells.select { |cell| pre === cell.value }
554
+ raise "No headers matched #{pre.inspect}" if mapped_cells.empty?
555
+ raise "#{mapped_cells.length} headers matched #{pre.inspect}: #{mapped_cells.map { |c| c.value }.inspect}" if mapped_cells.length > 1
556
+ mapped_cells[0].value = post
557
+ if @conversion_procs.has_key?(pre)
558
+ @conversion_procs[post] = @conversion_procs.delete(pre)
559
+ end
560
+ end
561
+ end
562
+
563
+ def require_diff_lcs #:nodoc:
564
+ begin
565
+ require 'diff/lcs'
566
+ rescue LoadError => e
567
+ e.message << "\n Please gem install diff-lcs\n"
568
+ raise e
569
+ end
570
+ end
571
+
572
+ def clear_cache! #:nodoc:
573
+ @hashes = @rows_hash = @col_names = @rows = @columns = nil
574
+ end
575
+
576
+ # Pads two cell matrices to same column width and matches columns according to header value.
577
+ # The first cell matrix is the reference matrix with the second matrix matched against it.
578
+ def pad_and_match(a_cell_matrix, other_cell_matrix) #:nodoc:
579
+ clear_cache!
580
+ cols = a_cell_matrix.transpose
581
+ unmatched_cols = other_cell_matrix.transpose
582
+
583
+
584
+ header_values = cols.map(&:first)
585
+ matched_cols = []
586
+
587
+ header_values.each_with_index do |v, i|
588
+ mapped_index = unmatched_cols.index{|unmapped_col| unmapped_col.first == v}
589
+ if (mapped_index)
590
+ matched_cols << unmatched_cols.delete_at(mapped_index)
591
+ else
592
+ mark_as_missing(cols[i])
593
+ empty_col = other_cell_matrix.collect {SurplusCell.new(nil, self, -1)}
594
+ empty_col.first.value = v
595
+ matched_cols << empty_col
596
+ end
597
+ end
598
+
599
+
600
+ empty_col = cell_matrix.collect {SurplusCell.new(nil, self, -1)}
601
+ unmatched_cols.each do
602
+ cols << empty_col
603
+ end
604
+
605
+ return cols.transpose, (matched_cols + unmatched_cols).transpose
606
+ end
607
+
608
+ def ensure_table(table_or_array) #:nodoc:
609
+ return table_or_array if DataTable === table_or_array
610
+ DataTable.from(table_or_array)
611
+ end
612
+
613
+ def ensure_array_of_array(array)
614
+ Hash === array[0] ? hashes_to_array(array) : array
615
+ end
616
+
617
+ def hashes_to_array(hashes) #:nodoc:
618
+ header = hashes[0].keys.sort
619
+ [header] + hashes.map{|hash| header.map{|key| hash[key]}}
620
+ end
621
+
622
+ def ensure_green! #:nodoc:
623
+ each_cell{|cell| cell.status = :passed}
624
+ end
625
+
626
+ def each_cell(&proc) #:nodoc:
627
+ cell_matrix.each{|row| row.each(&proc)}
628
+ end
629
+
630
+ def mark_as_missing(col) #:nodoc:
631
+ col.each do |cell|
632
+ cell.status = :undefined
633
+ end
634
+ end
635
+
636
+ def symbolize_key(key)
637
+ key.downcase.tr(' ', '_').to_sym
638
+ end
639
+
640
+ # Represents a row of cells or columns of cells
641
+ class Cells #:nodoc:
642
+ include Enumerable
643
+ include Cucumber::Gherkin::Formatter::Escaping
644
+
645
+ attr_reader :exception
646
+
647
+ def initialize(table, cells)
648
+ @table, @cells = table, cells
649
+ end
650
+
651
+ def accept(visitor)
652
+ return if Cucumber.wants_to_quit
653
+ each do |cell|
654
+ visitor.visit_table_cell(cell)
655
+ end
656
+ nil
657
+ end
658
+
659
+ # For testing only
660
+ def to_sexp #:nodoc:
661
+ [:row, line, *@cells.map{|cell| cell.to_sexp}]
662
+ end
663
+
664
+ def to_hash #:nodoc:
665
+ @to_hash ||= @table.to_hash(self)
666
+ end
667
+
668
+ def value(n) #:nodoc:
669
+ self[n].value
670
+ end
671
+
672
+ def [](n)
673
+ @cells[n]
674
+ end
675
+
676
+ def line
677
+ @cells[0].line
678
+ end
679
+
680
+ def dom_id
681
+ "row_#{line}"
682
+ end
683
+
684
+ def each(&proc)
685
+ @cells.each(&proc)
686
+ end
687
+
688
+ private
689
+
690
+ def index
691
+ @table.index(self)
692
+ end
693
+
694
+ def width
695
+ map{|cell| cell.value ? escape_cell(cell.value.to_s).unpack('U*').length : 0}.max
696
+ end
697
+ end
698
+
699
+ class Cell #:nodoc:
700
+ attr_reader :line, :table
701
+ attr_accessor :status, :value
702
+
703
+ def initialize(value, table, line)
704
+ @value, @table, @line = value, table, line
705
+ end
706
+
707
+ def inspect!
708
+ @value = "(i) #{value.inspect}"
709
+ end
710
+
711
+ def ==(o)
712
+ SurplusCell === o || value == o.value
713
+ end
714
+
715
+ def eql?(o)
716
+ self == o
717
+ end
718
+
719
+ def hash
720
+ 0
721
+ end
722
+
723
+ # For testing only
724
+ def to_sexp #:nodoc:
725
+ [:cell, @value]
726
+ end
727
+ end
728
+
729
+ class SurplusCell < Cell #:nodoc:
730
+ def status
731
+ :comment
732
+ end
733
+
734
+ def ==(o)
735
+ true
736
+ end
737
+
738
+ def hash
739
+ 0
740
+ end
741
+ end
742
+ end
743
+ end
744
+ end