ruby_raider 1.1.4 → 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 (278) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/e2e_tests.yml +58 -0
  3. data/.github/workflows/integration.yml +4 -6
  4. data/.github/workflows/reek.yml +6 -5
  5. data/.github/workflows/release.yml +175 -0
  6. data/.github/workflows/rubocop.yml +7 -6
  7. data/.github/workflows/steep.yml +21 -0
  8. data/.github/workflows/system_tests.yml +83 -0
  9. data/.gitignore +1 -1
  10. data/.reek.yml +46 -4
  11. data/.rubocop.yml +24 -0
  12. data/.ruby-version +1 -1
  13. data/README.md +140 -77
  14. data/RELEASE.md +412 -0
  15. data/RELEASE_QUICK_GUIDE.md +77 -0
  16. data/Steepfile +22 -0
  17. data/assets/ruby_raider_logo.svg +51 -0
  18. data/bin/release +186 -0
  19. data/lib/adopter/adopt_menu.rb +146 -0
  20. data/lib/adopter/converters/base_converter.rb +84 -0
  21. data/lib/adopter/converters/identity_converter.rb +53 -0
  22. data/lib/adopter/migration_plan.rb +74 -0
  23. data/lib/adopter/migrator.rb +96 -0
  24. data/lib/adopter/plan_builder.rb +275 -0
  25. data/lib/adopter/project_analyzer.rb +252 -0
  26. data/lib/adopter/project_detector.rb +157 -0
  27. data/lib/commands/adopt_commands.rb +42 -0
  28. data/lib/commands/plugin_commands.rb +0 -2
  29. data/lib/commands/scaffolding_commands.rb +220 -37
  30. data/lib/commands/utility_commands.rb +82 -2
  31. data/lib/generators/automation/automation_generator.rb +0 -7
  32. data/lib/generators/automation/templates/account.tt +9 -5
  33. data/lib/generators/automation/templates/appium_caps.tt +60 -6
  34. data/lib/generators/automation/templates/home.tt +4 -4
  35. data/lib/generators/automation/templates/login.tt +61 -4
  36. data/lib/generators/automation/templates/page.tt +13 -7
  37. data/lib/generators/automation/templates/partials/element.tt +1 -1
  38. data/lib/generators/automation/templates/partials/home_page_selector.tt +4 -4
  39. data/lib/generators/automation/templates/partials/initialize_selector.tt +3 -8
  40. data/lib/generators/automation/templates/partials/pdp_page_selector.tt +4 -4
  41. data/lib/generators/automation/templates/partials/url_methods.tt +0 -1
  42. data/lib/generators/automation/templates/partials/visit_method.tt +11 -1
  43. data/lib/generators/automation/templates/pdp.tt +1 -1
  44. data/lib/generators/common_generator.rb +12 -0
  45. data/lib/generators/cucumber/cucumber_generator.rb +36 -0
  46. data/lib/generators/cucumber/templates/accessibility_feature.tt +5 -0
  47. data/lib/generators/cucumber/templates/accessibility_steps.tt +21 -0
  48. data/lib/generators/cucumber/templates/cucumber.tt +8 -1
  49. data/lib/generators/cucumber/templates/env.tt +6 -4
  50. data/lib/generators/cucumber/templates/feature.tt +0 -4
  51. data/lib/generators/cucumber/templates/partials/appium_env.tt +5 -0
  52. data/lib/generators/cucumber/templates/partials/capybara_env.tt +38 -0
  53. data/lib/generators/cucumber/templates/partials/capybara_world.tt +6 -0
  54. data/lib/generators/cucumber/templates/partials/driver_world.tt +1 -4
  55. data/lib/generators/cucumber/templates/partials/mobile_steps.tt +2 -2
  56. data/lib/generators/cucumber/templates/partials/selenium_env.tt +22 -35
  57. data/lib/generators/cucumber/templates/partials/watir_env.tt +20 -1
  58. data/lib/generators/cucumber/templates/partials/web_steps.tt +10 -15
  59. data/lib/generators/cucumber/templates/performance_feature.tt +5 -0
  60. data/lib/generators/cucumber/templates/performance_steps.tt +17 -0
  61. data/lib/generators/cucumber/templates/steps.tt +2 -2
  62. data/lib/generators/cucumber/templates/visual_feature.tt +5 -0
  63. data/lib/generators/cucumber/templates/visual_steps.tt +19 -0
  64. data/lib/generators/cucumber/templates/world.tt +5 -3
  65. data/lib/generators/generator.rb +50 -7
  66. data/lib/generators/helper_generator.rb +39 -9
  67. data/lib/generators/infrastructure/github_generator.rb +6 -0
  68. data/lib/generators/infrastructure/templates/github.tt +12 -8
  69. data/lib/generators/infrastructure/templates/github_appium.tt +108 -0
  70. data/lib/generators/infrastructure/templates/gitlab.tt +6 -3
  71. data/lib/generators/invoke_generators.rb +43 -9
  72. data/lib/generators/menu_generator.rb +122 -11
  73. data/lib/generators/minitest/minitest_generator.rb +35 -0
  74. data/lib/generators/minitest/templates/accessibility_test.tt +26 -0
  75. data/lib/generators/minitest/templates/performance_test.tt +18 -0
  76. data/lib/generators/minitest/templates/test.tt +64 -0
  77. data/lib/generators/minitest/templates/visual_test.tt +23 -0
  78. data/lib/generators/rspec/rspec_generator.rb +16 -4
  79. data/lib/generators/rspec/templates/accessibility_spec.tt +25 -0
  80. data/lib/generators/rspec/templates/performance_spec.tt +18 -0
  81. data/lib/generators/rspec/templates/spec.tt +13 -41
  82. data/lib/generators/rspec/templates/visual_spec.tt +20 -0
  83. data/lib/generators/template_renderer/partial_cache.rb +126 -0
  84. data/lib/generators/template_renderer/partial_resolver.rb +110 -0
  85. data/lib/generators/template_renderer/template_error.rb +50 -0
  86. data/lib/generators/template_renderer.rb +106 -0
  87. data/lib/generators/templates/common/config.tt +2 -2
  88. data/lib/generators/templates/common/gemfile.tt +36 -9
  89. data/lib/generators/templates/common/git_ignore.tt +6 -1
  90. data/lib/generators/templates/common/partials/mobile_config.tt +5 -1
  91. data/lib/generators/templates/common/partials/web_config.tt +17 -8
  92. data/lib/generators/templates/common/rakefile.tt +36 -0
  93. data/lib/generators/templates/common/read_me.tt +43 -91
  94. data/lib/generators/templates/common/rspec.tt +3 -0
  95. data/lib/generators/templates/common/ruby_version.tt +1 -0
  96. data/lib/generators/templates/helpers/allure_helper.tt +13 -2
  97. data/lib/generators/templates/helpers/browser_helper.tt +13 -2
  98. data/lib/generators/templates/helpers/capybara_helper.tt +32 -0
  99. data/lib/generators/templates/helpers/debug_helper.tt +190 -0
  100. data/lib/generators/templates/helpers/driver_helper.tt +3 -11
  101. data/lib/generators/templates/helpers/partials/allure_imports.tt +3 -1
  102. data/lib/generators/templates/helpers/partials/allure_requirements.tt +3 -1
  103. data/lib/generators/templates/helpers/partials/appium_driver.tt +44 -0
  104. data/lib/generators/templates/helpers/partials/browserstack_config.tt +13 -0
  105. data/lib/generators/templates/helpers/partials/debug_diagnostics.tt +7 -0
  106. data/lib/generators/templates/helpers/partials/debug_start.tt +7 -0
  107. data/lib/generators/templates/helpers/partials/driver_and_options.tt +5 -115
  108. data/lib/generators/templates/helpers/partials/quit_driver.tt +3 -1
  109. data/lib/generators/templates/helpers/partials/screenshot.tt +3 -1
  110. data/lib/generators/templates/helpers/partials/selenium_driver.tt +26 -0
  111. data/lib/generators/templates/helpers/partials/video_start.tt +9 -0
  112. data/lib/generators/templates/helpers/partials/video_stop.tt +4 -0
  113. data/lib/generators/templates/helpers/performance_helper.tt +57 -0
  114. data/lib/generators/templates/helpers/spec_helper.tt +72 -10
  115. data/lib/generators/templates/helpers/test_helper.tt +94 -0
  116. data/lib/generators/templates/helpers/video_helper.tt +270 -0
  117. data/lib/generators/templates/helpers/visual_helper.tt +39 -46
  118. data/lib/llm/client.rb +79 -0
  119. data/lib/llm/config.rb +57 -0
  120. data/lib/llm/prompts.rb +84 -0
  121. data/lib/llm/provider.rb +27 -0
  122. data/lib/llm/providers/anthropic_provider.rb +43 -0
  123. data/lib/llm/providers/ollama_provider.rb +56 -0
  124. data/lib/llm/providers/openai_provider.rb +42 -0
  125. data/lib/llm/response_parser.rb +67 -0
  126. data/lib/plugin/plugin.rb +22 -20
  127. data/lib/plugin/plugin_exposer.rb +16 -38
  128. data/lib/ruby_raider.rb +51 -11
  129. data/lib/scaffolding/crud_generator.rb +94 -0
  130. data/lib/scaffolding/dry_run_presenter.rb +16 -0
  131. data/lib/scaffolding/name_normalizer.rb +63 -0
  132. data/lib/scaffolding/page_introspector.rb +45 -0
  133. data/lib/scaffolding/project_detector.rb +72 -0
  134. data/lib/scaffolding/scaffold_menu.rb +103 -0
  135. data/lib/scaffolding/scaffolding.rb +158 -11
  136. data/lib/scaffolding/templates/component.tt +30 -0
  137. data/lib/scaffolding/templates/feature.tt +4 -4
  138. data/lib/scaffolding/templates/helper.tt +15 -1
  139. data/lib/scaffolding/templates/page_from_url.tt +75 -0
  140. data/lib/scaffolding/templates/page_object.tt +50 -1
  141. data/lib/scaffolding/templates/spec.tt +33 -2
  142. data/lib/scaffolding/templates/spec_from_page.tt +31 -0
  143. data/lib/scaffolding/templates/spec_from_url.tt +46 -0
  144. data/lib/scaffolding/templates/steps.tt +17 -5
  145. data/lib/scaffolding/url_analyzer.rb +179 -0
  146. data/lib/utilities/desktop_downloader.rb +177 -0
  147. data/lib/utilities/logo.rb +83 -0
  148. data/lib/utilities/utilities.rb +53 -20
  149. data/lib/version +1 -1
  150. data/ruby_raider.gemspec +1 -0
  151. data/sig/adopter/adopt_menu.rbs +25 -0
  152. data/sig/adopter/converters/base_converter.rbs +23 -0
  153. data/sig/adopter/converters/identity_converter.rbs +16 -0
  154. data/sig/adopter/migration_plan.rbs +34 -0
  155. data/sig/adopter/migrator.rbs +21 -0
  156. data/sig/adopter/plan_builder.rbs +38 -0
  157. data/sig/adopter/project_analyzer.rbs +39 -0
  158. data/sig/adopter/project_detector.rbs +26 -0
  159. data/sig/commands/adopt_commands.rbs +8 -0
  160. data/sig/commands/loaded_commands.rbs +5 -0
  161. data/sig/commands/plugin_commands.rbs +9 -0
  162. data/sig/commands/scaffolding_commands.rbs +28 -0
  163. data/sig/commands/utility_commands.rbs +21 -0
  164. data/sig/generators/automation/automation_generator.rbs +20 -0
  165. data/sig/generators/common_generator.rbs +12 -0
  166. data/sig/generators/cucumber/cucumber_generator.rbs +16 -0
  167. data/sig/generators/generator.rbs +40 -0
  168. data/sig/generators/helper_generator.rbs +18 -0
  169. data/sig/generators/infrastructure/github_generator.rbs +5 -0
  170. data/sig/generators/infrastructure/gitlab_generator.rbs +4 -0
  171. data/sig/generators/invoke_generators.rbs +10 -0
  172. data/sig/generators/menu_generator.rbs +29 -0
  173. data/sig/generators/minitest/minitest_generator.rbs +8 -0
  174. data/sig/generators/rspec/rspec_generator.rbs +8 -0
  175. data/sig/generators/template_renderer/partial_cache.rbs +20 -0
  176. data/sig/generators/template_renderer/partial_resolver.rbs +20 -0
  177. data/sig/generators/template_renderer/template_error.rbs +19 -0
  178. data/sig/generators/template_renderer.rbs +10 -0
  179. data/sig/llm/client.rbs +15 -0
  180. data/sig/llm/config.rbs +20 -0
  181. data/sig/llm/prompts.rbs +8 -0
  182. data/sig/llm/provider.rbs +12 -0
  183. data/sig/llm/providers/anthropic_provider.rbs +16 -0
  184. data/sig/llm/providers/ollama_provider.rbs +18 -0
  185. data/sig/llm/providers/openai_provider.rbs +16 -0
  186. data/sig/llm/response_parser.rbs +13 -0
  187. data/sig/plugin/plugin.rbs +24 -0
  188. data/sig/plugin/plugin_exposer.rbs +20 -0
  189. data/sig/ruby_raider.rbs +15 -0
  190. data/sig/scaffolding/crud_generator.rbs +16 -0
  191. data/sig/scaffolding/dry_run_presenter.rbs +4 -0
  192. data/sig/scaffolding/name_normalizer.rbs +17 -0
  193. data/sig/scaffolding/page_introspector.rbs +14 -0
  194. data/sig/scaffolding/project_detector.rbs +14 -0
  195. data/sig/scaffolding/scaffold_menu.rbs +18 -0
  196. data/sig/scaffolding/scaffolding.rbs +55 -0
  197. data/sig/scaffolding/url_analyzer.rbs +28 -0
  198. data/sig/utilities/desktop_downloader.rbs +23 -0
  199. data/sig/utilities/logger.rbs +13 -0
  200. data/sig/utilities/logo.rbs +16 -0
  201. data/sig/utilities/utilities.rbs +30 -0
  202. data/sig/vendor/thor.rbs +34 -0
  203. data/sig/vendor/tty_prompt.rbs +15 -0
  204. data/spec/adopter/adopt_menu_spec.rb +176 -0
  205. data/spec/adopter/converters/identity_converter_spec.rb +145 -0
  206. data/spec/adopter/migration_plan_spec.rb +113 -0
  207. data/spec/adopter/migrator_spec.rb +277 -0
  208. data/spec/adopter/plan_builder_spec.rb +298 -0
  209. data/spec/adopter/project_analyzer_spec.rb +337 -0
  210. data/spec/adopter/project_detector_spec.rb +295 -0
  211. data/spec/commands/raider_commands_spec.rb +129 -0
  212. data/spec/generators/fixtures/templates/test.tt +1 -0
  213. data/spec/generators/fixtures/templates/test_partial.tt +1 -0
  214. data/spec/generators/generator_spec.rb +23 -0
  215. data/spec/generators/template_renderer_spec.rb +298 -0
  216. data/spec/integration/commands/scaffolding_commands_spec.rb +2 -2
  217. data/spec/integration/commands/utility_commands_spec.rb +24 -4
  218. data/spec/integration/content/ci_content_spec.rb +119 -0
  219. data/spec/integration/content/common_content_spec.rb +288 -0
  220. data/spec/integration/content/config_content_spec.rb +175 -0
  221. data/spec/integration/content/content_helper.rb +32 -0
  222. data/spec/integration/content/gemfile_content_spec.rb +209 -0
  223. data/spec/integration/content/helper_content_spec.rb +485 -0
  224. data/spec/integration/content/page_content_spec.rb +259 -0
  225. data/spec/integration/content/reporter_content_spec.rb +236 -0
  226. data/spec/integration/content/skip_flags_content_spec.rb +206 -0
  227. data/spec/integration/content/syntax_validation_spec.rb +30 -0
  228. data/spec/integration/content/test_content_spec.rb +266 -0
  229. data/spec/integration/end_to_end_features_spec.rb +690 -0
  230. data/spec/integration/end_to_end_spec.rb +361 -0
  231. data/spec/integration/generators/automation_generator_spec.rb +9 -21
  232. data/spec/integration/generators/axe_addon_spec.rb +150 -0
  233. data/spec/integration/generators/common_generator_spec.rb +48 -49
  234. data/spec/integration/generators/config_features_spec.rb +155 -0
  235. data/spec/integration/generators/cucumber_generator_spec.rb +7 -7
  236. data/spec/integration/generators/debug_helper_spec.rb +68 -0
  237. data/spec/integration/generators/github_generator_spec.rb +8 -8
  238. data/spec/integration/generators/gitlab_generator_spec.rb +8 -8
  239. data/spec/integration/generators/helpers_generator_spec.rb +70 -44
  240. data/spec/integration/generators/lighthouse_addon_spec.rb +132 -0
  241. data/spec/integration/generators/minitest_generator_spec.rb +64 -0
  242. data/spec/integration/generators/reporter_spec.rb +159 -0
  243. data/spec/integration/generators/rspec_generator_spec.rb +7 -7
  244. data/spec/integration/generators/skip_flags_spec.rb +134 -0
  245. data/spec/integration/generators/visual_addon_spec.rb +148 -0
  246. data/spec/integration/settings_helper.rb +1 -4
  247. data/spec/integration/spec_helper.rb +46 -11
  248. data/spec/llm/client_spec.rb +79 -0
  249. data/spec/llm/config_spec.rb +92 -0
  250. data/spec/llm/prompts_spec.rb +49 -0
  251. data/spec/llm/response_parser_spec.rb +92 -0
  252. data/spec/menus/adopter_adopt_menu_spec.rb +97 -0
  253. data/spec/menus/menu_generator_spec.rb +263 -0
  254. data/spec/scaffolding/name_normalizer_spec.rb +113 -0
  255. data/spec/scaffolding/page_introspector_spec.rb +82 -0
  256. data/spec/scaffolding/scaffold_project_detector_spec.rb +104 -0
  257. data/spec/scaffolding/scaffolding_features_spec.rb +311 -0
  258. data/spec/scaffolding/url_analyzer_spec.rb +110 -0
  259. data/spec/system/adopt_matrix_spec.rb +537 -0
  260. data/spec/system/adopt_spec.rb +225 -0
  261. data/spec/system/capybara_spec.rb +42 -0
  262. data/spec/system/selenium_spec.rb +19 -17
  263. data/spec/system/support/system_test_helper.rb +33 -0
  264. data/spec/system/watir_spec.rb +19 -17
  265. data/spec/utilities/desktop_downloader_spec.rb +92 -0
  266. metadata +193 -18
  267. data/.github/workflows/push_gem.yml +0 -37
  268. data/.github/workflows/selenium.yml +0 -22
  269. data/.github/workflows/watir.yml +0 -22
  270. data/lib/generators/automation/templates/partials/android_caps.tt +0 -17
  271. data/lib/generators/automation/templates/partials/cross_platform_caps.tt +0 -25
  272. data/lib/generators/automation/templates/partials/ios_caps.tt +0 -18
  273. data/lib/generators/automation/templates/partials/selenium_account.tt +0 -9
  274. data/lib/generators/automation/templates/partials/selenium_login.tt +0 -34
  275. data/lib/generators/automation/templates/partials/watir_account.tt +0 -7
  276. data/lib/generators/automation/templates/partials/watir_login.tt +0 -32
  277. data/lib/generators/automation/templates/visual_options.tt +0 -16
  278. data/lib/generators/templates/helpers/visual_spec_helper.tt +0 -35
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec'
4
+ require_relative '../../lib/ruby_raider'
5
+
6
+ RSpec.describe RubyRaider::Raider do
7
+ describe 'new command' do
8
+ context 'with parameters' do
9
+ it 'calls InvokeGenerators.generate_framework with parsed hash' do
10
+ allow(InvokeGenerators).to receive(:generate_framework)
11
+
12
+ described_class.new.invoke(:new, nil, %w[my_project -p framework:rspec automation:selenium])
13
+
14
+ expect(InvokeGenerators).to have_received(:generate_framework).with(
15
+ hash_including(framework: 'rspec', automation: 'selenium', name: 'my_project')
16
+ )
17
+ end
18
+ end
19
+
20
+ context 'without parameters' do
21
+ it 'calls MenuGenerator.generate_choice_menu' do
22
+ menu = instance_double(MenuGenerator, generate_choice_menu: nil)
23
+ allow(MenuGenerator).to receive(:new).and_return(menu)
24
+
25
+ described_class.new.invoke(:new, nil, %w[my_project])
26
+
27
+ expect(menu).to have_received(:generate_choice_menu)
28
+ end
29
+ end
30
+ end
31
+
32
+ describe 'version command' do
33
+ it 'outputs the version from lib/version' do
34
+ expected_version = File.read(File.expand_path('../../lib/version', __dir__)).strip
35
+ expect { described_class.new.invoke(:version) }.to output(/#{Regexp.escape(expected_version)}/).to_stdout
36
+ end
37
+ end
38
+
39
+ describe 'subcommand aliases' do
40
+ it 'maps n to new' do
41
+ expect(described_class.map['n']).to eq('new')
42
+ end
43
+
44
+ it 'maps v to version' do
45
+ expect(described_class.map['v']).to eq('version')
46
+ end
47
+
48
+ it 'maps a to adopt' do
49
+ expect(described_class.map['a']).to eq('adopt')
50
+ end
51
+
52
+ it 'maps g to generate' do
53
+ expect(described_class.map['g']).to eq('generate')
54
+ end
55
+
56
+ it 'maps u to utility' do
57
+ expect(described_class.map['u']).to eq('utility')
58
+ end
59
+
60
+ it 'maps pm to plugin_manager' do
61
+ expect(described_class.map['pm']).to eq('plugin_manager')
62
+ end
63
+ end
64
+
65
+ describe 'registered subcommands' do
66
+ let(:subcommands) { described_class.subcommands }
67
+
68
+ it 'includes adopt' do
69
+ expect(subcommands).to include('adopt')
70
+ end
71
+
72
+ it 'includes generate' do
73
+ expect(subcommands).to include('generate')
74
+ end
75
+
76
+ it 'includes utility' do
77
+ expect(subcommands).to include('utility')
78
+ end
79
+
80
+ it 'includes plugin_manager' do
81
+ expect(subcommands).to include('plugin_manager')
82
+ end
83
+ end
84
+ end
85
+
86
+ RSpec.describe AdoptCommands do
87
+ let(:source_dir) { 'tmp_cmd_adopt_source' }
88
+ let(:output_dir) { 'tmp_cmd_adopt_output' }
89
+
90
+ before do
91
+ FileUtils.mkdir_p(source_dir)
92
+ File.write("#{source_dir}/Gemfile", "gem 'rspec'\ngem 'selenium-webdriver'\n")
93
+ FileUtils.mkdir_p("#{source_dir}/spec")
94
+ end
95
+
96
+ after do
97
+ FileUtils.rm_rf(source_dir)
98
+ FileUtils.rm_rf(output_dir)
99
+ end
100
+
101
+ describe 'project command with parameters' do
102
+ it 'calls AdoptMenu.adopt with parsed params' do
103
+ result = { plan: double(warnings: [], output_path: output_dir), results: { pages: 0, tests: 0, features: 0, steps: 0, errors: [] } } # rubocop:disable RSpec/VerifiedDoubles
104
+ allow(Adopter::AdoptMenu).to receive(:adopt).and_return(result)
105
+
106
+ expect do
107
+ described_class.new.invoke(:project, nil, [source_dir, '-p',
108
+ "output_path:#{output_dir}",
109
+ 'target_automation:selenium',
110
+ 'target_framework:rspec'])
111
+ end.to output(/Adoption complete/).to_stdout
112
+
113
+ expect(Adopter::AdoptMenu).to have_received(:adopt).with(
114
+ hash_including(source_path: source_dir, target_automation: 'selenium', target_framework: 'rspec')
115
+ )
116
+ end
117
+ end
118
+
119
+ describe 'project command without parameters' do
120
+ it 'calls AdoptMenu#run' do
121
+ menu = instance_double(Adopter::AdoptMenu, run: nil)
122
+ allow(Adopter::AdoptMenu).to receive(:new).and_return(menu)
123
+
124
+ described_class.new.invoke(:project, nil, [source_dir])
125
+
126
+ expect(menu).to have_received(:run)
127
+ end
128
+ end
129
+ end
@@ -0,0 +1 @@
1
+ Test content
@@ -0,0 +1 @@
1
+ Test partial content
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec'
4
+ require_relative '../../lib/generators/generator'
5
+
6
+ RSpec.describe Generator do
7
+ describe '#ruby_version' do
8
+ it 'returns the default version when no ruby_version arg is present' do
9
+ generator = described_class.new(%w[selenium rspec test_project])
10
+ expect(generator.ruby_version).to eq('3.4')
11
+ end
12
+
13
+ it 'returns the specified version from args' do
14
+ generator = described_class.new(%w[selenium rspec test_project ruby_version:3.3])
15
+ expect(generator.ruby_version).to eq('3.3')
16
+ end
17
+
18
+ it 'handles version with multiple colons gracefully' do
19
+ generator = described_class.new(%w[selenium rspec test_project ruby_version:3.2])
20
+ expect(generator.ruby_version).to eq('3.2')
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,298 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../integration/spec_helper'
4
+ require_relative '../../lib/generators/template_renderer'
5
+ require_relative '../../lib/generators/generator'
6
+
7
+ RSpec.describe TemplateRenderer do
8
+ # Create a test generator class that includes TemplateRenderer
9
+ let(:test_generator_class) do
10
+ Class.new do
11
+ include TemplateRenderer
12
+
13
+ def self.source_paths
14
+ [File.expand_path('../fixtures/templates', __dir__)]
15
+ end
16
+
17
+ # Mock predicate methods that templates might use
18
+ def selenium_based?
19
+ true
20
+ end
21
+
22
+ def watir?
23
+ false
24
+ end
25
+
26
+ def mobile?
27
+ false
28
+ end
29
+ end
30
+ end
31
+
32
+ let(:test_instance) { test_generator_class.new }
33
+
34
+ describe '#partial' do
35
+ context 'when rendering a simple partial' do
36
+ it 'renders the partial content' do
37
+ # This test requires a fixtures directory with test templates
38
+ # For now, we'll test the API interface
39
+ expect(test_instance).to respond_to(:partial)
40
+ end
41
+
42
+ it 'accepts a partial name as first argument' do
43
+ expect { test_instance.partial('test_partial') }.not_to raise_error(ArgumentError)
44
+ end
45
+
46
+ it 'accepts options hash' do
47
+ expect { test_instance.partial('test', trim_mode: '-', strip: true) }.not_to raise_error(ArgumentError)
48
+ end
49
+ end
50
+
51
+ context 'with strip option' do
52
+ it 'applies strip when strip: true' do
53
+ # Mock the template renderer to verify strip is called
54
+ allow_any_instance_of(TemplateRenderer::PartialCache).to receive(:render_partial)
55
+ .and_return(" content \n")
56
+
57
+ result = test_instance.partial('test', strip: true)
58
+ expect(result).to eq('content')
59
+ end
60
+
61
+ it 'does not apply strip when strip: false (default)' do
62
+ allow_any_instance_of(TemplateRenderer::PartialCache).to receive(:render_partial)
63
+ .and_return(" content \n")
64
+
65
+ result = test_instance.partial('test', strip: false)
66
+ expect(result).to eq(" content \n")
67
+ end
68
+ end
69
+
70
+ context 'with trim_mode option' do
71
+ it 'uses default trim_mode: "-" when not specified' do
72
+ cache = test_generator_class.template_renderer
73
+ expect(cache).to receive(:render_partial).with('test', anything, hash_including(trim_mode: '-'))
74
+ test_instance.partial('test')
75
+ end
76
+
77
+ it 'allows custom trim_mode' do
78
+ cache = test_generator_class.template_renderer
79
+ expect(cache).to receive(:render_partial).with('test', anything, hash_including(trim_mode: '<>'))
80
+ test_instance.partial('test', trim_mode: '<>')
81
+ end
82
+
83
+ it 'disables trim_mode when trim: false' do
84
+ cache = test_generator_class.template_renderer
85
+ expect(cache).to receive(:render_partial).with('test', anything, hash_including(trim_mode: nil))
86
+ test_instance.partial('test', trim: false)
87
+ end
88
+ end
89
+ end
90
+
91
+ describe 'ClassMethods' do
92
+ describe '.template_renderer' do
93
+ it 'returns a PartialCache instance' do
94
+ expect(test_generator_class.template_renderer).to be_a(TemplateRenderer::PartialCache)
95
+ end
96
+
97
+ it 'returns the same instance on multiple calls (memoization)' do
98
+ first = test_generator_class.template_renderer
99
+ second = test_generator_class.template_renderer
100
+ expect(first).to be(second)
101
+ end
102
+ end
103
+
104
+ describe '.clear_template_cache' do
105
+ it 'clears the template cache' do
106
+ test_generator_class.template_renderer # Initialize cache
107
+ test_generator_class.clear_template_cache
108
+ expect(test_generator_class.instance_variable_get(:@template_renderer)).to be_nil
109
+ end
110
+ end
111
+
112
+ describe '.template_cache_stats' do
113
+ it 'returns cache statistics' do
114
+ stats = test_generator_class.template_cache_stats
115
+ expect(stats).to have_key(:size)
116
+ expect(stats).to have_key(:entries)
117
+ expect(stats).to have_key(:memory_estimate)
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ RSpec.describe TemplateRenderer::PartialCache do
124
+ let(:generator_class) do
125
+ Class.new do
126
+ def self.source_paths
127
+ [File.expand_path('../fixtures/templates', __dir__)]
128
+ end
129
+ end
130
+ end
131
+
132
+ let(:cache) { described_class.new(generator_class) }
133
+ let(:test_binding) { binding }
134
+
135
+ describe '#render_partial' do
136
+ context 'when partial does not exist' do
137
+ it 'raises TemplateNotFoundError' do
138
+ expect do
139
+ cache.render_partial('nonexistent', test_binding, {})
140
+ end.to raise_error(TemplateRenderer::TemplateNotFoundError)
141
+ end
142
+
143
+ it 'includes searched paths in error message' do
144
+ expect do
145
+ cache.render_partial('missing', test_binding, {})
146
+ end.to raise_error(TemplateRenderer::TemplateNotFoundError, /Searched in/)
147
+ end
148
+ end
149
+
150
+ context 'when partial has syntax errors' do
151
+ it 'raises TemplateRenderError' do
152
+ # This would require a fixture file with ERB syntax errors
153
+ # For now, test that the error class exists
154
+ expect(TemplateRenderer::TemplateRenderError).to be < TemplateRenderer::TemplateError
155
+ end
156
+ end
157
+ end
158
+
159
+ describe '#clear' do
160
+ it 'clears the cache' do
161
+ cache.clear
162
+ expect(cache.stats[:size]).to eq(0)
163
+ end
164
+ end
165
+
166
+ describe '#stats' do
167
+ it 'returns cache statistics hash' do
168
+ stats = cache.stats
169
+ expect(stats).to be_a(Hash)
170
+ expect(stats).to have_key(:size)
171
+ expect(stats).to have_key(:entries)
172
+ expect(stats).to have_key(:memory_estimate)
173
+ end
174
+
175
+ it 'estimates memory usage' do
176
+ stats = cache.stats
177
+ expect(stats[:memory_estimate]).to be >= 0
178
+ end
179
+ end
180
+
181
+ describe 'caching behavior' do
182
+ it 'caches compiled ERB objects' do
183
+ # This test would require actual fixture files
184
+ # Verifies that repeated renders use cache
185
+ expect(cache.instance_variable_get(:@cache)).to be_a(Hash)
186
+ end
187
+ end
188
+ end
189
+
190
+ RSpec.describe TemplateRenderer::PartialResolver do
191
+ let(:generator_class) do
192
+ Class.new do
193
+ def self.source_paths
194
+ [
195
+ '/path/to/templates',
196
+ '/another/path/templates'
197
+ ]
198
+ end
199
+ end
200
+ end
201
+
202
+ let(:resolver) { described_class.new(generator_class) }
203
+ let(:test_binding) { binding }
204
+
205
+ describe '#resolve' do
206
+ it 'attempts to resolve relative to caller first' do
207
+ # Mock file existence check
208
+ allow(File).to receive(:exist?).and_return(false)
209
+
210
+ expect do
211
+ resolver.resolve('test', test_binding)
212
+ end.to raise_error(TemplateRenderer::TemplateNotFoundError)
213
+ end
214
+ end
215
+
216
+ describe '#search_paths' do
217
+ it 'returns all paths that were searched' do
218
+ paths = resolver.search_paths('screenshot', test_binding)
219
+
220
+ expect(paths).to be_an(Array)
221
+ expect(paths).not_to be_empty
222
+ expect(paths.first).to include('screenshot.tt')
223
+ end
224
+
225
+ it 'includes relative path if caller file is available' do
226
+ paths = resolver.search_paths('screenshot', test_binding)
227
+ # Should include relative path attempt
228
+ expect(paths.length).to be >= 1
229
+ end
230
+
231
+ it 'includes all source_paths in search' do
232
+ paths = resolver.search_paths('screenshot', test_binding)
233
+
234
+ # Should search in all configured source paths
235
+ expect(paths.any? { |p| p.include?('/path/to/templates') }).to be true
236
+ end
237
+ end
238
+ end
239
+
240
+ RSpec.describe TemplateRenderer::TemplateError do
241
+ describe TemplateRenderer::TemplateNotFoundError do
242
+ it 'formats error message with searched paths' do
243
+ error = described_class.new(
244
+ 'Partial not found',
245
+ partial_name: 'test',
246
+ searched_paths: ['/path/1', '/path/2']
247
+ )
248
+
249
+ message = error.to_s
250
+ expect(message).to include("Partial 'test' not found")
251
+ expect(message).to include('/path/1')
252
+ expect(message).to include('/path/2')
253
+ expect(message).to include('Searched in:')
254
+ end
255
+ end
256
+
257
+ describe TemplateRenderer::TemplateRenderError do
258
+ it 'includes original error information' do
259
+ original = StandardError.new('syntax error')
260
+ original.set_backtrace(['line 1', 'line 2'])
261
+
262
+ error = described_class.new(
263
+ 'Render failed',
264
+ partial_name: 'test',
265
+ original_error: original
266
+ )
267
+
268
+ message = error.to_s
269
+ expect(message).to include('Error rendering partial')
270
+ expect(message).to include('syntax error')
271
+ expect(message).to include('Backtrace:')
272
+ end
273
+ end
274
+ end
275
+
276
+ # Integration test with Generator base class
277
+ RSpec.describe Generator do
278
+ it 'includes TemplateRenderer module' do
279
+ expect(described_class.ancestors).to include(TemplateRenderer)
280
+ end
281
+
282
+ it 'has partial method available' do
283
+ # Generator requires arguments, so we'll test the class directly
284
+ expect(described_class.instance_methods).to include(:partial)
285
+ end
286
+
287
+ it 'has template_renderer class method' do
288
+ expect(described_class).to respond_to(:template_renderer)
289
+ end
290
+
291
+ it 'has clear_template_cache class method' do
292
+ expect(described_class).to respond_to(:clear_template_cache)
293
+ end
294
+
295
+ it 'has template_cache_stats class method' do
296
+ expect(described_class).to respond_to(:template_cache_stats)
297
+ end
298
+ end
@@ -18,7 +18,7 @@ describe ScaffoldingCommands do
18
18
  context 'with a spec folder' do
19
19
  let(:new_path) { 'test_folder' }
20
20
 
21
- path = "#{FRAMEWORKS.last}_#{AUTOMATION_TYPES[2]}"
21
+ path = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::SELENIUM}"
22
22
 
