diff --git a/pkg/cmd/pr/list/fixtures/prList.json b/pkg/cmd/pr/list/fixtures/prList.json index 13d124f52..eb2b5d31f 100644 --- a/pkg/cmd/pr/list/fixtures/prList.json +++ b/pkg/cmd/pr/list/fixtures/prList.json @@ -8,6 +8,7 @@ "number": 32, "title": "New feature", "url": "https://github.com/monalisa/hello/pull/32", + "createtedAt": "2011-01-26T19:01:12Z", "headRefName": "feature", "state": "OPEN", "isDraft": true @@ -16,6 +17,7 @@ "number": 29, "title": "Fixed bad bug", "url": "https://github.com/monalisa/hello/pull/29", + "createtedAt": "2011-01-26T19:01:12Z", "headRefName": "bug-fix", "state": "OPEN", "isDraft": false, @@ -29,6 +31,7 @@ "state": "MERGED", "isDraft": false, "title": "Improve documentation", + "createtedAt": "2011-01-26T19:01:12Z", "url": "https://github.com/monalisa/hello/pull/28", "headRefName": "docs" } diff --git a/pkg/cmd/pr/list/list.go b/pkg/cmd/pr/list/list.go index bc20d1ee4..ff0f93788 100644 --- a/pkg/cmd/pr/list/list.go +++ b/pkg/cmd/pr/list/list.go @@ -5,6 +5,7 @@ import ( "net/http" "strconv" "strings" + "time" "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" @@ -120,6 +121,7 @@ var defaultFields = []string{ "headRepositoryOwner", "isCrossRepository", "isDraft", + "createdAt", } func listRun(opts *ListOptions) error { @@ -199,12 +201,20 @@ func listRun(opts *ListOptions) error { if table.IsTTY() { prNum = "#" + prNum } + now := time.Now() + ago := now.Sub(pr.CreatedAt) + table.AddField(prNum, nil, cs.ColorFromString(shared.ColorForPRState(pr))) table.AddField(text.ReplaceExcessiveWhitespace(pr.Title), nil, nil) table.AddField(pr.HeadLabel(), nil, cs.Cyan) if !table.IsTTY() { table.AddField(prStateWithDraft(&pr), nil, nil) } + if table.IsTTY() { + table.AddField(utils.FuzzyAgo(ago), nil, cs.Gray) + } else { + table.AddField(pr.CreatedAt.String(), nil, nil) + } table.EndRow() } err = table.Render() diff --git a/pkg/cmd/pr/list/list_test.go b/pkg/cmd/pr/list/list_test.go index 44f26c05d..318bad63d 100644 --- a/pkg/cmd/pr/list/list_test.go +++ b/pkg/cmd/pr/list/list_test.go @@ -4,6 +4,7 @@ import ( "bytes" "io" "net/http" + "regexp" "strings" "testing" @@ -72,14 +73,18 @@ func TestPRList(t *testing.T) { t.Fatal(err) } + out := output.String() + timeRE := regexp.MustCompile(`\d+ years`) + out = timeRE.ReplaceAllString(out, "X years") + assert.Equal(t, heredoc.Doc(` Showing 3 of 3 open pull requests in OWNER/REPO - #32 New feature feature - #29 Fixed bad bug hubot:bug-fix - #28 Improve documentation docs - `), output.String()) + #32 New feature feature about X years ago + #29 Fixed bad bug hubot:bug-fix about X years ago + #28 Improve documentation docs about X years ago + `), out) assert.Equal(t, ``, output.Stderr()) } @@ -96,10 +101,14 @@ func TestPRList_nontty(t *testing.T) { assert.Equal(t, "", output.Stderr()) - assert.Equal(t, `32 New feature feature DRAFT -29 Fixed bad bug hubot:bug-fix OPEN -28 Improve documentation docs MERGED -`, output.String()) + out := output.String() + timeRE := regexp.MustCompile(`\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d .\d\d\d\d UTC`) + out = timeRE.ReplaceAllString(out, "XXXX-XX-XX XX:XX:XX +XXXX UTC") + + assert.Equal(t, `32 New feature feature DRAFT XXXX-XX-XX XX:XX:XX +XXXX UTC +29 Fixed bad bug hubot:bug-fix OPEN XXXX-XX-XX XX:XX:XX +XXXX UTC +28 Improve documentation docs MERGED XXXX-XX-XX XX:XX:XX +XXXX UTC +`, out) } func TestPRList_filtering(t *testing.T) { diff --git a/pkg/cmd/pr/view/view.go b/pkg/cmd/pr/view/view.go index 20947a01d..884cc3d8d 100644 --- a/pkg/cmd/pr/view/view.go +++ b/pkg/cmd/pr/view/view.go @@ -5,6 +5,7 @@ import ( "sort" "strconv" "strings" + "time" "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" @@ -28,12 +29,15 @@ type ViewOptions struct { SelectorArg string BrowserMode bool Comments bool + + Now func() time.Time } func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Command { opts := &ViewOptions{ IO: f.IOStreams, Browser: f.Browser, + Now: time.Now, } cmd := &cobra.Command{ @@ -78,7 +82,7 @@ var defaultFields = []string{ "isDraft", "maintainerCanModify", "mergeable", "additions", "deletions", "commitsCount", "baseRefName", "headRefName", "headRepositoryOwner", "headRepository", "isCrossRepository", "reviewRequests", "reviews", "assignees", "labels", "projectCards", "milestone", - "comments", "reactionGroups", + "comments", "reactionGroups", "createdAt", } func viewRun(opts *ViewOptions) error { @@ -164,16 +168,19 @@ func printRawPrPreview(io *iostreams.IOStreams, pr *api.PullRequest) error { func printHumanPrPreview(opts *ViewOptions, pr *api.PullRequest) error { out := opts.IO.Out cs := opts.IO.ColorScheme() + now := opts.Now() + ago := now.Sub(pr.CreatedAt) // Header (Title and State) fmt.Fprintf(out, "%s #%d\n", cs.Bold(pr.Title), pr.Number) fmt.Fprintf(out, - "%s • %s wants to merge %s into %s from %s • %s %s \n", + "%s • %s wants to merge %s into %s from %s • %s • %s %s \n", shared.StateTitleWithColor(cs, *pr), pr.Author.Login, utils.Pluralize(pr.Commits.TotalCount, "commit"), pr.BaseRefName, pr.HeadRefName, + utils.FuzzyAgo(ago), cs.Green("+"+strconv.Itoa(pr.Additions)), cs.Red("-"+strconv.Itoa(pr.Deletions)), ) diff --git a/pkg/cmd/pr/view/view_test.go b/pkg/cmd/pr/view/view_test.go index 41e28f257..755e07542 100644 --- a/pkg/cmd/pr/view/view_test.go +++ b/pkg/cmd/pr/view/view_test.go @@ -7,6 +7,7 @@ import ( "io" "net/http" "os" + "regexp" "testing" "github.com/cli/cli/v2/api" @@ -352,7 +353,7 @@ func TestPRView_Preview(t *testing.T) { }, expectedOutputs: []string{ `Blueberries are from a fork #12`, - `Open.*nobody wants to merge 12 commits into master from blueberries.+100.-10`, + `Open.*nobody wants to merge 12 commits into master from blueberries . about X years ago.+100.-10`, `blueberries taste good`, `View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12`, }, @@ -365,7 +366,7 @@ func TestPRView_Preview(t *testing.T) { }, expectedOutputs: []string{ `Blueberries are from a fork #12`, - `Open.*nobody wants to merge 12 commits into master from blueberries.+100.-10`, + `Open.*nobody wants to merge 12 commits into master from blueberries . about X years ago.+100.-10`, `Reviewers:.*1 \(.*Requested.*\)\n`, `Assignees:.*marseilles, monaco\n`, `Labels:.*one, two, three, four, five\n`, @@ -397,7 +398,7 @@ func TestPRView_Preview(t *testing.T) { }, expectedOutputs: []string{ `Blueberries are from a fork #12`, - `Closed.*nobody wants to merge 12 commits into master from blueberries.+100.-10`, + `Closed.*nobody wants to merge 12 commits into master from blueberries . about X years ago.+100.-10`, `blueberries taste good`, `View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12`, }, @@ -410,7 +411,7 @@ func TestPRView_Preview(t *testing.T) { }, expectedOutputs: []string{ `Blueberries are from a fork #12`, - `Merged.*nobody wants to merge 12 commits into master from blueberries.+100.-10`, + `Merged.*nobody wants to merge 12 commits into master from blueberries . about X years ago.+100.-10`, `blueberries taste good`, `View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12`, }, @@ -423,7 +424,7 @@ func TestPRView_Preview(t *testing.T) { }, expectedOutputs: []string{ `Blueberries are from a fork #12`, - `Draft.*nobody wants to merge 12 commits into master from blueberries.+100.-10`, + `Draft.*nobody wants to merge 12 commits into master from blueberries . about X years ago.+100.-10`, `blueberries taste good`, `View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12`, }, @@ -446,8 +447,12 @@ func TestPRView_Preview(t *testing.T) { assert.Equal(t, "", output.Stderr()) + out := output.String() + timeRE := regexp.MustCompile(`\d+ years`) + out = timeRE.ReplaceAllString(out, "X years") + //nolint:staticcheck // prefer exact matchers over ExpectLines - test.ExpectLines(t, output.String(), tc.expectedOutputs...) + test.ExpectLines(t, out, tc.expectedOutputs...) }) } }