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
49 changes: 49 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ jobs:
exclude:
- rails: '8.0.0'
activeadmin: '3.2.0'
include:
- ruby: '3.4'
rails: '7.2.0'
activeadmin: '4.0.0.beta22'
- ruby: '3.4'
rails: '8.0.0'
activeadmin: '4.0.0.beta22'
env:
RAILS: ${{ matrix.rails }}
AA: ${{ matrix.activeadmin }}
Expand All @@ -31,6 +38,11 @@ jobs:
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: AA v4 environment summary
if: matrix.activeadmin == '4.0.0.beta22'
run: |
bundle exec gem list activeadmin
bundle info activeadmin
- name: Run tests
run: bundle exec rspec spec
test-mysql:
Expand Down Expand Up @@ -98,6 +110,43 @@ jobs:
bundler-cache: true
- name: Run tests
run: bundle exec rspec spec
test-postgres-aa4:
name: Ruby 3.4 / Rails 8.0.0 / AA 4.0.0.beta22 / PostgreSQL 16
runs-on: ubuntu-latest
env:
RAILS: '8.0.0'
AA: '4.0.0.beta22'
DB: postgres
DB_HOST: 127.0.0.1
DB_PORT: 5432
DB_USERNAME: postgres
DB_PASSWORD: postgres
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: active_admin_import_test
ports:
- 5432:5432
options: >-
--health-cmd="pg_isready -U postgres"
--health-interval=10s
--health-timeout=5s
--health-retries=10
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.4'
bundler-cache: true
- name: AA v4 environment summary
run: |
bundle exec gem list activeadmin
bundle info activeadmin
- name: Run tests
run: bundle exec rspec spec
coverage:
name: Coverage
runs-on: ubuntu-latest
Expand Down
20 changes: 15 additions & 5 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,22 @@ gemspec
default_rails_version = '7.1.0'
default_activeadmin_version = '3.2.0'

# `~> 4.0.0.beta22` would admit 4.0.0 GA — pin prereleases exactly so the
# CI cell tests the AA build it claims to test.
aa_version = ENV['AA'] || default_activeadmin_version
aa_op = aa_version.match?(/[a-z]/) ? '=' : '~>'

gem 'rails', "~> #{ENV['RAILS'] || default_rails_version}"
gem 'activeadmin', "~> #{ENV['AA'] || default_activeadmin_version}"
gem 'sprockets-rails'
gem 'sass-rails'
gem 'activeadmin', "#{aa_op} #{aa_version}"

if ENV['AA']&.start_with?('4')
# AA 4 uses Tailwind + importmap; sass-rails conflicts with Tailwind v4.
gem 'cssbundling-rails'
gem 'importmap-rails'
else
gem 'sprockets-rails'
gem 'sass-rails'
end

group :test do
gem 'simplecov', require: false
Expand All @@ -22,6 +34,4 @@ group :test do
end
gem 'database_cleaner'
gem 'capybara'
gem 'cuprite'
gem 'webrick', require: false
end
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Tool | Description
:error_limit |Limit the number of errors reported (default `5`, set to `nil` for all)
:headers_rewrites |hash with key (csv header) - value (db column name) rows mapping
:if |Controls whether the 'Import' button is displayed. It supports a proc to be evaluated into a boolean value within the activeadmin render context.
:action_item_html_options |HTML options passed to the index-page "Import …" action_item link. Defaults to `{ class: 'action-item-button' }` so the link matches AA 4's built-in action_items; the class is a no-op on AA 3. Override to drop the class or add your own (`{ class: 'my-btn' }`, `{ class: '', data: { turbo: false } }`, etc.).



Expand Down
2 changes: 1 addition & 1 deletion active_admin_import.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ Gem::Specification.new do |gem|
gem.add_runtime_dependency 'activerecord-import', '>= 2.0'
gem.add_runtime_dependency 'rchardet', '>= 1.6'
gem.add_runtime_dependency 'rubyzip', '>= 1.2'
gem.add_dependency 'activeadmin', '>= 3.0', '< 4.0'
gem.add_dependency 'activeadmin', '>= 3.0', '< 4.1'
end
7 changes: 5 additions & 2 deletions lib/active_admin_import/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ module ActiveAdminImport
module DSL
CONTEXT_METHOD = :active_admin_import_context

ACTIVE_ADMIN_V4 = Gem::Version.new(ActiveAdmin::VERSION) >= Gem::Version.new('4.0.0.beta1')