23
23
  before do
24
24
  Dir.chdir path
@@ -63,7 +63,7 @@ describe ScaffoldingCommands do
63
63
  context 'with a features folder' do
64
64
  let(:new_path) { 'test_folder' }
65
65
 
66
- path = "#{FRAMEWORKS.first}_#{AUTOMATION_TYPES.last}"
66
+ path = "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::SELENIUM}"
67
67
 
68
68
  before do
69
69
  Dir.chdir path
@@ -19,7 +19,7 @@ describe UtilityCommands do
19
19
  context 'with a spec folder' do
20
20
  let(:new_path) { 'test_folder' }
21
21
 
22
- path = "#{FRAMEWORKS.last}_#{AUTOMATION_TYPES[2]}"
22
+ path = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::SELENIUM}"
23
23
 
24
24
  before do
25
25
  Dir.chdir path
@@ -43,17 +43,37 @@ describe UtilityCommands do
43
43
  expect(config['browser']).to eql ':firefox'
44
44
  end
45
45
 
46
- it 'updates the browser options', skip: 'review browser options method' do
46
+ it 'updates the browser options' do
47
47
  utility.new.invoke(:browser, nil, %w[:firefox --opts headless start-maximized start-fullscreen])
48
48
  config = YAML.load_file('config/config.yml')
