mitgliederverwaltung/test/mv_web/live/global_settings_live_test.exs
Simon 2bb01bd201
All checks were successful
continuous-integration/drone/push Build is passing
Improve UX of join requests and fix minor bugs (#492)
## Description of the implemented changes
The changes were:
- [x] Bugfixing
- [x] New Feature
- [ ] Breaking Change
- [ ] Refactoring

This PR improves the join-request flow and presentation quality, fixes several data-display issues in join/join-request screens, and adds a usability improvement in global settings (directly opening the join link). It also includes dependency updates and changelog maintenance.

## What has been changed?
- Join form (`JoinLive`) now renders inputs based on actual field types (including checkbox/date/number/email behavior instead of generic text-only handling).
- Join form custom-field labels are resolved from configured custom fields (fallback remains safe if lookup fails).
- Join-request details page (`JoinRequestLive.Show`) now:
  - resolves and shows custom field names instead of raw IDs,
  - formats boolean-like values (`on/true/1`, `off/false/0`) as localized `Yes/No`,
  - formats ISO date strings for better readability,
  - keeps legacy field handling while improving output consistency.
- Join-request detail layout was improved semantically and visually (`dl/dt/dd` structure for label/value rows).
- Global settings page now includes an **Open** button for the join URL (`target="_blank"`, `rel="noopener noreferrer"`, ARIA label).
- Added/updated tests around:
  - join field type rendering,
  - custom field labels in join-request views,
  - related auth/global-settings behavior.
- Updated translations (`default.pot`, `en`, `de`) for new UI strings.
- Updated dependencies/tooling (`mix.lock`, `mix.exs`, CI/renovate-related updates).
- Updated `CHANGELOG.md` entries for unreleased changes.

## Definition of Done
### Code Quality
- [x] No new technical depths
- [x] Linting passed
- [x] Documentation is added were needed

### Accessibility
- [x] New elements are properly defined with html-tags
- [x] Colour contrast follows WCAG criteria
- [x] Aria labels are added when needed
- [x] Everything is accessible by keyboard
- [x] Tab-Order is comprehensible
- [x] All interactive elements have a visible focus

### Testing
- [x] Tests for new code are written
- [ ] All tests pass
- [ ] axe-core dev tools show no critical or major issues

## Additional Notes
- Reviewer focus areas:
  - `lib/mv_web/live/join_live.ex`: input type derivation and custom field lookup strategy (`authorize?: false` read path used intentionally for field metadata).
  - `lib/mv_web/live/join_request_live/show.ex`: value-formatting logic (especially backward compatibility for legacy `form_data` payloads).
  - `lib/mv_web/live/global_settings_live.ex`: external-link behavior and accessibility attributes.
- The branch also contains dependency update commits; please review lockfile and CI-related changes separately from functional join/join-request changes.

Reviewed-on: #492
Co-authored-by: Simon <s.thiessen@local-it.org>
Co-committed-by: Simon <s.thiessen@local-it.org>
2026-05-06 14:34:42 +02:00

193 lines
6.6 KiB
Elixir

defmodule MvWeb.GlobalSettingsLiveTest do
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
alias Mv.Membership
describe "Global Settings LiveView" do
setup %{conn: conn} do
user = create_test_user(%{email: "admin@example.com"})
conn = conn_with_oidc_user(conn, user)
{:ok, conn: conn, user: user}
end
test "renders the global settings page", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/settings")
assert html =~ "Club Settings"
assert html =~ "Settings"
end
test "displays current club name", %{conn: conn} do
# Set initial club name
{:ok, settings} = Membership.get_settings()
{:ok, _updated} = Membership.update_settings(settings, %{club_name: "Test Club"})
{:ok, _view, html} = live(conn, ~p"/settings")
assert html =~ "Test Club"
end
test "can update club name via form", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/settings")
# Submit form with new club name
assert view
|> form("#settings-form", %{setting: %{club_name: "Updated Club Name"}})
|> render_submit()
# Check for success message
assert render(view) =~ "Settings updated successfully"
assert render(view) =~ "Updated Club Name"
end
test "shows error when club_name is empty", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/settings")
# Submit form with empty club name
html =
view
|> form("#settings-form", %{setting: %{club_name: ""}})
|> render_submit()
assert html =~ "must be present"
end
test "shows error when club_name is missing", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/settings")
# Submit form with club_name explicitly set to empty string
# (Phoenix forms will keep existing value if field is omitted)
html =
view
|> form("#settings-form", %{setting: %{club_name: ""}})
|> render_submit()
assert html =~ "must be present"
end
test "shows open button for join page URL in same row as copy", %{conn: conn} do
{:ok, settings} = Membership.get_settings()
{:ok, _} = Membership.update_settings(settings, %{join_form_enabled: true})
{:ok, view, _html} = live(conn, ~p"/settings")
assert has_element?(view, "#copy-join-url-btn")
assert has_element?(
view,
"a[href][target=\"_blank\"][rel=\"noopener noreferrer\"]",
"Open"
)
end
end
describe "SMTP / E-Mail section" do
setup %{conn: conn} do
user = create_test_user(%{email: "admin@example.com"})
conn = conn_with_oidc_user(conn, user)
{:ok, conn: conn, user: user}
end
test "renders SMTP section with host/port fields and test email area", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/settings")
# Section title (Gettext key: SMTP or E-Mail per concept)
assert html =~ "SMTP" or html =~ "E-Mail"
end
test "shows Send test email button when SMTP is configured", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/settings")
# When Mv.Config.smtp_configured?() is true, button and recipient input should be present
# In test env SMTP is typically not configured; we only assert the section exists
html = render(view)
assert html =~ "SMTP" or html =~ "E-Mail"
end
test "send test email with valid address shows success or error result", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/settings")
if has_element?(view, "[data-testid='smtp-test-email-form']") do
# Submit the test-email form (phx-submit) with a valid recipient address
view
|> form("[data-testid='smtp-test-email-form']", %{"to_email" => "test@example.com"})
|> render_submit()
# Result area must appear regardless of success or error
assert has_element?(view, "[data-testid='smtp-test-result']")
else
assert render(view) =~ "Settings"
end
end
test "shows warning when SMTP is not configured in production", %{conn: conn} do
# Concept: in prod, show warning "SMTP is not configured. Transactional emails..."
# In test we only check that the section exists; warning visibility is env-dependent
{:ok, _view, html} = live(conn, ~p"/settings")
assert html =~ "SMTP" or html =~ "E-Mail" or html =~ "Settings"
end
end
describe "Authentication section when OIDC-only is enabled" do
setup %{conn: conn} do
user = create_test_user(%{email: "admin@example.com"})
conn = conn_with_oidc_user(conn, user)
{:ok, settings} = Membership.get_settings()
original_oidc_only = Map.get(settings, :oidc_only, false)
{:ok, _} = Membership.update_settings(settings, %{oidc_only: true})
{:ok, conn: conn, original_oidc_only: original_oidc_only}
end
@describetag :ui
test "registration checkbox is disabled when OIDC-only is enabled", %{
conn: conn,
original_oidc_only: original
} do
try do
{:ok, view, _html} = live(conn, ~p"/settings")
assert has_element?(view, "#registration-enabled-checkbox[disabled]")
after
{:ok, s} = Membership.get_settings()
Membership.update_settings(s, %{oidc_only: original})
end
end
@describetag :ui
test "OIDC-only hint is visible when OIDC-only is enabled", %{
conn: conn,
original_oidc_only: original
} do
try do
{:ok, view, _html} = live(conn, ~p"/settings")
assert has_element?(view, "[data-testid='oidc-only-registration-hint']")
after
{:ok, s} = Membership.get_settings()
Membership.update_settings(s, %{oidc_only: original})
end
end
test "when OIDC-only is disabled, registration checkbox is enabled and can be toggled", %{
conn: conn,
original_oidc_only: original
} do
try do
{:ok, settings} = Membership.get_settings()
Membership.update_settings(settings, %{oidc_only: false})
{:ok, view, _html} = live(conn, ~p"/settings")
refute has_element?(view, "#registration-enabled-checkbox[disabled]")
initial_checked =
view |> element("#registration-enabled-checkbox") |> render() =~ "checked"
view
|> element("#registration-enabled-checkbox")
|> render_click()
new_checked = view |> element("#registration-enabled-checkbox") |> render() =~ "checked"
assert new_checked != initial_checked
after
{:ok, s} = Membership.get_settings()
Membership.update_settings(s, %{oidc_only: original})
end
end
end
end