Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
273 lines
8.8 KiB
Elixir
273 lines
8.8 KiB
Elixir
defmodule TarakanWeb.RepositoryCommitsLiveTest do
|
|
use TarakanWeb.ConnCase, async: false
|
|
|
|
import Phoenix.LiveViewTest
|
|
|
|
alias Tarakan.Accounts
|
|
alias Tarakan.HostedRepositories
|
|
alias Tarakan.HostedRepositories.Storage
|
|
|
|
@moduletag :tmp_dir
|
|
|
|
setup %{tmp_dir: tmp_dir} do
|
|
on_exit(fn ->
|
|
File.rm_rf(Application.fetch_env!(:tarakan, Tarakan.HostedRepositories)[:root])
|
|
end)
|
|
|
|
Tarakan.RepositoryCode.Cache.clear()
|
|
|
|
account = account_fixture()
|
|
scope = Accounts.scope_for_account(account)
|
|
{:ok, repository} = HostedRepositories.create(scope, %{"name" => "history"})
|
|
|
|
%{repository: repository, tmp_dir: tmp_dir}
|
|
end
|
|
|
|
defp push_commit(repository, tmp_dir, dir_name, message) do
|
|
work = Path.join(tmp_dir, dir_name)
|
|
|
|
{_output, 0} =
|
|
System.cmd("git", ["clone", "--quiet", Storage.dir(repository), work],
|
|
stderr_to_stdout: true
|
|
)
|
|
|
|
File.write!(Path.join(work, "README.md"), "# #{message}\n")
|
|
|
|
env = [
|
|
{"GIT_AUTHOR_NAME", "t"},
|
|
{"GIT_AUTHOR_EMAIL", "t@example.com"},
|
|
{"GIT_COMMITTER_NAME", "t"},
|
|
{"GIT_COMMITTER_EMAIL", "t@example.com"}
|
|
]
|
|
|
|
{_output, 0} = System.cmd("git", ["-C", work, "add", "."], env: env)
|
|
{_output, 0} = System.cmd("git", ["-C", work, "commit", "--quiet", "-m", message], env: env)
|
|
|
|
{_output, 0} =
|
|
System.cmd("git", ["-C", work, "push", "--quiet", "origin", "HEAD"],
|
|
env: env,
|
|
stderr_to_stdout: true
|
|
)
|
|
|
|
{sha, 0} = System.cmd("git", ["-C", work, "rev-parse", "HEAD"])
|
|
String.trim(sha)
|
|
end
|
|
|
|
defp push_merge_commit(repository, tmp_dir, dir_name, branch, message) do
|
|
work = Path.join(tmp_dir, dir_name)
|
|
|
|
{_output, 0} =
|
|
System.cmd("git", ["clone", "--quiet", Storage.dir(repository), work],
|
|
stderr_to_stdout: true
|
|
)
|
|
|
|
env = [
|
|
{"GIT_AUTHOR_NAME", "t"},
|
|
{"GIT_AUTHOR_EMAIL", "t@example.com"},
|
|
{"GIT_COMMITTER_NAME", "t"},
|
|
{"GIT_COMMITTER_EMAIL", "t@example.com"}
|
|
]
|
|
|
|
{main, 0} = System.cmd("git", ["-C", work, "rev-parse", "--abbrev-ref", "HEAD"])
|
|
main = String.trim(main)
|
|
|
|
{_output, 0} = System.cmd("git", ["-C", work, "checkout", "--quiet", "-b", branch], env: env)
|
|
File.write!(Path.join(work, "side.txt"), "side\n")
|
|
{_output, 0} = System.cmd("git", ["-C", work, "add", "."], env: env)
|
|
|
|
{_output, 0} =
|
|
System.cmd("git", ["-C", work, "commit", "--quiet", "-m", "side change"], env: env)
|
|
|
|
{_output, 0} = System.cmd("git", ["-C", work, "checkout", "--quiet", main], env: env)
|
|
File.write!(Path.join(work, "main.txt"), "main\n")
|
|
{_output, 0} = System.cmd("git", ["-C", work, "add", "."], env: env)
|
|
|
|
{_output, 0} =
|
|
System.cmd("git", ["-C", work, "commit", "--quiet", "-m", "main change"], env: env)
|
|
|
|
{_output, 0} =
|
|
System.cmd("git", ["-C", work, "merge", "--quiet", "--no-ff", "-m", message, branch],
|
|
env: env,
|
|
stderr_to_stdout: true
|
|
)
|
|
|
|
{_output, 0} =
|
|
System.cmd("git", ["-C", work, "push", "--quiet", "origin", "HEAD"],
|
|
env: env,
|
|
stderr_to_stdout: true
|
|
)
|
|
|
|
{sha, 0} = System.cmd("git", ["-C", work, "rev-parse", "HEAD"])
|
|
String.trim(sha)
|
|
end
|
|
|
|
defp push_branch_commit(repository, tmp_dir, dir_name, branch, message) do
|
|
work = Path.join(tmp_dir, dir_name)
|
|
|
|
{_output, 0} =
|
|
System.cmd("git", ["clone", "--quiet", Storage.dir(repository), work],
|
|
stderr_to_stdout: true
|
|
)
|
|
|
|
File.write!(Path.join(work, "FEATURE.md"), "# #{message}\n")
|
|
|
|
env = [
|
|
{"GIT_AUTHOR_NAME", "t"},
|
|
{"GIT_AUTHOR_EMAIL", "t@example.com"},
|
|
{"GIT_COMMITTER_NAME", "t"},
|
|
{"GIT_COMMITTER_EMAIL", "t@example.com"}
|
|
]
|
|
|
|
{_output, 0} = System.cmd("git", ["-C", work, "checkout", "--quiet", "-b", branch], env: env)
|
|
{_output, 0} = System.cmd("git", ["-C", work, "add", "."], env: env)
|
|
{_output, 0} = System.cmd("git", ["-C", work, "commit", "--quiet", "-m", message], env: env)
|
|
|
|
{_output, 0} =
|
|
System.cmd("git", ["-C", work, "push", "--quiet", "origin", branch],
|
|
env: env,
|
|
stderr_to_stdout: true
|
|
)
|
|
|
|
{sha, 0} = System.cmd("git", ["-C", work, "rev-parse", "HEAD"])
|
|
String.trim(sha)
|
|
end
|
|
|
|
test "lists the default branch history and links each commit to its detail page", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
tmp_dir: tmp_dir
|
|
} do
|
|
first_sha = push_commit(repository, tmp_dir, "work-first", "initial")
|
|
tip = push_commit(repository, tmp_dir, "work-second", "second")
|
|
|
|
{:ok, view, _html} = live(conn, "/#{repository.owner}/#{repository.name}/commits")
|
|
render_async(view, 1_000)
|
|
|
|
assert has_element?(view, "#repository-commits-tab[aria-current='page']")
|
|
|
|
# The active tab is still a link: from a commit detail page it points at the
|
|
# commit list, which is the obvious way back and used to be unclickable.
|
|
assert has_element?(view, "a#repository-commits-tab[aria-current='page'][href]")
|
|
assert has_element?(view, "#commits-list a", "second")
|
|
assert has_element?(view, "#commits-list a", "initial")
|
|
|
|
assert has_element?(
|
|
view,
|
|
"#commits-list a[href='/#{repository.owner}/#{repository.name}/commits/#{tip}']"
|
|
)
|
|
|
|
assert has_element?(
|
|
view,
|
|
"#commits-list a[href='/#{repository.owner}/#{repository.name}/commits/#{first_sha}']"
|
|
)
|
|
|
|
refute has_element?(view, "#commits-load-more")
|
|
end
|
|
|
|
test "the commit detail page shows the full message, patch, and code link", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
tmp_dir: tmp_dir
|
|
} do
|
|
_first_sha = push_commit(repository, tmp_dir, "work-first", "initial")
|
|
tip = push_commit(repository, tmp_dir, "work-second", "second")
|
|
|
|
{:ok, view, _html} =
|
|
live(conn, "/#{repository.owner}/#{repository.name}/commits/#{tip}")
|
|
|
|
render_async(view, 1_000)
|
|
|
|
assert has_element?(view, "#commit-subject", "second")
|
|
assert has_element?(view, "#commit-detail-sha", String.slice(tip, 0, 7))
|
|
assert has_element?(view, "#commit-patch", "+# second")
|
|
|
|
assert has_element?(
|
|
view,
|
|
"#commit-browse-code[href='/#{repository.owner}/#{repository.name}/code/#{tip}']"
|
|
)
|
|
|
|
# The code link opens the browser pinned at this exact commit.
|
|
{:ok, code_view, _html} = live(conn, "/#{repository.owner}/#{repository.name}/code/#{tip}")
|
|
render_async(code_view, 1_000)
|
|
assert has_element?(code_view, "#code-tree")
|
|
end
|
|
|
|
test "merge commits render with an empty patch state", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
tmp_dir: tmp_dir
|
|
} do
|
|
_main_sha = push_commit(repository, tmp_dir, "work-first", "initial")
|
|
merge_sha = push_merge_commit(repository, tmp_dir, "work-merge", "side", "merge side")
|
|
|
|
{:ok, view, _html} =
|
|
live(conn, "/#{repository.owner}/#{repository.name}/commits/#{merge_sha}")
|
|
|
|
render_async(view, 1_000)
|
|
|
|
assert has_element?(view, "#commit-subject", "merge side")
|
|
assert has_element?(view, "#commit-patch-empty", "No textual changes")
|
|
refute has_element?(view, "#commits-error")
|
|
end
|
|
|
|
test "the commit detail page rejects unreachable commits", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
tmp_dir: tmp_dir
|
|
} do
|
|
_sha = push_commit(repository, tmp_dir, "work-first", "initial")
|
|
unknown = String.duplicate("d", 40)
|
|
|
|
{:ok, view, _html} =
|
|
live(conn, "/#{repository.owner}/#{repository.name}/commits/#{unknown}")
|
|
|
|
render_async(view, 1_000)
|
|
|
|
assert has_element?(view, "#commits-error", "Commit not found")
|
|
refute has_element?(view, "#commit-detail")
|
|
end
|
|
|
|
test "the branch picker switches the listed history", %{
|
|
conn: conn,
|
|
repository: repository,
|
|
tmp_dir: tmp_dir
|
|
} do
|
|
_main_sha = push_commit(repository, tmp_dir, "work-first", "initial")
|
|
|
|
feature_sha =
|
|
push_branch_commit(repository, tmp_dir, "work-feature", "feature", "feature work")
|
|
|
|
{:ok, view, _html} = live(conn, "/#{repository.owner}/#{repository.name}/commits")
|
|
render_async(view, 1_000)
|
|
|
|
assert has_element?(view, "#commits-branch-select")
|
|
refute has_element?(view, "#commits-list a", "feature work")
|
|
|
|
view
|
|
|> form("#commits-branch-form", %{branch: "feature"})
|
|
|> render_change()
|
|
|
|
render_async(view, 1_000)
|
|
|
|
assert has_element?(view, "#commits-list a", "feature work")
|
|
|
|
assert has_element?(
|
|
view,
|
|
"#commits-list a[href='/#{repository.owner}/#{repository.name}/commits/#{feature_sha}']"
|
|
)
|
|
end
|
|
|
|
test "an empty repository shows an empty state", %{conn: conn, repository: repository} do
|
|
{:ok, view, _html} = live(conn, "/#{repository.owner}/#{repository.name}/commits")
|
|
render_async(view, 1_000)
|
|
|
|
assert has_element?(view, "#commits-error", "No commits yet")
|
|
refute has_element?(view, "#commits-list")
|
|
end
|
|
|
|
test "unknown repositories 404", %{conn: conn} do
|
|
assert_raise Ecto.NoResultsError, fn ->
|
|
live(conn, "/nobody/here/commits")
|
|
end
|
|
end
|
|
end
|