diff --git a/api/queries_comments.go b/api/queries_comments.go new file mode 100644 index 000000000..70ea382a1 --- /dev/null +++ b/api/queries_comments.go @@ -0,0 +1,65 @@ +package api + +import ( + "context" + "time" + + "github.com/cli/cli/internal/ghrepo" + "github.com/shurcooL/githubv4" +) + +type Comments struct { + Nodes []Comment + TotalCount int + PageInfo PageInfo +} + +type Comment struct { + Author Author + AuthorAssociation string + Body string + CreatedAt time.Time + IncludesCreatedEdit bool + ReactionGroups ReactionGroups +} + +type PageInfo struct { + HasNextPage bool + EndCursor string +} + +func CommentsForIssue(client *Client, repo ghrepo.Interface, issue *Issue) (*Comments, error) { + type response struct { + Repository struct { + Issue struct { + Comments Comments `graphql:"comments(first: 100, after: $endCursor)"` + } `graphql:"issue(number: $number)"` + } `graphql:"repository(owner: $owner, name: $repo)"` + } + + variables := map[string]interface{}{ + "owner": githubv4.String(repo.RepoOwner()), + "repo": githubv4.String(repo.RepoName()), + "number": githubv4.Int(issue.Number), + "endCursor": (*githubv4.String)(nil), + } + + gql := graphQLClient(client.http, repo.RepoHost()) + + var comments []Comment + for { + var query response + err := gql.QueryNamed(context.Background(), "CommentsForIssue", &query, variables) + if err != nil { + return nil, err + } + + comments = append(comments, query.Repository.Issue.Comments.Nodes...) + if !query.Repository.Issue.Comments.PageInfo.HasNextPage { + break + } + variables["endCursor"] = githubv4.String(query.Repository.Issue.Comments.PageInfo.EndCursor) + } + + return &Comments{Nodes: comments, TotalCount: len(comments)}, nil +} diff --git a/api/queries_issue.go b/api/queries_issue.go index 1aa83ad89..17f32eabd 100644 --- a/api/queries_issue.go +++ b/api/queries_issue.go @@ -33,7 +33,7 @@ type Issue struct { Body string CreatedAt time.Time UpdatedAt time.Time - Comments IssueComments + Comments Comments Author Author Assignees struct { Nodes []struct { @@ -68,20 +68,6 @@ type IssuesDisabledError struct { error } -type IssueComments struct { - Nodes []IssueComment - TotalCount int -} - -type IssueComment struct { - Author Author - AuthorAssociation string - Body string - CreatedAt time.Time - IncludesCreatedEdit bool - ReactionGroups ReactionGroups -} - type Author struct { Login string } @@ -335,7 +321,7 @@ loop: return &res, nil } -func IssueByNumber(client *Client, repo ghrepo.Interface, number, comments int) (*Issue, error) { +func IssueByNumber(client *Client, repo ghrepo.Interface, number int) (*Issue, error) { type response struct { Repository struct { Issue Issue @@ -344,7 +330,7 @@ func IssueByNumber(client *Client, repo ghrepo.Interface, number, comments int) } query := ` - query IssueByNumber($owner: String!, $repo: String!, $issue_number: Int!, $comments: Int!) { + query IssueByNumber($owner: String!, $repo: String!, $issue_number: Int!) { repository(owner: $owner, name: $repo) { hasIssuesEnabled issue(number: $issue_number) { @@ -356,7 +342,7 @@ func IssueByNumber(client *Client, repo ghrepo.Interface, number, comments int) author { login } - comments(last: $comments) { + comments(last: 1) { nodes { author { login @@ -417,7 +403,6 @@ func IssueByNumber(client *Client, repo ghrepo.Interface, number, comments int) "owner": repo.RepoOwner(), "repo": repo.RepoName(), "issue_number": number, - "comments": comments, } var resp response diff --git a/api/reaction_groups.go b/api/reaction_groups.go index 68c527079..381504dd7 100644 --- a/api/reaction_groups.go +++ b/api/reaction_groups.go @@ -1,10 +1,5 @@ package api -import ( - "fmt" - "strings" -) - type ReactionGroups []ReactionGroup type ReactionGroup struct { @@ -16,28 +11,12 @@ type ReactionGroupUsers struct { TotalCount int } -func (rg ReactionGroup) String() string { - c := rg.Users.TotalCount - if c == 0 { - return "" - } - e := reactionEmoji[rg.Content] - if e == "" { - return "" - } - return fmt.Sprintf("%v %s", c, e) +func (rg ReactionGroup) Count() int { + return rg.Users.TotalCount } -func (rgs ReactionGroups) String() string { - var rs []string - - for _, rg := range rgs { - if r := rg.String(); r != "" { - rs = append(rs, r) - } - } - - return strings.Join(rs, " • ") +func (rg ReactionGroup) Emoji() string { + return reactionEmoji[rg.Content] } var reactionEmoji = map[string]string{ diff --git a/api/reaction_groups_test.go b/api/reaction_groups_test.go index 4e501c338..e30a9e1f8 100644 --- a/api/reaction_groups_test.go +++ b/api/reaction_groups_test.go @@ -8,48 +8,93 @@ import ( func Test_String(t *testing.T) { tests := map[string]struct { - rgs ReactionGroups - output string + rg ReactionGroup + emoji string + count int }{ - "empty reaction groups": { - rgs: []ReactionGroup{}, - output: `^$`, + "empty reaction group": { + rg: ReactionGroup{}, + emoji: "", + count: 0, }, - "non-empty reaction groups": { - rgs: []ReactionGroup{ - { - Content: "LAUGH", - Users: ReactionGroupUsers{TotalCount: 0}, - }, - { - Content: "HOORAY", - Users: ReactionGroupUsers{TotalCount: 1}, - }, - { - Content: "CONFUSED", - Users: ReactionGroupUsers{TotalCount: 0}, - }, - { - Content: "HEART", - Users: ReactionGroupUsers{TotalCount: 2}, - }, + "unknown reaction group": { + rg: ReactionGroup{ + Content: "UNKNOWN", + Users: ReactionGroupUsers{TotalCount: 1}, }, - output: `^1 \x{1f389} • 2 \x{2764}\x{fe0f}$`, + emoji: "", + count: 1, }, - "reaction groups with unmapped emoji": { - rgs: []ReactionGroup{ - { - Content: "UNKNOWN", - Users: ReactionGroupUsers{TotalCount: 1}, - }, + "thumbs up reaction group": { + rg: ReactionGroup{ + Content: "THUMBS_UP", + Users: ReactionGroupUsers{TotalCount: 2}, }, - output: `^$`, + emoji: "\U0001f44d", + count: 2, + }, + "thumbs down reaction group": { + rg: ReactionGroup{ + Content: "THUMBS_DOWN", + Users: ReactionGroupUsers{TotalCount: 3}, + }, + emoji: "\U0001f44e", + count: 3, + }, + "laugh reaction group": { + rg: ReactionGroup{ + Content: "LAUGH", + Users: ReactionGroupUsers{TotalCount: 4}, + }, + emoji: "\U0001f604", + count: 4, + }, + "hooray reaction group": { + rg: ReactionGroup{ + Content: "HOORAY", + Users: ReactionGroupUsers{TotalCount: 5}, + }, + emoji: "\U0001f389", + count: 5, + }, + "confused reaction group": { + rg: ReactionGroup{ + Content: "CONFUSED", + Users: ReactionGroupUsers{TotalCount: 6}, + }, + emoji: "\U0001f615", + count: 6, + }, + "heart reaction group": { + rg: ReactionGroup{ + Content: "HEART", + Users: ReactionGroupUsers{TotalCount: 7}, + }, + emoji: "\u2764\ufe0f", + count: 7, + }, + "rocket reaction group": { + rg: ReactionGroup{ + Content: "ROCKET", + Users: ReactionGroupUsers{TotalCount: 8}, + }, + emoji: "\U0001f680", + count: 8, + }, + "eyes reaction group": { + rg: ReactionGroup{ + Content: "EYES", + Users: ReactionGroupUsers{TotalCount: 9}, + }, + emoji: "\U0001f440", + count: 9, }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { - assert.Regexp(t, tt.output, tt.rgs.String()) + assert.Equal(t, tt.emoji, tt.rg.Emoji()) + assert.Equal(t, tt.count, tt.rg.Count()) }) } } diff --git a/pkg/cmd/issue/shared/lookup.go b/pkg/cmd/issue/shared/lookup.go index 09be5bab6..675574f7c 100644 --- a/pkg/cmd/issue/shared/lookup.go +++ b/pkg/cmd/issue/shared/lookup.go @@ -11,7 +11,7 @@ import ( "github.com/cli/cli/internal/ghrepo" ) -func IssueWithCommentsFromArg(apiClient *api.Client, baseRepoFn func() (ghrepo.Interface, error), arg string, comments int) (*api.Issue, ghrepo.Interface, error) { +func IssueFromArg(apiClient *api.Client, baseRepoFn func() (ghrepo.Interface, error), arg string) (*api.Issue, ghrepo.Interface, error) { issueNumber, baseRepo := issueMetadataFromURL(arg) if baseRepo == nil { @@ -30,14 +30,10 @@ func IssueWithCommentsFromArg(apiClient *api.Client, baseRepoFn func() (ghrepo.I } } - issue, err := issueFromNumber(apiClient, baseRepo, issueNumber, comments) + issue, err := issueFromNumber(apiClient, baseRepo, issueNumber) return issue, baseRepo, err } -func IssueFromArg(apiClient *api.Client, baseRepoFn func() (ghrepo.Interface, error), arg string) (*api.Issue, ghrepo.Interface, error) { - return IssueWithCommentsFromArg(apiClient, baseRepoFn, arg, 0) -} - var issueURLRE = regexp.MustCompile(`^/([^/]+)/([^/]+)/issues/(\d+)`) func issueMetadataFromURL(s string) (int, ghrepo.Interface) { @@ -60,6 +56,6 @@ func issueMetadataFromURL(s string) (int, ghrepo.Interface) { return issueNumber, repo } -func issueFromNumber(apiClient *api.Client, repo ghrepo.Interface, issueNumber, comments int) (*api.Issue, error) { - return api.IssueByNumber(apiClient, repo, issueNumber, comments) +func issueFromNumber(apiClient *api.Client, repo ghrepo.Interface, issueNumber int) (*api.Issue, error) { + return api.IssueByNumber(apiClient, repo, issueNumber) } diff --git a/pkg/cmd/issue/view/fixtures/issueView_previewFullComments.json b/pkg/cmd/issue/view/fixtures/issueView_previewFullComments.json index 78fe07597..d2cd27f30 100644 --- a/pkg/cmd/issue/view/fixtures/issueView_previewFullComments.json +++ b/pkg/cmd/issue/view/fixtures/issueView_previewFullComments.json @@ -1,81 +1,7 @@ { "data": { "repository": { - "hasIssuesEnabled": true, "issue": { - "number": 123, - "body": "some body", - "title": "some title", - "state": "OPEN", - "createdAt": "2020-01-01T12:00:00Z", - "author": { - "login": "marseilles" - }, - "assignees": { - "nodes": [], - "totalCount": 0 - }, - "labels": { - "nodes": [], - "totalCount": 0 - }, - "projectcards": { - "nodes": [], - "totalCount": 0 - }, - "milestone": { - "title": "" - }, - "reactionGroups": [ - { - "content": "CONFUSED", - "users": { - "totalCount": 0 - } - }, - { - "content": "EYES", - "users": { - "totalCount": 0 - } - }, - { - "content": "HEART", - "users": { - "totalCount": 0 - } - }, - { - "content": "HOORAY", - "users": { - "totalCount": 0 - } - }, - { - "content": "LAUGH", - "users": { - "totalCount": 0 - } - }, - { - "content": "ROCKET", - "users": { - "totalCount": 0 - } - }, - { - "content": "THUMBS_DOWN", - "users": { - "totalCount": 0 - } - }, - { - "content": "THUMBS_UP", - "users": { - "totalCount": 0 - } - } - ], "comments": { "nodes": [ { @@ -375,8 +301,7 @@ } ], "totalCount": 5 - }, - "url": "https://github.com/OWNER/REPO/issues/123" + } } } } diff --git a/pkg/cmd/issue/view/fixtures/issueView_previewThreeComments.json b/pkg/cmd/issue/view/fixtures/issueView_previewThreeComments.json deleted file mode 100644 index 74d5945e1..000000000 --- a/pkg/cmd/issue/view/fixtures/issueView_previewThreeComments.json +++ /dev/null @@ -1,265 +0,0 @@ -{ - "data": { - "repository": { - "hasIssuesEnabled": true, - "issue": { - "number": 123, - "body": "some body", - "title": "some title", - "state": "OPEN", - "createdAt": "2020-01-01T12:00:00Z", - "author": { - "login": "marseilles" - }, - "assignees": { - "nodes": [], - "totalCount": 0 - }, - "labels": { - "nodes": [], - "totalCount": 0 - }, - "projectcards": { - "nodes": [], - "totalCount": 0 - }, - "milestone": { - "title": "" - }, - "reactionGroups": [ - { - "content": "CONFUSED", - "users": { - "totalCount": 0 - } - }, - { - "content": "EYES", - "users": { - "totalCount": 0 - } - }, - { - "content": "HEART", - "users": { - "totalCount": 0 - } - }, - { - "content": "HOORAY", - "users": { - "totalCount": 0 - } - }, - { - "content": "LAUGH", - "users": { - "totalCount": 0 - } - }, - { - "content": "ROCKET", - "users": { - "totalCount": 0 - } - }, - { - "content": "THUMBS_DOWN", - "users": { - "totalCount": 0 - } - }, - { - "content": "THUMBS_UP", - "users": { - "totalCount": 0 - } - } - ], - "comments": { - "nodes": [ - { - "author": { - "login": "elvisp" - }, - "authorAssociation": "MEMBER", - "body": "Comment 3", - "createdAt": "2020-01-01T12:00:00Z", - "includesCreatedEdit": false, - "reactionGroups": [ - { - "content": "CONFUSED", - "users": { - "totalCount": 0 - } - }, - { - "content": "EYES", - "users": { - "totalCount": 0 - } - }, - { - "content": "HEART", - "users": { - "totalCount": 0 - } - }, - { - "content": "HOORAY", - "users": { - "totalCount": 0 - } - }, - { - "content": "LAUGH", - "users": { - "totalCount": 0 - } - }, - { - "content": "ROCKET", - "users": { - "totalCount": 0 - } - }, - { - "content": "THUMBS_DOWN", - "users": { - "totalCount": 0 - } - }, - { - "content": "THUMBS_UP", - "users": { - "totalCount": 0 - } - } - ] - }, - { - "author": { - "login": "loislane" - }, - "authorAssociation": "OWNER", - "body": "Comment 4", - "createdAt": "2020-01-01T12:00:00Z", - "includesCreatedEdit": false, - "reactionGroups": [ - { - "content": "CONFUSED", - "users": { - "totalCount": 0 - } - }, - { - "content": "EYES", - "users": { - "totalCount": 0 - } - }, - { - "content": "HEART", - "users": { - "totalCount": 0 - } - }, - { - "content": "HOORAY", - "users": { - "totalCount": 0 - } - }, - { - "content": "LAUGH", - "users": { - "totalCount": 0 - } - }, - { - "content": "ROCKET", - "users": { - "totalCount": 0 - } - }, - { - "content": "THUMBS_DOWN", - "users": { - "totalCount": 0 - } - }, - { - "content": "THUMBS_UP", - "users": { - "totalCount": 0 - } - } - ] - }, - { - "author": { - "login": "marseilles" - }, - "authorAssociation": "COLLABORATOR", - "body": "Comment 5", - "createdAt": "2020-01-01T12:00:00Z", - "includesCreatedEdit": false, - "reactionGroups": [ - { - "content": "CONFUSED", - "users": { - "totalCount": 0 - } - }, - { - "content": "EYES", - "users": { - "totalCount": 0 - } - }, - { - "content": "HEART", - "users": { - "totalCount": 0 - } - }, - { - "content": "HOORAY", - "users": { - "totalCount": 0 - } - }, - { - "content": "LAUGH", - "users": { - "totalCount": 0 - } - }, - { - "content": "ROCKET", - "users": { - "totalCount": 0 - } - }, - { - "content": "THUMBS_DOWN", - "users": { - "totalCount": 0 - } - }, - { - "content": "THUMBS_UP", - "users": { - "totalCount": 0 - } - } - ] - } - ], - "totalCount": 5 - }, - "url": "https://github.com/OWNER/REPO/issues/123" - } - } - } -} diff --git a/pkg/cmd/issue/view/view.go b/pkg/cmd/issue/view/view.go index b1f4cba82..063be82c8 100644 --- a/pkg/cmd/issue/view/view.go +++ b/pkg/cmd/issue/view/view.go @@ -8,6 +8,7 @@ import ( "time" "github.com/MakeNowJust/heredoc" + "github.com/briandowns/spinner" "github.com/cli/cli/api" "github.com/cli/cli/internal/config" "github.com/cli/cli/internal/ghrepo" @@ -27,10 +28,9 @@ type ViewOptions struct { IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) - SelectorArg string - WebMode bool - Comments int - CommentsProvided bool + SelectorArg string + WebMode bool + Comments bool } func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Command { @@ -55,8 +55,6 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman // support `-R, --repo` override opts.BaseRepo = f.BaseRepo - opts.CommentsProvided = cmd.Flags().Changed("comments") - if len(args) > 0 { opts.SelectorArg = args[0] } @@ -69,8 +67,7 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman } cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "Open an issue in the browser") - cmd.Flags().IntVarP(&opts.Comments, "comments", "c", 1, "View issue comments sorted by most recent") - cmd.Flags().Lookup("comments").NoOptDefVal = "30" + cmd.Flags().BoolVarP(&opts.Comments, "comments", "c", false, "View issue comments") return cmd } @@ -82,20 +79,37 @@ func viewRun(opts *ViewOptions) error { } apiClient := api.NewClientFromHTTP(httpClient) - issue, _, err := issueShared.IssueWithCommentsFromArg(apiClient, opts.BaseRepo, opts.SelectorArg, opts.Comments) + issue, repo, err := issueShared.IssueFromArg(apiClient, opts.BaseRepo, opts.SelectorArg) if err != nil { return err } - openURL := issue.URL - if opts.WebMode { + openURL := issue.URL if opts.IO.IsStdoutTTY() { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) } return utils.OpenInBrowser(openURL) } + if opts.Comments { + var s *spinner.Spinner + if opts.IO.IsStdoutTTY() { + s = utils.Spinner(opts.IO.ErrOut) + utils.StartSpinner(s) + } + + comments, err := api.CommentsForIssue(apiClient, repo, issue) + if err != nil { + return err + } + issue.Comments = *comments + + if opts.IO.IsStdoutTTY() { + utils.StopSpinner(s) + } + } + opts.IO.DetectTerminalTheme() err = opts.IO.StartPager() @@ -108,7 +122,7 @@ func viewRun(opts *ViewOptions) error { return printHumanIssuePreview(opts.IO, issue) } - if opts.CommentsProvided { + if opts.Comments { return printRawIssueComments(opts.IO.Out, issue) } @@ -144,7 +158,7 @@ func printRawIssueComments(out io.Writer, issue *api.Issue) error { return nil } -func rawIssueComment(comment api.IssueComment) string { +func rawIssueComment(comment api.Comment) string { var b strings.Builder fmt.Fprintf(&b, "author:\t%s\n", comment.Author.Login) fmt.Fprintf(&b, "association:\t%s\n", strings.ToLower(comment.AuthorAssociation)) @@ -172,7 +186,7 @@ func printHumanIssuePreview(io *iostreams.IOStreams, issue *api.Issue) error { ) // Reactions - if reactions := issue.ReactionGroups.String(); reactions != "" { + if reactions := reactionGroupList(issue.ReactionGroups); reactions != "" { fmt.Fprint(out, reactions) fmt.Fprintln(out) } @@ -210,7 +224,7 @@ func printHumanIssuePreview(io *iostreams.IOStreams, issue *api.Issue) error { // Comments if issue.Comments.TotalCount > 0 { - comments, err := issueComments(io, issue.Comments) + comments, err := issueCommentList(io, issue.Comments) if err != nil { return err } @@ -223,20 +237,20 @@ func printHumanIssuePreview(io *iostreams.IOStreams, issue *api.Issue) error { return nil } -func issueComments(io *iostreams.IOStreams, comments api.IssueComments) (string, error) { +func issueCommentList(io *iostreams.IOStreams, comments api.Comments) (string, error) { var b strings.Builder cs := io.ColorScheme() retrievedCount := len(comments.Nodes) hiddenCount := comments.TotalCount - retrievedCount if hiddenCount > 0 { - fmt.Fprint(&b, cs.Gray(fmt.Sprintf("———————— Hiding %v comments ————————", hiddenCount))) + fmt.Fprint(&b, cs.Gray(fmt.Sprintf("———————— Not showing %s ————————", utils.Pluralize(hiddenCount, "comment")))) fmt.Fprintf(&b, "\n\n\n") } for i, comment := range comments.Nodes { last := i+1 == retrievedCount - cmt, err := issueComment(io, comment, last) + cmt, err := formatIssueComment(io, comment, last) if err != nil { return "", err } @@ -254,7 +268,7 @@ func issueComments(io *iostreams.IOStreams, comments api.IssueComments) (string, return b.String(), nil } -func issueComment(io *iostreams.IOStreams, comment api.IssueComment, newest bool) (string, error) { +func formatIssueComment(io *iostreams.IOStreams, comment api.Comment, newest bool) (string, error) { var b strings.Builder cs := io.ColorScheme() @@ -274,7 +288,7 @@ func issueComment(io *iostreams.IOStreams, comment api.IssueComment, newest bool fmt.Fprintln(&b) // Reactions - if reactions := comment.ReactionGroups.String(); reactions != "" { + if reactions := reactionGroupList(comment.ReactionGroups); reactions != "" { fmt.Fprint(&b, reactions) fmt.Fprintln(&b) } @@ -334,3 +348,27 @@ func issueProjectList(issue api.Issue) string { } return list } + +func reactionGroupList(rgs api.ReactionGroups) string { + var rs []string + + for _, rg := range rgs { + if r := formatReactionGroup(rg); r != "" { + rs = append(rs, r) + } + } + + return strings.Join(rs, " • ") +} + +func formatReactionGroup(rg api.ReactionGroup) string { + c := rg.Count() + if c == 0 { + return "" + } + e := rg.Emoji() + if e == "" { + return "" + } + return fmt.Sprintf("%v %s", c, e) +} diff --git a/pkg/cmd/issue/view/view_test.go b/pkg/cmd/issue/view/view_test.go index 977e73dad..2b94c5a70 100644 --- a/pkg/cmd/issue/view/view_test.go +++ b/pkg/cmd/issue/view/view_test.go @@ -2,11 +2,13 @@ package view import ( "bytes" + "fmt" "io/ioutil" "net/http" "os/exec" "testing" + "github.com/briandowns/spinner" "github.com/cli/cli/internal/config" "github.com/cli/cli/internal/ghrepo" "github.com/cli/cli/internal/run" @@ -14,6 +16,7 @@ import ( "github.com/cli/cli/pkg/httpmock" "github.com/cli/cli/pkg/iostreams" "github.com/cli/cli/test" + "github.com/cli/cli/utils" "github.com/google/shlex" "github.com/stretchr/testify/assert" ) @@ -337,26 +340,31 @@ func TestIssueView_web_urlArg(t *testing.T) { func TestIssueView_tty_Comments(t *testing.T) { tests := map[string]struct { cli string - fixture string + fixtures map[string]string expectedOutputs []string wantsErr bool }{ "without comments flag": { - cli: "123", - fixture: "./fixtures/issueView_previewSingleComment.json", + cli: "123", + fixtures: map[string]string{ + "IssueByNumber": "./fixtures/issueView_previewSingleComment.json", + }, expectedOutputs: []string{ `some title`, `some body`, - `———————— Hiding 4 comments ————————`, + `———————— Not showing 4 comments ————————`, `marseilles \(collaborator\) • Jan 1, 2020 • Newest comment`, `Comment 5`, `Use --comments to view the full conversation`, `View this issue on GitHub: https://github.com/OWNER/REPO/issues/123`, }, }, - "with default comments flag": { - cli: "123 --comments", - fixture: "./fixtures/issueView_previewFullComments.json", + "with comments flag": { + cli: "123 --comments", + fixtures: map[string]string{ + "IssueByNumber": "./fixtures/issueView_previewSingleComment.json", + "CommentsForIssue": "./fixtures/issueView_previewFullComments.json", + }, expectedOutputs: []string{ `some title`, `some body`, @@ -374,35 +382,19 @@ func TestIssueView_tty_Comments(t *testing.T) { `View this issue on GitHub: https://github.com/OWNER/REPO/issues/123`, }, }, - "with specified comments flag": { - cli: "123 --comments=3", - fixture: "./fixtures/issueView_previewThreeComments.json", - expectedOutputs: []string{ - `some title`, - `some body`, - `———————— Hiding 2 comments ————————`, - `elvisp \(member\) • Jan 1, 2020`, - `Comment 3`, - `loislane \(owner\) • Jan 1, 2020`, - `Comment 4`, - `marseilles \(collaborator\) • Jan 1, 2020 • Newest comment`, - `Comment 5`, - `Use --comments to view the full conversation`, - `View this issue on GitHub: https://github.com/OWNER/REPO/issues/123`, - }, - }, - "with incorrect comments flag": { + "with invalid comments flag": { cli: "123 --comments 3", - fixture: "", wantsErr: true, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { + stubSpinner() http := &httpmock.Registry{} defer http.Verify(t) - if tc.fixture != "" { - http.Register(httpmock.GraphQL(`query IssueByNumber\b`), httpmock.FileResponse(tc.fixture)) + for name, file := range tc.fixtures { + name := fmt.Sprintf(`query %s\b`, name) + http.Register(httpmock.GraphQL(name), httpmock.FileResponse(file)) } output, err := runCommand(http, true, tc.cli) if tc.wantsErr { @@ -419,13 +411,15 @@ func TestIssueView_tty_Comments(t *testing.T) { func TestIssueView_nontty_Comments(t *testing.T) { tests := map[string]struct { cli string - fixture string + fixtures map[string]string expectedOutputs []string wantsErr bool }{ "without comments flag": { - cli: "123", - fixture: "./fixtures/issueView_previewSingleComment.json", + cli: "123", + fixtures: map[string]string{ + "IssueByNumber": "./fixtures/issueView_previewSingleComment.json", + }, expectedOutputs: []string{ `title:\tsome title`, `state:\tOPEN`, @@ -434,9 +428,12 @@ func TestIssueView_nontty_Comments(t *testing.T) { `some body`, }, }, - "with default comments flag": { - cli: "123 --comments", - fixture: "./fixtures/issueView_previewFullComments.json", + "with comments flag": { + cli: "123 --comments", + fixtures: map[string]string{ + "IssueByNumber": "./fixtures/issueView_previewSingleComment.json", + "CommentsForIssue": "./fixtures/issueView_previewFullComments.json", + }, expectedOutputs: []string{ `author:\tmonalisa`, `association:\t`, @@ -460,27 +457,8 @@ func TestIssueView_nontty_Comments(t *testing.T) { `Comment 5`, }, }, - "with specified comments flag": { - cli: "123 --comments=3", - fixture: "./fixtures/issueView_previewThreeComments.json", - expectedOutputs: []string{ - `author:\telvisp`, - `association:\tmember`, - `edited:\tfalse`, - `Comment 3`, - `author:\tloislane`, - `association:\towner`, - `edited:\tfalse`, - `Comment 4`, - `author:\tmarseilles`, - `association:\tcollaborator`, - `edited:\tfalse`, - `Comment 5`, - }, - }, - "with incorrect comments flag": { + "with invalid comments flag": { cli: "123 --comments 3", - fixture: "", wantsErr: true, }, } @@ -488,8 +466,9 @@ func TestIssueView_nontty_Comments(t *testing.T) { t.Run(name, func(t *testing.T) { http := &httpmock.Registry{} defer http.Verify(t) - if tc.fixture != "" { - http.Register(httpmock.GraphQL(`query IssueByNumber\b`), httpmock.FileResponse(tc.fixture)) + for name, file := range tc.fixtures { + name := fmt.Sprintf(`query %s\b`, name) + http.Register(httpmock.GraphQL(name), httpmock.FileResponse(file)) } output, err := runCommand(http, false, tc.cli) if tc.wantsErr { @@ -502,3 +481,8 @@ func TestIssueView_nontty_Comments(t *testing.T) { }) } } + +func stubSpinner() { + utils.StartSpinner = func(_ *spinner.Spinner) {} + utils.StopSpinner = func(_ *spinner.Spinner) {} +}