github.com/dcarley/cf-cli@v6.24.1-0.20170220111324-4225ff346898+incompatible/util/ui/ui_test.go (about)

     1  package ui_test
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  
     7  	"code.cloudfoundry.org/cli/util/configv3"
     8  	. "code.cloudfoundry.org/cli/util/ui"
     9  	"code.cloudfoundry.org/cli/util/ui/uifakes"
    10  	. "github.com/onsi/ginkgo"
    11  	. "github.com/onsi/gomega"
    12  	. "github.com/onsi/gomega/gbytes"
    13  )
    14  
    15  var _ = Describe("UI", func() {
    16  	var (
    17  		ui         *UI
    18  		fakeConfig *uifakes.FakeConfig
    19  	)
    20  
    21  	BeforeEach(func() {
    22  		fakeConfig = new(uifakes.FakeConfig)
    23  		fakeConfig.ColorEnabledReturns(configv3.ColorEnabled)
    24  
    25  		var err error
    26  		ui, err = NewUI(fakeConfig)
    27  		Expect(err).NotTo(HaveOccurred())
    28  
    29  		ui.Out = NewBuffer()
    30  		ui.Err = NewBuffer()
    31  	})
    32  
    33  	It("sets the TimezoneLocation to the local timezone", func() {
    34  		location := time.Now().Location()
    35  		Expect(ui.TimezoneLocation).To(Equal(location))
    36  	})
    37  
    38  	Describe("TranslateText", func() {
    39  		It("returns the template", func() {
    40  			Expect(ui.TranslateText("some-template")).To(Equal("some-template"))
    41  		})
    42  
    43  		Context("when an optional map is passed in", func() {
    44  			It("returns the template with map values substituted in", func() {
    45  				expected := ui.TranslateText(
    46  					"template {{.SomeMapValue}}",
    47  					map[string]interface{}{
    48  						"SomeMapValue": "map-value",
    49  					})
    50  				Expect(expected).To(Equal("template map-value"))
    51  			})
    52  		})
    53  
    54  		Context("when multiple optional maps are passed in", func() {
    55  			It("returns the template with only the first map values substituted in", func() {
    56  				expected := ui.TranslateText(
    57  					"template with {{.SomeMapValue}} and {{.SomeOtherMapValue}}",
    58  					map[string]interface{}{
    59  						"SomeMapValue": "map-value",
    60  					},
    61  					map[string]interface{}{
    62  						"SomeOtherMapValue": "other-map-value",
    63  					})
    64  				Expect(expected).To(Equal("template with map-value and <no value>"))
    65  			})
    66  		})
    67  
    68  		Context("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  
    77  			It("returns the translated template", func() {
    78  				expected := ui.TranslateText("   View allowable quotas with 'CF_NAME quotas'")
    79  				Expect(expected).To(Equal("   Affichez les quotas pouvant être alloués avec 'CF_NAME quotas'"))
    80  			})
    81  		})
    82  	})
    83  
    84  	Describe("UserFriendlyDate", func() {
    85  		It("formats a time into an ISO8601 string", func() {
    86  			Expect(ui.UserFriendlyDate(time.Unix(0, 0))).To(Equal("1970-01-01T00:00:00Z"))
    87  		})
    88  	})
    89  
    90  	Describe("DisplayOK", func() {
    91  		It("displays 'OK' in green and bold", func() {
    92  			ui.DisplayOK()
    93  			Expect(ui.Out).To(Say("\x1b\\[32;1mOK\x1b\\[0m"))
    94  		})
    95  	})
    96  
    97  	Describe("DisplayNewline", func() {
    98  		It("displays a new line", func() {
    99  			ui.DisplayNewline()
   100  			Expect(ui.Out).To(Say("\n"))
   101  		})
   102  	})
   103  
   104  	Describe("DisplayBoolPrompt", func() {
   105  		var inBuffer *Buffer
   106  
   107  		BeforeEach(func() {
   108  			inBuffer = NewBuffer()
   109  			ui.In = inBuffer
   110  		})
   111  
   112  		It("displays the passed in string", func() {
   113  			ui.DisplayBoolPrompt("some-prompt", false)
   114  			Expect(ui.Out).To(Say("some-prompt\x1b\\[36;1m>>\x1b\\[0m"))
   115  		})
   116  
   117  		Context("when the user chooses yes", func() {
   118  			BeforeEach(func() {
   119  				inBuffer.Write([]byte("y\n"))
   120  			})
   121  
   122  			It("returns true", func() {
   123  				response, err := ui.DisplayBoolPrompt("some-prompt", false)
   124  				Expect(err).ToNot(HaveOccurred())
   125  				Expect(response).To(BeTrue())
   126  			})
   127  		})
   128  
   129  		Context("when the user chooses no", func() {
   130  			BeforeEach(func() {
   131  				inBuffer.Write([]byte("n\n"))
   132  			})
   133  
   134  			It("returns false", func() {
   135  				response, err := ui.DisplayBoolPrompt("some-prompt", true)
   136  				Expect(err).ToNot(HaveOccurred())
   137  				Expect(response).To(BeFalse())
   138  			})
   139  		})
   140  
   141  		Context("when the user chooses the default", func() {
   142  			BeforeEach(func() {
   143  				inBuffer.Write([]byte("\n"))
   144  			})
   145  
   146  			Context("when the default is true", func() {
   147  				It("returns true", func() {
   148  					response, err := ui.DisplayBoolPrompt("some-prompt", true)
   149  					Expect(err).ToNot(HaveOccurred())
   150  					Expect(response).To(BeTrue())
   151  				})
   152  			})
   153  
   154  			Context("when the default is false", func() {
   155  				It("returns false", func() {
   156  					response, err := ui.DisplayBoolPrompt("some-prompt", false)
   157  					Expect(err).ToNot(HaveOccurred())
   158  					Expect(response).To(BeFalse())
   159  				})
   160  			})
   161  		})
   162  
   163  		Context("when the interact library returns an error", func() {
   164  			It("returns the error", func() {
   165  				inBuffer.Write([]byte("invalid\n"))
   166  				_, err := ui.DisplayBoolPrompt("some-prompt", false)
   167  				Expect(err).To(HaveOccurred())
   168  			})
   169  		})
   170  	})
   171  
   172  	Describe("DisplayTable", func() {
   173  		It("displays a string matrix as a table with the provided prefix and padding to ui.Out", func() {
   174  			ui.DisplayTable(
   175  				"some-prefix",
   176  				[][]string{
   177  					{"aaaaaaaaa", "bb", "ccccccc"},
   178  					{"dddd", "eeeeeeeeeee", "fff"},
   179  					{"gg", "hh", "ii"},
   180  				},
   181  				3)
   182  			Expect(ui.Out).To(Say(`some-prefixaaaaaaaaa   bb            ccccccc
   183  some-prefixdddd        eeeeeeeeeee   fff
   184  some-prefixgg          hh            ii`))
   185  		})
   186  	})
   187  
   188  	// Covers the happy paths, additional cases are tested in TranslateText.
   189  	Describe("DisplayText", func() {
   190  		It("displays the template with map values substituted in to ui.Out with a newline", func() {
   191  			ui.DisplayText(
   192  				"template with {{.SomeMapValue}}",
   193  				map[string]interface{}{
   194  					"SomeMapValue": "map-value",
   195  				})
   196  			Expect(ui.Out).To(Say("template with map-value\n"))
   197  		})
   198  
   199  		Context("when the locale is not set to english", func() {
   200  			BeforeEach(func() {
   201  				fakeConfig.LocaleReturns("fr-FR")
   202  
   203  				var err error
   204  				ui, err = NewUI(fakeConfig)
   205  				Expect(err).NotTo(HaveOccurred())
   206  
   207  				ui.Out = NewBuffer()
   208  			})
   209  
   210  			It("displays the translated template with map values substituted in to ui.Out", func() {
   211  				ui.DisplayText(
   212  					"\nTIP: Use '{{.Command}}' to target new org",
   213  					map[string]interface{}{
   214  						"Command": "foo",
   215  					})
   216  				Expect(ui.Out).To(Say("\nASTUCE : utilisez 'foo' pour cibler une nouvelle organisation"))
   217  			})
   218  		})
   219  	})
   220  
   221  	// Covers the happy paths, additional cases are tested in TranslateText.
   222  	Describe("DisplayPair", func() {
   223  		It("displays the pair with map values substituted in to ui.Out", func() {
   224  			ui.DisplayPair(
   225  				"some-key",
   226  				"some-value with {{.SomeMapValue}}",
   227  				map[string]interface{}{
   228  					"SomeMapValue": "map-value",
   229  				})
   230  			Expect(ui.Out).To(Say("some-key: some-value with map-value\n"))
   231  		})
   232  
   233  		Context("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 = NewBuffer()
   242  			})
   243  
   244  			It("displays the translated pair with map values substituted in to ui.Out", func() {
   245  				ui.DisplayPair(
   246  					"ADVANCED",
   247  					"App {{.AppName}} does not exist.",
   248  					map[string]interface{}{
   249  						"AppName": "some-app-name",
   250  					})
   251  				Expect(ui.Out).To(Say("AVANCE: L'application some-app-name n'existe pas.\n"))
   252  			})
   253  		})
   254  	})
   255  
   256  	Describe("DisplayHeader", func() {
   257  		It("displays the header colorized and bolded to ui.Out", func() {
   258  			ui.DisplayHeader("some-header")
   259  			Expect(ui.Out).To(Say("\x1b\\[38;1msome-header\x1b\\[0m"))
   260  		})
   261  
   262  		Context("when the locale is not set to english", func() {
   263  			BeforeEach(func() {
   264  				fakeConfig.LocaleReturns("fr-FR")
   265  
   266  				var err error
   267  				ui, err = NewUI(fakeConfig)
   268  				Expect(err).NotTo(HaveOccurred())
   269  
   270  				ui.Out = NewBuffer()
   271  			})
   272  
   273  			It("displays the translated header colorized and bolded to ui.Out", func() {
   274  				ui.DisplayHeader("FEATURE FLAGS")
   275  				Expect(ui.Out).To(Say("\x1b\\[38;1mINDICATEURS DE FONCTION\x1b\\[0m"))
   276  			})
   277  		})
   278  	})
   279  
   280  	Describe("DisplayTextWithFlavor", func() {
   281  		It("displays the template to ui.Out", func() {
   282  			ui.DisplayTextWithFlavor("some-template")
   283  			Expect(ui.Out).To(Say("some-template"))
   284  		})
   285  
   286  		Context("when an optional map is passed in", func() {
   287  			It("displays the template with map values colorized, bolded, and substituted in to ui.Out", func() {
   288  				ui.DisplayTextWithFlavor(
   289  					"template with {{.SomeMapValue}}",
   290  					map[string]interface{}{
   291  						"SomeMapValue": "map-value",
   292  					})
   293  				Expect(ui.Out).To(Say("template with \x1b\\[36;1mmap-value\x1b\\[0m"))
   294  			})
   295  		})
   296  
   297  		Context("when multiple optional maps are passed in", func() {
   298  			It("displays the template with only the first map values colorized, bolded, and substituted in to ui.Out", func() {
   299  				ui.DisplayTextWithFlavor(
   300  					"template with {{.SomeMapValue}} and {{.SomeOtherMapValue}}",
   301  					map[string]interface{}{
   302  						"SomeMapValue": "map-value",
   303  					},
   304  					map[string]interface{}{
   305  						"SomeOtherMapValue": "other-map-value",
   306  					})
   307  				Expect(ui.Out).To(Say("template with \x1b\\[36;1mmap-value\x1b\\[0m and <no value>"))
   308  			})
   309  		})
   310  
   311  		Context("when the locale is not set to english", func() {
   312  			BeforeEach(func() {
   313  				fakeConfig.LocaleReturns("fr-FR")
   314  
   315  				var err error
   316  				ui, err = NewUI(fakeConfig)
   317  				Expect(err).NotTo(HaveOccurred())
   318  
   319  				ui.Out = NewBuffer()
   320  			})
   321  
   322  			It("displays the translated template with map values colorized, bolded and substituted in to ui.Out", func() {
   323  				ui.DisplayTextWithFlavor(
   324  					"App {{.AppName}} does not exist.",
   325  					map[string]interface{}{
   326  						"AppName": "some-app-name",
   327  					})
   328  				Expect(ui.Out).To(Say("L'application \x1b\\[36;1msome-app-name\x1b\\[0m n'existe pas.\n"))
   329  			})
   330  		})
   331  	})
   332  
   333  	// Covers the happy paths, additional cases are tested in TranslateText.
   334  	Describe("DisplayWarning", func() {
   335  		It("displays the warning to ui.Err", func() {
   336  			ui.DisplayWarning(
   337  				"template with {{.SomeMapValue}}",
   338  				map[string]interface{}{
   339  					"SomeMapValue": "map-value",
   340  				})
   341  			Expect(ui.Err).To(Say("template with map-value"))
   342  		})
   343  
   344  		Context("when the locale is not set to english", func() {
   345  			BeforeEach(func() {
   346  				fakeConfig.LocaleReturns("fr-FR")
   347  
   348  				var err error
   349  				ui, err = NewUI(fakeConfig)
   350  				Expect(err).NotTo(HaveOccurred())
   351  
   352  				ui.Err = NewBuffer()
   353  			})
   354  
   355  			It("displays the translated warning to ui.Err", func() {
   356  				ui.DisplayWarning(
   357  					"'{{.VersionShort}}' and '{{.VersionLong}}' are also accepted.",
   358  					map[string]interface{}{
   359  						"VersionShort": "some-value",
   360  						"VersionLong":  "some-other-value",
   361  					})
   362  				Expect(ui.Err).To(Say("'some-value' et 'some-other-value' sont également acceptés.\n"))
   363  			})
   364  		})
   365  	})
   366  
   367  	// Covers the happy paths, additional cases are tested in TranslateText.
   368  	Describe("DisplayWarnings", func() {
   369  		It("displays the warnings to ui.Err", func() {
   370  			ui.DisplayWarnings([]string{"warning-1", "warning-2"})
   371  			Expect(ui.Err).To(Say("warning-1\n"))
   372  			Expect(ui.Err).To(Say("warning-2\n"))
   373  		})
   374  
   375  		Context("when the locale is not set to english", func() {
   376  			BeforeEach(func() {
   377  				fakeConfig.LocaleReturns("fr-FR")
   378  
   379  				var err error
   380  				ui, err = NewUI(fakeConfig)
   381  				Expect(err).NotTo(HaveOccurred())
   382  
   383  				ui.Err = NewBuffer()
   384  			})
   385  
   386  			It("displays the translated warnings to ui.Err", func() {
   387  				ui.DisplayWarnings([]string{"Also delete any mapped routes", "FEATURE FLAGS"})
   388  				Expect(ui.Err).To(Say("Supprimer aussi les routes mappées\n"))
   389  				Expect(ui.Err).To(Say("INDICATEURS DE FONCTION\n"))
   390  			})
   391  		})
   392  	})
   393  
   394  	Describe("DisplayError", func() {
   395  		Context("when passed a TranslatableError", func() {
   396  			var fakeTranslateErr *uifakes.FakeTranslatableError
   397  
   398  			BeforeEach(func() {
   399  				fakeTranslateErr = new(uifakes.FakeTranslatableError)
   400  				fakeTranslateErr.TranslateReturns("I am an error")
   401  
   402  				ui.DisplayError(fakeTranslateErr)
   403  			})
   404  
   405  			It("displays the error to ui.Err and displays FAILED in bold red to ui.Out", func() {
   406  				Expect(ui.Err).To(Say("I am an error\n"))
   407  				Expect(ui.Out).To(Say("\x1b\\[31;1mFAILED\x1b\\[0m\n"))
   408  			})
   409  
   410  			Context("when the locale is not set to english", func() {
   411  				It("translates the error text", func() {
   412  					Expect(fakeTranslateErr.TranslateCallCount()).To(Equal(1))
   413  					Expect(fakeTranslateErr.TranslateArgsForCall(0)).NotTo(BeNil())
   414  				})
   415  			})
   416  		})
   417  
   418  		Context("when passed a generic error", func() {
   419  			It("displays the error text to ui.Err and displays FAILED in bold red to ui.Out", func() {
   420  				ui.DisplayError(errors.New("I am a BANANA!"))
   421  				Expect(ui.Err).To(Say("I am a BANANA!\n"))
   422  				Expect(ui.Out).To(Say("\x1b\\[31;1mFAILED\x1b\\[0m\n"))
   423  			})
   424  		})
   425  	})
   426  
   427  	Describe("DisplayLogMessage", func() {
   428  		var message *uifakes.FakeLogMessage
   429  
   430  		BeforeEach(func() {
   431  			var err error
   432  			ui.TimezoneLocation, err = time.LoadLocation("America/Los_Angeles")
   433  			Expect(err).NotTo(HaveOccurred())
   434  
   435  			message = new(uifakes.FakeLogMessage)
   436  			message.MessageReturns("This is a log message\r\n")
   437  			message.TypeReturns("OUT")
   438  			message.TimestampReturns(time.Unix(1468969692, 0)) // "2016-07-19T16:08:12-07:00"
   439  			message.ApplicationIDReturns("app-guid")
   440  			message.SourceTypeReturns("APP/PROC/WEB")
   441  			message.SourceInstanceReturns("12")
   442  		})
   443  
   444  		Context("with header", func() {
   445  			Context("single line log message", func() {
   446  				It("prints out a single line to STDOUT", func() {
   447  					ui.DisplayLogMessage(message, true)
   448  					Expect(ui.Out).To(Say("2016-07-19T16:08:12.00-0700 \\[APP/PROC/WEB/12\\] OUT This is a log message\n"))
   449  				})
   450  			})
   451  
   452  			Context("multi-line log message", func() {
   453  				BeforeEach(func() {
   454  					var err error
   455  					ui.TimezoneLocation, err = time.LoadLocation("America/Los_Angeles")
   456  					Expect(err).NotTo(HaveOccurred())
   457  
   458  					message.MessageReturns("This is a log message\nThis is also a log message")
   459  				})
   460  
   461  				It("prints out mutliple lines to STDOUT", func() {
   462  					ui.DisplayLogMessage(message, true)
   463  					Expect(ui.Out).To(Say("2016-07-19T16:08:12.00-0700 \\[APP/PROC/WEB/12\\] OUT This is a log message\n"))
   464  					Expect(ui.Out).To(Say("2016-07-19T16:08:12.00-0700 \\[APP/PROC/WEB/12\\] OUT This is also a log message\n"))
   465  				})
   466  			})
   467  		})
   468  
   469  		Context("without header", func() {
   470  			Context("single line log message", func() {
   471  				It("prints out a single line to STDOUT", func() {
   472  					ui.DisplayLogMessage(message, false)
   473  					Expect(ui.Out).To(Say("This is a log message\n"))
   474  				})
   475  			})
   476  
   477  			Context("multi-line log message", func() {
   478  				BeforeEach(func() {
   479  					var err error
   480  					ui.TimezoneLocation, err = time.LoadLocation("America/Los_Angeles")
   481  					Expect(err).NotTo(HaveOccurred())
   482  
   483  					message.MessageReturns("This is a log message\nThis is also a log message")
   484  				})
   485  
   486  				It("prints out mutliple lines to STDOUT", func() {
   487  					ui.DisplayLogMessage(message, false)
   488  					Expect(ui.Out).To(Say("This is a log message\n"))
   489  					Expect(ui.Out).To(Say("This is also a log message\n"))
   490  				})
   491  			})
   492  		})
   493  
   494  		Context("error log lines", func() {
   495  			BeforeEach(func() {
   496  				message.TypeReturns("ERR")
   497  			})
   498  			It("colors the line red", func() {
   499  				ui.DisplayLogMessage(message, false)
   500  				Expect(ui.Out).To(Say("\x1b\\[31mThis is a log message\x1b\\[0m\n"))
   501  			})
   502  		})
   503  	})
   504  })