ruby_raider 3.0.0 → 3.1.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 -62
- data/lib/commands/scaffolding_commands.rb +22 -188
- data/lib/commands/utility_commands.rb +10 -44
- 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 -5
- data/lib/generators/cucumber/templates/partials/watir_env.tt +0 -5
- 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 -42
- 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 +0 -4
- 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 +1 -1
- data/lib/generators/templates/helpers/partials/debug_diagnostics.tt +1 -3
- data/lib/generators/templates/helpers/partials/debug_start.tt +1 -3
- 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 +1 -1
- data/lib/generators/templates/helpers/spec_helper.tt +2 -39
- data/lib/ruby_raider.rb +2 -50
- data/lib/scaffolding/project_detector.rb +30 -13
- 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 +26 -24
- data/lib/version +1 -1
- data/sig/commands/scaffolding_commands.rbs +0 -12
- data/sig/commands/utility_commands.rbs +0 -6
- data/sig/generators/cucumber/cucumber_generator.rbs +0 -4
- data/sig/generators/generator.rbs +0 -11
- data/sig/generators/helper_generator.rbs +1 -5
- 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 -4
- data/spec/commands/raider_commands_spec.rb +0 -61
- data/spec/commands/scaffolding_commands_spec.rb +22 -0
- 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 +9 -9
- 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 +1 -51
- 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 +4 -240
- 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 -411
- 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/debug_helper_spec.rb +0 -20
- 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 +540 -0
- 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 +42 -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 +82 -0
- data/spec/utilities/utilities_spec.rb +105 -0
- metadata +7 -97
- 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/helpers/capybara_helper.tt +0 -32
- 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/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
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'pathname'
|
|
5
|
+
require_relative '../../lib/commands/scaffolding_commands'
|
|
6
|
+
require_relative '../../lib/scaffolding/scaffolding'
|
|
7
|
+
|
|
8
|
+
# Custom matchers (defined inline to avoid pulling in integration spec_helper)
|
|
9
|
+
RSpec::Matchers.define :have_frozen_string_literal do
|
|
10
|
+
match { |content| content.include?('# frozen_string_literal: true') }
|
|
11
|
+
failure_message { 'expected file to contain frozen_string_literal magic comment' }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
RSpec::Matchers.define :have_valid_ruby_syntax do
|
|
15
|
+
match do |content|
|
|
16
|
+
RubyVM::InstructionSequence.compile(content)
|
|
17
|
+
true
|
|
18
|
+
rescue SyntaxError
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
failure_message { |_content| "expected valid Ruby syntax but got SyntaxError: #{$ERROR_INFO&.message}" }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# End-to-end scaffolding tests that verify the scaffold command creates the right
|
|
25
|
+
# files with the right content across all automation × framework combinations.
|
|
26
|
+
describe 'Scaffolding E2E' do # rubocop:disable RSpec/DescribeClass
|
|
27
|
+
|
|
28
|
+
WEB_AUTOMATIONS = %w[selenium watir].freeze
|
|
29
|
+
TEST_FRAMEWORKS = %w[rspec cucumber].freeze
|
|
30
|
+
|
|
31
|
+
def self.scaffold_style(framework)
|
|
32
|
+
case framework
|
|
33
|
+
when 'rspec' then :spec
|
|
34
|
+
when 'cucumber' then :feature
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Build a minimal mock project that the scaffolding system can detect.
|
|
39
|
+
def self.build_mock_project(automation, framework) # rubocop:disable Metrics/MethodLength
|
|
40
|
+
dir = File.expand_path("tmp_scaffold_e2e_#{framework}_#{automation}")
|
|
41
|
+
FileUtils.rm_rf(dir)
|
|
42
|
+
FileUtils.mkdir_p("#{dir}/page_objects/pages")
|
|
43
|
+
FileUtils.mkdir_p("#{dir}/page_objects/abstract")
|
|
44
|
+
FileUtils.mkdir_p("#{dir}/page_objects/components")
|
|
45
|
+
FileUtils.mkdir_p("#{dir}/helpers")
|
|
46
|
+
FileUtils.mkdir_p("#{dir}/config")
|
|
47
|
+
FileUtils.mkdir_p("#{dir}/models/data")
|
|
48
|
+
|
|
49
|
+
case framework
|
|
50
|
+
when 'rspec' then FileUtils.mkdir_p("#{dir}/spec")
|
|
51
|
+
when 'cucumber' then FileUtils.mkdir_p("#{dir}/features/step_definitions")
|
|
52
|
+
FileUtils.mkdir_p("#{dir}/features/support")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
gems = []
|
|
56
|
+
case automation
|
|
57
|
+
when 'selenium' then gems << "gem 'selenium-webdriver'"
|
|
58
|
+
when 'watir' then gems << "gem 'watir'"
|
|
59
|
+
end
|
|
60
|
+
case framework
|
|
61
|
+
when 'rspec' then gems << "gem 'rspec'"
|
|
62
|
+
when 'cucumber' then gems << "gem 'cucumber'"
|
|
63
|
+
end
|
|
64
|
+
File.write("#{dir}/Gemfile", gems.join("\n") + "\n")
|
|
65
|
+
File.write("#{dir}/config/config.yml", "browser: chrome\nurl: http://localhost:3000\n")
|
|
66
|
+
File.write("#{dir}/page_objects/abstract/page.rb", "class Page; end\n")
|
|
67
|
+
File.write("#{dir}/page_objects/abstract/component.rb", "class Component; end\n")
|
|
68
|
+
|
|
69
|
+
dir
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# ──────────────────────────────────────────────
|
|
73
|
+
# Shared examples: Page object content
|
|
74
|
+
# ──────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
shared_examples 'valid scaffolded page' do
|
|
77
|
+
it 'has frozen_string_literal' do
|
|
78
|
+
expect(page_content).to have_frozen_string_literal
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'has valid Ruby syntax' do
|
|
82
|
+
expect(page_content).to have_valid_ruby_syntax
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'inherits from Page' do
|
|
86
|
+
expect(page_content).to include('class CheckoutPage < Page')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'requires abstract page' do
|
|
90
|
+
expect(page_content).to include("require_relative '../abstract/page'")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'includes url method (config has url)' do
|
|
94
|
+
expect(page_content).to include("def url(_page)")
|
|
95
|
+
expect(page_content).to include("'checkout'")
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
shared_examples 'selenium scaffolded page' do
|
|
100
|
+
it 'has driver.find_element example' do
|
|
101
|
+
expect(page_content).to include('driver.find_element')
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'has private section' do
|
|
105
|
+
expect(page_content).to include('private')
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it 'does not have capybara fill_in' do
|
|
109
|
+
expect(page_content).not_to include('fill_in')
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it 'does not have watir browser.text_field' do
|
|
113
|
+
expect(page_content).not_to include('browser.text_field')
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
shared_examples 'watir scaffolded page' do
|
|
118
|
+
it 'has browser.text_field example' do
|
|
119
|
+
expect(page_content).to include('browser.text_field')
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'has private section' do
|
|
123
|
+
expect(page_content).to include('private')
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'does not have selenium find_element' do
|
|
127
|
+
expect(page_content).not_to include('driver.find_element')
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'does not have capybara fill_in' do
|
|
131
|
+
expect(page_content).not_to include('fill_in')
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# ──────────────────────────────────────────────
|
|
136
|
+
# Shared examples: Spec content (RSpec)
|
|
137
|
+
# ──────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
shared_examples 'valid scaffolded spec' do
|
|
140
|
+
it 'has frozen_string_literal' do
|
|
141
|
+
expect(spec_content).to have_frozen_string_literal
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it 'has valid Ruby syntax' do
|
|
145
|
+
expect(spec_content).to have_valid_ruby_syntax
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it 'describes the page class' do
|
|
149
|
+
expect(spec_content).to include("describe 'CheckoutPage'")
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it 'requires the page object' do
|
|
153
|
+
expect(spec_content).to include("require_relative '../page_objects/pages/checkout'")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'has pending test' do
|
|
157
|
+
expect(spec_content).to include("pending 'implement test'")
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it 'has before block with navigation' do
|
|
161
|
+
expect(spec_content).to include('before do')
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
shared_examples 'selenium scaffolded spec' do
|
|
166
|
+
it 'instantiates page with driver' do
|
|
167
|
+
expect(spec_content).to include('CheckoutPage.new(driver)')
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it 'navigates with driver' do
|
|
171
|
+
expect(spec_content).to include('driver.navigate.to')
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it 'does not use capybara visit' do
|
|
175
|
+
expect(spec_content).not_to match(/^\s+visit '/)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
shared_examples 'watir scaffolded spec' do
|
|
180
|
+
it 'instantiates page with browser' do
|
|
181
|
+
expect(spec_content).to include('CheckoutPage.new(browser)')
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it 'navigates with browser.goto' do
|
|
185
|
+
expect(spec_content).to include('browser.goto')
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# ──────────────────────────────────────────────
|
|
190
|
+
# Shared examples: Feature content (Cucumber)
|
|
191
|
+
# ──────────────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
shared_examples 'valid scaffolded feature' do
|
|
194
|
+
it 'has Feature keyword' do
|
|
195
|
+
expect(feature_content).to include('Feature: Checkout')
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it 'has Scenario keyword' do
|
|
199
|
+
expect(feature_content).to include('Scenario: Scenario name')
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it 'has Given/When/Then steps' do
|
|
203
|
+
expect(feature_content).to include('Given I am on the checkout page')
|
|
204
|
+
expect(feature_content).to include('When I perform an action')
|
|
205
|
+
expect(feature_content).to include('Then I see the expected result')
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# ──────────────────────────────────────────────
|
|
210
|
+
# Shared examples: Steps content (Cucumber)
|
|
211
|
+
# ──────────────────────────────────────────────
|
|
212
|
+
|
|
213
|
+
shared_examples 'valid scaffolded steps' do
|
|
214
|
+
it 'has frozen_string_literal' do
|
|
215
|
+
expect(steps_content).to have_frozen_string_literal
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
it 'has valid Ruby syntax' do
|
|
219
|
+
expect(steps_content).to have_valid_ruby_syntax
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it 'requires the page object' do
|
|
223
|
+
expect(steps_content).to include("require_relative '../../page_objects/pages/checkout'")
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
it 'has Given/When/Then blocks' do
|
|
227
|
+
expect(steps_content).to include("Given('I am on the checkout page')")
|
|
228
|
+
expect(steps_content).to include("When('I perform an action')")
|
|
229
|
+
expect(steps_content).to include("Then('I see the expected result')")
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
it 'has pending steps' do
|
|
233
|
+
expect(steps_content).to include("pending 'implement step'")
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
shared_examples 'selenium scaffolded steps' do
|
|
238
|
+
it 'instantiates page with driver' do
|
|
239
|
+
expect(steps_content).to include('CheckoutPage.new(driver)')
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it 'does not use capybara visit' do
|
|
243
|
+
expect(steps_content).not_to match(/^\s+visit '/)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
shared_examples 'watir scaffolded steps' do
|
|
248
|
+
it 'instantiates page with browser' do
|
|
249
|
+
expect(steps_content).to include('CheckoutPage.new(browser)')
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# ──────────────────────────────────────────────
|
|
254
|
+
# Shared examples: Helper content
|
|
255
|
+
# ──────────────────────────────────────────────
|
|
256
|
+
|
|
257
|
+
shared_examples 'valid scaffolded helper' do
|
|
258
|
+
it 'has frozen_string_literal' do
|
|
259
|
+
expect(helper_content).to have_frozen_string_literal
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
it 'has valid Ruby syntax' do
|
|
263
|
+
expect(helper_content).to have_valid_ruby_syntax
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it 'defines helper module' do
|
|
267
|
+
expect(helper_content).to include('module CheckoutHelper')
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
it 'has helper placeholder comment' do
|
|
271
|
+
expect(helper_content).to include('# Add your helper code here')
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# ──────────────────────────────────────────────
|
|
276
|
+
# Shared examples: Component content
|
|
277
|
+
# ──────────────────────────────────────────────
|
|
278
|
+
|
|
279
|
+
shared_examples 'valid scaffolded component' do
|
|
280
|
+
it 'has frozen_string_literal' do
|
|
281
|
+
expect(component_content).to have_frozen_string_literal
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
it 'has valid Ruby syntax' do
|
|
285
|
+
expect(component_content).to have_valid_ruby_syntax
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
it 'inherits from Component' do
|
|
289
|
+
expect(component_content).to include('class Sidebar < Component')
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
it 'requires abstract component' do
|
|
293
|
+
expect(component_content).to include("require_relative '../abstract/component'")
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
shared_examples 'selenium scaffolded component' do
|
|
298
|
+
it 'has driver.find_element example' do
|
|
299
|
+
expect(component_content).to include('driver.find_element')
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
it 'has private section' do
|
|
303
|
+
expect(component_content).to include('private')
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
it 'has content method' do
|
|
307
|
+
expect(component_content).to include('def content')
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
shared_examples 'watir scaffolded component' do
|
|
312
|
+
it 'has browser.element example' do
|
|
313
|
+
expect(component_content).to include('browser.element')
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
it 'has private section' do
|
|
317
|
+
expect(component_content).to include('private')
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
it 'has content method' do
|
|
321
|
+
expect(component_content).to include('def content')
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# ══════════════════════════════════════════════
|
|
326
|
+
# Test matrix: automation × framework
|
|
327
|
+
# ══════════════════════════════════════════════
|
|
328
|
+
|
|
329
|
+
WEB_AUTOMATIONS.each do |automation|
|
|
330
|
+
TEST_FRAMEWORKS.each do |framework|
|
|
331
|
+
style = scaffold_style(framework)
|
|
332
|
+
|
|
333
|
+
context "with #{framework} and #{automation}" do # rubocop:disable RSpec/ContextWording
|
|
334
|
+
let(:scaffold) { ScaffoldingCommands }
|
|
335
|
+
|
|
336
|
+
# Use absolute path so Dir.chdir is safe regardless of current CWD
|
|
337
|
+
project_dir = build_mock_project(automation, framework)
|
|
338
|
+
|
|
339
|
+
before do
|
|
340
|
+
Dir.chdir(project_dir)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
after do
|
|
344
|
+
Dir.chdir(File.dirname(project_dir))
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
after(:all) do # rubocop:disable RSpec/BeforeAfterAll
|
|
348
|
+
FileUtils.rm_rf(project_dir)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# ── Default scaffold ───────────────────
|
|
352
|
+
|
|
353
|
+
describe 'default scaffold' do
|
|
354
|
+
before do
|
|
355
|
+
scaffold.new.invoke(:scaffold, nil, %w[checkout])
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
after do
|
|
359
|
+
FileUtils.rm_f('page_objects/pages/checkout.rb')
|
|
360
|
+
FileUtils.rm_f('spec/checkout_page_spec.rb')
|
|
361
|
+
if framework == 'cucumber'
|
|
362
|
+
FileUtils.rm_f('features/checkout.feature')
|
|
363
|
+
FileUtils.rm_f('features/step_definitions/checkout_steps.rb')
|
|
364
|
+
else
|
|
365
|
+
FileUtils.rm_rf('features')
|
|
366
|
+
FileUtils.rm_rf('spec') unless framework == 'rspec'
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
it 'creates page object' do
|
|
371
|
+
expect(Pathname.new('page_objects/pages/checkout.rb')).to be_file
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
if style == :spec
|
|
375
|
+
it 'creates spec file' do
|
|
376
|
+
expect(Pathname.new('spec/checkout_page_spec.rb')).to be_file
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
it 'does not create feature file' do
|
|
380
|
+
expect(Pathname.new('features/checkout.feature')).not_to be_file
|
|
381
|
+
end
|
|
382
|
+
else # :feature
|
|
383
|
+
it 'creates feature file' do
|
|
384
|
+
expect(Pathname.new('features/checkout.feature')).to be_file
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
it 'creates steps file' do
|
|
388
|
+
expect(Pathname.new('features/step_definitions/checkout_steps.rb')).to be_file
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# Page content validation
|
|
393
|
+
let(:page_content) { File.read('page_objects/pages/checkout.rb') }
|
|
394
|
+
|
|
395
|
+
include_examples 'valid scaffolded page'
|
|
396
|
+
include_examples "#{automation} scaffolded page"
|
|
397
|
+
|
|
398
|
+
# Test content validation (depends on scaffold style)
|
|
399
|
+
if style == :spec
|
|
400
|
+
let(:spec_content) { File.read('spec/checkout_page_spec.rb') }
|
|
401
|
+
include_examples 'valid scaffolded spec'
|
|
402
|
+
include_examples "#{automation} scaffolded spec"
|
|
403
|
+
else # :feature
|
|
404
|
+
let(:feature_content) { File.read('features/checkout.feature') }
|
|
405
|
+
include_examples 'valid scaffolded feature'
|
|
406
|
+
|
|
407
|
+
let(:steps_content) { File.read('features/step_definitions/checkout_steps.rb') }
|
|
408
|
+
include_examples 'valid scaffolded steps'
|
|
409
|
+
include_examples "#{automation} scaffolded steps"
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
# ── Component scaffold ─────────────────
|
|
414
|
+
|
|
415
|
+
describe 'component scaffold' do
|
|
416
|
+
before do
|
|
417
|
+
scaffold.new.invoke(:component, nil, %w[sidebar])
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
after do
|
|
421
|
+
FileUtils.rm_f('page_objects/components/sidebar.rb')
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
let(:component_content) { File.read('page_objects/components/sidebar.rb') }
|
|
425
|
+
|
|
426
|
+
include_examples 'valid scaffolded component'
|
|
427
|
+
include_examples "#{automation} scaffolded component"
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
# ── Helper scaffold ────────────────────
|
|
431
|
+
|
|
432
|
+
describe 'helper scaffold' do
|
|
433
|
+
before do
|
|
434
|
+
scaffold.new.invoke(:helper, nil, %w[checkout])
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
after do
|
|
438
|
+
FileUtils.rm_f('helpers/checkout_helper.rb')
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
let(:helper_content) { File.read('helpers/checkout_helper.rb') }
|
|
442
|
+
|
|
443
|
+
include_examples 'valid scaffolded helper'
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
# ── Batch scaffold ─────────────────────
|
|
447
|
+
|
|
448
|
+
describe 'batch scaffold' do
|
|
449
|
+
after do
|
|
450
|
+
%w[search filter].each do |name|
|
|
451
|
+
FileUtils.rm_f("page_objects/pages/#{name}.rb")
|
|
452
|
+
FileUtils.rm_f("spec/#{name}_page_spec.rb")
|
|
453
|
+
FileUtils.rm_f("features/#{name}.feature")
|
|
454
|
+
FileUtils.rm_f("features/step_definitions/#{name}_steps.rb")
|
|
455
|
+
end
|
|
456
|
+
FileUtils.rm_rf('features') unless framework == 'cucumber'
|
|
457
|
+
FileUtils.rm_rf('spec') unless framework == 'rspec'
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
it 'generates multiple scaffolds at once' do
|
|
461
|
+
scaffold.new.invoke(:scaffold, nil, %w[search filter])
|
|
462
|
+
expect(Pathname.new('page_objects/pages/search.rb')).to be_file
|
|
463
|
+
expect(Pathname.new('page_objects/pages/filter.rb')).to be_file
|
|
464
|
+
|
|
465
|
+
if style == :spec
|
|
466
|
+
expect(Pathname.new('spec/search_page_spec.rb')).to be_file
|
|
467
|
+
expect(Pathname.new('spec/filter_page_spec.rb')).to be_file
|
|
468
|
+
else
|
|
469
|
+
expect(Pathname.new('features/search.feature')).to be_file
|
|
470
|
+
expect(Pathname.new('features/filter.feature')).to be_file
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
# ── Name normalization ─────────────────
|
|
476
|
+
|
|
477
|
+
describe 'name normalization' do
|
|
478
|
+
after do
|
|
479
|
+
%w[cart shopping_cart checkout].each do |name|
|
|
480
|
+
FileUtils.rm_f("page_objects/pages/#{name}.rb")
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
it 'strips _page suffix' do
|
|
485
|
+
scaffold.new.invoke(:page, nil, %w[cart_page])
|
|
486
|
+
expect(Pathname.new('page_objects/pages/cart.rb')).to be_file
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
it 'converts CamelCase to snake_case' do
|
|
490
|
+
scaffold.new.invoke(:page, nil, %w[ShoppingCart])
|
|
491
|
+
expect(Pathname.new('page_objects/pages/shopping_cart.rb')).to be_file
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
it 'handles CamelCase with Page suffix' do
|
|
495
|
+
scaffold.new.invoke(:page, nil, %w[CheckoutPage])
|
|
496
|
+
expect(Pathname.new('page_objects/pages/checkout.rb')).to be_file
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# ── Relationships ──────────────────────
|
|
501
|
+
|
|
502
|
+
describe 'relationships' do
|
|
503
|
+
before do
|
|
504
|
+
File.write('page_objects/pages/login.rb', "class LoginPage < Page; end\n")
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
after do
|
|
508
|
+
FileUtils.rm_f('page_objects/pages/dashboard.rb')
|
|
509
|
+
FileUtils.rm_f('spec/dashboard_page_spec.rb')
|
|
510
|
+
FileUtils.rm_f('features/dashboard.feature')
|
|
511
|
+
FileUtils.rm_f('features/step_definitions/dashboard_steps.rb')
|
|
512
|
+
FileUtils.rm_f('page_objects/pages/login.rb')
|
|
513
|
+
FileUtils.rm_rf('features') unless framework == 'cucumber'
|
|
514
|
+
FileUtils.rm_rf('spec') unless framework == 'rspec'
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
it 'adds require_relative for dependent page' do
|
|
518
|
+
scaffold.new.invoke(:page, nil, %w[dashboard --uses login])
|
|
519
|
+
content = File.read('page_objects/pages/dashboard.rb')
|
|
520
|
+
expect(content).to include("require_relative 'login'")
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
if style == :spec
|
|
524
|
+
it 'adds dependency to spec let declarations' do
|
|
525
|
+
scaffold.new.invoke(:spec, nil, %w[dashboard --uses login])
|
|
526
|
+
content = File.read('spec/dashboard_page_spec.rb')
|
|
527
|
+
expect(content).to include('LoginPage')
|
|
528
|
+
|
|
529
|
+
case automation
|
|
530
|
+
when 'selenium' then expect(content).to include('LoginPage.new(driver)')
|
|
531
|
+
when 'watir' then expect(content).to include('LoginPage.new(browser)')
|
|
532
|
+
end
|
|
533
|
+
end
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
end
|
|
@@ -2,12 +2,10 @@ module SettingsHelper
|
|
|
2
2
|
def create_settings(options)
|
|
3
3
|
automation = options[:automation]
|
|
4
4
|
framework = options[:framework]
|
|
5
|
-
ci_platform = options[:ci_platform]
|
|
6
5
|
{
|
|
7
6
|
automation:,
|
|
8
7
|
framework:,
|
|
9
|
-
|
|
10
|
-
name: ci_platform ? "#{framework}_#{automation}_#{ci_platform}" : "#{framework}_#{automation}"
|
|
8
|
+
name: "#{framework}_#{automation}"
|
|
11
9
|
}
|
|
12
10
|
end
|
|
13
11
|
end
|
|
@@ -5,15 +5,13 @@ require 'rspec'
|
|
|
5
5
|
require_relative '../../lib/generators/invoke_generators'
|
|
6
6
|
require_relative 'settings_helper'
|
|
7
7
|
|
|
8
|
-
AUTOMATION_TYPES = %w[android ios selenium watir cross_platform
|
|
9
|
-
FRAMEWORKS = %w[cucumber rspec
|
|
10
|
-
CI_PLATFORMS = [nil, 'github', 'gitlab'].freeze
|
|
8
|
+
AUTOMATION_TYPES = %w[android ios selenium watir cross_platform].freeze
|
|
9
|
+
FRAMEWORKS = %w[cucumber rspec].freeze
|
|
11
10
|
|
|
12
11
|
# Named constants for readable spec references — no more fragile numeric indices
|
|
13
12
|
module FrameworkIndex
|
|
14
13
|
CUCUMBER = FRAMEWORKS[0]
|
|
15
14
|
RSPEC = FRAMEWORKS[1]
|
|
16
|
-
MINITEST = FRAMEWORKS[2]
|
|
17
15
|
end
|
|
18
16
|
|
|
19
17
|
module AutomationIndex
|
|
@@ -22,17 +20,16 @@ module AutomationIndex
|
|
|
22
20
|
SELENIUM = AUTOMATION_TYPES[2]
|
|
23
21
|
WATIR = AUTOMATION_TYPES[3]
|
|
24
22
|
CROSS_PLATFORM = AUTOMATION_TYPES[4]
|
|
25
|
-
CAPYBARA = AUTOMATION_TYPES[5]
|
|
26
23
|
end
|
|
27
24
|
|
|
28
25
|
# Lazy project generation — only generates a project the first time it's needed.
|
|
29
|
-
# Replaces the old before(:all) that generated all
|
|
26
|
+
# Replaces the old before(:all) that generated all combinations upfront.
|
|
30
27
|
module LazyProjectGenerator
|
|
31
28
|
GENERATED = Set.new
|
|
32
29
|
MUTEX = Mutex.new
|
|
33
30
|
|
|
34
|
-
def ensure_project(framework:, automation
|
|
35
|
-
settings = create_settings(framework:, automation
|
|
31
|
+
def ensure_project(framework:, automation:)
|
|
32
|
+
settings = create_settings(framework:, automation:)
|
|
36
33
|
project_name = settings[:name]
|
|
37
34
|
|
|
38
35
|
MUTEX.synchronize do
|
|
@@ -57,9 +54,7 @@ RSpec.configure do |config|
|
|
|
57
54
|
# Individual specs can also call ensure_project() for on-demand generation.
|
|
58
55
|
FRAMEWORKS.each do |framework|
|
|
59
56
|
AUTOMATION_TYPES.each do |automation|
|
|
60
|
-
|
|
61
|
-
ensure_project(framework:, automation:, ci_platform:)
|
|
62
|
-
end
|
|
57
|
+
ensure_project(framework:, automation:)
|
|
63
58
|
end
|
|
64
59
|
end
|
|
65
60
|
end
|