ruby_raider 3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d6b5fccbc2b3752d7f63391226d903a61a1368a404831ef10b1d0d230774fa5
4
- data.tar.gz: 7d4be1564e22cb7bf47e0d08ced4ae00868140bbd0c65833e733a904e634ecf4
3
+ metadata.gz: 4ef04718aea3c6036141557edd000ddc0ecb4df784bbfa6434ddf7a275edb73f
4
+ data.tar.gz: 1ba77ec6eeb15fe8520819dce31dde3b4a8763b83d901ddf32a4b7ca47411cdc
5
5
  SHA512:
6
- metadata.gz: d9c67f3a57b7bfe7c3426ea0a5220af9b2623d9ce7003c1ebf2052bb19e23125a2f88ee277e89349dece675d13e07833818a93b25dbdbaf2a712dc8661fd0fa3
7
- data.tar.gz: 840873be251968d311b86e15586219fe402f433a7cf81f5eccd46e1b8227dfa52c95274099818d4eabf764b015e28cce116bcaac2334ba7e840be6a379a4d6d0
6
+ metadata.gz: a78ba73874f84e22f137f7c996a44fbe5703b80527422966bb31ecb83e1dfc0e21442edcfdcc1bf015bcc7129a4683a711d10c4c1c78da6f362ac7faf70b1230
7
+ data.tar.gz: eaac9b04da1d97f865ba8ebee4643bb9a594ffbaf884aa905b204ed2e1759d5189a560d3e1165721af4fa60de4c603a1e7be31572e5ebbc53eeace5d60b4a7d4
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'thor'
4
4
  require 'fileutils'
5
+ require 'pathname'
5
6
  require_relative '../generators/menu_generator'
6
7
  require_relative '../scaffolding/scaffolding'
7
8
  require_relative '../scaffolding/name_normalizer'
@@ -165,7 +166,27 @@ class ScaffoldingCommands < Thor
165
166
 
166
167
  no_commands do
167
168
  def load_config_path(type)
168
- YAML.load_file('config/config.yml')["#{type}_path"] if Pathname.new('config/config.yml').exist?
169
+ ScaffoldProjectDetector.config_path(type)
170
+ end
171
+
172
+ def validate_project!
173
+ warnings = ScaffoldProjectDetector.validate_project
174
+ warnings.each { |w| say "Warning: #{w}", :yellow }
175
+ show_detection_defaults
176
+ end
177
+
178
+ def show_detection_defaults
179
+ gemfile = ScaffoldProjectDetector.read_gemfile
180
+ if gemfile.empty?
181
+ say 'Warning: No Gemfile found, defaulting to selenium + rspec templates.', :yellow
182
+ else
183
+ automation = ScaffoldProjectDetector.detect_automation(gemfile)
184
+ framework = ScaffoldProjectDetector.detect_framework(gemfile)
185
+ if automation.nil?
186
+ say 'Warning: Could not detect automation library from Gemfile, defaulting to selenium.', :yellow
187
+ end
188
+ say 'Warning: Could not detect test framework from Gemfile, defaulting to rspec.', :yellow if framework.nil?
189
+ end
169
190
  end
170
191
 
171
192
  def delete_scaffolding(name, type)
@@ -186,6 +207,7 @@ class ScaffoldingCommands < Thor
186
207
  end
187
208
 
188
209
  def generate_default_scaffold(name)
210
+ validate_project!
189
211
  uses = options[:uses]
190
212
  if Pathname.new('spec').exist? && !Pathname.new('features').exist?
191
213
  generate_scaffolding(name, 'spec', load_config_path('spec'), uses:)
@@ -197,6 +219,7 @@ class ScaffoldingCommands < Thor
197
219
  end
198
220
 
199
221
  def generate_selected_components(name, components)
222
+ validate_project!
200
223
  uses = options[:uses]
201
224
  components.each do |comp|
202
225
  comp = comp.downcase.strip
@@ -211,6 +234,7 @@ class ScaffoldingCommands < Thor
211
234
 
212
235
  def generate_crud(name)
213
236
  require_relative '../scaffolding/crud_generator'