def self.prepare_import_model(template_object, controller, params: nil)
model = template_object.is_a?(Proc) ? template_object.call : template_object
if params
Expand All @@ -45,7 +47,7 @@ def self.prepare_import_model(template_object, controller, params: nil)
model_name = options[:resource_label].downcase
plural_model_name = options[:plural_resource_label].downcase
if result.empty?
flash[:warning] = I18n.t('active_admin_import.file_empty_error')
flash[ACTIVE_ADMIN_V4 ? :alert : :warning] = I18n.t('active_admin_import.file_empty_error')
else
if result.failed?
flash[:error] = I18n.t(
Expand Down Expand Up @@ -81,7 +83,8 @@ def active_admin_import(options = {}, &block)
if authorized?(ActiveAdminImport::Auth::IMPORT, active_admin_config.resource_class)
link_to(
I18n.t('active_admin_import.import_model', plural_model: options[:plural_resource_label]),
action: :import
{ action: :import },
options[:action_item_html_options]
)
end
end
Expand Down
8 changes: 6 additions & 2 deletions lib/active_admin_import/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ module Options
:plural_resource_label,
:error_limit,
:headers_rewrites,
:if
:if,
:action_item_html_options
].freeze

def self.options_for(config, options = {})
Expand All @@ -39,7 +40,10 @@ def self.options_for(config, options = {})
plural_resource_label: config.plural_resource_label,
error_limit: 5,
headers_rewrites: {},
if: true
if: true,
# AA 4's built-in action_items hardcode this class for Tailwind styling
# (lib/active_admin/resource/action_items.rb). It's a no-op on AA 3.
action_item_html_options: { class: 'action-item-button' }
}.deep_merge(options)
end
end
Expand Down
20 changes: 10 additions & 10 deletions spec/import_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ def with_zipped_csv(name, &block)
end

def upload_file!(name, ext = 'csv')
attach_file('active_admin_import_model_file', File.expand_path("./spec/fixtures/files/#{name}.#{ext}"))
find_button('Import').click
attach_file(ImportFormSelectors.file_input_id, File.expand_path("./spec/fixtures/files/#{name}.#{ext}"))
find_button(ImportFormSelectors.import_button_text).click
end

context 'posts index' do
Expand Down Expand Up @@ -118,7 +118,7 @@ def upload_file!(name, ext = 'csv')
# reload page
visit '/admin/posts/import'
# submit form without file
find_button('Import').click
find_button(ImportFormSelectors.import_button_text).click
end

it 'should render validation error' do
Expand Down Expand Up @@ -171,7 +171,7 @@ def upload_file!(name, ext = 'csv')
# TODO: removing this causes undefined method `ransack' for #<ActiveRecord::Relation []>
allow_any_instance_of(Admin::AuthorsController).to receive(:find_collection).and_return(Author.all)
visit '/admin/authors'
find_link('Import Authors').click
find_link(ImportFormSelectors.import_link_text).click
expect(current_path).to eq('/admin/authors/import')
end
end
Expand Down Expand Up @@ -228,14 +228,14 @@ def upload_file!(name, ext = 'csv')
end

it 'has valid form' do
form = find('#new_active_admin_import_model')
form = find(ImportFormSelectors.form_css)
expect(form['action']).to eq('/admin/authors/do_import')
expect(form['enctype']).to eq('multipart/form-data')
file_input = form.find('input#active_admin_import_model_file')
file_input = form.find(ImportFormSelectors.file_input_css)
expect(file_input[:type]).to eq('file')
expect(file_input.value).to be_blank
submit_input = form.find('#active_admin_import_model_submit_action input')
expect(submit_input[:value]).to eq('Import')
submit_input = form.find(ImportFormSelectors.submit_css)
expect(submit_input[:value]).to eq(ImportFormSelectors.import_button_text)
expect(submit_input[:type]).to eq('submit')
end

Expand All @@ -261,7 +261,7 @@ def upload_file!(name, ext = 'csv')

context 'when no file' do
it 'should render error' do
find_button('Import').click
find_button(ImportFormSelectors.import_button_text).click
expect(Author.count).to eq(0)
expect(page).to have_content I18n.t('active_admin_import.no_file_error')
end
Expand Down Expand Up @@ -605,7 +605,7 @@ def upload_file!(name, ext = 'csv')

# Second submission without selecting a file
expect do
find_button('Import').click
find_button(ImportFormSelectors.import_button_text).click
expect(page).to have_content(I18n.t('active_admin_import.no_file_error'))
end.not_to change { Author.count }
end
Expand Down
15 changes: 6 additions & 9 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@

