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,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'template_error'
4
+
5
+ module TemplateRenderer
6
+ # Resolves partial template paths with context-aware searching
7
+ #
8
+ # Resolution strategy:
9
+ # 1. Try relative to calling template: ./partials/{name}.tt
10
+ # 2. Fall back to all Generator.source_paths searching for partials/{name}.tt
11
+ class PartialResolver
12
+ PARTIAL_EXTENSION = '.tt'
13
+ PARTIALS_DIR = 'partials'
14
+
15
+ def initialize(generator_class)
16
+ @generator_class = generator_class
17
+ end
18
+
19
+ # Resolve a partial name to its absolute file path
20
+ #
21
+ # @param name [String] The partial name (without .tt extension)
22
+ # @param binding [Binding] The binding context from the caller
23
+ # @return [String] Absolute path to the partial file
24
+ # @raise [TemplateNotFoundError] If partial cannot be found
25
+ def resolve(name, binding)
26
+ caller_file = caller_file_from_binding(binding)
27
+ partial_filename = "#{name}#{PARTIAL_EXTENSION}"
28
+
29
+ # Try relative to caller first
30
+ if caller_file
31
+ relative_path = File.join(File.dirname(caller_file), PARTIALS_DIR, partial_filename)
32
+ return File.expand_path(relative_path) if File.exist?(relative_path)
33
+ end
34
+
35
+ # Fall back to searching all source paths
36
+ searched = search_source_paths(partial_filename)
37
+ return searched[:found] if searched[:found]
38
+
39
+ # Not found - raise with helpful error
40
+ raise TemplateNotFoundError.new(
41
+ "Partial '#{name}' not found",
42
+ partial_name: name,
43
+ searched_paths: search_paths(name, binding)
44
+ )
45
+ end
46
+
47
+ # Get all paths that were searched (for error messages)
48
+ def search_paths(name, binding)
49
+ paths = []
50
+ partial_filename = "#{name}#{PARTIAL_EXTENSION}"
51
+
52
+ # Add relative path if available
53
+ caller_file = caller_file_from_binding(binding)
54
+ if caller_file
55
+ relative_path = File.join(File.dirname(caller_file), PARTIALS_DIR, partial_filename)
56
+ paths << relative_path
57
+ end
58
+
59
+ # Add all source path possibilities (including subdirectories)
60
+ source_paths.each do |source_path|
61
+ paths << File.join(source_path, PARTIALS_DIR, partial_filename)
62
+
63
+ # Also include subdirectories (e.g., templates/common/partials/, templates/helpers/partials/)
64
+ Dir.glob(File.join(source_path, '*', PARTIALS_DIR)).each do |subdir|
65
+ paths << File.join(subdir, partial_filename)
66
+ end
67
+ end
68
+
69
+ paths
70
+ end
71
+
72
+ private
73
+
74
+ # Extract the calling template file from binding context
75
+ def caller_file_from_binding(binding)
76
+ binding.eval('__FILE__')
77
+ rescue StandardError
78
+ nil
79
+ end
80
+
81
+ # Search all Generator.source_paths for the partial.
82
+ # Uses a cached directory index to avoid repeated Dir.glob calls.
83
+ def search_source_paths(partial_filename)
84
+ # Check the pre-built index first (fast path)
85
+ partial_dirs.each do |dir|
86
+ full_path = File.join(dir, partial_filename)
87
+ return { found: File.expand_path(full_path), searched: [full_path] } if File.exist?(full_path)
88
+ end
89
+
90
+ { found: nil, searched: [] }
91
+ end
92
+
93
+ # Get all configured source paths from the generator class
94
+ def source_paths
95
+ @source_paths ||= @generator_class.source_paths || []
96
+ end
97
+
98
+ # Pre-built index of all directories that could contain partials.
99
+ # Scanned once via Dir.glob, then reused for every partial lookup.
100
+ def partial_dirs
101
+ @partial_dirs ||= source_paths.flat_map do |source_path|
102
+ dirs = []
103
+ direct = File.join(source_path, PARTIALS_DIR)
104
+ dirs << direct if File.directory?(direct)
105
+ Dir.glob(File.join(source_path, '*', PARTIALS_DIR)).each { |d| dirs << d }
106
+ dirs
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TemplateRenderer
4
+ # Base error class for all template-related errors
5
+ class TemplateError < StandardError
6
+ attr_reader :partial_name, :searched_paths, :original_error
7
+
8
+ def initialize(message, partial_name: nil, searched_paths: nil, original_error: nil)
9
+ @partial_name = partial_name
10
+ @searched_paths = searched_paths || []
11
+ @original_error = original_error
12
+ super(message)
13
+ end
14
+ end
15
+
16
+ # Raised when a partial template cannot be found
17
+ class TemplateNotFoundError < TemplateError
18
+ def to_s
19
+ message_parts = ["Partial '#{@partial_name}' not found."]
20
+
21
+ if @searched_paths.any?
22
+ message_parts << "\nSearched in:"
23
+ message_parts.concat(@searched_paths.map { |path| " - #{path}" })
24
+ end
25
+
26
+ message_parts.join("\n")
27
+ end
28
+ end
29
+
30
+ # Raised when a template has syntax errors or rendering fails
31
+ class TemplateRenderError < TemplateError
32
+ def initialize(message, partial_name:, original_error: nil)
33
+ super(message, partial_name:, original_error:)
34
+ end
35
+
36
+ def to_s
37
+ message_parts = ["Error rendering partial '#{@partial_name}': #{super()}"]
38
+
39
+ if @original_error
40
+ message_parts << "\nOriginal error: #{@original_error.class}: #{@original_error.message}"
41
+ if @original_error.backtrace
42
+ message_parts << "\nBacktrace:"
43
+ message_parts.concat(@original_error.backtrace.first(5).map { |line| " #{line}" })
44
+ end
45
+ end
46
+
47
+ message_parts.join("\n")
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'template_renderer/partial_cache'
4
+ require_relative 'template_renderer/partial_resolver'
5
+ require_relative 'template_renderer/template_error'
6
+
7
+ # Template rendering module for Ruby Raider generators
8
+ #
9
+ # Provides a clean partial() helper for including ERB templates with:
10
+ # - Automatic caching of compiled ERB objects (10x performance improvement)
11
+ # - Context-aware path resolution (relative to caller, then all source_paths)
12
+ # - Helpful error messages when partials are missing
13
+ # - Flexible whitespace handling (trim_mode, strip options)
14
+ #
15
+ # Usage in templates:
16
+ # <%= partial('screenshot') %> # Default: trim_mode: '-', no strip
17
+ # <%= partial('screenshot', strip: true) %> # With .strip!
18
+ # <%= partial('screenshot', trim: false) %> # No trim_mode
19
+ # <%= partial('driver_config', trim_mode: '<>') %> # Custom trim_mode
20
+ #
21
+ # The partial() method automatically has access to all generator instance
22
+ # variables and methods (cucumber?, mobile?, selenium?, etc.) through binding.
23
+ module TemplateRenderer
24
+ # Render a partial template with caching and smart path resolution
25
+ #
26
+ # @param name [String] Partial name without .tt extension (e.g., 'screenshot')
27
+ # @param options [Hash] Rendering options
28
+ # @option options [String, nil] :trim_mode ERB trim mode (default: '-')
29
+ # - '-' : Trim lines ending with -%>
30
+ # - '<>' : Omit newlines for lines starting with <% and ending with %>
31
+ # - nil : No trimming
32
+ # @option options [Boolean] :strip Whether to call .strip! on result (default: false)
33
+ # @option options [Boolean] :trim Whether to use trim_mode (default: true)
34
+ #
35
+ # @return [String] Rendered template content
36
+ #
37
+ # @example Basic usage
38
+ # <%= partial('screenshot') %>
39
+ #
40
+ # @example With strip
41
+ # <%= partial('screenshot', strip: true) %>
42
+ #
43
+ # @example No trim mode
44
+ # <%= partial('quit_driver', trim: false) %>
45
+ #
46
+ # @raise [TemplateNotFoundError] If partial cannot be found
47
+ # @raise [TemplateRenderError] If rendering fails
48
+ def partial(name, options = {})
49
+ # Default options
50
+ options = {
51
+ trim_mode: '-',
52
+ strip: false,
53
+ trim: true
54
+ }.merge(options)
55
+
56
+ # Handle trim: false by setting trim_mode to nil
57
+ options[:trim_mode] = nil if options[:trim] == false
58
+
59
+ # Render the partial through the cache
60
+ result = self.class.template_renderer.render_partial(name, binding, options)
61
+
62
+ # Apply strip if requested
63
+ options[:strip] ? result.strip : result
64
+ end
65
+
66
+ # Module hook for including in classes
67
+ def self.included(base)
68
+ base.extend(ClassMethods)
69
+ end
70
+
71
+ # Class methods added to the including class
72
+ module ClassMethods
73
+ # Get the shared template renderer instance
74
+ # Each generator class gets its own cache instance
75
+ def template_renderer
76
+ @template_renderer ||= PartialCache.new(self)
77
+ end
78
+
79
+ # Clear the template cache (useful for testing)
80
+ # Clears cache for this class and all subclasses
81
+ def clear_template_cache
82
+ @template_renderer&.clear
83
+ @template_renderer = nil
84
+ subclasses.each do |subclass|
85
+ subclass.clear_template_cache if subclass.respond_to?(:clear_template_cache)
86
+ end
87
+ end
88
+
89
+ # Get cache statistics (useful for debugging)
90
+ # Aggregates stats across this class and all subclasses
91
+ def template_cache_stats
92
+ own = @template_renderer&.stats || { size: 0, entries: [], memory_estimate: 0 }
93
+ subclasses.each do |subclass|
94
+ next unless subclass.respond_to?(:template_cache_stats)
95
+
96
+ sub_stats = subclass.instance_variable_get(:@template_renderer)&.stats
97
+ next unless sub_stats
98
+
99
+ own[:size] += sub_stats[:size]
100
+ own[:entries].concat(sub_stats[:entries])
101
+ own[:memory_estimate] += sub_stats[:memory_estimate]
102
+ end
103
+ own
104
+ end
105
+ end
106
+ end
@@ -1,5 +1,5 @@
1
1
  <% if mobile? -%>
