forked from fixin.me/fixin.me
Compare commits
3 Commits
feature/ex
...
extend-all
| Author | SHA1 | Date | |
|---|---|---|---|
| dd9572689a | |||
| 376640cfd7 | |||
| 65edb866d0 |
@@ -231,12 +231,17 @@ textarea:invalid {
|
||||
text-decoration: underline 1px var(--color-border-gray);
|
||||
text-underline-offset: 0.25em;
|
||||
}
|
||||
[name=cancel],
|
||||
.auxiliary {
|
||||
border-color: var(--color-border-gray);
|
||||
border-color: var(--color-nav-gray);
|
||||
color: 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,
|
||||
.tab:focus-visible,
|
||||
.tab:hover {
|
||||
@@ -259,13 +264,6 @@ textarea:invalid {
|
||||
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
|
||||
@@ -360,20 +358,20 @@ header {
|
||||
line-height: 2.2em;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.flash:before {
|
||||
filter: invert();
|
||||
.flash::before {
|
||||
filter: invert(1);
|
||||
height: 1.4em;
|
||||
margin: 0 0.5em;
|
||||
width: 1.4em;
|
||||
}
|
||||
.flash.alert:before {
|
||||
.flash.alert::before {
|
||||
content: url('pictograms/alert-outline.svg');
|
||||
}
|
||||
.flash.alert {
|
||||
border-color: var(--color-red);
|
||||
background-color: var(--color-red);
|
||||
}
|
||||
.flash.notice:before {
|
||||
.flash.notice::before {
|
||||
content: url('pictograms/check-circle-outline.svg');
|
||||
}
|
||||
.flash.notice {
|
||||
@@ -437,15 +435,18 @@ header {
|
||||
|
||||
.tabular-form table {
|
||||
border: none;
|
||||
border-spacing: 0.4em 0;
|
||||
margin-inline: -0.4em;
|
||||
border-spacing: 0;
|
||||
}
|
||||
.tabular-form table td {
|
||||
border: none;
|
||||
padding-inline-start: 0.4em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.tabular-form table td {
|
||||
padding-inline: 0;
|
||||
.tabular-form table td:first-child {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
.tabular-form table td:last-child {
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
.tabular-form table :is(form, input, select, textarea):only-child {
|
||||
margin-inline-start: 0;
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<div class="flex reverse">
|
||||
<%= form.button id: :create_measurement_button, disabled: true -%>
|
||||
<%= 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>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env ruby
|
||||
APP_PATH = File.expand_path("../config/application", __dir__)
|
||||
require_relative "../config/boot"
|
||||
require_relative "../lib/core_ext/test_multiple_adapters.rb"
|
||||
require "rails/commands"
|
||||
|
||||
@@ -42,30 +42,24 @@ production:
|
||||
# <<: *default
|
||||
# database: fixinme_dev
|
||||
|
||||
# Warning: The database defined as "test" will be erased and
|
||||
# re-generated from your development database when you run "rake".
|
||||
# Do not set this db to the same as development or production.
|
||||
# Warning: Test databases will be erased and re-generated from your development
|
||||
# database when you run "rake". Do not set these dbs to the same as development
|
||||
# or production.
|
||||
#
|
||||
# Multiple test databases can be provided. When more than one test db is
|
||||
# present, every test task (test, test:models, test:system, etc.) automatically
|
||||
# runs against all of them.
|
||||
#
|
||||
# In additional test databases only "adapter", "database", "host", "port" and
|
||||
# "socket" settings are used. All settings default to values specified in
|
||||
# "test" - which is required in all test setups.
|
||||
#test:
|
||||
# <<: *default
|
||||
# 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:
|
||||
#test_sqlite3:
|
||||
# adapter: sqlite3
|
||||
# database: db/fixinme_test.sqlite3
|
||||
#
|
||||
#test_pg:
|
||||
#test_postgresql:
|
||||
# adapter: postgresql
|
||||
# database: fixinme_test
|
||||
# username: fixinme
|
||||
# password: Some-password1%
|
||||
# host: localhost
|
||||
|
||||
42
lib/core_ext/test_multiple_adapters.rb
Normal file
42
lib/core_ext/test_multiple_adapters.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
require "byebug"
|
||||
|
||||
module CoreExt
|
||||
module TestMultipleAdapters
|
||||
def perform(...)
|
||||
#require APP_PATH
|
||||
$LOAD_PATH << Rails::Command.root.join("test").to_s
|
||||
require "test_helper"
|
||||
|
||||
Rails.application.config.database_configuration.each_pair do |name, config|
|
||||
next unless name.start_with?('test_')
|
||||
|
||||
puts config['adapter']
|
||||
ENV['DATABASE_URL'] = dbconfig_to_url(config)
|
||||
byebug
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :test, :perform
|
||||
|
||||
private
|
||||
|
||||
def dbconfig_to_url(cfg)
|
||||
return cfg['url'] if cfg.has_key?("url")
|
||||
|
||||
config = cfg.transform_values { |v| URI.encode_www_form_component(v) }
|
||||
|
||||
username, password = config.delete('username'), config.delete('password')
|
||||
auth = username ? "#{username}:#{password}" : ''
|
||||
host, port = config.delete('host'), config.delete('port')
|
||||
server = port ? "#{host}:#{port}" : host
|
||||
url = "#{config.delete('adapter')}:"
|
||||
url += "//#{auth}@#{server}" if auth || server
|
||||
url += "/#{config.delete('database')}"
|
||||
url += "?#{config.map { |k,v| "#{k}=#{v}" }.join('&')}" unless config.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#require "rails/commands/test/test_command"
|
||||
#Rails::Command::TestCommand.prepend CoreExt::TestMultipleAdapters
|
||||
@@ -1,164 +0,0 @@
|
||||
require 'uri'
|
||||
|
||||
# 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.
|
||||
#
|
||||
# Database configuration is read via Rails.application.config.database_configuration
|
||||
# so ERB interpolation and all Rails path overrides are respected automatically.
|
||||
# Each subprocess receives DATABASE_URL built from the selected config, which
|
||||
# Rails merges on top of the test: entry from database.yml — no temporary files needed.
|
||||
|
||||
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 ||= Rails.application.config.database_configuration
|
||||
.select { |k, v| k.to_s.start_with?('test') && v.is_a?(Hash) }
|
||||
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
|
||||
|
||||
env = { 'DATABASE_URL' => config_to_url(config) }
|
||||
|
||||
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
|
||||
|
||||
print_summary(cfgs, results)
|
||||
|
||||
failed = results.count { |_, s| [:fail, :prepare_failed].include?(s) }
|
||||
exit(false) if failed > 0
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Build a DATABASE_URL string from a database.yml config hash.
|
||||
# Rails merges DATABASE_URL on top of the test: entry, so adapter and
|
||||
# connection details from the URL take precedence over the YAML file.
|
||||
def config_to_url(config)
|
||||
adapter = config['adapter'].to_s
|
||||
database = config['database'].to_s
|
||||
|
||||
return "sqlite3:#{database}" if adapter == 'sqlite3'
|
||||
|
||||
user = config['username']
|
||||
pass = config['password']
|
||||
host = config['host'] || 'localhost'
|
||||
port = config['port']
|
||||
socket = config['socket']
|
||||
|
||||
userinfo = user ? "#{URI.encode_www_form_component(user)}:#{URI.encode_www_form_component(pass.to_s)}@" : ''
|
||||
hostport = port ? "#{host}:#{port}" : host
|
||||
url = "#{adapter}://#{userinfo}#{hostport}/#{database}"
|
||||
url += "?socket=#{URI.encode_www_form_component(socket)}" if socket
|
||||
url
|
||||
end
|
||||
|
||||
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
|
||||
@@ -25,9 +25,10 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
||||
user
|
||||
end
|
||||
|
||||
def inject_button_to(after, *button_options)
|
||||
def inject_button_to(inside, *button_options)
|
||||
button = button_to *button_options
|
||||
evaluate_script("arguments[0].insertAdjacentHTML('beforeend', '#{button.html_safe}');", after)
|
||||
inside.evaluate_script("this.insertAdjacentHTML('beforeend', arguments[0]);",
|
||||
button.html_safe)
|
||||
end
|
||||
|
||||
# Allow skipping interpolations when translating for testing purposes
|
||||
@@ -55,4 +56,8 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
||||
# find('a[disabled]').click
|
||||
# end
|
||||
#end
|
||||
|
||||
# TODO: override #test, creating per-adapter test methods running within
|
||||
# #with_connection?
|
||||
# define shards for test database
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@ require "test_helper"
|
||||
|
||||
class UsersControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@user = users(:one)
|
||||
@user = users(:admin)
|
||||
end
|
||||
|
||||
test "should get index" do
|
||||
|
||||
@@ -6,6 +6,7 @@ class UsersTest < ApplicationSystemTestCase
|
||||
end
|
||||
|
||||
test 'sign in' do
|
||||
byebug
|
||||
visit root_url
|
||||
assert find_link(href: new_user_session_path)[:disabled]
|
||||
|
||||
@@ -227,7 +228,8 @@ class UsersTest < ApplicationSystemTestCase
|
||||
|
||||
within all(:xpath, "//tbody//tr[not(descendant::select)]").sample do |tr|
|
||||
user = User.find_by_email!(first(:link).text)
|
||||
inject_button_to first('td:not(.link)'), "update status", user_path(user), method: :patch,
|
||||
inject_button_to find('td', exact_text: user.status), "update status",
|
||||
user_path(user), method: :patch,
|
||||
params: {user: {status: User.statuses.keys.sample}}, data: {turbo: false}
|
||||
click_on "update status"
|
||||
end
|
||||
@@ -237,8 +239,8 @@ class UsersTest < ApplicationSystemTestCase
|
||||
test 'update status forbidden for non admin' do
|
||||
sign_in user: users.reject(&:admin?).select(&:confirmed?).sample
|
||||
visit units_path
|
||||
inject_button_to find('body'), "update status", user_path(User.all.sample), method: :patch,
|
||||
params: {user: {status: User.statuses.keys.sample}}
|
||||
inject_button_to find('body'), "update status", user_path(User.all.sample),
|
||||
method: :patch, params: {user: {status: User.statuses.keys.sample}}
|
||||
click_on "update status"
|
||||
assert_text t('actioncontroller.exceptions.status.forbidden')
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user