diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 953d99c6d..3600d1861 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -19,6 +19,7 @@ jobs: RAILS_ENV: test COVERAGE: true PARALLEL_TEST_PROCESSORS: ${{ matrix.ci_node_total }} + LIGHTPANDA: true services: postgres: image: postgres:17 @@ -41,20 +42,23 @@ jobs: # .ruby-version provides the Ruby version implicitly. bundler-cache: true - - name: Cache Playwright browsers + - name: Cache Lightpanda binary uses: actions/cache@v5 - id: playwright-cache + id: lightpanda-cache with: - path: ~/.cache/ms-playwright - key: playwright-${{ runner.os }}-1.59.0 + path: ~/.cache/lightpanda + key: lightpanda-${{ runner.os }}-nightly - - name: Install Playwright browsers (cache miss) - if: steps.playwright-cache.outputs.cache-hit != 'true' - run: npx --yes playwright@1.59.0 install --with-deps chromium + - name: Download Lightpanda binary (cache miss) + if: steps.lightpanda-cache.outputs.cache-hit != 'true' + run: | + mkdir -p ~/.cache/lightpanda + curl -L -o ~/.cache/lightpanda/lightpanda https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-x86_64-linux + chmod +x ~/.cache/lightpanda/lightpanda - - name: Install Playwright system deps (cache hit) - if: steps.playwright-cache.outputs.cache-hit == 'true' - run: npx --yes playwright@1.59.0 install-deps chromium + - name: Verify Lightpanda + run: | + ~/.cache/lightpanda/lightpanda version - name: Setup test databases env: @@ -67,7 +71,7 @@ jobs: DATABASE_URL: postgres://postgres:postgres@localhost:5432 CI_NODE_INDEX: ${{ matrix.ci_node_index }} RAILS_ENV: test - PLAYWRIGHT_HEADLESS: true + LIGHTPANDA_PATH: ~/.cache/lightpanda/lightpanda run: | bundle exec parallel_rspec spec/ \ -n ${{ matrix.ci_node_total }} \ diff --git a/Gemfile b/Gemfile index 2c117a7f8..146f0a321 100644 --- a/Gemfile +++ b/Gemfile @@ -111,6 +111,7 @@ group :test do # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] gem 'capybara' gem 'capybara-playwright-driver' + gem 'capybara-lightpanda', '~> 0.2.0' gem 'database_cleaner' gem 'shoulda-matchers', '~> 7.0' gem 'simplecov', require: false @@ -127,3 +128,5 @@ gem 'scout_apm' gem 'carrierwave-aws', '~> 1.6' gem 'sitemap_generator', '~> 7.0' + + diff --git a/Gemfile.lock b/Gemfile.lock index 79fd7db41..8eedc9c57 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -128,6 +128,10 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) + capybara-lightpanda (0.2.0) + capybara (>= 3.0, < 5) + concurrent-ruby (~> 1.3) + websocket-driver (~> 0.8) capybara-playwright-driver (0.5.9) addressable capybara @@ -618,6 +622,7 @@ DEPENDENCIES bootstrap (~> 5) bullet capybara + capybara-lightpanda (~> 0.2.0) capybara-playwright-driver carrierwave carrierwave-aws (~> 1.6) diff --git a/spec/support/lightpanda.rb b/spec/support/lightpanda.rb new file mode 100644 index 000000000..c77cb423b --- /dev/null +++ b/spec/support/lightpanda.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Capybara driver for Lightpanda browser using capybara-lightpanda gem +# https://github.com/navidemad/capybara-lightpanda +# +# Usage: +# Run tests with LIGHTPANDA=true: LIGHTPANDA=true bundle exec rspec spec/features/some_test.rb + +if ENV['LIGHTPANDA'] == 'true' + require 'capybara-lightpanda' + + Capybara.javascript_driver = :lightpanda + puts "Using Lightpanda browser (capybara-lightpanda) for JavaScript tests" +end diff --git a/spec/support/select_from_tom_select.rb b/spec/support/select_from_tom_select.rb index bc5c28053..d5dd7b3e9 100644 --- a/spec/support/select_from_tom_select.rb +++ b/spec/support/select_from_tom_select.rb @@ -8,10 +8,13 @@ module SelectFromTomSelect # @param from [String, Symbol] The field ID (for documentation purposes) def select_from_tom_select(item_text, from: nil) # Ensure TomSelect is initialized (workaround for pages where it doesn't auto-init) - ensure_tom_select_initialized('meeting_organisers') + # Use the 'from' parameter as the element ID, fallback to 'meeting_organisers' for backwards compatibility + element_id = from || 'meeting_organisers' + ensure_tom_select_initialized(element_id) # Wait for TomSelect to initialize - give extra time for page to fully load JS - expect(page).to have_css('.ts-wrapper', wait: 10) + # Lightpanda may need longer for JavaScript execution + expect(page).to have_css('.ts-wrapper', wait: 15) # Open dropdown and type search query find('.ts-control').click @@ -42,7 +45,8 @@ def remove_from_tom_select(item_text) ensure_tom_select_initialized('meeting_organisers') # Wait for TomSelect to initialize and items to be present - expect(page).to have_css('.ts-wrapper', wait: 10) + # Lightpanda may need longer for JavaScript execution + expect(page).to have_css('.ts-wrapper', wait: 15) expect(page).to have_css('.ts-wrapper .item', text: item_text, wait: 5) within '.ts-wrapper' do @@ -53,8 +57,9 @@ def remove_from_tom_select(item_text) private def ensure_tom_select_initialized(element_id) - # Check if TomSelect is already initialized - return if page.has_css?('.ts-wrapper', wait: 2) + # Check if TomSelect is already initialized with longer wait for Lightpanda + wait_time = ENV['LIGHTPANDA'] == 'true' ? 5 : 2 + return if page.has_css?('.ts-wrapper', wait: wait_time) # Try to initialize TomSelect manually if the element exists # Note: This is a minimal initialization for tests where TomSelect doesn't auto-init @@ -83,8 +88,9 @@ def ensure_tom_select_initialized(element_id) JS page.execute_script(script) - # Wait for initialization - expect(page).to have_css('.ts-wrapper', wait: 5) + # Wait for initialization with longer timeout for Lightpanda + init_wait = ENV['LIGHTPANDA'] == 'true' ? 10 : 5 + expect(page).to have_css('.ts-wrapper', wait: init_wait) end end