2
- <%= ERB.new(File.read(File.expand_path('./partials/mobile_config.tt', __dir__))).result(binding) %>
2
+ <%= partial('mobile_config', trim: false) %>
3
3
  <% else -%>
4
- <%= ERB.new(File.read(File.expand_path('./partials/web_config.tt', __dir__)), trim_mode: '-').result(binding) %>
4
+ <%= partial('web_config') %>
5
5
  <% end -%>
@@ -1,38 +1,61 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ ruby '~> <%= ruby_version %>.0'
4
+
3
5
  gem 'activesupport'
4
6
  <% if mobile? %>
5
7
  gem 'appium_lib'
6
8
  gem 'appium_console'
7
9
  <% end %>
10
+ <%- if allure_reporter? -%>
8
11
  <%- if cucumber? -%>
9
- gem 'allure-cucumber', '2.23.0'
12
+ gem 'allure-cucumber', '2.28.0'
13
+ <%- elsif minitest? -%>
14
+ gem 'allure-ruby-commons', '2.28.0'
10
15
  <%- else -%>
11
- gem 'allure-rspec', '2.23.0'
12
- gem 'allure-ruby-commons', '2.23.0'
16
+ gem 'allure-rspec', '2.28.0'
17
+ gem 'allure-ruby-commons', '2.28.0'
18
+ <%- end -%>
19
+ <%- end -%>
20
+ <%- if capybara? -%>
21
+ gem 'capybara'
22
+ gem 'selenium-webdriver'
13
23
  <%- end -%>