237
+ validate_project!
214
238
  if options[:dry_run]
215
239
  crud = CrudGenerator.new(name, Scaffolding, method(:load_config_path))
216
240
  DryRunPresenter.preview(crud.planned_files)
@@ -266,6 +290,7 @@ class ScaffoldingCommands < Thor
266
290
 
267
291
  def interactive_scaffold
268
292
  require_relative '../scaffolding/scaffold_menu'
293
+ validate_project!
269
294
  result = ScaffoldMenu.new.run
270
295
  return unless result
271
296
 
@@ -35,7 +35,7 @@ class UtilityCommands < Thor
35
35
  browser_options(selected_options) if selected_options || options[:delete]
36
36
  end
37
37
 
38
- desc 'browser_options [OPTIONS]', 'Sets the browser options for the project'
38
+ desc 'browser_options [OPTIONS]', 'Sets the browser arguments for the current browser (e.g. no-sandbox, headless)'
39
39
  option :delete,
40
40
  type: :boolean, required: false, desc: 'This will delete your browser options', aliases: '-d'
41
41
 
@@ -44,6 +44,15 @@ class UtilityCommands < Thor
44
44
  Utilities.delete_browser_options if options[:delete]
45
45
  end
46
46
 
47
+ desc 'headless [on/off]', 'Toggles headless mode for browser tests'
48
+
49
+ def headless(toggle)
50
+ enabled = %w[on true 1 yes].include?(toggle.downcase)
51
+ Utilities.headless = enabled
52
+ state = enabled ? 'enabled' : 'disabled'
53
+ say "Headless mode #{state}", :green
54
+ end
55
+
47
56
  desc 'raid', 'It runs all the tests in a project'
48
57
  option :parallel,
49
58
  type: :boolean, required: false, desc: 'It runs the tests in parallel', aliases: '-p'
@@ -20,7 +20,7 @@ module BrowserHelper
20
20
  Watir.default_timeout = config.fetch('timeout', 10)
21
21
  browser_name = config['browser'].to_s
22
22
  args = args.empty? ? config['browser_arguments'][browser_name] : args
23
- args += ['--headless'] if ENV['HEADLESS'] && !args.include?('--headless')
23
+ args += ['--headless'] if (ENV['HEADLESS'] || config['headless']) && !args.include?('--headless')
24
24
  capitalized = browser_name == 'ie' ? browser_name.upcase : browser_name.capitalize
25
25
  browser_options = "Selenium::WebDriver::#{capitalized}::Options".constantize.new(args:)
26
26
  debug_cfg = config['debug'] || {}
@@ -20,7 +20,7 @@ module CapybaraHelper
20
20
  Capybara.register_driver :selenium do |app|
21
21
  browser = config['browser'].to_sym
22
22
  args = config['browser_arguments'][config['browser']] || []
23
- args += ['--headless'] if ENV['HEADLESS'] && !args.include?('--headless')
23
+ args += ['--headless'] if (ENV['HEADLESS'] || config['headless']) && !args.include?('--headless')
24
24
  options = Selenium::WebDriver::Chrome::Options.new(args:)
25
25
  debug_cfg = config['debug'] || {}
26
26
  if debug_cfg.fetch('enabled', false) || ENV['DEBUG']&.downcase == 'true'
@@ -8,7 +8,7 @@
8
8
 
9
9
  def browser_arguments(*opts)
10
10
  args = opts.empty? ? @config['browser_arguments'][@config['browser']] : opts
11
- args += ['--headless'] if ENV['HEADLESS'] && !args.include?('--headless')
11
+ args += ['--headless'] if (ENV['HEADLESS'] || @config['headless']) && !args.include?('--headless')
12
12
  args
13
13
  end
14
14
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'yaml'
4
+
3
5
  module ScaffoldProjectDetector
4
6
  AUTOMATION_GEMS = {
5
7
  'capybara' => 'capybara',
@@ -17,6 +19,10 @@ module ScaffoldProjectDetector
17
19
  'minitest' => 'minitest'
18
20
  }.freeze
19
21
 
