defmodule ToweropsWeb.UserSettingsLive.HelpersTest do use ExUnit.Case, async: true alias ToweropsWeb.UserSettingsLive.Helpers describe "current_session?/2" do test "returns true when session token matches current token" do session = %{user_token_id: "abc123"} assert Helpers.current_session?(session, "abc123") end test "returns false when session token does not match" do session = %{user_token_id: "abc123"} refute Helpers.current_session?(session, "different-token") end test "returns false when current token is nil" do session = %{user_token_id: "abc123"} refute Helpers.current_session?(session, nil) end test "returns true when both are nil" do session = %{user_token_id: nil} assert Helpers.current_session?(session, nil) end end describe "format_timestamp_in_timezone/2" do test "formats datetime in the given timezone" do {:ok, datetime} = DateTime.new(~D[2025-03-15], ~T[14:30:00], "Etc/UTC") result = Helpers.format_timestamp_in_timezone(datetime, "America/New_York") assert result =~ "Mar 15, 2025" assert result =~ "EDT" or result =~ "EST" end test "formats datetime in UTC with 24h default" do {:ok, datetime} = DateTime.new(~D[2025-06-20], ~T[08:15:00], "Etc/UTC") result = Helpers.format_timestamp_in_timezone(datetime, "Etc/UTC") assert result =~ "Jun 20, 2025" assert result =~ "08:15:00" assert result =~ "UTC" end test "formats datetime in 12h format when specified" do {:ok, datetime} = DateTime.new(~D[2025-06-20], ~T[08:15:00], "Etc/UTC") result = Helpers.format_timestamp_in_timezone(datetime, "Etc/UTC", "12h") assert result =~ "Jun 20, 2025" assert result =~ "08:15 AM" assert result =~ "UTC" end test "falls back to UTC for invalid timezone" do {:ok, datetime} = DateTime.new(~D[2025-01-10], ~T[12:00:00], "Etc/UTC") result = Helpers.format_timestamp_in_timezone(datetime, "Invalid/Zone") assert result =~ "Jan 10, 2025" assert result =~ "UTC" end test "formats PM times correctly in 24h format" do {:ok, datetime} = DateTime.new(~D[2025-12-25], ~T[23:45:00], "Etc/UTC") result = Helpers.format_timestamp_in_timezone(datetime, "Etc/UTC") assert result =~ "Dec 25, 2025" assert result =~ "23:45:00" end test "formats PM times correctly in 12h format" do {:ok, datetime} = DateTime.new(~D[2025-12-25], ~T[23:45:00], "Etc/UTC") result = Helpers.format_timestamp_in_timezone(datetime, "Etc/UTC", "12h") assert result =~ "Dec 25, 2025" assert result =~ "11:45 PM" end test "formats midnight correctly in 24h format" do {:ok, datetime} = DateTime.new(~D[2025-07-04], ~T[00:00:00], "Etc/UTC") result = Helpers.format_timestamp_in_timezone(datetime, "Etc/UTC") assert result =~ "Jul 04, 2025" assert result =~ "00:00:00" end end describe "format_browser_info/1" do test "returns device_name when browser_name is nil and device_name is set" do session = %{browser_name: nil, browser_version: nil, os_name: nil, device_name: "iPhone 15"} assert Helpers.format_browser_info(session) == "iPhone 15" end test "returns 'Unknown Browser' when browser_name and device_name are nil" do session = %{browser_name: nil, browser_version: nil, os_name: nil, device_name: nil} assert Helpers.format_browser_info(session) == "Unknown Browser" end test "returns browser name only when version and os are nil" do session = %{browser_name: "Firefox", browser_version: nil, os_name: nil, device_name: nil} assert Helpers.format_browser_info(session) == "Firefox" end test "returns browser with version when os is nil" do session = %{browser_name: "Chrome", browser_version: "120", os_name: nil, device_name: nil} assert Helpers.format_browser_info(session) == "Chrome 120" end test "returns browser on os when version is nil" do session = %{browser_name: "Safari", browser_version: nil, os_name: "macOS", device_name: nil} assert Helpers.format_browser_info(session) == "Safari on macOS" end test "returns browser version on os when all present" do session = %{ browser_name: "Chrome", browser_version: "120", os_name: "Windows 11", device_name: nil } assert Helpers.format_browser_info(session) == "Chrome 120 on Windows 11" end test "prioritizes device_name over os/version when browser_name is nil" do session = %{ browser_name: nil, browser_version: "1.0", os_name: "iOS", device_name: "iPad Pro" } assert Helpers.format_browser_info(session) == "iPad Pro" end end describe "format_location/1" do test "returns full location with city, state, and country" do record = %{city_name: "Portland", subdivision_1_name: "Oregon", country_name: "United States"} assert Helpers.format_location(record) == "Portland, Oregon, United States" end test "returns city and country when state is nil" do record = %{city_name: "London", subdivision_1_name: nil, country_name: "United Kingdom"} assert Helpers.format_location(record) == "London, United Kingdom" end test "returns country only when city and state are nil" do record = %{city_name: nil, subdivision_1_name: nil, country_name: "Japan"} assert Helpers.format_location(record) == "Japan" end test "returns 'Unknown Location' when all fields are nil" do record = %{city_name: nil, subdivision_1_name: nil, country_name: nil} assert Helpers.format_location(record) == "Unknown Location" end test "returns 'Unknown Location' for state only (no city or country)" do record = %{city_name: nil, subdivision_1_name: "California", country_name: nil} assert Helpers.format_location(record) == "Unknown Location" end test "returns 'Unknown Location' for city and state without country" do record = %{city_name: "Portland", subdivision_1_name: "Oregon", country_name: nil} assert Helpers.format_location(record) == "Unknown Location" end end describe "format_relative_time/1" do test "returns 'Just now' for times less than 60 seconds ago" do datetime = DateTime.utc_now() assert Helpers.format_relative_time(datetime) == "Just now" end test "returns 'Just now' for time 30 seconds ago" do datetime = DateTime.add(DateTime.utc_now(), -30, :second) assert Helpers.format_relative_time(datetime) == "Just now" end test "returns minutes ago for times between 60 seconds and 1 hour" do datetime = DateTime.add(DateTime.utc_now(), -300, :second) assert Helpers.format_relative_time(datetime) == "5 minutes ago" end test "returns 1 minutes ago for times just over 60 seconds" do datetime = DateTime.add(DateTime.utc_now(), -90, :second) assert Helpers.format_relative_time(datetime) == "1 minutes ago" end test "returns hours ago for times between 1 hour and 24 hours" do datetime = DateTime.add(DateTime.utc_now(), -7200, :second) assert Helpers.format_relative_time(datetime) == "2 hours ago" end test "returns days ago for times between 1 day and 7 days" do datetime = DateTime.add(DateTime.utc_now(), -259_200, :second) assert Helpers.format_relative_time(datetime) == "3 days ago" end test "returns formatted date for times older than 7 days" do {:ok, datetime} = DateTime.new(~D[2024-01-15], ~T[10:00:00], "Etc/UTC") result = Helpers.format_relative_time(datetime) assert result =~ "Jan 15, 2024" end end describe "login_method_info/1" do test "returns lock icon and 'Password' for password method" do assert Helpers.login_method_info("password") == {"hero-lock-closed", "Password"} end test "returns envelope icon and 'Magic Link' for magic_link method" do assert Helpers.login_method_info("magic_link") == {"hero-envelope", "Magic Link"} end test "returns phone icon and 'Mobile QR' for mobile_qr method" do assert Helpers.login_method_info("mobile_qr") == {"hero-device-phone-mobile", "Mobile QR"} end test "returns question mark icon and method string for unknown methods" do assert Helpers.login_method_info("sso") == {"hero-question-mark-circle", "sso"} end test "returns the raw method string as label for unknown methods" do assert Helpers.login_method_info("webauthn") == {"hero-question-mark-circle", "webauthn"} end end describe "pagination_range/2" do test "returns all pages when total is 7 or fewer" do assert Helpers.pagination_range(1, 5) == [1, 2, 3, 4, 5] assert Helpers.pagination_range(3, 7) == [1, 2, 3, 4, 5, 6, 7] assert Helpers.pagination_range(1, 1) == [1] end test "returns start-biased range when current page is near the start" do assert Helpers.pagination_range(1, 20) == [1, 2, 3, 4, 5, :ellipsis, 20] assert Helpers.pagination_range(3, 20) == [1, 2, 3, 4, 5, :ellipsis, 20] assert Helpers.pagination_range(4, 20) == [1, 2, 3, 4, 5, :ellipsis, 20] end test "returns end-biased range when current page is near the end" do assert Helpers.pagination_range(20, 20) == [1, :ellipsis, 16, 17, 18, 19, 20] assert Helpers.pagination_range(18, 20) == [1, :ellipsis, 16, 17, 18, 19, 20] assert Helpers.pagination_range(17, 20) == [1, :ellipsis, 16, 17, 18, 19, 20] end test "returns middle range with ellipsis on both sides" do assert Helpers.pagination_range(10, 20) == [1, :ellipsis, 9, 10, 11, :ellipsis, 20] assert Helpers.pagination_range(5, 10) == [1, :ellipsis, 4, 5, 6, :ellipsis, 10] end test "handles boundary between start and middle" do # Page 5 of 10 should be in the middle range assert Helpers.pagination_range(5, 10) == [1, :ellipsis, 4, 5, 6, :ellipsis, 10] end test "handles boundary between middle and end" do # Page 7 of 10 should be near the end assert Helpers.pagination_range(7, 10) == [1, :ellipsis, 6, 7, 8, 9, 10] end test "handles 8 total pages with page 1 selected" do assert Helpers.pagination_range(1, 8) == [1, 2, 3, 4, 5, :ellipsis, 8] end test "handles 8 total pages with page 8 selected" do assert Helpers.pagination_range(8, 8) == [1, :ellipsis, 4, 5, 6, 7, 8] end end end