14
- <%- if axe? -%>
24
+ <%- if axe_addon? -%>
25
+ <%- if minitest? -%>
26
+ gem 'axe-core-minitest'
27
+ <%- else -%>
15
28
  gem 'axe-core-rspec'
29
+ <%- end -%>
16
30
  gem 'axe-core-selenium'
17
31
  <%- end -%>
18
- <%- if visual? -%>
19
- gem 'eyes_selenium', '~> 4.6', '>= 4.6.1'
20
- gem 'eyes_universal', '~> 3.3', '>= 3.3.1'
32
+ <%- if visual_addon? -%>
33
+ gem 'chunky_png'
21
34
  <%- end -%>
22
- <%- unless visual? -%>
23
35
  gem 'parallel_split_test'
24
36
  gem 'parallel_tests'
25
- <%- end -%>
26
37
  gem 'rake'
27
38
  gem 'reek'
39
+ <%- if rspec? -%>
40
+ gem 'rspec-retry'
41
+ <%- end -%>
42
+ <%- if junit_reporter? && rspec? -%>
43
+ gem 'rspec_junit_formatter'
44
+ <%- end -%>
28
45
  gem '<%= framework %>'
29
46
  <%- if cucumber? -%>
30
47
  gem 'rspec'
31
48
  <%- end -%>