49
49
  expect(config['browser_options']).to eql %w[headless start-maximized start-fullscreen]
50
50
  end
51
51
  end
52
52
 
53
+ context 'with timeout and viewport commands' do
54
+ path = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::SELENIUM}"
55
+
56
+ before do
57
+ Dir.chdir path
58
+ end
59
+
60
+ it 'updates the timeout' do
61
+ utility.new.invoke(:timeout, nil, %w[30])
62
+ config = YAML.load_file('config/config.yml')
63
+ expect(config['timeout']).to eq 30
64
+ end
65
+
66
+ it 'updates the viewport' do
67
+ utility.new.invoke(:viewport, nil, %w[375x812])
68
+ config = YAML.load_file('config/config.yml')
69
+ expect(config['viewport']).to eq({ 'width' => 375, 'height' => 812 })
70
+ end
71
+ end
72
+
53
73
  context 'with a features folder' do
54
74
  let(:new_path) { 'test_folder' }
55
75
 
56
- path = "#{FRAMEWORKS.first}_#{AUTOMATION_TYPES.last}"
76
+ path = "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::SELENIUM}"
57
77
 
58
78
  before do
59
79
  Dir.chdir path
@@ -71,7 +91,7 @@ describe UtilityCommands do
71
91
  expect(config['feature_path']).to eql path
