mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 19:11:25 +01:00
feat(time): readable time tables with totals and gh-style feedback
time list printed Go durations (1h30m0s), raw timestamps and no issue title. It now shows ID ISSUE USER SPENT WHEN with "#1 Login fails", "1h 30m" and relative times on a terminal, followed by the total of the listed time, and points at --mine when no repository is selected. --issue takes issue URLs, --username takes @me, and --since replaces --from, which still works. time create (also add and log) reports "✓ Logged 1h 25m on alice/x#1 (title)"; time delete and reset name the time they delete in their confirmation and ✓ line. Time records gain an issue_title JSON field. Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
5 files changed
+261
-96
No files matched your search
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.dev/tea/internal/clierr"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -23,9 +24,9 @@ func TestTimeListShowsRepositoryAndDurations(t *testing.T) {
|
||||
defer server.Close()
|
||||
deps, out, _ := serverDeps(t, server.URL)
|
||||
require.NoError(t, Execute(context.Background(), deps, []string{"time", "list", "--mine", "--total"}))
|
||||
require.Equal(t, "1 alice/project#3 alice 1h25m0s 2026-10-01T10:00:00Z\n"+
|
||||
"2 bob/tools#3 alice 15m0s 2026-10-02T10:00:00Z\n"+
|
||||
"Total on this page: 1h40m0s\n", out.String())
|
||||
require.Equal(t, "1\talice/project#3\talice\t1h 25m\t2026-10-01T10:00:00Z\n"+
|
||||
"2\tbob/tools#3\talice\t15m\t2026-10-02T10:00:00Z\n"+
|
||||
"Total: 1h 40m\n", out.String())
|
||||
out.Reset()
|
||||
require.NoError(t, Execute(context.Background(), deps, []string{"time", "list", "--mine", "--json=repository,issue_number"}))
|
||||
require.JSONEq(t, `[{"repository":"alice/project","issue_number":3},{"repository":"bob/tools","issue_number":3}]`, out.String())
|
||||
@@ -57,3 +58,56 @@ func TestTimeCreateReadsDurationsAndWholeSecondTimes(t *testing.T) {
|
||||
}
|
||||
require.Len(t, seconds, 3)
|
||||
}
|
||||
|
||||
func TestTimeTablesAndDoneLines(t *testing.T) {
|
||||
var changed []string
|
||||
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method + " " + r.URL.Path {
|
||||
case "GET /api/v1/repos/alice/project/times", "GET /api/v1/repos/alice/project/issues/1/times":
|
||||
if r.URL.Query().Get("page") != "1" {
|
||||
_, _ = w.Write([]byte(`[]`))
|
||||
return
|
||||
}
|
||||
_, _ = w.Write([]byte(`[{"id":8,"time":5400,"created":"2026-10-11T18:00:00Z","user_name":"alice","issue":{"number":1,"title":"Login fails","repository":{"full_name":"alice/project"}}},` +
|
||||
`{"id":9,"time":900,"created":"2026-10-11T19:00:00Z","user_name":"bob","issue":{"number":2,"title":"Docs"}}]`))
|
||||
case "POST /api/v1/repos/alice/project/issues/1/times":
|
||||
changed = append(changed, r.Method+" "+r.URL.Path)
|
||||
_, _ = w.Write([]byte(`{"id":10,"time":5100,"user_name":"alice","issue":{"number":1,"title":"Login fails"}}`))
|
||||
default:
|
||||
changed = append(changed, r.Method+" "+r.URL.Path)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
deps, out, diagnostics := serverDeps(t, server.URL)
|
||||
deps.Now = func() time.Time { return time.Date(2026, 10, 11, 20, 0, 0, 0, time.UTC) }
|
||||
deps.Getenv = serverEnv(server.URL, map[string]string{"NO_COLOR": "1"})
|
||||
deps.Streams.OutTTY = true
|
||||
run := func(args ...string) error {
|
||||
out.Reset()
|
||||
diagnostics.Reset()
|
||||
return Execute(context.Background(), deps, append(args, "-R", "alice/project", "--no-input"))
|
||||
}
|
||||
require.NoError(t, run("time", "list"))
|
||||
require.Equal(t, "Showing 2 of 2 time records in alice/project\n\n"+
|
||||
"ID ISSUE USER SPENT WHEN\n"+
|
||||
"8 #1 Login fails alice 1h 30m about 2 hours ago\n"+
|
||||
"9 #2 Docs bob 15m about 1 hour ago\n"+
|
||||
"Total: 1h 45m\n", out.String())
|
||||
require.NoError(t, run("time", "add", "#1", "1h 25m"))
|
||||
require.Equal(t, "✓ Logged 1h 25m on alice/project#1 (Login fails)\n", diagnostics.String())
|
||||
require.NoError(t, run("time", "delete", "1", "8", "--yes"))
|
||||
require.Equal(t, "✓ Deleted 1h 30m logged by alice on alice/project#1\n", diagnostics.String())
|
||||
require.NoError(t, run("time", "reset", "1", "--yes"))
|
||||
require.Equal(t, "✓ Deleted all time logged on alice/project#1\n", diagnostics.String())
|
||||
require.ErrorContains(t, run("time", "delete", "1", "77", "--yes"), "tracked time 77 not found in alice/project#1")
|
||||
require.EqualError(t, run("time", "delete", "1", "8"), "confirmation required to delete 1h 30m logged by alice on alice/project#1; rerun with --yes")
|
||||
require.Equal(t, []string{
|
||||
"POST /api/v1/repos/alice/project/issues/1/times",
|
||||
"DELETE /api/v1/repos/alice/project/issues/1/times/8",
|
||||
"DELETE /api/v1/repos/alice/project/issues/1/times",
|
||||
}, changed)
|
||||
for seconds, want := range map[int64]string{0: "0s", 45: "45s", 90: "1m 30s", 900: "15m", 3600: "1h", 5400: "1h 30m", 5410: "1h 30m"} {
|
||||
require.Equal(t, want, spent(seconds))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user