49
+ <%- if minitest? -%>
50
+ gem 'minitest-reporters'
51
+ <%- end -%>
32
52
  gem 'rubocop'
33
53
  <%- if rspec? -%>
34
54
  gem 'rubocop-rspec'
35
55
  <%- end -%>
56
+ <%- if minitest? -%>
57
+ gem 'rubocop-minitest'
58
+ <%- end -%>
36
59
  gem 'ruby_raider', '~> 1.1.0'
37
60
  <%- if selenium_based? %>
38
61
  gem 'selenium-webdriver'
@@ -40,3 +63,7 @@ gem 'selenium-webdriver'
40
63
  <%- if watir? -%>
41
64
  gem 'watir'
42
65
  <%- end -%>
66
+ <%- if web? -%>
67
+ gem 'debug', '>= 1.0'
68
+ gem 'pry'
69
+ <%- end -%>
@@ -1 +1,6 @@
1
- allure-results
1
+ <%- if allure_reporter? -%>
2
+ allure-results
3
+ <%- end -%>
4
+ <%- if rspec? -%>
5
+ spec/examples.txt
6
+ <%- end -%>
@@ -1 +1,5 @@
1
- platform: :ios
1
+ platform: :ios
2
+
3
+ video:
4
+ enabled: false
5
+ strategy: auto # auto, appium, or screen
@@ -1,11 +1,9 @@
1
1
  browser: chrome
2
- url: 'https://automationteststore.com/'
3
-
4
- <%- if selenium_based? -%>
5
- driver_options:
6
- :timeouts:
7
- :implicit: 10000 # 10 seconds
8
- <% end -%>
2
+ url: 'https://raider-test-site.onrender.com/'
3
+ timeout: 10 # seconds — used for implicit waits and max wait time
4
+ viewport:
5
+ width: 1920
6
+ height: 1080
9
7
 
10
8
  browser_arguments:
11
9
  chrome:
@@ -15,4 +13,15 @@ browser_arguments:
15
13
  - search-engine-choice-country
16
14
  firefox:
17
15
  - acceptInsecureCerts
18
- - no-sandbox
16
+ - no-sandbox
17
+
18
+ video:
19
+ enabled: false
20
+ strategy: auto # auto, cdp, or screen
21
+
22
+ debug:
23
+ enabled: false # also toggled by DEBUG=true env var
24
+ console_logs: true # capture browser console logs on failure
25
+ action_logging: true # log driver actions with timestamps
26
+ network_logging: true # capture HTTP requests on failure (Chrome/Edge only)
27
+ log_dir: 'debug_logs'
@@ -2,3 +2,39 @@ desc 'Print Ruby Raider version'
2
2
  task :raider do
