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
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
require 'tty-prompt'
|
|
4
4
|
require_relative '../generators/invoke_generators'
|
|
5
5
|
|
|
6
|
-
# :reek:FeatureEnvy { enabled: false }
|
|
7
|
-
# :reek:UtilityFunction { enabled: false }
|
|
8
6
|
class MenuGenerator
|
|
9
7
|
attr_reader :prompt, :name
|
|
10
8
|
|
|
@@ -32,10 +30,16 @@ class MenuGenerator
|
|
|
32
30
|
automation: options[:automation],
|
|
33
31
|
framework: options[:framework],
|
|
34
32
|
ci_platform: options[:ci_platform],
|
|
33
|
+
reporter: options[:reporter],
|
|
34
|
+
accessibility: options[:accessibility],
|
|
35
|
+
visual: options[:visual],
|
|
36
|
+
performance: options[:performance],
|
|
37
|
+
ruby_version: options[:ruby_version],
|
|
35
38
|
name: @name
|
|
36
39
|
}
|
|
37
40
|
generate_framework(structure)
|
|
38
41
|
system "cd #{name} && gem install bundler && bundle install"
|
|
42
|
+
offer_desktop_download
|
|
39
43
|
end
|
|
40
44
|
|
|
41
45
|
def choose_mobile_platform
|
|
@@ -49,6 +53,25 @@ class MenuGenerator
|
|
|
49
53
|
|
|
50
54
|
private
|
|
51
55
|
|
|
56
|
+
def offer_desktop_download
|
|
57
|
+
return unless prompt.yes?('Would you like to download Raider Desktop (GUI companion app)?')
|
|
58
|
+
|
|
59
|
+
require_relative '../utilities/desktop_downloader'
|
|
60
|
+
version = DesktopDownloader.latest_version
|
|
61
|
+
unless version
|
|
62
|
+
prompt.warn('Could not reach GitHub releases. You can download it later with: raider u desktop')
|
|
63
|
+
return
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
prompt.say("Downloading Raider Desktop v#{version} for #{DesktopDownloader.platform_display_name}...")
|
|
67
|
+
result = DesktopDownloader.download
|
|
68
|
+
if result
|
|
69
|
+
prompt.ok("Downloaded to: #{result}")
|
|
70
|
+
else
|
|
71
|
+
prompt.warn('No release found for your platform. Visit: https://github.com/RaiderHQ/raider_desktop/releases')
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
52
75
|
def select_test_framework(automation)
|
|
53
76
|
prompt.select('Please select your test framework') do |menu|
|
|
54
77
|
menu.choice :Cucumber, -> { select_ci_platform('Cucumber', automation) }
|
|
@@ -58,21 +81,109 @@ class MenuGenerator
|
|
|
58
81
|
end
|
|
59
82
|
end
|
|
60
83
|
|
|
61
|
-
FrameworkOptions = Struct.new(:automation, :framework, :ci_platform
|
|
84
|
+
FrameworkOptions = Struct.new(:automation, :framework, :ci_platform, :reporter, :accessibility, :visual, :performance,
|
|
85
|
+
:ruby_version)
|
|
62
86
|
|
|
63
87
|
def create_framework_options(params)
|
|
64
|
-
FrameworkOptions.new(params[:automation], params[:framework], params[:ci_platform]
|
|
88
|
+
FrameworkOptions.new(params[:automation], params[:framework], params[:ci_platform], params[:reporter],
|
|
89
|
+
params[:accessibility], params[:visual], params[:performance], params[:ruby_version])
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def select_reporter(framework, automation_type, ci_platform = nil)
|
|
93
|
+
prompt.select('Select your test reporter') do |menu|
|
|
94
|
+
menu.choice :Allure, -> { select_accessibility(framework, automation_type, ci_platform, 'allure') }
|
|
95
|
+
menu.choice :JUnit, -> { select_accessibility(framework, automation_type, ci_platform, 'junit') }
|
|
96
|
+
menu.choice :JSON, -> { select_accessibility(framework, automation_type, ci_platform, 'json') }
|
|
97
|
+
menu.choice :Both, -> { select_accessibility(framework, automation_type, ci_platform, 'both') }
|
|
98
|
+
menu.choice :All, -> { select_accessibility(framework, automation_type, ci_platform, 'all') }
|
|
99
|
+
menu.choice :None, -> { select_accessibility(framework, automation_type, ci_platform, 'none') }
|
|
100
|
+
menu.choice :Quit, -> { exit }
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def select_accessibility(framework, automation_type, ci_platform = nil, reporter = nil)
|
|
105
|
+
return create_framework(framework, automation_type, ci_platform:, reporter:) if mobile_automation?(automation_type)
|
|
106
|
+
|
|
107
|
+
prompt.select('Add accessibility testing (axe)?') do |menu|
|
|
108
|
+
menu.choice :Yes, -> { select_visual(framework, automation_type, ci_platform, reporter, accessibility: true) }
|
|
109
|
+
menu.choice :No, -> { select_visual(framework, automation_type, ci_platform, reporter) }
|
|
110
|
+
menu.choice :Quit, -> { exit }
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def select_visual(framework, automation_type, ci_platform = nil, reporter = nil, accessibility: false)
|
|
115
|
+
prompt.select('Add visual regression testing?') do |menu|
|
|
116
|
+
menu.choice :Yes, lambda {
|
|
117
|
+
select_performance(framework, automation_type, ci_platform, reporter, accessibility:, visual: true)
|
|
118
|
+
}
|
|
119
|
+
menu.choice :No, lambda {
|
|
120
|
+
select_performance(framework, automation_type, ci_platform, reporter, accessibility:)
|
|
121
|
+
}
|
|
122
|
+
menu.choice :Quit, -> { exit }
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def select_performance(framework, automation_type, ci_platform = nil, reporter = nil, accessibility: false,
|
|
127
|
+
visual: false)
|
|
128
|
+
prompt.select('Add Lighthouse performance auditing?') do |menu|
|
|
129
|
+
menu.choice :Yes, lambda {
|
|
130
|
+
select_ruby_version(framework, automation_type, ci_platform:, reporter:, accessibility:, visual:,
|
|
131
|
+
performance: true)
|
|
132
|
+
}
|
|
133
|
+
menu.choice :No, lambda {
|
|
134
|
+
select_ruby_version(framework, automation_type, ci_platform:, reporter:, accessibility:, visual:)
|
|
135
|
+
}
|
|
136
|
+
menu.choice :Quit, -> { exit }
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def select_ruby_version(framework, automation_type, ci_platform: nil, reporter: nil, accessibility: false,
|
|
141
|
+
visual: false, performance: false)
|
|
142
|
+
prompt.select('Select Ruby version for your project') do |menu|
|
|
143
|
+
menu.choice :'3.4 (latest)', lambda {
|
|
144
|
+
create_framework(framework, automation_type, ci_platform:, reporter:, accessibility:, visual:, performance:,
|
|
145
|
+
ruby_version: '3.4')
|
|
146
|
+
}
|
|
147
|
+
menu.choice :'3.3', lambda {
|
|
148
|
+
create_framework(framework, automation_type, ci_platform:, reporter:, accessibility:, visual:, performance:,
|
|
149
|
+
ruby_version: '3.3')
|
|
150
|
+
}
|
|
151
|
+
menu.choice :'3.2', lambda {
|
|
152
|
+
create_framework(framework, automation_type, ci_platform:, reporter:, accessibility:, visual:, performance:,
|
|
153
|
+
ruby_version: '3.2')
|
|
154
|
+
}
|
|
155
|
+
menu.choice :'3.1', lambda {
|
|
156
|
+
create_framework(framework, automation_type, ci_platform:, reporter:, accessibility:, visual:, performance:,
|
|
157
|
+
ruby_version: '3.1')
|
|
158
|
+
}
|
|
159
|
+
menu.choice :Quit, -> { exit }
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def mobile_automation?(automation_type)
|
|
164
|
+
%w[ios android cross_platform].include?(automation_type)
|
|
65
165
|
end
|
|
66
166
|
|
|
67
|
-
def create_framework(framework, automation_type, ci_platform
|
|
167
|
+
def create_framework(framework, automation_type, ci_platform: nil, reporter: nil, accessibility: false,
|
|
168
|
+
visual: false, performance: false, ruby_version: nil)
|
|
68
169
|
options = create_framework_options(automation: automation_type,
|
|
69
170
|
framework: framework.downcase,
|
|
70
|
-
ci_platform
|
|
171
|
+
ci_platform:,
|
|
172
|
+
reporter:,
|
|
173
|
+
accessibility:,
|
|
174
|
+
visual:,
|
|
175
|
+
performance:,
|
|
176
|
+
ruby_version:)
|
|
71
177
|
|
|
72
178
|
puts 'Chosen Options:'
|
|
73
179
|
puts " Automation Type: #{options[:automation]}"
|
|
74
180
|
puts " Framework: #{options[:framework]}"
|
|
75
181
|
puts " CI Platform: #{options[:ci_platform]}" if options[:ci_platform]
|
|
182
|
+
puts " Reporter: #{options[:reporter]}" if options[:reporter]
|
|
183
|
+
puts ' Accessibility: enabled' if options[:accessibility]
|
|
184
|
+
puts ' Visual Testing: enabled' if options[:visual]
|
|
185
|
+
puts ' Performance Auditing: enabled' if options[:performance]
|
|
186
|
+
puts " Ruby Version: #{options[:ruby_version]}" if options[:ruby_version]
|
|
76
187
|
|
|
77
188
|
set_up_framework(options)
|
|
78
189
|
prompt.say("You have chosen to use #{framework} with #{automation_type}")
|
|
@@ -96,15 +207,13 @@ class MenuGenerator
|
|
|
96
207
|
menu.choice :Capybara, -> { choose_test_framework('capybara') }
|
|
97
208
|
menu.choice :Appium, -> { choose_test_framework('appium') }
|
|
98
209
|
menu.choice :Watir, -> { choose_test_framework('watir') }
|
|
99
|
-
menu.choice :Applitools, -> { choose_test_framework('applitools') }
|
|
100
|
-
menu.choice :Axe, -> { choose_test_framework('axe') }
|
|
101
210
|
end
|
|
102
211
|
|
|
103
212
|
def select_ci_platform(framework, automation)
|
|
104
213
|
prompt.select('Would you like to configure CI?') do |menu|
|
|
105
|
-
menu.choice :'Github Actions', -> {
|
|
106
|
-
menu.choice :'Gitlab CI/CD', -> {
|
|
107
|
-
menu.choice :No, -> {
|
|
214
|
+
menu.choice :'Github Actions', -> { select_reporter(framework, automation, 'github') }
|
|
215
|
+
menu.choice :'Gitlab CI/CD', -> { select_reporter(framework, automation, 'gitlab') }
|
|
216
|
+
menu.choice :No, -> { select_reporter(framework, automation) }
|
|
108
217
|
menu.choice :Quit, -> { exit }
|
|
109
218
|
end
|
|
110
219
|
end
|
|
@@ -4,7 +4,7 @@ require_relative '../generator'
|
|
|
4
4
|
|
|
5
5
|
class MinitestGenerator < Generator
|
|
6
6
|
def generate_login_test
|
|
7
|
-
return unless web?
|
|
7
|
+
return unless web?
|
|
8
8
|
|
|
9
9
|
template('test.tt', "#{name}/test/test_login_page.rb")
|
|
10
10
|
end
|
|
@@ -15,9 +15,21 @@ class MinitestGenerator < Generator
|
|
|
15
15
|
template('test.tt', "#{name}/test/test_pdp_page.rb")
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def
|
|
19
|
-
return unless
|
|
18
|
+
def generate_visual_test
|
|
19
|
+
return unless visual_addon? && web?
|
|
20
20
|
|
|
21
|
-
template('
|
|
21
|
+
template('visual_test.tt', "#{name}/test/test_visual.rb")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def generate_accessibility_test
|
|
25
|
+
return unless axe_addon? && web?
|
|
26
|
+
|
|
27
|
+
template('accessibility_test.tt', "#{name}/test/test_accessibility.rb")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def generate_performance_test
|
|
31
|
+
return unless lighthouse_addon? && web?
|
|
32
|
+
|
|
33
|
+
template('performance_test.tt', "#{name}/test/test_performance.rb")
|
|
22
34
|
end
|
|
23
35
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../helpers/test_helper'
|
|
4
|
+
|
|
5
|
+
class TestAccessibility < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
super
|
|
8
|
+
<% if capybara? -%>
|
|
9
|
+
visit '/'
|
|
10
|
+
<% elsif watir? -%>
|
|
11
|
+
browser.goto 'https://raider-test-site.onrender.com/'
|
|
12
|
+
<% else -%>
|
|
13
|
+
driver.get 'https://raider-test-site.onrender.com/'
|
|
14
|
+
<% end -%>
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_no_accessibility_violations_on_page
|
|
18
|
+
<% if capybara? -%>
|
|
19
|
+
assert_axe_clean page
|
|
20
|
+
<% elsif watir? -%>
|
|
21
|
+
assert_axe_clean browser.driver
|
|
22
|
+
<% else -%>
|
|
23
|
+
assert_axe_clean driver
|
|
24
|
+
<% end -%>
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../helpers/test_helper'
|
|
4
|
+
require_relative '../helpers/performance_helper'
|
|
5
|
+
|
|
6
|
+
class TestPerformance < Minitest::Test
|
|
7
|
+
include PerformanceHelper
|
|
8
|
+
|
|
9
|
+
def test_meets_performance_threshold
|
|
10
|
+
result = assert_performance_above('https://raider-test-site.onrender.com/', threshold: 0.7)
|
|
11
|
+
|
|
12
|
+
refute_equal :error, result[:status],
|
|
13
|
+
"Lighthouse error: #{result[:message]}"
|
|
14
|
+
|
|
15
|
+
assert result[:passed],
|
|
16
|
+
"Performance below threshold (#{result[:threshold]}): #{result[:scores]}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -6,33 +6,11 @@ require_relative '../models/model_factory'
|
|
|
6
6
|
require_relative '../page_objects/pages/account'
|
|
7
7
|
require_relative '../page_objects/pages/login'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@login = Login.new(driver)
|
|
13
|
-
@account = Account.new(driver)
|
|
14
|
-
@user = ModelFactory.for('users')['registered user']
|
|
15
|
-
@login.visit
|
|
16
|
-
@login.log_as(@user['username'], @user['password'])
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def teardown
|
|
20
|
-
driver.quit
|
|
21
|
-
end
|
|
9
|
+
# Uncomment for interactive debugging:
|
|
10
|
+
# require 'debug' # Ruby's built-in debugger (binding.break)
|
|
11
|
+
# require 'pry' # Pry debugger (binding.pry)
|
|
22
12
|
|
|
23
|
-
|
|
24
|
-
assert_axe_clean @account.page
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def test_no_accessibility_errors_on_transaction_history
|
|
28
|
-
assert_axe_clean @account.page, within: '.account-card'
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def test_no_accessibility_errors_on_heading
|
|
32
|
-
assert_axe_clean @account.page, within: '.page-title'
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
<%- elsif selenium_based? || watir? || capybara? -%>
|
|
13
|
+
<%- if selenium_based? || watir? || capybara? -%>
|
|
36
14
|
class TestLogin < Minitest::Test
|
|
37
15
|
def setup
|
|
38
16
|
@user = ModelFactory.for('users')['registered user']
|
|
@@ -45,25 +23,18 @@ class TestLogin < Minitest::Test
|
|
|
45
23
|
end
|
|
46
24
|
|
|
47
25
|
def test_login_with_right_credentials
|
|
26
|
+
# binding.break # Uncomment to pause here and inspect state
|
|
48
27
|
@login_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
|
|
49
28
|
@login_page.login(@user['username'], @user['password'])
|
|
50
|
-
<%- if visual? -%>
|
|
51
|
-
check_page @account_page
|
|
52
|
-
<%- else -%>
|
|
53
29
|
@account_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
|
|
54
30
|
assert_equal "Welcome back #{@user['name']}", @account_page.header.customer_name
|
|
55
|
-
<%- end -%>
|
|
56
31
|
end
|
|
57
32
|
|
|
58
33
|
def test_login_with_wrong_credentials
|
|
59
34
|
@login_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
|
|
60
35
|
@login_page.login(@user['username'], 'wrongPassword')
|
|
61
|
-
<%- if visual? -%>
|
|
62
|
-
check_page @login_page
|
|
63
|
-
<%- else -%>
|
|
64
36
|
@login_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
|
|
65
37
|
assert_equal 'Login or register', @account_page.header.customer_name
|
|
66
|
-
<%- end -%>
|
|
67
38
|
end
|
|
68
39
|
end
|
|
69
40
|
<%- end -%>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../helpers/test_helper'
|
|
4
|
+
require_relative '../helpers/visual_helper'
|
|
5
|
+
require_relative '../page_objects/pages/login'
|
|
6
|
+
|
|
7
|
+
class TestVisualRegression < Minitest::Test
|
|
8
|
+
include VisualHelper
|
|
9
|
+
|
|
10
|
+
def setup
|
|
11
|
+
super
|
|
12
|
+
@login_page = <% if capybara? -%>Login.new<% elsif watir? -%>Login.new(browser)<% else -%>Login.new(driver)<% end %>
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def test_login_page_matches_baseline
|
|
16
|
+
@login_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
|
|
17
|
+
screenshot_path = <% if capybara? -%>page.save_screenshot('tmp/visual_login.png')<% elsif watir? -%>browser.screenshot.save('tmp/visual_login.png')<% else -%>driver.save_screenshot('tmp/visual_login.png')<% end %>
|
|
18
|
+
result = compare_screenshot('login_page', screenshot_path)
|
|
19
|
+
|
|
20
|
+
refute_equal :mismatch, result[:status],
|
|
21
|
+
"Visual diff: #{(result[:diff].to_f * 100).round(2)}% difference. See: #{result[:diff_path]}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -4,7 +4,7 @@ require_relative '../generator'
|
|
|
4
4
|
|
|
5
5
|
class RspecGenerator < Generator
|
|
6
6
|
def generate_login_spec
|
|
7
|
-
return unless web?
|
|
7
|
+
return unless web?
|
|
8
8
|
|
|
9
9
|
template('spec.tt', "#{name}/spec/login_page_spec.rb")
|
|
10
10
|
end
|
|
@@ -15,9 +15,21 @@ class RspecGenerator < Generator
|
|
|
15
15
|
template('spec.tt', "#{name}/spec/pdp_page_spec.rb")
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def
|
|
19
|
-
return unless
|
|
18
|
+
def generate_visual_spec
|
|
19
|
+
return unless visual_addon? && web?
|
|
20
20
|
|
|
21
|
-
template('
|
|
21
|
+
template('visual_spec.tt', "#{name}/spec/visual_spec.rb")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def generate_accessibility_spec
|
|
25
|
+
return unless axe_addon? && web?
|
|
26
|
+
|
|
27
|
+
template('accessibility_spec.tt', "#{name}/spec/accessibility_spec.rb")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def generate_performance_spec
|
|
31
|
+
return unless lighthouse_addon? && web?
|
|
32
|
+
|
|
33
|
+
template('performance_spec.tt', "#{name}/spec/performance_spec.rb")
|
|
22
34
|
end
|
|
23
35
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../helpers/spec_helper'
|
|
4
|
+
|
|
5
|
+
describe 'Accessibility' do
|
|
6
|
+
before do
|
|
7
|
+
<% if capybara? -%>
|
|
8
|
+
visit '/'
|
|
9
|
+
<% elsif watir? -%>
|
|
10
|
+
browser.goto 'https://raider-test-site.onrender.com/'
|
|
11
|
+
<% else -%>
|
|
12
|
+
driver.get 'https://raider-test-site.onrender.com/'
|
|
13
|
+
<% end -%>
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'has no accessibility violations on the page' do
|
|
17
|
+
<% if capybara? -%>
|
|
18
|
+
expect(page).to be_axe_clean
|
|
19
|
+
<% elsif watir? -%>
|
|
20
|
+
expect(browser.driver).to be_axe_clean
|
|
21
|
+
<% else -%>
|
|
22
|
+
expect(driver).to be_axe_clean
|
|
23
|
+
<% end -%>
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../helpers/spec_helper'
|
|
4
|
+
require_relative '../helpers/performance_helper'
|
|
5
|
+
|
|
6
|
+
describe 'Performance Audit' do
|
|
7
|
+
include PerformanceHelper
|
|
8
|
+
|
|
9
|
+
it 'meets performance threshold' do
|
|
10
|
+
result = assert_performance_above('https://raider-test-site.onrender.com/', threshold: 0.7)
|
|
11
|
+
|
|
12
|
+
expect(result[:status]).not_to eq(:error),
|
|
13
|
+
"Lighthouse error: #{result[:message]}"
|
|
14
|
+
|
|
15
|
+
expect(result[:passed]).to be(true),
|
|
16
|
+
"Performance below threshold (#{result[:threshold]}): #{result[:scores]}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -6,34 +6,11 @@ require_relative '../models/model_factory'
|
|
|
6
6
|
require_relative '../page_objects/pages/account'
|
|
7
7
|
require_relative '../page_objects/pages/login'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
let(:account) { Account.new(driver) }
|
|
13
|
-
let(:user) { ModelFactory.for('users')['registered user'] }
|
|
14
|
-
|
|
15
|
-
before do
|
|
16
|
-
login.visit
|
|
17
|
-
login.log_as(user['username'], user['password'])
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
context 'with a login user on the account page' do
|
|
21
|
-
it 'no accessibility errors are present on the page' do
|
|
22
|
-
expect(account.page).to be_axe_clean
|
|
23
|
-
end
|
|
9
|
+
# Uncomment for interactive debugging:
|
|
10
|
+
# require 'debug' # Ruby's built-in debugger (binding.break)
|
|
11
|
+
# require 'pry' # Pry debugger (binding.pry)
|
|
24
12
|
|
|
25
|
-
|
|
26
|
-
account_details = '.account-card'
|
|
27
|
-
expect(account.page).to be_axe_clean.within account_details
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
it 'no accessibility errors are present on the heading' do
|
|
31
|
-
heading = '.page-title'
|
|
32
|
-
expect(account.page).to be_axe_clean.within heading
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
<%- elsif selenium_based? || watir? || capybara? -%>
|
|
13
|
+
<%- if selenium_based? || watir? || capybara? -%>
|
|
37
14
|
describe 'Login' do
|
|
38
15
|
subject(:header) { account_page.header.customer_name }
|
|
39
16
|
|
|
@@ -50,12 +27,9 @@ describe 'Login' do
|
|
|
50
27
|
let(:password) { user['password'] }
|
|
51
28
|
|
|
52
29
|
it 'can successfully log in' do
|
|
53
|
-
|
|
54
|
-
check_page account_page
|
|
55
|
-
<%- else -%>
|
|
30
|
+
# binding.break # Uncomment to pause here and inspect state
|
|
56
31
|
account_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
|
|
57
32
|
expect(header).to eq "Welcome back #{user['name']}"
|
|
58
|
-
<%- end -%>
|
|
59
33
|
end
|
|
60
34
|
end
|
|
61
35
|
|
|
@@ -63,12 +37,8 @@ describe 'Login' do
|
|
|
63
37
|
let(:password) { 'wrongPassword' }
|
|
64
38
|
|
|
65
39
|
it 'cannot log in' do
|
|
66
|
-
<%- if visual? -%>
|
|
67
|
-
check_page login_page
|
|
68
|
-
<%- else -%>
|
|
69
40
|
login_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
|
|
70
41
|
expect(header).to eq 'Login or register'
|
|
71
|
-
<%- end -%>
|
|
72
42
|
end
|
|
73
43
|
end
|
|
74
44
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../helpers/spec_helper'
|
|
4
|
+
require_relative '../helpers/visual_helper'
|
|
5
|
+
require_relative '../page_objects/pages/login'
|
|
6
|
+
|
|
7
|
+
describe 'Visual Regression' do
|
|
8
|
+
include VisualHelper
|
|
9
|
+
|
|
10
|
+
let(:login_page) { <% if capybara? -%>Login.new<% elsif watir? -%>Login.new(browser)<% else -%>Login.new(driver)<% end -%> }
|
|
11
|
+
|
|
12
|
+
it 'login page matches baseline' do
|
|
13
|
+
login_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
|
|
14
|
+
screenshot_path = <% if capybara? -%>page.save_screenshot('tmp/visual_login.png')<% elsif watir? -%>browser.screenshot.save('tmp/visual_login.png')<% else -%>driver.save_screenshot('tmp/visual_login.png')<% end %>
|
|
15
|
+
result = compare_screenshot('login_page', screenshot_path)
|
|
16
|
+
|
|
17
|
+
expect(result[:status]).not_to eq(:mismatch),
|
|
18
|
+
"Visual diff: #{(result[:diff].to_f * 100).round(2)}% difference. See: #{result[:diff_path]}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -16,9 +16,13 @@ module TemplateRenderer
|
|
|
16
16
|
#
|
|
17
17
|
# Performance: ~10x speedup on cached renders (135ms → ~13.5ms)
|
|
18
18
|
class PartialCache
|
|
19
|
+
# Set to true during batch generation to skip File.mtime syscalls on cache hits
|
|
20
|
+
attr_accessor :batch_mode
|
|
21
|
+
|
|
19
22
|
def initialize(generator_class)
|
|
20
23
|
@cache = {}
|
|
21
24
|
@resolver = PartialResolver.new(generator_class)
|
|
25
|
+
@batch_mode = false
|
|
22
26
|
end
|
|
23
27
|
|
|
24
28
|
# Render a partial with caching
|
|
@@ -81,9 +85,15 @@ module TemplateRenderer
|
|
|
81
85
|
"#{name}:#{trim_mode}"
|
|
82
86
|
end
|
|
83
87
|
|
|
84
|
-
# Get cached ERB or compile and cache it
|
|
88
|
+
# Get cached ERB or compile and cache it.
|
|
89
|
+
# In batch_mode, skip File.mtime syscalls on cache hits since templates
|
|
90
|
+
# don't change mid-generation (~2-5ms saved per partial render).
|
|
85
91
|
def get_or_compile(cache_key, path, trim_mode)
|
|
86
92
|
cached = @cache[cache_key]
|
|
93
|
+
|
|
94
|
+
# Fast path: batch mode cache hit (skip mtime check)
|
|
95
|
+
return cached[:erb] if cached && @batch_mode
|
|
96
|
+
|
|
87
97
|
current_mtime = File.mtime(path)
|
|
88
98
|
|
|
89
99
|
# Cache miss or stale cache (file modified)
|
|
@@ -78,18 +78,13 @@ module TemplateRenderer
|
|
|
78
78
|
nil
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
-
# Search all Generator.source_paths for the partial
|
|
82
|
-
#
|
|
81
|
+
# Search all Generator.source_paths for the partial.
|
|
82
|
+
# Uses a cached directory index to avoid repeated Dir.glob calls.
|
|
83
83
|
def search_source_paths(partial_filename)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
full_path = File.join(
|
|
84
|
+
# Check the pre-built index first (fast path)
|
|
85
|
+
partial_dirs.each do |dir|
|
|
86
|
+
full_path = File.join(dir, partial_filename)
|
|
87
87
|
return { found: File.expand_path(full_path), searched: [full_path] } if File.exist?(full_path)
|
|
88
|
-
|
|
89
|
-
# Then try subdirectories (e.g., templates/common/partials/, templates/helpers/partials/)
|
|
90
|
-
Dir.glob(File.join(source_path, '*', PARTIALS_DIR, partial_filename)).each do |path|
|
|
91
|
-
return { found: File.expand_path(path), searched: [path] } if File.exist?(path)
|
|
92
|
-
end
|
|
93
88
|
end
|
|
94
89
|
|
|
95
90
|
{ found: nil, searched: [] }
|
|
@@ -99,5 +94,17 @@ module TemplateRenderer
|
|
|
99
94
|
def source_paths
|
|
100
95
|
@source_paths ||= @generator_class.source_paths || []
|
|
101
96
|
end
|
|
97
|
+
|
|
98
|
+
# Pre-built index of all directories that could contain partials.
|
|
99
|
+
# Scanned once via Dir.glob, then reused for every partial lookup.
|
|
100
|
+
def partial_dirs
|
|
101
|
+
@partial_dirs ||= source_paths.flat_map do |source_path|
|
|
102
|
+
dirs = []
|
|
103
|
+
direct = File.join(source_path, PARTIALS_DIR)
|
|
104
|
+
dirs << direct if File.directory?(direct)
|
|
105
|
+
Dir.glob(File.join(source_path, '*', PARTIALS_DIR)).each { |d| dirs << d }
|
|
106
|
+
dirs
|
|
107
|
+
end
|
|
108
|
+
end
|
|
102
109
|
end
|
|
103
110
|
end
|
|
@@ -77,14 +77,30 @@ module TemplateRenderer
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
# Clear the template cache (useful for testing)
|
|
80
|
+
# Clears cache for this class and all subclasses
|
|
80
81
|
def clear_template_cache
|
|
81
82
|
@template_renderer&.clear
|
|
82
83
|
@template_renderer = nil
|
|
84
|
+
subclasses.each do |subclass|
|
|
85
|
+
subclass.clear_template_cache if subclass.respond_to?(:clear_template_cache)
|
|
86
|
+
end
|
|
83
87
|
end
|
|
84
88
|
|
|
85
89
|
# Get cache statistics (useful for debugging)
|
|
90
|
+
# Aggregates stats across this class and all subclasses
|
|
86
91
|
def template_cache_stats
|
|
87
|
-
@template_renderer&.stats || { size: 0, entries: [], memory_estimate: 0 }
|
|
92
|
+
own = @template_renderer&.stats || { size: 0, entries: [], memory_estimate: 0 }
|
|
93
|
+
subclasses.each do |subclass|
|
|
94
|
+
next unless subclass.respond_to?(:template_cache_stats)
|
|
95
|
+
|
|
96
|
+
sub_stats = subclass.instance_variable_get(:@template_renderer)&.stats
|
|
97
|
+
next unless sub_stats
|
|
98
|
+
|
|
99
|
+
own[:size] += sub_stats[:size]
|
|
100
|
+
own[:entries].concat(sub_stats[:entries])
|
|
101
|
+
own[:memory_estimate] += sub_stats[:memory_estimate]
|
|
102
|
+
end
|
|
103
|
+
own
|
|
88
104
|
end
|
|
89
105
|
end
|
|
90
106
|
end
|