forked from fixin.me/fixin.me
Compare commits
3 Commits
feature/mu
...
extend-all
| Author | SHA1 | Date | |
|---|---|---|---|
| dd9572689a | |||
| 376640cfd7 | |||
| 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 %>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
APP_PATH = File.expand_path("../config/application", __dir__)
|
APP_PATH = File.expand_path("../config/application", __dir__)
|
||||||
require_relative "../config/boot"
|
require_relative "../config/boot"
|
||||||
|
require_relative "../lib/core_ext/test_multiple_adapters.rb"
|
||||||
require "rails/commands"
|
require "rails/commands"
|
||||||
|
|||||||
@@ -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 `rails test:all_databases` to test against multiple DB adapters.
|
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
@@ -42,32 +42,24 @@ production:
|
|||||||
# <<: *default
|
# <<: *default
|
||||||
# database: fixinme_dev
|
# database: fixinme_dev
|
||||||
|
|
||||||
# Warning: The database defined as "test" will be erased and
|
# Warning: Test databases will be erased and re-generated from your development
|
||||||
# re-generated from your development database when you run "rake".
|
# database when you run "rake". Do not set these dbs to the same as development
|
||||||
# Do not set this db to the same as development or production.
|
# 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:
|
#test:
|
||||||
# <<: *default
|
# <<: *default
|
||||||
# database: fixinme_test
|
# database: fixinme_test
|
||||||
|
|
||||||
# Multi-database testing — `bundle exec rails test:all_databases`
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
# Add any number of `test_<name>:` blocks to run the full test suite
|
|
||||||
# against additional database adapters in a single command.
|
|
||||||
# Each adapter's gem must be available in the bundle:
|
|
||||||
# bundle config --local with "mysql:sqlite" # mysql + sqlite
|
|
||||||
# bundle config --local with "mysql:pg" # mysql + postgresql
|
|
||||||
#
|
#
|
||||||
# Example — run tests against MySQL and SQLite:
|
#test_sqlite3:
|
||||||
#
|
|
||||||
#test_sqlite:
|
|
||||||
# adapter: sqlite3
|
# adapter: sqlite3
|
||||||
# database: db/fixinme_test.sqlite3
|
# database: db/fixinme_test.sqlite3
|
||||||
#
|
#
|
||||||
# Example — run tests against MySQL and PostgreSQL:
|
#test_postgresql:
|
||||||
#
|
|
||||||
#test_pg:
|
|
||||||
# adapter: 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,118 +0,0 @@
|
|||||||
require 'yaml'
|
|
||||||
require 'erb'
|
|
||||||
require 'tmpdir'
|
|
||||||
|
|
||||||
namespace :test do
|
|
||||||
desc <<~DESC
|
|
||||||
Run the full test suite against every test database configured in database.yml.
|
|
||||||
|
|
||||||
Any top-level key that starts with "test" and contains a Hash is treated as a
|
|
||||||
test database configuration:
|
|
||||||
|
|
||||||
test: # always required — the primary test database
|
|
||||||
adapter: mysql2
|
|
||||||
database: fixinme_test
|
|
||||||
...
|
|
||||||
|
|
||||||
test_sqlite: # optional additional databases
|
|
||||||
adapter: sqlite3
|
|
||||||
database: db/fixinme_test.sqlite3
|
|
||||||
|
|
||||||
test_pg:
|
|
||||||
adapter: postgresql
|
|
||||||
database: fixinme_test
|
|
||||||
...
|
|
||||||
|
|
||||||
For each database the task will:
|
|
||||||
1. Check that the required adapter gem is available (skip with warning if not).
|
|
||||||
2. Run `rails db:test:prepare` to create/migrate the database.
|
|
||||||
3. Run `rails test` and record pass/fail.
|
|
||||||
|
|
||||||
A summary is printed at the end. The task exits non-zero if any database fails.
|
|
||||||
DESC
|
|
||||||
task :all_databases do
|
|
||||||
db_file = Rails.root.join('config', 'database.yml')
|
|
||||||
all = YAML.safe_load(ERB.new(db_file.read).result, aliases: true) || {}
|
|
||||||
test_cfgs = all.select { |k, v| k.to_s.start_with?('test') && v.is_a?(Hash) }
|
|
||||||
non_test = all.reject { |k, _| k.to_s.start_with?('test') }
|
|
||||||
|
|
||||||
abort "No test database configurations found in #{db_file}." if test_cfgs.empty?
|
|
||||||
|
|
||||||
results = {}
|
|
||||||
|
|
||||||
test_cfgs.each do |name, config|
|
|
||||||
adapter = config['adapter'].to_s
|
|
||||||
|
|
||||||
puts "\n#{'─' * 64}"
|
|
||||||
puts " #{name} (adapter: #{adapter})"
|
|
||||||
puts '─' * 64
|
|
||||||
|
|
||||||
unless adapter_available?(adapter)
|
|
||||||
puts " SKIPPED — gem for '#{adapter}' adapter not available in bundle."
|
|
||||||
puts " Add it to Gemfile (e.g. `bundle config --local with #{adapter_group(adapter)}`)"
|
|
||||||
results[name] = :skipped
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
Dir.mktmpdir('rails_test_db_') do |tmpdir|
|
|
||||||
tmp_yml = File.join(tmpdir, 'database.yml')
|
|
||||||
# Write a standalone database.yml with just this config as `test:`
|
|
||||||
# (non-test configs are preserved so boot doesn't fail on `production:` lookup)
|
|
||||||
File.write(tmp_yml, non_test.merge('test' => config).to_yaml)
|
|
||||||
|
|
||||||
env = { 'RAILS_DATABASE_YML' => tmp_yml }
|
|
||||||
|
|
||||||
if system(env, 'bundle exec rails db:test:prepare')
|
|
||||||
results[name] = system(env, 'bundle exec rails test') ? :pass : :fail
|
|
||||||
else
|
|
||||||
results[name] = :prepare_failed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
puts "\n#{'═' * 64}"
|
|
||||||
puts " SUMMARY"
|
|
||||||
puts '═' * 64
|
|
||||||
results.each do |name, status|
|
|
||||||
adapter = test_cfgs[name]['adapter']
|
|
||||||
icon = { pass: '✓', fail: '✗', prepare_failed: '✗', skipped: '–' }[status]
|
|
||||||
label = { pass: 'PASS', fail: 'FAIL',
|
|
||||||
prepare_failed: 'PREPARE FAILED', skipped: 'SKIPPED' }[status]
|
|
||||||
puts " #{icon} #{name.ljust(26)} (#{adapter.ljust(12)}) #{label}"
|
|
||||||
end
|
|
||||||
puts '═' * 64
|
|
||||||
|
|
||||||
failed = results.count { |_, s| s == :fail || s == :prepare_failed }
|
|
||||||
abort "\n #{failed} database(s) failed." if failed > 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Returns true if the gem required for this adapter is loadable.
|
|
||||||
ADAPTER_GEMS = {
|
|
||||||
'mysql2' => 'mysql2',
|
|
||||||
'sqlite3' => 'sqlite3',
|
|
||||||
'postgresql' => 'pg',
|
|
||||||
'pg' => 'pg',
|
|
||||||
}.freeze
|
|
||||||
|
|
||||||
def adapter_available?(adapter)
|
|
||||||
gem_name = ADAPTER_GEMS.fetch(adapter, adapter)
|
|
||||||
require gem_name
|
|
||||||
true
|
|
||||||
rescue LoadError
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the Bundler group name that installs the adapter gem.
|
|
||||||
ADAPTER_GROUPS = {
|
|
||||||
'mysql2' => 'mysql',
|
|
||||||
'sqlite3' => 'sqlite',
|
|
||||||
'postgresql' => 'postgresql',
|
|
||||||
'pg' => 'postgresql',
|
|
||||||
}.freeze
|
|
||||||
|
|
||||||
def adapter_group(adapter)
|
|
||||||
ADAPTER_GROUPS.fetch(adapter, adapter)
|
|
||||||
end
|
|
||||||
@@ -25,9 +25,10 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
|||||||
user
|
user
|
||||||
end
|
end
|
||||||
|
|
||||||
def inject_button_to(after, *button_options)
|
def inject_button_to(inside, *button_options)
|
||||||
button = button_to *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
|
end
|
||||||
|
|
||||||
# Allow skipping interpolations when translating for testing purposes
|
# Allow skipping interpolations when translating for testing purposes
|
||||||
@@ -55,4 +56,8 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
|||||||
# find('a[disabled]').click
|
# find('a[disabled]').click
|
||||||
# end
|
# end
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
# TODO: override #test, creating per-adapter test methods running within
|
||||||
|
# #with_connection?
|
||||||
|
# define shards for test database
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ require "test_helper"
|
|||||||
|
|
||||||
class UsersControllerTest < ActionDispatch::IntegrationTest
|
class UsersControllerTest < ActionDispatch::IntegrationTest
|
||||||
setup do
|
setup do
|
||||||
@user = users(:one)
|
@user = users(:admin)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "should get index" do
|
test "should get index" do
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ class UsersTest < ApplicationSystemTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test 'sign in' do
|
test 'sign in' do
|
||||||
|
byebug
|
||||||
visit root_url
|
visit root_url
|
||||||
assert find_link(href: new_user_session_path)[:disabled]
|
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|
|
within all(:xpath, "//tbody//tr[not(descendant::select)]").sample do |tr|
|
||||||
user = User.find_by_email!(first(:link).text)
|
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}
|
params: {user: {status: User.statuses.keys.sample}}, data: {turbo: false}
|
||||||
click_on "update status"
|
click_on "update status"
|
||||||
end
|
end
|
||||||
@@ -237,8 +239,8 @@ class UsersTest < ApplicationSystemTestCase
|
|||||||
test 'update status forbidden for non admin' do
|
test 'update status forbidden for non admin' do
|
||||||
sign_in user: users.reject(&:admin?).select(&:confirmed?).sample
|
sign_in user: users.reject(&:admin?).select(&:confirmed?).sample
|
||||||
visit units_path
|
visit units_path
|
||||||
inject_button_to find('body'), "update status", user_path(User.all.sample), method: :patch,
|
inject_button_to find('body'), "update status", user_path(User.all.sample),
|
||||||
params: {user: {status: User.statuses.keys.sample}}
|
method: :patch, params: {user: {status: User.statuses.keys.sample}}
|
||||||
click_on "update status"
|
click_on "update status"
|
||||||
assert_text t('actioncontroller.exceptions.status.forbidden')
|
assert_text t('actioncontroller.exceptions.status.forbidden')
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user