3
3
  system 'raider version'
4
4
  end
5
+ <%- if rspec? -%>
6
+
7
+ require 'rspec/core/rake_task'
8
+
9
+ RSpec::Core::RakeTask.new(:spec)
10
+ RSpec::Core::RakeTask.new(:smoke) do |t|
11
+ t.rspec_opts = '--tag smoke'
12
+ end
13
+ RSpec::Core::RakeTask.new(:regression) do |t|
14
+ t.rspec_opts = '--tag regression'
15
+ end
16
+
17
+ task default: :spec
18
+ <%- elsif cucumber? -%>
19
+
20
+ require 'cucumber/rake/task'
21
+
22
+ Cucumber::Rake::Task.new(:features)
23
+ Cucumber::Rake::Task.new(:smoke) do |t|
24
+ t.cucumber_opts = '--tags @smoke'
25
+ end
26
+ Cucumber::Rake::Task.new(:regression) do |t|
27
+ t.cucumber_opts = '--tags @regression'
28
+ end
29
+
30
+ task default: :features
31
+ <%- elsif minitest? -%>
32
+
33
+ require 'rake/testtask'
34
+
35
+ Rake::TestTask.new(:test) do |t|
36
+ t.test_files = FileList['test/**/test_*.rb']
37
+ end
38
+
39
+ task default: :test
40
+ <%- end -%>
@@ -4,10 +4,10 @@
4
4
  <br />
5
5
  <div align="center">
6
6
  <a href="https://github.com/RubyRaider/ruby_raider">
7
- <img src="https://private-user-images.githubusercontent.com/33221555/391053089-3a1a492f-a70e-45ff-8fd0-5f83cef91f49.svg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzI4ODUyNTEsIm5iZiI6MTczMjg4NDk1MSwicGF0aCI6Ii8zMzIyMTU1NS8zOTEwNTMwODktM2ExYTQ5MmYtYTcwZS00NWZmLThmZDAtNWY4M2NlZjkxZjQ5LnN2Zz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDExMjklMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQxMTI5VDEyNTU1MVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTQ1MDM2ZjAzMmIwMWRmZjdlMjBkMWYxNGFkNTAxYTM0OTEyZjhmNjQ4YmE5NWMwOWE4MTY1Njc5ZjllNTMxYjQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.EnwLs1vZClwzNl1el0SRflyUJrwzno_LhBnnAXMHz7E" alt="Logo" style="width:200px;">
7
+ <img src="https://www.ruby-raider.com/assets/icon-DYY74ofR.png" alt="Logo" style="width:200px;">
8
8
  </a>
9
9
  <p align="center">
10
- <a href="https://github.com/RubyRaider/ruby_raider#getting-started"><strong>Explore the docs »</strong></a>
10
+ <a href="https://github.com/RubyRaider/ruby_raider#getting-started"><strong>Explore the docs</strong></a>
11
11
  <br />
12
12
  <br />
13
13
  <a href="https://rubygems.org/gems/ruby_raider">Rubygems</a>
@@ -21,119 +21,71 @@
21
21
 
22
22
  ## What is Ruby Raider?
23
23
 
24
- Ruby Raider is a generator and scaffolding gem to make UI test automation easier
24
+ Ruby Raider is a generator and scaffolding gem to make UI test automation easier.
25
25
 
26
- ### At the moment Ruby Raider supports generating the following frameworks:
26
+ ### Supported Frameworks
27
27
 
