github.com/cloudfoundry/cli@v7.1.0+incompatible/cf/terminal/ui_test.go (about)

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