mirror of https://github.com/pulumi/pulumi.git
291 lines
6.9 KiB
Go
291 lines
6.9 KiB
Go
// Copyright 2016-2021, Pulumi Corporation.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package cmdutil
|
|
|
|
import (
|
|
"regexp"
|
|
"testing"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestMeasureText(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
text string
|
|
expected int
|
|
}{
|
|
{
|
|
text: "",
|
|
expected: 0,
|
|
},
|
|
{
|
|
text: "a",
|
|
expected: 1,
|
|
},
|
|
{
|
|
text: "├",
|
|
expected: 1,
|
|
},
|
|
{
|
|
text: "├─ ",
|
|
expected: 4,
|
|
},
|
|
{
|
|
text: "\x1b[4m\x1b[38;5;12mType\x1b[0m",
|
|
expected: 4,
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
c := c
|
|
t.Run(c.text, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
count := MeasureText(c.text)
|
|
assert.Equal(t, c.expected, count)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTablePrinting(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rows := []TableRow{
|
|
{Columns: []string{"A", "B", "C"}},
|
|
{Columns: []string{"Some A", "B", "Some C"}},
|
|
}
|
|
|
|
table := &Table{
|
|
Headers: []string{"ColumnA", "Long column B", "C"},
|
|
Rows: rows,
|
|
Prefix: " ",
|
|
}
|
|
|
|
expected := "" +
|
|
" ColumnA Long column B C\n" +
|
|
" A B C\n" +
|
|
" Some A B Some C\n"
|
|
|
|
assert.Equal(t, expected, table.ToStringWithGap(" "))
|
|
}
|
|
|
|
func TestColorTablePrinting(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
greenText := func(msg string) string {
|
|
return colors.Always.Colorize(colors.Green + msg + colors.Reset)
|
|
}
|
|
|
|
rows := []TableRow{
|
|
{Columns: []string{greenText("+"), "pulumi:pulumi:Stack", "aws-cs-webserver-test", greenText("create")}},
|
|
{Columns: []string{greenText("+"), "├─ aws:ec2/instance:Instance", "web-server-www", greenText("create")}},
|
|
{Columns: []string{greenText("+"), "├─ aws:ec2/securityGroup:SecurityGroup", "web-secgrp", greenText("create")}},
|
|
{Columns: []string{greenText("+"), "└─ pulumi:providers:aws", "default_4_25_0", greenText("create")}},
|
|
}
|
|
|
|
columnHeader := func(msg string) string {
|
|
return colors.Always.Colorize(colors.Underline + colors.BrightBlue + msg + colors.Reset)
|
|
}
|
|
|
|
table := &Table{
|
|
Headers: []string{"", columnHeader("Type"), columnHeader("Name"), columnHeader("Plan")},
|
|
Rows: rows,
|
|
Prefix: " ",
|
|
}
|
|
|
|
expected := "" +
|
|
" Type Name Plan\n" +
|
|
" + pulumi:pulumi:Stack aws-cs-webserver-test create\n" +
|
|
" + ├─ aws:ec2/instance:Instance web-server-www create\n" +
|
|
" + ├─ aws:ec2/securityGroup:SecurityGroup web-secgrp create\n" +
|
|
" + └─ pulumi:providers:aws default_4_25_0 create\n"
|
|
|
|
colorTable := table.ToStringWithGap(" ")
|
|
// 7-bit C1 ANSI sequences
|
|
ansiEscape := regexp.MustCompile(`\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])`)
|
|
cleanTable := ansiEscape.ReplaceAllString(colorTable, "")
|
|
|
|
assert.Equal(t, expected, cleanTable)
|
|
}
|
|
|
|
func TestIsTruthy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
give string
|
|
want bool
|
|
}{
|
|
{"1", true},
|
|
{"true", true},
|
|
{"True", true},
|
|
{"TRUE", true},
|
|
{"0", false},
|
|
{"false", false},
|
|
{"False", false},
|
|
{"FALSE", false},
|
|
{"", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.give, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert.Equal(t, tt.want, IsTruthy(tt.give))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReadConsoleFancy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
desc string
|
|
model readConsoleModel
|
|
|
|
// Prompt expected on the command line, if any
|
|
// after which we begin typing.
|
|
expectPrompt string
|
|
|
|
// Messages to send to the model in order.
|
|
// Does not include Enter or Ctrl+C.
|
|
giveMsgs []tea.Msg
|
|
|
|
// Output visible before we hit Enter.
|
|
wantEcho string
|
|
|
|
// Output visible after we hit Enter.
|
|
wantAccepted string
|
|
|
|
// Value returned by the model.
|
|
wantValue string
|
|
}{
|
|
{
|
|
desc: "plain",
|
|
model: newReadConsoleModel("Enter a value", false /* secret */),
|
|
expectPrompt: "Enter a value: ",
|
|
giveMsgs: []tea.Msg{
|
|
tea.KeyMsg{
|
|
Type: tea.KeyRunes,
|
|
Runes: []rune("hello"),
|
|
},
|
|
},
|
|
wantEcho: "Enter a value: hello",
|
|
wantAccepted: "Enter a value: hello ",
|
|
wantValue: "hello",
|
|
},
|
|
{
|
|
desc: "secret",
|
|
model: newReadConsoleModel("Password", true /* secret */),
|
|
expectPrompt: "Password: ",
|
|
giveMsgs: []tea.Msg{
|
|
tea.KeyMsg{
|
|
Type: tea.KeyRunes,
|
|
Runes: []rune("hunter2"),
|
|
},
|
|
},
|
|
wantEcho: "Password: *******",
|
|
wantAccepted: "Password: ",
|
|
wantValue: "hunter2",
|
|
},
|
|
{
|
|
desc: "no prompt",
|
|
model: newReadConsoleModel("" /* prompt */, false /* secret */),
|
|
giveMsgs: []tea.Msg{
|
|
tea.KeyMsg{
|
|
Type: tea.KeyRunes,
|
|
Runes: []rune("hello"),
|
|
},
|
|
},
|
|
wantEcho: "> hello",
|
|
wantAccepted: "> hello ",
|
|
wantValue: "hello",
|
|
},
|
|
{
|
|
desc: "backspace",
|
|
model: newReadConsoleModel("" /* prompt */, false /* secret */),
|
|
giveMsgs: []tea.Msg{
|
|
tea.KeyMsg{
|
|
Type: tea.KeyRunes,
|
|
Runes: []rune("foobar"),
|
|
},
|
|
tea.KeyMsg{Type: tea.KeyBackspace},
|
|
tea.KeyMsg{Type: tea.KeyBackspace},
|
|
tea.KeyMsg{
|
|
Type: tea.KeyRunes,
|
|
Runes: []rune("az"),
|
|
},
|
|
},
|
|
wantEcho: "> foobaz",
|
|
wantAccepted: "> foobaz ",
|
|
wantValue: "foobaz",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.desc, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var m tea.Model = tt.model
|
|
m.Init()
|
|
|
|
if tt.expectPrompt != "" {
|
|
assert.Contains(t, m.View(), tt.expectPrompt,
|
|
"initial view should contain prompt")
|
|
}
|
|
|
|
for _, msg := range tt.giveMsgs {
|
|
m, _ = m.Update(msg)
|
|
}
|
|
|
|
assert.Contains(t, m.View(), tt.wantEcho,
|
|
"prompt before pressing enter did not match")
|
|
|
|
m, _ = m.Update(tea.KeyMsg{Type: tea.KeyEnter})
|
|
|
|
assert.Contains(t, m.View(), tt.wantAccepted,
|
|
"prompt after pressing enter did not match")
|
|
|
|
final, ok := m.(readConsoleModel)
|
|
assert.True(t, ok, "expected readConsoleModel, got %T", m)
|
|
|
|
assert.Equal(t, tt.wantValue, final.Value, "final value should match")
|
|
assert.False(t, final.Canceled, "should not be canceled")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReadConsoleFancy_cancel(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var m tea.Model = newReadConsoleModel("Name:", false /* secret */)
|
|
m.Init()
|
|
|
|
m, _ = m.Update(tea.KeyMsg{
|
|
Type: tea.KeyRunes,
|
|
Runes: []rune("hello"),
|
|
})
|
|
m, _ = m.Update(tea.KeyMsg{Type: tea.KeyEsc})
|
|
|
|
final, ok := m.(readConsoleModel)
|
|
assert.True(t, ok, "expected readConsoleModel, got %T", m)
|
|
assert.True(t, final.Canceled, "should be canceled")
|
|
}
|