github.com/jasonkeene/cli@v6.14.1-0.20160816203908-ca5715166dfb+incompatible/cf/terminal/ui_test.go (about) 1 package terminal_test 2 3 import ( 4 "io" 5 "os" 6 "strings" 7 8 "github.com/cloudfoundry/cli/cf" 9 "github.com/cloudfoundry/cli/cf/configuration/coreconfig" 10 "github.com/cloudfoundry/cli/cf/i18n" 11 "github.com/cloudfoundry/cli/cf/models" 12 "github.com/cloudfoundry/cli/cf/trace/tracefakes" 13 "github.com/cloudfoundry/cli/testhelpers/configuration" 14 testconfig "github.com/cloudfoundry/cli/testhelpers/configuration" 15 io_helpers "github.com/cloudfoundry/cli/testhelpers/io" 16 go_i18n "github.com/nicksnyder/go-i18n/i18n" 17 18 . "github.com/cloudfoundry/cli/cf/terminal" 19 "github.com/cloudfoundry/cli/cf/trace" 20 . "github.com/cloudfoundry/cli/testhelpers/matchers" 21 . "github.com/onsi/ginkgo" 22 . "github.com/onsi/gomega" 23 "github.com/onsi/gomega/gbytes" 24 ) 25 26 var _ = Describe("UI", func() { 27 var fakeLogger *tracefakes.FakePrinter 28 BeforeEach(func() { 29 fakeLogger = new(tracefakes.FakePrinter) 30 }) 31 32 Describe("Printing message to stdout with PrintCapturingNoOutput", func() { 33 It("prints strings without using the TeePrinter", func() { 34 bucket := gbytes.NewBuffer() 35 36 printer := NewTeePrinter(os.Stdout) 37 printer.SetOutputBucket(bucket) 38 39 io_helpers.SimulateStdin("", func(reader io.Reader) { 40 output := io_helpers.CaptureOutput(func() { 41 ui := NewUI(reader, os.Stdout, printer, fakeLogger) 42 ui.PrintCapturingNoOutput("Hello") 43 }) 44 45 Expect("Hello").To(Equal(strings.Join(output, ""))) 46 Expect(bucket.Contents()).To(HaveLen(0)) 47 }) 48 }) 49 }) 50 51 Describe("Printing message to stdout with Say", func() { 52 It("prints strings", func() { 53 io_helpers.SimulateStdin("", func(reader io.Reader) { 54 output := io_helpers.CaptureOutput(func() { 55 ui := NewUI(reader, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 56 ui.Say("Hello") 57 }) 58 59 Expect("Hello").To(Equal(strings.Join(output, ""))) 60 }) 61 }) 62 63 It("prints formatted strings", func() { 64 io_helpers.SimulateStdin("", func(reader io.Reader) { 65 output := io_helpers.CaptureOutput(func() { 66 ui := NewUI(reader, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 67 ui.Say("Hello %s", "World!") 68 }) 69 70 Expect("Hello World!").To(Equal(strings.Join(output, ""))) 71 }) 72 }) 73 74 It("does not format strings when provided no args", func() { 75 output := io_helpers.CaptureOutput(func() { 76 ui := NewUI(os.Stdin, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 77 ui.Say("Hello %s World!") // whoops 78 }) 79 80 Expect(strings.Join(output, "")).To(Equal("Hello %s World!")) 81 }) 82 }) 83 84 Describe("Asking user for input", func() { 85 It("allows string with whitespaces", func() { 86 _ = io_helpers.CaptureOutput(func() { 87 io_helpers.SimulateStdin("foo bar\n", func(reader io.Reader) { 88 ui := NewUI(reader, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 89 Expect(ui.Ask("?")).To(Equal("foo bar")) 90 }) 91 }) 92 }) 93 94 It("returns empty string if an error occured while reading string", func() { 95 _ = io_helpers.CaptureOutput(func() { 96 io_helpers.SimulateStdin("string without expected delimiter", func(reader io.Reader) { 97 ui := NewUI(reader, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 98 Expect(ui.Ask("?")).To(Equal("")) 99 }) 100 }) 101 }) 102 103 It("always outputs the prompt, even when output is disabled", func() { 104 output := io_helpers.CaptureOutput(func() { 105 io_helpers.SimulateStdin("things are great\n", func(reader io.Reader) { 106 printer := NewTeePrinter(os.Stdout) 107 printer.DisableTerminalOutput(true) 108 ui := NewUI(reader, os.Stdout, printer, fakeLogger) 109 ui.Ask("You like things?") 110 }) 111 }) 112 Expect(strings.Join(output, "")).To(ContainSubstring("You like things?")) 113 }) 114 }) 115 116 Describe("Confirming user input", func() { 117 It("treats 'y' as an affirmative confirmation", func() { 118 io_helpers.SimulateStdin("y\n", func(reader io.Reader) { 119 out := io_helpers.CaptureOutput(func() { 120 ui := NewUI(reader, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 121 Expect(ui.Confirm("Hello World?")).To(BeTrue()) 122 }) 123 124 Expect(out).To(ContainSubstrings([]string{"Hello World?"})) 125 }) 126 }) 127 128 It("treats 'yes' as an affirmative confirmation when default language is not en_US", func() { 129 oldLang := os.Getenv("LC_ALL") 130 defer os.Setenv("LC_ALL", oldLang) 131 132 oldT := i18n.T 133 defer func() { 134 i18n.T = oldT 135 }() 136 137 os.Setenv("LC_ALL", "fr_FR") 138 139 config := configuration.NewRepositoryWithDefaults() 140 i18n.T = i18n.Init(config) 141 142 io_helpers.SimulateStdin("yes\n", func(reader io.Reader) { 143 out := io_helpers.CaptureOutput(func() { 144 ui := NewUI(reader, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 145 Expect(ui.Confirm("Hello World?")).To(BeTrue()) 146 }) 147 Expect(out).To(ContainSubstrings([]string{"Hello World?"})) 148 }) 149 }) 150 151 It("treats 'yes' as an affirmative confirmation", func() { 152 io_helpers.SimulateStdin("yes\n", func(reader io.Reader) { 153 out := io_helpers.CaptureOutput(func() { 154 ui := NewUI(reader, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 155 Expect(ui.Confirm("Hello World?")).To(BeTrue()) 156 }) 157 158 Expect(out).To(ContainSubstrings([]string{"Hello World?"})) 159 }) 160 }) 161 162 It("treats other input as a negative confirmation", func() { 163 io_helpers.SimulateStdin("wat\n", func(reader io.Reader) { 164 out := io_helpers.CaptureOutput(func() { 165 ui := NewUI(reader, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 166 Expect(ui.Confirm("Hello World?")).To(BeFalse()) 167 }) 168 169 Expect(out).To(ContainSubstrings([]string{"Hello World?"})) 170 }) 171 }) 172 }) 173 174 Describe("Confirming deletion", func() { 175 It("formats a nice output string with exactly one prompt", func() { 176 io_helpers.SimulateStdin("y\n", func(reader io.Reader) { 177 out := io_helpers.CaptureOutput(func() { 178 ui := NewUI(reader, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 179 Expect(ui.ConfirmDelete("fizzbuzz", "bizzbump")).To(BeTrue()) 180 }) 181 182 Expect(out).To(ContainSubstrings([]string{ 183 "Really delete the fizzbuzz", 184 "bizzbump", 185 "?> ", 186 })) 187 }) 188 }) 189 190 It("treats 'yes' as an affirmative confirmation", func() { 191 io_helpers.SimulateStdin("yes\n", func(reader io.Reader) { 192 out := io_helpers.CaptureOutput(func() { 193 ui := NewUI(reader, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 194 Expect(ui.ConfirmDelete("modelType", "modelName")).To(BeTrue()) 195 }) 196 197 Expect(out).To(ContainSubstrings([]string{"modelType modelName"})) 198 }) 199 }) 200 201 It("treats other input as a negative confirmation and warns the user", func() { 202 io_helpers.SimulateStdin("wat\n", func(reader io.Reader) { 203 out := io_helpers.CaptureOutput(func() { 204 ui := NewUI(reader, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 205 Expect(ui.ConfirmDelete("modelType", "modelName")).To(BeFalse()) 206 }) 207 208 Expect(out).To(ContainSubstrings([]string{"Delete cancelled"})) 209 }) 210 }) 211 }) 212 213 Describe("Confirming deletion with associations", func() { 214 It("warns the user that associated objects will also be deleted", func() { 215 io_helpers.SimulateStdin("wat\n", func(reader io.Reader) { 216 out := io_helpers.CaptureOutput(func() { 217 ui := NewUI(reader, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 218 Expect(ui.ConfirmDeleteWithAssociations("modelType", "modelName")).To(BeFalse()) 219 }) 220 221 Expect(out).To(ContainSubstrings([]string{"Delete cancelled"})) 222 }) 223 }) 224 }) 225 226 Context("when user is not logged in", func() { 227 var config coreconfig.Reader 228 229 BeforeEach(func() { 230 config = testconfig.NewRepository() 231 }) 232 233 It("prompts the user to login", func() { 234 output := io_helpers.CaptureOutput(func() { 235 ui := NewUI(os.Stdin, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 236 ui.ShowConfiguration(config) 237 }) 238 239 Expect(output).ToNot(ContainSubstrings([]string{"API endpoint:"})) 240 Expect(output).To(ContainSubstrings([]string{"Not logged in", "Use", "log in"})) 241 }) 242 }) 243 244 Context("when an api endpoint is set and the user logged in", func() { 245 var config coreconfig.ReadWriter 246 247 BeforeEach(func() { 248 accessToken := coreconfig.TokenInfo{ 249 UserGUID: "my-user-guid", 250 Username: "my-user", 251 Email: "my-user-email", 252 } 253 config = testconfig.NewRepositoryWithAccessToken(accessToken) 254 config.SetAPIEndpoint("https://test.example.org") 255 config.SetAPIVersion("☃☃☃") 256 }) 257 258 Describe("tells the user what is set in the config", func() { 259 var output []string 260 261 JustBeforeEach(func() { 262 output = io_helpers.CaptureOutput(func() { 263 ui := NewUI(os.Stdin, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 264 ui.ShowConfiguration(config) 265 }) 266 }) 267 268 It("tells the user which api endpoint is set", func() { 269 Expect(output).To(ContainSubstrings([]string{"API endpoint:", "https://test.example.org"})) 270 }) 271 272 It("tells the user the api version", func() { 273 Expect(output).To(ContainSubstrings([]string{"API version:", "☃☃☃"})) 274 }) 275 276 It("tells the user which user is logged in", func() { 277 Expect(output).To(ContainSubstrings([]string{"User:", "my-user-email"})) 278 }) 279 280 Context("when an org is targeted", func() { 281 BeforeEach(func() { 282 config.SetOrganizationFields(models.OrganizationFields{ 283 Name: "org-name", 284 GUID: "org-guid", 285 }) 286 }) 287 288 It("tells the user which org is targeted", func() { 289 Expect(output).To(ContainSubstrings([]string{"Org:", "org-name"})) 290 }) 291 }) 292 293 Context("when a space is targeted", func() { 294 BeforeEach(func() { 295 config.SetSpaceFields(models.SpaceFields{ 296 Name: "my-space", 297 GUID: "space-guid", 298 }) 299 }) 300 301 It("tells the user which space is targeted", func() { 302 Expect(output).To(ContainSubstrings([]string{"Space:", "my-space"})) 303 }) 304 }) 305 }) 306 307 It("prompts the user to target an org and space when no org or space is targeted", func() { 308 output := io_helpers.CaptureOutput(func() { 309 ui := NewUI(os.Stdin, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 310 ui.ShowConfiguration(config) 311 }) 312 313 Expect(output).To(ContainSubstrings([]string{"No", "org", "space", "targeted", "-o ORG", "-s SPACE"})) 314 }) 315 316 It("prompts the user to target an org when no org is targeted", func() { 317 sf := models.SpaceFields{} 318 sf.GUID = "guid" 319 sf.Name = "name" 320 321 output := io_helpers.CaptureOutput(func() { 322 ui := NewUI(os.Stdin, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 323 ui.ShowConfiguration(config) 324 }) 325 326 Expect(output).To(ContainSubstrings([]string{"No", "org", "targeted", "-o ORG"})) 327 }) 328 329 It("prompts the user to target a space when no space is targeted", func() { 330 of := models.OrganizationFields{} 331 of.GUID = "of-guid" 332 of.Name = "of-name" 333 334 output := io_helpers.CaptureOutput(func() { 335 ui := NewUI(os.Stdin, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 336 ui.ShowConfiguration(config) 337 }) 338 339 Expect(output).To(ContainSubstrings([]string{"No", "space", "targeted", "-s SPACE"})) 340 }) 341 }) 342 343 Describe("failing", func() { 344 Context("when 'T' func is not initialized", func() { 345 var t go_i18n.TranslateFunc 346 BeforeEach(func() { 347 t = i18n.T 348 i18n.T = nil 349 }) 350 351 AfterEach(func() { 352 i18n.T = t 353 }) 354 355 It("does not duplicate output if logger is set to stdout", func() { 356 output := io_helpers.CaptureOutput(func() { 357 logger := trace.NewWriterPrinter(os.Stdout, true) 358 NewUI(os.Stdin, os.Stdout, NewTeePrinter(os.Stdout), logger).Failed("this should print only once") 359 }) 360 361 Expect(output).To(HaveLen(3)) 362 Expect(output[0]).To(Equal("FAILED")) 363 Expect(output[1]).To(Equal("this should print only once")) 364 Expect(output[2]).To(Equal("")) 365 }) 366 }) 367 368 Context("when 'T' func is initialized", func() { 369 It("does not duplicate output if logger is set to stdout", func() { 370 output := io_helpers.CaptureOutput( 371 func() { 372 logger := trace.NewWriterPrinter(os.Stdout, true) 373 NewUI(os.Stdin, os.Stdout, NewTeePrinter(os.Stdout), logger).Failed("this should print only once") 374 }) 375 376 Expect(output).To(HaveLen(3)) 377 Expect(output[0]).To(Equal("FAILED")) 378 Expect(output[1]).To(Equal("this should print only once")) 379 Expect(output[2]).To(Equal("")) 380 }) 381 }) 382 }) 383 384 Describe("NotifyUpdateIfNeeded", func() { 385 var ( 386 output []string 387 config coreconfig.ReadWriter 388 ) 389 390 BeforeEach(func() { 391 config = testconfig.NewRepository() 392 }) 393 394 It("Prints a notification to user if current version < min cli version", func() { 395 config.SetMinCLIVersion("6.0.0") 396 config.SetMinRecommendedCLIVersion("6.5.0") 397 config.SetAPIVersion("2.15.1") 398 cf.Version = "5.0.0" 399 output = io_helpers.CaptureOutput(func() { 400 ui := NewUI(os.Stdin, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 401 ui.NotifyUpdateIfNeeded(config) 402 }) 403 404 Expect(output).To(ContainSubstrings([]string{"Cloud Foundry API version", 405 "requires CLI version 6.0.0", 406 "You are currently on version 5.0.0", 407 "To upgrade your CLI, please visit: https://github.com/cloudfoundry/cli#downloads", 408 })) 409 }) 410 411 It("Doesn't print a notification to user if current version >= min cli version", func() { 412 config.SetMinCLIVersion("6.0.0") 413 config.SetMinRecommendedCLIVersion("6.5.0") 414 config.SetAPIVersion("2.15.1") 415 cf.Version = "6.0.0" 416 output = io_helpers.CaptureOutput(func() { 417 ui := NewUI(os.Stdin, os.Stdout, NewTeePrinter(os.Stdout), fakeLogger) 418 ui.NotifyUpdateIfNeeded(config) 419 }) 420 421 Expect(output[0]).To(Equal("")) 422 }) 423 }) 424 })