github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/util/ui/ui_test.go (about)

     1  package ui_test
     2  
     3  import (
     4  	"errors"
     5  	"strings"
     6  	"time"
     7  
     8  	"code.cloudfoundry.org/cli/command/translatableerror/translatableerrorfakes"
     9  	"code.cloudfoundry.org/cli/util/configv3"
    10  	. "code.cloudfoundry.org/cli/util/ui"
    11  	"code.cloudfoundry.org/cli/util/ui/uifakes"
    12  	. "github.com/onsi/ginkgo"
    13  	. "github.com/onsi/gomega"
    14  	. "github.com/onsi/gomega/gbytes"
    15  )
    16  
    17  var _ = Describe("UI", func() {
    18  	var (
    19  		ui         *UI
    20  		fakeConfig *uifakes.FakeConfig
    21  		out        *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.OutForInteration = out
    35  		ui.Err = NewBuffer()
    36  	})
    37  
    38  	It("sets the TimezoneLocation to the local timezone", func() {
    39  		location := time.Now().Location()
    40  		Expect(ui.TimezoneLocation).To(Equal(location))
    41  	})
    42  
    43  	Describe("DisplayPasswordPrompt", func() {
    44  		var inBuffer *Buffer
    45  
    46  		BeforeEach(func() {
    47  			inBuffer = NewBuffer()
    48  			ui.In = inBuffer
    49  			inBuffer.Write([]byte("some-input\n"))
    50  		})
    51  
    52  		It("displays the passed in string", func() {
    53  			_, _ = ui.DisplayPasswordPrompt("App {{.AppName}} does not exist.", map[string]interface{}{
    54  				"AppName": "some-app",
    55  			})
    56  			Expect(out).To(Say("App some-app does not exist."))
    57  		})
    58  
    59  		It("returns the user input", func() {
    60  			userInput, err := ui.DisplayPasswordPrompt("App {{.AppName}} does not exist.", map[string]interface{}{
    61  				"AppName": "some-app",
    62  			})
    63  
    64  			Expect(err).ToNot(HaveOccurred())
    65  			Expect(userInput).To(Equal("some-input"))
    66  			Expect(out).ToNot(Say("some-input"))
    67  		})
    68  
    69  		Context("when the locale is not set to English", func() {
    70  			BeforeEach(func() {
    71  				fakeConfig.LocaleReturns("fr-FR")
    72  
    73  				var err error
    74  				ui, err = NewUI(fakeConfig)
    75  				Expect(err).NotTo(HaveOccurred())
    76  
    77  				ui.Out = out
    78  				ui.OutForInteration = out
    79  			})
    80  
    81  			It("translates and displays the prompt", func() {
    82  				_, _ = ui.DisplayPasswordPrompt("App {{.AppName}} does not exist.", map[string]interface{}{
    83  					"AppName": "some-app",
    84  				})
    85  				Expect(out).To(Say("L'application some-app n'existe pas.\n"))
    86  			})
    87  		})
    88  	})
    89  
    90  	Describe("DisplayBoolPrompt", func() {
    91  		var inBuffer *Buffer
    92  
    93  		BeforeEach(func() {
    94  			inBuffer = NewBuffer()
    95  			ui.In = inBuffer
    96  		})
    97  
    98  		It("displays the passed in string", func() {
    99  			_, _ = ui.DisplayBoolPrompt(false, "some-prompt", nil)
   100  			Expect(out).To(Say("some-prompt \\[yN\\]:"))
   101  		})
   102  
   103  		Context("when the user chooses yes", func() {
   104  			BeforeEach(func() {
   105  				_, err := inBuffer.Write([]byte("y\n"))
   106  				Expect(err).ToNot(HaveOccurred())
   107  			})
   108  
   109  			It("returns true", func() {
   110  				response, err := ui.DisplayBoolPrompt(false, "some-prompt", nil)
   111  				Expect(err).ToNot(HaveOccurred())
   112  				Expect(response).To(BeTrue())
   113  			})
   114  		})
   115  
   116  		Context("when the user chooses no", func() {
   117  			BeforeEach(func() {
   118  				_, err := inBuffer.Write([]byte("n\n"))
   119  				Expect(err).ToNot(HaveOccurred())
   120  			})
   121  
   122  			It("returns false", func() {
   123  				response, err := ui.DisplayBoolPrompt(false, "some-prompt", nil)
   124  				Expect(err).ToNot(HaveOccurred())
   125  				Expect(response).To(BeFalse())
   126  			})
   127  		})
   128  
   129  		Context("when the user chooses the default", func() {
   130  			BeforeEach(func() {
   131  				_, err := inBuffer.Write([]byte("\n"))
   132  				Expect(err).ToNot(HaveOccurred())
   133  			})
   134  
   135  			Context("when the default is true", func() {
   136  				It("returns true", func() {
   137  					response, err := ui.DisplayBoolPrompt(true, "some-prompt", nil)
   138  					Expect(err).ToNot(HaveOccurred())
   139  					Expect(response).To(BeTrue())
   140  				})
   141  			})
   142  
   143  			Context("when the default is false", func() {
   144  				It("returns false", func() {
   145  					response, err := ui.DisplayBoolPrompt(false, "some-prompt", nil)
   146  					Expect(err).ToNot(HaveOccurred())
   147  					Expect(response).To(BeFalse())
   148  				})
   149  			})
   150  		})
   151  
   152  		Context("when the interact library returns an error", func() {
   153  			It("returns the error", func() {
   154  				_, err := inBuffer.Write([]byte("invalid\n"))
   155  				Expect(err).ToNot(HaveOccurred())
   156  				_, err = ui.DisplayBoolPrompt(false, "some-prompt", nil)
   157  				Expect(err).To(HaveOccurred())
   158  			})
   159  		})
   160  	})
   161  
   162  	Describe("DisplayError", func() {
   163  		Context("when passed a TranslatableError", func() {
   164  			var fakeTranslateErr *translatableerrorfakes.FakeTranslatableError
   165  
   166  			BeforeEach(func() {
   167  				fakeTranslateErr = new(translatableerrorfakes.FakeTranslatableError)
   168  				fakeTranslateErr.TranslateReturns("I am an error")
   169  
   170  				ui.DisplayError(fakeTranslateErr)
   171  			})
   172  
   173  			It("displays the error to ui.Err and displays FAILED in bold red to ui.Out", func() {
   174  				Expect(ui.Err).To(Say("I am an error\n"))
   175  				Expect(out).To(Say("\x1b\\[31;1mFAILED\x1b\\[0m\n"))
   176  			})
   177  
   178  			Context("when the locale is not set to english", func() {
   179  				It("translates the error text", func() {
   180  					Expect(fakeTranslateErr.TranslateCallCount()).To(Equal(1))
   181  					Expect(fakeTranslateErr.TranslateArgsForCall(0)).NotTo(BeNil())
   182  				})
   183  			})
   184  		})
   185  
   186  		Context("when passed a generic error", func() {
   187  			It("displays the error text to ui.Err and displays FAILED in bold red to ui.Out", func() {
   188  				ui.DisplayError(errors.New("I am a BANANA!"))
   189  				Expect(ui.Err).To(Say("I am a BANANA!\n"))
   190  				Expect(out).To(Say("\x1b\\[31;1mFAILED\x1b\\[0m\n"))
   191  			})
   192  		})
   193  	})
   194  
   195  	Describe("DisplayHeader", func() {
   196  		It("displays the header colorized and bolded to ui.Out", func() {
   197  			ui.DisplayHeader("some-header")
   198  			Expect(out).To(Say("\x1b\\[1msome-header\x1b\\[0m"))
   199  		})
   200  
   201  		Context("when the locale is not set to English", func() {
   202  			BeforeEach(func() {
   203  				fakeConfig.LocaleReturns("fr-FR")
   204  
   205  				var err error
   206  				ui, err = NewUI(fakeConfig)
   207  				Expect(err).NotTo(HaveOccurred())
   208  
   209  				ui.Out = out
   210  			})
   211  
   212  			It("displays the translated header colorized and bolded to ui.Out", func() {
   213  				ui.DisplayHeader("FEATURE FLAGS")
   214  				Expect(out).To(Say("\x1b\\[1mINDICATEURS DE FONCTION\x1b\\[0m"))
   215  			})
   216  		})
   217  	})
   218  
   219  	Describe("DisplayKeyValueTable", func() {
   220  		JustBeforeEach(func() {
   221  			ui.DisplayKeyValueTable(" ",
   222  				[][]string{
   223  					{"wut0:", ""},
   224  					{"wut1:", "hi hi"},
   225  					{"wut2:", strings.Repeat("a", 9)},
   226  					{"wut3:", "hi hi " + strings.Repeat("a", 9)},
   227  					{"wut4:", strings.Repeat("a", 15) + " " + strings.Repeat("b", 15)},
   228  				},
   229  				2)
   230  		})
   231  
   232  		Context("in a TTY", func() {
   233  			BeforeEach(func() {
   234  				ui.IsTTY = true
   235  				ui.TerminalWidth = 20
   236  			})
   237  
   238  			It("displays a table with the last column wrapping according to width", func() {
   239  				Expect(out).To(Say(" wut0:  " + "\n"))
   240  				Expect(out).To(Say(" wut1:  " + "hi hi\n"))
   241  				Expect(out).To(Say(" wut2:  " + strings.Repeat("a", 9) + "\n"))
   242  				Expect(out).To(Say(" wut3:  hi hi\n"))
   243  				Expect(out).To(Say("        " + strings.Repeat("a", 9) + "\n"))
   244  				Expect(out).To(Say(" wut4:  " + strings.Repeat("a", 15) + "\n"))
   245  				Expect(out).To(Say("        " + strings.Repeat("b", 15) + "\n"))
   246  			})
   247  		})
   248  	})
   249  
   250  	Describe("DisplayLogMessage", func() {
   251  		var message *uifakes.FakeLogMessage
   252  
   253  		BeforeEach(func() {
   254  			var err error
   255  			ui.TimezoneLocation, err = time.LoadLocation("America/Los_Angeles")
   256  			Expect(err).NotTo(HaveOccurred())
   257  
   258  			message = new(uifakes.FakeLogMessage)
   259  			message.MessageReturns("This is a log message\r\n")
   260  			message.TypeReturns("OUT")
   261  			message.TimestampReturns(time.Unix(1468969692, 0)) // "2016-07-19T16:08:12-07:00"
   262  			message.SourceTypeReturns("APP/PROC/WEB")
   263  			message.SourceInstanceReturns("12")
   264  		})
   265  
   266  		Context("with header", func() {
   267  			Context("single line log message", func() {
   268  				It("prints out a single line to STDOUT", func() {
   269  					ui.DisplayLogMessage(message, true)
   270  					Expect(out).To(Say("2016-07-19T16:08:12.00-0700 \\[APP/PROC/WEB/12\\] OUT This is a log message\n"))
   271  				})
   272  			})
   273  
   274  			Context("multi-line log message", func() {
   275  				BeforeEach(func() {
   276  					var err error
   277  					ui.TimezoneLocation, err = time.LoadLocation("America/Los_Angeles")
   278  					Expect(err).NotTo(HaveOccurred())
   279  
   280  					message.MessageReturns("This is a log message\nThis is also a log message")
   281  				})
   282  
   283  				It("prints out mutliple lines to STDOUT", func() {
   284  					ui.DisplayLogMessage(message, true)
   285  					Expect(out).To(Say("2016-07-19T16:08:12.00-0700 \\[APP/PROC/WEB/12\\] OUT This is a log message\n"))
   286  					Expect(out).To(Say("2016-07-19T16:08:12.00-0700 \\[APP/PROC/WEB/12\\] OUT This is also a log message\n"))
   287  				})
   288  			})
   289  		})
   290  
   291  		Context("without header", func() {
   292  			Context("single line log message", func() {
   293  				It("prints out a single line to STDOUT", func() {
   294  					ui.DisplayLogMessage(message, false)
   295  					Expect(out).To(Say("This is a log message\n"))
   296  				})
   297  			})
   298  
   299  			Context("multi-line log message", func() {
   300  				BeforeEach(func() {
   301  					var err error
   302  					ui.TimezoneLocation, err = time.LoadLocation("America/Los_Angeles")
   303  					Expect(err).NotTo(HaveOccurred())
   304  
   305  					message.MessageReturns("This is a log message\nThis is also a log message")
   306  				})
   307  
   308  				It("prints out mutliple lines to STDOUT", func() {
   309  					ui.DisplayLogMessage(message, false)
   310  					Expect(out).To(Say("This is a log message\n"))
   311  					Expect(out).To(Say("This is also a log message\n"))
   312  				})
   313  			})
   314  		})
   315  
   316  		Context("error log lines", func() {
   317  			BeforeEach(func() {
   318  				message.TypeReturns("ERR")
   319  			})
   320  			It("colors the line red", func() {
   321  				ui.DisplayLogMessage(message, false)
   322  				Expect(out).To(Say("\x1b\\[31mThis is a log message\x1b\\[0m\n"))
   323  			})
   324  		})
   325  	})
   326  
   327  	Describe("DisplayNewline", func() {
   328  		It("displays a new line", func() {
   329  			ui.DisplayNewline()
   330  			Expect(out).To(Say("\n"))
   331  		})
   332  	})
   333  
   334  	Describe("DisplayOK", func() {
   335  		It("displays 'OK' in green and bold", func() {
   336  			ui.DisplayOK()
   337  			Expect(out).To(Say("\x1b\\[32;1mOK\x1b\\[0m"))
   338  		})
   339  	})
   340  
   341  	Describe("DisplayTableWithHeader", func() {
   342  		It("makes the first row bold", func() {
   343  			ui.DisplayTableWithHeader(" ",
   344  				[][]string{
   345  					{"", "header1", "header2", "header3"},
   346  					{"#0", "data1", "data2", "data3"},
   347  				},
   348  				2)
   349  			Expect(out).To(Say("    \x1b\\[1mheader1\x1b\\[0m")) // Makes sure empty values are not bolded
   350  			Expect(out).To(Say("\x1b\\[1mheader2\x1b\\[0m"))
   351  			Expect(out).To(Say("\x1b\\[1mheader3\x1b\\[0m"))
   352  			Expect(out).To(Say("#0  data1    data2    data3"))
   353  		})
   354  	})
   355  
   356  	// Covers the happy paths, additional cases are tested in TranslateText
   357  	Describe("DisplayText", func() {
   358  		It("displays the template with map values substituted in to ui.Out with a newline", func() {
   359  			ui.DisplayText(
   360  				"template with {{.SomeMapValue}}",
   361  				map[string]interface{}{
   362  					"SomeMapValue": "map-value",
   363  				})
   364  			Expect(out).To(Say("template with map-value\n"))
   365  		})
   366  
   367  		Context("when the locale is not set to english", func() {
   368  			BeforeEach(func() {
   369  				fakeConfig.LocaleReturns("fr-FR")
   370  
   371  				var err error
   372  				ui, err = NewUI(fakeConfig)
   373  				Expect(err).NotTo(HaveOccurred())
   374  
   375  				ui.Out = out
   376  			})
   377  
   378  			It("displays the translated template with map values substituted in to ui.Out", func() {
   379  				ui.DisplayText(
   380  					"\nTIP: Use '{{.Command}}' to target new org",
   381  					map[string]interface{}{
   382  						"Command": "foo",
   383  					})
   384  				Expect(out).To(Say("\nASTUCE : utilisez 'foo' pour cibler une nouvelle organisation"))
   385  			})
   386  		})
   387  	})
   388  
   389  	Describe("DisplayTextWithFlavor", func() {
   390  		It("displays the template to ui.Out", func() {
   391  			ui.DisplayTextWithFlavor("some-template")
   392  			Expect(out).To(Say("some-template"))
   393  		})
   394  
   395  		Context("when an optional map is passed in", func() {
   396  			It("displays the template with map values colorized, bolded, and substituted in to ui.Out", func() {
   397  				ui.DisplayTextWithFlavor(
   398  					"template with {{.SomeMapValue}}",
   399  					map[string]interface{}{
   400  						"SomeMapValue": "map-value",
   401  					})
   402  				Expect(out).To(Say("template with \x1b\\[36;1mmap-value\x1b\\[0m"))
   403  			})
   404  		})
   405  
   406  		Context("when multiple optional maps are passed in", func() {
   407  			It("displays the template with only the first map values colorized, bolded, and substituted in to ui.Out", func() {
   408  				ui.DisplayTextWithFlavor(
   409  					"template with {{.SomeMapValue}} and {{.SomeOtherMapValue}}",
   410  					map[string]interface{}{
   411  						"SomeMapValue": "map-value",
   412  					},
   413  					map[string]interface{}{
   414  						"SomeOtherMapValue": "other-map-value",
   415  					})
   416  				Expect(out).To(Say("template with \x1b\\[36;1mmap-value\x1b\\[0m and <no value>"))
   417  			})
   418  		})
   419  
   420  		Context("when the locale is not set to english", func() {
   421  			BeforeEach(func() {
   422  				fakeConfig.LocaleReturns("fr-FR")
   423  
   424  				var err error
   425  				ui, err = NewUI(fakeConfig)
   426  				Expect(err).NotTo(HaveOccurred())
   427  
   428  				ui.Out = out
   429  			})
   430  
   431  			It("displays the translated template with map values colorized, bolded and substituted in to ui.Out", func() {
   432  				ui.DisplayTextWithFlavor(
   433  					"App {{.AppName}} does not exist.",
   434  					map[string]interface{}{
   435  						"AppName": "some-app-name",
   436  					})
   437  				Expect(out).To(Say("L'application \x1b\\[36;1msome-app-name\x1b\\[0m n'existe pas.\n"))
   438  			})
   439  		})
   440  	})
   441  
   442  	Describe("DisplayTextWithBold", func() {
   443  		It("displays the template to ui.Out", func() {
   444  			ui.DisplayTextWithBold("some-template")
   445  			Expect(out).To(Say("some-template"))
   446  		})
   447  
   448  		Context("when an optional map is passed in", func() {
   449  			It("displays the template with map values bolded and substituted in to ui.Out", func() {
   450  				ui.DisplayTextWithBold(
   451  					"template with {{.SomeMapValue}}",
   452  					map[string]interface{}{
   453  						"SomeMapValue": "map-value",
   454  					})
   455  				Expect(out).To(Say("template with \x1b\\[1mmap-value\x1b\\[0m"))
   456  			})
   457  		})
   458  
   459  		Context("when multiple optional maps are passed in", func() {
   460  			It("displays the template with only the first map values bolded and substituted in to ui.Out", func() {
   461  				ui.DisplayTextWithBold(
   462  					"template with {{.SomeMapValue}} and {{.SomeOtherMapValue}}",
   463  					map[string]interface{}{
   464  						"SomeMapValue": "map-value",
   465  					},
   466  					map[string]interface{}{
   467  						"SomeOtherMapValue": "other-map-value",
   468  					})
   469  				Expect(out).To(Say("template with \x1b\\[1mmap-value\x1b\\[0m and <no value>"))
   470  			})
   471  		})
   472  
   473  		Context("when the locale is not set to english", func() {
   474  			BeforeEach(func() {
   475  				fakeConfig.LocaleReturns("fr-FR")
   476  
   477  				var err error
   478  				ui, err = NewUI(fakeConfig)
   479  				Expect(err).NotTo(HaveOccurred())
   480  
   481  				ui.Out = out
   482  			})
   483  
   484  			It("displays the translated template with map values bolded and substituted in to ui.Out", func() {
   485  				ui.DisplayTextWithBold(
   486  					"App {{.AppName}} does not exist.",
   487  					map[string]interface{}{
   488  						"AppName": "some-app-name",
   489  					})
   490  				Expect(out).To(Say("L'application \x1b\\[1msome-app-name\x1b\\[0m n'existe pas.\n"))
   491  			})
   492  		})
   493  	})
   494  
   495  	// Covers the happy paths, additional cases are tested in TranslateText
   496  	Describe("DisplayWarning", func() {
   497  		It("displays the warning to ui.Err", func() {
   498  			ui.DisplayWarning(
   499  				"template with {{.SomeMapValue}}",
   500  				map[string]interface{}{
   501  					"SomeMapValue": "map-value",
   502  				})
   503  			Expect(ui.Err).To(Say("template with map-value"))
   504  		})
   505  
   506  		Context("when the locale is not set to english", func() {
   507  			BeforeEach(func() {
   508  				fakeConfig.LocaleReturns("fr-FR")
   509  
   510  				var err error
   511  				ui, err = NewUI(fakeConfig)
   512  				Expect(err).NotTo(HaveOccurred())
   513  
   514  				ui.Err = NewBuffer()
   515  			})
   516  
   517  			It("displays the translated warning to ui.Err", func() {
   518  				ui.DisplayWarning(
   519  					"'{{.VersionShort}}' and '{{.VersionLong}}' are also accepted.",
   520  					map[string]interface{}{
   521  						"VersionShort": "some-value",
   522  						"VersionLong":  "some-other-value",
   523  					})
   524  				Expect(ui.Err).To(Say("'some-value' et 'some-other-value' sont également acceptés.\n"))
   525  			})
   526  		})
   527  	})
   528  
   529  	// Covers the happy paths, additional cases are tested in TranslateText
   530  	Describe("DisplayWarnings", func() {
   531  		It("displays the warnings to ui.Err", func() {
   532  			ui.DisplayWarnings([]string{"warning-1", "warning-2"})
   533  			Expect(ui.Err).To(Say("warning-1\n"))
   534  			Expect(ui.Err).To(Say("warning-2\n"))
   535  		})
   536  
   537  		Context("when the locale is not set to english", func() {
   538  			BeforeEach(func() {
   539  				fakeConfig.LocaleReturns("fr-FR")
   540  
   541  				var err error
   542  				ui, err = NewUI(fakeConfig)
   543  				Expect(err).NotTo(HaveOccurred())
   544  
   545  				ui.Err = NewBuffer()
   546  			})
   547  
   548  			It("displays the translated warnings to ui.Err", func() {
   549  				ui.DisplayWarnings([]string{"Also delete any mapped routes", "FEATURE FLAGS"})
   550  				Expect(ui.Err).To(Say("Supprimer aussi les routes mappées\n"))
   551  				Expect(ui.Err).To(Say("INDICATEURS DE FONCTION\n"))
   552  			})
   553  		})
   554  	})
   555  
   556  	Describe("RequestLoggerFileWriter", func() {
   557  		It("returns a RequestLoggerFileWriter with the consistent filewriting mutex", func() {
   558  			logger1 := ui.RequestLoggerFileWriter(nil)
   559  			logger2 := ui.RequestLoggerFileWriter(nil)
   560  
   561  			c := make(chan bool)
   562  			err := logger1.Start()
   563  			Expect(err).ToNot(HaveOccurred())
   564  			go func() {
   565  				Expect(logger2.Start()).ToNot(HaveOccurred())
   566  				c <- true
   567  			}()
   568  			Consistently(c).ShouldNot(Receive())
   569  			Expect(logger1.Stop()).ToNot(HaveOccurred())
   570  			Eventually(c).Should(Receive())
   571  		})
   572  	})
   573  
   574  	Describe("RequestLoggerTerminalDisplay", func() {
   575  		It("returns a RequestLoggerTerminalDisplay with the consistent display mutex", func() {
   576  			logger1 := ui.RequestLoggerTerminalDisplay()
   577  			logger2 := ui.RequestLoggerTerminalDisplay()
   578  
   579  			c := make(chan bool)
   580  			err := logger1.Start()
   581  			Expect(err).ToNot(HaveOccurred())
   582  			go func() {
   583  				Expect(logger2.Start()).ToNot(HaveOccurred())
   584  				c <- true
   585  			}()
   586  			Consistently(c).ShouldNot(Receive())
   587  			Expect(logger1.Stop()).ToNot(HaveOccurred())
   588  			Eventually(c).Should(Receive())
   589  		})
   590  	})
   591  
   592  	Describe("TranslateText", func() {
   593  		It("returns the template", func() {
   594  			Expect(ui.TranslateText("some-template")).To(Equal("some-template"))
   595  		})
   596  
   597  		Context("when an optional map is passed in", func() {
   598  			It("returns the template with map values substituted in", func() {
   599  				expected := ui.TranslateText(
   600  					"template {{.SomeMapValue}}",
   601  					map[string]interface{}{
   602  						"SomeMapValue": "map-value",
   603  					})
   604  				Expect(expected).To(Equal("template map-value"))
   605  			})
   606  		})
   607  
   608  		Context("when multiple optional maps are passed in", func() {
   609  			It("returns the template with only the first map values substituted in", func() {
   610  				expected := ui.TranslateText(
   611  					"template with {{.SomeMapValue}} and {{.SomeOtherMapValue}}",
   612  					map[string]interface{}{
   613  						"SomeMapValue": "map-value",
   614  					},
   615  					map[string]interface{}{
   616  						"SomeOtherMapValue": "other-map-value",
   617  					})
   618  				Expect(expected).To(Equal("template with map-value and <no value>"))
   619  			})
   620  		})
   621  
   622  		Context("when the locale is not set to english", func() {
   623  			BeforeEach(func() {
   624  				fakeConfig.LocaleReturns("fr-FR")
   625  
   626  				var err error
   627  				ui, err = NewUI(fakeConfig)
   628  				Expect(err).NotTo(HaveOccurred())
   629  			})
   630  
   631  			It("returns the translated template", func() {
   632  				expected := ui.TranslateText("   View allowable quotas with 'CF_NAME quotas'")
   633  				Expect(expected).To(Equal("   Affichez les quotas pouvant être alloués avec 'CF_NAME quotas'"))
   634  			})
   635  		})
   636  	})
   637  
   638  	Describe("UserFriendlyDate", func() {
   639  		It("formats a time into an ISO8601 string", func() {
   640  			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}"))
   641  		})
   642  	})
   643  })