m's report: /new?parent=admin doesn't pre-select admin. Root cause is
worse than the report — the Parents <select> was COMPLETELY EMPTY: the
handler never passed ParentOptions to the template, so the
`{{range .ParentOptions}}` block iterated nil. There was nothing to
pre-select.
handleNewForm now calls s.parentOptions(r.Context()) the same way
handleClassify already did, and threads the result through the data
map as "ParentOptions". The template's existing pre-select expression
`{{if and $.Parent (eq .ID $.Parent.ID)}}selected{{end}}` already
handles id/path resolution — once the options exist, the `selected`
attribute lands on the right one.
Regression test (web/new_form_test.go):
- TestNewFormPreselectsParent — probes /new?parent=admin against the
HTTP integration server, asserts (1) <option> tags are rendered in
the Parents <select>, (2) the admin <option> exists with `selected`
on its opening tag, (3) other root options (dev) do NOT carry
`selected`. Confirmed failing pre-fix (no admin option at all),
passing post-fix.
- TestNewFormNoParentParamRendersAllOptions — bare /new with no
?parent= still populates the Parents <select> so the user can pick
any parent. Belt-and-braces guard.
Full web suite green. Pre-existing db/TestBackfillTagsFromArea failure
unchanged.
Net: +105 / -0.
97 lines
3.3 KiB
Go
97 lines
3.3 KiB
Go
package web_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestNewFormPreselectsParent reproduces m's bug report: GET /new?parent=admin
|
|
// must render the Parents <select> populated with the full project list AND
|
|
// pre-select the option whose value matches admin's item id. Pre-fix the
|
|
// handler passed no ParentOptions to the template, so the <select> was empty
|
|
// and there was nothing to pre-select.
|
|
func TestNewFormPreselectsParent(t *testing.T) {
|
|
srv, pool := mustServer(t)
|
|
defer pool.Close()
|
|
h := srv.Routes()
|
|
|
|
code, body := get(t, h, "/new?parent=admin")
|
|
if code != 200 {
|
|
t.Fatalf("GET /new?parent=admin → %d body=%s", code, body)
|
|
}
|
|
|
|
// The Parents <select> must be populated. admin is a root area present
|
|
// in every projax instance — its option should be there.
|
|
if !strings.Contains(body, `<option value="`) {
|
|
t.Fatalf("Parents <select> is empty — no <option> rendered. Body excerpt: %s",
|
|
body[strings.Index(body, "parent_ids"):min(len(body), strings.Index(body, "parent_ids")+800)])
|
|
}
|
|
if !strings.Contains(body, `>admin</option>`) {
|
|
t.Errorf("expected an <option>...>admin</option> in the Parents <select>")
|
|
}
|
|
|
|
// The admin option must be the selected one — that's the prefill contract.
|
|
// We anchor on the path (rendered as the option label) since the id is a
|
|
// uuid we'd otherwise have to look up.
|
|
adminIdx := strings.Index(body, `>admin</option>`)
|
|
if adminIdx < 0 {
|
|
t.Fatalf("admin option not found in rendered Parents select")
|
|
}
|
|
// Look back ~200 chars to the <option ... selected> opening tag.
|
|
from := adminIdx - 200
|
|
if from < 0 {
|
|
from = 0
|
|
}
|
|
openingTag := body[from:adminIdx]
|
|
if !strings.Contains(openingTag, "selected") {
|
|
t.Errorf("admin <option> not marked selected; opening tag was: %s", openingTag)
|
|
}
|
|
|
|
// And other unrelated options must NOT be selected. Pick `dev` (another
|
|
// root area) as the counter-anchor.
|
|
devIdx := strings.Index(body, `>dev</option>`)
|
|
if devIdx >= 0 {
|
|
from := devIdx - 200
|
|
if from < 0 {
|
|
from = 0
|
|
}
|
|
devTag := body[from:devIdx]
|
|
if strings.Contains(devTag, "selected") {
|
|
t.Errorf("dev <option> should NOT be selected when ?parent=admin; opening tag was: %s", devTag)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestNewFormNoParentParamRendersAllOptions confirms the Parents <select>
|
|
// is populated even when no ?parent= is supplied — clicking "+ New" from the
|
|
// nav should still let the user pick any parent.
|
|
func TestNewFormNoParentParamRendersAllOptions(t *testing.T) {
|
|
srv, pool := mustServer(t)
|
|
defer pool.Close()
|
|
h := srv.Routes()
|
|
|
|
code, body := get(t, h, "/new")
|
|
if code != 200 {
|
|
t.Fatalf("GET /new → %d", code)
|
|
}
|
|
// At least one option exists.
|
|
if !strings.Contains(body, `<option value="`) {
|
|
t.Fatalf("Parents <select> is empty on /new (no ?parent= param)")
|
|
}
|
|
// Nothing pre-selected.
|
|
if strings.Contains(body, `<option value="`) && strings.Contains(body, `" selected>`) {
|
|
// Make sure no Parents <select> option is selected — Status options
|
|
// might use selected for the default, so anchor on parent_ids context.
|
|
pIdx := strings.Index(body, `name="parent_ids"`)
|
|
if pIdx >= 0 {
|
|
selectClose := strings.Index(body[pIdx:], `</select>`)
|
|
if selectClose > 0 {
|
|
parentBlock := body[pIdx : pIdx+selectClose]
|
|
if strings.Contains(parentBlock, "selected") {
|
|
t.Errorf("no Parents option should be selected on bare /new, but block contains 'selected': %s", parentBlock)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|