ruby_raider 3.0.1 → 3.2.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 (194) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/e2e_tests.yml +0 -17
  3. data/.github/workflows/system_tests.yml +0 -22
  4. data/README.md +8 -63
  5. data/lib/commands/scaffolding_commands.rb +1 -192
  6. data/lib/commands/utility_commands.rb +0 -52
  7. data/lib/generators/automation/templates/login.tt +1 -9
  8. data/lib/generators/automation/templates/page.tt +1 -7
  9. data/lib/generators/automation/templates/partials/initialize_selector.tt +1 -3
  10. data/lib/generators/automation/templates/partials/visit_method.tt +2 -6
  11. data/lib/generators/common_generator.rb +0 -6
  12. data/lib/generators/cucumber/cucumber_generator.rb +0 -24
  13. data/lib/generators/cucumber/templates/accessibility_steps.tt +2 -6
  14. data/lib/generators/cucumber/templates/cucumber.tt +0 -7
  15. data/lib/generators/cucumber/templates/env.tt +1 -3
  16. data/lib/generators/cucumber/templates/partials/appium_env.tt +1 -6
  17. data/lib/generators/cucumber/templates/partials/selenium_env.tt +0 -13
  18. data/lib/generators/cucumber/templates/partials/watir_env.tt +0 -13
  19. data/lib/generators/cucumber/templates/partials/web_steps.tt +4 -4
  20. data/lib/generators/cucumber/templates/world.tt +1 -3
  21. data/lib/generators/generator.rb +2 -46
  22. data/lib/generators/helper_generator.rb +4 -49
  23. data/lib/generators/infrastructure/templates/github.tt +2 -2
  24. data/lib/generators/infrastructure/templates/github_appium.tt +2 -6
  25. data/lib/generators/invoke_generators.rb +4 -25
  26. data/lib/generators/menu_generator.rb +10 -100
  27. data/lib/generators/rspec/rspec_generator.rb +0 -11
  28. data/lib/generators/rspec/templates/accessibility_spec.tt +2 -6
  29. data/lib/generators/rspec/templates/spec.tt +6 -8
  30. data/lib/generators/templates/common/gemfile.tt +0 -26
  31. data/lib/generators/templates/common/git_ignore.tt +0 -2
  32. data/lib/generators/templates/common/partials/mobile_config.tt +0 -4
  33. data/lib/generators/templates/common/partials/web_config.tt +1 -12
  34. data/lib/generators/templates/common/rakefile.tt +0 -9
  35. data/lib/generators/templates/common/read_me.tt +4 -10
  36. data/lib/generators/templates/helpers/allure_helper.tt +0 -10
  37. data/lib/generators/templates/helpers/browser_helper.tt +0 -5
  38. data/lib/generators/templates/helpers/partials/quit_driver.tt +1 -3
  39. data/lib/generators/templates/helpers/partials/screenshot.tt +1 -3
  40. data/lib/generators/templates/helpers/partials/selenium_driver.tt +0 -5
  41. data/lib/generators/templates/helpers/spec_helper.tt +2 -44
  42. data/lib/generators/templates/helpers/test_helper.tt +0 -7
  43. data/lib/ruby_raider.rb +2 -50
  44. data/lib/scaffolding/project_detector.rb +2 -9
  45. data/lib/scaffolding/scaffolding.rb +0 -109
  46. data/lib/scaffolding/templates/component.tt +1 -4
  47. data/lib/scaffolding/templates/page_object.tt +0 -10
  48. data/lib/scaffolding/templates/spec.tt +2 -6
  49. data/lib/scaffolding/templates/steps.tt +0 -2
  50. data/lib/utilities/utilities.rb +20 -33
  51. data/lib/version +1 -1
  52. data/sig/commands/scaffolding_commands.rbs +0 -12
  53. data/sig/commands/utility_commands.rbs +0 -7
  54. data/sig/generators/cucumber/cucumber_generator.rbs +0 -4
  55. data/sig/generators/generator.rbs +0 -11
  56. data/sig/generators/helper_generator.rbs +1 -6
  57. data/sig/generators/invoke_generators.rbs +1 -2
  58. data/sig/generators/menu_generator.rbs +2 -6
  59. data/sig/generators/rspec/rspec_generator.rbs +0 -2
  60. data/sig/ruby_raider.rbs +1 -3
  61. data/sig/scaffolding/project_detector.rbs +0 -1
  62. data/sig/scaffolding/scaffolding.rbs +0 -23
  63. data/sig/utilities/utilities.rbs +0 -5
  64. data/spec/commands/raider_commands_spec.rb +0 -61
  65. data/spec/integration/commands/browser_update_after_creation_spec.rb +123 -0
  66. data/spec/integration/commands/scaffolding_commands_spec.rb +0 -20
  67. data/spec/integration/commands/utility_commands_spec.rb +5 -5
  68. data/spec/integration/content/ci_content_spec.rb +6 -61
  69. data/spec/integration/content/common_content_spec.rb +0 -64
  70. data/spec/integration/content/config_content_spec.rb +0 -86
  71. data/spec/integration/content/content_helper.rb +2 -2
  72. data/spec/integration/content/gemfile_content_spec.rb +0 -71
  73. data/spec/integration/content/helper_content_spec.rb +2 -284
  74. data/spec/integration/content/page_content_spec.rb +0 -89
  75. data/spec/integration/content/syntax_validation_spec.rb +2 -2
  76. data/spec/integration/content/test_content_spec.rb +0 -119
  77. data/spec/integration/end_to_end_features_spec.rb +13 -435
  78. data/spec/integration/end_to_end_spec.rb +0 -96
  79. data/spec/integration/generators/axe_addon_spec.rb +2 -52
  80. data/spec/integration/generators/common_generator_spec.rb +1 -13
  81. data/spec/integration/generators/config_features_spec.rb +3 -81
  82. data/spec/integration/generators/github_generator_spec.rb +5 -15
  83. data/spec/integration/generators/helpers_generator_spec.rb +0 -37
  84. data/spec/integration/scaffolding_e2e_spec.rb +2 -237
  85. data/spec/integration/settings_helper.rb +1 -3
  86. data/spec/integration/spec_helper.rb +6 -11
  87. data/spec/menus/menu_generator_spec.rb +4 -107
  88. data/spec/scaffolding/scaffold_project_detector_spec.rb +4 -26
  89. data/spec/scaffolding/scaffolding_features_spec.rb +0 -183
  90. data/spec/system/selenium_spec.rb +1 -4
  91. data/spec/system/support/system_test_helper.rb +0 -1
  92. data/spec/system/watir_spec.rb +1 -4
  93. data/spec/utilities/headless_config_spec.rb +0 -7
  94. metadata +3 -102
  95. data/lib/adopter/adopt_menu.rb +0 -146
  96. data/lib/adopter/converters/base_converter.rb +0 -84
  97. data/lib/adopter/converters/identity_converter.rb +0 -53
  98. data/lib/adopter/migration_plan.rb +0 -74
  99. data/lib/adopter/migrator.rb +0 -96
  100. data/lib/adopter/plan_builder.rb +0 -275
  101. data/lib/adopter/project_analyzer.rb +0 -252
  102. data/lib/adopter/project_detector.rb +0 -157
  103. data/lib/generators/cucumber/templates/partials/capybara_env.tt +0 -38
  104. data/lib/generators/cucumber/templates/partials/capybara_world.tt +0 -6
  105. data/lib/generators/cucumber/templates/performance_feature.tt +0 -5
  106. data/lib/generators/cucumber/templates/performance_steps.tt +0 -17
  107. data/lib/generators/cucumber/templates/visual_feature.tt +0 -5
  108. data/lib/generators/cucumber/templates/visual_steps.tt +0 -19
  109. data/lib/generators/infrastructure/gitlab_generator.rb +0 -11
  110. data/lib/generators/infrastructure/templates/gitlab.tt +0 -46
  111. data/lib/generators/minitest/minitest_generator.rb +0 -35
  112. data/lib/generators/minitest/templates/accessibility_test.tt +0 -26
  113. data/lib/generators/minitest/templates/performance_test.tt +0 -18
  114. data/lib/generators/minitest/templates/test.tt +0 -64
  115. data/lib/generators/minitest/templates/visual_test.tt +0 -23
  116. data/lib/generators/rspec/templates/performance_spec.tt +0 -18
  117. data/lib/generators/rspec/templates/visual_spec.tt +0 -20
  118. data/lib/generators/templates/common/ruby_version.tt +0 -1
  119. data/lib/generators/templates/helpers/capybara_helper.tt +0 -32
  120. data/lib/generators/templates/helpers/debug_helper.tt +0 -190
  121. data/lib/generators/templates/helpers/partials/debug_diagnostics.tt +0 -7
  122. data/lib/generators/templates/helpers/partials/debug_start.tt +0 -7
  123. data/lib/generators/templates/helpers/performance_helper.tt +0 -57
  124. data/lib/generators/templates/helpers/visual_helper.tt +0 -58
  125. data/lib/llm/client.rb +0 -79
  126. data/lib/llm/config.rb +0 -57
  127. data/lib/llm/prompts.rb +0 -84
  128. data/lib/llm/provider.rb +0 -27
  129. data/lib/llm/providers/anthropic_provider.rb +0 -43
  130. data/lib/llm/providers/ollama_provider.rb +0 -56
  131. data/lib/llm/providers/openai_provider.rb +0 -42
  132. data/lib/llm/response_parser.rb +0 -67
  133. data/lib/plugin/plugin.rb +0 -111
  134. data/lib/plugin/plugin_exposer.rb +0 -55
  135. data/lib/scaffolding/crud_generator.rb +0 -94
  136. data/lib/scaffolding/dry_run_presenter.rb +0 -16
  137. data/lib/scaffolding/page_introspector.rb +0 -45
  138. data/lib/scaffolding/scaffold_menu.rb +0 -103
  139. data/lib/scaffolding/templates/page_from_url.tt +0 -75
  140. data/lib/scaffolding/templates/spec_from_page.tt +0 -31
  141. data/lib/scaffolding/templates/spec_from_url.tt +0 -46
  142. data/lib/scaffolding/url_analyzer.rb +0 -179
  143. data/sig/adopter/adopt_menu.rbs +0 -25
  144. data/sig/adopter/converters/base_converter.rbs +0 -23
  145. data/sig/adopter/converters/identity_converter.rbs +0 -16
  146. data/sig/adopter/migration_plan.rbs +0 -34
  147. data/sig/adopter/migrator.rbs +0 -21
  148. data/sig/adopter/plan_builder.rbs +0 -38
  149. data/sig/adopter/project_analyzer.rbs +0 -39
  150. data/sig/adopter/project_detector.rbs +0 -26
  151. data/sig/generators/infrastructure/gitlab_generator.rbs +0 -4
  152. data/sig/generators/minitest/minitest_generator.rbs +0 -8
  153. data/sig/llm/client.rbs +0 -15
  154. data/sig/llm/config.rbs +0 -20
  155. data/sig/llm/prompts.rbs +0 -8
  156. data/sig/llm/provider.rbs +0 -12
  157. data/sig/llm/providers/anthropic_provider.rbs +0 -16
  158. data/sig/llm/providers/ollama_provider.rbs +0 -18
  159. data/sig/llm/providers/openai_provider.rbs +0 -16
  160. data/sig/llm/response_parser.rbs +0 -13
  161. data/sig/plugin/plugin.rbs +0 -24
  162. data/sig/plugin/plugin_exposer.rbs +0 -20
  163. data/sig/scaffolding/crud_generator.rbs +0 -16
  164. data/sig/scaffolding/dry_run_presenter.rbs +0 -4
  165. data/sig/scaffolding/page_introspector.rbs +0 -14
  166. data/sig/scaffolding/scaffold_menu.rbs +0 -18
  167. data/sig/scaffolding/url_analyzer.rbs +0 -28
  168. data/spec/adopter/adopt_menu_spec.rb +0 -176
  169. data/spec/adopter/converters/identity_converter_spec.rb +0 -145
  170. data/spec/adopter/migration_plan_spec.rb +0 -113
  171. data/spec/adopter/migrator_spec.rb +0 -277
  172. data/spec/adopter/plan_builder_spec.rb +0 -298
  173. data/spec/adopter/project_analyzer_spec.rb +0 -337
  174. data/spec/adopter/project_detector_spec.rb +0 -295
  175. data/spec/generators/generator_spec.rb +0 -23
  176. data/spec/integration/content/reporter_content_spec.rb +0 -236
  177. data/spec/integration/content/skip_flags_content_spec.rb +0 -206
  178. data/spec/integration/generators/debug_helper_spec.rb +0 -68
  179. data/spec/integration/generators/gitlab_generator_spec.rb +0 -38
  180. data/spec/integration/generators/lighthouse_addon_spec.rb +0 -132
  181. data/spec/integration/generators/minitest_generator_spec.rb +0 -64
  182. data/spec/integration/generators/reporter_spec.rb +0 -159
  183. data/spec/integration/generators/skip_flags_spec.rb +0 -134
  184. data/spec/integration/generators/visual_addon_spec.rb +0 -148
  185. data/spec/llm/client_spec.rb +0 -79
  186. data/spec/llm/config_spec.rb +0 -92
  187. data/spec/llm/prompts_spec.rb +0 -49
  188. data/spec/llm/response_parser_spec.rb +0 -92
  189. data/spec/menus/adopter_adopt_menu_spec.rb +0 -97
  190. data/spec/scaffolding/page_introspector_spec.rb +0 -82
  191. data/spec/scaffolding/url_analyzer_spec.rb +0 -110
  192. data/spec/system/adopt_matrix_spec.rb +0 -537
  193. data/spec/system/adopt_spec.rb +0 -225
  194. data/spec/system/capybara_spec.rb +0 -42
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../provider'
4
-
5
- module Llm
6
- module Providers
7
- class OpenaiProvider < Provider
8
- DEFAULT_MODEL = 'gpt-4o-mini'
9
-
10
- def initialize(api_key:, model: nil)
11
- super()
12
- @api_key = api_key
13
- @model = model || DEFAULT_MODEL
14
- end
15
-
16
- def complete(prompt, system_prompt: nil)
17
- client = build_client
18
- messages = build_messages(prompt, system_prompt)
19
- response = client.chat(parameters: { model: @model, messages:, temperature: 0.2 })
20
- response.dig('choices', 0, 'message', 'content')
21
- rescue StandardError => e
22
- warn "[Ruby Raider] OpenAI error: #{e.message}"
23
- nil
24
- end
25
-
26
- def available?
27
- require 'openai'
28
- !@api_key.nil? && !@api_key.empty?
29
- rescue LoadError
30
- warn '[Ruby Raider] Install ruby-openai gem: gem install ruby-openai'
31
- false
32
- end
33
-
34
- private
35
-
36
- def build_client
37
- require 'openai'
38
- OpenAI::Client.new(access_token: @api_key)
39
- end
40
- end
41
- end
42
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
-
5
- module Llm
6
- # Extracts JSON from LLM responses, handling markdown wrapping and malformed output.
7
- # Returns nil on parse failure — callers should fall back to non-AI path.
8
- module ResponseParser
9
- module_function
10
-
11
- def parse_json(response)
12
- return nil if response.nil? || response.strip.empty?
13
-
14
- json_str = extract_json(response)
15
- result = JSON.parse(json_str, symbolize_names: true)
16
- result.is_a?(Hash) ? result : nil
17
- rescue JSON::ParserError
18
- nil
19
- end
20
-
21
- def extract_elements(response)
22
- parsed = parse_json(response)
23
- return nil unless parsed && parsed[:elements].is_a?(Array)
24
-
25
- parsed[:elements].map { |el| normalize_element(el) }.compact
26
- end
27
-
28
- def extract_scenarios(response)
29
- parsed = parse_json(response)
30
- return nil unless parsed && parsed[:scenarios].is_a?(Array)
31
-
32
- parsed[:scenarios]
33
- end
34
-
35
- def normalize_element(element)
36
- return nil unless element[:name] && element[:type] && element[:locator]
37
-
38
- locator = element[:locator]
39
- locator = { type: locator[:type]&.to_sym, value: locator[:value] } if locator.is_a?(Hash)
40
-
41
- {
42
- name: element[:name].to_s.gsub(/[^a-z0-9_]/i, '_').downcase,
43
- type: element[:type].to_sym,
44
- locator:,
45
- purpose: element[:purpose],
46
- input_type: element[:input_type],
47
- text: element[:text]
48
- }.compact
49
- end
50
-
51
- def extract_json(text)
52
- # Try raw JSON first
53
- stripped = text.strip
54
- return stripped if stripped.start_with?('{')
55
-
56
- # Try markdown code block
57
- match = text.match(/```(?:json)?\s*\n?(.*?)\n?\s*```/m)
58
- return match[1].strip if match
59
-
60
- # Try finding first { ... } block
61
- brace_match = text.match(/(\{.*\})/m)
62
- return brace_match[1] if brace_match
63
-
64
- text
65
- end
66
- end
67
- end
data/lib/plugin/plugin.rb DELETED
@@ -1,111 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
- require_relative 'plugin_exposer'
5
-
6
- module RubyRaider
7
- module Plugin
8
- class << self
9
- def add_plugin(plugin_name)
10
- return gemfile_guard unless File.exist?('Gemfile')
11
- return pp 'The plugin was not found' unless available?(plugin_name)
12
- return pp 'The plugin is already installed' if installed?(plugin_name)
13
-
14
- pp "Adding #{plugin_name}..."
15
- add_plugin_to_gemfile(plugin_name)
16
- invalidate_gemfile_cache
17
- system('bundle install')
18
- PluginExposer.expose_commands(plugin_name)
19
- pp "The plugin #{plugin_name} is added"
20
- end
21
-
22
- def delete_plugin(plugin_name)
23
- return gemfile_guard unless File.exist?('Gemfile')
24
- return 'The plugin is not installed' unless installed_plugins.include?(plugin_name)
25
-
26
- pp "Deleting #{plugin_name}..."
27
- remove_plugin_from_gemfile(plugin_name)
28
- invalidate_gemfile_cache
29
- PluginExposer.remove_command(plugin_name)
30
- system('bundle install')
31
- pp "The plugin #{plugin_name} is deleted"
32
- end
33
-
34
- def installed_plugins
35
- return gemfile_guard unless File.exist?('Gemfile')
36
-
37
- cached_gemfile_lines.filter_map do |line|
38
- stripped = line.sub('gem ', '').strip.delete("'")
39
- stripped if plugins.include?(stripped)
40
- end
41
- end
42
-
43
- def installed?(plugin_name)
44
- installed_plugins.include?(plugin_name)
45
- end
46
-
47
- def available?(plugin_name)
48
- plugins.include?(plugin_name)
49
- end
50
-
51
- def camelize(str)
52
- str.split('_').collect(&:capitalize).join
53
- end
54
-
55
- def plugins
56
- ['great_axe']
57
- end
58
-
59
- private
60
-
61
- # Read Gemfile once and cache until explicitly invalidated
62
- def cached_gemfile_lines
63
- @cached_gemfile_lines ||= File.readlines('Gemfile')
64
- end
65
-
66
- def invalidate_gemfile_cache
67
- @cached_gemfile_lines = nil
68
- end
69
-
70
- def add_plugin_to_gemfile(plugin_name)
71
- return gemfile_guard unless File.exist?('Gemfile')
72
-
73
- lines = cached_gemfile_lines
74
- has_comment = lines.any? { |l| l.include?('Ruby Raider Plugins') }
75
- has_plugin = lines.any? { |l| l.include?(plugin_name) }
76
-
77
- File.open('Gemfile', 'a') do |file|
78
- file.puts "\n# Ruby Raider Plugins\n" unless has_comment
79
- file.puts "gem '#{plugin_name}'" unless has_plugin
80
- end
81
- end
82
-
83
- def remove_plugin_from_gemfile(plugin_name)
84
- output_lines = remove_plugins_and_comments(plugin_name)
85
- update_gemfile(output_lines)
86
- end
87
-
88
- def last_plugin?
89
- installed_plugins.count == 1
90
- end
91
-
92
- def remove_plugins_and_comments(plugin_name)
93
- cached_gemfile_lines.reject do |line|
94
- line.include?(plugin_name) || line.include?('Ruby Raider Plugins') && last_plugin?
95
- end
96
- end
97
-
98
- def update_gemfile(output_lines)
99
- return gemfile_guard unless File.exist?('Gemfile')
100
-
101
- File.open('Gemfile', 'w') do |file|
102
- output_lines.each { |line| file.puts line }
103
- end
104
- end
105
-
106
- def gemfile_guard
107
- pp 'There is no Gemfile, please create one to install plugins'
108
- end
109
- end
110
- end
111
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../plugin/plugin'
4
-
5
- module RubyRaider
6
- module PluginExposer
7
- class << self
8
- FILE_PATH = File.expand_path('../commands/loaded_commands.rb', __dir__)
9
- def expose_commands(plugin_name)
10
- commands = read_loaded_commands
11
- return pp 'The plugin is already installed' if commands.any? { |l| l.include?(plugin_name) }
12
-
13
- has_subcommands = commands.any? { |l| l.include?('subcommand') }
14
-
15
- File.open(FILE_PATH, 'w') do |file|
16
- commands.each do |line|
17
- file.puts line
18
- file.puts "require '#{plugin_name}'" if line.include?("require 'thor'")
19
- if line.strip == 'class LoadedCommands < Thor' && !has_subcommands
20
- file.puts formatted_command_without_space(plugin_name)
21
- elsif line.strip == 'class LoadedCommands < Thor' && has_subcommands
22
- file.puts formatted_command_with_space(plugin_name)
23
- end
24
- end
25
- end
26
- end
27
-
28
- def remove_command(plugin_name)
29
- commands = read_loaded_commands
30
- return pp 'The plugin is not installed' unless commands.any? { |l| l.include?(plugin_name) }
31
-
32
- output_lines = commands.reject { |line| line.include?(plugin_name) }
33
-
34
- File.open(FILE_PATH, 'w') do |file|
35
- output_lines.each { |line| file.puts line }
36
- end
37
- end
38
-
39
- private
40
-
41
- def read_loaded_commands
42
- File.readlines(FILE_PATH)
43
- end
44
-
45
- def formatted_command_without_space(plugin_name)
46
- "desc '#{plugin_name}', 'Provides access to all the commands for #{plugin_name}'\n" \
47
- "subcommand '#{plugin_name}', #{Plugin.camelize(plugin_name)}::PluginCommands"
48
- end
49
-
50
- def formatted_command_with_space(plugin_name)
51
- "\n#{formatted_command_without_space(plugin_name)}"
52
- end
53
- end
54
- end
55
- end
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'name_normalizer'
4
-
5
- class CrudGenerator
6
- ACTIONS = %w[list create detail edit].freeze
7
-
8
- def initialize(base_name, scaffolding_class, config_loader)
9
- @base_name = NameNormalizer.normalize(base_name)
10
- @scaffolding_class = scaffolding_class
11
- @config_loader = config_loader
12
- end
13
-
14
- def generate
15
- generated = []
16
- ACTIONS.each do |action|
17
- name = "#{@base_name}_#{action}"
18
- generate_page(name)
19
- generate_test(name)
20
- generated << name
21
- end
22
- generate_model
23
- generated
24
- end
25
-
26
- def planned_files
27
- files = []
28
- ACTIONS.each do |action|
29
- name = "#{@base_name}_#{action}"
30
- files << "page_objects/pages/#{name}.rb"
31
- files << test_path(name)
32
- end
33
- files << "models/data/#{@base_name}.yml"
34
- files
35
- end
36
-
37
- private
38
-
39
- def generate_page(name)
40
- path = @config_loader.call('page')
41
- @scaffolding_class.new([name, path]).generate_page
42
- end
43
-
44
- def generate_test(name)
45
- if Dir.exist?('features')
46
- path = @config_loader.call('feature')
47
- @scaffolding_class.new([name, path]).generate_feature
48
- path = @config_loader.call('steps')
49
- @scaffolding_class.new([name, path]).generate_steps
50
- elsif Dir.exist?('test')
51
- path = @config_loader.call('spec')
52
- @scaffolding_class.new([name, path]).generate_spec
53
- else
54
- path = @config_loader.call('spec')
55
- @scaffolding_class.new([name, path]).generate_spec
56
- end
57
- end
58
-
59
- def generate_model
60
- model_path = "models/data/#{@base_name}.yml"
61
- return if File.exist?(model_path)
62
-
63
- FileUtils.mkdir_p(File.dirname(model_path))
64
- File.write(model_path, model_content)
65
- end
66
-
67
- def model_content
68
- <<~YAML
69
- # Data model for #{@base_name}
70
- # Used with ModelFactory for test data generation
71
- default:
72
- name: 'Test #{@base_name.capitalize}'
73
- email: 'test@example.com'
74
-
75
- valid:
76
- name: 'Valid #{@base_name.capitalize}'
77
- email: 'valid@example.com'
78
-
79
- invalid:
80
- name: ''
81
- email: 'invalid'
82
- YAML
83
- end
84
-
85
- def test_path(name)
86
- if Dir.exist?('features')
87
- "features/#{name}.feature"
88
- elsif Dir.exist?('test')
89
- "test/test_#{name}.rb"
90
- else
91
- "spec/#{name}_page_spec.rb"
92
- end
93
- end
94
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DryRunPresenter
4
- module_function
5
-
6
- def preview(planned_files)
7
- return [] if planned_files.empty?
8
-
9
- puts '[dry-run] Would create:'
10
- planned_files.each do |file|
11
- puts " #{file}"
12
- end
13
- puts ''
14
- planned_files
15
- end
16
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'ripper'
4
-
5
- class PageIntrospector
6
- SKIP_METHODS = %w[initialize url to_s inspect].freeze
7
-
8
- attr_reader :class_name, :methods
9
-
10
- def initialize(file_path)
11
- @source = File.read(file_path)
12
- @class_name = extract_class_name
13
- @methods = extract_public_methods
14
- end
15
-
16
- private
17
-
18
- def extract_class_name
19
- match = @source.match(/class\s+(\w+)/)
20
- match ? match[1] : 'UnknownPage'
21
- end
22
-
23
- def extract_public_methods
24
- in_private = false
25
- results = []
26
-
27
- @source.each_line do |line|
28
- stripped = line.strip
29
- in_private = true if stripped.match?(/^\s*(private|protected)\s*$/)
30
- next if in_private
31
-
32
- match = stripped.match(/^\s*def\s+(\w+)(\(([^)]*)\))?/)
33
- next unless match
34
-
35
- method_name = match[1]
36
- next if SKIP_METHODS.include?(method_name)
37
- next if method_name.start_with?('_')
38
-
39
- params = match[3]&.split(',')&.map(&:strip) || []
40
- results << { name: method_name, params: }
41
- end
42
-
43
- results
44
- end
45
- end
@@ -1,103 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'tty-prompt'
4
- require_relative 'project_detector'
5
-
6
- class ScaffoldMenu
7
- COMPONENTS = {
8
- 'Page object' => :page,
9
- 'Spec (RSpec)' => :spec,
10
- 'Feature (Cucumber)' => :feature,
11
- 'Steps (Cucumber)' => :steps,
12
- 'Helper' => :helper,
13
- 'Component' => :component,
14
- 'Model data' => :model
15
- }.freeze
16
-
17
- def initialize
18
- @prompt = TTY::Prompt.new
19
- end
20
-
21
- def run
22
- names = ask_names
23
- components = ask_components
24
- uses = ask_relationships
25
- preview_and_confirm(names, components, uses)
26
- end
27
-
28
- # Programmatic entry point for raider_desktop
29
- def self.build_options(names:, components:, uses: [])
30
- { names: Array(names), components: Array(components), uses: Array(uses) }
31
- end
32
-
33
- private
34
-
35
- def ask_names
36
- input = @prompt.ask('Enter scaffold name(s), comma-separated:') do |q|
37
- q.required true
38
- end
39
- input.split(',').map(&:strip).reject(&:empty?)
40
- end
41
-
42
- def ask_components
43
- project = ScaffoldProjectDetector.detect
44
- defaults = default_components(project)
45
-
46
- @prompt.multi_select('Select components to generate:', default: defaults) do |menu|
47
- COMPONENTS.each do |label, value|
48
- menu.choice label, value
49
- end
50
- end
51
- end
52
-
53
- def ask_relationships
54
- return [] unless @prompt.yes?('Add page dependencies (--uses)?', default: false)
55
-
56
- pages = Dir.glob('page_objects/pages/*.rb').map { |f| File.basename(f, '.rb') }
57
- return [] if pages.empty?
58
-
59
- @prompt.multi_select('Select dependent pages:', pages)
60
- end
61
-
62
- def preview_and_confirm(names, components, uses)
63
- files = planned_files(names, components)
64
-
65
- @prompt.say("\nWill create:")
66
- files.each { |f| @prompt.say(" #{f}") }
67
- @prompt.say('')
68
-
69
- return nil unless @prompt.yes?('Proceed?')
70
-
71
- { names:, components:, uses: }
72
- end
73
-
74
- def planned_files(names, components)
75
- names.flat_map do |name|
76
- components.map { |comp| file_for(name, comp) }
77
- end
78
- end
79
-
80
- def file_for(name, component)
81
- case component
82
- when :page then "page_objects/pages/#{name}.rb"
83
- when :spec then "spec/#{name}_page_spec.rb"
84
- when :feature then "features/#{name}.feature"
85
- when :steps then "features/step_definitions/#{name}_steps.rb"
86
- when :helper then "helpers/#{name}_helper.rb"
87
- when :component then "page_objects/components/#{name}.rb"
88
- when :model then "models/data/#{name}.yml"
89
- end
90
- end
91
-
92
- def default_components(project)
93
- defaults = [:page]
94
- if project[:has_features]
95
- defaults += %i[feature steps]
96
- elsif project[:has_test]
97
- defaults << :spec
98
- else
99
- defaults << :spec
100
- end
101
- defaults.map { |d| COMPONENTS.key(d) }.compact
102
- end
103
- end
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../abstract/page'
4
-
5
- class <%= page_class_name %> < Page
6
- def url(_page)
7
- '<%= url_data[:url_path] %>'
8
- end
9
-
10
- # Actions
11
- <%- form_elements = url_data[:elements].select { |e| %i[input textarea select].include?(e[:type]) } -%>
12
- <%- button_elements = url_data[:elements].select { |e| %i[button submit].include?(e[:type]) } -%>
13
- <%- unless form_elements.empty? -%>
14
-
15
- <%- if capybara? -%>
16
- def fill_form(<%= form_elements.map { |e| e[:name] }.join(', ') %>)
17
- <%- form_elements.each do |el| -%>
18
- <%- if el[:locator][:type] == :id -%>
19
- fill_in '<%= el[:locator][:value] %>', with: <%= el[:name] %>
20
- <%- elsif el[:locator][:type] == :name -%>
21
- fill_in '<%= el[:locator][:value] %>', with: <%= el[:name] %>
22
- <%- else -%>
23
- find('<%= el[:locator][:value] %>').set(<%= el[:name] %>)
24
- <%- end -%>
25
- <%- end -%>
26
- end
27
- <%- else -%>
28
- def fill_form(<%= form_elements.map { |e| e[:name] }.join(', ') %>)
29
- <%- form_elements.each do |el| -%>
30
- <%= el[:name] %>_field.<%= selenium? ? 'send_keys' : 'set' %> <%= el[:name] %>
31
- <%- end -%>
32
- end
33
- <%- end -%>
34
- <%- end -%>
35
- <%- unless button_elements.empty? -%>
36
-
37
- <%- button_elements.each do |el| -%>
38
- <%- if capybara? -%>
39
- def click_<%= el[:name] %>
40
- click_button '<%= el[:text] || el[:name] %>'
41
- end
42
- <%- else -%>
43
- def click_<%= el[:name] %>
44
- <%= el[:name] %>_button.click
45
- end
46
- <%- end -%>
47
- <%- end -%>
48
- <%- end -%>
49
- <%- unless capybara? -%>
50
-
51
- private
52
-
53
- # Elements
54
- <%- form_elements.each do |el| -%>
55
-
56
- def <%= el[:name] %>_field
57
- <%- if selenium? -%>
58
- driver.find_element(<%= el[:locator][:type] %>: '<%= el[:locator][:value] %>')
59
- <%- elsif watir? -%>
60
- browser.<%= el[:type] == :select ? 'select' : 'text_field' %>(<%= el[:locator][:type] %>: '<%= el[:locator][:value] %>')
61
- <%- end -%>
62
- end
63
- <%- end -%>
64
- <%- button_elements.each do |el| -%>
65
-
66
- def <%= el[:name] %>_button
67
- <%- if selenium? -%>
68
- driver.find_element(<%= el[:locator][:type] %>: '<%= el[:locator][:value] %>')
69
- <%- elsif watir? -%>
70
- browser.button(<%= el[:locator][:type] %>: '<%= el[:locator][:value] %>')
71
- <%- end -%>
72
- end
73
- <%- end -%>
74
- <%- end -%>
75
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../page_objects/pages/<%= normalized_name %>'
4
-
5
- describe '<%= introspected.class_name %>' do
6
- <%- if selenium? -%>
7
- let(:page) { <%= introspected.class_name %>.new(driver) }
8
- <%- elsif watir? -%>
9
- let(:page) { <%= introspected.class_name %>.new(browser) }
10
- <%- elsif capybara? -%>
11
- let(:page) { <%= introspected.class_name %>.new }
12
- <%- end -%>
13
- <%- introspected.methods.each do |method| -%>
14
- <%- scenario = ai_scenarios[method[:name]] -%>
15
-
16
- describe '#<%= method[:name] %>' do
17
- it '<%= scenario ? scenario[:description] : method[:name].tr("_", " ") %>' do
18
- <%- if method[:params].empty? -%>
19
- page.<%= method[:name] %>
20
- <%- else -%>
21
- page.<%= method[:name] %>(<%= method[:params].map { |p| "'#{p}'" }.join(', ') %>)
22
- <%- end -%>
23
- <%- if scenario && scenario[:assertion_hint] -%>
24
- pending '<%= scenario[:assertion_hint] %>'
25
- <%- else -%>
26
- pending 'add assertions'
27
- <%- end -%>
28
- end
29
- end
30
- <%- end -%>
31
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../page_objects/pages/<%= normalized_name %>'
4
-
5
- describe '<%= page_class_name %>' do
6
- <%- if selenium? -%>
7
- let(:page) { <%= page_class_name %>.new(driver) }
8
- <%- elsif watir? -%>
9
- let(:page) { <%= page_class_name %>.new(browser) }
10
- <%- elsif capybara? -%>
11
- let(:page) { <%= page_class_name %>.new }
12
- <%- end -%>
13
-
14
- before do
15
- <%- if capybara? -%>
16
- visit '<%= url_data[:url_path] %>'
17
- <%- elsif selenium? -%>
18
- driver.navigate.to '<%= url_data[:url] %>'
19
- <%- elsif watir? -%>
20
- browser.goto '<%= url_data[:url] %>'
21
- <%- end -%>
22
- end
23
- <%- form_elements = url_data[:elements].select { |e| %i[input textarea select].include?(e[:type]) } -%>
24
- <%- unless form_elements.empty? -%>
25
-
26
- describe '#fill_form' do
27
- it 'fills in the form fields' do
28
- page.fill_form(<%= form_elements.map { |e| "'test_#{e[:name]}'" }.join(', ') %>)
29
- pending 'add assertions'
30
- end
31
- end
32
- <%- end -%>
33
- <%- url_data[:elements].select { |e| %i[button submit].include?(e[:type]) }.each do |el| -%>
34
-
35
- describe '#click_<%= el[:name] %>' do
36
- it 'clicks the <%= el[:name].tr("_", " ") %> button' do
37
- page.click_<%= el[:name] %>
38
- pending 'add assertions'
39
- end
40
- end
41
- <%- end -%>
42
-
43
- it 'loads the page' do
44
- pending 'add assertions'
45
- end
46
- end