72
92
  end
73
93
 
74
- it 'updates only the browser options', skip: 'review browser options method' do
94
+ it 'updates only the browser options' do
75
95
  utility.new.invoke(:browser, nil, %w[:firefox --opts headless])
76
96
  config = YAML.load_file('config/config.yml')
77
97
  expect(config['browser_options']).to eql %w[headless]
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require_relative 'content_helper'
5
+
6
+ describe 'CI pipeline content' do
7
+ # --- GitHub Actions ---
8
+
9
+ shared_examples 'valid github pipeline' do |project_name|
10
+ subject(:pipeline) { read_generated("#{project_name}_github", '.github/workflows/test_pipeline.yml') }
11
+
12
+ it 'is valid YAML' do
13
+ expect { YAML.safe_load(pipeline) }.not_to raise_error
14
+ end
15
+
16
+ it 'has name' do
17
+ expect(pipeline).to match(/^name:/)
18
+ end
19
+
20
+ it 'has jobs section' do
21
+ expect(pipeline).to include('jobs:')
22
+ end
23
+
24
+ it 'sets up Ruby' do
25
+ expect(pipeline).to include('ruby')
26
+ end
27
+
28
+ it 'installs dependencies' do
29
+ expect(pipeline).to include('bundler-cache: true').or include('bundle install')
30
+ end
31
+ end
32
+
33
+ shared_examples 'github rspec pipeline' do |project_name|
34
+ subject(:pipeline) { read_generated("#{project_name}_github", '.github/workflows/test_pipeline.yml') }
35
+
36
+ it 'runs rspec' do
37
+ expect(pipeline).to include('rspec')
38
+ end
39
+ end
40
+
41
+ shared_examples 'github cucumber pipeline' do |project_name|
42
+ subject(:pipeline) { read_generated("#{project_name}_github", '.github/workflows/test_pipeline.yml') }
43
+
44
+ it 'runs cucumber' do
45
+ expect(pipeline).to include('cucumber')
46
+ end
47
+ end
48
+
49
+ shared_examples 'github minitest pipeline' do |project_name|
50
+ subject(:pipeline) { read_generated("#{project_name}_github", '.github/workflows/test_pipeline.yml') }
51
+
52
+ it 'runs minitest' do
53
+ expect(pipeline).to match(/minitest|ruby.*test/)
54
+ end
55
+ end
56
+
57
+ context 'with rspec and selenium on github' do
58
+ name = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::SELENIUM}"
59
+ include_examples 'valid github pipeline', name
60
+ include_examples 'github rspec pipeline', name
61
+ end
62
+
63
+ context 'with cucumber and selenium on github' do
64
+ name = "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::SELENIUM}"
65
+ include_examples 'valid github pipeline', name
66
+ include_examples 'github cucumber pipeline', name
67
+ end
68
+
69
+ context 'with minitest and selenium on github' do
70
+ name = "#{FrameworkIndex::MINITEST}_#{AutomationIndex::SELENIUM}"
71
+ include_examples 'valid github pipeline', name
72
+ include_examples 'github minitest pipeline', name
73
+ end
74
+
75
+ context 'with rspec and capybara on github' do
76
+ name = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::CAPYBARA}"
77
+ include_examples 'valid github pipeline', name
78
+ include_examples 'github rspec pipeline', name
79
+ end
80
+
81
+ context 'with rspec and watir on github' do
82
+ name = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::WATIR}"
83
+ include_examples 'valid github pipeline', name
84
+ include_examples 'github rspec pipeline', name
85
+ end
86
+
87
+ # --- GitLab CI ---
88
+
89
+ shared_examples 'valid gitlab pipeline' do |project_name|
90
+ subject(:pipeline) { read_generated("#{project_name}_gitlab", 'gitlab-ci.yml') }
91
+
92
+ it 'is valid YAML' do
93
+ expect { YAML.safe_load(pipeline) }.not_to raise_error
94
+ end
95
+
96
+ it 'has stages' do
97
+ expect(pipeline).to include('stages:')
98
+ end
99
+
100
+ it 'has script section' do
101
+ expect(pipeline).to include('script:')
102
+ end
103
+ end
104
+
105
+ context 'with rspec and selenium on gitlab' do
106
+ name = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::SELENIUM}"
107
+ include_examples 'valid gitlab pipeline', name
108
+ end
109
+
110
+ context 'with cucumber and selenium on gitlab' do
111
+ name = "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::SELENIUM}"
112
+ include_examples 'valid gitlab pipeline', name
113
+ end
114
+
115
+ context 'with minitest and selenium on gitlab' do
116
+ name = "#{FrameworkIndex::MINITEST}_#{AutomationIndex::SELENIUM}"
117
+ include_examples 'valid gitlab pipeline', name
118
+ end
119
+ end