Skip to content

Commit 2bee63f

Browse files
authored
Add prerelease version support and Playwright testing (#12)
1 parent 633b890 commit 2bee63f

File tree

6 files changed

+373
-8
lines changed

6 files changed

+373
-8
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ vendor/bundle/
4545
/demos/*/coverage/
4646
/demos/*/test/reports/
4747
spec/examples.txt
48+
/test-results/
49+
/playwright-report/
50+
51+
# Generated demo config files
52+
/packages/shakacode_demo_common/config/.eslintrc.js
53+
/packages/shakacode_demo_common/config/.rubocop.yml
4854

4955
# Database
5056
/demos/*/db/*.sqlite3

bin/new-demo

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ options = {
1010
react_on_rails_version: nil,
1111
rails_args: [],
1212
react_on_rails_args: [],
13-
dry_run: false
13+
dry_run: false,
14+
shakapacker_prerelease: false,
15+
react_on_rails_prerelease: false
1416
}
1517

1618
parser = OptionParser.new do |opts|
@@ -21,6 +23,11 @@ parser = OptionParser.new do |opts|
2123
opts.separator ' Shakapacker, and React on Rails pre-configured. Uses version defaults'
2224
opts.separator ' from .new-demo-versions unless overridden.'
2325
opts.separator ''
26+
opts.separator ' Automatically installs:'
27+
opts.separator ' - Playwright E2E testing (run with: npx playwright test)'
28+
opts.separator ' - Linting and code quality tools'
29+
opts.separator ' - Git hooks via Lefthook'
30+
opts.separator ''
2431
opts.separator 'Example: bin/new-demo react_on_rails-demo-v16-typescript-setup'
2532
opts.separator ''
2633
opts.separator 'Options:'
@@ -46,15 +53,42 @@ parser = OptionParser.new do |opts|
4653
options[:react_on_rails_args] = args.split(',').map(&:strip)
4754
end
4855

56+
opts.on('--shakapacker-prerelease', 'Use latest prerelease (beta/rc) version for Shakapacker') do
57+
options[:shakapacker_prerelease] = true
58+
end
59+
60+
opts.on('--react-on-rails-prerelease', 'Use latest prerelease (beta/rc) version for React on Rails') do
61+
options[:react_on_rails_prerelease] = true
62+
end
63+
64+
opts.on('--prerelease', 'Use latest prerelease (beta/rc) versions for both gems') do
65+
options[:shakapacker_prerelease] = true
66+
options[:react_on_rails_prerelease] = true
67+
end
68+
4969
opts.on('-h', '--help', 'Show this help message') do
5070
puts opts
5171
puts ''
5272
puts 'Examples:'
73+
puts ' # Basic demo with defaults'
74+
puts ' bin/new-demo my-demo'
75+
puts ''
76+
puts ' # Use prerelease versions'
77+
puts ' bin/new-demo my-demo --prerelease'
78+
puts ' bin/new-demo my-demo --shakapacker-prerelease'
79+
puts ' bin/new-demo my-demo --react-on-rails-prerelease'
80+
puts ''
81+
puts ' # Use GitHub branches'
82+
puts ' bin/new-demo my-demo --shakapacker-version="github:shakacode/shakapacker@my-branch"'
83+
puts ' bin/new-demo my-demo --react-on-rails-version="github:shakacode/react_on_rails@fix-hmr"'
84+
puts ''
85+
puts ' # Customize Rails and React on Rails setup'
5386
puts ' bin/new-demo my-demo --rails-args="--skip-test,--api"'
5487
puts ' bin/new-demo my-demo --react-on-rails-args="--redux,--node"'
5588
puts ' bin/new-demo my-demo --rails-args="--skip-test" --react-on-rails-args="--redux"'
56-
puts ' bin/new-demo my-demo --shakapacker-version="github:shakacode/shakapacker@my-branch"'
57-
puts ' bin/new-demo my-demo --react-on-rails-version="github:shakacode/react_on_rails@fix-hmr"'
89+
puts ''
90+
puts 'After creation, run E2E tests with:'
91+
puts ' cd demos/my-demo && npx playwright test'
5892
exit
5993
end
6094
end

lib/demo_scripts/config.rb

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,41 @@ class Config
99

1010
attr_reader :shakapacker_version, :react_on_rails_version, :rails_version
1111

12-
def initialize(config_file: nil, shakapacker_version: nil, react_on_rails_version: nil, rails_version: nil)
12+
def initialize(config_file: nil, shakapacker_version: nil, react_on_rails_version: nil, rails_version: nil,
13+
shakapacker_prerelease: false, react_on_rails_prerelease: false)
1314
@config_file = config_file || File.join(Dir.pwd, '.new-demo-versions')
1415
load_config if File.exist?(@config_file)
1516

16-
@shakapacker_version = shakapacker_version || @shakapacker_version || DEFAULT_SHAKAPACKER_VERSION
17-
@react_on_rails_version = react_on_rails_version || @react_on_rails_version || DEFAULT_REACT_ON_RAILS_VERSION
17+
@shakapacker_version = resolve_version('shakapacker', shakapacker_version, shakapacker_prerelease)
18+
@react_on_rails_version = resolve_version('react_on_rails', react_on_rails_version, react_on_rails_prerelease)
1819
@rails_version = rails_version || @rails_version || DEFAULT_RAILS_VERSION
1920
end
2021

2122
private
2223

24+
def resolve_version(gem_name, custom_version, use_prerelease)
25+
return custom_version if custom_version
26+
27+
if use_prerelease
28+
prerelease = fetch_latest_prerelease(gem_name)
29+
# Prefer config file version over default if prerelease fetch fails
30+
prerelease || instance_variable_get("@#{gem_name}_version") || default_version_for(gem_name)
31+
else
32+
instance_variable_get("@#{gem_name}_version") || default_version_for(gem_name)
33+
end
34+
end
35+
36+
def default_version_for(gem_name)
37+
case gem_name
38+
when 'shakapacker'
39+
DEFAULT_SHAKAPACKER_VERSION
40+
when 'react_on_rails'
41+
DEFAULT_REACT_ON_RAILS_VERSION
42+
else
43+
raise ArgumentError, "Unknown gem: #{gem_name}"
44+
end
45+
end
46+
2347
def load_config
2448
File.readlines(@config_file, encoding: 'UTF-8').each do |line|
2549
next if line.strip.empty? || line.strip.start_with?('#')
@@ -34,5 +58,51 @@ def load_config
3458
end
3559
end
3660
end
61+
62+
def fetch_latest_prerelease(gem_name)
63+
require 'open3'
64+
65+
# Use array syntax to prevent command injection
66+
stdout, stderr, status = Open3.capture3('gem', 'search', '-ra', "^#{gem_name}$")
67+
68+
unless status.success?
69+
warn "Warning: Failed to fetch prerelease version for #{gem_name}: #{stderr}"
70+
return nil
71+
end
72+
73+
versions = parse_gem_versions(stdout)
74+
prerelease = find_latest_prerelease(versions)
75+
76+
if prerelease
77+
puts " Found prerelease version for #{gem_name}: #{prerelease}"
78+
prerelease
79+
else
80+
warn "Warning: No prerelease version found for #{gem_name}"
81+
nil
82+
end
83+
rescue StandardError => e
84+
warn "Warning: Error fetching prerelease version for #{gem_name}: #{e.message}"
85+
nil
86+
end
87+
88+
def parse_gem_versions(stdout)
89+
# Expected format: "gem_name (version1, version2, ...)"
90+
match = stdout.match(/\(([^)]+)\)/)
91+
return [] unless match
92+
93+
match[1].split(',').map(&:strip)
94+
end
95+
96+
def find_latest_prerelease(versions)
97+
# Strict semver prerelease pattern: must have major.minor.patch followed by -beta.N or -rc.N
98+
# This ensures we only match valid semver prereleases, not arbitrary strings
99+
prerelease_versions = versions.grep(/^\d+\.\d+\.\d+[.-](beta|rc)(\.\d+)?$/i)
100+
101+
return nil if prerelease_versions.empty?
102+
103+
# Sort by version to get the latest (versions are already sorted by rubygems, but be explicit)
104+
# rubygems returns versions in descending order, so first match is latest
105+
prerelease_versions.first
106+
end
37107
end
38108
end

lib/demo_scripts/demo_creator.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@ def initialize(
1717
rails_args: [],
1818
react_on_rails_args: [],
1919
dry_run: false,
20-
skip_pre_flight: false
20+
skip_pre_flight: false,
21+
shakapacker_prerelease: false,
22+
react_on_rails_prerelease: false
2123
)
2224
@demo_name = demo_name
2325
@demo_dir = File.join('demos', demo_name)
2426
@config = Config.new(
2527
shakapacker_version: shakapacker_version,
26-
react_on_rails_version: react_on_rails_version
28+
react_on_rails_version: react_on_rails_version,
29+
shakapacker_prerelease: shakapacker_prerelease,
30+
react_on_rails_prerelease: react_on_rails_prerelease
2731
)
2832
@rails_args = rails_args || []
2933
@react_on_rails_args = react_on_rails_args || []
@@ -50,6 +54,7 @@ def create!
5054
create_symlinks
5155
install_shakapacker
5256
install_react_on_rails
57+
install_demo_common_generator
5358
build_github_npm_packages if using_github_sources?
5459
create_readme
5560
cleanup_unnecessary_files
@@ -312,6 +317,12 @@ def install_react_on_rails
312317
)
313318
end
314319

320+
def install_demo_common_generator
321+
puts ''
322+
puts '📦 Installing demo common tools (Playwright, linting, git hooks)...'
323+
@runner.run!('bin/rails generate shakacode_demo_common:install', dir: @demo_dir)
324+
end
325+
315326
def create_readme
316327
puts ''
317328
puts '📝 Creating README...'

packages/shakacode_demo_common/lib/generators/shakacode_demo_common/install/install_generator.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,24 +98,66 @@ def add_to_gitignore
9898
coverage/
9999
.nyc_output/
100100
101+
# Playwright
102+
/playwright-report/
103+
/test-results/
104+
101105
# IDE
102106
.vscode/
103107
.idea/
104108
IGNORE
105109
end
106110

111+
def add_playwright_gem
112+
say 'Adding cypress-playwright-on-rails gem'
113+
gem 'cypress-playwright-on-rails', group: %i[development test]
114+
run 'bundle install'
115+
end
116+
117+
def install_playwright
118+
say 'Installing Playwright'
119+
run 'bin/rails generate cypress_playwright_on_rails:install --playwright'
120+
end
121+
122+
def create_playwright_test
123+
say 'Creating basic Playwright test for /hello_world'
124+
create_file 'spec/e2e/hello_world.spec.js', <<~JS
125+
// @ts-check
126+
const { test, expect } = require('@playwright/test');
127+
128+
test.describe('Hello World Page', () => {
129+
test('should load successfully', async ({ page }) => {
130+
await page.goto('/hello_world');
131+
await expect(page).toHaveTitle(/React on Rails/);
132+
});
133+
134+
test('should render React component', async ({ page }) => {
135+
await page.goto('/hello_world');
136+
// Wait for React to hydrate
137+
await page.waitForLoadState('networkidle');
138+
139+
// Check for common React on Rails elements
140+
const content = await page.textContent('body');
141+
expect(content).toBeTruthy();
142+
});
143+
});
144+
JS
145+
end
146+
107147
def display_post_install
108148
say "\n✅ React on Rails Demo Common installed successfully!", :green
109149
say "\nNext steps:", :yellow
110150
say " 1. Run 'bundle exec rubocop' to check Ruby code style"
111151
say " 2. Run 'npm run lint' to check JavaScript code style"
112152
say " 3. Run 'bundle exec rake demo_common:all' to run all checks"
113153
say ' 4. Commit hooks are now active via Lefthook'
154+
say " 5. Run 'npx playwright test' to run E2E tests"
114155
say "\nCustomize configurations in:", :blue
115156
say ' - .rubocop.yml'
116157
say ' - .eslintrc.js'
117158
say ' - .prettierrc.js'
118159
say ' - lefthook.yml'
160+
say ' - playwright.config.js'
119161
end
120162

121163
private

0 commit comments

Comments
 (0)