ENV['RAILS_ENV'] = 'test'
require 'rails'
require 'test_app_paths'
ENV['RAILS'] = Rails.version
ENV['DB'] ||= 'sqlite'
ENV['RAILS_ROOT'] = File.expand_path("../rails/rails-#{ENV['RAILS']}-#{ENV['DB']}", __FILE__)
ENV['RAILS_ROOT'] = TestAppPaths.app_root
system 'rake setup' unless File.exist?(ENV['RAILS_ROOT'])

require 'active_model'
Expand All @@ -28,16 +28,13 @@

require 'rspec/rails'
require 'support/admin'
require 'support/import_form_selectors'
require 'capybara/rails'
require 'capybara/rspec'
require 'capybara/cuprite'

Capybara.server = :webrick
Capybara.register_driver :cuprite do |app|
Capybara::Cuprite::Driver.new(app, headless: true, window_size: [1280, 800])
end
Capybara.javascript_driver = :cuprite
Capybara.default_max_wait_time = 5
# Specs exercise ActiveAdmin through Capybara's default rack_test driver — no
# JavaScript or real browser is needed, so no Cuprite/Chrome or app server.
Capybara.default_driver = :rack_test

RSpec.configure do |config|
config.use_transactional_fixtures = false
Expand Down
24 changes: 24 additions & 0 deletions spec/support/import_form_selectors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

# Formtastic 6 (AA 4) keeps the same DOM IDs as Formtastic 4 (AA 3) for this
# form, so one selector set serves both — branch here if a future AA shifts an ID.
module ImportFormSelectors
module_function

SELECTORS = {
form_id: 'new_active_admin_import_model',
file_input_id: 'active_admin_import_model_file',
file_input_css: 'input#active_admin_import_model_file',
submit_css: '#active_admin_import_model_submit_action input',
import_button_text: 'Import',
import_link_text: 'Import Authors'
}.freeze

def form_id = SELECTORS[:form_id]
def form_css = "##{form_id}"
def file_input_id = SELECTORS[:file_input_id]
def file_input_css = SELECTORS[:file_input_css]
def submit_css = SELECTORS[:submit_css]
def import_button_text = SELECTORS[:import_button_text]
def import_link_text = SELECTORS[:import_link_text]
end
13 changes: 12 additions & 1 deletion spec/support/rails_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,19 @@

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))

aa_v4 = ENV['AA']&.start_with?('4')

generate :'active_admin:install --skip-users'
generate :'formtastic:install'

if aa_v4
# `active_admin:assets` swaps AA 3's Sprockets SCSS/JS for AA 4's Tailwind CSS
# stub. We don't compile it — specs assert on DOM and flash text, not styling,
# so the stub suffices and no Node is needed. `builds/` satisfies cssbundling-rails.
generate :'active_admin:assets'
run 'mkdir -p app/assets/builds'
else
generate :'formtastic:install'
end

run 'rm -rf test'
route "root :to => 'admin/dashboard#index'"
Expand Down
14 changes: 14 additions & 0 deletions spec/support/test_app_paths.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

module TestAppPaths
module_function

def app_dir_name
"rails-#{Rails::VERSION::STRING}-#{ENV['DB'] || 'sqlite'}-aa#{ENV['AA'] || 'default'}"
end

# Absolute path under spec/rails/, used as RAILS_ROOT.
def app_root
File.expand_path("../rails/#{app_dir_name}", __dir__)
end
end
11 changes: 10 additions & 1 deletion tasks/test.rake
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require_relative '../spec/support/test_app_paths'

desc "Creates a test rails app for the specs to run against"
task :setup do
require 'rails/version'
Expand All @@ -8,6 +10,9 @@ task :setup do
when 'postgres', 'postgresql' then 'postgresql'
else 'sqlite3'
end
aa_v4 = ENV['AA']&.start_with?('4')

puts "[setup] ActiveAdmin: #{ENV['AA'] || '(Gemfile default)'} / Rails: #{Rails::VERSION::STRING} / DB: #{rails_db}"

rails_new_opts = %W(
--skip-turbolinks
Expand All @@ -17,5 +22,9 @@ task :setup do
-m
spec/support/rails_template.rb
)
system "bundle exec rails new spec/rails/rails-#{Rails::VERSION::STRING}-#{db} #{rails_new_opts.join(' ')}"
# v4 drops sprockets-rails (see Gemfile), so skip the asset pipeline to
# avoid the auto-generated `config/initializers/assets.rb` crashing at boot.
rails_new_opts.unshift('--skip-asset-pipeline') if aa_v4

system "bundle exec rails new spec/rails/#{TestAppPaths.app_dir_name} #{rails_new_opts.join(' ')}"
end
Loading