Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions .github/workflows/test_solidus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ on:
jobs:
RSpec:
name: Rails ${{ matrix.rails }}, Ruby ${{ matrix.ruby }}, ${{ matrix.database }}, ${{ matrix.storage }}
runs-on: ubuntu-22.04
# NOTE: ubuntu-24.04 is required for libvips 8.13+. ActiveStorage 8.1 refuses to
# load with older libvips, because it cannot disable the unfuzzed operations that
# are unsafe on untrusted content. ubuntu-22.04 only ships libvips 8.12.1.
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -83,11 +86,21 @@ jobs:
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- uses: awalsh128/cache-apt-pkgs-action@v1
# NOTE: Install libvips with plain apt-get rather than a caching action. A
# cached install can report a cache hit while restoring zero packages, which
# leaves libvips missing and surfaces much later as an opaque
# "undefined method 'new' for nil" from ImageProcessing::Vips inside the
# specs. `vips --version` makes a bad install fail here instead.
#
# ruby-vips binds libvips.so through FFI at runtime, so the docs and the GUI
# tools apt recommends are dead weight; libvips-tools is requested explicitly
# because it provides the `vips` binary used by the check below.
- name: Install libvips
if: ${{ matrix.storage == 'activestorage' }}
name: Install libvips
with:
packages: libvips-dev
run: |
sudo apt-get update
sudo apt-get install -yq --no-install-recommends libvips-dev libvips-tools
vips --version
- name: Setup coverage
id: setup-coverage
if: ${{ inputs.coverage && matrix.rails == '8.0' && matrix.ruby == '3.4' }}
Expand Down
1 change: 1 addition & 0 deletions admin/spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
Capybara.disable_animation = true
Capybara.default_max_wait_time = ENV["DEFAULT_MAX_WAIT_TIME"].to_f if ENV["DEFAULT_MAX_WAIT_TIME"].present?
Capybara.enable_aria_label = true
Capybara.server = :puma, {Silent: true} # A workaround for https://github.com/rspec/rspec-rails/issues/1897

# DATABASE CLEANER
require "database_cleaner"
Expand Down
10 changes: 9 additions & 1 deletion core/lib/spree/testing_support/capybara_ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ module Spree
module TestingSupport
module CapybaraExt
def click_icon(type)
find(".fa-#{type}").click
el = find(".fa-#{type}", visible: :all)
begin
el.click
rescue Selenium::WebDriver::Error::ElementClickInterceptedError
# When a floating element (eg. tooltips/overlays) intercepts the click,
# scroll the target into view and dispatch a click via JS to keep tests stable.
page.execute_script('arguments[0].scrollIntoView({block: "center"});', el.native)
page.execute_script("arguments[0].click();", el.native)
end
end

def eventually_fill_in(field, options = {})
Expand Down
17 changes: 16 additions & 1 deletion core/lib/spree/testing_support/dummy_app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
ENV["RAILS_ENV"] = "test"
ENV["DISABLE_DATABASE_ENVIRONMENT_CHECK"] = "1"

# Speed up the sqlite runs: `fast_sqlite` patches SQLite3::Database#initialize to
# set `PRAGMA synchronous = OFF` and `journal_mode = MEMORY`, trading durability we
# don't need for a database the suite recreates anyway.
#
# NOTE: The gem is declared `require: false`, and only when DB is sqlite, so it is
# genuinely absent for the mysql and postgres runs.
begin
require "fast_sqlite"
rescue LoadError
# Not running against sqlite, nothing to speed up.
end

require "rails"
require "active_record/railtie"
require "action_controller/railtie"
Expand Down Expand Up @@ -106,7 +118,10 @@ class Application < ::Rails::Application

# Set the preview path within the dummy app:
if ActionMailer::Base.respond_to? :preview_paths # Rails 7.1+
config.action_mailer.preview_paths << File.expand_path("dummy_app/mailer_previews", __dir__)
# Some Rails versions return a frozen array here; assign a new array
# to avoid FrozenError when augmenting the paths.
existing = Array(config.action_mailer.preview_paths)
config.action_mailer.preview_paths = existing + [File.expand_path("dummy_app/mailer_previews", __dir__)]
else
config.action_mailer.preview_path = File.expand_path("dummy_app/mailer_previews", __dir__)
end
Expand Down
76 changes: 76 additions & 0 deletions core/spec/lib/spree/testing_support/capybara_ext_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# frozen_string_literal: true

require "rails_helper"
require "selenium-webdriver"
require "spree/testing_support/capybara_ext"

RSpec.describe Spree::TestingSupport::CapybaraExt do
subject(:page_object) do
Class.new do
include Spree::TestingSupport::CapybaraExt

attr_reader :page

def initialize(element:, page:)
@element = element
@page = page
end

def find(*) = @element
end.new(element: element, page: page)
end

let(:element) { instance_double(Capybara::Node::Element, native: :native_element) }
let(:page) { instance_double(Capybara::Session, execute_script: nil) }

describe "#click_icon" do
it "clicks the icon it finds" do
allow(element).to receive(:click)

page_object.click_icon(:edit)

expect(element).to have_received(:click)
end

context "when a floating element intercepts the click" do
before do
allow(element).to receive(:click)
.and_raise(Selenium::WebDriver::Error::ElementClickInterceptedError)
end

it "scrolls the element into view and clicks it via JavaScript" do
page_object.click_icon(:edit)

expect(page).to have_received(:execute_script)
.with('arguments[0].scrollIntoView({block: "center"});', :native_element).ordered
expect(page).to have_received(:execute_script)
.with("arguments[0].click();", :native_element).ordered
end
end

context "when finding the icon is itself intercepted" do
subject(:page_object) do
Class.new do
include Spree::TestingSupport::CapybaraExt

attr_reader :page

def initialize(page:)
@page = page
end

def find(*)
raise Selenium::WebDriver::Error::ElementClickInterceptedError
end
end.new(page: page)
end

# The fallback needs an element to scroll to, so a failure to find one has to
# surface as itself rather than as a NoMethodError on nil.
it "lets the error through instead of retrying without an element" do
expect { page_object.click_icon(:edit) }
.to raise_error(Selenium::WebDriver::Error::ElementClickInterceptedError)
end
end
end
end
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: '3.7'
version: "3.7"

services:
mysql:
Expand All @@ -18,12 +18,12 @@ services:
- postgres:/var/lib/postgresql/data:cached

app:
shm_size: '512mb'
shm_size: "512mb"
build:
context: .dockerdev
dockerfile: Dockerfile
args:
RUBY_VERSION: "3.1"
RUBY_VERSION: "3.4.6"
PG_VERSION: 13
NODE_VERSION: 20
MYSQL_VERSION: "8.0"
Expand Down
Loading