28
- | Web Testing Framework | Visual Testing Framework | Mobile Testing Framework | Accessibility Testing Framework |
29
- |-----------------------|-----------------------------------|------------------------------------|---------------------------------|
30
- | Cucumber and Selenium | Cucumber, Applitools and Selenium | Cucumber and Appium for IOS | Cucumber and Axe for web |
31
- | Rspec and Selenium | Rspec, Applitools and Selenium | Rspec and Appium for IOS | Rspec and Axe for web |
32
- | Cucumber and Watir | | Cucumber and Appium for Android | |
33
- | Rspec and Watir | | Rspec and Appium for Android | |
34
- | | | Cucumber and Appium Cross-platform | |
35
- | | | Rspec and Appium Cross-platform | |
28
+ | Web Testing Framework | Mobile Testing Framework |
29
+ |-----------------------|------------------------------------|
30
+ | Cucumber and Selenium | Cucumber and Appium for iOS |
31
+ | RSpec and Selenium | RSpec and Appium for iOS |
32
+ | Cucumber and Watir | Cucumber and Appium for Android |
33
+ | RSpec and Watir | RSpec and Appium for Android |
34
+ | Cucumber and Capybara | Cucumber and Appium Cross-platform |
35
+ | RSpec and Capybara | RSpec and Appium Cross-platform |
36
+ | Minitest and Selenium | Minitest and Appium for iOS |
37
+ | Minitest and Watir | Minitest and Appium for Android |
38
+ | Minitest and Capybara | Minitest and Appium Cross-platform |
36
39
 
