243 lines
6.5 KiB
Elixir
243 lines
6.5 KiB
Elixir
defmodule MvWeb.MemberExportControllerTest do
|
|
use MvWeb.ConnCase, async: true
|
|
|
|
alias Mv.Fixtures
|
|
|
|
defp csrf_token_from_conn(conn) do
|
|
get_session(conn, "_csrf_token") || csrf_token_from_html(response(conn, 200))
|
|
end
|
|
|
|
defp csrf_token_from_html(html) when is_binary(html) do
|
|
case Regex.run(~r/name="csrf-token"\s+content="([^"]+)"/, html) do
|
|
[_, token] -> token
|
|
_ -> nil
|
|
end
|
|
end
|
|
|
|
describe "POST /members/export.csv" do
|
|
setup %{conn: conn} do
|
|
# Create 3 members for export tests
|
|
m1 =
|
|
Fixtures.member_fixture(%{
|
|
first_name: "Alice",
|
|
last_name: "One",
|
|
email: "alice.one@example.com"
|
|
})
|
|
|
|
m2 =
|
|
Fixtures.member_fixture(%{
|
|
first_name: "Bob",
|
|
last_name: "Two",
|
|
email: "bob.two@example.com"
|
|
})
|
|
|
|
m3 =
|
|
Fixtures.member_fixture(%{
|
|
first_name: "Carol",
|
|
last_name: "Three",
|
|
email: "carol.three@example.com"
|
|
})
|
|
|
|
%{member1: m1, member2: m2, member3: m3, conn: conn}
|
|
end
|
|
|
|
test "selected export: returns 200, text/csv, header + exactly 2 data rows", %{
|
|
conn: conn,
|
|
member1: m1,
|
|
member2: m2
|
|
} do
|
|
payload = %{
|
|
"selected_ids" => [m1.id, m2.id],
|
|
"member_fields" => ["first_name", "last_name", "email"],
|
|
"custom_field_ids" => [],
|
|
"query" => nil,
|
|
"sort_field" => nil,
|
|
"sort_order" => nil
|
|
}
|
|
|
|
conn = get(conn, "/members")
|
|
csrf_token = csrf_token_from_conn(conn)
|
|
|
|
conn =
|
|
post(conn, "/members/export.csv", %{
|
|
"payload" => Jason.encode!(payload),
|
|
"_csrf_token" => csrf_token
|
|
})
|
|
|
|
assert conn.status == 200
|
|
assert get_resp_header(conn, "content-type") |> List.first() =~ "text/csv"
|
|
|
|
body = response(conn, 200)
|
|
lines = String.split(body, "\n", trim: true)
|
|
|
|
# Header + 2 data rows (headers are localized labels)
|
|
assert length(lines) == 3
|
|
assert hd(lines) =~ "First Name"
|
|
assert hd(lines) =~ "Email"
|
|
assert body =~ "Alice"
|
|
assert body =~ "Bob"
|
|
refute body =~ "Carol"
|
|
end
|
|
|
|
test "all export: selected_ids=[] returns all members (at least 3 data rows)", %{
|
|
conn: conn,
|
|
member1: _m1,
|
|
member2: _m2,
|
|
member3: _m3
|
|
} do
|
|
payload = %{
|
|
"selected_ids" => [],
|
|
"member_fields" => ["first_name", "email"],
|
|
"custom_field_ids" => [],
|
|
"query" => nil,
|
|
"sort_field" => nil,
|
|
"sort_order" => nil
|
|
}
|
|
|
|
conn = get(conn, "/members")
|
|
csrf_token = csrf_token_from_conn(conn)
|
|
|
|
conn =
|
|
post(conn, "/members/export.csv", %{
|
|
"payload" => Jason.encode!(payload),
|
|
"_csrf_token" => csrf_token
|
|
})
|
|
|
|
assert conn.status == 200
|
|
body = response(conn, 200)
|
|
lines = String.split(body, "\n", trim: true)
|
|
|
|
# Header + at least 3 data rows (headers are localized labels)
|
|
assert length(lines) >= 4
|
|
assert hd(lines) =~ "First Name"
|
|
assert body =~ "Alice"
|
|
assert body =~ "Bob"
|
|
assert body =~ "Carol"
|
|
end
|
|
|
|
test "whitelist: unknown member_fields are not in header", %{conn: conn, member1: m1} do
|
|
payload = %{
|
|
"selected_ids" => [m1.id],
|
|
"member_fields" => ["first_name", "unknown_field", "email"],
|
|
"custom_field_ids" => [],
|
|
"query" => nil,
|
|
"sort_field" => nil,
|
|
"sort_order" => nil
|
|
}
|
|
|
|
conn = get(conn, "/members")
|
|
csrf_token = csrf_token_from_conn(conn)
|
|
|
|
conn =
|
|
post(conn, "/members/export.csv", %{
|
|
"payload" => Jason.encode!(payload),
|
|
"_csrf_token" => csrf_token
|
|
})
|
|
|
|
assert conn.status == 200
|
|
body = response(conn, 200)
|
|
header = body |> String.split("\n", trim: true) |> hd()
|
|
|
|
assert header =~ "First Name"
|
|
assert header =~ "Email"
|
|
refute header =~ "unknown_field"
|
|
end
|
|
|
|
test "export includes membership_fee_status column when requested", %{
|
|
conn: conn,
|
|
member1: m1
|
|
} do
|
|
payload = %{
|
|
"selected_ids" => [m1.id],
|
|
"member_fields" => ["first_name", "membership_fee_status"],
|
|
"custom_field_ids" => [],
|
|
"query" => nil,
|
|
"sort_field" => nil,
|
|
"sort_order" => nil
|
|
}
|
|
|
|
conn = get(conn, "/members")
|
|
csrf_token = csrf_token_from_conn(conn)
|
|
|
|
conn =
|
|
post(conn, "/members/export.csv", %{
|
|
"payload" => Jason.encode!(payload),
|
|
"_csrf_token" => csrf_token
|
|
})
|
|
|
|
assert conn.status == 200
|
|
body = response(conn, 200)
|
|
header = body |> String.split("\n", trim: true) |> hd()
|
|
|
|
assert header =~ "First Name"
|
|
assert header =~ "Membership Fee Status"
|
|
assert body =~ "Alice"
|
|
end
|
|
|
|
test "export with payment_status alias: header shows Membership Fee Status", %{
|
|
conn: conn,
|
|
member1: m1
|
|
} do
|
|
payload = %{
|
|
"selected_ids" => [m1.id],
|
|
"member_fields" => ["first_name", "payment_status"],
|
|
"custom_field_ids" => [],
|
|
"query" => nil,
|
|
"sort_field" => nil,
|
|
"sort_order" => nil
|
|
}
|
|
|
|
conn = get(conn, "/members")
|
|
csrf_token = csrf_token_from_conn(conn)
|
|
|
|
conn =
|
|
post(conn, "/members/export.csv", %{
|
|
"payload" => Jason.encode!(payload),
|
|
"_csrf_token" => csrf_token
|
|
})
|
|
|
|
assert conn.status == 200
|
|
body = response(conn, 200)
|
|
header = body |> String.split("\n", trim: true) |> hd()
|
|
|
|
assert header =~ "Membership Fee Status"
|
|
assert body =~ "Alice"
|
|
end
|
|
|
|
test "export with show_current_cycle: membership fee status column exists stably", %{
|
|
conn: conn,
|
|
member1: _m1,
|
|
member2: _m2,
|
|
member3: _m3
|
|
} do
|
|
payload = %{
|
|
"selected_ids" => [],
|
|
"member_fields" => ["first_name", "email", "membership_fee_status"],
|
|
"custom_field_ids" => [],
|
|
"query" => nil,
|
|
"sort_field" => nil,
|
|
"sort_order" => nil,
|
|
"show_current_cycle" => true
|
|
}
|
|
|
|
conn = get(conn, "/members")
|
|
csrf_token = csrf_token_from_conn(conn)
|
|
|
|
conn =
|
|
post(conn, "/members/export.csv", %{
|
|
"payload" => Jason.encode!(payload),
|
|
"_csrf_token" => csrf_token
|
|
})
|
|
|
|
assert conn.status == 200
|
|
body = response(conn, 200)
|
|
lines = String.split(body, "\n", trim: true)
|
|
|
|
assert length(lines) >= 4
|
|
header = hd(lines)
|
|
assert header =~ "First Name"
|
|
assert header =~ "Email"
|
|
assert header =~ "Membership Fee Status"
|
|
end
|
|
end
|
|
end
|