Show the deploy branch in the app page title and early in the app list (closes #240)
Check / check (pull_request) Skipped
Check / check (pull_request) Skipped
The app page shows the configured branch as a neutral label next to the status badge; the line under the title now shows only the repository. The app list moves the Branch column to right after Name and lets the table scroll sideways inside its card, so narrow screens no longer cut columns off. A new test renders both pages for an app on a non-main branch. Model: opus-5-5
This commit is contained in:
@@ -20,6 +20,11 @@ regress.
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-09-29: The app page shows the app's branch as a label in its title, next
|
||||||
|
to the status badge, instead of after the repository under it; the app list
|
||||||
|
shows the Branch column right after Name and scrolls sideways on narrow
|
||||||
|
screens instead of cutting columns off (#240).
|
||||||
|
|
||||||
- 2026-09-29: `docker-compose.yml` now sets `UPAAS_PORT` to 8080 as well as
|
- 2026-09-29: `docker-compose.yml` now sets `UPAAS_PORT` to 8080 as well as
|
||||||
`PORT`, since upaas reads `UPAAS_PORT` first and a `UPAAS_PORT` in `.env` made
|
`PORT`, since upaas reads `UPAAS_PORT` first and a `UPAAS_PORT` in `.env` made
|
||||||
it listen away from the port mapping and healthcheck; the README's Compose
|
it listen away from the port mapping and healthcheck; the README's Compose
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"sneak.berlin/go/upaas/internal/service/app"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestAppBranchIsShown checks that an app's branch can be read from the app
|
||||||
|
// page title and from the app list without opening the edit page.
|
||||||
|
func TestAppBranchIsShown(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
|
createdApp, err := testCtx.appSvc.CreateApp(t.Context(), app.CreateAppInput{
|
||||||
|
Name: "branch-shown-app",
|
||||||
|
RepoURL: "git@example.com:user/branch-shown-app.git",
|
||||||
|
Branch: "staging",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("in the app page title next to the status badge", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(), http.MethodGet, "/apps/"+createdApp.ID, nil,
|
||||||
|
)
|
||||||
|
request = addChiURLParams(request, map[string]string{"id": createdApp.ID})
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
testCtx.handlers.HandleAppDetail().ServeHTTP(recorder, request)
|
||||||
|
|
||||||
|
require.Equal(t, http.StatusOK, recorder.Code)
|
||||||
|
|
||||||
|
// The title row runs from the app name heading to the end of its div.
|
||||||
|
_, afterHeading, found := strings.Cut(recorder.Body.String(), "<h1")
|
||||||
|
require.True(t, found, "app page has no heading")
|
||||||
|
|
||||||
|
titleRow, _, _ := strings.Cut(afterHeading, "</div>")
|
||||||
|
assert.Contains(t, titleRow, `x-text="statusLabel"`)
|
||||||
|
assert.Contains(t, titleRow, ">staging</span>")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("in the app list before the repository", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(), http.MethodGet, "/", nil,
|
||||||
|
)
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
testCtx.handlers.HandleDashboard().ServeHTTP(recorder, request)
|
||||||
|
|
||||||
|
require.Equal(t, http.StatusOK, recorder.Code)
|
||||||
|
|
||||||
|
_, afterRowStart, found := strings.Cut(
|
||||||
|
recorder.Body.String(), `<tr class="table-row-hover">`,
|
||||||
|
)
|
||||||
|
require.True(t, found, "app list has no row for the app")
|
||||||
|
|
||||||
|
row, _, _ := strings.Cut(afterRowStart, "</tr>")
|
||||||
|
branchAt := strings.Index(row, ">staging</td>")
|
||||||
|
repoAt := strings.Index(row, createdApp.RepoURL)
|
||||||
|
|
||||||
|
require.NotEqual(t, -1, branchAt, "branch missing from the app's row")
|
||||||
|
require.NotEqual(t, -1, repoAt, "repository missing from the app's row")
|
||||||
|
assert.Less(t, branchAt, repoAt, "branch should come before repository")
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -26,11 +26,12 @@
|
|||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8">
|
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
<h1 class="text-2xl font-medium text-gray-900">{{.App.Name}}</h1>
|
<h1 class="text-2xl font-medium text-gray-900">{{.App.Name}}</h1>
|
||||||
<span x-bind:class="statusBadgeClass" x-text="statusLabel"></span>
|
<span x-bind:class="statusBadgeClass" x-text="statusLabel"></span>
|
||||||
|
<span class="badge-neutral font-mono break-all" title="Branch">{{.App.Branch}}</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-500 font-mono text-sm mt-1">{{.App.RepoURL}}@{{.App.Branch}}</p>
|
<p class="text-gray-500 font-mono text-sm mt-1">{{.App.RepoURL}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<a href="/apps/{{.App.ID}}/edit" class="btn-secondary">Edit</a>
|
<a href="/apps/{{.App.ID}}/edit" class="btn-secondary">Edit</a>
|
||||||
|
|||||||
@@ -21,12 +21,13 @@
|
|||||||
|
|
||||||
{{if .AppStats}}
|
{{if .AppStats}}
|
||||||
<div class="card overflow-hidden">
|
<div class="card overflow-hidden">
|
||||||
|
<div class="overflow-x-auto">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead class="table-header">
|
<thead class="table-header">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Repository</th>
|
|
||||||
<th>Branch</th>
|
<th>Branch</th>
|
||||||
|
<th>Repository</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Last Deploy</th>
|
<th>Last Deploy</th>
|
||||||
<th>Deploys</th>
|
<th>Deploys</th>
|
||||||
@@ -41,8 +42,8 @@
|
|||||||
{{.App.Name}}
|
{{.App.Name}}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-gray-500 font-mono text-xs">{{.App.RepoURL}}</td>
|
|
||||||
<td class="text-gray-500">{{.App.Branch}}</td>
|
<td class="text-gray-500">{{.App.Branch}}</td>
|
||||||
|
<td class="text-gray-500 font-mono text-xs">{{.App.RepoURL}}</td>
|
||||||
<td>
|
<td>
|
||||||
{{if eq .App.Status "running"}}
|
{{if eq .App.Status "running"}}
|
||||||
<span class="badge-success">Running</span>
|
<span class="badge-success">Running</span>
|
||||||
@@ -78,6 +79,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|||||||
Reference in New Issue
Block a user