diff --git a/Makefile b/Makefile index 31b29c02..5ebdbae0 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: build contrib_check install binary_msg lint run size test uninstall +.PHONY: build contrib_check coverage install binary_msg lint run size test uninstall # detect GOPATH if not set ifndef $(GOPATH) @@ -30,6 +30,10 @@ build: contrib_check: npx all-contributors-cli check +coverage: + go test -coverprofile=coverage.out ./... + go tool cover -html=coverage.out + install: @echo "Installing wtfutil..." @go clean diff --git a/cfg/common_settings.go b/cfg/common_settings.go index e2032cd3..da30ba84 100644 --- a/cfg/common_settings.go +++ b/cfg/common_settings.go @@ -14,8 +14,8 @@ type Colors struct { BorderNormal string Checked string Foreground string - HighlightFore string HighlightBack string + HighlightFore string Text string Title string @@ -83,11 +83,11 @@ func NewCommonSettingsFromModule(name, defaultTitle string, defaultFocusable boo PositionSettings: NewPositionSettingsFromYAML(name, moduleConfig), Bordered: moduleConfig.UBool("border", true), + Config: moduleConfig, Enabled: moduleConfig.UBool("enabled", false), Focusable: moduleConfig.UBool("focusable", defaultFocusable), RefreshInterval: moduleConfig.UInt("refreshInterval", 300), Title: moduleConfig.UString("title", defaultTitle), - Config: moduleConfig, focusChar: moduleConfig.UInt("focusChar", -1), } @@ -135,7 +135,7 @@ func (common *Common) RightAlignFormat(width int) string { return fmt.Sprintf("%%%ds", width-borderOffset) } -func (common *Common) SigilStr(len, pos int, width int) string { +func (common *Common) SigilStr(len, pos, width int) string { sigils := "" if len > 1 { diff --git a/cfg/common_settings_test.go b/cfg/common_settings_test.go new file mode 100644 index 00000000..f9d188ce --- /dev/null +++ b/cfg/common_settings_test.go @@ -0,0 +1,187 @@ +package cfg + +import ( + "testing" + + "github.com/olebedev/config" + "github.com/stretchr/testify/assert" +) + +var ( + testYaml = ` +wtf: + colors: +` + + moduleConfig, _ = config.ParseYaml(testYaml) + globalSettings, _ = config.ParseYaml(testYaml) + + testCfg = NewCommonSettingsFromModule( + "test", + "Test Config", + true, + moduleConfig, + globalSettings, + ) +) + +func Test_NewCommonSettingsFromModule(t *testing.T) { + assert.Equal(t, true, testCfg.Bordered) + assert.Equal(t, false, testCfg.Enabled) + assert.Equal(t, true, testCfg.Focusable) + assert.Equal(t, "test", testCfg.Module.Name) + assert.Equal(t, "test", testCfg.Module.Type) + assert.Equal(t, "", testCfg.FocusChar()) + assert.Equal(t, 300, testCfg.RefreshInterval) + assert.Equal(t, "Test Config", testCfg.Title) +} + +func Test_DefaultFocusedRowColor(t *testing.T) { + assert.Equal(t, "black:green", testCfg.DefaultFocusedRowColor()) +} + +func Test_DefaultRowColor(t *testing.T) { + assert.Equal(t, "white:transparent", testCfg.DefaultRowColor()) +} + +func Test_FocusChar(t *testing.T) { + tests := []struct { + name string + expectedChar string + before func(testCfg *Common) + }{ + { + name: "with no focus char specified", + expectedChar: "", + before: func(testCfg *Common) { + testCfg.focusChar = -1 + }, + }, + { + name: "with explicit focus char specified", + expectedChar: "3", + before: func(testCfg *Common) { + testCfg.focusChar = 3 + }, + }, + { + name: "with ridiculous focus char specified", + expectedChar: "Q", + before: func(testCfg *Common) { + testCfg.focusChar = 33 + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.before(testCfg) + + assert.Equal(t, tt.expectedChar, testCfg.FocusChar()) + }) + } +} + +func Test_RowColor(t *testing.T) { + tests := []struct { + name string + idx int + expectedColor string + }{ + { + name: "odd rows, default", + idx: 3, + expectedColor: "lightblue", + }, + { + name: "even rows, default", + idx: 8, + expectedColor: "white", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expectedColor, testCfg.RowColor(tt.idx)) + }) + } +} + +func Test_RightAlignFormat(t *testing.T) { + tests := []struct { + name string + width int + expected string + }{ + { + name: "with zero", + width: 0, + expected: "%-2s", + }, + { + name: "with positive integer", + width: 3, + expected: "%1s", + }, + { + name: "with negative integer", + width: -3, + expected: "%-5s", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, testCfg.RightAlignFormat(tt.width)) + }) + } +} + +func Test_SigilStr(t *testing.T) { + tests := []struct { + name string + len int + pos int + width int + expected string + }{ + { + name: "with zero pages", + len: 0, + pos: 1, + width: 5, + expected: "", + }, + { + name: "with one page", + len: 1, + pos: 1, + width: 5, + expected: "", + }, + { + name: "with multiple pages", + len: 3, + pos: 1, + width: 5, + expected: "[lightblue]*_*[white]", + }, + { + name: "with negative pages", + len: -3, + pos: 1, + width: 5, + expected: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, testCfg.SigilStr(tt.len, tt.pos, tt.width)) + }) + } +} + +func Test_Validations(t *testing.T) { + assert.Equal(t, 4, len(testCfg.Validations())) +} diff --git a/cfg/error_messages_test.go b/cfg/error_messages_test.go new file mode 100644 index 00000000..4537725e --- /dev/null +++ b/cfg/error_messages_test.go @@ -0,0 +1,14 @@ +package cfg + +import ( + "errors" +) + +var ( + errTest = errors.New("Busted") +) + +func ExampleDisplayError() { + displayError(errTest) + // Output: Error: Busted +} diff --git a/cfg/position_settings.go b/cfg/position_settings.go index 5de86d64..6358a352 100644 --- a/cfg/position_settings.go +++ b/cfg/position_settings.go @@ -41,10 +41,10 @@ func NewPositionSettingsFromYAML(moduleName string, moduleConfig *config.Config) pos := PositionSettings{ Validations: validations, - Top: validations.valueFor("top"), - Left: validations.valueFor("left"), - Width: validations.valueFor("width"), - Height: validations.valueFor("height"), + Top: validations.intValueFor("top"), + Left: validations.intValueFor("left"), + Width: validations.intValueFor("width"), + Height: validations.intValueFor("height"), } return pos diff --git a/cfg/position_validation.go b/cfg/position_validation.go index 245ca631..0f3130ec 100644 --- a/cfg/position_validation.go +++ b/cfg/position_validation.go @@ -54,6 +54,8 @@ func (posVal *positionValidation) String() string { return fmt.Sprintf("Invalid value for %s:\t%d", aurora.Yellow(posVal.name), posVal.intVal) } +/* -------------------- Unexported Functions -------------------- */ + func newPositionValidation(name string, intVal int, err error) *positionValidation { posVal := &positionValidation{ err: err, diff --git a/cfg/position_validation_test.go b/cfg/position_validation_test.go new file mode 100644 index 00000000..e5a3e182 --- /dev/null +++ b/cfg/position_validation_test.go @@ -0,0 +1,26 @@ +package cfg + +import ( + "errors" + "testing" + + "github.com/alecthomas/assert" +) + +var ( + posVal = &positionValidation{ + err: errors.New("Busted"), + name: "top", + intVal: -3, + } +) + +func Test_Attributes(t *testing.T) { + assert.EqualError(t, posVal.Error(), "Busted") + assert.Equal(t, true, posVal.HasError()) + assert.Equal(t, -3, posVal.IntValue()) + + assert.Contains(t, posVal.String(), "Invalid") + assert.Contains(t, posVal.String(), "top") + assert.Contains(t, posVal.String(), "-3") +} diff --git a/cfg/validations.go b/cfg/validations.go index fc117b71..edcd1080 100644 --- a/cfg/validations.go +++ b/cfg/validations.go @@ -18,7 +18,7 @@ func (vals *Validations) append(key string, posVal Validatable) { vals.validations[key] = posVal } -func (vals *Validations) valueFor(key string) int { +func (vals *Validations) intValueFor(key string) int { val := vals.validations[key] if val != nil { return val.IntValue() diff --git a/cfg/validations_test.go b/cfg/validations_test.go new file mode 100644 index 00000000..9419bd0b --- /dev/null +++ b/cfg/validations_test.go @@ -0,0 +1,38 @@ +package cfg + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var ( + vals = NewValidations() +) + +func Test_intValueFor(t *testing.T) { + vals.append("left", newPositionValidation("left", 3, nil)) + + tests := []struct { + name string + key string + expected int + }{ + { + name: "with valid key", + key: "left", + expected: 3, + }, + { + name: "with invalid key", + key: "cat", + expected: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, vals.intValueFor(tt.key)) + }) + } +} diff --git a/checklist/checklist.go b/checklist/checklist.go index e3d336f5..58b7124f 100644 --- a/checklist/checklist.go +++ b/checklist/checklist.go @@ -22,7 +22,8 @@ func NewChecklist(checkedIcon, uncheckedIcon string) Checklist { /* -------------------- Exported Functions -------------------- */ -// Add creates a new item in the checklist +// Add creates a new checklist item and prepends it onto the existing +// list of items. The new one is at the start of the list func (list *Checklist) Add(checked bool, text string) { item := NewChecklistItem( checked, @@ -49,7 +50,9 @@ func (list *Checklist) CheckedItems() []*ChecklistItem { // Delete removes the selected item from the checklist func (list *Checklist) Delete(selectedIndex int) { - list.Items = append(list.Items[:selectedIndex], list.Items[selectedIndex+1:]...) + if selectedIndex >= 0 && selectedIndex < len(list.Items) { + list.Items = append(list.Items[:selectedIndex], list.Items[selectedIndex+1:]...) + } } // IsSelectable returns true if the checklist has selectable items, false if it does not @@ -75,7 +78,7 @@ func (list *Checklist) LongestLine() int { return maxLen } -// IndexByItem returns the index of a giving item if found ,otherwise returns 0 with ok set to false +// IndexByItem returns the index of a giving item if found, otherwise returns 0 with ok set to false func (list *Checklist) IndexByItem(selectableItem *ChecklistItem) (index int, ok bool) { for idx, item := range list.Items { if item == selectableItem { diff --git a/checklist/checklist_item_test.go b/checklist/checklist_item_test.go index 8c96fcdd..bbaf0372 100644 --- a/checklist/checklist_item_test.go +++ b/checklist/checklist_item_test.go @@ -10,8 +10,8 @@ func testChecklistItem() *ChecklistItem { item := NewChecklistItem( false, "test", - "x", - " ", + "", + "", ) return item } diff --git a/checklist/checklist_test.go b/checklist/checklist_test.go new file mode 100644 index 00000000..6972c6a6 --- /dev/null +++ b/checklist/checklist_test.go @@ -0,0 +1,436 @@ +package checklist + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_NewCheckist(t *testing.T) { + cl := NewChecklist("o", "-") + + assert.IsType(t, Checklist{}, cl) + assert.Equal(t, "o", cl.checkedIcon) + assert.Equal(t, -1, cl.selected) + assert.Equal(t, "-", cl.uncheckedIcon) + assert.Equal(t, 0, len(cl.Items)) +} + +func Test_Add(t *testing.T) { + cl := NewChecklist("o", "-") + cl.Add(true, "test item") + + assert.Equal(t, 1, len(cl.Items)) +} + +func Test_CheckedItems(t *testing.T) { + tests := []struct { + name string + expectedLen int + checkedLen int + before func(cl *Checklist) + }{ + { + name: "with no items", + expectedLen: 0, + checkedLen: 0, + before: func(cl *Checklist) {}, + }, + { + name: "with no checked items", + expectedLen: 1, + checkedLen: 0, + before: func(cl *Checklist) { + cl.Add(false, "unchecked item") + }, + }, + { + name: "with one checked item", + expectedLen: 2, + checkedLen: 1, + before: func(cl *Checklist) { + cl.Add(false, "unchecked item") + cl.Add(true, "checked item") + }, + }, + { + name: "with multiple checked items", + expectedLen: 3, + checkedLen: 2, + before: func(cl *Checklist) { + cl.Add(false, "unchecked item") + cl.Add(true, "checked item 11") + cl.Add(true, "checked item 2") + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cl := NewChecklist("o", "-") + tt.before(&cl) + + assert.Equal(t, tt.expectedLen, len(cl.Items)) + assert.Equal(t, tt.checkedLen, len(cl.CheckedItems())) + }) + } +} + +func Test_Delete(t *testing.T) { + tests := []struct { + name string + idx int + expectedLen int + }{ + { + name: "with valid index", + idx: 0, + expectedLen: 0, + }, + { + name: "with invalid index", + idx: 2, + expectedLen: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cl := NewChecklist("o", "-") + + cl.Add(true, "test item") + cl.Delete(tt.idx) + + assert.Equal(t, tt.expectedLen, len(cl.Items)) + }) + } +} + +func Test_IsSelectable(t *testing.T) { + tests := []struct { + name string + selected int + expected bool + }{ + { + name: "nothing selected", + selected: -1, + expected: false, + }, + { + name: "valid selection", + selected: 1, + expected: true, + }, + { + name: "invalid selection", + selected: 3, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cl := NewChecklist("o", "-") + cl.Add(true, "test item 1") + cl.Add(false, "test item 2") + + cl.selected = tt.selected + + assert.Equal(t, tt.expected, cl.IsSelectable()) + }) + } +} + +func Test_IsUnselectable(t *testing.T) { + tests := []struct { + name string + selected int + expected bool + }{ + { + name: "nothing selected", + selected: -1, + expected: true, + }, + { + name: "valid selection", + selected: 1, + expected: false, + }, + { + name: "invalid selection", + selected: 3, + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cl := NewChecklist("o", "-") + cl.Add(true, "test item 1") + cl.Add(false, "test item 2") + + cl.selected = tt.selected + + assert.Equal(t, tt.expected, cl.IsUnselectable()) + }) + } +} + +func Test_LongestLine(t *testing.T) { + tests := []struct { + name string + expectedLen int + before func(cl *Checklist) + }{ + { + name: "with no items", + expectedLen: 0, + before: func(cl *Checklist) {}, + }, + { + name: "with different-length items", + expectedLen: 12, + before: func(cl *Checklist) { + cl.Add(true, "test item 1") + cl.Add(false, "test item 22") + }, + }, + { + name: "with same-length items", + expectedLen: 11, + before: func(cl *Checklist) { + cl.Add(true, "test item 1") + cl.Add(false, "test item 2") + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cl := NewChecklist("o", "-") + tt.before(&cl) + + assert.Equal(t, tt.expectedLen, cl.LongestLine()) + }) + } +} + +func Test_IndexByItem(t *testing.T) { + cl := NewChecklist("o", "-") + cl.Add(false, "unchecked item") + cl.Add(true, "checked item") + + tests := []struct { + name string + item *ChecklistItem + expectedIdx int + expectedOk bool + }{ + { + name: "with nil", + item: nil, + expectedIdx: 0, + expectedOk: false, + }, + { + name: "with valid item", + item: cl.Items[1], + expectedIdx: 1, + expectedOk: true, + }, + { + name: "with valid item", + item: NewChecklistItem(false, "invalid", "x", " "), + expectedIdx: 0, + expectedOk: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + idx, ok := cl.IndexByItem(tt.item) + + assert.Equal(t, tt.expectedIdx, idx) + assert.Equal(t, tt.expectedOk, ok) + }) + } +} + +func Test_UncheckedItems(t *testing.T) { + tests := []struct { + name string + expectedLen int + checkedLen int + before func(cl *Checklist) + }{ + { + name: "with no items", + expectedLen: 0, + checkedLen: 0, + before: func(cl *Checklist) {}, + }, + { + name: "with no unchecked items", + expectedLen: 1, + checkedLen: 0, + before: func(cl *Checklist) { + cl.Add(true, "unchecked item") + }, + }, + { + name: "with one unchecked item", + expectedLen: 2, + checkedLen: 1, + before: func(cl *Checklist) { + cl.Add(false, "unchecked item") + cl.Add(true, "checked item") + }, + }, + { + name: "with multiple unchecked items", + expectedLen: 3, + checkedLen: 2, + before: func(cl *Checklist) { + cl.Add(false, "unchecked item") + cl.Add(true, "checked item 11") + cl.Add(false, "checked item 2") + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cl := NewChecklist("o", "-") + tt.before(&cl) + + assert.Equal(t, tt.expectedLen, len(cl.Items)) + assert.Equal(t, tt.checkedLen, len(cl.UncheckedItems())) + }) + } +} + +func Test_Unselect(t *testing.T) { + cl := NewChecklist("o", "-") + cl.Add(false, "unchecked item") + + cl.selected = 0 + assert.Equal(t, 0, cl.selected) + + cl.Unselect() + assert.Equal(t, -1, cl.selected) +} + +/* -------------------- Sort Interface -------------------- */ + +func Test_Len(t *testing.T) { + tests := []struct { + name string + expectedLen int + before func(cl *Checklist) + }{ + { + name: "with no items", + expectedLen: 0, + before: func(cl *Checklist) {}, + }, + { + name: "with one item", + expectedLen: 1, + before: func(cl *Checklist) { + cl.Add(false, "unchecked item") + }, + }, + { + name: "with multiple items", + expectedLen: 3, + before: func(cl *Checklist) { + cl.Add(false, "unchecked item") + cl.Add(true, "checked item 1") + cl.Add(false, "checked item 2") + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cl := NewChecklist("o", "-") + tt.before(&cl) + + assert.Equal(t, tt.expectedLen, cl.Len()) + }) + } +} + +func Test_Less(t *testing.T) { + tests := []struct { + name string + first string + second string + expected bool + }{ + { + name: "same", + first: "", + second: "", + expected: false, + }, + { + name: "last less", + first: "beta", + second: "alpha", + expected: true, + }, + { + name: "first less", + first: "alpha", + second: "beta", + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cl := NewChecklist("o", "-") + cl.Add(false, tt.first) + cl.Add(false, tt.second) + + assert.Equal(t, tt.expected, cl.Less(0, 1)) + }) + } +} + +func Test_Swap(t *testing.T) { + tests := []struct { + name string + first string + second string + expected bool + }{ + { + name: "same", + first: "", + second: "", + }, + { + name: "last less", + first: "alpha", + second: "beta", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cl := NewChecklist("o", "-") + cl.Add(false, tt.first) + cl.Add(false, tt.second) + + cl.Swap(0, 1) + + assert.Equal(t, tt.expected, cl.Items[0].Text == "beta") + assert.Equal(t, tt.expected, cl.Items[1].Text == "alpha") + }) + } +} diff --git a/flags/flags.go b/flags/flags.go index d77e6009..cc55c35c 100644 --- a/flags/flags.go +++ b/flags/flags.go @@ -8,7 +8,6 @@ import ( goFlags "github.com/jessevdk/go-flags" "github.com/olebedev/config" "github.com/wtfutil/wtf/help" - "github.com/wtfutil/wtf/utils" ) // Flags is the container for command line flag data @@ -80,7 +79,7 @@ func (flags *Flags) Parse() { } // If no config file is explicitly passed in as a param then set the flag to the default config file - homeDir, err := utils.Home() + homeDir, err := os.UserHomeDir() if err != nil { fmt.Printf("Error: %v\n", err) os.Exit(1) diff --git a/go.mod b/go.mod index 39a739ce..e11c79e0 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/VictorAvelar/devto-api-go v1.0.0 github.com/adlio/trello v1.4.0 + github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 github.com/alecthomas/chroma v0.6.8 github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8 github.com/briandowns/openweathermap v0.0.0-20180804155945-5f41b7c9d92d diff --git a/logger/log.go b/logger/log.go index c095ea42..f72d1aef 100644 --- a/logger/log.go +++ b/logger/log.go @@ -4,8 +4,6 @@ import ( "log" "os" "path/filepath" - - "github.com/wtfutil/wtf/utils" ) /* -------------------- Exported Functions -------------------- */ @@ -30,7 +28,7 @@ func LogFileMissing() bool { } func LogFilePath() string { - dir, err := utils.Home() + dir, err := os.UserHomeDir() if err != nil { return "" } diff --git a/modules/buildkite/client.go b/modules/buildkite/client.go index a0d151cc..22b1133f 100644 --- a/modules/buildkite/client.go +++ b/modules/buildkite/client.go @@ -2,8 +2,9 @@ package buildkite import ( "fmt" - "github.com/wtfutil/wtf/utils" "net/http" + + "github.com/wtfutil/wtf/utils" ) type Pipeline struct { @@ -61,7 +62,7 @@ func (widget *Widget) recentBuilds(pipeline PipelineSettings) ([]Build, error) { } builds := []Build{} - err = utils.ParseJson(&builds, resp.Body) + err = utils.ParseJSON(&builds, resp.Body) if err != nil { return nil, err } diff --git a/modules/circleci/client.go b/modules/circleci/client.go index e6bd7c19..5a609dd8 100644 --- a/modules/circleci/client.go +++ b/modules/circleci/client.go @@ -28,7 +28,7 @@ func (client *Client) BuildsFor() ([]*Build, error) { return builds, err } - err = utils.ParseJson(&builds, resp.Body) + err = utils.ParseJSON(&builds, resp.Body) if err != nil { return builds, err } diff --git a/modules/gitter/client.go b/modules/gitter/client.go index 074828fa..01976447 100644 --- a/modules/gitter/client.go +++ b/modules/gitter/client.go @@ -16,7 +16,7 @@ func GetMessages(roomId string, numberOfMessages int, apiToken string) ([]Messag return nil, err } - err = utils.ParseJson(&messages, resp.Body) + err = utils.ParseJSON(&messages, resp.Body) if err != nil { return nil, err } @@ -32,7 +32,7 @@ func GetRoom(roomUri, apiToken string) (*Room, error) { return nil, err } - err = utils.ParseJson(&rooms, resp.Body) + err = utils.ParseJSON(&rooms, resp.Body) if err != nil { return nil, err } diff --git a/modules/hackernews/client.go b/modules/hackernews/client.go index 9658bf3c..62f83942 100644 --- a/modules/hackernews/client.go +++ b/modules/hackernews/client.go @@ -19,7 +19,7 @@ func GetStories(storyType string) ([]int, error) { return storyIds, err } - err = utils.ParseJson(&storyIds, resp.Body) + err = utils.ParseJSON(&storyIds, resp.Body) if err != nil { return storyIds, err } @@ -36,7 +36,7 @@ func GetStory(id int) (Story, error) { return story, err } - err = utils.ParseJson(&story, resp.Body) + err = utils.ParseJSON(&story, resp.Body) if err != nil { return story, err } diff --git a/modules/jenkins/client.go b/modules/jenkins/client.go index eb4445f0..1f2bd1a6 100644 --- a/modules/jenkins/client.go +++ b/modules/jenkins/client.go @@ -40,7 +40,7 @@ func (widget *Widget) Create(jenkinsURL string, username string, apiKey string) return view, err } - err = utils.ParseJson(view, resp.Body) + err = utils.ParseJSON(view, resp.Body) if err != nil { return view, err } diff --git a/modules/jira/client.go b/modules/jira/client.go index 32bd0810..b59cb19c 100644 --- a/modules/jira/client.go +++ b/modules/jira/client.go @@ -38,7 +38,7 @@ func (widget *Widget) IssuesFor(username string, projects []string, jql string) } searchResult := &SearchResult{} - err = utils.ParseJson(searchResult, resp.Body) + err = utils.ParseJSON(searchResult, resp.Body) if err != nil { return nil, err } diff --git a/modules/kubernetes/widget_test.go b/modules/kubernetes/widget_test.go index 6fb2c973..274bf2e2 100644 --- a/modules/kubernetes/widget_test.go +++ b/modules/kubernetes/widget_test.go @@ -11,6 +11,7 @@ func Test_generateTitle(t *testing.T) { title string namespaces []string } + testCases := []struct { name string fields fields @@ -46,6 +47,7 @@ func Test_generateTitle(t *testing.T) { want: "Test Explicit Title", }, } + for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { widget := &Widget{ diff --git a/modules/rollbar/client.go b/modules/rollbar/client.go index bd8bf771..332e03f9 100644 --- a/modules/rollbar/client.go +++ b/modules/rollbar/client.go @@ -18,7 +18,7 @@ func CurrentActiveItems(accessToken, assignedToName string, activeOnly bool) (*A return items, err } - err = utils.ParseJson(&items, resp.Body) + err = utils.ParseJSON(&items, resp.Body) return items, nil } diff --git a/modules/subreddit/api.go b/modules/subreddit/api.go index 506a84cb..73fec593 100644 --- a/modules/subreddit/api.go +++ b/modules/subreddit/api.go @@ -33,7 +33,7 @@ func GetLinks(subreddit string, sortMode string, topTimePeriod string) ([]Link, return nil, fmt.Errorf(resp.Status) } var m RedditDocument - err = utils.ParseJson(&m, resp.Body) + err = utils.ParseJSON(&m, resp.Body) if err != nil { return nil, err diff --git a/modules/travisci/client.go b/modules/travisci/client.go index a7ad3eb3..de696992 100644 --- a/modules/travisci/client.go +++ b/modules/travisci/client.go @@ -26,7 +26,7 @@ func BuildsFor(settings *Settings) (*Builds, error) { return builds, err } - err = utils.ParseJson(&builds, resp.Body) + err = utils.ParseJSON(&builds, resp.Body) if err != nil { return builds, err } diff --git a/utils/conversions_test.go b/utils/conversions_test.go index ab27a76a..12af7f2c 100644 --- a/utils/conversions_test.go +++ b/utils/conversions_test.go @@ -3,7 +3,7 @@ package utils import ( "testing" - . "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func Test_MapToStrs(t *testing.T) { @@ -18,7 +18,7 @@ func Test_MapToStrs(t *testing.T) { source[val] = val } - Equal(t, expected, MapToStrs(source)) + assert.Equal(t, expected, MapToStrs(source)) } func Test_ToInts(t *testing.T) { @@ -29,16 +29,23 @@ func Test_ToInts(t *testing.T) { source[idx] = val } - Equal(t, expected, ToInts(source)) + assert.Equal(t, expected, ToInts(source)) } func Test_ToStrs(t *testing.T) { - expected := []string{"cat", "dog", "rat"} + expectedInts := []int{1, 2, 3} + expectedStrs := []string{"1", "2", "3"} - source := make([]interface{}, len(expected)) - for idx, val := range expected { - source[idx] = val + fromInts := make([]interface{}, 3) + for idx, val := range expectedInts { + fromInts[idx] = val } - Equal(t, expected, ToStrs(source)) + fromStrs := make([]interface{}, 3) + for idx, val := range expectedStrs { + fromStrs[idx] = val + } + + assert.Equal(t, expectedStrs, ToStrs(fromInts)) + assert.Equal(t, expectedStrs, ToStrs(fromStrs)) } diff --git a/utils/homedir.go b/utils/homedir.go index 5a5d0fd2..b22e7055 100644 --- a/utils/homedir.go +++ b/utils/homedir.go @@ -26,16 +26,10 @@ func ExpandHomeDir(path string) (string, error) { return "", errors.New("cannot expand user-specific home dir") } - dir, err := Home() + dir, err := os.UserHomeDir() if err != nil { return "", err } return filepath.Join(dir, path[1:]), nil } - -// Home returns the home directory for the executing user. -// An error is returned if a home directory cannot be detected. -func Home() (string, error) { - return os.UserHomeDir() -} diff --git a/utils/homedir_test.go b/utils/homedir_test.go new file mode 100644 index 00000000..afd36327 --- /dev/null +++ b/utils/homedir_test.go @@ -0,0 +1,52 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_ExpandHomeDir(t *testing.T) { + tests := []struct { + name string + path string + expectedStart string + expectedContains string + expectedError error + }{ + { + name: "with empty path", + path: "", + expectedStart: "", + expectedContains: "", + expectedError: nil, + }, + { + name: "with relative path", + path: "~/test", + expectedStart: "/", + expectedContains: "/test", + expectedError: nil, + }, + { + name: "with absolute path", + path: "/Users/test", + expectedStart: "/", + expectedContains: "/test", + expectedError: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual, err := ExpandHomeDir(tt.path) + + if len(tt.path) > 0 { + assert.Equal(t, tt.expectedStart, string(actual[0])) + } + + assert.Contains(t, actual, tt.expectedContains) + assert.Equal(t, tt.expectedError, err) + }) + } +} diff --git a/utils/text_test.go b/utils/text_test.go index 86e4f77b..23d878b3 100644 --- a/utils/text_test.go +++ b/utils/text_test.go @@ -3,18 +3,26 @@ package utils import ( "testing" - . "github.com/stretchr/testify/assert" + "github.com/rivo/tview" + "github.com/stretchr/testify/assert" ) func Test_CenterText(t *testing.T) { - Equal(t, "cat", CenterText("cat", -9)) - Equal(t, "cat", CenterText("cat", 0)) - Equal(t, " cat ", CenterText("cat", 9)) + assert.Equal(t, "cat", CenterText("cat", -9)) + assert.Equal(t, "cat", CenterText("cat", 0)) + assert.Equal(t, " cat ", CenterText("cat", 9)) +} + +func Test_HighlightableHelper(t *testing.T) { + view := tview.NewTextView() + actual := HighlightableHelper(view, "cats", 0, 5) + + assert.Equal(t, "[\"0\"][\"\"]cats [\"\"]\n", actual) } func Test_RowPadding(t *testing.T) { - Equal(t, "", RowPadding(0, 0)) - Equal(t, "", RowPadding(5, 2)) - Equal(t, " ", RowPadding(1, 2)) - Equal(t, " ", RowPadding(0, 5)) + assert.Equal(t, "", RowPadding(0, 0)) + assert.Equal(t, "", RowPadding(5, 2)) + assert.Equal(t, " ", RowPadding(1, 2)) + assert.Equal(t, " ", RowPadding(0, 5)) } diff --git a/utils/utils.go b/utils/utils.go index 29831a65..e40ec917 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -121,8 +121,8 @@ func ReadFileBytes(filePath string) ([]byte, error) { return fileData, nil } -// ParseJson is a standard JSON reader from text -func ParseJson(obj interface{}, text io.Reader) error { +// ParseJSON is a standard JSON reader from text +func ParseJSON(obj interface{}, text io.Reader) error { d := json.NewDecoder(text) return d.Decode(obj) } @@ -139,10 +139,10 @@ func CalculateDimensions(moduleConfig, globalConfig *config.Config) (int, int) { rows := ToInts(globalConfig.UList("wtf.grid.rows")) // Make sure the values are in bounds - left = clamp(left, 0, len(cols)-1) - top = clamp(top, 0, len(rows)-1) - width = clamp(width, 0, len(cols)-left) - height = clamp(height, 0, len(rows)-top) + left = Clamp(left, 0, len(cols)-1) + top = Clamp(top, 0, len(rows)-1) + width = Clamp(width, 0, len(cols)-left) + height = Clamp(height, 0, len(rows)-top) // Start with the border subtracted and add all the spanned rows and cols w, h := -2, -2 @@ -154,20 +154,35 @@ func CalculateDimensions(moduleConfig, globalConfig *config.Config) (int, int) { } // The usable space may be empty - w = max(w, 0) - h = max(h, 0) + w = MaxInt(w, 0) + h = MaxInt(h, 0) return w, h } -func max(a, b int) int { - if a > b { - return a +// MaxInt returns the larger of x or y +// +// Examples: +// +// MaxInt(3, 2) => 3 +// MaxInt(2, 3) => 3 +// +func MaxInt(x, y int) int { + if x > y { + return x } - return b + return y } -func clamp(x, a, b int) int { +// Clamp restricts values to a minimum and maximum value +// +// Examples: +// +// clamp(6, 3, 8) => 4 +// clamp(1, 3, 8) => 3 +// clamp(9, 3, 8) => 8 +// +func Clamp(x, a, b int) int { if a > x { return a } diff --git a/utils/utils_test.go b/utils/utils_test.go index e396cc4e..2534b445 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -5,7 +5,7 @@ import ( "reflect" "testing" - . "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) func Test_DoesNotInclude(t *testing.T) { @@ -74,7 +74,8 @@ func Test_FindMatch(t *testing.T) { expected := [][]string([][]string{[]string{"SSID: 7E5B5C", "7E5B5C"}}) result = FindMatch(`s*SSID: (.+)s*`, "SSID: 7E5B5C") - Equal(t, expected, result) + + assert.Equal(t, expected, result) } func Test_Includes(t *testing.T) {