From 3967188799cfdda9529e04b7cddf3aa97c4b9f0c Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 19:39:08 -0400 Subject: [PATCH 001/111] Store capybara screenshots --- .circleci/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index baec602..ca2ced1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -81,6 +81,9 @@ jobs: - store_artifacts: path: log/ + - store_artifacts: + path: tmp/capybara/ + - run: name: Stash Coverage Results command: | From 7d1fc18b6e34c68f2ff2be5d8b33d442de269465 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 19:41:03 -0400 Subject: [PATCH 002/111] remove system tests --- .../emergency_passkey_registrations_test.rb | 42 ------------------- test/dummy/test/system/user/signin_test.rb | 15 ------- 2 files changed, 57 deletions(-) delete mode 100644 test/dummy/test/system/user/emergency_passkey_registrations_test.rb delete mode 100644 test/dummy/test/system/user/signin_test.rb diff --git a/test/dummy/test/system/user/emergency_passkey_registrations_test.rb b/test/dummy/test/system/user/emergency_passkey_registrations_test.rb deleted file mode 100644 index 9b573cb..0000000 --- a/test/dummy/test/system/user/emergency_passkey_registrations_test.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true - -require "application_system_test_case" - -class User::EmergencyPasskeyRegistrationsTest < SlowBrowserSystemTestCase - test "can emergency register a passkey" do - user = users.user_1 - user.emergency_passkey_registrations.delete_all - - authenticator = add_virtual_authenticator - - visit new_user_emergency_passkey_registration_url - - fill_in "Email", with: user.email - - click_on "Get registration instructions" - - assert_current_path new_user_session_url - - assert_toast_message(text: I18n.translate("emergency_passkey_registrations.sent_message")) - - emergency_registration = user.emergency_passkey_registrations.reload.first - - token = emergency_registration.generate_token_for(:emergency_registration) - - visit user_emergency_passkey_registration_url(token) - - fill_in "Passkey label", with: Faker::Computer.stack - - click_button "Register passkey" - - assert_current_path new_user_session_url - - assert_toast_message(text: I18n.translate("emergency_passkey_registrations.registered_message")) - - fill_in "Email", with: user.email - - click_on "Sign in" - - assert_current_path root_url - end -end diff --git a/test/dummy/test/system/user/signin_test.rb b/test/dummy/test/system/user/signin_test.rb deleted file mode 100644 index ae8192b..0000000 --- a/test/dummy/test/system/user/signin_test.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require "application_system_test_case" - -class User::RackSigninTest < ApplicationSystemTestCase - test "can sign in with an existing passkey" do - assert_sign_in_user(user: users.user_1) - end -end - -class User::BrowserSigninTest < SlowBrowserSystemTestCase - test "can sign in with an existing passkey" do - assert_sign_in_user(user: users.user_1) - end -end \ No newline at end of file From 9c626f24bfb60ec820040d37a0b2e78ddcdbafd0 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 19:47:48 -0400 Subject: [PATCH 003/111] Add rake tasks for coverage & deployment tracking; CircleCI update --- .circleci/config.yml | 2 +- lib/practical/framework/engine.rb | 5 +++++ lib/tasks/practical/framework_tasks.rake | 6 ------ lib/tasks/practical/utility.rake | 20 ++++++++++++++++++++ 4 files changed, 26 insertions(+), 7 deletions(-) delete mode 100644 lib/tasks/practical/framework_tasks.rake create mode 100644 lib/tasks/practical/utility.rake diff --git a/.circleci/config.yml b/.circleci/config.yml index ca2ced1..531e46a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -43,7 +43,7 @@ jobs: - run: name: Merge and check coverage command: | - bundle exec rake coverage:report + bundle exec rake app:coverage:report - store_artifacts: path: coverage diff --git a/lib/practical/framework/engine.rb b/lib/practical/framework/engine.rb index 47e1e75..9b0e5f8 100644 --- a/lib/practical/framework/engine.rb +++ b/lib/practical/framework/engine.rb @@ -3,6 +3,11 @@ module Practical module Framework class Engine < ::Rails::Engine + rake_tasks do + load 'tasks/practical/coverage.rake' + load 'tasks/practical/utility.rake' + end + config.app_generators do |g| g.apply_rubocop_autocorrect_after_generate! g.test_framework :test_unit, fixture: false, fixture_replacement: :oaken diff --git a/lib/tasks/practical/framework_tasks.rake b/lib/tasks/practical/framework_tasks.rake deleted file mode 100644 index b35e4dc..0000000 --- a/lib/tasks/practical/framework_tasks.rake +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -# desc "Explaining what the task does" -# task :practical_framework do -# # Task goes here -# end diff --git a/lib/tasks/practical/utility.rake b/lib/tasks/practical/utility.rake new file mode 100644 index 0000000..861bf9b --- /dev/null +++ b/lib/tasks/practical/utility.rake @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +namespace :utility do + desc "Track the deployment" + task track_deployment: :environment do + Honeybadger.track_deployment( + environment: AppSettings.app_env, + revision: AppSettings.app_revision, + local_username: "rake", + repository: "git@github.com:practical-computer/app_repo.git" + ) + + HTTPX.get("https://api.honeybadger.io/v1/deploys", params: { + "deploy[environment]"=> AppSettings.app_env, + "deploy[local_username]" => "rake", + "deploy[revision]" => AppSettings.app_revision, + "api_key" => AppSettings.honeybadger_js_api_key + }) + end +end From 554389d84f5f57657d040f37f6d97a9d04487893 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 19:58:43 -0400 Subject: [PATCH 004/111] Add `Practical::Helpers::SelectorHelper` * These helpers will be used to streamline setting up DOM selectors --- lib/practical.rb | 3 ++- lib/practical/framework/engine.rb | 1 + lib/practical/helpers/selector_helper.rb | 8 ++++++ .../practical/helpers/selector_helper_test.rb | 27 +++++++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 lib/practical/helpers/selector_helper.rb create mode 100644 test/lib/practical/helpers/selector_helper_test.rb diff --git a/lib/practical.rb b/lib/practical.rb index c213807..55612b4 100644 --- a/lib/practical.rb +++ b/lib/practical.rb @@ -18,4 +18,5 @@ module Views require "practical/helpers/form_with_helper" require "practical/helpers/icon_helper" require "practical/helpers/text_helper" -require "practical/helpers/translation_helper" \ No newline at end of file +require "practical/helpers/translation_helper" +require "practical/helpers/selector_helper" \ No newline at end of file diff --git a/lib/practical/framework/engine.rb b/lib/practical/framework/engine.rb index 9b0e5f8..be18d68 100644 --- a/lib/practical/framework/engine.rb +++ b/lib/practical/framework/engine.rb @@ -33,6 +33,7 @@ def practical_form_with(**args, &block) include Practical::Helpers::IconHelper include Practical::Helpers::TextHelper include Practical::Helpers::TranslationHelper + include Practical::Helpers::SelectorHelper end end end diff --git a/lib/practical/helpers/selector_helper.rb b/lib/practical/helpers/selector_helper.rb new file mode 100644 index 0000000..f86ca3f --- /dev/null +++ b/lib/practical/helpers/selector_helper.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Practical::Helpers::SelectorHelper + def id_selector(text) + text = text.to_param if text.is_a?(GlobalID) + "##{text}" + end +end \ No newline at end of file diff --git a/test/lib/practical/helpers/selector_helper_test.rb b/test/lib/practical/helpers/selector_helper_test.rb new file mode 100644 index 0000000..95e2cce --- /dev/null +++ b/test/lib/practical/helpers/selector_helper_test.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require "test_helper" + +class Practical::Helpers::SelectorHelperTest < ActiveSupport::TestCase + class TestClass + extend Practical::Helpers::SelectorHelper + end + + class TestModel + include GlobalID::Identification + + def id + 3 + end + end + + test "id_selector" do + assert_equal "#test1234", TestClass.id_selector("test1234") + assert_equal "#1234", TestClass.id_selector(1234) + + record = TestModel.new + + assert_equal "##{record.to_gid_param}", TestClass.id_selector(record.to_gid) + assert_equal "##{record.to_gid_param}", TestClass.id_selector(record.to_gid_param) + end +end \ No newline at end of file From 3344a61e59eaf94bea753308af3f376d0fd847ea Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 20:19:00 -0400 Subject: [PATCH 005/111] Add `Practical::Helper::HoneybadgerHelper` --- lib/practical.rb | 3 ++- lib/practical/framework/engine.rb | 1 + lib/practical/helpers/honeybadger_helper.rb | 11 +++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 lib/practical/helpers/honeybadger_helper.rb diff --git a/lib/practical.rb b/lib/practical.rb index 55612b4..7dd5790 100644 --- a/lib/practical.rb +++ b/lib/practical.rb @@ -19,4 +19,5 @@ module Views require "practical/helpers/icon_helper" require "practical/helpers/text_helper" require "practical/helpers/translation_helper" -require "practical/helpers/selector_helper" \ No newline at end of file +require "practical/helpers/selector_helper" +require "practical/helpers/honeybadger_helper" \ No newline at end of file diff --git a/lib/practical/framework/engine.rb b/lib/practical/framework/engine.rb index be18d68..81193df 100644 --- a/lib/practical/framework/engine.rb +++ b/lib/practical/framework/engine.rb @@ -34,6 +34,7 @@ def practical_form_with(**args, &block) include Practical::Helpers::TextHelper include Practical::Helpers::TranslationHelper include Practical::Helpers::SelectorHelper + include Practical::Helpers::HoneybadgerHelpers end end end diff --git a/lib/practical/helpers/honeybadger_helper.rb b/lib/practical/helpers/honeybadger_helper.rb new file mode 100644 index 0000000..1578723 --- /dev/null +++ b/lib/practical/helpers/honeybadger_helper.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Practical::Helpers::HoneybadgerHelpers + def honeybadger_js_configuration + { + "apiKey" => AppSettings.honeybadger_js_api_key, + "environment" => AppSettings.app_env, + "revision" => AppSettings.app_revision + } + end +end \ No newline at end of file From 944762811d46807591e2a1bcb503fbcb8fe66ece Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 20:31:07 -0400 Subject: [PATCH 006/111] Add `Practical::Views::ThemeHelper` --- lib/practical.rb | 1 + lib/practical/framework/engine.rb | 1 + lib/practical/views/theme_helper.rb | 11 +++++++++++ 3 files changed, 13 insertions(+) create mode 100644 lib/practical/views/theme_helper.rb diff --git a/lib/practical.rb b/lib/practical.rb index 7dd5790..9d8f664 100644 --- a/lib/practical.rb +++ b/lib/practical.rb @@ -15,6 +15,7 @@ module Views end require "practical/views/element_helper" +require "practical/views/theme_helper" require "practical/helpers/form_with_helper" require "practical/helpers/icon_helper" require "practical/helpers/text_helper" diff --git a/lib/practical/framework/engine.rb b/lib/practical/framework/engine.rb index 81193df..30ea420 100644 --- a/lib/practical/framework/engine.rb +++ b/lib/practical/framework/engine.rb @@ -29,6 +29,7 @@ def practical_form_with(**args, &block) initializer 'practical-framework.view_helpers' do ActiveSupport.on_load(:action_view) do include Practical::Framework::Engine::PracticalFrameworkFormBuilderActiveSupportExtension + include Practical::Views::ThemeHelper include Practical::Helpers::FormWithHelper include Practical::Helpers::IconHelper include Practical::Helpers::TextHelper diff --git a/lib/practical/views/theme_helper.rb b/lib/practical/views/theme_helper.rb new file mode 100644 index 0000000..c5f746f --- /dev/null +++ b/lib/practical/views/theme_helper.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Practical::Views::ThemeHelper + def theme_attribute + if user_signed_in? + return current_user.theme + else + return "match-system" + end + end +end \ No newline at end of file From c3b9517a700b88995d516e44fa471d516b1f322f Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 20:31:20 -0400 Subject: [PATCH 007/111] DA: add `theme_attribute` to HTML markup --- test/dummy/app/views/layouts/application.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dummy/app/views/layouts/application.html.erb b/test/dummy/app/views/layouts/application.html.erb index e7de7b4..f46830b 100644 --- a/test/dummy/app/views/layouts/application.html.erb +++ b/test/dummy/app/views/layouts/application.html.erb @@ -1,5 +1,5 @@ - + <%= capybara_lockstep if defined?(Capybara::Lockstep) %> <%= content_for(:title) || "Dummy" %> From c043509ce4f618dfa8becdb40dc24323e5e50e29 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 20:34:01 -0400 Subject: [PATCH 008/111] Add `Practical::RelationBuilders::Base` --- app/lib/practical/relation_builders/base.rb | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 app/lib/practical/relation_builders/base.rb diff --git a/app/lib/practical/relation_builders/base.rb b/app/lib/practical/relation_builders/base.rb new file mode 100644 index 0000000..2246f61 --- /dev/null +++ b/app/lib/practical/relation_builders/base.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class Practical::RelationBuilders::Base + attr_accessor :payload, :relation + + def initialize(payload:, relation:) + self.payload = payload + self.relation = relation + end + + def applied_relation + scope = apply_filtering(scope: relation) + scope = apply_ordering(scope: scope) + + return scope + end + + def apply_filtering(scope:) + raise NotImplementedError + end + + def apply_ordering(scope:) + scope = first_order_sorting(scope: scope) + scope = second_order_sorting(scope: scope) + return scope + end + + def first_order_sorting(scope:) + raise NotImplementedError + end + + def second_order_sorting(scope:) + raise NotImplementedError + end +end \ No newline at end of file From ddf5c1bbe04333b16f6aa20bcc11507b5dfb04a6 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 20:34:58 -0400 Subject: [PATCH 009/111] Add `Practical::Loaders::Base` --- app/lib/practical/loaders/base.rb | 44 +++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 app/lib/practical/loaders/base.rb diff --git a/app/lib/practical/loaders/base.rb b/app/lib/practical/loaders/base.rb new file mode 100644 index 0000000..efa8da1 --- /dev/null +++ b/app/lib/practical/loaders/base.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class Practical::Loaders::Base + include Pagy::Backend + + attr_accessor :params, :base_relation, :datatable_form, :relation_builder, :pagy_instance, :records + + def initialize(params:, base_relation:) + self.params = params + self.base_relation = base_relation + end + + def self.load(params:, base_relation:) + instance = self.new(params: params, base_relation: base_relation) + instance.load + return instance + end + + def load + self.datatable_form = build_datatable_form + self.relation_builder = build_relation_builder + self.pagy_instance, self.records = pagy(relation_builder.applied_relation, overflow: :last_page) + end + + def datatable_payload + (datatable_params[:datatable] || default_payload) + end + + def datatable_params + params.permit(datatable: [:sort_key, :sort_direction, filters: {}]) + end + + def build_datatable_form + raise NotImplementedError + end + + def build_relation_builder + raise NotImplementedError + end + + def default_payload + raise NotImplementedError + end +end \ No newline at end of file From fed539f06a6283e101d82664614889d37f1eef70 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 20:37:14 -0400 Subject: [PATCH 010/111] Add `Practical::Forms::Datatables::Base` --- app/lib/practical/forms/datatables/base.rb | 80 ++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 app/lib/practical/forms/datatables/base.rb diff --git a/app/lib/practical/forms/datatables/base.rb b/app/lib/practical/forms/datatables/base.rb new file mode 100644 index 0000000..bbbf108 --- /dev/null +++ b/app/lib/practical/forms/datatables/base.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +module Practical::Forms::Datatables::Base + extend ActiveSupport::Concern + + included do + attr_accessor :sort_key, :sort_direction, :filters, :sanitized + + before_validation :normalize_sort_key_and_direction + validate :matches_schema? + end + + def initialize(attributes = {}) + super + self.sanitize! + if self.filters.present? + self.filters = self.class.filter_class.new(self.filters) + end + end + + def sanitized? + return self.sanitized == true + end + + def payload + { + sort_key: sort_key, + sort_direction: sort_direction, + filters: filters.to_h, + } + end + + def merged_payload(filters: nil, sort_key: nil, sort_direction: nil) + result = payload + result[:filters] ||= {} + if filters.present? + result[:filters].merge!(filters) + end + + result[:sort_key] = sort_key if sort_key.present? + result[:sort_direction] = sort_direction if sort_direction.present? + + return result + end + + def sanitize! + validate! + self.sanitized = true + rescue ActiveModel::ValidationError + self.attributes = self.class.default_payload + self.sanitized = true + self.errors.clear + self.validate! + end + + def sort_direction_for(key:) + return nil unless key.downcase.strip == self.sort_key.downcase.strip + return sort_direction + end + + def inverted_sort_direction_for(key:) + return "asc" unless key.downcase.strip == self.sort_key.downcase.strip + case sort_direction + when "asc" + "desc" + else + "asc" + end + end + + def normalize_sort_key_and_direction + self.sort_key = sort_key.to_s.downcase.strip + self.sort_direction = sort_direction.to_s.downcase.strip + end + + def matches_schema? + return if self.class.schema.validate?(payload) + errors.add(:base, :payload_does_not_match_schema) + end +end \ No newline at end of file From 7eb5ec781cf28b27c999c496caaf5644a3829f17 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 20:48:18 -0400 Subject: [PATCH 011/111] Dummy App: Add `theme` enum to `User` --- test/dummy/app/models/user.rb | 2 ++ test/dummy/db/migrate/20250603003858_add_theme_to_users.rb | 5 +++++ test/dummy/db/schema.rb | 5 ++--- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 test/dummy/db/migrate/20250603003858_add_theme_to_users.rb diff --git a/test/dummy/app/models/user.rb b/test/dummy/app/models/user.rb index 76686e6..c46457b 100644 --- a/test/dummy/app/models/user.rb +++ b/test/dummy/app/models/user.rb @@ -16,6 +16,8 @@ class User < ApplicationRecord has_many :organizations, through: :memberships has_many :membership_invitations + enum :theme, { "match-system": 0, "light": 1, "dark": 2 }, default: :"match-system", suffix: "theme" + def self.passkeys_class Passkey end diff --git a/test/dummy/db/migrate/20250603003858_add_theme_to_users.rb b/test/dummy/db/migrate/20250603003858_add_theme_to_users.rb new file mode 100644 index 0000000..33853a7 --- /dev/null +++ b/test/dummy/db/migrate/20250603003858_add_theme_to_users.rb @@ -0,0 +1,5 @@ +class AddThemeToUsers < ActiveRecord::Migration[8.0] + def change + add_column :users, :theme, :integer, limit: 2, default: 0 + end +end diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb index 0b7a713..0517e53 100644 --- a/test/dummy/db/schema.rb +++ b/test/dummy/db/schema.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -12,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_06_02_221215) do +ActiveRecord::Schema[8.0].define(version: 2025_06_03_003858) do create_table "emergency_passkey_registrations", force: :cascade do |t| t.integer "user_id", null: false t.integer "passkey_id" @@ -165,6 +163,7 @@ t.datetime "updated_at", null: false t.datetime "remember_created_at" t.text "remember_token" + t.integer "theme", limit: 2, default: 0 t.index ["email"], name: "index_users_on_email", unique: true t.index ["remember_token"], name: "index_users_on_remember_token", unique: true end From 484236a48d5f09fc3ac6be9321fb97b49b23ec98 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 20:48:37 -0400 Subject: [PATCH 012/111] `Practical::Views::ThemeHelper` fallsback to `match-system` on error --- lib/practical/views/theme_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/practical/views/theme_helper.rb b/lib/practical/views/theme_helper.rb index c5f746f..70c04cc 100644 --- a/lib/practical/views/theme_helper.rb +++ b/lib/practical/views/theme_helper.rb @@ -7,5 +7,7 @@ def theme_attribute else return "match-system" end + rescue + return "match-system" end end \ No newline at end of file From 5a8bb0b7fa9d44cdf73728ba92eebea2b7418185 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 20:49:35 -0400 Subject: [PATCH 013/111] Disable `coverage` step for CircleCI, OSS repos for orgs not supported --- .circleci/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 531e46a..ffcdb74 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -103,8 +103,8 @@ workflows: - test: context: - "Practical Framework" - - coverage: - context: - - "Practical Framework" - requires: - - test + # - coverage: + # context: + # - "Practical Framework" + # requires: + # - test From f2362a5be48675d5731a14e0da26ffcf2e91cdaa Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 20:50:22 -0400 Subject: [PATCH 014/111] All Hail Rubocop --- test/dummy/db/migrate/20250603003858_add_theme_to_users.rb | 2 ++ test/dummy/db/schema.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/dummy/db/migrate/20250603003858_add_theme_to_users.rb b/test/dummy/db/migrate/20250603003858_add_theme_to_users.rb index 33853a7..58c400d 100644 --- a/test/dummy/db/migrate/20250603003858_add_theme_to_users.rb +++ b/test/dummy/db/migrate/20250603003858_add_theme_to_users.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AddThemeToUsers < ActiveRecord::Migration[8.0] def change add_column :users, :theme, :integer, limit: 2, default: 0 diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb index 0517e53..9cdd122 100644 --- a/test/dummy/db/schema.rb +++ b/test/dummy/db/schema.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. From 04823dab1753c025b5be1e5d3a45194c6e6629bd Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 20:53:24 -0400 Subject: [PATCH 015/111] Update README to prep for 3.0.0 alphas --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 774d54d..209adb4 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # `practical`: The Practical Framework -_This gem is currently being open-sourced from a private repo, while also being singificantly reorganized. Its 3.0 release will be live soon!_ +_This gem has been open-sourced from a private repo, while also being singificantly reorganized. The code is functional and (mostly) tested; but it is marked as alpha until it can be properly documented, and existing apps switched over to use 3.0_ ## Installation -TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org. +TODO: Replace `practical` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org. Install the gem and add to the application's Gemfile by executing: - $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG + $ bundle add practical If bundler is not being used to manage dependencies, install the gem by executing: - $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG + $ gem install practical ## Usage From 6d1525d37367066f226e49c0ddb16b7d334015c7 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 20:53:42 -0400 Subject: [PATCH 016/111] Set version to `3.0.0-alpha1` --- Gemfile.lock | 2 +- lib/practical/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index cafba01..8ad443e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - practical (0.1.0) + practical (3.0.0.pre.alpha1) action_policy devise-passkeys honeybadger diff --git a/lib/practical/version.rb b/lib/practical/version.rb index 738287e..f61b446 100644 --- a/lib/practical/version.rb +++ b/lib/practical/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Practical - VERSION = "0.1.0" + VERSION = "3.0.0-alpha1" end From 414bfb443411809dfc3380eada9167494f908f38 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 21:14:16 -0400 Subject: [PATCH 017/111] npm update --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5827dbb..c81b76c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,12 +57,12 @@ }, "node_modules/@practical-computer/error-handling": { "version": "2.0.0", - "resolved": "git+ssh://git@github.com/practical-computer/practical-error-handling-js.git#9b1c92465f6c47c57ded478ef918a28204d83591", + "resolved": "git+ssh://git@github.com/practical-computer/practical-error-handling-js.git#4e67699c393df69095170ea7fa3e8f0f3bd0d68a", "license": "MIT" }, "node_modules/@practical-computer/practical-views": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/practical-computer/practical-views-js.git#9b5afe86551a8bf88703ffd28fff95381a73c1d8", + "resolved": "git+ssh://git@github.com/practical-computer/practical-views-js.git#7ffc03c5f424af209152f6dfcd7d136799a7b023", "license": "MIT", "dependencies": { "@github/webauthn-json": "^2.1.1", From b101dd145e1b3a0dbaf1c85169a9bd5922567e74 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Mon, 2 Jun 2025 21:14:20 -0400 Subject: [PATCH 018/111] Add `toast-dialog` from `practical-views` --- test/dummy/app/javascript/elements/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/dummy/app/javascript/elements/index.js b/test/dummy/app/javascript/elements/index.js index 13d3e80..bf78bd6 100644 --- a/test/dummy/app/javascript/elements/index.js +++ b/test/dummy/app/javascript/elements/index.js @@ -1,2 +1,3 @@ import '@practical-computer/practical-views/passkeys/elements/submit-form-after-reauthentication' -import '@practical-computer/practical-views/passkeys/elements/unlock-form-after-reauthentication' \ No newline at end of file +import '@practical-computer/practical-views/passkeys/elements/unlock-form-after-reauthentication' +import '@practical-computer/practical-views/elements/toast-dialog' \ No newline at end of file From e9833ad65a0ee335b64982068abba3d45c026743 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Fri, 6 Jun 2025 21:48:32 -0400 Subject: [PATCH 019/111] `Practical::Views::ErrorResponse: render_json/html_error` return 422 * In order to match the status code expected by Practical Error's server-side rendering, the `render_json_error` & `render_html_error` methods need to return a status code of `422 Unprocessable Entity` > The provided request processing functions expect a 422 response to > indicate that the request has validation errors. It's an error, and the response's status code should reflect that. > See: https://github.com/practical-computer/practical-error-handling-js?tab=readme-ov-file#server-side-errors --- app/lib/practical/views/error_response.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/practical/views/error_response.rb b/app/lib/practical/views/error_response.rb index 5a1bcfe..0963011 100644 --- a/app/lib/practical/views/error_response.rb +++ b/app/lib/practical/views/error_response.rb @@ -7,14 +7,14 @@ def render_json_error(format:, model:) format.json do errors = Practical::Views::ErrorHandling.build_error_json(model: model, helpers: helpers) yield(errors) if block_given? - render json: errors, status: :bad_request + render json: errors, status: :unprocessable_entity end end def render_html_error(action:, format:) format.html do yield if block_given? - render action, status: :bad_request + render action, status: :unprocessable_entity end end From 15a520151e86beef23987adbb791e02735a6b1f3 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Fri, 6 Jun 2025 21:51:09 -0400 Subject: [PATCH 020/111] Shared Membership controller tests expect `422` for error responses * In order to match the status code expected by Practical Error's server-side rendering, the `render_json_error` & `render_html_error` methods need to return a status code of `422 Unprocessable Entity` > The provided request processing functions expect a 422 response to > indicate that the request has validation errors. It's an error, and the response's status code should reflect that. > See: https://github.com/practical-computer/practical-error-handling-js?tab=readme-ov-file#server-side-errors --- .../controllers/organization/membership.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/lib/practical/test/shared/memberships/controllers/organization/membership.rb b/app/lib/practical/test/shared/memberships/controllers/organization/membership.rb index f1105ce..c573d01 100644 --- a/app/lib/practical/test/shared/memberships/controllers/organization/membership.rb +++ b/app/lib/practical/test/shared/memberships/controllers/organization/membership.rb @@ -160,7 +160,7 @@ module Practical::Test::Shared::Memberships::Controllers::Organization::Membersh end end - assert_response :bad_request + assert_response :unprocessable_entity assert_error_json_contains( container_id: "organization_new_membership_invitation_form_base_errors", @@ -192,7 +192,7 @@ module Practical::Test::Shared::Memberships::Controllers::Organization::Membersh end end - assert_response :bad_request + assert_response :unprocessable_entity assert_error_json_contains( container_id: "organization_new_membership_invitation_form_base_errors", @@ -224,7 +224,7 @@ module Practical::Test::Shared::Memberships::Controllers::Organization::Membersh end end - assert_response :bad_request + assert_response :unprocessable_entity assert_error_json_contains( container_id: "organization_new_membership_invitation_form_base_errors", @@ -252,7 +252,7 @@ module Practical::Test::Shared::Memberships::Controllers::Organization::Membersh post organization_memberships_url(organization), params: params, as: :json end end - assert_response :bad_request + assert_response :unprocessable_entity assert_error_json_contains( container_id: "organization_new_membership_invitation_form_email_errors", @@ -280,7 +280,7 @@ module Practical::Test::Shared::Memberships::Controllers::Organization::Membersh post organization_memberships_url(organization), params: params end end - assert_response :bad_request + assert_response :unprocessable_entity assert_error_dom( container_id: "organization_new_membership_invitation_form_email_errors", @@ -387,7 +387,7 @@ module Practical::Test::Shared::Memberships::Controllers::Organization::Membersh }} end - assert_response :bad_request + assert_response :unprocessable_entity assert_error_dom( container_id: "generic_errors_organization_membership_form", From 6d754c91cb20ded1e540362bf770cdbf843685a8 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Fri, 6 Jun 2025 21:52:06 -0400 Subject: [PATCH 021/111] `practical_form_with`: wraps form with `application-error-handling` * `application-error-handling` is a Custom Element that each application can define for its specific error-handling behavior, under the assumption that `@practical-computer/practical-error-handling` is being used: * https://github.com/practical-computer/practical-error-handling-js?tab=readme-ov-file#extending-the-custom-elements --- lib/practical/framework/engine.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/practical/framework/engine.rb b/lib/practical/framework/engine.rb index 30ea420..7bf5c24 100644 --- a/lib/practical/framework/engine.rb +++ b/lib/practical/framework/engine.rb @@ -18,7 +18,7 @@ module PracticalFrameworkFormBuilderActiveSupportExtension def practical_form_with(**args, &block) original_field_error_proc = ::ActionView::Base.field_error_proc ::ActionView::Base.field_error_proc = ->(html_tag, instance) { html_tag } - content_tag(:"practical-framework-error-handling") do + content_tag(:"application-error-handling") do form_with(**args, &block) end ensure From a62d0d659063acc227c26c162b46a568527cbf6e Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Fri, 6 Jun 2025 21:54:19 -0400 Subject: [PATCH 022/111] Update `Practical::Views::Form` for `@practical-computer/error-handling` * Updated the view components for form errors to follow the conventions and apply the data attributes that `@practical-computer/error-handling` expects * See: https://github.com/practical-computer/practical-error-handling-js?tab=readme-ov-file#data-attributes --- app/components/practical/views/form/error_list_component.rb | 2 +- .../practical/views/form/error_list_item_component.rb | 4 ++-- .../views/form/error_list_item_template_component.rb | 2 +- .../practical/views/form/fallback_errors_section_component.rb | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/components/practical/views/form/error_list_component.rb b/app/components/practical/views/form/error_list_component.rb index d1c9c5b..e301fe1 100644 --- a/app/components/practical/views/form/error_list_component.rb +++ b/app/components/practical/views/form/error_list_component.rb @@ -8,7 +8,7 @@ def initialize(errors:) end def call - tag.ul(class: 'error-list') { + tag.ul(data: {'data-pf-error-container': true}) { safe_join(errors.map{|error| render Practical::Views::Form::ErrorListItemComponent.new(error: error) }) } end diff --git a/app/components/practical/views/form/error_list_item_component.rb b/app/components/practical/views/form/error_list_item_component.rb index bb2c774..86652a3 100644 --- a/app/components/practical/views/form/error_list_item_component.rb +++ b/app/components/practical/views/form/error_list_item_component.rb @@ -12,9 +12,9 @@ def before_render end def call - tag.li(class: 'wa-flank', data: {"error-type": error.type}) { + tag.li(class: 'wa-flank', data: {"pf-error-type": error.type, "pf-error-visible": true}) { render(icon_set.error_list_icon) + - tag.span(error.message) + tag.span(error.message, "data": { "pf-error-message": true }) } end end diff --git a/app/components/practical/views/form/error_list_item_template_component.rb b/app/components/practical/views/form/error_list_item_template_component.rb index cab3a98..05c03d7 100644 --- a/app/components/practical/views/form/error_list_item_template_component.rb +++ b/app/components/practical/views/form/error_list_item_template_component.rb @@ -2,7 +2,7 @@ class Practical::Views::Form::ErrorListItemTemplateComponent < Practical::Views::BaseComponent def call - tag.template(id: 'error-list-item-template') { + tag.template(id: 'pf-error-list-item-template') { render Practical::Views::Form::ErrorListItemComponent.new(error: ActiveModel::Error.new(nil, nil, nil)) } end diff --git a/app/components/practical/views/form/fallback_errors_section_component.rb b/app/components/practical/views/form/fallback_errors_section_component.rb index 2c91007..3a3e23f 100644 --- a/app/components/practical/views/form/fallback_errors_section_component.rb +++ b/app/components/practical/views/form/fallback_errors_section_component.rb @@ -11,7 +11,8 @@ def initialize(f:, id:, blurb:, options:) def finalized_options mix({ - class: ["error-section", "fallback-error-section", "wa-callout", "wa-danger"], + class: ["wa-callout", "wa-danger"], + data: {"pf-error-container": true, "pf-fallback-error-section": true}, id: id }, @options) end From b3882904b3179d260999ef30c6fd649c96364a87 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Fri, 6 Jun 2025 22:01:02 -0400 Subject: [PATCH 023/111] All Hail Rubocop --- .../practical/views/form/error_list_item_component.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/practical/views/form/error_list_item_component.rb b/app/components/practical/views/form/error_list_item_component.rb index 86652a3..4c8d54a 100644 --- a/app/components/practical/views/form/error_list_item_component.rb +++ b/app/components/practical/views/form/error_list_item_component.rb @@ -14,7 +14,7 @@ def before_render def call tag.li(class: 'wa-flank', data: {"pf-error-type": error.type, "pf-error-visible": true}) { render(icon_set.error_list_icon) + - tag.span(error.message, "data": { "pf-error-message": true }) + tag.span(error.message, data: { "pf-error-message": true }) } end end From 654bc446304d5ad4a3ea3d14466c1766484aeb75 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Fri, 6 Jun 2025 22:06:02 -0400 Subject: [PATCH 024/111] Bump to `3.0.0.pre.alpha2` --- Gemfile.lock | 2 +- lib/practical/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8ad443e..b1309f7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - practical (3.0.0.pre.alpha1) + practical (3.0.0.pre.alpha2) action_policy devise-passkeys honeybadger diff --git a/lib/practical/version.rb b/lib/practical/version.rb index f61b446..373f121 100644 --- a/lib/practical/version.rb +++ b/lib/practical/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Practical - VERSION = "3.0.0-alpha1" + VERSION = "3.0.0-alpha2" end From c6490c93a01775bc21f8f8be3d54ee0916f65089 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Fri, 6 Jun 2025 22:06:55 -0400 Subject: [PATCH 025/111] `bundle update` --- Gemfile.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b1309f7..8e0e23a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -96,7 +96,7 @@ GEM base64 (0.3.0) bcrypt (3.1.20) benchmark (0.4.1) - bigdecimal (3.2.1) + bigdecimal (3.2.2) bindata (2.5.1) builder (3.3.0) capybara (3.40.0) @@ -253,7 +253,7 @@ GEM puma (6.6.0) nio4r (~> 2.0) racc (1.8.1) - rack (3.1.15) + rack (3.1.16) rack-session (2.1.1) base64 (>= 0.1.0) rack (>= 3.0.0) @@ -302,7 +302,7 @@ GEM actionpack (>= 5.2) railties (>= 5.2) rexml (3.4.1) - rubocop (1.75.8) + rubocop (1.76.0) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -310,10 +310,10 @@ GEM parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.44.0, < 2.0) + rubocop-ast (>= 1.45.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.44.1) + rubocop-ast (1.45.0) parser (>= 3.3.7.2) prism (~> 1.4) rubocop-capybara (2.22.1) From bd0ecd94527b47fb37c6579365b050cbdbc52979 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Fri, 6 Jun 2025 22:28:00 -0400 Subject: [PATCH 026/111] FIXUP: `FieldErrorsComponent` is the `data-pf-error-container` --- app/components/practical/views/form/error_list_component.rb | 2 +- app/components/practical/views/form/field_errors_component.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/practical/views/form/error_list_component.rb b/app/components/practical/views/form/error_list_component.rb index e301fe1..2ee587e 100644 --- a/app/components/practical/views/form/error_list_component.rb +++ b/app/components/practical/views/form/error_list_component.rb @@ -8,7 +8,7 @@ def initialize(errors:) end def call - tag.ul(data: {'data-pf-error-container': true}) { + tag.ul { safe_join(errors.map{|error| render Practical::Views::Form::ErrorListItemComponent.new(error: error) }) } end diff --git a/app/components/practical/views/form/field_errors_component.rb b/app/components/practical/views/form/field_errors_component.rb index 2ddeafd..9bf8989 100644 --- a/app/components/practical/views/form/field_errors_component.rb +++ b/app/components/practical/views/form/field_errors_component.rb @@ -19,7 +19,7 @@ def call errors = [] end - finalized_options = mix({id: id, class: classes}, options) + finalized_options = mix({id: id, class: classes, data: {'pf-error-container': true}}, options) return label(object_method, nil, finalized_options) { render Practical::Views::Form::ErrorListComponent.new(errors: errors) From 27a67dc163b09e3fb2f2a37a8bf91f0c946bdd81 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Fri, 6 Jun 2025 22:28:45 -0400 Subject: [PATCH 027/111] Bump to `3.0.0.pre.alpha3` --- Gemfile.lock | 2 +- lib/practical/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8e0e23a..dc3078f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - practical (3.0.0.pre.alpha2) + practical (3.0.0.pre.alpha3) action_policy devise-passkeys honeybadger diff --git a/lib/practical/version.rb b/lib/practical/version.rb index 373f121..d2d6149 100644 --- a/lib/practical/version.rb +++ b/lib/practical/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Practical - VERSION = "3.0.0-alpha2" + VERSION = "3.0.0-alpha3" end From 4ce798bb4c4b45fe209390c1169cbe86a30424f3 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Fri, 6 Jun 2025 23:20:59 -0400 Subject: [PATCH 028/111] `assert_toast_message` ignores visiblity of dialog message * Because of timing issues in selenium, we want to check for all visibility states; because the dialog message might not be present yet in the context that selenium is checking, but screenshots prove that the element is rendered --- app/lib/practical/test/helpers/system/assertions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/practical/test/helpers/system/assertions.rb b/app/lib/practical/test/helpers/system/assertions.rb index 0a03896..4bcecf3 100644 --- a/app/lib/practical/test/helpers/system/assertions.rb +++ b/app/lib/practical/test/helpers/system/assertions.rb @@ -3,7 +3,7 @@ module Practical::Test::Helpers::System::Assertions def assert_toast_message(text:) within(".notification-messages") do - assert_selector("dialog", text: text) + assert_selector("dialog", text: text, visible: :all) end end From 39421815023da239a469d7ad3b8e2d6757affc41 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Sat, 7 Jun 2025 00:20:49 -0400 Subject: [PATCH 029/111] Fix `Practical::Test::Helpers::Passkey::System::Selenium` `driven_by` * Do not have the browser open the devtools automatically if running in non-headless mode, since that seems to have significant flakiness in general * Use the `--headless=new` flag if `ENV.has_key?(HEADLESS_TESTS)` --- app/lib/practical/test/helpers/passkey/system/selenium.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/lib/practical/test/helpers/passkey/system/selenium.rb b/app/lib/practical/test/helpers/passkey/system/selenium.rb index 5114b25..32bd38d 100644 --- a/app/lib/practical/test/helpers/passkey/system/selenium.rb +++ b/app/lib/practical/test/helpers/passkey/system/selenium.rb @@ -24,7 +24,9 @@ def self.selenium_driver_key driven_by :selenium, using: selenium_driver_key, screen_size: [1400, 1400] do |options| options.accept_insecure_certs = true - options.args << "--auto-open-devtools-for-tabs" + if ENV.has_key?("HEADLESS_TESTS") + options.args << "--headless=new" + end end end From 45a0dd432ffdb65dfb0251feeb71a825e2a4d054 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Sat, 7 Jun 2025 00:38:10 -0400 Subject: [PATCH 030/111] Reduce flakiness in tests by not using faker data for passkey labels * Using a random hex value reduces the chances of collisions --- .../controllers/emergency_registration/base.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/lib/practical/test/shared/auth/passkeys/controllers/emergency_registration/base.rb b/app/lib/practical/test/shared/auth/passkeys/controllers/emergency_registration/base.rb index 4a1cc13..aa71fdd 100644 --- a/app/lib/practical/test/shared/auth/passkeys/controllers/emergency_registration/base.rb +++ b/app/lib/practical/test/shared/auth/passkeys/controllers/emergency_registration/base.rb @@ -84,7 +84,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati client = webauthn_client raw_credential = create_credential_and_return_payload_from_challenge(client: client, challenge: challenge) - label = Faker::Computer.os + label = SecureRandom.hex params = params_for_using_emergency_passkey_registration(label: label, raw_credential: raw_credential) @@ -125,7 +125,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati client = webauthn_client raw_credential = create_credential_and_return_payload_from_challenge(client: client, challenge: challenge) - label = Faker::Computer.os + label = SecureRandom.hex params = params_that_try_to_override_owner_during_emergency_registration(label: label, raw_credential: raw_credential) @@ -168,7 +168,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati client = webauthn_client raw_credential = create_credential_and_return_payload_from_challenge(client: client, challenge: challenge) - label = Faker::Computer.os + label = SecureRandom.hex params = params_for_using_emergency_passkey_registration(label: label, raw_credential: raw_credential) @@ -198,7 +198,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati client = webauthn_client raw_credential = create_credential_and_return_payload_from_challenge(client: client, challenge: challenge) - label = Faker::Computer.os + label = SecureRandom.hex params = params_for_using_emergency_passkey_registration(label: label, raw_credential: raw_credential) @@ -228,7 +228,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati client = webauthn_client raw_credential = create_credential_and_return_payload_from_challenge(client: client, challenge: challenge) - label = Faker::Computer.os + label = SecureRandom.hex params = params_for_using_emergency_passkey_registration(label: label, raw_credential: raw_credential) @@ -291,7 +291,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati client = webauthn_client raw_credential = create_credential_and_return_payload_from_challenge(client: client, challenge: challenge) - label = Faker::Computer.os + label = SecureRandom.hex params = params_for_using_emergency_passkey_registration(label: label, raw_credential: raw_credential) @@ -325,7 +325,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati client = webauthn_client create_credential_and_return_payload_from_challenge(client: client, challenge: challenge) - label = Faker::Computer.os + label = SecureRandom.hex params = params_for_using_emergency_passkey_registration(label: label, raw_credential: nil) @@ -359,7 +359,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati client = webauthn_client create_credential_and_return_payload_from_challenge(client: client, challenge: challenge) - label = Faker::Computer.os + label = SecureRandom.hex params = params_for_using_emergency_passkey_registration(label: label, raw_credential: "blah") From b000b28285038e23825f84dd453b70f7cb8add25 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Sat, 7 Jun 2025 00:38:55 -0400 Subject: [PATCH 031/111] Shared emergency passkey registration controller tests are agnostic * The tests no long explicitly require JSON rediretions for actions, since the implementation could have different requirements --- .../controllers/emergency_registration/base.rb | 12 ++++++------ .../emergency_registration/self_service.rb | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/lib/practical/test/shared/auth/passkeys/controllers/emergency_registration/base.rb b/app/lib/practical/test/shared/auth/passkeys/controllers/emergency_registration/base.rb index aa71fdd..4ae781a 100644 --- a/app/lib/practical/test/shared/auth/passkeys/controllers/emergency_registration/base.rb +++ b/app/lib/practical/test/shared/auth/passkeys/controllers/emergency_registration/base.rb @@ -90,7 +90,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati assert_difference "#{passkey_class}.count", +1 do use_emergency_registration_action(token: token, params: params) - assert_json_redirected_to expected_new_session_url + assert_successful_use_redirection end credential = hydrate_response_from_raw_credential(client: client, relying_party: webauthn_relying_party, @@ -132,7 +132,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati assert_difference "#{passkey_class}.count", +1 do use_emergency_registration_action(token: token, params: params) - assert_json_redirected_to expected_new_session_url + assert_successful_use_redirection end credential = hydrate_response_from_raw_credential(client: client, relying_party: webauthn_relying_party, @@ -237,7 +237,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati end end - test "use: returns a unprocessable_entity with the PracticalFramework error JSON if the passkey label is missing" do + test "use: returns a unprocessable_entity with a form error if the passkey label is missing" do emergency_passkey_registration = valid_emergency_registration assert_nil emergency_passkey_registration.used_at @@ -270,7 +270,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati assert_form_error_for_label(message: "can't be blank", type: :blank) end - test "use: returns a unprocessable_entity with the PracticalFramework error JSON if the passkey challenge fails" do + test "use: returns a unprocessable_entity with a form error if the passkey challenge fails" do emergency_passkey_registration = valid_emergency_registration assert_nil emergency_passkey_registration.used_at @@ -304,7 +304,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati assert_form_error_for_credential(message: I18n.translate("devise.emergency_passkey_registrations.webauthn_challenge_verification_error")) end - test "use: returns a unprocessable_entity with the PracticalFramework error JSON if the credential was missing" do + test "use: returns a unprocessable_entity with a form error if the credential was missing" do emergency_passkey_registration = valid_emergency_registration assert_nil emergency_passkey_registration.used_at @@ -338,7 +338,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati assert_form_error_for_credential(message: I18n.translate("devise.emergency_passkey_registrations.credential_missing_or_could_not_be_parsed")) end - test "use: returns a unprocessable_entity with the PracticalFramework error JSON if the credential could not be parsed" do + test "use: returns a unprocessable_entity with a form error if the credential could not be parsed" do emergency_passkey_registration = valid_emergency_registration assert_nil emergency_passkey_registration.used_at diff --git a/app/lib/practical/test/shared/auth/passkeys/controllers/emergency_registration/self_service.rb b/app/lib/practical/test/shared/auth/passkeys/controllers/emergency_registration/self_service.rb index 8b41755..7972e9f 100644 --- a/app/lib/practical/test/shared/auth/passkeys/controllers/emergency_registration/self_service.rb +++ b/app/lib/practical/test/shared/auth/passkeys/controllers/emergency_registration/self_service.rb @@ -27,7 +27,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati request_emergency_registration_action(params: params, env: env) - assert_json_redirected_to expected_new_session_url + assert_registration_sent_redirection assert_flash_message( type: :notice, message: I18n.translate('emergency_passkey_registrations.sent_message'), @@ -53,7 +53,7 @@ module Practical::Test::Shared::Auth::Passkeys::Controllers::EmergencyRegistrati request_emergency_registration_action(params: params, env: env) - assert_json_redirected_to expected_new_session_url + assert_registration_sent_redirection assert_flash_message( type: :notice, message: I18n.translate('emergency_passkey_registrations.sent_message'), From fa166d1208cf664a3065031e7bb89fe12e9e2a6d Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Sat, 7 Jun 2025 01:18:50 -0400 Subject: [PATCH 032/111] `Practical::Test::Helpers::Setup::Debug` only debug prints if included --- app/lib/practical/test/helpers/setup/debug.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/lib/practical/test/helpers/setup/debug.rb b/app/lib/practical/test/helpers/setup/debug.rb index eda984f..cadb2ab 100644 --- a/app/lib/practical/test/helpers/setup/debug.rb +++ b/app/lib/practical/test/helpers/setup/debug.rb @@ -1,8 +1,11 @@ # frozen_string_literal: true module Practical::Test::Helpers::Setup::Debug - # rubocop:disable Rails/Output - puts "MINITEST_PARALLEL_EXECUTOR_SIZE: #{Minitest.parallel_executor.size}" - puts "PARALLEL_WORKERS: #{ENV["PARALLEL_WORKERS"]}" - # rubocop:enable Rails/Output + extend ActiveSupport::Concern + included do + # rubocop:disable Rails/Output + puts "MINITEST_PARALLEL_EXECUTOR_SIZE: #{Minitest.parallel_executor.size}" + puts "PARALLEL_WORKERS: #{ENV["PARALLEL_WORKERS"]}" + # rubocop:enable Rails/Output + end end \ No newline at end of file From 0d1b4085836900e0a489cc9e375b36d20665383f Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Sat, 21 Jun 2025 15:42:22 -0400 Subject: [PATCH 033/111] Fix `Practical::Views::Navigation::PaginationComponent` i18n lookup * Do not look for the `v2_` versions of the prev/next keys --- .../practical/views/navigation/pagination_component.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/practical/views/navigation/pagination_component.rb b/app/components/practical/views/navigation/pagination_component.rb index 1a235cc..7d84b05 100644 --- a/app/components/practical/views/navigation/pagination_component.rb +++ b/app/components/practical/views/navigation/pagination_component.rb @@ -39,7 +39,7 @@ def previous_item text = icon_text( icon: icon_set.previous_arrow, - text: pagy_t('pagy.nav.v2_prev') + text: pagy_t('pagy.nav.prev') ) tag.div(class: classes, role: :listitem){ @@ -58,7 +58,7 @@ def next_item text = icon_text( icon: icon_set.next_arrow, - text: pagy_t('pagy.nav.v2_next') + text: pagy_t('pagy.nav.next') ) tag.div(class: classes, role: :listitem){ From cc30762c3a42ccdee8735a99656f3d7000c0561e Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Sat, 21 Jun 2025 15:42:55 -0400 Subject: [PATCH 034/111] Fix `Practical::Views::Navigation::Pagination::GotoFormComponent` * Use `Practical::Views::FormWrapper` --- .../pagination/goto_form_component.html.erb | 8 +------- .../navigation/pagination/goto_form_component.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/components/practical/views/navigation/pagination/goto_form_component.html.erb b/app/components/practical/views/navigation/pagination/goto_form_component.html.erb index 548a846..ba3cbe5 100644 --- a/app/components/practical/views/navigation/pagination/goto_form_component.html.erb +++ b/app/components/practical/views/navigation/pagination/goto_form_component.html.erb @@ -9,13 +9,7 @@ <% end %>