vole
Clone
Read only, no credentials:
https://rodent.software/git/vole.git
Write over SSH with a registered key:
git clone ssh://git@0.0.0.0:2222/vole.git
A free and open-source forge that lives in your Git repo.
Search for an issue when linking it to a change
838f282eebf7e68f61318eae93d03263df1341df · Keenan Nemetz · 10d ago · parent 267feec2
Search for an issue when linking it to a change The link field only accepted an id that the author already knew. It is now a search box over the issue index that matches a title, a #number, or a ULID prefix. A matching issue is a button: picking one posts the same link write and replaces the Issues section with the new list. Issues already linked are left out of the results.
server/issues_changes_test.go +23−5
| @@ -338,7 +338,7 @@ func TestChangeLinksIssuesFromTheIssuesSection(t *testing.T) { | |||
| 338 | 338 | _, page := f.browserGet(t, "/r/"+f.repo+"/requests/"+id, cookie) | |
| 339 | 339 | for _, want := range []string{ | |
| 340 | 340 | `id="request-issues"`, "First linked issue", `value="` + first + `"`, | |
| 341 | `value="add"`, `value="remove"`, `request-unlink`, | ||
| 341 | `value="remove"`, `request-unlink`, `class="request-issue-search"`, | ||
| 342 | 342 | } { | |
| 343 | 343 | if !strings.Contains(page, want) { | |
| 344 | 344 | t.Fatalf("the request page is missing %q:\n%s", want, page) | |
| @@ -348,7 +348,8 @@ func TestChangeLinksIssuesFromTheIssuesSection(t *testing.T) { | |||
| 348 | 348 | t.Fatalf("the old closes line is still on the page:\n%s", page) | |
| 349 | 349 | } | |
| 350 | 350 | ||
| 351 | // A number works in the link field, and the section comes back in place. | ||
| 351 | // The search box filters by title, `#number` and ULID prefix. A match is a | ||
| 352 | // button that posts the link, and the Issues section comes back in place. | ||
| 352 | 353 | m := mustRepo(t, f) | |
| 353 | 354 | nums, err := metadata.LoadNumbers(m.Git(), metadata.Ref) | |
| 354 | 355 | if err != nil { | |
| @@ -358,8 +359,25 @@ func TestChangeLinksIssuesFromTheIssuesSection(t *testing.T) { | |||
| 358 | 359 | if !ok { | |
| 359 | 360 | t.Fatalf("the second issue has no number") | |
| 360 | 361 | } | |
| 362 | for _, query := range []string{"Second", "#" + strconv.Itoa(number), second[:8]} { | ||
| 363 | resp, results := f.htmxGet(t, "/r/"+f.repo+"/requests/"+id+"/issues/search?q="+url.QueryEscape(query), cookie) | ||
| 364 | if resp.StatusCode != http.StatusOK || !strings.Contains(results, "Second linked issue") || | ||
| 365 | !strings.Contains(results, `value="`+second+`"`) || !strings.Contains(results, `value="add"`) || | ||
| 366 | !strings.Contains(results, `hx-target="#request-issues"`) { | ||
| 367 | t.Fatalf("search %q is not the expected result (%d):\n%s", query, resp.StatusCode, results) | ||
| 368 | } | ||
| 369 | } | ||
| 370 | // An issue already linked is not offered, so a shared title cannot hide the | ||
| 371 | // rows that are still available. | ||
| 372 | resp, results := f.htmxGet(t, "/r/"+f.repo+"/requests/"+id+"/issues/search?q=First", cookie) | ||
| 373 | if resp.StatusCode != http.StatusOK || strings.Contains(results, "First linked issue") || | ||
| 374 | !strings.Contains(results, "No issues match") { | ||
| 375 | t.Fatalf("a linked issue is still in the search results (%d):\n%s", resp.StatusCode, results) | ||
| 376 | } | ||
| 377 | |||
| 378 | // Clicking a result posts its id, and the section comes back with both links. | ||
| 361 | 379 | resp, body = f.htmxPost(t, "/r/"+f.repo+"/requests/"+id+"/issues", cookie, url.Values{ | |
| 362 | "csrf": {sess.CSRF}, "action": {"add"}, "issue": {"#" + strconv.Itoa(number)}, | ||
| 380 | "csrf": {sess.CSRF}, "action": {"add"}, "issue": {second}, | ||
| 363 | 381 | }) | |
| 364 | 382 | if resp.StatusCode != http.StatusOK || !strings.Contains(body, `id="request-issues"`) || | |
| 365 | 383 | !strings.Contains(body, "Second linked issue") || !strings.Contains(body, "First linked issue") { | |
| @@ -399,8 +417,8 @@ func TestChangeLinksIssuesFromTheIssuesSection(t *testing.T) { | |||
| 399 | 417 | t.Fatalf("close change: %d\n%s", resp.StatusCode, body) | |
| 400 | 418 | } | |
| 401 | 419 | _, page = f.browserGet(t, "/r/"+f.repo+"/requests/"+id, cookie) | |
| 402 | if !strings.Contains(page, "Second linked issue") || strings.Contains(page, `value="remove"`) || | ||
| 403 | strings.Contains(page, `value="add"`) { | ||
| 420 | if !strings.Contains(page, "Second linked issue") || strings.Contains(page, `request-unlink`) || | ||
| 421 | strings.Contains(page, `class="request-issue-search"`) { | ||
| 404 | 422 | t.Fatalf("a closed request does not show the right Issues section:\n%s", page) | |
| 405 | 423 | } | |
| 406 | 424 | resp, _ = f.htmxPost(t, "/r/"+f.repo+"/requests/"+id+"/issues", cookie, url.Values{ | |