defmodule GitGudWeb.ApiTokenScopesTest do
@moduledoc """
The API tokens page, with the per-surface scopes and the "Mobile app"
and "Desktop widget" bundle toggles.
The bundle is the part worth testing: it's not a scope, it's a
checkbox that ticks a set of them, and the pairing QR only appears
when the minted token actually covers that set. Getting either half
wrong produces a device that pairs and then fails on every screen.
"""
use GitGudWeb.ConnCase, async: false
import Phoenix.LiveViewTest
import GitGud.AccountsFixtures
alias GitGud.Accounts
alias GitGud.Accounts.PersonalAccessToken
setup %{conn: conn} do
user = user_fixture()
%{conn: log_in_user(conn, user), user: user}
end
test "lists every scope with its description", %{conn: conn} do
{:ok, _lv, html} = live(conn, ~p"/users/settings/tokens")
for scope <- PersonalAccessToken.scopes() do
assert html =~ scope
end
assert html =~ "Read workflow runs, jobs and logs"
assert html =~ "Re-run and cancel workflow runs"
end
test "the mobile toggle ticks and unticks the whole bundle", %{conn: conn, user: user} do
{:ok, lv, _html} = live(conn, ~p"/users/settings/tokens")
lv |> element(~s{input[phx-value-bundle="mobile"]}) |> render_click()
lv |> form("form[phx-submit=create_token]", %{"name" => "phone"}) |> render_submit()
token = Enum.find(Accounts.list_personal_access_tokens(user), &(&1.name == "phone"))
assert PersonalAccessToken.mobile?(token.scopes)
# Untoggling clears the bundle but leaves the default `repo` scope,
# which the user picked separately.
lv |> element(~s{input[phx-value-bundle="mobile"]}) |> render_click()
lv |> form("form[phx-submit=create_token]", %{"name" => "script"}) |> render_submit()
script = Enum.find(Accounts.list_personal_access_tokens(user), &(&1.name == "script"))
assert script.scopes == ["repo"]
end
test "renders the pairing QR only for a token covering the bundle", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/users/settings/tokens")
plain = lv |> form("form[phx-submit=create_token]", %{"name" => "script"}) |> render_submit()
refute plain =~ "gitgud://pair"
lv |> element(~s{input[phx-value-bundle="mobile"]}) |> render_click()
mobile = lv |> form("form[phx-submit=create_token]", %{"name" => "phone"}) |> render_submit()
assert mobile =~ "Pair a device"
assert mobile =~ "gitgud://pair"
# An <svg> QR of the same URI, not just the text fallback.
assert mobile =~ "<svg"
end
test "the desktop toggle ticks exactly the widget's scopes", %{conn: conn, user: user} do
{:ok, lv, _html} = live(conn, ~p"/users/settings/tokens")
# Drop the default `repo` so the token carries the bundle and nothing else.
lv |> element(~s{input[phx-value-scope="repo"]}) |> render_click()
lv |> element(~s{input[phx-value-bundle="desktop"]}) |> render_click()
html = lv |> form("form[phx-submit=create_token]", %{"name" => "panel"}) |> render_submit()
token = Enum.find(Accounts.list_personal_access_tokens(user), &(&1.name == "panel"))
assert Enum.sort(token.scopes) == Enum.sort(PersonalAccessToken.desktop_scopes())
assert PersonalAccessToken.desktop?(token.scopes)
refute PersonalAccessToken.mobile?(token.scopes)
# The widget hint shows; the phone pairing QR doesn't.
assert html =~ "GitGud CI widget"
refute html =~ "gitgud://pair"
end
test "unticking desktop keeps scopes picked separately", %{conn: conn, user: user} do
{:ok, lv, _html} = live(conn, ~p"/users/settings/tokens")
lv |> element(~s{input[phx-value-bundle="desktop"]}) |> render_click()
lv |> element(~s{input[phx-value-scope="issues:read"]}) |> render_click()
lv |> element(~s{input[phx-value-bundle="desktop"]}) |> render_click()
lv |> form("form[phx-submit=create_token]", %{"name" => "mixed"}) |> render_submit()
token = Enum.find(Accounts.list_personal_access_tokens(user), &(&1.name == "mixed"))
assert Enum.sort(token.scopes) == ["issues:read", "repo"]
end
test "a mobile token also covers the desktop bundle" do
assert PersonalAccessToken.desktop?(PersonalAccessToken.mobile_scopes())
end
test "an individual scope can be toggled on its own", %{conn: conn, user: user} do
{:ok, lv, _html} = live(conn, ~p"/users/settings/tokens")
lv |> element(~s{input[phx-value-scope="ci:read"]}) |> render_click()
lv |> form("form[phx-submit=create_token]", %{"name" => "dashboard"}) |> render_submit()
token = Enum.find(Accounts.list_personal_access_tokens(user), &(&1.name == "dashboard"))
assert Enum.sort(token.scopes) == ["ci:read", "repo"]
end
test "refuses a token with no scopes at all", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/users/settings/tokens")
lv |> element(~s{input[phx-value-scope="repo"]}) |> render_click()
html = lv |> form("form[phx-submit=create_token]", %{"name" => "empty"}) |> render_submit()
assert html =~ "Pick at least one scope"
end
end
neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
4.9 KiB · text
History
b868907