github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/util/ui/ui_test.go (about) 1 package ui_test 2 3 import ( 4 "errors" 5 "time" 6 7 "code.cloudfoundry.org/cli/command/translatableerror/translatableerrorfakes" 8 "code.cloudfoundry.org/cli/util/configv3" 9 . "code.cloudfoundry.org/cli/util/ui" 10 "code.cloudfoundry.org/cli/util/ui/uifakes" 11 . "github.com/onsi/ginkgo" 12 . "github.com/onsi/gomega" 13 . "github.com/onsi/gomega/gbytes" 14 ) 15 16 var _ = Describe("UI", func() { 17 var ( 18 ui *UI 19 fakeConfig *uifakes.FakeConfig 20 out *Buffer 21 errBuff *Buffer 22 ) 23 24 BeforeEach(func() { 25 fakeConfig = new(uifakes.FakeConfig) 26 fakeConfig.ColorEnabledReturns(configv3.ColorEnabled) 27 28 var err error 29 ui, err = NewUI(fakeConfig) 30 Expect(err).NotTo(HaveOccurred()) 31 32 out = NewBuffer() 33 ui.Out = out 34 ui.OutForInteraction = out 35 errBuff = NewBuffer() 36 ui.Err = errBuff 37 }) 38 39 Describe("DisplayDeprecationWarning", func() { 40 It("displays the deprecation warning to ui.Err", func() { 41 ui.DisplayDeprecationWarning() 42 Expect(ui.Err).To(Say("Deprecation warning: This command has been deprecated. This feature will be removed in the future.\n")) 43 }) 44 45 When("the locale is not set to English", func() { 46 BeforeEach(func() { 47 fakeConfig.LocaleReturns("fr-FR") 48 49 var err error 50 ui, err = NewUI(fakeConfig) 51 Expect(err).NotTo(HaveOccurred()) 52 53 ui.Err = NewBuffer() 54 }) 55 56 PIt("displays the translated deprecation warning to ui.Err", func() { 57 // TODO: Test implementation awaits translated version of deprecation warning string literal #164098152. 58 }) 59 }) 60 }) 61 62 Describe("DisplayFileDeprecationWarning", func() { 63 It("displays the `cf files` deprecation warning to ui.Err", func() { 64 ui.DisplayFileDeprecationWarning() 65 Expect(ui.Err).To(Say("Deprecation warning: This command has been deprecated and will be removed in the future. For similar functionality, please use the `cf ssh` command instead.\n")) 66 }) 67 68 When("the locale is not set to English", func() { 69 BeforeEach(func() { 70 fakeConfig.LocaleReturns("fr-FR") 71 72 var err error 73 ui, err = NewUI(fakeConfig) 74 Expect(err).NotTo(HaveOccurred()) 75 76 ui.Err = NewBuffer() 77 }) 78 79 PIt("displays the translated deprecation warning to ui.Err", func() { 80 // TODO: Test implementation awaits translated version of deprecation warning string literal #164098103. 81 }) 82 }) 83 }) 84 85 Describe("DisplayError", func() { 86 When("passed a TranslatableError", func() { 87 var fakeTranslateErr *translatableerrorfakes.FakeTranslatableError 88 89 BeforeEach(func() { 90 fakeTranslateErr = new(translatableerrorfakes.FakeTranslatableError) 91 fakeTranslateErr.TranslateReturns("I am an error") 92 93 ui.DisplayError(fakeTranslateErr) 94 }) 95 96 It("displays the error to ui.Err and displays FAILED in bold red to ui.Out", func() { 97 Expect(ui.Err).To(Say("I am an error\n")) 98 Expect(out).To(Say("\x1b\\[31;1mFAILED\x1b\\[0m\n")) 99 }) 100 101 When("the locale is not set to english", func() { 102 It("translates the error text", func() { 103 Expect(fakeTranslateErr.TranslateCallCount()).To(Equal(1)) 104 Expect(fakeTranslateErr.TranslateArgsForCall(0)).NotTo(BeNil()) 105 }) 106 }) 107 }) 108 109 When("passed a generic error", func() { 110 It("displays the error text to ui.Err and displays FAILED in bold red to ui.Out", func() { 111 ui.DisplayError(errors.New("I am a BANANA!")) 112 Expect(ui.Err).To(Say("I am a BANANA!\n")) 113 Expect(out).To(Say("\x1b\\[31;1mFAILED\x1b\\[0m\n")) 114 }) 115 }) 116 }) 117 118 Describe("DisplayHeader", func() { 119 It("displays the header colorized and bolded to ui.Out", func() { 120 ui.DisplayHeader("some-header") 121 Expect(out).To(Say("\x1b\\[1msome-header\x1b\\[0m")) 122 }) 123 124 When("the locale is not set to English", func() { 125 BeforeEach(func() { 126 fakeConfig.LocaleReturns("fr-FR") 127 128 var err error 129 ui, err = NewUI(fakeConfig) 130 Expect(err).NotTo(HaveOccurred()) 131 132 ui.Out = out 133 }) 134 135 It("displays the translated header colorized and bolded to ui.Out", func() { 136 ui.DisplayHeader("FEATURE FLAGS") 137 Expect(out).To(Say("\x1b\\[1mINDICATEURS DE FONCTION\x1b\\[0m")) 138 }) 139 }) 140 }) 141 142 Describe("DisplayNewline", func() { 143 It("displays a new line", func() { 144 ui.DisplayNewline() 145 Expect(out).To(Say("\n")) 146 }) 147 }) 148 149 Describe("DisplayOK", func() { 150 It("displays 'OK' in green and bold", func() { 151 ui.DisplayOK() 152 Expect(out).To(Say("\x1b\\[32;1mOK\x1b\\[0m")) 153 }) 154 }) 155 156 // Covers the happy paths, additional cases are tested in TranslateText 157 Describe("DisplayText", func() { 158 It("displays the template with map values substituted into ui.Out with a newline", func() { 159 ui.DisplayText( 160 "template with {{.SomeMapValue}}", 161 map[string]interface{}{ 162 "SomeMapValue": "map-value", 163 }) 164 Expect(out).To(Say("template with map-value\n")) 165 }) 166 167 When("the locale is not set to english", func() { 168 BeforeEach(func() { 169 fakeConfig.LocaleReturns("fr-FR") 170 171 var err error 172 ui, err = NewUI(fakeConfig) 173 Expect(err).NotTo(HaveOccurred()) 174 175 ui.Out = out 176 }) 177 178 It("displays the translated template with map values substituted into ui.Out", func() { 179 ui.DisplayText( 180 "\nTIP: Use '{{.Command}}' to target new org", 181 map[string]interface{}{ 182 "Command": "foo", 183 }) 184 Expect(out).To(Say("\nASTUCE : utilisez 'foo' pour cibler une nouvelle organisation")) 185 }) 186 }) 187 }) 188 189 Describe("Display JSON", func() { 190 It("displays the indented JSON object", func() { 191 obj := map[string]interface{}{ 192 "str": "hello", 193 "bool": true, 194 "int": 42, 195 "pass": "abc>&gd!f", 196 "map": map[string]interface{}{"float": 123.03}, 197 "arr": []string{"a", "b"}, 198 } 199 200 _ = ui.DisplayJSON("named_json", obj) 201 202 Expect(out).To(SatisfyAll( 203 Say("named_json: {\n"), 204 Say(" \"arr\": \\[\n"), 205 Say(" \"a\","), 206 Say(" \"b\"\n"), 207 Say(" \\],\n"), 208 Say(" \"bool\": true,\n"), 209 Say(" \"int\": 42,\n"), 210 Say(" \"map\": {\n"), 211 Say(" \"float\": 123.03\n"), 212 Say(" },\n"), 213 Say(" \"pass\": \"abc>&gd!f\",\n"), 214 Say(" \"str\": \"hello\"\n"), 215 Say("}\n"), 216 Say("\n"), 217 )) 218 }) 219 }) 220 221 Describe("DeferText", func() { 222 It("defers the template with map values substituted into ui.Out with a newline", func() { 223 ui.DeferText( 224 "template with {{.SomeMapValue}}", 225 map[string]interface{}{ 226 "SomeMapValue": "map-value", 227 }) 228 Expect(out).NotTo(Say("template with map-value\n")) 229 ui.FlushDeferred() 230 Expect(out).To(Say("template with map-value\n")) 231 }) 232 233 When("the locale is not set to english", func() { 234 BeforeEach(func() { 235 fakeConfig.LocaleReturns("fr-FR") 236 237 var err error 238 ui, err = NewUI(fakeConfig) 239 Expect(err).NotTo(HaveOccurred()) 240 241 ui.Out = out 242 }) 243 244 It("defers the translated template with map values substituted into ui.Out", func() { 245 ui.DeferText( 246 "\nTIP: Use '{{.Command}}' to target new org", 247 map[string]interface{}{ 248 "Command": "foo", 249 }) 250 Expect(out).NotTo(Say("\nASTUCE : utilisez 'foo' pour cibler une nouvelle organisation")) 251 ui.FlushDeferred() 252 Expect(out).To(Say("\nASTUCE : utilisez 'foo' pour cibler une nouvelle organisation")) 253 ui.FlushDeferred() 254 Expect(out).NotTo(Say("\nASTUCE : utilisez 'foo' pour cibler une nouvelle organisation")) 255 }) 256 }) 257 }) 258 259 Describe("DisplayTextWithBold", func() { 260 It("displays the template to ui.Out", func() { 261 ui.DisplayTextWithBold("some-template") 262 Expect(out).To(Say("some-template")) 263 }) 264 265 When("an optional map is passed in", func() { 266 It("displays the template with map values bolded and substituted into ui.Out", func() { 267 ui.DisplayTextWithBold( 268 "template with {{.SomeMapValue}}", 269 map[string]interface{}{ 270 "SomeMapValue": "map-value", 271 }) 272 Expect(out).To(Say("template with \x1b\\[1mmap-value\x1b\\[0m")) 273 }) 274 }) 275 276 When("multiple optional maps are passed in", func() { 277 It("displays the template with only the first map values bolded and substituted into ui.Out", func() { 278 ui.DisplayTextWithBold( 279 "template with {{.SomeMapValue}} and {{.SomeOtherMapValue}}", 280 map[string]interface{}{ 281 "SomeMapValue": "map-value", 282 }, 283 map[string]interface{}{ 284 "SomeOtherMapValue": "other-map-value", 285 }) 286 Expect(out).To(Say("template with \x1b\\[1mmap-value\x1b\\[0m and <no value>")) 287 }) 288 }) 289 290 When("the locale is not set to english", func() { 291 BeforeEach(func() { 292 fakeConfig.LocaleReturns("fr-FR") 293 294 var err error 295 ui, err = NewUI(fakeConfig) 296 Expect(err).NotTo(HaveOccurred()) 297 298 ui.Out = out 299 }) 300 301 It("displays the translated template with map values bolded and substituted into ui.Out", func() { 302 ui.DisplayTextWithBold( 303 "App {{.AppName}} does not exist.", 304 map[string]interface{}{ 305 "AppName": "some-app-name", 306 }) 307 Expect(out).To(Say("L'application \x1b\\[1msome-app-name\x1b\\[0m n'existe pas.\n")) 308 }) 309 }) 310 }) 311 312 Describe("DisplayTextWithFlavor", func() { 313 It("displays the template to ui.Out", func() { 314 ui.DisplayTextWithFlavor("some-template") 315 Expect(out).To(Say("some-template")) 316 }) 317 318 When("an optional map is passed in", func() { 319 It("displays the template with map values colorized, bolded, and substituted into ui.Out", func() { 320 ui.DisplayTextWithFlavor( 321 "template with {{.SomeMapValue}}", 322 map[string]interface{}{ 323 "SomeMapValue": "map-value", 324 }) 325 Expect(out).To(Say("template with \x1b\\[36;1mmap-value\x1b\\[0m")) 326 }) 327 }) 328 329 When("multiple optional maps are passed in", func() { 330 It("displays the template with only the first map values colorized, bolded, and substituted into ui.Out", func() { 331 ui.DisplayTextWithFlavor( 332 "template with {{.SomeMapValue}} and {{.SomeOtherMapValue}}", 333 map[string]interface{}{ 334 "SomeMapValue": "map-value", 335 }, 336 map[string]interface{}{ 337 "SomeOtherMapValue": "other-map-value", 338 }) 339 Expect(out).To(Say("template with \x1b\\[36;1mmap-value\x1b\\[0m and <no value>")) 340 }) 341 }) 342 343 When("the locale is not set to english", func() { 344 BeforeEach(func() { 345 fakeConfig.LocaleReturns("fr-FR") 346 347 var err error 348 ui, err = NewUI(fakeConfig) 349 Expect(err).NotTo(HaveOccurred()) 350 351 ui.Out = out 352 }) 353 354 It("displays the translated template with map values colorized, bolded and substituted into ui.Out", func() { 355 ui.DisplayTextWithFlavor( 356 "App {{.AppName}} does not exist.", 357 map[string]interface{}{ 358 "AppName": "some-app-name", 359 }) 360 Expect(out).To(Say("L'application \x1b\\[36;1msome-app-name\x1b\\[0m n'existe pas.\n")) 361 }) 362 }) 363 }) 364 365 Describe("DisplayDiffAddition", func() { 366 It("displays a green indented line with a +", func() { 367 ui.DisplayDiffAddition("added", 3, false) 368 Expect(out).To(Say(`\x1b\[32m\+ added\x1b\[0m`)) 369 }) 370 It("displays a hyphen when the addHyphen is true", func() { 371 ui.DisplayDiffAddition("added", 3, true) 372 Expect(out).To(Say(`\x1b\[32m\+ - added\x1b\[0m`)) 373 }) 374 375 }) 376 377 Describe("DisplayDiffRemoval", func() { 378 It("displays a red indented line with a -", func() { 379 ui.DisplayDiffRemoval("removed", 3, false) 380 Expect(out).To(Say(`\x1b\[31m\- removed\x1b\[0m`)) 381 }) 382 It("displays a a hyphen when addHyphen is true", func() { 383 ui.DisplayDiffRemoval("removed", 3, true) 384 Expect(out).To(Say(`\x1b\[31m\- - removed\x1b\[0m`)) 385 }) 386 }) 387 388 Describe("DisplayDiffUnchanged", func() { 389 It("displays a plain indented line with no prefix", func() { 390 ui.DisplayDiffUnchanged("unchanged", 3, false) 391 Expect(out).To(Say(" unchanged")) 392 }) 393 It("displays a a hyphen when addHyphen is true", func() { 394 ui.DisplayDiffUnchanged("unchanged", 3, true) 395 Expect(out).To(Say(" - unchanged")) 396 }) 397 }) 398 399 Describe("TranslateText", func() { 400 It("returns the template", func() { 401 Expect(ui.TranslateText("some-template")).To(Equal("some-template")) 402 }) 403 404 When("an optional map is passed in", func() { 405 It("returns the template with map values substituted in", func() { 406 expected := ui.TranslateText( 407 "template {{.SomeMapValue}}", 408 map[string]interface{}{ 409 "SomeMapValue": "map-value", 410 }) 411 Expect(expected).To(Equal("template map-value")) 412 }) 413 }) 414 415 When("multiple optional maps are passed in", func() { 416 It("returns the template with only the first map values substituted in", func() { 417 expected := ui.TranslateText( 418 "template with {{.SomeMapValue}} and {{.SomeOtherMapValue}}", 419 map[string]interface{}{ 420 "SomeMapValue": "map-value", 421 }, 422 map[string]interface{}{ 423 "SomeOtherMapValue": "other-map-value", 424 }) 425 Expect(expected).To(Equal("template with map-value and <no value>")) 426 }) 427 }) 428 429 When("the locale is not set to english", func() { 430 BeforeEach(func() { 431 fakeConfig.LocaleReturns("fr-FR") 432 433 var err error 434 ui, err = NewUI(fakeConfig) 435 Expect(err).NotTo(HaveOccurred()) 436 }) 437 438 It("returns the translated template", func() { 439 expected := ui.TranslateText(" View allowable quotas with 'CF_NAME quotas'") 440 Expect(expected).To(Equal(" Affichez les quotas pouvant être alloués avec 'CF_NAME quotas'")) 441 }) 442 }) 443 }) 444 445 Describe("UserFriendlyDate", func() { 446 It("formats a time into an ISO8601 string", func() { 447 Expect(ui.UserFriendlyDate(time.Unix(0, 0))).To(MatchRegexp(`\w{3} [0-3]\d \w{3} [0-2]\d:[0-5]\d:[0-5]\d \w+ \d{4}`)) 448 }) 449 }) 450 })