37
- ***In order to run the Appium tests, download the example [app](https://github.com/saucelabs/my-demo-app-rn).***
38
- ***Remember to use the full path of the app that you download in the capabilities file and start the server using one of
39
- the commands below:***
40
+ **Optional add-ons for web projects:**
41
+ - **Accessibility testing (axe)**: `--accessibility` flag
42
+ - **Visual regression testing (chunky_png)**: `--visual` flag
43
+ - **Performance auditing (Lighthouse)**: `--performance` flag
40
44
 
41
- ```ruby
42
- raider u start_appium
43
- appium --base /wd/hub
44
- ```
45
-
46
- ***In order to run the visual tests with applitools, you need to create an account and get your api key, you can read
47
- more [here](https://applitools.com/docs/topics/overview/obtain-api-key.html#:~:text=If%20you%20already%20have%20an,Your%20key%20will%20be%20displayed.)
48
- .***
45
+ ***The web tests run against the [Raider Test Store](https://raider-test-site.onrender.com/).***
49
46
 
50
- ***To use the open ai integration you need to set up the OPENAI_ACCESS_TOKEN environment variable and
51
- you can also set the optional OPENAI_ORGANIZATION_ID if you have an organization***
47
+ ***To run Appium tests, download the example [app](https://github.com/RaiderHQ/raider_test_app) and start the server:***
52
48
 
53
- This works in all the platforms (Tested on Mac OS, Linux and Windows).
54
-
55
- ## Getting started
49
+ ```bash
50
+ raider u start_appium
51
+ ```
56
52
 
57
- To get the project up and running.
53
+ This works on all platforms (Mac OS, Linux and Windows).
58
54
 
59
- **Just do:**
55
+ ## Getting Started
60
56
 
61
- ```ruby
57
+ ```bash
62
58
  gem install ruby_raider
63
- ```
64
-
65
- **Then do:**
66
-
67
- ```ruby
68
59
  raider new [name_of_project]
69
60
  ```
70
61
 
71
- Then a TUI/CLI will appear where the configuration of which frameworks you want to be generated/scaffolded can be
72
- selected.
62
+ Or with parameters:
73
63
 
74
- Select the ones you will like to work with.
75
-
76
- If you already know which frameworks you want to use, you can do:
77
-
78
- ```ruby
64
+ ```bash
79
65
  raider new [name_of_project] -p framework:[framework] automation:[automation_type]
80
66
  ```
81
67
 
82
- An example of the command above would be:
68
+ ### Commands
83
69
 
84
- ```ruby
85
- raider new test_project -p framework:rspec automation:selenium
86
70
  ```
87
-
88
- ### Ruby raider provides the following list of basic commands
89
-
90
- ###### Anything between square brackets([...]) is where your imput goes
91
-
92
- ```ruby
93
- Commands :
94
- raider generate # Provides access to all the scaffolding commands
95
- raider help [COMMAND] # Describe available commands or one specific command
96
- raider new [PROJECT_NAME] # Creates a new framework based on settings picked
97
- raider utility # Provides access to all the utility commands
98
- raider version # It shows the version of Ruby Raider you are currently using
71
+ raider generate # Scaffolding commands
72
+ raider utility # Utility commands
73
+ raider plugin_manager # Plugin management
74
+ raider new [NAME] # Create new project
75
+ raider version # Show version
76
+ raider help [COMMAND] # Describe commands
99
77
  ```
100
78
 
101
- All the basic commands have their corresponding shortcut:
102
-
103
- * g for generate
104
- * n for new
105
- * u for utility
106
- * v for version
107
-
108
- ### Scaffolding Commands
109
-
110
- Ruby Raider also supports scaffolding:
111
-
112
- * To create a new page object you do: ```raider g page [PAGE_NAME]```
113
- * To create a new spec you do: ```raider g spec [SPEC_NAME]```
114
- * To create a new feature you do: ```raider g feature [FEATURE_NAME]```
115
- * To create a new steps definition you do: ```raider g steps [STEPS_NAME]```
116
- * To create both a page/spec or a page/feature/steps you do: ```raider g scaffold [SCAFFOLD_NAME]```
79
+ Shortcuts: `g` (generate), `n` (new), `u` (utility), `v` (version), `pm` (plugin_manager)
117
80
 
118
- It's possible to add the option --path or p if you want to specify where to create your features, pages, helpers and
119
- specs.
81
+ ### Scaffolding
120
82
 
121
- If you want to set the default path for the creation of your features, helpers and specs:
122
-
123
- ```ruby
124
- raider u path [PATH_NAME] - -feature or f
125
- raider u path [PATH_NAME] - -spec or s
126
- raider u path [PATH_NAME] - -helper or h
127
83
  ```
128
-
129
- If you don't specify an option, path will assume you want to change the default path for pages.
130
-
131
- ### Appium Server Command
132
-
133
- To initialise Appium server run this command:
134
-
135
- ```ruby
136
- raider u start_appium
84
+ raider g page [NAME] # Create page object
85
+ raider g spec [NAME] # Create RSpec test
86
+ raider g feature [NAME] # Create Cucumber feature
87
+ raider g steps [NAME] # Create step definitions
88
+ raider g scaffold [NAME] # Create page + test + steps
137
89
  ```
138
90
 
139
91
  ### Sponsors
@@ -0,0 +1,3 @@
1
+ -I .
2
+ --require helpers/spec_helper
3
+ --format documentation
@@ -0,0 +1 @@
1
+ <%= ruby_version %>
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- <%=- ERB.new(File.read(File.expand_path('./partials/allure_requirements.tt', __dir__))).result(binding).strip! -%>
4
- <%- allure = ERB.new(File.read(File.expand_path('./partials/allure_imports.tt', __dir__))).result(binding).strip! -%>
3
+ <%=- partial('allure_requirements', trim: false, strip: true) -%>
4
+ <%- allure = partial('allure_imports', trim: false, strip: true) -%>
5
5
 
6
6
 
7
7
  module AllureHelper
@@ -21,6 +21,17 @@ module AllureHelper
21
21
  test_case: true
22
22
  )
23
23
  end
24
+
25
+ def add_video(name, file)
26
+ return unless file && File.exist?(file)
27
+
28
+ Allure.add_attachment(
29
+ name: "Recording: #{name}",
30
+ source: File.new(file),
31
+ type: 'video/mp4',
32
+ test_case: true
33
+ )
34
+ end
24
35
  <%- if rspec? -%>
25
36
 
26
37
  def formatter