defmodule BerrypodWeb.Admin.EmailSettingsTest do use BerrypodWeb.ConnCase, async: false import Phoenix.LiveViewTest import Berrypod.AccountsFixtures alias Berrypod.{Mailer, Settings} setup do # Ensure mailer starts as test adapter and restore on exit original = Application.get_env(:berrypod, Berrypod.Mailer) Application.put_env(:berrypod, Berrypod.Mailer, adapter: Swoosh.Adapters.Test) on_exit(fn -> Application.put_env(:berrypod, Berrypod.Mailer, original) end) user = user_fixture() %{user: user} end describe "authenticated" do setup %{conn: conn, user: user} do conn = log_in_user(conn, user) %{conn: conn} end test "renders email settings page with provider cards", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/admin/settings/email") assert html =~ "Email settings" assert html =~ "Choose a provider" # Provider names rendered as radio cards assert html =~ "Brevo" assert html =~ "Mailjet" assert html =~ "Postal" end test "shows providers and advanced section", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/admin/settings/email") assert html =~ "Brevo" assert html =~ "SendGrid" assert html =~ "Mailjet" assert html =~ "MailerSend" # Advanced in details assert html =~ "Already have your own email server?" assert html =~ "SMTP" assert html =~ "Postal" end test "shows setup guidance", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/admin/settings/email") assert html =~ "needs an email provider" end test "renders all adapter field sections in the form", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/admin/settings/email") # All adapters have their config sections rendered (CSS controls visibility) assert html =~ ~s(data-adapter="brevo") assert html =~ ~s(data-adapter="sendgrid") assert html =~ ~s(data-adapter="mailjet") assert html =~ ~s(data-adapter="smtp") assert html =~ ~s(data-adapter="postal") end test "selecting a provider updates via phx-change", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/admin/settings/email") # Select SMTP via form change (radio inputs fire phx-change) html = view |> form("form[phx-change=\"form_change\"]", %{email: %{adapter: "smtp"}}) |> render_change() # SMTP fields are always rendered; check the step title changes assert html =~ "Server host" assert html =~ "Port" end test "saving config persists settings", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/admin/settings/email") # Select Brevo via form change view |> form("form[phx-change=\"form_change\"]", %{email: %{adapter: "brevo"}}) |> render_change() # Submit with namespaced fields: email[brevo][api_key] html = view |> form("form[phx-submit=\"save\"]", %{ email: %{adapter: "brevo", brevo: %{api_key: "xkeysib-abc123def456"}} }) |> render_submit() assert html =~ "Now send a test email" assert Settings.get_setting("email_adapter") == "brevo" end test "saving without required fields shows error", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/admin/settings/email") # Select Brevo view |> form("form[phx-change=\"form_change\"]", %{email: %{adapter: "brevo"}}) |> render_change() # Submit without API key html = view |> form("form[phx-submit=\"save\"]", %{ email: %{adapter: "brevo", brevo: %{api_key: ""}} }) |> render_submit() assert html =~ "API key is required" end test "saving with invalid port shows error", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/admin/settings/email") view |> form("form[phx-change=\"form_change\"]", %{email: %{adapter: "smtp"}}) |> render_change() html = view |> form("form[phx-submit=\"save\"]", %{ email: %{adapter: "smtp", smtp: %{relay: "smtp.example.com", port: "abc"}} }) |> render_submit() assert html =~ "must be a number" end test "shows test email section when configured", %{conn: conn} do Settings.put_setting("email_adapter", "brevo") Settings.put_secret("email_brevo_api_key", "xkeysib-test-abc") {:ok, _view, html} = live(conn, ~p"/admin/settings/email") assert html =~ "Send a test email" assert html =~ "Send test email" end test "hides test email section when not configured", %{conn: conn} do # Ensure clean state — no adapter configured Settings.delete_setting("email_adapter") Application.put_env(:berrypod, Berrypod.Mailer, adapter: Swoosh.Adapters.Local) {:ok, _view, html} = live(conn, ~p"/admin/settings/email") refute html =~ "Send test email" end test "sending test email shows success and sets verified flag", %{conn: conn} do Settings.put_setting("email_adapter", "brevo") Settings.put_secret("email_brevo_api_key", "xkeysib-test-abc") {:ok, view, _html} = live(conn, ~p"/admin/settings/email") # send_test is now async — click triggers the event, then handle_info completes it render_click(view, "send_test") # Wait for the async handle_info to complete html = render(view) assert html =~ "Email is working" assert html =~ "Send again" assert Mailer.email_verified?() end test "switching provider clears old provider settings", %{conn: conn} do # Save Mailjet config first Settings.put_setting("email_adapter", "mailjet") Settings.put_secret("email_mailjet_api_key", "mj-key-123") Settings.put_secret("email_mailjet_secret", "mj-secret-456") {:ok, view, _html} = live(conn, ~p"/admin/settings/email") # Switch to Brevo and save (namespaced fields) view |> form("form[phx-change=\"form_change\"]", %{email: %{adapter: "brevo"}}) |> render_change() view |> form("form[phx-submit=\"save\"]", %{ email: %{adapter: "brevo", brevo: %{api_key: "xkeysib-switch-test"}} }) |> render_submit() # Old Mailjet settings should be deleted assert Settings.get_setting("email_adapter") == "brevo" refute Settings.has_secret?("email_mailjet_api_key") refute Settings.has_secret?("email_mailjet_secret") # New Brevo key should exist assert Settings.has_secret?("email_brevo_api_key") end test "saving config clears verified flag", %{conn: conn} do Mailer.mark_email_verified() assert Mailer.email_verified?() {:ok, view, _html} = live(conn, ~p"/admin/settings/email") view |> form("form[phx-change=\"form_change\"]", %{email: %{adapter: "brevo"}}) |> render_change() view |> form("form[phx-submit=\"save\"]", %{ email: %{adapter: "brevo", brevo: %{api_key: "xkeysib-def789ghi012"}} }) |> render_submit() refute Mailer.email_verified?() end test "phx-change is no-op when adapter hasn't changed", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/admin/settings/email") # Select Brevo view |> form("form[phx-change=\"form_change\"]", %{email: %{adapter: "brevo"}}) |> render_change() # Trigger another change with the same adapter (simulates text field input) html = view |> form("form[phx-change=\"form_change\"]", %{email: %{adapter: "brevo"}}) |> render_change() # Should still show Brevo config assert html =~ "Brevo" assert html =~ "API key" end test "from_checklist param shows checklist banner", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/admin/settings/email?from=checklist") assert html =~ "setting up email" assert html =~ "Back to checklist" end test "has fieldset legend for accessibility", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/admin/settings/email") assert html =~ "Email provider" assert html =~ "sr-only" end test "only selected adapter config is open (details element)", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/admin/settings/email") # Select SMTP view |> form("form[phx-change=\"form_change\"]", %{email: %{adapter: "smtp"}}) |> render_change() # SMTP details should be open assert has_element?(view, ~s(details[data-adapter="smtp"][open])) # Others should not be open refute has_element?(view, ~s(details[data-adapter="brevo"][open])) refute has_element?(view, ~s(details[data-adapter="sendgrid"][open])) refute has_element?(view, ~s(details[data-adapter="postal"][open])) end end describe "unauthenticated" do test "redirects to login", %{conn: conn} do {:error, redirect} = live(conn, ~p"/admin/settings/email") assert {:redirect, %{to: path}} = redirect assert path == ~p"/users/log-in" end end end