github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/pr/diff/diff_test.go (about) 1 package diff 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "net/http" 8 "strings" 9 "testing" 10 11 "github.com/ungtb10d/cli/v2/api" 12 "github.com/ungtb10d/cli/v2/internal/browser" 13 "github.com/ungtb10d/cli/v2/internal/ghrepo" 14 "github.com/ungtb10d/cli/v2/pkg/cmd/pr/shared" 15 "github.com/ungtb10d/cli/v2/pkg/cmdutil" 16 "github.com/ungtb10d/cli/v2/pkg/httpmock" 17 "github.com/ungtb10d/cli/v2/pkg/iostreams" 18 "github.com/google/shlex" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 ) 22 23 func Test_NewCmdDiff(t *testing.T) { 24 tests := []struct { 25 name string 26 args string 27 isTTY bool 28 want DiffOptions 29 wantErr string 30 }{ 31 { 32 name: "name only", 33 args: "--name-only", 34 want: DiffOptions{ 35 NameOnly: true, 36 }, 37 }, 38 { 39 name: "number argument", 40 args: "123", 41 isTTY: true, 42 want: DiffOptions{ 43 SelectorArg: "123", 44 UseColor: true, 45 }, 46 }, 47 { 48 name: "no argument", 49 args: "", 50 isTTY: true, 51 want: DiffOptions{ 52 SelectorArg: "", 53 UseColor: true, 54 }, 55 }, 56 { 57 name: "no color when redirected", 58 args: "", 59 isTTY: false, 60 want: DiffOptions{ 61 SelectorArg: "", 62 UseColor: false, 63 }, 64 }, 65 { 66 name: "force color", 67 args: "--color always", 68 isTTY: false, 69 want: DiffOptions{ 70 SelectorArg: "", 71 UseColor: true, 72 }, 73 }, 74 { 75 name: "disable color", 76 args: "--color never", 77 isTTY: true, 78 want: DiffOptions{ 79 SelectorArg: "", 80 UseColor: false, 81 }, 82 }, 83 { 84 name: "no argument with --repo override", 85 args: "-R owner/repo", 86 isTTY: true, 87 wantErr: "argument required when using the `--repo` flag", 88 }, 89 { 90 name: "invalid --color argument", 91 args: "--color doublerainbow", 92 isTTY: true, 93 wantErr: "invalid argument \"doublerainbow\" for \"--color\" flag: valid values are {always|never|auto}", 94 }, 95 { 96 name: "web mode", 97 args: "123 --web", 98 isTTY: true, 99 want: DiffOptions{ 100 SelectorArg: "123", 101 UseColor: true, 102 BrowserMode: true, 103 }, 104 }, 105 } 106 for _, tt := range tests { 107 t.Run(tt.name, func(t *testing.T) { 108 ios, _, _, _ := iostreams.Test() 109 ios.SetStdoutTTY(tt.isTTY) 110 ios.SetStdinTTY(tt.isTTY) 111 ios.SetStderrTTY(tt.isTTY) 112 ios.SetColorEnabled(tt.isTTY) 113 114 f := &cmdutil.Factory{ 115 IOStreams: ios, 116 } 117 118 var opts *DiffOptions 119 cmd := NewCmdDiff(f, func(o *DiffOptions) error { 120 opts = o 121 return nil 122 }) 123 cmd.PersistentFlags().StringP("repo", "R", "", "") 124 125 argv, err := shlex.Split(tt.args) 126 require.NoError(t, err) 127 cmd.SetArgs(argv) 128 129 cmd.SetIn(&bytes.Buffer{}) 130 cmd.SetOut(io.Discard) 131 cmd.SetErr(io.Discard) 132 133 _, err = cmd.ExecuteC() 134 if tt.wantErr != "" { 135 require.EqualError(t, err, tt.wantErr) 136 return 137 } else { 138 require.NoError(t, err) 139 } 140 141 assert.Equal(t, tt.want.SelectorArg, opts.SelectorArg) 142 assert.Equal(t, tt.want.UseColor, opts.UseColor) 143 assert.Equal(t, tt.want.BrowserMode, opts.BrowserMode) 144 }) 145 } 146 } 147 148 func Test_diffRun(t *testing.T) { 149 pr := &api.PullRequest{Number: 123, URL: "https://github.com/OWNER/REPO/pull/123"} 150 151 tests := []struct { 152 name string 153 opts DiffOptions 154 wantFields []string 155 wantStdout string 156 wantStderr string 157 wantBrowsedURL string 158 httpStubs func(*httpmock.Registry) 159 }{ 160 { 161 name: "no color", 162 opts: DiffOptions{ 163 SelectorArg: "123", 164 UseColor: false, 165 Patch: false, 166 }, 167 wantFields: []string{"number"}, 168 wantStdout: fmt.Sprintf(testDiff, "", "", "", ""), 169 httpStubs: func(reg *httpmock.Registry) { 170 stubDiffRequest(reg, "application/vnd.github.v3.diff", fmt.Sprintf(testDiff, "", "", "", "")) 171 }, 172 }, 173 { 174 name: "with color", 175 opts: DiffOptions{ 176 SelectorArg: "123", 177 UseColor: true, 178 Patch: false, 179 }, 180 wantFields: []string{"number"}, 181 wantStdout: fmt.Sprintf(testDiff, "\x1b[m", "\x1b[1;38m", "\x1b[32m", "\x1b[31m"), 182 httpStubs: func(reg *httpmock.Registry) { 183 stubDiffRequest(reg, "application/vnd.github.v3.diff", fmt.Sprintf(testDiff, "", "", "", "")) 184 }, 185 }, 186 { 187 name: "patch format", 188 opts: DiffOptions{ 189 SelectorArg: "123", 190 UseColor: false, 191 Patch: true, 192 }, 193 wantFields: []string{"number"}, 194 wantStdout: fmt.Sprintf(testDiff, "", "", "", ""), 195 httpStubs: func(reg *httpmock.Registry) { 196 stubDiffRequest(reg, "application/vnd.github.v3.patch", fmt.Sprintf(testDiff, "", "", "", "")) 197 }, 198 }, 199 { 200 name: "name only", 201 opts: DiffOptions{ 202 SelectorArg: "123", 203 UseColor: false, 204 Patch: false, 205 NameOnly: true, 206 }, 207 wantFields: []string{"number"}, 208 wantStdout: ".github/workflows/releases.yml\nMakefile\n", 209 httpStubs: func(reg *httpmock.Registry) { 210 stubDiffRequest(reg, "application/vnd.github.v3.diff", fmt.Sprintf(testDiff, "", "", "", "")) 211 }, 212 }, 213 { 214 name: "web mode", 215 opts: DiffOptions{ 216 SelectorArg: "123", 217 BrowserMode: true, 218 }, 219 wantFields: []string{"url"}, 220 wantStderr: "Opening github.com/OWNER/REPO/pull/123/files in your browser.\n", 221 wantBrowsedURL: "https://github.com/OWNER/REPO/pull/123/files", 222 }, 223 } 224 for _, tt := range tests { 225 t.Run(tt.name, func(t *testing.T) { 226 httpReg := &httpmock.Registry{} 227 defer httpReg.Verify(t) 228 if tt.httpStubs != nil { 229 tt.httpStubs(httpReg) 230 } 231 tt.opts.HttpClient = func() (*http.Client, error) { 232 return &http.Client{Transport: httpReg}, nil 233 } 234 235 browser := &browser.Stub{} 236 tt.opts.Browser = browser 237 238 ios, _, stdout, stderr := iostreams.Test() 239 ios.SetStdoutTTY(true) 240 tt.opts.IO = ios 241 242 finder := shared.NewMockFinder("123", pr, ghrepo.New("OWNER", "REPO")) 243 finder.ExpectFields(tt.wantFields) 244 tt.opts.Finder = finder 245 246 err := diffRun(&tt.opts) 247 assert.NoError(t, err) 248 249 assert.Equal(t, tt.wantStdout, stdout.String()) 250 assert.Equal(t, tt.wantStderr, stderr.String()) 251 assert.Equal(t, tt.wantBrowsedURL, browser.BrowsedURL()) 252 }) 253 } 254 } 255 256 const testDiff = `%[2]sdiff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml%[1]s 257 %[2]sindex 73974448..b7fc0154 100644%[1]s 258 %[2]s--- a/.github/workflows/releases.yml%[1]s 259 %[2]s+++ b/.github/workflows/releases.yml%[1]s 260 @@ -44,6 +44,11 @@ jobs: 261 token: ${{secrets.SITE_GITHUB_TOKEN}} 262 - name: Publish documentation site 263 if: "!contains(github.ref, '-')" # skip prereleases 264 %[3]s+ env:%[1]s 265 %[3]s+ GIT_COMMITTER_NAME: cli automation%[1]s 266 %[3]s+ GIT_AUTHOR_NAME: cli automation%[1]s 267 %[3]s+ GIT_COMMITTER_EMAIL: noreply@github.com%[1]s 268 %[3]s+ GIT_AUTHOR_EMAIL: noreply@github.com%[1]s 269 run: make site-publish 270 - name: Move project cards 271 if: "!contains(github.ref, '-')" # skip prereleases 272 %[2]sdiff --git a/Makefile b/Makefile%[1]s 273 %[2]sindex f2b4805c..3d7bd0f9 100644%[1]s 274 %[2]s--- a/Makefile%[1]s 275 %[2]s+++ b/Makefile%[1]s 276 @@ -22,8 +22,8 @@ test: 277 go test ./... 278 .PHONY: test 279 280 %[4]s-site:%[1]s 281 %[4]s- git clone https://github.com/github/cli.github.com.git "$@"%[1]s 282 %[3]s+site: bin/gh%[1]s 283 %[3]s+ bin/gh repo clone github/cli.github.com "$@"%[1]s 284 285 site-docs: site 286 git -C site pull 287 ` 288 289 func Test_colorDiffLines(t *testing.T) { 290 inputs := []struct { 291 input, output string 292 }{ 293 { 294 input: "", 295 output: "", 296 }, 297 { 298 input: "\n", 299 output: "\n", 300 }, 301 { 302 input: "foo\nbar\nbaz\n", 303 output: "foo\nbar\nbaz\n", 304 }, 305 { 306 input: "foo\nbar\nbaz", 307 output: "foo\nbar\nbaz\n", 308 }, 309 { 310 input: fmt.Sprintf("+foo\n-b%sr\n+++ baz\n", strings.Repeat("a", 2*lineBufferSize)), 311 output: fmt.Sprintf( 312 "%[4]s+foo%[2]s\n%[5]s-b%[1]sr%[2]s\n%[3]s+++ baz%[2]s\n", 313 strings.Repeat("a", 2*lineBufferSize), 314 "\x1b[m", 315 "\x1b[1;38m", 316 "\x1b[32m", 317 "\x1b[31m", 318 ), 319 }, 320 } 321 for _, tt := range inputs { 322 buf := bytes.Buffer{} 323 if err := colorDiffLines(&buf, strings.NewReader(tt.input)); err != nil { 324 t.Fatalf("unexpected error: %s", err) 325 } 326 if got := buf.String(); got != tt.output { 327 t.Errorf("expected: %q, got: %q", tt.output, got) 328 } 329 } 330 } 331 332 func Test_changedFileNames(t *testing.T) { 333 inputs := []struct { 334 input, output string 335 }{ 336 { 337 input: "", 338 output: "", 339 }, 340 { 341 input: "\n", 342 output: "", 343 }, 344 { 345 input: "diff --git a/cmd.go b/cmd.go\n--- /dev/null\n+++ b/cmd.go\n@@ -0,0 +1,313 @@", 346 output: "cmd.go\n", 347 }, 348 { 349 input: "diff --git a/cmd.go b/cmd.go\n--- a/cmd.go\n+++ /dev/null\n@@ -0,0 +1,313 @@", 350 output: "cmd.go\n", 351 }, 352 { 353 input: fmt.Sprintf("diff --git a/baz.go b/rename.go\n--- a/baz.go\n+++ b/rename.go\n+foo\n-b%sr", strings.Repeat("a", 2*lineBufferSize)), 354 output: "rename.go\n", 355 }, 356 { 357 input: fmt.Sprintf("diff --git a/baz.go b/baz.go\n--- a/baz.go\n+++ b/baz.go\n+foo\n-b%sr", strings.Repeat("a", 2*lineBufferSize)), 358 output: "baz.go\n", 359 }, 360 } 361 for _, tt := range inputs { 362 buf := bytes.Buffer{} 363 if err := changedFilesNames(&buf, strings.NewReader(tt.input)); err != nil { 364 t.Fatalf("unexpected error: %s", err) 365 } 366 if got := buf.String(); got != tt.output { 367 t.Errorf("expected: %q, got: %q", tt.output, got) 368 } 369 } 370 } 371 372 func stubDiffRequest(reg *httpmock.Registry, accept, diff string) { 373 reg.Register( 374 func(req *http.Request) bool { 375 if !strings.EqualFold(req.Method, "GET") { 376 return false 377 } 378 if req.URL.EscapedPath() != "/repos/OWNER/REPO/pulls/123" { 379 return false 380 } 381 return req.Header.Get("Accept") == accept 382 }, 383 func(req *http.Request) (*http.Response, error) { 384 return &http.Response{ 385 StatusCode: 200, 386 Request: req, 387 Body: io.NopCloser(strings.NewReader(diff)), 388 }, nil 389 }) 390 }