22
+ CONFIG_PATH = 'config/config.yml'
23
+
24
+ class Error < StandardError; end
25
+
20
26
  module_function
21
27
 
22
28
  def detect
@@ -62,11 +68,29 @@ module ScaffoldProjectDetector
62
68
  detect_automation == 'watir'
63
69
  end
64
70
 
71
+ # Validates that the current directory looks like a Ruby Raider project.
72
+ # Returns an array of warning messages (empty = valid project).
73
+ def validate_project
74
+ warnings = []
75
+ warnings << 'Gemfile not found. Are you in a Ruby Raider project directory?' unless File.exist?('Gemfile')
76
+ unless File.exist?(CONFIG_PATH)
77
+ warnings << "#{CONFIG_PATH} not found. Scaffolded pages won't include URL navigation methods."
78
+ end
79
+ warnings
80
+ end
81
+
82
+ # Loads and parses config/config.yml. Returns a hash.
83
+ # Raises ScaffoldProjectDetector::Error on malformed YAML.
65
84
  def config
66
- return {} unless File.exist?('config/config.yml')
85
+ return {} unless File.exist?(CONFIG_PATH)
86
+
87
+ YAML.safe_load(File.read(CONFIG_PATH), permitted_classes: [Symbol]) || {}
88
+ rescue Psych::SyntaxError => e
89
+ raise Error, "#{CONFIG_PATH} has invalid YAML syntax: #{e.message}"
90
+ end
67
91
 
68
- YAML.safe_load(File.read('config/config.yml'), permitted_classes: [Symbol]) || {}
69
- rescue StandardError
70
- {}
92
+ # Returns the custom path for a scaffold type from config, or nil.
93
+ def config_path(type)
94
+ config["#{type}_path"]
71
95
  end
72
96
  end
@@ -44,14 +44,22 @@ module Utilities
44
44
  end
45
45
 
46
46
  def browser_options=(opts)
47
- set('browser_options', Array(opts).flatten)
47
+ browser_name = config['browser'] || 'chrome'
48
+ config['browser_arguments'] ||= {}
49
+ config['browser_arguments'][browser_name] = Array(opts).flatten
50
+ overwrite_yaml
48
51
  end
49
52
 
50
53
  def delete_browser_options
51
- config.delete('browser_options')
54
+ browser_name = config['browser'] || 'chrome'
55
+ config['browser_arguments']&.delete(browser_name)
52
56
  overwrite_yaml
53
57
  end
54
58
 
59
+ def headless=(enabled)
60
+ set('headless', enabled)
61
+ end
62
+
55
63
  def llm_provider=(provider)
56
64
  set('llm_provider', provider)
57
65
  end
data/lib/version CHANGED
@@ -1 +1 @@
1
- 3.0.0
1
+ 3.0.1
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+
5
+ RSpec.describe 'ScaffoldingCommands' do
6
+ describe 'dependencies' do
7
+ it 'loads without relying on transitive requires for Pathname' do
8
+ # Loads the file in a clean Ruby process where nothing else has required 'pathname'.
9
+ # This catches the exact bug where Pathname is used but never explicitly required,
10
+ # which works in test suites (rspec loads pathname transitively) but crashes at runtime.
11
+ script = <<~RUBY
12
+ require_relative '../../lib/commands/scaffolding_commands'
13
+ puts 'OK'
14
+ RUBY
15
+
16
+ stdout, stderr, status = Open3.capture3('ruby', '-e', script, chdir: __dir__)
17
+ expect(status.success?).to be(true),
18
+ "ScaffoldingCommands failed to load in isolation:\n#{stderr}"
19
+ expect(stdout.strip).to eq('OK')
20
+ end
21
+ end
22
+ end
@@ -43,10 +43,10 @@ describe UtilityCommands do
43
43
  expect(config['browser']).to eql ':firefox'
44
44
  end
45
45
 
46
- it 'updates the browser options' do
46
+ it 'updates the browser arguments for the current browser' do
47
47
  utility.new.invoke(:browser, nil, %w[:firefox --opts headless start-maximized start-fullscreen])
48
48
  config = YAML.load_file('config/config.yml')
