Use one name for each thing the UI shows (closes #399)
check / check (push) Successful in 3m31s

The database target type is called an archive on its badge, in the add target form's type list and on its edit page, and its settings read "Archive expiry" and "Archive rotation" everywhere, the new webhook page included. The retry field is labelled "Delivery attempts", with its help text and error messages to match, on both target forms and in the target list, where a stored 0 shows as one attempt. The target list's other labels take the same capitalisation. The navbar says "Sign out" and the sign-in page "Sign in", and the README follows. The resubmit notice says "webhook". The stored values (`database`, `max_retries`) and their meaning are unchanged.

Model: opus-5-5
This commit is contained in:
2026-10-03 02:54:47 +00:00
committed by sneak
parent 74a96b2226
commit 12deb0d79b
23 changed files with 180 additions and 98 deletions
+17 -13
View File
@@ -318,31 +318,35 @@ const (
func checkAddEachTargetType(ctx context.Context, t *testing.T, url string) {
t.Helper()
// Each target type, with the fields its add target form submits, in
// page order. Only http and slack have a url field.
// Each target type, with the badge its targets are listed with and
// the fields its add target form submits, in page order. Only http
// and slack have a url field.
targetTypes := []struct {
name string
badge string
fields string
values map[string]string
}{
{
"http", "csrf_token name type url headers timeout max_retries",
"http", "http",
"csrf_token name type url headers timeout max_retries",
map[string]string{"url": publicTargetURL},
},
{
"slack", "csrf_token name type url max_retries",
"slack", "slack", "csrf_token name type url max_retries",
map[string]string{"url": publicTargetURL},
},
{
"database", "csrf_token name type expiry rotation",
"database", "archive", "csrf_token name type expiry rotation",
map[string]string{"expiry": "720h", "rotation": "daily"},
},
{"log", "csrf_token name type", nil},
{"log", "log", "csrf_token name type", nil},
}
for _, tt := range targetTypes {
checkAddTarget(
ctx, t, url, tt.name, strings.Fields(tt.fields), tt.values,
ctx, t, url, tt.name, tt.badge,
strings.Fields(tt.fields), tt.values,
)
}
}
@@ -353,11 +357,11 @@ func checkAddEachTargetType(ctx context.Context, t *testing.T, url string) {
// type's own fields in place of the choice, and the form then submits
// exactly fields, so a field another type uses, such as url, is absent;
// Cancel closes it again. It then adds a target of the type, filling in
// values, and checks that the section lists it with that type.
// values, and checks that the section lists it with badge.
func checkAddTarget(
ctx context.Context,
t *testing.T,
url, targetType string,
url, targetType, badge string,
fields []string,
values map[string]string,
) {
@@ -412,8 +416,8 @@ func checkAddTarget(
click(ctx, t, saveButton)
assert.Truef(t, shown(ctx, `//span[text()="`+name+
`"]/following-sibling::div/span[text()="`+targetType+`"]`),
"%s: the added target is not listed with its type", targetType)
`"]/following-sibling::div/span[text()="`+badge+`"]`),
"%s: the added target is not listed as %s", targetType, badge)
}
// chooseTargetType clicks Add, picks targetType and clicks Next, and
@@ -466,10 +470,10 @@ func checkArchiveChoices(ctx context.Context, t *testing.T, url string) {
assert.Equal(t, "none", startRotation,
"the add target form's archive rotation does not start on none")
assert.True(t, shown(ctx, row+`//span[text()="Archive Expiry:"]`+
assert.True(t, shown(ctx, row+`//span[text()="Archive expiry:"]`+
`/following-sibling::span[text()="30 days"]`),
"a database target added with 720h is not listed as 30 days")
assert.True(t, shown(ctx, row+`//span[text()="Archive Rotation:"]`+
assert.True(t, shown(ctx, row+`//span[text()="Archive rotation:"]`+
`/following-sibling::span[text()="daily"]`),
"a database target added with daily is not listed as daily")
+3 -3
View File
@@ -72,7 +72,7 @@ func TestEventResubmit_SignedOutRequestsNeverReachTheRateLimit(
env.requireNotice(
t, env.post(path, csrfForm(token), cookies),
logsPath, "resubmit-no-targets",
"this source has no active targets", cookies,
"this webhook has no active targets", cookies,
)
}
@@ -117,7 +117,7 @@ func TestEventResubmit_RefusedWithoutAValidCSRFToken(t *testing.T) {
env.requireNotice(
t, env.post(path, csrfForm(token), cookies),
logsPath, "resubmit-no-targets",
"this source has no active targets", cookies,
"this webhook has no active targets", cookies,
)
}
@@ -171,7 +171,7 @@ func TestEventResubmit_AnotherWebhooksEvent404s(t *testing.T) {
csrfForm(token), cookies,
),
intrudersLogs, "resubmit-no-targets",
"this source has no active targets", cookies,
"this webhook has no active targets", cookies,
)
}
+21 -1
View File
@@ -914,6 +914,26 @@ func TestPagesLogout_SaysSignedOut(t *testing.T) {
env.requireNotice(t, w, "/pages/login", "signed-out", "Signed out.", nil)
}
// TestSignInAndSignOutWording pins one wording for both: the sign-in
// page's button reads "Sign in" and the navbar's buttons "Sign out", as
// the sign-in page's heading, the error page's link and the notice
// after signing out do.
func TestSignInAndSignOutWording(t *testing.T) {
t.Parallel()
env := newTestEnv(t)
assert.Contains(
t, env.get("/pages/login", nil).Body.String(), ">Sign in</button>",
)
userID, _ := env.seedUser(t, "reader", "somepassword")
page := env.get("/hooks", env.authCookies(t, userID, "reader")).Body.String()
assert.Equal(t, 2, strings.Count(page, ">Sign out</button>"),
"the desktop and the mobile navbar each say Sign out")
}
// --- /user/{username} group ---
// TestPasswordChange_OversizeBody_RejectedAndPasswordUnchanged
@@ -1363,7 +1383,7 @@ func TestHook_ResubmitFromEventLog(t *testing.T) {
)
env.requireNotice(
t, w, logsPath, "resubmit-no-targets",
"this source has no active targets", cookies,
"this webhook has no active targets", cookies,
)
webhookDB, err := env.dbMgr.GetDB(wh.ID)