6.5 KiB · text History 6280797
defmodule GitGudWeb.OrgInvitationTest do
@moduledoc """
Joining an org is something you accept, not something done to you:
an admin invites, the invitee answers from their dashboard.
"""
use GitGudWeb.ConnCase, async: false
import Phoenix.LiveViewTest
import GitGud.AccountsFixtures
alias GitGud.Organizations
defp org_with_admin(handle) do
admin = user_fixture()
{:ok, org} = Organizations.create_organization(admin, %{"handle" => handle})
{admin, org}
end
defp members_path(org), do: ~p"/orgs/#{org.handle}/settings/members"
defp invite!(lv, who) do
lv
|> form("#invite-member-form", %{"invite" => %{"handle_or_email" => who.handle}})
|> render_submit()
end
describe "inviting" do
test "an admin invites rather than adding outright", %{conn: conn} do
{admin, org} = org_with_admin("inv-basic")
invitee = user_fixture()
{:ok, lv, _html} = live(log_in_user(conn, admin), members_path(org))
html = invite!(lv, invitee)
assert html =~ "Pending invitations"
assert html =~ "awaiting reply"
# Crucially, not a member yet.
refute Organizations.member?(org, invitee)
assert [inv] = Organizations.list_pending_invitations(org)
assert inv.invited_user_id == invitee.id
assert inv.invited_by_id == admin.id
end
test "inviting an existing member is refused", %{conn: conn} do
{admin, org} = org_with_admin("inv-existing")
member = user_fixture()
{:ok, _} = Organizations.add_member(org, member, "member")
{:ok, lv, _html} = live(log_in_user(conn, admin), members_path(org))
html = invite!(lv, member)
assert html =~ "already a member"
assert Organizations.list_pending_invitations(org) == []
end
test "inviting twice keeps one pending invitation", %{conn: _conn} do
{admin, org} = org_with_admin("inv-twice")
invitee = user_fixture()
{:ok, first} = Organizations.invite_member(org, invitee, admin)
{:ok, second} = Organizations.invite_member(org, invitee, admin)
assert first.id == second.id
assert length(Organizations.list_pending_invitations(org)) == 1
end
test "an admin can withdraw an invitation", %{conn: conn} do
{admin, org} = org_with_admin("inv-withdraw")
invitee = user_fixture()
{:ok, lv, _html} = live(log_in_user(conn, admin), members_path(org))
_ = invite!(lv, invitee)
[inv] = Organizations.list_pending_invitations(org)
html = render_hook(lv, "revoke_invitation", %{"id" => to_string(inv.id)})
refute html =~ "awaiting reply"
assert Organizations.list_pending_invitations(org) == []
refute Organizations.member?(org, invitee)
end
test "a non-admin can't reach the members page at all", %{conn: conn} do
{_admin, org} = org_with_admin("inv-nonadmin")
outsider = user_fixture()
assert {:error, {:live_redirect, _}} =
live(log_in_user(conn, outsider), members_path(org))
end
end
describe "the dashboard panel" do
test "names who invited you and to which org", %{conn: conn} do
{admin, org} = org_with_admin("inv-dash")
invitee = user_fixture()
{:ok, _} = Organizations.invite_member(org, invitee, admin)
{:ok, _lv, html} = live(log_in_user(conn, invitee), ~p"/")
assert html =~ "Organization invitations"
assert html =~ admin.handle
assert html =~ "has invited you to join"
assert html =~ "inv-dash"
end
test "accepting makes you a member", %{conn: conn} do
{admin, org} = org_with_admin("inv-accept")
invitee = user_fixture()
{:ok, inv} = Organizations.invite_member(org, invitee, admin)
{:ok, lv, _html} = live(log_in_user(conn, invitee), ~p"/")
html = render_hook(lv, "accept_org_invitation", %{"id" => to_string(inv.id)})
assert Organizations.member?(org, invitee)
refute html =~ "has invited you to join"
# The org now shows in their own list.
assert html =~ "inv-accept"
end
test "declining does not", %{conn: conn} do
{admin, org} = org_with_admin("inv-decline")
invitee = user_fixture()
{:ok, inv} = Organizations.invite_member(org, invitee, admin)
{:ok, lv, _html} = live(log_in_user(conn, invitee), ~p"/")
html = render_hook(lv, "decline_org_invitation", %{"id" => to_string(inv.id)})
refute Organizations.member?(org, invitee)
refute html =~ "has invited you to join"
assert Organizations.list_pending_invitations(org) == []
end
test "an answered invitation can't be answered again", %{conn: _conn} do
{admin, org} = org_with_admin("inv-again")
invitee = user_fixture()
{:ok, inv} = Organizations.invite_member(org, invitee, admin)
{:ok, _} = Organizations.accept_invitation(inv, invitee)
reloaded = Organizations.get_invitation(inv.id)
assert {:error, :not_pending} = Organizations.decline_invitation(reloaded, invitee)
end
test "only the invitee can answer their invitation", %{conn: _conn} do
{admin, org} = org_with_admin("inv-forbidden")
invitee = user_fixture()
bystander = user_fixture()
{:ok, inv} = Organizations.invite_member(org, invitee, admin)
assert {:error, :forbidden} = Organizations.accept_invitation(inv, bystander)
assert {:error, :forbidden} = Organizations.decline_invitation(inv, bystander)
refute Organizations.member?(org, bystander)
end
test "someone else's invitation isn't answerable from your dashboard", %{conn: conn} do
{admin, org} = org_with_admin("inv-notmine")
invitee = user_fixture()
{:ok, inv} = Organizations.invite_member(org, invitee, admin)
bystander = user_fixture()
{:ok, lv, html} = live(log_in_user(conn, bystander), ~p"/")
refute html =~ "Organization invitations"
render_hook(lv, "accept_org_invitation", %{"id" => to_string(inv.id)})
refute Organizations.member?(org, bystander)
assert Organizations.member?(org, invitee) == false
end
test "no panel when you have no invitations", %{conn: conn} do
{:ok, _lv, html} = live(log_in_user(conn, user_fixture()), ~p"/")
refute html =~ "Organization invitations"
end
test "accepting honours the invited role", %{conn: _conn} do
{admin, org} = org_with_admin("inv-role")
invitee = user_fixture()
{:ok, inv} = Organizations.invite_member(org, invitee, admin, "admin")
{:ok, _} = Organizations.accept_invitation(inv, invitee)
assert Organizations.org_admin?(org, invitee)
end
end
end