Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions front/lib/front/project_page/model.ex
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ defmodule Front.ProjectPage.Model do
{:ok, organization} = Async.await(fetch_organization)
{:ok, project} = Async.await(fetch_project)

workflows = pin_default_branch_workflows(workflows, project, params, workflow_fetch_error)

previous = if previous_page_token != "", do: previous_page_token, else: nil
next = if next_page_token != "", do: next_page_token, else: nil
newest = if params.page_token == "", do: false, else: true
Expand Down Expand Up @@ -343,4 +345,58 @@ defmodule Front.ProjectPage.Model do

defp workflow_error_message(%MatchError{term: term}), do: inspect(term)
defp workflow_error_message(error), do: inspect(error)

defp pin_default_branch_workflows(workflows, _project, _params, error) when not is_nil(error),
do: workflows

defp pin_default_branch_workflows(workflows, nil, _params, _error), do: workflows

defp pin_default_branch_workflows(workflows, project, params, _error) do
default_branch = project.repo_default_branch

if pin_default_branch?(params, default_branch) do
reorder_default_branch(workflows, default_branch, params)
else
workflows
end
end

defp pin_default_branch?(params, default_branch) do
(params.list_mode || "latest") != "all_pipelines" and
is_binary(default_branch) and
default_branch != "" and
branch_ref_type_included?(params.ref_types)
end

defp branch_ref_type_included?(ref_types) when ref_types in [nil, []], do: true
defp branch_ref_type_included?(ref_types), do: Enum.member?(ref_types, "branch")

defp reorder_default_branch(workflows, default_branch, params) do
{pinned, rest} = Enum.split_with(workflows, &default_branch_workflow?(&1, default_branch))

cond do
first_page?(params) and pinned != [] ->
pinned ++ rest

first_page?(params) ->
prepend_stale_default_branch(workflows, default_branch, params.project_id)

true ->
rest
end
end

defp default_branch_workflow?(workflow, default_branch) do
workflow.type == "branch" and workflow.branch_name == default_branch
end

defp prepend_stale_default_branch(workflows, default_branch, project_id) do
case Models.Workflow.find_latest(project_id: project_id, branch_name: default_branch) do
nil ->
workflows

workflow ->
[Decorators.Workflow.decorate_one(workflow) | workflows]
end
end
end
162 changes: 162 additions & 0 deletions front/test/front/project_page/model_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,166 @@ defmodule Front.ProjectPage.ModelTest do
assert {:ok, 1} = params |> Model.invalidate()
end
end

describe "pins default branch" do
setup do
Cacheman.clear(:front)
Support.Stubs.init()
Support.Stubs.build_shared_factories()
Support.Stubs.DB.clear(:workflows)

user = Support.Stubs.User.default()
organization = Support.Stubs.Organization.default()

%{user: user, organization: organization}
end

test "moves the default branch to the front of page one", %{
user: user,
organization: organization
} do
project = create_project(organization, user)
create_branch_workflow(project, user, "feature-a")
create_branch_workflow(project, user, "main")
create_branch_workflow(project, user, "feature-b")

{:ok, data, :from_api} = Model.load_from_api(load_params(project, organization))

assert Enum.map(data.workflows, & &1.branch_name) == ["main", "feature-a", "feature-b"]
end

test "prepends a stale default branch missing from the first page", %{
user: user,
organization: organization
} do
project = create_project(organization, user)
feature = create_branch_workflow(project, user, "feature-a")
main = create_branch_workflow(project, user, "main")

with_mock Front.Models.Workflow, [:passthrough],
list_latest_workflows: fn _params ->
{[Front.Models.Workflow.find(feature.id)], "", ""}
end,
find_latest: fn _opts -> Front.Models.Workflow.find(main.id) end do
{:ok, data, :from_api} = Model.load_from_api(load_params(project, organization))

assert Enum.map(data.workflows, & &1.branch_name) == ["main", "feature-a"]
end
end

test "leaves plumber order when find_latest returns nil", %{
user: user,
organization: organization
} do
project = create_project(organization, user)
feature = create_branch_workflow(project, user, "feature-a")

with_mock Front.Models.Workflow, [:passthrough],
list_latest_workflows: fn _params ->
{[Front.Models.Workflow.find(feature.id)], "", ""}
end,
find_latest: fn _opts -> nil end do
{:ok, data, :from_api} = Model.load_from_api(load_params(project, organization))

assert Enum.map(data.workflows, & &1.branch_name) == ["feature-a"]
end
end

test "filters the default branch out of later pages", %{
user: user,
organization: organization
} do
project = create_project(organization, user)
create_branch_workflow(project, user, "feature-a")
create_branch_workflow(project, user, "main")
create_branch_workflow(project, user, "feature-b")

params =
load_params(project, organization,
page_token: "next-page",
direction: "next"
)

{:ok, data, :from_api} = Model.load_from_api(params)

assert Enum.map(data.workflows, & &1.branch_name) == ["feature-a", "feature-b"]
end

test "does not pin when the default branch is blank", %{
user: user,
organization: organization
} do
project = create_project(organization, user, repo_default_branch: "")
create_branch_workflow(project, user, "feature-a")
create_branch_workflow(project, user, "main")
create_branch_workflow(project, user, "feature-b")

{:ok, data, :from_api} = Model.load_from_api(load_params(project, organization))

assert Enum.map(data.workflows, & &1.branch_name) == ["feature-a", "main", "feature-b"]
end

test "does not pin on non-branch tabs", %{user: user, organization: organization} do
project = create_project(organization, user)
create_branch_workflow(project, user, "feature-a")
create_branch_workflow(project, user, "main")
create_branch_workflow(project, user, "feature-b")

params = load_params(project, organization, ref_types: ["pr"])
{:ok, data, :from_api} = Model.load_from_api(params)

assert Enum.map(data.workflows, & &1.branch_name) == ["feature-a", "main", "feature-b"]
end

test "does not change all_pipelines order", %{user: user, organization: organization} do
project = create_project(organization, user)
create_branch_workflow(project, user, "feature-a")
create_branch_workflow(project, user, "main")
create_branch_workflow(project, user, "feature-b")

params = load_params(project, organization, list_mode: "all_pipelines")
{:ok, data, :from_api} = Model.load_from_api(params)

assert Enum.map(data.workflows, & &1.branch_name) == ["feature-a", "main", "feature-b"]
end

defp load_params(project, organization, opts \\ []) do
struct!(
LoadParams,
[
project_id: project.id,
organization_id: organization.id,
page_token: "",
direction: "next",
list_mode: "latest",
user_page?: false,
ref_types: ["branch"]
]
|> Keyword.merge(opts)
)
end

defp create_project(organization, user, opts \\ []) do
Support.Stubs.Project.create(
organization,
user,
Keyword.merge(
[
repo_default_branch: "main",
run_on: ["branches"],
state: InternalApi.Projecthub.Project.Status.State.value(:READY)
],
opts
)
)
end

defp create_branch_workflow(project, user, branch_name) do
branch = Support.Stubs.Branch.create(project, name: branch_name, display_name: branch_name)
hook = Support.Stubs.Hook.create(branch)
workflow = Support.Stubs.Workflow.create(hook, user)
Support.Stubs.Pipeline.create_initial(workflow)
workflow
end
end
end