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