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