49
- expect(config['browser_options']).to eql %w[headless start-maximized start-fullscreen]
49
+ expect(config['browser_arguments'][':firefox']).to eql %w[headless start-maximized start-fullscreen]
50
50
  end
51
51
  end
52
52
 
@@ -91,23 +91,23 @@ describe UtilityCommands do
91
91
  expect(config['feature_path']).to eql path
92
92
  end
93
93
 
94
- it 'updates only the browser options' do
94
+ it 'updates the browser arguments for the current browser' do
95
95
  utility.new.invoke(:browser, nil, %w[:firefox --opts headless])
96
96
  config = YAML.load_file('config/config.yml')
97
- expect(config['browser_options']).to eql %w[headless]
97
+ expect(config['browser_arguments'][':firefox']).to eql %w[headless]
98
98
  end
99
99
 
100
- it 'deletes the browser options when passed with the delete parameter' do
100
+ it 'deletes the browser arguments when passed with the delete parameter' do
101
101
  utility.new.invoke(:browser, nil, %w[:firefox --opts headless --delete])
102
102
  config = YAML.load_file('config/config.yml')
103
- expect(config['browser_options']).to be_nil
103
+ expect(config.dig('browser_arguments', ':firefox')).to be_nil
104
104
  end
105
105
 
106
- it 'deletes the browser options' do
106
+ it 'deletes the browser arguments' do
107
107
  utility.new.invoke(:browser, nil, %w[:firefox --opts headless])
108
108
  utility.new.invoke(:browser, nil, %w[--delete])
109
109
  config = YAML.load_file('config/config.yml')
110
- expect(config['browser_options']).to be_nil
110
+ expect(config.dig('browser_arguments', ':firefox')).to be_nil
111
111
  end
112
112
  end
113
113
  end
@@ -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
@@ -87,6 +87,44 @@ RSpec.describe ScaffoldProjectDetector do
87
87
  end
88
88
  end
89
89
 
90
+ describe '.validate_project' do
91
+ it 'warns when Gemfile is missing' do
92
+ warnings = described_class.validate_project
93
+ expect(warnings).to include(match(/Gemfile not found/))
94
+ end
95
+
96
+ it 'warns when config/config.yml is missing' do
97
+ File.write('Gemfile', "gem 'rspec'\n")
98
+ warnings = described_class.validate_project
99
+ expect(warnings).to include(match(/config\.yml not found/))
100
+ end
101
+
102
+ it 'returns no warnings for valid project' do
103
+ File.write('Gemfile', "gem 'rspec'\n")
104
+ FileUtils.mkdir_p('config')
105
+ File.write('config/config.yml', "browser: chrome\n")
106
+ expect(described_class.validate_project).to be_empty
107
+ end
108
+ end
109
+
110
+ describe '.config' do
111
+ it 'returns empty hash when config missing' do
112
+ expect(described_class.config).to eq({})
113
+ end
114
+
115
+ it 'parses valid config' do
116
+ FileUtils.mkdir_p('config')
117
+ File.write('config/config.yml', "browser: chrome\nurl: http://localhost\n")
118
+ expect(described_class.config).to eq({ 'browser' => 'chrome', 'url' => 'http://localhost' })
119
+ end
120
+
121
+ it 'raises Error on malformed YAML' do
122
+ FileUtils.mkdir_p('config')
123
+ File.write('config/config.yml', "browser: chrome\n bad:\nindent")
124
+ expect { described_class.config }.to raise_error(ScaffoldProjectDetector::Error, /invalid YAML syntax/)
125
+ end
126
+ end
127
+
90
128
  describe '.detect' do
91
129
  before do
