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.
- checksums.yaml +4 -4
- data/.github/workflows/e2e_tests.yml +0 -17
- data/.github/workflows/system_tests.yml +0 -22
- data/README.md +8 -63
- data/lib/commands/scaffolding_commands.rb +1 -192
- data/lib/commands/utility_commands.rb +0 -52
- data/lib/generators/automation/templates/login.tt +1 -9
- data/lib/generators/automation/templates/page.tt +1 -7
- data/lib/generators/automation/templates/partials/initialize_selector.tt +1 -3
- data/lib/generators/automation/templates/partials/visit_method.tt +2 -6
- data/lib/generators/common_generator.rb +0 -6
- data/lib/generators/cucumber/cucumber_generator.rb +0 -24
- data/lib/generators/cucumber/templates/accessibility_steps.tt +2 -6
- data/lib/generators/cucumber/templates/cucumber.tt +0 -7
- data/lib/generators/cucumber/templates/env.tt +1 -3
- data/lib/generators/cucumber/templates/partials/appium_env.tt +1 -6
- data/lib/generators/cucumber/templates/partials/selenium_env.tt +0 -13
- data/lib/generators/cucumber/templates/partials/watir_env.tt +0 -13
- data/lib/generators/cucumber/templates/partials/web_steps.tt +4 -4
- data/lib/generators/cucumber/templates/world.tt +1 -3
- data/lib/generators/generator.rb +2 -46
- data/lib/generators/helper_generator.rb +4 -49
- data/lib/generators/infrastructure/templates/github.tt +2 -2
- data/lib/generators/infrastructure/templates/github_appium.tt +2 -6
- data/lib/generators/invoke_generators.rb +4 -25
- data/lib/generators/menu_generator.rb +10 -100
- data/lib/generators/rspec/rspec_generator.rb +0 -11
- data/lib/generators/rspec/templates/accessibility_spec.tt +2 -6
- data/lib/generators/rspec/templates/spec.tt +6 -8
- data/lib/generators/templates/common/gemfile.tt +0 -26
- data/lib/generators/templates/common/git_ignore.tt +0 -2
- data/lib/generators/templates/common/partials/mobile_config.tt +0 -4
- data/lib/generators/templates/common/partials/web_config.tt +1 -12
- data/lib/generators/templates/common/rakefile.tt +0 -9
- data/lib/generators/templates/common/read_me.tt +4 -10
- data/lib/generators/templates/helpers/allure_helper.tt +0 -10
- data/lib/generators/templates/helpers/browser_helper.tt +0 -5
- data/lib/generators/templates/helpers/partials/quit_driver.tt +1 -3
- data/lib/generators/templates/helpers/partials/screenshot.tt +1 -3
- data/lib/generators/templates/helpers/partials/selenium_driver.tt +0 -5
- data/lib/generators/templates/helpers/spec_helper.tt +2 -44
- data/lib/generators/templates/helpers/test_helper.tt +0 -7
- data/lib/ruby_raider.rb +2 -50
- data/lib/scaffolding/project_detector.rb +2 -9
- data/lib/scaffolding/scaffolding.rb +0 -109
- data/lib/scaffolding/templates/component.tt +1 -4
- data/lib/scaffolding/templates/page_object.tt +0 -10
- data/lib/scaffolding/templates/spec.tt +2 -6
- data/lib/scaffolding/templates/steps.tt +0 -2
- data/lib/utilities/utilities.rb +20 -33
- data/lib/version +1 -1
- data/sig/commands/scaffolding_commands.rbs +0 -12
- data/sig/commands/utility_commands.rbs +0 -7
- data/sig/generators/cucumber/cucumber_generator.rbs +0 -4
- data/sig/generators/generator.rbs +0 -11
- data/sig/generators/helper_generator.rbs +1 -6
- data/sig/generators/invoke_generators.rbs +1 -2
- data/sig/generators/menu_generator.rbs +2 -6
- data/sig/generators/rspec/rspec_generator.rbs +0 -2
- data/sig/ruby_raider.rbs +1 -3
- data/sig/scaffolding/project_detector.rbs +0 -1
- data/sig/scaffolding/scaffolding.rbs +0 -23
- data/sig/utilities/utilities.rbs +0 -5
- data/spec/commands/raider_commands_spec.rb +0 -61
- data/spec/integration/commands/browser_update_after_creation_spec.rb +123 -0
- data/spec/integration/commands/scaffolding_commands_spec.rb +0 -20
- data/spec/integration/commands/utility_commands_spec.rb +5 -5
- data/spec/integration/content/ci_content_spec.rb +6 -61
- data/spec/integration/content/common_content_spec.rb +0 -64
- data/spec/integration/content/config_content_spec.rb +0 -86
- data/spec/integration/content/content_helper.rb +2 -2
- data/spec/integration/content/gemfile_content_spec.rb +0 -71
- data/spec/integration/content/helper_content_spec.rb +2 -284
- data/spec/integration/content/page_content_spec.rb +0 -89
- data/spec/integration/content/syntax_validation_spec.rb +2 -2
- data/spec/integration/content/test_content_spec.rb +0 -119
- data/spec/integration/end_to_end_features_spec.rb +13 -435
- data/spec/integration/end_to_end_spec.rb +0 -96
- data/spec/integration/generators/axe_addon_spec.rb +2 -52
- data/spec/integration/generators/common_generator_spec.rb +1 -13
- data/spec/integration/generators/config_features_spec.rb +3 -81
- data/spec/integration/generators/github_generator_spec.rb +5 -15
- data/spec/integration/generators/helpers_generator_spec.rb +0 -37
- data/spec/integration/scaffolding_e2e_spec.rb +2 -237
- data/spec/integration/settings_helper.rb +1 -3
- data/spec/integration/spec_helper.rb +6 -11
- data/spec/menus/menu_generator_spec.rb +4 -107
- data/spec/scaffolding/scaffold_project_detector_spec.rb +4 -26
- data/spec/scaffolding/scaffolding_features_spec.rb +0 -183
- data/spec/system/selenium_spec.rb +1 -4
- data/spec/system/support/system_test_helper.rb +0 -1
- data/spec/system/watir_spec.rb +1 -4
- data/spec/utilities/headless_config_spec.rb +0 -7
- metadata +3 -102
- data/lib/adopter/adopt_menu.rb +0 -146
- data/lib/adopter/converters/base_converter.rb +0 -84
- data/lib/adopter/converters/identity_converter.rb +0 -53
- data/lib/adopter/migration_plan.rb +0 -74
- data/lib/adopter/migrator.rb +0 -96
- data/lib/adopter/plan_builder.rb +0 -275
- data/lib/adopter/project_analyzer.rb +0 -252
- data/lib/adopter/project_detector.rb +0 -157
- data/lib/generators/cucumber/templates/partials/capybara_env.tt +0 -38
- data/lib/generators/cucumber/templates/partials/capybara_world.tt +0 -6
- data/lib/generators/cucumber/templates/performance_feature.tt +0 -5
- data/lib/generators/cucumber/templates/performance_steps.tt +0 -17
- data/lib/generators/cucumber/templates/visual_feature.tt +0 -5
- data/lib/generators/cucumber/templates/visual_steps.tt +0 -19
- data/lib/generators/infrastructure/gitlab_generator.rb +0 -11
- data/lib/generators/infrastructure/templates/gitlab.tt +0 -46
- data/lib/generators/minitest/minitest_generator.rb +0 -35
- data/lib/generators/minitest/templates/accessibility_test.tt +0 -26
- data/lib/generators/minitest/templates/performance_test.tt +0 -18
- data/lib/generators/minitest/templates/test.tt +0 -64
- data/lib/generators/minitest/templates/visual_test.tt +0 -23
- data/lib/generators/rspec/templates/performance_spec.tt +0 -18
- data/lib/generators/rspec/templates/visual_spec.tt +0 -20
- data/lib/generators/templates/common/ruby_version.tt +0 -1
- data/lib/generators/templates/helpers/capybara_helper.tt +0 -32
- data/lib/generators/templates/helpers/debug_helper.tt +0 -190
- data/lib/generators/templates/helpers/partials/debug_diagnostics.tt +0 -7
- data/lib/generators/templates/helpers/partials/debug_start.tt +0 -7
- data/lib/generators/templates/helpers/performance_helper.tt +0 -57
- data/lib/generators/templates/helpers/visual_helper.tt +0 -58
- data/lib/llm/client.rb +0 -79
- data/lib/llm/config.rb +0 -57
- data/lib/llm/prompts.rb +0 -84
- data/lib/llm/provider.rb +0 -27
- data/lib/llm/providers/anthropic_provider.rb +0 -43
- data/lib/llm/providers/ollama_provider.rb +0 -56
- data/lib/llm/providers/openai_provider.rb +0 -42
- data/lib/llm/response_parser.rb +0 -67
- data/lib/plugin/plugin.rb +0 -111
- data/lib/plugin/plugin_exposer.rb +0 -55
- data/lib/scaffolding/crud_generator.rb +0 -94
- data/lib/scaffolding/dry_run_presenter.rb +0 -16
- data/lib/scaffolding/page_introspector.rb +0 -45
- data/lib/scaffolding/scaffold_menu.rb +0 -103
- data/lib/scaffolding/templates/page_from_url.tt +0 -75
- data/lib/scaffolding/templates/spec_from_page.tt +0 -31
- data/lib/scaffolding/templates/spec_from_url.tt +0 -46
- data/lib/scaffolding/url_analyzer.rb +0 -179
- data/sig/adopter/adopt_menu.rbs +0 -25
- data/sig/adopter/converters/base_converter.rbs +0 -23
- data/sig/adopter/converters/identity_converter.rbs +0 -16
- data/sig/adopter/migration_plan.rbs +0 -34
- data/sig/adopter/migrator.rbs +0 -21
- data/sig/adopter/plan_builder.rbs +0 -38
- data/sig/adopter/project_analyzer.rbs +0 -39
- data/sig/adopter/project_detector.rbs +0 -26
- data/sig/generators/infrastructure/gitlab_generator.rbs +0 -4
- data/sig/generators/minitest/minitest_generator.rbs +0 -8
- data/sig/llm/client.rbs +0 -15
- data/sig/llm/config.rbs +0 -20
- data/sig/llm/prompts.rbs +0 -8
- data/sig/llm/provider.rbs +0 -12
- data/sig/llm/providers/anthropic_provider.rbs +0 -16
- data/sig/llm/providers/ollama_provider.rbs +0 -18
- data/sig/llm/providers/openai_provider.rbs +0 -16
- data/sig/llm/response_parser.rbs +0 -13
- data/sig/plugin/plugin.rbs +0 -24
- data/sig/plugin/plugin_exposer.rbs +0 -20
- data/sig/scaffolding/crud_generator.rbs +0 -16
- data/sig/scaffolding/dry_run_presenter.rbs +0 -4
- data/sig/scaffolding/page_introspector.rbs +0 -14
- data/sig/scaffolding/scaffold_menu.rbs +0 -18
- data/sig/scaffolding/url_analyzer.rbs +0 -28
- data/spec/adopter/adopt_menu_spec.rb +0 -176
- data/spec/adopter/converters/identity_converter_spec.rb +0 -145
- data/spec/adopter/migration_plan_spec.rb +0 -113
- data/spec/adopter/migrator_spec.rb +0 -277
- data/spec/adopter/plan_builder_spec.rb +0 -298
- data/spec/adopter/project_analyzer_spec.rb +0 -337
- data/spec/adopter/project_detector_spec.rb +0 -295
- data/spec/generators/generator_spec.rb +0 -23
- data/spec/integration/content/reporter_content_spec.rb +0 -236
- data/spec/integration/content/skip_flags_content_spec.rb +0 -206
- data/spec/integration/generators/debug_helper_spec.rb +0 -68
- data/spec/integration/generators/gitlab_generator_spec.rb +0 -38
- data/spec/integration/generators/lighthouse_addon_spec.rb +0 -132
- data/spec/integration/generators/minitest_generator_spec.rb +0 -64
- data/spec/integration/generators/reporter_spec.rb +0 -159
- data/spec/integration/generators/skip_flags_spec.rb +0 -134
- data/spec/integration/generators/visual_addon_spec.rb +0 -148
- data/spec/llm/client_spec.rb +0 -79
- data/spec/llm/config_spec.rb +0 -92
- data/spec/llm/prompts_spec.rb +0 -49
- data/spec/llm/response_parser_spec.rb +0 -92
- data/spec/menus/adopter_adopt_menu_spec.rb +0 -97
- data/spec/scaffolding/page_introspector_spec.rb +0 -82
- data/spec/scaffolding/url_analyzer_spec.rb +0 -110
- data/spec/system/adopt_matrix_spec.rb +0 -537
- data/spec/system/adopt_spec.rb +0 -225
- 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
|
data/lib/llm/response_parser.rb
DELETED
|
@@ -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
|