diff --git a/lib/react_on_rails/system_checker.rb b/lib/react_on_rails/system_checker.rb index cc7bdca932..7298127024 100644 --- a/lib/react_on_rails/system_checker.rb +++ b/lib/react_on_rails/system_checker.rb @@ -213,17 +213,20 @@ def check_package_version_sync # rubocop:disable Metrics/CyclomaticComplexity return unless npm_version && defined?(ReactOnRails::VERSION) - # Clean version strings for comparison (remove ^, ~, =, etc.) - clean_npm_version = npm_version.gsub(/[^0-9.]/, "") + # Normalize NPM version format to Ruby gem format for comparison + # Uses existing VersionSyntaxConverter to handle dash/dot differences + # (e.g., "16.2.0-beta.10" → "16.2.0.beta.10") + converter = ReactOnRails::VersionSyntaxConverter.new + normalized_npm_version = converter.npm_to_rubygem(npm_version) gem_version = ReactOnRails::VERSION - if clean_npm_version == gem_version + if normalized_npm_version == gem_version add_success("✅ React on Rails gem and NPM package versions match (#{gem_version})") check_version_patterns(npm_version, gem_version) else # Check for major version differences gem_major = gem_version.split(".")[0].to_i - npm_major = clean_npm_version.split(".")[0].to_i + npm_major = normalized_npm_version.split(".")[0].to_i if gem_major != npm_major # rubocop:disable Style/NegatedIfElseCondition add_error(<<~MSG.strip) diff --git a/spec/lib/react_on_rails/system_checker_spec.rb b/spec/lib/react_on_rails/system_checker_spec.rb index e8497443ec..d95ff9b4ef 100644 --- a/spec/lib/react_on_rails/system_checker_spec.rb +++ b/spec/lib/react_on_rails/system_checker_spec.rb @@ -321,6 +321,165 @@ end end + describe "#check_package_version_sync" do + before do + stub_const("ReactOnRails::VERSION", "16.2.0.beta.10") + end + + context "when package.json does not exist" do + before do + allow(File).to receive(:exist?).with("package.json").and_return(false) + end + + it "does not add any messages" do + messages_count_before = checker.messages.count + checker.send(:check_package_version_sync) + expect(checker.messages.count).to eq(messages_count_before) + end + end + + context "when package.json exists with matching beta versions" do + let(:package_json_content) do + { "dependencies" => { "react-on-rails" => "16.2.0-beta.10" } }.to_json + end + + before do + allow(File).to receive(:exist?).with("package.json").and_return(true) + allow(File).to receive(:read).with("package.json").and_return(package_json_content) + end + + it "adds a success message" do + checker.send(:check_package_version_sync) + expect(checker.messages.any? do |msg| + msg[:type] == :success && msg[:content].include?("versions match") + end).to be true + end + end + + context "when package.json has beta version with caret prefix" do + let(:package_json_content) do + { "dependencies" => { "react-on-rails" => "^16.2.0-beta.10" } }.to_json + end + + before do + allow(File).to receive(:exist?).with("package.json").and_return(true) + allow(File).to receive(:read).with("package.json").and_return(package_json_content) + end + + it "adds a success message and version pattern warning" do + checker.send(:check_package_version_sync) + expect(checker.messages.any? do |msg| + msg[:type] == :success && msg[:content].include?("versions match") + end).to be true + expect(checker.warnings?).to be true + end + end + + context "when package.json has alpha version" do + let(:package_json_content) do + { "dependencies" => { "react-on-rails" => "16.2.0-alpha.5" } }.to_json + end + + before do + stub_const("ReactOnRails::VERSION", "16.2.0.alpha.5") + allow(File).to receive(:exist?).with("package.json").and_return(true) + allow(File).to receive(:read).with("package.json").and_return(package_json_content) + end + + it "correctly matches alpha versions" do + checker.send(:check_package_version_sync) + expect(checker.messages.any? do |msg| + msg[:type] == :success && msg[:content].include?("versions match") + end).to be true + end + end + + context "when package.json has rc version" do + let(:package_json_content) do + { "dependencies" => { "react-on-rails" => "16.2.0-rc.1" } }.to_json + end + + before do + stub_const("ReactOnRails::VERSION", "16.2.0.rc.1") + allow(File).to receive(:exist?).with("package.json").and_return(true) + allow(File).to receive(:read).with("package.json").and_return(package_json_content) + end + + it "correctly matches rc versions" do + checker.send(:check_package_version_sync) + expect(checker.messages.any? do |msg| + msg[:type] == :success && msg[:content].include?("versions match") + end).to be true + end + end + + context "when package.json has stable version" do + let(:package_json_content) do + { "dependencies" => { "react-on-rails" => "16.2.0" } }.to_json + end + + before do + stub_const("ReactOnRails::VERSION", "16.2.0") + allow(File).to receive(:exist?).with("package.json").and_return(true) + allow(File).to receive(:read).with("package.json").and_return(package_json_content) + end + + it "correctly matches stable versions" do + checker.send(:check_package_version_sync) + expect(checker.messages.any? do |msg| + msg[:type] == :success && msg[:content].include?("versions match") + end).to be true + end + end + + context "when versions have minor mismatch" do + let(:package_json_content) do + { "dependencies" => { "react-on-rails" => "16.2.0-beta.9" } }.to_json + end + + before do + allow(File).to receive(:exist?).with("package.json").and_return(true) + allow(File).to receive(:read).with("package.json").and_return(package_json_content) + end + + it "adds a warning message" do + checker.send(:check_package_version_sync) + expect(checker.warnings?).to be true + expect(checker.messages.last[:content]).to include("Version mismatch detected") + end + end + + context "when versions have major mismatch" do + let(:package_json_content) do + { "dependencies" => { "react-on-rails" => "15.0.0" } }.to_json + end + + before do + allow(File).to receive(:exist?).with("package.json").and_return(true) + allow(File).to receive(:read).with("package.json").and_return(package_json_content) + end + + it "adds an error message" do + checker.send(:check_package_version_sync) + expect(checker.errors?).to be true + expect(checker.messages.last[:content]).to include("Major version mismatch") + end + end + + context "when package.json has invalid JSON" do + before do + allow(File).to receive(:exist?).with("package.json").and_return(true) + allow(File).to receive(:read).with("package.json").and_return("invalid json") + end + + it "handles parsing errors gracefully" do + expect do + checker.send(:check_package_version_sync) + end.not_to raise_error + end + end + end + describe "private methods" do describe "#cli_exists?" do it "returns true when command exists" do