92
130
  File.write('Gemfile', "gem 'selenium-webdriver'\ngem 'rspec'\n")
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'yaml'
5
+ require 'erb'
6
+
7
+ RSpec.describe 'Headless config support in driver templates' do
8
+ let(:tmp_dir) { 'tmp_headless_test' }
9
+
10
+ before do
11
+ FileUtils.mkdir_p(tmp_dir)
12
+ Dir.chdir(tmp_dir)
13
+ end
14
+
15
+ after do
16
+ Dir.chdir('..')
17
+ FileUtils.rm_rf(tmp_dir)
18
+ end
19
+
20
+ describe 'selenium driver template' do
21
+ it 'checks config headless key' do
22
+ content = File.read(File.expand_path('../../lib/generators/templates/helpers/partials/selenium_driver.tt',
23
+ __dir__))
24
+ expect(content).to include("@config['headless']")
25
+ end
26
+
27
+ it 'checks ENV HEADLESS' do
28
+ content = File.read(File.expand_path('../../lib/generators/templates/helpers/partials/selenium_driver.tt',
29
+ __dir__))
30
+ expect(content).to include("ENV['HEADLESS']")
31
+ end
32
+ end
33
+
34
+ describe 'capybara helper template' do
35
+ it 'checks config headless key' do
36
+ content = File.read(File.expand_path('../../lib/generators/templates/helpers/capybara_helper.tt', __dir__))
37
+ expect(content).to include("config['headless']")
38
+ end
39
+ end
40
+
41
+ describe 'browser helper template (watir)' do
42
+ it 'checks config headless key' do
43
+ content = File.read(File.expand_path('../../lib/generators/templates/helpers/browser_helper.tt', __dir__))
44
+ expect(content).to include("config['headless']")
45
+ end
46
+ end
47
+
48
+ describe 'headless via config.yml' do
49
+ it 'config key is used when YAML has headless: true' do
50
+ FileUtils.mkdir_p('config')
51
+ File.write('config/config.yml', <<~YAML)
52
+ browser: chrome
53
+ headless: true
54
+ browser_arguments:
55
+ chrome:
56
+ - no-sandbox
57
+ YAML
58
+
59
+ config = YAML.load_file('config/config.yml')
60
+ args = config['browser_arguments'][config['browser']]
61
+
62
+ # Simulate the template logic
63
+ args += ['--headless'] if (ENV['HEADLESS'] || config['headless']) && !args.include?('--headless')
64
+
65
+ expect(args).to include('--headless')
66
+ end
67
+
68
+ it 'config key is not used when headless: false' do
69
+ FileUtils.mkdir_p('config')
70
+ File.write('config/config.yml', <<~YAML)
71
+ browser: chrome
72
+ headless: false
73
+ browser_arguments:
74
+ chrome:
75
+ - no-sandbox
76
+ YAML
77
+
78
+ config = YAML.load_file('config/config.yml')
79
+ args = config['browser_arguments'][config['browser']]
80
+
81
+ # Temporarily clear ENV to isolate config behavior
82
+ original_env = ENV.delete('HEADLESS')
83
+ args += ['--headless'] if (ENV['HEADLESS'] || config['headless']) && !args.include?('--headless')
84
+ ENV['HEADLESS'] = original_env if original_env
85
+
86
+ expect(args).not_to include('--headless')
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'yaml'
5
+ require_relative '../../lib/utilities/utilities'
6
+
7
+ RSpec.describe Utilities do
8
+ let(:tmp_dir) { 'tmp_utilities_test' }
9
+ let(:config_path) { 'config/config.yml' }
10
+
11
+ before do
12
+ FileUtils.mkdir_p(tmp_dir)
13
+ Dir.chdir(tmp_dir)
14
+ FileUtils.mkdir_p('config')
15
+ File.write(config_path, <<~YAML)
16
+ browser: chrome
17
+ url: 'http://localhost:3000'
18
+ timeout: 10
19
+ viewport:
20
+ width: 1920
21
+ height: 1080
22
+ browser_arguments:
23
+ chrome:
24
+ - no-sandbox
25
+ - disable-dev-shm-usage
26
+ firefox:
27
+ - acceptInsecureCerts
28
+ YAML
29
+ # Reset cached config between tests
30
+ described_class.instance_variable_set(:@config, nil)
31
+ end
32
+
33
+ after do
34
+ Dir.chdir('..')
35
+ FileUtils.rm_rf(tmp_dir)
36
+ end
37
+
38
+ describe '.browser_options=' do
39
+ it 'writes to browser_arguments for the current browser' do
40
+ described_class.browser_options = %w[no-sandbox headless]
41
+ config = YAML.load_file(config_path)
42
+ expect(config['browser_arguments']['chrome']).to eq(%w[no-sandbox headless])
43
+ end
44
+
45
+ it 'writes to the correct browser key when browser is firefox' do
46
+ described_class.browser = 'firefox'
47
+ described_class.instance_variable_set(:@config, nil)
48
+ described_class.browser_options = %w[headless]
49
+ config = YAML.load_file(config_path)
50
+ expect(config['browser_arguments']['firefox']).to eq(%w[headless])
51
+ end
52
+
53
+ it 'preserves other browser arguments' do
54
+ described_class.browser_options = %w[headless]
55
+ config = YAML.load_file(config_path)
56
+ expect(config['browser_arguments']['firefox']).to eq(%w[acceptInsecureCerts])
57
+ end
58
+ end
59
+
60
+ describe '.delete_browser_options' do
61
+ it 'removes browser_arguments for the current browser' do
62
+ described_class.delete_browser_options
63
+ config = YAML.load_file(config_path)
64
+ expect(config['browser_arguments']['chrome']).to be_nil
65
+ end
66
+
67
+ it 'preserves other browser arguments' do
68
+ described_class.delete_browser_options
69
+ config = YAML.load_file(config_path)
70
+ expect(config['browser_arguments']['firefox']).to eq(%w[acceptInsecureCerts])
71
+ end
72
+ end
73
+
74
+ describe '.headless=' do
75
+ it 'sets headless to true' do
76
+ described_class.headless = true
77
+ config = YAML.load_file(config_path)
78
+ expect(config['headless']).to be true
79
+ end
80
+
81
+ it 'sets headless to false' do
82
+ described_class.headless = true
83
+ described_class.instance_variable_set(:@config, nil)
84
+ described_class.headless = false
85
+ config = YAML.load_file(config_path)
86
+ expect(config['headless']).to be false
87
+ end
88
+ end
89
+
90
+ describe '.viewport=' do
91
+ it 'sets viewport dimensions' do
92
+ described_class.viewport = '375x812'
93
+ config = YAML.load_file(config_path)
94
+ expect(config['viewport']).to eq({ 'width' => 375, 'height' => 812 })
95
+ end
96
+ end
97
+
98
+ describe '.browser_options= writes to browser_arguments not browser_options' do
99
+ it 'does not create a browser_options key' do
100
+ described_class.browser_options = %w[headless]
101
+ config = YAML.load_file(config_path)
102
+ expect(config).not_to have_key('browser_options')
103
+ end
104
+ end
105
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_raider
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Agustin Pequeno
@@ -375,6 +375,7 @@ files:
375
375
  - spec/adopter/project_analyzer_spec.rb
