<% if entry.account.investment? && !transaction.transfer? %>
<%# For investment accounts, show activity label instead of category %>
<%= render "investment_activity/quick_edit_badge", entry: entry, entryable: transaction %>
diff --git a/app/views/transactions/_transaction_category.html.erb b/app/views/transactions/_transaction_category.html.erb
index 3b5c8d1b9..434e422af 100644
--- a/app/views/transactions/_transaction_category.html.erb
+++ b/app/views/transactions/_transaction_category.html.erb
@@ -1,6 +1,6 @@
<%# locals: (transaction:, variant:) %>
-
">
+
" class="min-w-0 overflow-hidden">
<% if transaction.transfer&.categorizable? || transaction.transfer.nil? %>
<%= render "categories/menu", transaction: transaction %>
<% else %>
diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb
index 313c12efe..770b6256e 100644
--- a/test/application_system_test_case.rb
+++ b/test/application_system_test_case.rb
@@ -2,6 +2,9 @@ require "test_helper"
require "socket"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ DEFAULT_VIEWPORT_WIDTH = 1400
+ DEFAULT_VIEWPORT_HEIGHT = 1400
+
setup do
Capybara.default_max_wait_time = 5
@@ -14,6 +17,8 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
Capybara.always_include_port = true
Capybara.app_host = "http://#{app_host}:#{server_port}"
end
+
+ reset_viewport
end
if ENV["SELENIUM_REMOTE_URL"].present?
@@ -34,8 +39,17 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: ENV["CI"].present? ? :headless_chrome : ENV.fetch("E2E_BROWSER", :chrome).to_sym, screen_size: [ 1400, 1400 ]
end
+ def teardown
+ reset_viewport
+ super
+ end
+
private
+ def reset_viewport
+ page.current_window.resize_to(DEFAULT_VIEWPORT_WIDTH, DEFAULT_VIEWPORT_HEIGHT) if page&.current_window
+ end
+
def sign_in(user)
visit new_session_path
within %(form[action='#{sessions_path}']) do
diff --git a/test/controllers/accounts_controller_test.rb b/test/controllers/accounts_controller_test.rb
index 74514a68c..cda1e81e7 100644
--- a/test/controllers/accounts_controller_test.rb
+++ b/test/controllers/accounts_controller_test.rb
@@ -50,6 +50,31 @@ class AccountsControllerTest < ActionDispatch::IntegrationTest
assert_select "a[href*='page=2'][href*='tab=holdings']", count: 0
end
+ test "account activity constrains long category labels before the amount on wide screens" do
+ category = categories(:food_and_drink)
+ category.update!(name: "Super Long Category Name That Should Stop Before The Amount On Wide Screens Too")
+
+ entry = @account.entries.create!(
+ name: "Wide category verification",
+ date: Date.current,
+ amount: 187.65,
+ currency: @account.currency,
+ entryable: Transaction.new(category: category)
+ )
+
+ get account_url(@account, tab: "activity")
+
+ assert_response :success
+ assert_select "##{dom_id(entry.entryable, "category_menu_desktop")}"
+ assert_select "##{dom_id(entry.entryable, "category_menu_desktop")}.min-w-0"
+ assert_select "##{dom_id(entry.entryable, "category_menu_desktop")}.overflow-hidden"
+ assert_select "##{dom_id(entry.entryable, "category_menu_desktop")} button.block"
+ assert_select "##{dom_id(entry.entryable, "category_menu_desktop")} button.w-full"
+ assert_select "##{dom_id(entry.entryable, "category_menu_desktop")} button.overflow-hidden"
+ assert_select "##{dom_id(entry.entryable, "category_menu_desktop")} [data-testid='category-name']"
+ assert_select "div.hidden.md\\:flex.min-w-0"
+ end
+
test "should sync account" do
post sync_account_url(@account)
assert_redirected_to account_url(@account)
diff --git a/test/controllers/categories_controller_test.rb b/test/controllers/categories_controller_test.rb
index 2f7c8ff5e..33bdf6641 100644
--- a/test/controllers/categories_controller_test.rb
+++ b/test/controllers/categories_controller_test.rb
@@ -4,11 +4,15 @@ class CategoriesControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in users(:family_admin)
@transaction = transactions :one
+ ensure_tailwind_build
end
test "index" do
get categories_url
assert_response :success
+ assert_select "#category_#{categories(:food_and_drink).id} > [data-testid='category-content']", count: 1
+ assert_select "#category_#{categories(:food_and_drink).id} > [data-testid='category-actions']", count: 1
+ assert_select "#category_#{categories(:food_and_drink).id} [data-testid='category-name']", text: categories(:food_and_drink).name
end
test "new" do
diff --git a/test/controllers/category/deletions_controller_test.rb b/test/controllers/category/deletions_controller_test.rb
index c8bd3d37c..4cc5c166d 100644
--- a/test/controllers/category/deletions_controller_test.rb
+++ b/test/controllers/category/deletions_controller_test.rb
@@ -4,11 +4,14 @@ class Category::DeletionsControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in users(:family_admin)
@category = categories(:food_and_drink)
+ ensure_tailwind_build
end
test "new" do
get new_category_deletion_url(@category)
assert_response :success
+ assert_select "turbo-frame#modal"
+ assert_select "turbo-frame#modal button span.min-w-0.truncate", text: /Delete "Food & Drink" and leave uncategorized/
end
test "create with replacement" do
diff --git a/test/controllers/coinstats_items_controller_test.rb b/test/controllers/coinstats_items_controller_test.rb
index a3bfc24e0..13c1d7d4e 100644
--- a/test/controllers/coinstats_items_controller_test.rb
+++ b/test/controllers/coinstats_items_controller_test.rb
@@ -9,9 +9,7 @@ class CoinstatsItemsControllerTest < ActionDispatch::IntegrationTest
name: "Test CoinStats Connection",
api_key: "test_api_key_123"
)
- tailwind_build = Rails.root.join("app/assets/builds/tailwind.css")
- FileUtils.mkdir_p(tailwind_build.dirname)
- File.write(tailwind_build, "/* test */") unless tailwind_build.exist?
+ ensure_tailwind_build
end
# Helper to wrap data in Provider::Response
diff --git a/test/system/account_activity_test.rb b/test/system/account_activity_test.rb
index 5349b8b10..142314324 100644
--- a/test/system/account_activity_test.rb
+++ b/test/system/account_activity_test.rb
@@ -1,8 +1,13 @@
require "application_system_test_case"
class AccountActivityTest < ApplicationSystemTestCase
+ DEFAULT_VIEWPORT_WIDTH = 1400
+ DEFAULT_VIEWPORT_HEIGHT = 1400
+
setup do
+ ensure_tailwind_build
sign_in users(:family_admin)
+ reset_viewport
@account = accounts(:depository)
@transaction_entry = @account.entries.create!(
@@ -10,7 +15,7 @@ class AccountActivityTest < ApplicationSystemTestCase
date: Date.current,
amount: 42.50,
currency: "USD",
- entryable: Transaction.new
+ entryable: Transaction.new(category: categories(:food_and_drink))
)
@valuation_entry = @account.entries.create!(
name: "Current balance",
@@ -40,4 +45,74 @@ class AccountActivityTest < ApplicationSystemTestCase
assert_selector "a[title='Duplicate'].hidden", visible: false
end
end
+
+ test "account activity keeps long category names from overflowing the amount on mobile" do
+ category = categories(:food_and_drink)
+ category.update!(name: "Super Long Category Name That Should Stop Before The Amount On Mobile")
+
+ page.current_window.resize_to(315, 643)
+
+ visit account_url(@account, tab: "activity")
+
+ row = find("##{dom_id(@transaction_entry)}")
+ amount = row.find("p.privacy-sensitive", visible: true)
+ category_name = row.find("#category_name_mobile_#{@transaction_entry.entryable.id}", visible: true)
+
+ assert amount.visible?
+ assert category_name.visible?
+
+ row_rect = row.native.rect
+ amount_rect = amount.native.rect
+ viewport_width = page.evaluate_script("window.innerWidth")
+ page_scroll_width = page.evaluate_script("document.documentElement.scrollWidth")
+
+ assert_operator amount_rect.x + amount_rect.width, :<=, row_rect.x + row_rect.width
+ assert_operator page_scroll_width, :<=, viewport_width
+ end
+
+ test "account activity keeps long category names from overlapping the amount on wide screens" do
+ category = categories(:food_and_drink)
+ category.update!(name: "Super Long Category Name That Should Stop Before The Amount On Wide Screens Too")
+
+ page.current_window.resize_to(1280, 900)
+
+ visit account_url(@account, tab: "activity")
+
+ metrics = page.evaluate_script(<<~JS)
+ (() => {
+ const row = document.getElementById("#{dom_id(@transaction_entry)}");
+ const categoryButton = row.querySelector("##{dom_id(@transaction_entry.entryable, "category_menu_desktop")} button");
+ const categoryName = categoryButton.querySelector("[data-testid='category-name']");
+ const amount = row.querySelector(".privacy-sensitive");
+ const categoryRect = categoryButton.getBoundingClientRect();
+ const amountRect = amount.getBoundingClientRect();
+
+ return {
+ categoryRight: categoryRect.right,
+ amountLeft: amountRect.left,
+ categoryOverflow: categoryName.scrollWidth > categoryName.clientWidth
+ };
+ })()
+ JS
+
+ assert_operator metrics["categoryRight"], :<=, metrics["amountLeft"]
+ assert metrics["categoryOverflow"]
+ end
+
+ private
+ def ensure_tailwind_build
+ return if self.class.instance_variable_defined?(:@tailwind_css_built)
+
+ system({ "RAILS_ENV" => "test" }, "bin/rails", "tailwindcss:build", exception: true)
+ self.class.instance_variable_set(:@tailwind_css_built, true)
+ end
+
+ def teardown
+ reset_viewport
+ super
+ end
+
+ def reset_viewport
+ page.current_window.resize_to(DEFAULT_VIEWPORT_WIDTH, DEFAULT_VIEWPORT_HEIGHT) if page&.current_window
+ end
end
diff --git a/test/system/categories_test.rb b/test/system/categories_test.rb
index 5e92f1ad4..5a5bf1732 100644
--- a/test/system/categories_test.rb
+++ b/test/system/categories_test.rb
@@ -23,4 +23,23 @@ class CategoriesTest < ApplicationSystemTestCase
assert_text "Name has already been taken"
end
+
+ test "long category names truncate before the actions menu on mobile" do
+ category = categories(:food_and_drink)
+ category.update!(name: "Super Long Category Name That Should Stop Before The Menu Button On Mobile")
+
+ page.current_window.resize_to(315, 643)
+
+ visit categories_url
+
+ row = find("##{ActionView::RecordIdentifier.dom_id(category)}")
+ actions = row.find("[data-testid='category-actions'] button", visible: true)
+
+ assert actions.visible?
+
+ viewport_width = page.evaluate_script("window.innerWidth")
+ page_scroll_width = page.evaluate_script("document.documentElement.scrollWidth")
+
+ assert_operator page_scroll_width, :<=, viewport_width
+ end
end
diff --git a/test/system/property_test.rb b/test/system/property_test.rb
index 769c338f4..b0a15f35d 100644
--- a/test/system/property_test.rb
+++ b/test/system/property_test.rb
@@ -15,11 +15,7 @@ class PropertiesEditTest < ApplicationSystemTestCase
click_link "[system test] Property Account"
find("[data-testid='account-menu']").click
click_on "Edit"
- assert_selector "#account_accountable_attributes_subtype"
- assert_selector(
- "#account_accountable_attributes_subtype option[selected]",
- text: "Single Family Home"
- )
+ assert_equal "single_family_home", find("#account_accountable_attributes_subtype").value
end
private
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 5af3466ff..049641229 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -81,6 +81,12 @@ module ActiveSupport
post sessions_path, params: { email: user.email, password: user_password_test }
end
+ def ensure_tailwind_build
+ tailwind_build = Rails.root.join("app/assets/builds/tailwind.css")
+ FileUtils.mkdir_p(tailwind_build.dirname)
+ File.write(tailwind_build, "/* test */") unless tailwind_build.exist?
+ end
+
def with_env_overrides(overrides = {}, &block)
ClimateControl.modify(**overrides, &block)
end