ruby_raider 2.0.0 → 3.0.1
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 +246 -38
- data/lib/commands/utility_commands.rb +92 -3
- 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 +13 -3
- data/lib/generators/templates/helpers/capybara_helper.tt +6 -2
- 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 +9 -8
- 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 +96 -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 +61 -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/commands/scaffolding_commands_spec.rb +22 -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 +29 -9
- 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/scaffolding_e2e_spec.rb +775 -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 +142 -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
- data/spec/utilities/headless_config_spec.rb +89 -0
- data/spec/utilities/utilities_spec.rb +105 -0
- metadata +154 -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
|
@@ -0,0 +1,775 @@
|
|
|
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 capybara watir].freeze
|
|
29
|
+
TEST_FRAMEWORKS = %w[rspec cucumber minitest].freeze
|
|
30
|
+
|
|
31
|
+
# The scaffolding system's generate_default_scaffold checks for spec/ or features/.
|
|
32
|
+
# Minitest projects have test/ only — so the scaffold falls through to feature+steps.
|
|
33
|
+
def self.scaffold_style(framework)
|
|
34
|
+
case framework
|
|
35
|
+
when 'rspec' then :spec
|
|
36
|
+
when 'cucumber' then :feature
|
|
37
|
+
when 'minitest' then :feature # no spec/ dir → falls to else clause
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Build a minimal mock project that the scaffolding system can detect.
|
|
42
|
+
def self.build_mock_project(automation, framework) # rubocop:disable Metrics/MethodLength
|
|
43
|
+
dir = File.expand_path("tmp_scaffold_e2e_#{framework}_#{automation}")
|
|
44
|
+
FileUtils.rm_rf(dir)
|
|
45
|
+
FileUtils.mkdir_p("#{dir}/page_objects/pages")
|
|
46
|
+
FileUtils.mkdir_p("#{dir}/page_objects/abstract")
|
|
47
|
+
FileUtils.mkdir_p("#{dir}/page_objects/components")
|
|
48
|
+
FileUtils.mkdir_p("#{dir}/helpers")
|
|
49
|
+
FileUtils.mkdir_p("#{dir}/config")
|
|
50
|
+
FileUtils.mkdir_p("#{dir}/models/data")
|
|
51
|
+
|
|
52
|
+
case framework
|
|
53
|
+
when 'rspec' then FileUtils.mkdir_p("#{dir}/spec")
|
|
54
|
+
when 'cucumber' then FileUtils.mkdir_p("#{dir}/features/step_definitions")
|
|
55
|
+
FileUtils.mkdir_p("#{dir}/features/support")
|
|
56
|
+
when 'minitest' then FileUtils.mkdir_p("#{dir}/test")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
gems = []
|
|
60
|
+
case automation
|
|
61
|
+
when 'selenium' then gems << "gem 'selenium-webdriver'"
|
|
62
|
+
when 'capybara' then gems << "gem 'capybara'"
|
|
63
|
+
when 'watir' then gems << "gem 'watir'"
|
|
64
|
+
end
|
|
65
|
+
case framework
|
|
66
|
+
when 'rspec' then gems << "gem 'rspec'"
|
|
67
|
+
when 'cucumber' then gems << "gem 'cucumber'"
|
|
68
|
+
when 'minitest' then gems << "gem 'minitest'"
|
|
69
|
+
end
|
|
70
|
+
File.write("#{dir}/Gemfile", gems.join("\n") + "\n")
|
|
71
|
+
File.write("#{dir}/config/config.yml", "browser: chrome\nurl: http://localhost:3000\n")
|
|
72
|
+
File.write("#{dir}/page_objects/abstract/page.rb", "class Page; end\n")
|
|
73
|
+
File.write("#{dir}/page_objects/abstract/component.rb", "class Component; end\n")
|
|
74
|
+
|
|
75
|
+
dir
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# ──────────────────────────────────────────────
|
|
79
|
+
# Shared examples: Page object content
|
|
80
|
+
# ──────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
shared_examples 'valid scaffolded page' do
|
|
83
|
+
it 'has frozen_string_literal' do
|
|
84
|
+
expect(page_content).to have_frozen_string_literal
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'has valid Ruby syntax' do
|
|
88
|
+
expect(page_content).to have_valid_ruby_syntax
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'inherits from Page' do
|
|
92
|
+
expect(page_content).to include('class CheckoutPage < Page')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'requires abstract page' do
|
|
96
|
+
expect(page_content).to include("require_relative '../abstract/page'")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'includes url method (config has url)' do
|
|
100
|
+
expect(page_content).to include("def url(_page)")
|
|
101
|
+
expect(page_content).to include("'checkout'")
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
shared_examples 'selenium scaffolded page' do
|
|
106
|
+
it 'has driver.find_element example' do
|
|
107
|
+
expect(page_content).to include('driver.find_element')
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'has private section' do
|
|
111
|
+
expect(page_content).to include('private')
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'does not have capybara fill_in' do
|
|
115
|
+
expect(page_content).not_to include('fill_in')
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'does not have watir browser.text_field' do
|
|
119
|
+
expect(page_content).not_to include('browser.text_field')
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
shared_examples 'capybara scaffolded page' do
|
|
124
|
+
it 'has fill_in example' do
|
|
125
|
+
expect(page_content).to include('fill_in')
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'has no private section' do
|
|
129
|
+
expect(page_content).not_to include('private')
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it 'does not have selenium find_element' do
|
|
133
|
+
expect(page_content).not_to include('driver.find_element')
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'does not have watir browser.text_field' do
|
|
137
|
+
expect(page_content).not_to include('browser.text_field')
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
shared_examples 'watir scaffolded page' do
|
|
142
|
+
it 'has browser.text_field example' do
|
|
143
|
+
expect(page_content).to include('browser.text_field')
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it 'has private section' do
|
|
147
|
+
expect(page_content).to include('private')
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it 'does not have selenium find_element' do
|
|
151
|
+
expect(page_content).not_to include('driver.find_element')
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it 'does not have capybara fill_in' do
|
|
155
|
+
expect(page_content).not_to include('fill_in')
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# ──────────────────────────────────────────────
|
|
160
|
+
# Shared examples: Spec content (RSpec)
|
|
161
|
+
# ──────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
shared_examples 'valid scaffolded spec' do
|
|
164
|
+
it 'has frozen_string_literal' do
|
|
165
|
+
expect(spec_content).to have_frozen_string_literal
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'has valid Ruby syntax' do
|
|
169
|
+
expect(spec_content).to have_valid_ruby_syntax
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it 'describes the page class' do
|
|
173
|
+
expect(spec_content).to include("describe 'CheckoutPage'")
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it 'requires the page object' do
|
|
177
|
+
expect(spec_content).to include("require_relative '../page_objects/pages/checkout'")
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it 'has pending test' do
|
|
181
|
+
expect(spec_content).to include("pending 'implement test'")
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it 'has before block with navigation' do
|
|
185
|
+
expect(spec_content).to include('before do')
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
shared_examples 'selenium scaffolded spec' do
|
|
190
|
+
it 'instantiates page with driver' do
|
|
191
|
+
expect(spec_content).to include('CheckoutPage.new(driver)')
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it 'navigates with driver' do
|
|
195
|
+
expect(spec_content).to include('driver.navigate.to')
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it 'does not use capybara visit' do
|
|
199
|
+
expect(spec_content).not_to match(/^\s+visit '/)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
shared_examples 'capybara scaffolded spec' do
|
|
204
|
+
it 'instantiates page without arguments' do
|
|
205
|
+
expect(spec_content).to include('CheckoutPage.new')
|
|
206
|
+
expect(spec_content).not_to include('CheckoutPage.new(driver)')
|
|
207
|
+
expect(spec_content).not_to include('CheckoutPage.new(browser)')
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
it 'uses visit for navigation' do
|
|
211
|
+
expect(spec_content).to include("visit 'checkout'")
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
shared_examples 'watir scaffolded spec' do
|
|
216
|
+
it 'instantiates page with browser' do
|
|
217
|
+
expect(spec_content).to include('CheckoutPage.new(browser)')
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it 'navigates with browser.goto' do
|
|
221
|
+
expect(spec_content).to include('browser.goto')
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# ──────────────────────────────────────────────
|
|
226
|
+
# Shared examples: Feature content (Cucumber)
|
|
227
|
+
# ──────────────────────────────────────────────
|
|
228
|
+
|
|
229
|
+
shared_examples 'valid scaffolded feature' do
|
|
230
|
+
it 'has Feature keyword' do
|
|
231
|
+
expect(feature_content).to include('Feature: Checkout')
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
it 'has Scenario keyword' do
|
|
235
|
+
expect(feature_content).to include('Scenario: Scenario name')
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
it 'has Given/When/Then steps' do
|
|
239
|
+
expect(feature_content).to include('Given I am on the checkout page')
|
|
240
|
+
expect(feature_content).to include('When I perform an action')
|
|
241
|
+
expect(feature_content).to include('Then I see the expected result')
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# ──────────────────────────────────────────────
|
|
246
|
+
# Shared examples: Steps content (Cucumber)
|
|
247
|
+
# ──────────────────────────────────────────────
|
|
248
|
+
|
|
249
|
+
shared_examples 'valid scaffolded steps' do
|
|
250
|
+
it 'has frozen_string_literal' do
|
|
251
|
+
expect(steps_content).to have_frozen_string_literal
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
it 'has valid Ruby syntax' do
|
|
255
|
+
expect(steps_content).to have_valid_ruby_syntax
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
it 'requires the page object' do
|
|
259
|
+
expect(steps_content).to include("require_relative '../../page_objects/pages/checkout'")
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
it 'has Given/When/Then blocks' do
|
|
263
|
+
expect(steps_content).to include("Given('I am on the checkout page')")
|
|
264
|
+
expect(steps_content).to include("When('I perform an action')")
|
|
265
|
+
expect(steps_content).to include("Then('I see the expected result')")
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
it 'has pending steps' do
|
|
269
|
+
expect(steps_content).to include("pending 'implement step'")
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
shared_examples 'selenium scaffolded steps' do
|
|
274
|
+
it 'instantiates page with driver' do
|
|
275
|
+
expect(steps_content).to include('CheckoutPage.new(driver)')
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
it 'does not use capybara visit' do
|
|
279
|
+
expect(steps_content).not_to match(/^\s+visit '/)
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
shared_examples 'capybara scaffolded steps' do
|
|
284
|
+
it 'uses visit for Given step' do
|
|
285
|
+
expect(steps_content).to include("visit 'checkout'")
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
it 'does not instantiate with driver' do
|
|
289
|
+
expect(steps_content).not_to include('.new(driver)')
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
shared_examples 'watir scaffolded steps' do
|
|
294
|
+
it 'instantiates page with browser' do
|
|
295
|
+
expect(steps_content).to include('CheckoutPage.new(browser)')
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# ──────────────────────────────────────────────
|
|
300
|
+
# Shared examples: Helper content
|
|
301
|
+
# ──────────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
shared_examples 'valid scaffolded helper' do
|
|
304
|
+
it 'has frozen_string_literal' do
|
|
305
|
+
expect(helper_content).to have_frozen_string_literal
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
it 'has valid Ruby syntax' do
|
|
309
|
+
expect(helper_content).to have_valid_ruby_syntax
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
it 'defines helper module' do
|
|
313
|
+
expect(helper_content).to include('module CheckoutHelper')
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
it 'has helper placeholder comment' do
|
|
317
|
+
expect(helper_content).to include('# Add your helper code here')
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# ──────────────────────────────────────────────
|
|
322
|
+
# Shared examples: Component content
|
|
323
|
+
# ──────────────────────────────────────────────
|
|
324
|
+
|
|
325
|
+
shared_examples 'valid scaffolded component' do
|
|
326
|
+
it 'has frozen_string_literal' do
|
|
327
|
+
expect(component_content).to have_frozen_string_literal
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
it 'has valid Ruby syntax' do
|
|
331
|
+
expect(component_content).to have_valid_ruby_syntax
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
it 'inherits from Component' do
|
|
335
|
+
expect(component_content).to include('class Sidebar < Component')
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
it 'requires abstract component' do
|
|
339
|
+
expect(component_content).to include("require_relative '../abstract/component'")
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
shared_examples 'selenium scaffolded component' do
|
|
344
|
+
it 'has driver.find_element example' do
|
|
345
|
+
expect(component_content).to include('driver.find_element')
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
it 'has private section' do
|
|
349
|
+
expect(component_content).to include('private')
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
it 'has content method' do
|
|
353
|
+
expect(component_content).to include('def content')
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
shared_examples 'capybara scaffolded component' do
|
|
358
|
+
it 'has find(.selector) example' do
|
|
359
|
+
expect(component_content).to include("find('.selector')")
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
it 'has no private section' do
|
|
363
|
+
expect(component_content).not_to include('private')
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
it 'does not have driver.find_element' do
|
|
367
|
+
expect(component_content).not_to include('driver.find_element')
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
shared_examples 'watir scaffolded component' do
|
|
372
|
+
it 'has browser.element example' do
|
|
373
|
+
expect(component_content).to include('browser.element')
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
it 'has private section' do
|
|
377
|
+
expect(component_content).to include('private')
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
it 'has content method' do
|
|
381
|
+
expect(component_content).to include('def content')
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# ══════════════════════════════════════════════
|
|
386
|
+
# Test matrix: automation × framework
|
|
387
|
+
# ══════════════════════════════════════════════
|
|
388
|
+
|
|
389
|
+
WEB_AUTOMATIONS.each do |automation|
|
|
390
|
+
TEST_FRAMEWORKS.each do |framework|
|
|
391
|
+
style = scaffold_style(framework)
|
|
392
|
+
|
|
393
|
+
context "with #{framework} and #{automation}" do # rubocop:disable RSpec/ContextWording
|
|
394
|
+
let(:scaffold) { ScaffoldingCommands }
|
|
395
|
+
|
|
396
|
+
# Use absolute path so Dir.chdir is safe regardless of current CWD
|
|
397
|
+
project_dir = build_mock_project(automation, framework)
|
|
398
|
+
|
|
399
|
+
before do
|
|
400
|
+
Dir.chdir(project_dir)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
after do
|
|
404
|
+
Dir.chdir(File.dirname(project_dir))
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
after(:all) do # rubocop:disable RSpec/BeforeAfterAll
|
|
408
|
+
FileUtils.rm_rf(project_dir)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# ── Default scaffold ───────────────────
|
|
412
|
+
|
|
413
|
+
describe 'default scaffold' do
|
|
414
|
+
before do
|
|
415
|
+
scaffold.new.invoke(:scaffold, nil, %w[checkout])
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
after do
|
|
419
|
+
FileUtils.rm_f('page_objects/pages/checkout.rb')
|
|
420
|
+
FileUtils.rm_f('spec/checkout_page_spec.rb')
|
|
421
|
+
if framework == 'cucumber'
|
|
422
|
+
FileUtils.rm_f('features/checkout.feature')
|
|
423
|
+
FileUtils.rm_f('features/step_definitions/checkout_steps.rb')
|
|
424
|
+
else
|
|
425
|
+
FileUtils.rm_rf('features')
|
|
426
|
+
FileUtils.rm_rf('spec') unless framework == 'rspec'
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
it 'creates page object' do
|
|
431
|
+
expect(Pathname.new('page_objects/pages/checkout.rb')).to be_file
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
if style == :spec
|
|
435
|
+
it 'creates spec file' do
|
|
436
|
+
expect(Pathname.new('spec/checkout_page_spec.rb')).to be_file
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
it 'does not create feature file' do
|
|
440
|
+
expect(Pathname.new('features/checkout.feature')).not_to be_file
|
|
441
|
+
end
|
|
442
|
+
else # :feature
|
|
443
|
+
it 'creates feature file' do
|
|
444
|
+
expect(Pathname.new('features/checkout.feature')).to be_file
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
it 'creates steps file' do
|
|
448
|
+
expect(Pathname.new('features/step_definitions/checkout_steps.rb')).to be_file
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# Page content validation
|
|
453
|
+
let(:page_content) { File.read('page_objects/pages/checkout.rb') }
|
|
454
|
+
|
|
455
|
+
include_examples 'valid scaffolded page'
|
|
456
|
+
include_examples "#{automation} scaffolded page"
|
|
457
|
+
|
|
458
|
+
# Test content validation (depends on scaffold style)
|
|
459
|
+
if style == :spec
|
|
460
|
+
let(:spec_content) { File.read('spec/checkout_page_spec.rb') }
|
|
461
|
+
include_examples 'valid scaffolded spec'
|
|
462
|
+
include_examples "#{automation} scaffolded spec"
|
|
463
|
+
else # :feature
|
|
464
|
+
let(:feature_content) { File.read('features/checkout.feature') }
|
|
465
|
+
include_examples 'valid scaffolded feature'
|
|
466
|
+
|
|
467
|
+
let(:steps_content) { File.read('features/step_definitions/checkout_steps.rb') }
|
|
468
|
+
include_examples 'valid scaffolded steps'
|
|
469
|
+
include_examples "#{automation} scaffolded steps"
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
# ── Component scaffold ─────────────────
|
|
474
|
+
|
|
475
|
+
describe 'component scaffold' do
|
|
476
|
+
before do
|
|
477
|
+
scaffold.new.invoke(:component, nil, %w[sidebar])
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
after do
|
|
481
|
+
FileUtils.rm_f('page_objects/components/sidebar.rb')
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
let(:component_content) { File.read('page_objects/components/sidebar.rb') }
|
|
485
|
+
|
|
486
|
+
include_examples 'valid scaffolded component'
|
|
487
|
+
include_examples "#{automation} scaffolded component"
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# ── Helper scaffold ────────────────────
|
|
491
|
+
|
|
492
|
+
describe 'helper scaffold' do
|
|
493
|
+
before do
|
|
494
|
+
scaffold.new.invoke(:helper, nil, %w[checkout])
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
after do
|
|
498
|
+
FileUtils.rm_f('helpers/checkout_helper.rb')
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
let(:helper_content) { File.read('helpers/checkout_helper.rb') }
|
|
502
|
+
|
|
503
|
+
include_examples 'valid scaffolded helper'
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# ── CRUD scaffold ──────────────────────
|
|
507
|
+
# CrudGenerator checks Dir.exist?('features'), Dir.exist?('test') separately.
|
|
508
|
+
|
|
509
|
+
describe 'CRUD scaffold' do
|
|
510
|
+
before do
|
|
511
|
+
scaffold.new.invoke(:scaffold, nil, %w[product --crud])
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
after do
|
|
515
|
+
%w[product_list product_create product_detail product_edit].each do |page|
|
|
516
|
+
FileUtils.rm_f("page_objects/pages/#{page}.rb")
|
|
517
|
+
FileUtils.rm_f("spec/#{page}_page_spec.rb")
|
|
518
|
+
FileUtils.rm_f("features/#{page}.feature")
|
|
519
|
+
FileUtils.rm_f("features/step_definitions/#{page}_steps.rb")
|
|
520
|
+
end
|
|
521
|
+
FileUtils.rm_rf('features') unless framework == 'cucumber'
|
|
522
|
+
FileUtils.rm_rf('spec') unless framework == 'rspec'
|
|
523
|
+
FileUtils.rm_f('models/data/product.yml')
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
it 'creates pages for all CRUD actions' do
|
|
527
|
+
%w[product_list product_create product_detail product_edit].each do |page|
|
|
528
|
+
expect(Pathname.new("page_objects/pages/#{page}.rb")).to be_file
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
it 'creates model data file' do
|
|
533
|
+
expect(Pathname.new('models/data/product.yml')).to be_file
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
it 'model data has expected sections' do
|
|
537
|
+
content = File.read('models/data/product.yml')
|
|
538
|
+
expect(content).to include('default:')
|
|
539
|
+
expect(content).to include('valid:')
|
|
540
|
+
expect(content).to include('invalid:')
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
if framework == 'cucumber'
|
|
544
|
+
it 'creates feature files for all CRUD actions' do
|
|
545
|
+
%w[product_list product_create product_detail product_edit].each do |page|
|
|
546
|
+
expect(Pathname.new("features/#{page}.feature")).to be_file
|
|
547
|
+
end
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
it 'creates step files for all CRUD actions' do
|
|
551
|
+
%w[product_list product_create product_detail product_edit].each do |page|
|
|
552
|
+
expect(Pathname.new("features/step_definitions/#{page}_steps.rb")).to be_file
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
elsif framework == 'rspec'
|
|
556
|
+
it 'creates spec files for all CRUD actions' do
|
|
557
|
+
%w[product_list product_create product_detail product_edit].each do |page|
|
|
558
|
+
expect(Pathname.new("spec/#{page}_page_spec.rb")).to be_file
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
else # minitest — CrudGenerator checks test/ and generates specs
|
|
562
|
+
it 'creates spec files for all CRUD actions' do
|
|
563
|
+
%w[product_list product_create product_detail product_edit].each do |page|
|
|
564
|
+
expect(Pathname.new("spec/#{page}_page_spec.rb")).to be_file
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
it 'all CRUD pages have valid Ruby syntax' do
|
|
570
|
+
%w[product_list product_create product_detail product_edit].each do |page|
|
|
571
|
+
content = File.read("page_objects/pages/#{page}.rb")
|
|
572
|
+
expect(content).to have_valid_ruby_syntax
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
it 'all CRUD pages have frozen_string_literal' do
|
|
577
|
+
%w[product_list product_create product_detail product_edit].each do |page|
|
|
578
|
+
content = File.read("page_objects/pages/#{page}.rb")
|
|
579
|
+
expect(content).to have_frozen_string_literal
|
|
580
|
+
end
|
|
581
|
+
end
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
# ── Selective --with ───────────────────
|
|
585
|
+
|
|
586
|
+
describe 'selective scaffold' do
|
|
587
|
+
after do
|
|
588
|
+
%w[settings payment order].each do |name|
|
|
589
|
+
FileUtils.rm_f("page_objects/pages/#{name}.rb")
|
|
590
|
+
FileUtils.rm_f("spec/#{name}_page_spec.rb")
|
|
591
|
+
FileUtils.rm_f("helpers/#{name}_helper.rb")
|
|
592
|
+
FileUtils.rm_f("models/data/#{name}.yml")
|
|
593
|
+
end
|
|
594
|
+
FileUtils.rm_rf('features') unless framework == 'cucumber'
|
|
595
|
+
FileUtils.rm_rf('spec') unless framework == 'rspec'
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
it 'generates only page when --with page' do
|
|
599
|
+
scaffold.new.invoke(:scaffold, nil, %w[settings --with page])
|
|
600
|
+
expect(Pathname.new('page_objects/pages/settings.rb')).to be_file
|
|
601
|
+
expect(Pathname.new('spec/settings_page_spec.rb')).not_to be_file
|
|
602
|
+
expect(Pathname.new('features/settings.feature')).not_to be_file
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
it 'generates page and helper with --with page helper' do
|
|
606
|
+
scaffold.new.invoke(:scaffold, nil, %w[payment --with page helper])
|
|
607
|
+
expect(Pathname.new('page_objects/pages/payment.rb')).to be_file
|
|
608
|
+
expect(Pathname.new('helpers/payment_helper.rb')).to be_file
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
it 'generates model data with --with model' do
|
|
612
|
+
scaffold.new.invoke(:scaffold, nil, %w[order --with model])
|
|
613
|
+
expect(Pathname.new('models/data/order.yml')).to be_file
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
# ── Destroy ────────────────────────────
|
|
618
|
+
|
|
619
|
+
describe 'destroy' do
|
|
620
|
+
it 'removes scaffolded files' do
|
|
621
|
+
scaffold.new.invoke(:scaffold, nil, %w[temp_item])
|
|
622
|
+
expect(Pathname.new('page_objects/pages/temp_item.rb')).to be_file
|
|
623
|
+
|
|
624
|
+
scaffold.new.invoke(:destroy, nil, %w[temp_item])
|
|
625
|
+
expect(Pathname.new('page_objects/pages/temp_item.rb')).not_to be_file
|
|
626
|
+
|
|
627
|
+
if style == :spec
|
|
628
|
+
expect(Pathname.new('spec/temp_item_page_spec.rb')).not_to be_file
|
|
629
|
+
else
|
|
630
|
+
expect(Pathname.new('features/temp_item.feature')).not_to be_file
|
|
631
|
+
expect(Pathname.new('features/step_definitions/temp_item_steps.rb')).not_to be_file
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
# ── Batch scaffold ─────────────────────
|
|
637
|
+
|
|
638
|
+
describe 'batch scaffold' do
|
|
639
|
+
after do
|
|
640
|
+
%w[search filter].each do |name|
|
|
641
|
+
FileUtils.rm_f("page_objects/pages/#{name}.rb")
|
|
642
|
+
FileUtils.rm_f("spec/#{name}_page_spec.rb")
|
|
643
|
+
FileUtils.rm_f("features/#{name}.feature")
|
|
644
|
+
FileUtils.rm_f("features/step_definitions/#{name}_steps.rb")
|
|
645
|
+
end
|
|
646
|
+
FileUtils.rm_rf('features') unless framework == 'cucumber'
|
|
647
|
+
FileUtils.rm_rf('spec') unless framework == 'rspec'
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
it 'generates multiple scaffolds at once' do
|
|
651
|
+
scaffold.new.invoke(:scaffold, nil, %w[search filter])
|
|
652
|
+
expect(Pathname.new('page_objects/pages/search.rb')).to be_file
|
|
653
|
+
expect(Pathname.new('page_objects/pages/filter.rb')).to be_file
|
|
654
|
+
|
|
655
|
+
if style == :spec
|
|
656
|
+
expect(Pathname.new('spec/search_page_spec.rb')).to be_file
|
|
657
|
+
expect(Pathname.new('spec/filter_page_spec.rb')).to be_file
|
|
658
|
+
else
|
|
659
|
+
expect(Pathname.new('features/search.feature')).to be_file
|
|
660
|
+
expect(Pathname.new('features/filter.feature')).to be_file
|
|
661
|
+
end
|
|
662
|
+
end
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
# ── Name normalization ─────────────────
|
|
666
|
+
|
|
667
|
+
describe 'name normalization' do
|
|
668
|
+
after do
|
|
669
|
+
%w[cart shopping_cart checkout].each do |name|
|
|
670
|
+
FileUtils.rm_f("page_objects/pages/#{name}.rb")
|
|
671
|
+
end
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
it 'strips _page suffix' do
|
|
675
|
+
scaffold.new.invoke(:page, nil, %w[cart_page])
|
|
676
|
+
expect(Pathname.new('page_objects/pages/cart.rb')).to be_file
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
it 'converts CamelCase to snake_case' do
|
|
680
|
+
scaffold.new.invoke(:page, nil, %w[ShoppingCart])
|
|
681
|
+
expect(Pathname.new('page_objects/pages/shopping_cart.rb')).to be_file
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
it 'handles CamelCase with Page suffix' do
|
|
685
|
+
scaffold.new.invoke(:page, nil, %w[CheckoutPage])
|
|
686
|
+
expect(Pathname.new('page_objects/pages/checkout.rb')).to be_file
|
|
687
|
+
end
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
# ── Relationships ──────────────────────
|
|
691
|
+
|
|
692
|
+
describe 'relationships' do
|
|
693
|
+
before do
|
|
694
|
+
File.write('page_objects/pages/login.rb', "class LoginPage < Page; end\n")
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
after do
|
|
698
|
+
FileUtils.rm_f('page_objects/pages/dashboard.rb')
|
|
699
|
+
FileUtils.rm_f('spec/dashboard_page_spec.rb')
|
|
700
|
+
FileUtils.rm_f('features/dashboard.feature')
|
|
701
|
+
FileUtils.rm_f('features/step_definitions/dashboard_steps.rb')
|
|
702
|
+
FileUtils.rm_f('page_objects/pages/login.rb')
|
|
703
|
+
FileUtils.rm_rf('features') unless framework == 'cucumber'
|
|
704
|
+
FileUtils.rm_rf('spec') unless framework == 'rspec'
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
it 'adds require_relative for dependent page' do
|
|
708
|
+
scaffold.new.invoke(:page, nil, %w[dashboard --uses login])
|
|
709
|
+
content = File.read('page_objects/pages/dashboard.rb')
|
|
710
|
+
expect(content).to include("require_relative 'login'")
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
if style == :spec
|
|
714
|
+
it 'adds dependency to spec let declarations' do
|
|
715
|
+
scaffold.new.invoke(:spec, nil, %w[dashboard --uses login])
|
|
716
|
+
content = File.read('spec/dashboard_page_spec.rb')
|
|
717
|
+
expect(content).to include('LoginPage')
|
|
718
|
+
|
|
719
|
+
case automation
|
|
720
|
+
when 'selenium' then expect(content).to include('LoginPage.new(driver)')
|
|
721
|
+
when 'watir' then expect(content).to include('LoginPage.new(browser)')
|
|
722
|
+
when 'capybara' then expect(content).to include('LoginPage.new')
|
|
723
|
+
end
|
|
724
|
+
end
|
|
725
|
+
end
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
# ── Template overrides ─────────────────
|
|
729
|
+
|
|
730
|
+
describe 'template overrides' do
|
|
731
|
+
after do
|
|
732
|
+
FileUtils.rm_rf('.ruby_raider')
|
|
733
|
+
FileUtils.rm_f('page_objects/pages/custom_override.rb')
|
|
734
|
+
FileUtils.rm_f('page_objects/pages/default_fallback.rb')
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
it 'uses override template when present' do
|
|
738
|
+
FileUtils.mkdir_p('.ruby_raider/templates')
|
|
739
|
+
File.write('.ruby_raider/templates/page_object.tt', <<~ERB)
|
|
740
|
+
# frozen_string_literal: true
|
|
741
|
+
|
|
742
|
+
class <%= page_class_name %> < Page
|
|
743
|
+
# E2E_CUSTOM_MARKER
|
|
744
|
+
end
|
|
745
|
+
ERB
|
|
746
|
+
|
|
747
|
+
scaffold.new.invoke(:page, nil, %w[custom_override])
|
|
748
|
+
content = File.read('page_objects/pages/custom_override.rb')
|
|
749
|
+
expect(content).to include('E2E_CUSTOM_MARKER')
|
|
750
|
+
expect(content).to include('class CustomOverridePage < Page')
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
it 'falls back to default template without override' do
|
|
754
|
+
scaffold.new.invoke(:page, nil, %w[default_fallback])
|
|
755
|
+
content = File.read('page_objects/pages/default_fallback.rb')
|
|
756
|
+
expect(content).not_to include('E2E_CUSTOM_MARKER')
|
|
757
|
+
expect(content).to include('class DefaultFallbackPage < Page')
|
|
758
|
+
end
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
# ── Dry run ────────────────────────────
|
|
762
|
+
|
|
763
|
+
describe 'dry run' do
|
|
764
|
+
it 'does not create files with --dry-run' do
|
|
765
|
+
expect do
|
|
766
|
+
scaffold.new.invoke(:page, nil, %w[phantom --dry-run])
|
|
767
|
+
end.to output(/phantom/).to_stdout
|
|
768
|
+
|
|
769
|
+
expect(Pathname.new('page_objects/pages/phantom.rb')).not_to be_file
|
|
770
|
+
end
|
|
771
|
+
end
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
end
|