forked from fixin.me/fixin.me
Compare commits
1 Commits
feature/ex
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 65edb866d0 |
@@ -231,12 +231,17 @@ textarea:invalid {
|
|||||||
text-decoration: underline 1px var(--color-border-gray);
|
text-decoration: underline 1px var(--color-border-gray);
|
||||||
text-underline-offset: 0.25em;
|
text-underline-offset: 0.25em;
|
||||||
}
|
}
|
||||||
[name=cancel],
|
|
||||||
.auxiliary {
|
.auxiliary {
|
||||||
border-color: var(--color-border-gray);
|
border-color: var(--color-nav-gray);
|
||||||
color: var(--color-nav-gray);
|
color: var(--color-nav-gray);
|
||||||
fill: var(--color-nav-gray);
|
fill: var(--color-nav-gray);
|
||||||
}
|
}
|
||||||
|
table .button {
|
||||||
|
border-color: var(--color-border-gray);
|
||||||
|
font-weight: normal;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0.4em;
|
||||||
|
}
|
||||||
.button:focus-visible,
|
.button:focus-visible,
|
||||||
.tab:focus-visible,
|
.tab:focus-visible,
|
||||||
.tab:hover {
|
.tab:hover {
|
||||||
@@ -259,13 +264,6 @@ textarea:invalid {
|
|||||||
color: var(--color-blue);
|
color: var(--color-blue);
|
||||||
text-decoration-color: var(--color-blue);
|
text-decoration-color: var(--color-blue);
|
||||||
}
|
}
|
||||||
table .button {
|
|
||||||
border-color: var(--color-border-gray);
|
|
||||||
color: var(--color-table-gray);
|
|
||||||
font-weight: normal;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0.4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* NOTE: collapse gaps around empty rows (`topside`) once possible with
|
/* NOTE: collapse gaps around empty rows (`topside`) once possible with
|
||||||
@@ -360,20 +358,20 @@ header {
|
|||||||
line-height: 2.2em;
|
line-height: 2.2em;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
.flash:before {
|
.flash::before {
|
||||||
filter: invert();
|
filter: invert(1);
|
||||||
height: 1.4em;
|
height: 1.4em;
|
||||||
margin: 0 0.5em;
|
margin: 0 0.5em;
|
||||||
width: 1.4em;
|
width: 1.4em;
|
||||||
}
|
}
|
||||||
.flash.alert:before {
|
.flash.alert::before {
|
||||||
content: url('pictograms/alert-outline.svg');
|
content: url('pictograms/alert-outline.svg');
|
||||||
}
|
}
|
||||||
.flash.alert {
|
.flash.alert {
|
||||||
border-color: var(--color-red);
|
border-color: var(--color-red);
|
||||||
background-color: var(--color-red);
|
background-color: var(--color-red);
|
||||||
}
|
}
|
||||||
.flash.notice:before {
|
.flash.notice::before {
|
||||||
content: url('pictograms/check-circle-outline.svg');
|
content: url('pictograms/check-circle-outline.svg');
|
||||||
}
|
}
|
||||||
.flash.notice {
|
.flash.notice {
|
||||||
@@ -437,15 +435,18 @@ header {
|
|||||||
|
|
||||||
.tabular-form table {
|
.tabular-form table {
|
||||||
border: none;
|
border: none;
|
||||||
border-spacing: 0.4em 0;
|
border-spacing: 0;
|
||||||
margin-inline: -0.4em;
|
|
||||||
}
|
}
|
||||||
.tabular-form table td {
|
.tabular-form table td {
|
||||||
border: none;
|
border: none;
|
||||||
|
padding-inline-start: 0.4em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.tabular-form table td {
|
.tabular-form table td:first-child {
|
||||||
padding-inline: 0;
|
padding-inline-start: 0;
|
||||||
|
}
|
||||||
|
.tabular-form table td:last-child {
|
||||||
|
padding-inline-end: 0;
|
||||||
}
|
}
|
||||||
.tabular-form table :is(form, input, select, textarea):only-child {
|
.tabular-form table :is(form, input, select, textarea):only-child {
|
||||||
margin-inline-start: 0;
|
margin-inline-start: 0;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<div class="flex reverse">
|
<div class="flex reverse">
|
||||||
<%= form.button id: :create_measurement_button, disabled: true -%>
|
<%= form.button id: :create_measurement_button, disabled: true -%>
|
||||||
<%= image_link_to t(:cancel), "close-outline", measurements_path, name: :cancel,
|
<%= image_link_to t(:cancel), "close-outline", measurements_path, name: :cancel,
|
||||||
class: 'dangerous', onclick: render_turbo_stream('form_close') %>
|
class: 'auxiliary dangerous', onclick: render_turbo_stream('form_close') %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ Bundler.require(*Rails.groups)
|
|||||||
|
|
||||||
module FixinMe
|
module FixinMe
|
||||||
class Application < Rails::Application
|
class Application < Rails::Application
|
||||||
# Allow RAILS_DATABASE_YML to override the database config file path.
|
|
||||||
# Used by the multi-database test runner (lib/tasks/test_databases.rake).
|
|
||||||
config.paths['config/database'] = [ENV['RAILS_DATABASE_YML']] if ENV['RAILS_DATABASE_YML']
|
|
||||||
|
|
||||||
# Initialize configuration defaults for originally generated Rails version.
|
# Initialize configuration defaults for originally generated Rails version.
|
||||||
config.load_defaults 7.0
|
config.load_defaults 7.0
|
||||||
|
|
||||||
|
|||||||
@@ -48,24 +48,3 @@ production:
|
|||||||
#test:
|
#test:
|
||||||
# <<: *default
|
# <<: *default
|
||||||
# database: fixinme_test
|
# database: fixinme_test
|
||||||
|
|
||||||
# Multi-database testing
|
|
||||||
# ----------------------
|
|
||||||
# Any key starting with "test" is treated as a test database.
|
|
||||||
# When more than one is present, EVERY test task (rails test, rails test:models,
|
|
||||||
# rails test:system, …) automatically runs against all of them.
|
|
||||||
#
|
|
||||||
# The adapter gem must be available:
|
|
||||||
# bundle config --local with "mysql sqlite" # mysql + sqlite
|
|
||||||
# bundle config --local with "mysql pg" # mysql + postgresql
|
|
||||||
#
|
|
||||||
#test_sqlite:
|
|
||||||
# adapter: sqlite3
|
|
||||||
# database: db/fixinme_test.sqlite3
|
|
||||||
#
|
|
||||||
#test_pg:
|
|
||||||
# adapter: postgresql
|
|
||||||
# database: fixinme_test
|
|
||||||
# username: fixinme
|
|
||||||
# password: Some-password1%
|
|
||||||
# host: localhost
|
|
||||||
|
|||||||
@@ -1,158 +0,0 @@
|
|||||||
require 'yaml'
|
|
||||||
require 'erb'
|
|
||||||
require 'tmpdir'
|
|
||||||
|
|
||||||
# Multi-database test runner
|
|
||||||
# ==========================
|
|
||||||
# When database.yml contains more than one `test*` configuration, every
|
|
||||||
# standard test task (test, test:models, test:system, …) is automatically
|
|
||||||
# rewritten to run the full suite against EACH configured database in turn.
|
|
||||||
#
|
|
||||||
# Convention — any top-level key that starts with "test" and holds a Hash:
|
|
||||||
#
|
|
||||||
# test: ← required primary database
|
|
||||||
# adapter: mysql2
|
|
||||||
# database: fixinme_test
|
|
||||||
# ...
|
|
||||||
#
|
|
||||||
# test_sqlite: ← optional additional databases
|
|
||||||
# adapter: sqlite3
|
|
||||||
# database: db/fixinme_test.sqlite3
|
|
||||||
#
|
|
||||||
# test_pg:
|
|
||||||
# adapter: postgresql
|
|
||||||
# ...
|
|
||||||
#
|
|
||||||
# A single-database setup is unchanged: every task behaves exactly as before.
|
|
||||||
#
|
|
||||||
# The mechanism uses RAILS_DATABASE_YML — an env var read by
|
|
||||||
# config/application.rb(.dist) to override Rails' database config path before
|
|
||||||
# initialisation, giving each subprocess a clean, isolated database config.
|
|
||||||
|
|
||||||
module MultiDbTests
|
|
||||||
ADAPTER_GEMS = {
|
|
||||||
'mysql2' => 'mysql2',
|
|
||||||
'sqlite3' => 'sqlite3',
|
|
||||||
'postgresql' => 'pg',
|
|
||||||
'pg' => 'pg',
|
|
||||||
}.freeze
|
|
||||||
|
|
||||||
ADAPTER_BUNDLE_GROUPS = {
|
|
||||||
'mysql2' => 'mysql',
|
|
||||||
'sqlite3' => 'sqlite',
|
|
||||||
'postgresql' => 'postgresql',
|
|
||||||
'pg' => 'postgresql',
|
|
||||||
}.freeze
|
|
||||||
|
|
||||||
# Rake task names generated by railties/lib/rails/test_unit/testing.rake
|
|
||||||
# that use run_from_rake — these are the ones we rewrite.
|
|
||||||
WRAPPED_TASKS = (
|
|
||||||
['test'] +
|
|
||||||
Rails::TestUnit::Runner::TEST_FOLDERS.map { |f| "test:#{f}" } +
|
|
||||||
%w[test:all test:system test:generators test:units test:functionals]
|
|
||||||
).freeze
|
|
||||||
|
|
||||||
class << self
|
|
||||||
# Returns {name => config_hash} for every key starting with "test".
|
|
||||||
def test_configs
|
|
||||||
@test_configs ||= begin
|
|
||||||
db_file = Rails.root.join('config', 'database.yml')
|
|
||||||
all = YAML.safe_load(ERB.new(db_file.read).result, aliases: true) || {}
|
|
||||||
all.select { |k, v| k.to_s.start_with?('test') && v.is_a?(Hash) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def non_test_configs
|
|
||||||
@non_test_configs ||= begin
|
|
||||||
db_file = Rails.root.join('config', 'database.yml')
|
|
||||||
all = YAML.safe_load(ERB.new(db_file.read).result, aliases: true) || {}
|
|
||||||
all.reject { |k, _| k.to_s.start_with?('test') }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Run rails +task_name+ for every configured test database.
|
|
||||||
# Called from the rewritten rake task actions.
|
|
||||||
def run(task_name)
|
|
||||||
cfgs = test_configs
|
|
||||||
results = {}
|
|
||||||
|
|
||||||
cfgs.each do |db_name, config|
|
|
||||||
adapter = config['adapter'].to_s
|
|
||||||
puts "\n#{'─' * 64}"
|
|
||||||
puts " #{task_name} · #{db_name} (#{adapter})"
|
|
||||||
puts '─' * 64
|
|
||||||
|
|
||||||
unless adapter_available?(adapter)
|
|
||||||
warn " SKIPPED — '#{adapter}' gem not in bundle.\n" \
|
|
||||||
" Run: bundle config --local with \"#{current_with} #{adapter_group(adapter)}\""
|
|
||||||
results[db_name] = :skipped
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
Dir.mktmpdir('rails_test_') do |tmpdir|
|
|
||||||
tmp_yml = File.join(tmpdir, 'database.yml')
|
|
||||||
File.write(tmp_yml, non_test_configs.merge('test' => config).to_yaml)
|
|
||||||
env = { 'RAILS_DATABASE_YML' => tmp_yml }
|
|
||||||
|
|
||||||
if system(env, 'bundle exec rails db:test:prepare')
|
|
||||||
results[db_name] = system(env, 'rails', task_name) ? :pass : :fail
|
|
||||||
else
|
|
||||||
results[db_name] = :prepare_failed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
print_summary(cfgs, results)
|
|
||||||
|
|
||||||
failed = results.count { |_, s| [:fail, :prepare_failed].include?(s) }
|
|
||||||
exit(false) if failed > 0
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def adapter_available?(adapter)
|
|
||||||
require ADAPTER_GEMS.fetch(adapter, adapter)
|
|
||||||
true
|
|
||||||
rescue LoadError
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def adapter_group(adapter)
|
|
||||||
ADAPTER_BUNDLE_GROUPS.fetch(adapter, adapter)
|
|
||||||
end
|
|
||||||
|
|
||||||
def current_with
|
|
||||||
(Bundler.settings[:with] || '').split(':').join(' ')
|
|
||||||
end
|
|
||||||
|
|
||||||
def print_summary(cfgs, results)
|
|
||||||
return if results.size <= 1 # no summary for single-DB runs
|
|
||||||
|
|
||||||
puts "\n#{'═' * 64}"
|
|
||||||
puts ' SUMMARY'
|
|
||||||
puts '═' * 64
|
|
||||||
results.each do |db_name, status|
|
|
||||||
adapter = cfgs.dig(db_name, 'adapter') || '?'
|
|
||||||
icon = status == :pass ? '✓' : (status == :skipped ? '–' : '✗')
|
|
||||||
label = { pass: 'PASS', fail: 'FAIL',
|
|
||||||
prepare_failed: 'PREPARE FAILED', skipped: 'SKIPPED' }[status]
|
|
||||||
puts " #{icon} #{db_name.ljust(26)} (#{adapter.ljust(12)}) #{label}"
|
|
||||||
end
|
|
||||||
puts '═' * 64
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Rewrite every standard test task to run against all configured databases.
|
|
||||||
# This file loads after railties/testing.rake, so all tasks already exist.
|
|
||||||
# Single-database setups are completely unaffected.
|
|
||||||
if MultiDbTests.test_configs.size > 1
|
|
||||||
MultiDbTests::WRAPPED_TASKS.each do |task_name|
|
|
||||||
next unless Rake::Task.task_defined?(task_name)
|
|
||||||
|
|
||||||
Rake::Task[task_name].clear_actions
|
|
||||||
Rake::Task[task_name].enhance do
|
|
||||||
MultiDbTests.run(task_name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Reference in New Issue
Block a user