feat: enhance team planning and deployment
Test and publish / verify (push) Successful in 2m45s

This commit is contained in:
2026-07-29 12:09:21 +03:30
parent c3083b137f
commit eba6b3565c
10 changed files with 472 additions and 22 deletions
+65 -2
View File
@@ -331,7 +331,7 @@ func TestTeamDayShowsPresentRemoteAndAbsent(t *testing.T) {
if err != nil {
t.Fatal(err)
}
request := httptest.NewRequest(http.MethodGet, "/day?date="+selectedDay, nil)
request := httptest.NewRequest(http.MethodGet, "/day?date="+selectedDay+"&view=day", nil)
request.AddCookie(&http.Cookie{Name: "teammate_session", Value: token})
response := httptest.NewRecorder()
s.http.Handler.ServeHTTP(response, request)
@@ -372,6 +372,14 @@ func TestTeamDayShowsPresentRemoteAndAbsent(t *testing.T) {
t.Fatalf("week page does not include %q", expected)
}
}
defaultRequest := httptest.NewRequest(http.MethodGet, "/day?date="+selectedDay, nil)
defaultRequest.AddCookie(&http.Cookie{Name: "teammate_session", Value: token})
defaultResponse := httptest.NewRecorder()
s.http.Handler.ServeHTTP(defaultResponse, defaultRequest)
if defaultResponse.Code != http.StatusOK || !strings.Contains(defaultResponse.Body.String(), "TEAM WEEK") {
t.Fatalf("default team view is not weekly: %d %s", defaultResponse.Code, defaultResponse.Body.String())
}
}
func TestWorkUpdateSubmissionFeedAndCSV(t *testing.T) {
@@ -487,6 +495,30 @@ func TestSharedBoardCreateAssignMoveAndPermissions(t *testing.T) {
t.Fatal(err)
}
adminCookie := &http.Cookie{Name: "teammate_session", Value: adminToken}
for _, tag := range []struct {
name string
color string
}{
{"Backend", "green"},
{"Urgent", "red"},
} {
response := formRequest(t, s.http.Handler, "/board/tags", url.Values{
"csrf": {adminCSRF},
"name": {tag.name},
"color": {tag.color},
}, adminCookie)
if response.Code != http.StatusSeeOther {
t.Fatalf("create board tag %s: %d %s", tag.name, response.Code, response.Body.String())
}
}
tags, err := s.store.BoardTags()
if err != nil || len(tags) != 2 {
t.Fatalf("board tags after create: %#v, %v", tags, err)
}
reusedTag, err := s.store.FindOrCreateBoardTag("backend", "red")
if err != nil || reusedTag.ID != tags[0].ID || reusedTag.Color != "green" {
t.Fatalf("existing inline tag was not reused: %#v, %v", reusedTag, err)
}
create := formRequest(t, s.http.Handler, "/board/tasks", url.Values{
"csrf": {adminCSRF},
"title": {"Ship the onboarding flow"},
@@ -494,6 +526,12 @@ func TestSharedBoardCreateAssignMoveAndPermissions(t *testing.T) {
"status": {"backlog"},
"assignee_id": {strconv.FormatInt(memberID, 10)},
"due_date": {"1405-05-06"},
"tag_ids": {
strconv.FormatInt(tags[0].ID, 10),
strconv.FormatInt(tags[1].ID, 10),
},
"new_tag_name": {"Frontend"},
"new_tag_color": {"blue"},
}, adminCookie)
if create.Code != http.StatusSeeOther || create.Header().Get("Location") != "/board?flash=Task+added+to+the+team+board" {
t.Fatalf("create board task: got %d location %q body %s", create.Code, create.Header().Get("Location"), create.Body.String())
@@ -505,6 +543,9 @@ func TestSharedBoardCreateAssignMoveAndPermissions(t *testing.T) {
if tasks[0].AssigneeName != "Board Member" || tasks[0].DueDate != "2026-07-28" {
t.Fatalf("unexpected assigned board task: %#v", tasks[0])
}
if len(tasks[0].Tags) != 3 {
t.Fatalf("task tags after create: %#v", tasks[0].Tags)
}
memberToken, memberCSRF, err := s.store.CreateSession(memberID)
if err != nil {
@@ -518,12 +559,34 @@ func TestSharedBoardCreateAssignMoveAndPermissions(t *testing.T) {
if boardResponse.Code != http.StatusOK {
t.Fatalf("board page status: %d body %s", boardResponse.Code, boardResponse.Body.String())
}
for _, expected := range []string{"Ship the onboarding flow", "Board Member", "1405-05-06", "/static/board.js", `data-board-column="backlog"`} {
for _, expected := range []string{"Ship the onboarding flow", "Board Member", "1405-05-06", "Manage tags", "Tags", "select multiple", "Create a new tag", "Backend", "Frontend", "Urgent", "/static/board.js", `data-board-column="backlog"`} {
if !strings.Contains(boardResponse.Body.String(), expected) {
t.Fatalf("board page does not include %q", expected)
}
}
tagPath := "/board/tasks/" + strconv.FormatInt(tasks[0].ID, 10) + "/tags"
setTags := formRequest(t, s.http.Handler, tagPath, url.Values{
"csrf": {memberCSRF},
"tag_ids": {strconv.FormatInt(tags[0].ID, 10)},
}, memberCookie)
if setTags.Code != http.StatusSeeOther {
t.Fatalf("set task tags status: %d", setTags.Code)
}
tasks, _ = s.store.BoardTasks()
if len(tasks[0].Tags) != 1 || tasks[0].Tags[0].ID != tags[0].ID {
t.Fatalf("task tags were not updated: %#v", tasks[0].Tags)
}
deleteTagPath := "/board/tags/" + strconv.FormatInt(tags[1].ID, 10) + "/delete"
deleteTag := formRequest(t, s.http.Handler, deleteTagPath, url.Values{"csrf": {adminCSRF}}, adminCookie)
if deleteTag.Code != http.StatusSeeOther {
t.Fatalf("delete tag status: %d", deleteTag.Code)
}
if remainingTags, _ := s.store.BoardTags(); len(remainingTags) != 2 {
t.Fatalf("tag manager did not delete tag: %#v", remainingTags)
}
movePath := "/board/tasks/" + strconv.FormatInt(tasks[0].ID, 10) + "/move"
move := formRequest(t, s.http.Handler, movePath, url.Values{
"csrf": {memberCSRF},