ruby_raider 2.0.0 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/e2e_tests.yml +58 -0
- data/.github/workflows/steep.yml +21 -0
- data/.gitignore +1 -1
- data/.reek.yml +46 -4
- data/.ruby-version +1 -1
- data/README.md +138 -77
- data/Steepfile +22 -0
- data/assets/ruby_raider_logo.svg +51 -0
- data/lib/adopter/adopt_menu.rb +11 -15
- data/lib/adopter/converters/base_converter.rb +1 -2
- data/lib/adopter/converters/identity_converter.rb +3 -6
- data/lib/adopter/migration_plan.rb +0 -1
- data/lib/adopter/plan_builder.rb +2 -5
- data/lib/adopter/project_analyzer.rb +1 -5
- data/lib/adopter/project_detector.rb +3 -5
- data/lib/commands/adopt_commands.rb +0 -1
- data/lib/commands/plugin_commands.rb +0 -2
- data/lib/commands/scaffolding_commands.rb +220 -37
- data/lib/commands/utility_commands.rb +82 -2
- data/lib/generators/automation/automation_generator.rb +0 -7
- data/lib/generators/automation/templates/partials/element.tt +1 -1
- data/lib/generators/automation/templates/partials/initialize_selector.tt +0 -7
- data/lib/generators/automation/templates/partials/url_methods.tt +0 -1
- data/lib/generators/common_generator.rb +12 -0
- data/lib/generators/cucumber/cucumber_generator.rb +36 -0
- data/lib/generators/cucumber/templates/accessibility_feature.tt +5 -0
- data/lib/generators/cucumber/templates/accessibility_steps.tt +21 -0
- data/lib/generators/cucumber/templates/cucumber.tt +8 -1
- data/lib/generators/cucumber/templates/feature.tt +0 -4
- data/lib/generators/cucumber/templates/partials/appium_env.tt +5 -0
- data/lib/generators/cucumber/templates/partials/capybara_env.tt +19 -1
- data/lib/generators/cucumber/templates/partials/driver_world.tt +1 -4
- data/lib/generators/cucumber/templates/partials/selenium_env.tt +22 -35
- data/lib/generators/cucumber/templates/partials/watir_env.tt +20 -1
- data/lib/generators/cucumber/templates/partials/web_steps.tt +6 -12
- data/lib/generators/cucumber/templates/performance_feature.tt +5 -0
- data/lib/generators/cucumber/templates/performance_steps.tt +17 -0
- data/lib/generators/cucumber/templates/visual_feature.tt +5 -0
- data/lib/generators/cucumber/templates/visual_steps.tt +19 -0
- data/lib/generators/generator.rb +38 -7
- data/lib/generators/helper_generator.rb +24 -7
- data/lib/generators/infrastructure/templates/github.tt +1 -1
- data/lib/generators/infrastructure/templates/github_appium.tt +2 -2
- data/lib/generators/infrastructure/templates/gitlab.tt +1 -1
- data/lib/generators/invoke_generators.rb +42 -9
- data/lib/generators/menu_generator.rb +120 -11
- data/lib/generators/minitest/minitest_generator.rb +16 -4
- data/lib/generators/minitest/templates/accessibility_test.tt +26 -0
- data/lib/generators/minitest/templates/performance_test.tt +18 -0
- data/lib/generators/minitest/templates/test.tt +5 -34
- data/lib/generators/minitest/templates/visual_test.tt +23 -0
- data/lib/generators/rspec/rspec_generator.rb +16 -4
- data/lib/generators/rspec/templates/accessibility_spec.tt +25 -0
- data/lib/generators/rspec/templates/performance_spec.tt +18 -0
- data/lib/generators/rspec/templates/spec.tt +5 -35
- data/lib/generators/rspec/templates/visual_spec.tt +20 -0
- data/lib/generators/template_renderer/partial_cache.rb +11 -1
- data/lib/generators/template_renderer/partial_resolver.rb +17 -10
- data/lib/generators/template_renderer.rb +17 -1
- data/lib/generators/templates/common/gemfile.tt +21 -6
- data/lib/generators/templates/common/git_ignore.tt +6 -1
- data/lib/generators/templates/common/partials/mobile_config.tt +5 -1
- data/lib/generators/templates/common/partials/web_config.tt +16 -7
- data/lib/generators/templates/common/rakefile.tt +36 -0
- data/lib/generators/templates/common/read_me.tt +41 -91
- data/lib/generators/templates/common/rspec.tt +3 -0
- data/lib/generators/templates/common/ruby_version.tt +1 -0
- data/lib/generators/templates/helpers/allure_helper.tt +11 -0
- data/lib/generators/templates/helpers/browser_helper.tt +12 -2
- data/lib/generators/templates/helpers/capybara_helper.tt +5 -1
- data/lib/generators/templates/helpers/debug_helper.tt +190 -0
- data/lib/generators/templates/helpers/driver_helper.tt +2 -10
- data/lib/generators/templates/helpers/partials/appium_driver.tt +0 -2
- data/lib/generators/templates/helpers/partials/debug_diagnostics.tt +7 -0
- data/lib/generators/templates/helpers/partials/debug_start.tt +7 -0
- data/lib/generators/templates/helpers/partials/driver_and_options.tt +1 -3
- data/lib/generators/templates/helpers/partials/selenium_driver.tt +8 -7
- data/lib/generators/templates/helpers/partials/video_start.tt +9 -0
- data/lib/generators/templates/helpers/partials/video_stop.tt +4 -0
- data/lib/generators/templates/helpers/performance_helper.tt +57 -0
- data/lib/generators/templates/helpers/spec_helper.tt +57 -8
- data/lib/generators/templates/helpers/test_helper.tt +69 -1
- data/lib/generators/templates/helpers/video_helper.tt +270 -0
- data/lib/generators/templates/helpers/visual_helper.tt +39 -46
- data/lib/llm/client.rb +79 -0
- data/lib/llm/config.rb +57 -0
- data/lib/llm/prompts.rb +84 -0
- data/lib/llm/provider.rb +27 -0
- data/lib/llm/providers/anthropic_provider.rb +43 -0
- data/lib/llm/providers/ollama_provider.rb +56 -0
- data/lib/llm/providers/openai_provider.rb +42 -0
- data/lib/llm/response_parser.rb +67 -0
- data/lib/plugin/plugin.rb +22 -20
- data/lib/plugin/plugin_exposer.rb +16 -38
- data/lib/ruby_raider.rb +47 -12
- data/lib/scaffolding/crud_generator.rb +94 -0
- data/lib/scaffolding/dry_run_presenter.rb +16 -0
- data/lib/scaffolding/name_normalizer.rb +63 -0
- data/lib/scaffolding/page_introspector.rb +45 -0
- data/lib/scaffolding/project_detector.rb +72 -0
- data/lib/scaffolding/scaffold_menu.rb +103 -0
- data/lib/scaffolding/scaffolding.rb +158 -11
- data/lib/scaffolding/templates/component.tt +30 -0
- data/lib/scaffolding/templates/feature.tt +4 -4
- data/lib/scaffolding/templates/helper.tt +15 -1
- data/lib/scaffolding/templates/page_from_url.tt +75 -0
- data/lib/scaffolding/templates/page_object.tt +50 -1
- data/lib/scaffolding/templates/spec.tt +33 -2
- data/lib/scaffolding/templates/spec_from_page.tt +31 -0
- data/lib/scaffolding/templates/spec_from_url.tt +46 -0
- data/lib/scaffolding/templates/steps.tt +17 -5
- data/lib/scaffolding/url_analyzer.rb +179 -0
- data/lib/utilities/desktop_downloader.rb +177 -0
- data/lib/utilities/logo.rb +83 -0
- data/lib/utilities/utilities.rb +53 -20
- data/lib/version +1 -1
- data/ruby_raider.gemspec +1 -0
- data/sig/adopter/adopt_menu.rbs +25 -0
- data/sig/adopter/converters/base_converter.rbs +23 -0
- data/sig/adopter/converters/identity_converter.rbs +16 -0
- data/sig/adopter/migration_plan.rbs +34 -0
- data/sig/adopter/migrator.rbs +21 -0
- data/sig/adopter/plan_builder.rbs +38 -0
- data/sig/adopter/project_analyzer.rbs +39 -0
- data/sig/adopter/project_detector.rbs +26 -0
- data/sig/commands/adopt_commands.rbs +8 -0
- data/sig/commands/loaded_commands.rbs +5 -0
- data/sig/commands/plugin_commands.rbs +9 -0
- data/sig/commands/scaffolding_commands.rbs +28 -0
- data/sig/commands/utility_commands.rbs +21 -0
- data/sig/generators/automation/automation_generator.rbs +20 -0
- data/sig/generators/common_generator.rbs +12 -0
- data/sig/generators/cucumber/cucumber_generator.rbs +16 -0
- data/sig/generators/generator.rbs +40 -0
- data/sig/generators/helper_generator.rbs +18 -0
- data/sig/generators/infrastructure/github_generator.rbs +5 -0
- data/sig/generators/infrastructure/gitlab_generator.rbs +4 -0
- data/sig/generators/invoke_generators.rbs +10 -0
- data/sig/generators/menu_generator.rbs +29 -0
- data/sig/generators/minitest/minitest_generator.rbs +8 -0
- data/sig/generators/rspec/rspec_generator.rbs +8 -0
- data/sig/generators/template_renderer/partial_cache.rbs +20 -0
- data/sig/generators/template_renderer/partial_resolver.rbs +20 -0
- data/sig/generators/template_renderer/template_error.rbs +19 -0
- data/sig/generators/template_renderer.rbs +10 -0
- data/sig/llm/client.rbs +15 -0
- data/sig/llm/config.rbs +20 -0
- data/sig/llm/prompts.rbs +8 -0
- data/sig/llm/provider.rbs +12 -0
- data/sig/llm/providers/anthropic_provider.rbs +16 -0
- data/sig/llm/providers/ollama_provider.rbs +18 -0
- data/sig/llm/providers/openai_provider.rbs +16 -0
- data/sig/llm/response_parser.rbs +13 -0
- data/sig/plugin/plugin.rbs +24 -0
- data/sig/plugin/plugin_exposer.rbs +20 -0
- data/sig/ruby_raider.rbs +15 -0
- data/sig/scaffolding/crud_generator.rbs +16 -0
- data/sig/scaffolding/dry_run_presenter.rbs +4 -0
- data/sig/scaffolding/name_normalizer.rbs +17 -0
- data/sig/scaffolding/page_introspector.rbs +14 -0
- data/sig/scaffolding/project_detector.rbs +14 -0
- data/sig/scaffolding/scaffold_menu.rbs +18 -0
- data/sig/scaffolding/scaffolding.rbs +55 -0
- data/sig/scaffolding/url_analyzer.rbs +28 -0
- data/sig/utilities/desktop_downloader.rbs +23 -0
- data/sig/utilities/logger.rbs +13 -0
- data/sig/utilities/logo.rbs +16 -0
- data/sig/utilities/utilities.rbs +30 -0
- data/sig/vendor/thor.rbs +34 -0
- data/sig/vendor/tty_prompt.rbs +15 -0
- data/spec/adopter/adopt_menu_spec.rb +12 -12
- data/spec/adopter/migration_plan_spec.rb +1 -1
- data/spec/adopter/migrator_spec.rb +2 -2
- data/spec/adopter/project_detector_spec.rb +1 -1
- data/spec/commands/raider_commands_spec.rb +129 -0
- data/spec/generators/generator_spec.rb +23 -0
- data/spec/integration/commands/scaffolding_commands_spec.rb +1 -1
- data/spec/integration/commands/utility_commands_spec.rb +23 -3
- data/spec/integration/content/ci_content_spec.rb +119 -0
- data/spec/integration/content/common_content_spec.rb +288 -0
- data/spec/integration/content/config_content_spec.rb +175 -0
- data/spec/integration/content/content_helper.rb +32 -0
- data/spec/integration/content/gemfile_content_spec.rb +209 -0
- data/spec/integration/content/helper_content_spec.rb +485 -0
- data/spec/integration/content/page_content_spec.rb +259 -0
- data/spec/integration/content/reporter_content_spec.rb +236 -0
- data/spec/integration/content/skip_flags_content_spec.rb +206 -0
- data/spec/integration/content/syntax_validation_spec.rb +30 -0
- data/spec/integration/content/test_content_spec.rb +266 -0
- data/spec/integration/end_to_end_features_spec.rb +690 -0
- data/spec/integration/end_to_end_spec.rb +52 -16
- data/spec/integration/generators/automation_generator_spec.rb +0 -12
- data/spec/integration/generators/axe_addon_spec.rb +150 -0
- data/spec/integration/generators/common_generator_spec.rb +12 -13
- data/spec/integration/generators/config_features_spec.rb +155 -0
- data/spec/integration/generators/debug_helper_spec.rb +68 -0
- data/spec/integration/generators/helpers_generator_spec.rb +0 -12
- data/spec/integration/generators/lighthouse_addon_spec.rb +132 -0
- data/spec/integration/generators/minitest_generator_spec.rb +0 -6
- data/spec/integration/generators/reporter_spec.rb +159 -0
- data/spec/integration/generators/skip_flags_spec.rb +134 -0
- data/spec/integration/generators/visual_addon_spec.rb +148 -0
- data/spec/integration/settings_helper.rb +0 -3
- data/spec/integration/spec_helper.rb +30 -13
- data/spec/llm/client_spec.rb +79 -0
- data/spec/llm/config_spec.rb +92 -0
- data/spec/llm/prompts_spec.rb +49 -0
- data/spec/llm/response_parser_spec.rb +92 -0
- data/spec/menus/adopter_adopt_menu_spec.rb +97 -0
- data/spec/menus/menu_generator_spec.rb +263 -0
- data/spec/scaffolding/name_normalizer_spec.rb +113 -0
- data/spec/scaffolding/page_introspector_spec.rb +82 -0
- data/spec/scaffolding/scaffold_project_detector_spec.rb +104 -0
- data/spec/scaffolding/scaffolding_features_spec.rb +311 -0
- data/spec/scaffolding/url_analyzer_spec.rb +110 -0
- data/spec/system/adopt_matrix_spec.rb +537 -0
- data/spec/system/adopt_spec.rb +225 -0
- data/spec/system/support/system_test_helper.rb +0 -2
- data/spec/utilities/desktop_downloader_spec.rb +92 -0
- metadata +150 -5
- data/lib/generators/automation/templates/visual_options.tt +0 -16
- data/lib/generators/templates/helpers/partials/axe_driver.tt +0 -10
- data/lib/generators/templates/helpers/visual_spec_helper.tt +0 -35
data/lib/adopter/plan_builder.rb
CHANGED
|
@@ -4,7 +4,6 @@ require_relative 'migration_plan'
|
|
|
4
4
|
require_relative 'converters/identity_converter'
|
|
5
5
|
|
|
6
6
|
module Adopter
|
|
7
|
-
# :reek:TooManyMethods { enabled: false }
|
|
8
7
|
class PlanBuilder
|
|
9
8
|
def initialize(analysis, params)
|
|
10
9
|
@analysis = analysis
|
|
@@ -63,7 +62,7 @@ module Adopter
|
|
|
63
62
|
tests = @analysis[:tests] || []
|
|
64
63
|
return [] if target_cucumber?
|
|
65
64
|
|
|
66
|
-
tests.
|
|
65
|
+
tests.reject { |t| t[:type] == :cucumber }.map do |test|
|
|
67
66
|
source_file = File.join(@params[:source_path], test[:path])
|
|
68
67
|
content = File.read(source_file)
|
|
69
68
|
converted = convert_test(content, test)
|
|
@@ -87,7 +86,7 @@ module Adopter
|
|
|
87
86
|
|
|
88
87
|
ConvertedFile.new(
|
|
89
88
|
output_path: raider_feature_path(feature[:path]),
|
|
90
|
-
content
|
|
89
|
+
content:,
|
|
91
90
|
source_file: feature[:path],
|
|
92
91
|
conversion_notes: 'Feature file copied as-is'
|
|
93
92
|
)
|
|
@@ -125,7 +124,6 @@ module Adopter
|
|
|
125
124
|
end
|
|
126
125
|
end
|
|
127
126
|
|
|
128
|
-
# :reek:ControlParameter { enabled: false }
|
|
129
127
|
def convert_page_dsl(content, page, source_dsl, target)
|
|
130
128
|
converter = find_page_converter(source_dsl, target)
|
|
131
129
|
if converter
|
|
@@ -162,7 +160,6 @@ module Adopter
|
|
|
162
160
|
end
|
|
163
161
|
end
|
|
164
162
|
|
|
165
|
-
# :reek:ControlParameter { enabled: false }
|
|
166
163
|
def convert_test_framework(content, test, source_framework, target)
|
|
167
164
|
converter = find_test_converter(source_framework, target)
|
|
168
165
|
if converter
|
|
@@ -5,7 +5,6 @@ require_relative 'project_detector'
|
|
|
5
5
|
module Adopter
|
|
6
6
|
class MobileProjectError < StandardError; end
|
|
7
7
|
|
|
8
|
-
# :reek:TooManyMethods { enabled: false }
|
|
9
8
|
class ProjectAnalyzer
|
|
10
9
|
KNOWN_FRAMEWORK_GEMS = %w[
|
|
11
10
|
activesupport allure-cucumber allure-rspec allure-minitest allure-ruby-commons
|
|
@@ -79,7 +78,6 @@ module Adopter
|
|
|
79
78
|
'Mobile (Appium) projects cannot be adopted. Only web-based projects are supported.'
|
|
80
79
|
end
|
|
81
80
|
|
|
82
|
-
# :reek:FeatureEnvy { enabled: false }
|
|
83
81
|
def discover_pages
|
|
84
82
|
page_path = detect_page_dir
|
|
85
83
|
return [] unless page_path
|
|
@@ -115,7 +113,6 @@ module Adopter
|
|
|
115
113
|
results
|
|
116
114
|
end
|
|
117
115
|
|
|
118
|
-
# :reek:FeatureEnvy { enabled: false }
|
|
119
116
|
def discover_helpers
|
|
120
117
|
helper_files = Dir.glob(File.join(@source_path, '**', '*helper*.rb')) +
|
|
121
118
|
Dir.glob(File.join(@source_path, '**', 'support', '*.rb')) +
|
|
@@ -129,7 +126,7 @@ module Adopter
|
|
|
129
126
|
|
|
130
127
|
{
|
|
131
128
|
path: relative_path(file),
|
|
132
|
-
role
|
|
129
|
+
role:,
|
|
133
130
|
modules_defined: extract_modules(content)
|
|
134
131
|
}
|
|
135
132
|
end
|
|
@@ -153,7 +150,6 @@ module Adopter
|
|
|
153
150
|
end
|
|
154
151
|
end
|
|
155
152
|
|
|
156
|
-
# :reek:NestedIterators { enabled: false }
|
|
157
153
|
def discover_custom_gems
|
|
158
154
|
gemfile = File.join(@source_path, 'Gemfile')
|
|
159
155
|
return [] unless File.exist?(gemfile)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Adopter
|
|
4
|
-
# :reek:TooManyMethods { enabled: false }
|
|
5
4
|
module ProjectDetector
|
|
6
5
|
GEM_AUTOMATION_MAP = {
|
|
7
6
|
'site_prism' => 'capybara',
|
|
@@ -9,9 +8,9 @@ module Adopter
|
|
|
9
8
|
'selenium-webdriver' => 'selenium',
|
|
10
9
|
'watir' => 'watir',
|
|
11
10
|
'appium_lib' => 'appium',
|
|
12
|
-
'eyes_selenium' => '
|
|
13
|
-
'axe-core-selenium' => '
|
|
14
|
-
'axe-core-rspec' => '
|
|
11
|
+
'eyes_selenium' => 'selenium',
|
|
12
|
+
'axe-core-selenium' => 'selenium',
|
|
13
|
+
'axe-core-rspec' => 'selenium'
|
|
15
14
|
}.freeze
|
|
16
15
|
|
|
17
16
|
GEM_FRAMEWORK_MAP = {
|
|
@@ -78,7 +77,6 @@ module Adopter
|
|
|
78
77
|
find_existing_dir(path, candidates)
|
|
79
78
|
end
|
|
80
79
|
|
|
81
|
-
# :reek:NestedIterators { enabled: false }
|
|
82
80
|
def detect_browser(path)
|
|
83
81
|
config_files = helper_and_config_files(path)
|
|
84
82
|
config_files.each do |file|
|
|
@@ -1,86 +1,166 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'thor'
|
|
4
|
+
require 'fileutils'
|
|
4
5
|
require_relative '../generators/menu_generator'
|
|
5
6
|
require_relative '../scaffolding/scaffolding'
|
|
7
|
+
require_relative '../scaffolding/name_normalizer'
|
|
8
|
+
require_relative '../scaffolding/dry_run_presenter'
|
|
9
|
+
require_relative '../scaffolding/project_detector'
|
|
6
10
|
require_relative '../commands/utility_commands'
|
|
7
11
|
|
|
8
|
-
# :reek:FeatureEnvy { enabled: false }
|
|
9
|
-
# :reek:UtilityFunction { enabled: false }
|
|
10
|
-
# :reek:RepeatedConditional { enabled: false }
|
|
11
12
|
class ScaffoldingCommands < Thor
|
|
13
|
+
class_option :dry_run, type: :boolean, default: false,
|
|
14
|
+
desc: 'Preview files without creating them', banner: ''
|
|
15
|
+
|
|
12
16
|
desc 'page [PAGE_NAME]', 'Creates a new page object'
|
|
13
|
-
option :path,
|
|
14
|
-
|
|
15
|
-
option :delete,
|
|
16
|
-
|
|
17
|
+
option :path, type: :string, required: false,
|
|
18
|
+
desc: 'The path where your page will be created', aliases: '-p'
|
|
19
|
+
option :delete, type: :boolean, required: false,
|
|
20
|
+
desc: 'This will delete the selected page', aliases: '-d'
|
|
21
|
+
option :uses, type: :array, required: false,
|
|
22
|
+
desc: 'Dependent pages to require', aliases: '-u'
|
|
17
23
|
|
|
18
24
|
def page(name)
|
|
19
25
|
return delete_scaffolding(name, 'page') if options[:delete]
|
|
26
|
+
return dry_run_preview(name, 'page') if options[:dry_run]
|
|
20
27
|
|
|
21
|
-
generate_scaffolding(name, 'page', options[:path])
|
|
28
|
+
generate_scaffolding(name, 'page', options[:path], uses: options[:uses])
|
|
22
29
|
end
|
|
23
30
|
|
|
24
31
|
desc 'feature [NAME]', 'Creates a new feature'
|
|
25
|
-
option :path,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
type: :boolean,
|
|
30
|
-
required: false, desc: 'This will delete the selected feature', aliases: '-d'
|
|
32
|
+
option :path, type: :string, required: false,
|
|
33
|
+
desc: 'The path where your feature will be created', aliases: '-p'
|
|
34
|
+
option :delete, type: :boolean, required: false,
|
|
35
|
+
desc: 'This will delete the selected feature', aliases: '-d'
|
|
31
36
|
|
|
32
37
|
def feature(name)
|
|
33
38
|
return delete_scaffolding(name, 'feature') if options[:delete]
|
|
39
|
+
return dry_run_preview(name, 'feature') if options[:dry_run]
|
|
34
40
|
|
|
35
41
|
generate_scaffolding(name, 'feature', options[:path])
|
|
36
42
|
end
|
|
37
43
|
|
|
38
44
|
desc 'spec [SPEC_NAME]', 'Creates a new spec'
|
|
39
|
-
option :path,
|
|
40
|
-
|
|
41
|
-
option :delete,
|
|
42
|
-
|
|
45
|
+
option :path, type: :string, required: false,
|
|
46
|
+
desc: 'The path where your spec will be created', aliases: '-p'
|
|
47
|
+
option :delete, type: :boolean, required: false,
|
|
48
|
+
desc: 'This will delete the selected spec', aliases: '-d'
|
|
49
|
+
option :from, type: :string, required: false,
|
|
50
|
+
desc: 'Generate spec stubs from an existing page object file', aliases: '-f'
|
|
51
|
+
option :uses, type: :array, required: false,
|
|
52
|
+
desc: 'Dependent pages to require', aliases: '-u'
|
|
53
|
+
option :ai, type: :boolean, default: false,
|
|
54
|
+
desc: 'Use LLM to generate meaningful test scenarios'
|
|
43
55
|
|
|
44
56
|
def spec(name)
|
|
45
57
|
return delete_scaffolding(name, 'spec') if options[:delete]
|
|
58
|
+
return dry_run_preview(name, 'spec') if options[:dry_run]
|
|
59
|
+
return generate_spec_from_page(name, options[:from], ai: options[:ai]) if options[:from]
|
|
46
60
|
|
|
47
|
-
generate_scaffolding(name, 'spec', options[:path])
|
|
61
|
+
generate_scaffolding(name, 'spec', options[:path], uses: options[:uses])
|
|
48
62
|
end
|
|
49
63
|
|
|
50
64
|
desc 'helper [HELPER_NAME]', 'Creates a new helper'
|
|
51
|
-
option :path,
|
|
52
|
-
|
|
53
|
-
option :delete,
|
|
54
|
-
|
|
65
|
+
option :path, type: :string, required: false,
|
|
66
|
+
desc: 'The path where your helper will be created', aliases: '-p'
|
|
67
|
+
option :delete, type: :boolean, required: false,
|
|
68
|
+
desc: 'This will delete the selected helper', aliases: '-d'
|
|
55
69
|
|
|
56
70
|
def helper(name)
|
|
57
71
|
return delete_scaffolding(name, 'helper') if options[:delete]
|
|
72
|
+
return dry_run_preview(name, 'helper') if options[:dry_run]
|
|
58
73
|
|
|
59
74
|
generate_scaffolding(name, 'helper', options[:path])
|
|
60
75
|
end
|
|
61
76
|
|
|
62
77
|
desc 'steps [STEPS_NAME]', 'Creates a new steps definition'
|
|
63
|
-
option :path,
|
|
64
|
-
|
|
65
|
-
option :delete,
|
|
66
|
-
|
|
78
|
+
option :path, type: :string, required: false,
|
|
79
|
+
desc: 'The path where your steps will be created', aliases: '-p'
|
|
80
|
+
option :delete, type: :boolean, required: false,
|
|
81
|
+
desc: 'This will delete the selected steps', aliases: '-d'
|
|
67
82
|
|
|
68
83
|
def steps(name)
|
|
69
84
|
return delete_scaffolding(name, 'steps') if options[:delete]
|
|
85
|
+
return dry_run_preview(name, 'steps') if options[:dry_run]
|
|
70
86
|
|
|
71
87
|
generate_scaffolding(name, 'steps', options[:path])
|
|
72
88
|
end
|
|
73
89
|
|
|
74
|
-
desc '
|
|
90
|
+
desc 'component [NAME]', 'Creates a component inheriting from Component'
|
|
91
|
+
option :path, type: :string, required: false,
|
|
92
|
+
desc: 'The path where your component will be created', aliases: '-p'
|
|
93
|
+
option :delete, type: :boolean, required: false,
|
|
94
|
+
desc: 'This will delete the selected component', aliases: '-d'
|
|
95
|
+
|
|
96
|
+
def component(name)
|
|
97
|
+
return delete_scaffolding(name, 'component') if options[:delete]
|
|
98
|
+
return dry_run_preview(name, 'component') if options[:dry_run]
|
|
99
|
+
|
|
100
|
+
generate_scaffolding(name, 'component', options[:path])
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
desc 'scaffold [NAMES...]', 'Generates pages, specs/features, and helpers for one or more names'
|
|
104
|
+
option :with, type: :array, required: false,
|
|
105
|
+
desc: 'Components to generate (page,spec,feature,steps,helper,component,model)', aliases: '-w'
|
|
106
|
+
option :crud, type: :boolean, required: false,
|
|
107
|
+
desc: 'Generate CRUD pages (list, create, detail, edit) + tests + model'
|
|
108
|
+
option :uses, type: :array, required: false,
|
|
109
|
+
desc: 'Dependent pages to require', aliases: '-u'
|
|
110
|
+
|
|
111
|
+
def scaffold(*names)
|
|
112
|
+
return interactive_scaffold if names.empty?
|
|
113
|
+
|
|
114
|
+
names.each do |name|
|
|
115
|
+
if options[:crud]
|
|
116
|
+
generate_crud(name)
|
|
117
|
+
elsif options[:with]
|
|
118
|
+
generate_selected_components(name, options[:with])
|
|
119
|
+
else
|
|
120
|
+
generate_default_scaffold(name)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
desc 'destroy [NAMES...]', 'Removes all scaffolded files for the given names (page, spec/feature, steps)'
|
|
126
|
+
option :with, type: :array, required: false,
|
|
127
|
+
desc: 'Components to destroy (page,spec,feature,steps,helper,component)', aliases: '-w'
|
|
128
|
+
|
|
129
|
+
def destroy(*names)
|
|
130
|
+
if names.empty?
|
|
131
|
+
say 'Please provide at least one name to destroy', :red
|
|
132
|
+
return
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
names.each { |name| destroy_scaffold(name, options[:with]) }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
map 'd' => 'destroy'
|
|
139
|
+
|
|
140
|
+
desc 'from_url [URL]', 'Generates page object and spec from a live URL'
|
|
141
|
+
option :name, type: :string, required: false,
|
|
142
|
+
desc: 'Override the page object name', aliases: '-n'
|
|
143
|
+
option :ai, type: :boolean, default: false,
|
|
144
|
+
desc: 'Use LLM for intelligent page analysis'
|
|
145
|
+
|
|
146
|
+
def from_url(url)
|
|
147
|
+
require_relative '../scaffolding/url_analyzer'
|
|
148
|
+
analyzer = UrlAnalyzer.new(url, name_override: options[:name], ai: options[:ai])
|
|
149
|
+
analysis = analyzer.analyze.to_h
|
|
75
150
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
151
|
+
page_name = analysis[:page_name]
|
|
152
|
+
|
|
153
|
+
if options[:dry_run]
|
|
154
|
+
DryRunPresenter.preview([
|
|
155
|
+
"page_objects/pages/#{page_name}.rb",
|
|
156
|
+
"spec/#{page_name}_spec.rb"
|
|
157
|
+
])
|
|
158
|
+
return
|
|
82
159
|
end
|
|
83
|
-
|
|
160
|
+
|
|
161
|
+
Scaffolding.new([page_name]).generate_page_from_url(analysis)
|
|
162
|
+
Scaffolding.new([page_name]).generate_spec_from_url(analysis)
|
|
163
|
+
say "Generated page object and spec for #{url}"
|
|
84
164
|
end
|
|
85
165
|
|
|
86
166
|
no_commands do
|
|
@@ -92,9 +172,112 @@ class ScaffoldingCommands < Thor
|
|
|
92
172
|
Scaffolding.new([name]).send("delete_#{type}")
|
|
93
173
|
end
|
|
94
174
|
|
|
95
|
-
def generate_scaffolding(name, type, path)
|
|
175
|
+
def generate_scaffolding(name, type, path, uses: nil)
|
|
96
176
|
path ||= load_config_path(type)
|
|
97
|
-
Scaffolding.new([name, path])
|
|
177
|
+
scaffolding = Scaffolding.new([name, path])
|
|
178
|
+
scaffolding.uses = Array(uses) if uses
|
|
179
|
+
scaffolding.send("generate_#{type}")
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def dry_run_preview(name, type)
|
|
183
|
+
path = options[:path] || load_config_path(type)
|
|
184
|
+
file = Scaffolding.planned_path(name, type, path)
|
|
185
|
+
DryRunPresenter.preview([file])
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def generate_default_scaffold(name)
|
|
189
|
+
uses = options[:uses]
|
|
190
|
+
if Pathname.new('spec').exist? && !Pathname.new('features').exist?
|
|
191
|
+
generate_scaffolding(name, 'spec', load_config_path('spec'), uses:)
|
|
192
|
+
else
|
|
193
|
+
generate_scaffolding(name, 'feature', load_config_path('feature'))
|
|
194
|
+
generate_scaffolding(name, 'steps', load_config_path('steps'), uses:)
|
|
195
|
+
end
|
|
196
|
+
generate_scaffolding(name, 'page', load_config_path('page'), uses:)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def generate_selected_components(name, components)
|
|
200
|
+
uses = options[:uses]
|
|
201
|
+
components.each do |comp|
|
|
202
|
+
comp = comp.downcase.strip
|
|
203
|
+
case comp
|
|
204
|
+
when 'model'
|
|
205
|
+
generate_model_data(name)
|
|
206
|
+
else
|
|
207
|
+
generate_scaffolding(name, comp, load_config_path(comp), uses:)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def generate_crud(name)
|
|
213
|
+
require_relative '../scaffolding/crud_generator'
|
|
214
|
+
if options[:dry_run]
|
|
215
|
+
crud = CrudGenerator.new(name, Scaffolding, method(:load_config_path))
|
|
216
|
+
DryRunPresenter.preview(crud.planned_files)
|
|
217
|
+
return
|
|
218
|
+
end
|
|
219
|
+
crud = CrudGenerator.new(name, Scaffolding, method(:load_config_path))
|
|
220
|
+
generated = crud.generate
|
|
221
|
+
say "Generated CRUD scaffold for: #{generated.join(', ')}"
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def generate_spec_from_page(name, source_file, ai: false) # rubocop:disable Naming/MethodParameterName
|
|
225
|
+
Scaffolding.new([name]).generate_spec_from_page(source_file, ai:)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def generate_model_data(name)
|
|
229
|
+
normalized = NameNormalizer.normalize(name)
|
|
230
|
+
path = "models/data/#{normalized}.yml"
|
|
231
|
+
return if File.exist?(path)
|
|
232
|
+
|
|
233
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
234
|
+
File.write(path, model_template(normalized))
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def model_template(name)
|
|
238
|
+
<<~YAML
|
|
239
|
+
# Data model for #{name}
|
|
240
|
+
default:
|
|
241
|
+
name: 'Test #{name.capitalize}'
|
|
242
|
+
email: 'test@example.com'
|
|
243
|
+
|
|
244
|
+
valid:
|
|
245
|
+
name: 'Valid #{name.capitalize}'
|
|
246
|
+
email: 'valid@example.com'
|
|
247
|
+
|
|
248
|
+
invalid:
|
|
249
|
+
name: ''
|
|
250
|
+
email: 'invalid'
|
|
251
|
+
YAML
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def destroy_scaffold(name, components = nil)
|
|
255
|
+
types = components ? components.map { |c| c.downcase.strip } : detect_scaffold_types
|
|
256
|
+
types.each { |type| delete_scaffolding(name, type) }
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def detect_scaffold_types
|
|
260
|
+
if Pathname.new('spec').exist? && !Pathname.new('features').exist?
|
|
261
|
+
%w[page spec]
|
|
262
|
+
else
|
|
263
|
+
%w[page feature steps]
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def interactive_scaffold
|
|
268
|
+
require_relative '../scaffolding/scaffold_menu'
|
|
269
|
+
result = ScaffoldMenu.new.run
|
|
270
|
+
return unless result
|
|
271
|
+
|
|
272
|
+
result[:names].each do |name|
|
|
273
|
+
result[:components].each do |comp|
|
|
274
|
+
if comp == :model
|
|
275
|
+
generate_model_data(name)
|
|
276
|
+
else
|
|
277
|
+
generate_scaffolding(name, comp.to_s, load_config_path(comp.to_s), uses: result[:uses])
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
98
281
|
end
|
|
99
282
|
end
|
|
100
283
|
end
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
require 'thor'
|
|
4
4
|
require_relative '../utilities/utilities'
|
|
5
5
|
|
|
6
|
-
# :reek:FeatureEnvy { enabled: false }
|
|
7
|
-
# :reek:UtilityFunction { enabled: false }
|
|
8
6
|
class UtilityCommands < Thor
|
|
9
7
|
desc 'path [PATH]', 'Sets the default path for scaffolding'
|
|
10
8
|
option :feature,
|
|
@@ -67,14 +65,96 @@ class UtilityCommands < Thor
|
|
|
67
65
|
option :delete,
|
|
68
66
|
type: :boolean, required: false, desc: 'This will delete the selected config file', aliases: '-d'
|
|
69
67
|
|
|
68
|
+
desc 'timeout [SECONDS]', 'Sets the default test timeout in seconds'
|
|
69
|
+
|
|
70
|
+
def timeout(seconds)
|
|
71
|
+
Utilities.timeout = seconds
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
desc 'viewport [DIMENSIONS]', 'Sets the default viewport size (e.g. 1920x1080, 375x812)'
|
|
75
|
+
|
|
76
|
+
def viewport(dimensions)
|
|
77
|
+
Utilities.viewport = dimensions
|
|
78
|
+
end
|
|
79
|
+
|
|
70
80
|
desc 'platform [PLATFORM]', 'Sets the default platform for a cross-platform project'
|
|
71
81
|
|
|
72
82
|
def platform(platform)
|
|
73
83
|
Utilities.platform = platform
|
|
74
84
|
end
|
|
75
85
|
|
|
86
|
+
desc 'debug [on/off]', 'Toggles debug mode for failure diagnostics and logging'
|
|
87
|
+
|
|
88
|
+
def debug(toggle)
|
|
89
|
+
enabled = %w[on true 1 yes].include?(toggle.downcase)
|
|
90
|
+
Utilities.debug = enabled
|
|
91
|
+
state = enabled ? 'enabled' : 'disabled'
|
|
92
|
+
say "Debug mode #{state}", :green
|
|
93
|
+
end
|
|
94
|
+
|
|
76
95
|
desc 'start_appium', 'It starts the appium server'
|
|
77
96
|
def start_appium
|
|
78
97
|
system 'appium --base-path /wd/hub'
|
|
79
98
|
end
|
|
99
|
+
|
|
100
|
+
desc 'desktop', 'Downloads the Raider Desktop GUI application'
|
|
101
|
+
option :path, type: :string, required: false,
|
|
102
|
+
desc: 'Directory to save the download', aliases: '-p'
|
|
103
|
+
|
|
104
|
+
def desktop
|
|
105
|
+
require_relative '../utilities/desktop_downloader'
|
|
106
|
+
version = DesktopDownloader.latest_version
|
|
107
|
+
unless version
|
|
108
|
+
say 'Could not reach GitHub releases. Check your internet connection.', :red
|
|
109
|
+
return
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
say "Raider Desktop v#{version} available for #{DesktopDownloader.platform_display_name}"
|
|
113
|
+
DesktopDownloader.download(options[:path])
|
|
114
|
+
say 'Raider Desktop downloaded successfully!', :green
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
desc 'llm [PROVIDER]', 'Configures the LLM provider (openai, anthropic, ollama)'
|
|
118
|
+
option :key, type: :string, required: false, desc: 'API key for the provider', aliases: '-k'
|
|
119
|
+
option :model, type: :string, required: false, desc: 'Model name to use', aliases: '-m'
|
|
120
|
+
option :url, type: :string, required: false, desc: 'API URL (for ollama)', aliases: '-u'
|
|
121
|
+
option :status, type: :boolean, required: false, desc: 'Show current LLM configuration', aliases: '-s'
|
|
122
|
+
|
|
123
|
+
def llm(provider = nil)
|
|
124
|
+
if options[:status] || provider.nil?
|
|
125
|
+
show_llm_status
|
|
126
|
+
return
|
|
127
|
+
end
|
|
128
|
+
configure_llm(provider)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
no_commands do
|
|
132
|
+
def configure_llm(provider)
|
|
133
|
+
unless %w[openai anthropic ollama].include?(provider)
|
|
134
|
+
say "Unknown provider '#{provider}'. Choose: openai, anthropic, ollama", :red
|
|
135
|
+
return
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
Utilities.llm_provider = provider
|
|
139
|
+
Utilities.llm_api_key = options[:key] if options[:key]
|
|
140
|
+
Utilities.llm_model = options[:model] if options[:model]
|
|
141
|
+
Utilities.llm_url = options[:url] if options[:url]
|
|
142
|
+
say "LLM configured: #{provider}", :green
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def show_llm_status
|
|
146
|
+
require_relative '../llm/client'
|
|
147
|
+
status = Llm::Client.status
|
|
148
|
+
if status[:configured]
|
|
149
|
+
say "Provider: #{status[:provider]}"
|
|
150
|
+
say "Model: #{status[:model] || 'default'}"
|
|
151
|
+
say "Available: #{status[:available] ? 'yes' : 'no'}"
|
|
152
|
+
else
|
|
153
|
+
say 'No LLM configured. Use: raider u llm <provider>', :yellow
|
|
154
|
+
say ' Providers: openai, anthropic, ollama'
|
|
155
|
+
say ' Example: raider u llm ollama'
|
|
156
|
+
say ' Example: raider u llm openai -k sk-...'
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
80
160
|
end
|
|
@@ -9,7 +9,6 @@ class AutomationGenerator < Generator
|
|
|
9
9
|
generate_home_page
|
|
10
10
|
generate_pdp_page
|
|
11
11
|
else
|
|
12
|
-
generate_visual_options
|
|
13
12
|
generate_components
|
|
14
13
|
generate_model_files
|
|
15
14
|
generate_pages
|
|
@@ -47,12 +46,6 @@ class AutomationGenerator < Generator
|
|
|
47
46
|
template('appium_caps.tt', "#{name}/config/capabilities.yml")
|
|
48
47
|
end
|
|
49
48
|
|
|
50
|
-
def generate_visual_options
|
|
51
|
-
return unless visual?
|
|
52
|
-
|
|
53
|
-
template('visual_options.tt', "#{name}/config/options.yml")
|
|
54
|
-
end
|
|
55
|
-
|
|
56
49
|
def generate_login_page
|
|
57
50
|
template('login.tt', "#{name}/page_objects/pages/login.rb")
|
|
58
51
|
end
|
|
@@ -17,6 +17,10 @@ class CommonGenerator < Generator
|
|
|
17
17
|
template('common/rakefile.tt', "#{name}/Rakefile")
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
def generate_ruby_version_file
|
|
21
|
+
template('common/ruby_version.tt', "#{name}/.ruby-version")
|
|
22
|
+
end
|
|
23
|
+
|
|
20
24
|
def generate_gemfile
|
|
21
25
|
template('common/gemfile.tt', "#{name}/Gemfile")
|
|
22
26
|
end
|
|
@@ -33,7 +37,15 @@ class CommonGenerator < Generator
|
|
|
33
37
|
template('common/git_ignore.tt', "#{name}/.gitignore")
|
|
34
38
|
end
|
|
35
39
|
|
|
40
|
+
def generate_rspec_file
|
|
41
|
+
return unless rspec?
|
|
42
|
+
|
|
43
|
+
template('common/rspec.tt', "#{name}/.rspec")
|
|
44
|
+
end
|
|
45
|
+
|
|
36
46
|
def create_allure_folder
|
|
47
|
+
return unless allure_reporter?
|
|
48
|
+
|
|
37
49
|
empty_directory "#{name}/allure-results"
|
|
38
50
|
end
|
|
39
51
|
end
|
|
@@ -27,6 +27,42 @@ class CucumberGenerator < Generator
|
|
|
27
27
|
template('cucumber.tt', "#{name}/cucumber.yml")
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
def generate_visual_feature
|
|
31
|
+
return unless visual_addon? && web?
|
|
32
|
+
|
|
33
|
+
template('visual_feature.tt', "#{name}/features/visual.feature")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def generate_visual_steps
|
|
37
|
+
return unless visual_addon? && web?
|
|
38
|
+
|
|
39
|
+
template('visual_steps.tt', "#{name}/features/step_definitions/visual_steps.rb")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def generate_accessibility_feature
|
|
43
|
+
return unless axe_addon? && web?
|
|
44
|
+
|
|
45
|
+
template('accessibility_feature.tt', "#{name}/features/accessibility.feature")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def generate_accessibility_steps
|
|
49
|
+
return unless axe_addon? && web?
|
|
50
|
+
|
|
51
|
+
template('accessibility_steps.tt', "#{name}/features/step_definitions/accessibility_steps.rb")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def generate_performance_feature
|
|
55
|
+
return unless lighthouse_addon? && web?
|
|
56
|
+
|
|
57
|
+
template('performance_feature.tt', "#{name}/features/performance.feature")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def generate_performance_steps
|
|
61
|
+
return unless lighthouse_addon? && web?
|
|
62
|
+
|
|
63
|
+
template('performance_steps.tt', "#{name}/features/step_definitions/performance_steps.rb")
|
|
64
|
+
end
|
|
65
|
+
|
|
30
66
|
def template_name
|
|
31
67
|
@template_name ||= (@_initializer.first & %w[android ios cross_platform]).empty? ? 'login' : 'home'
|
|
32
68
|
end
|