376
376
  - spec/adopter/project_detector_spec.rb
377
377
  - spec/commands/raider_commands_spec.rb
378
+ - spec/commands/scaffolding_commands_spec.rb
378
379
  - spec/generators/fixtures/templates/test.tt
379
380
  - spec/generators/fixtures/templates/test_partial.tt
380
381
  - spec/generators/generator_spec.rb
@@ -409,6 +410,7 @@ files:
409
410
  - spec/integration/generators/rspec_generator_spec.rb
410
411
  - spec/integration/generators/skip_flags_spec.rb
411
412
  - spec/integration/generators/visual_addon_spec.rb
413
+ - spec/integration/scaffolding_e2e_spec.rb
412
414
  - spec/integration/settings_helper.rb
413
415
  - spec/integration/spec_helper.rb
414
416
  - spec/llm/client_spec.rb
@@ -429,6 +431,8 @@ files:
429
431
  - spec/system/support/system_test_helper.rb
430
432
  - spec/system/watir_spec.rb
431
433
  - spec/utilities/desktop_downloader_spec.rb
434
+ - spec/utilities/headless_config_spec.rb
435
+ - spec/utilities/utilities_spec.rb
432
436
  homepage: https://github.com/RubyRaider/ruby_raider
433
437
  licenses:
434
438
  - MIT