Merge branch 'master' into added-dutch-translations

This commit is contained in:
Mark Kopenga 2019-07-01 14:41:58 +02:00 committed by GitHub
commit 0b3eedc2e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 610 additions and 1140 deletions

5
.gitignore vendored
View file

@ -1,3 +1,4 @@
lazydocker*
TODO.md
Lazydocker.code-workspace
Lazydocker.code-workspace
.vscode

View file

@ -2,7 +2,7 @@
<img src="https://user-images.githubusercontent.com/8456633/59972109-8e9c8480-95cc-11e9-8350-38f7f86ba76d.png">
</p>
A simple terminal UI for docker and docker-compose, written in Go with the [gocui](https://github.com/jroimartin/gocui 'gocui') library.
A simple terminal UI for both docker and docker-compose, written in Go with the [gocui](https://github.com/jroimartin/gocui 'gocui') library.
[![CircleCI](https://circleci.com/gh/jesseduffield/lazydocker.svg?style=svg)](https://circleci.com/gh/jesseduffield/lazydocker) [![Go Report Card](https://goreportcard.com/badge/github.com/jesseduffield/lazydocker)](https://goreportcard.com/report/github.com/jesseduffield/lazydocker) [![GolangCI](https://golangci.com/badges/github.com/jesseduffield/lazydocker.svg)](https://golangci.com) [![GoDoc](https://godoc.org/github.com/jesseduffield/lazydocker?status.svg)](http://godoc.org/github.com/jesseduffield/lazydocker) [![GitHub tag](https://img.shields.io/github/tag/jesseduffield/lazydocker.svg)]()
@ -80,7 +80,7 @@ everything is one keypress away (or one click away! Mouse support FTW):
There is still a lot of work to go! Please check out the [contributing guide](CONTRIBUTING.md).
For contributor discussion about things not better discussed here in the repo, join the slack channel
[![Slack](/docs/resources/slack_rgb.png)](https://join.slack.com/t/lazydocker/shared_invite/enQtNDE3MjIwNTYyMDA0LTM3Yjk3NzdiYzhhNTA1YjM4Y2M4MWNmNDBkOTI0YTE4YjQ1ZmI2YWRhZTgwNjg2YzhhYjg3NDBlMmQyMTI5N2M)
[![Slack](/docs/resources/slack_rgb.png)](https://join.slack.com/t/lazydocker/shared_invite/enQtNjgwMjc0Njk3MzgwLTM0NThlMTZiZmNkNWJkY2VlYWYwZmY1NWYyYWViZmE0ZTcxMWZjMTFjNTU1ZTEwMDBiNWIxZTIxYzkwNDgyY2M)
## Donate

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
# Lazydocker menu
## Status
<pre>
<kbd>e</kbd>: bearbeite lazydocker Konfiguration
<kbd>o</kbd>: öffne lazydocker Konfiguration
<kbd>[</kbd>: vorheriges Tab
<kbd>]</kbd>: nächstes tab
<kbd>m</kbd>: zeige Protokoll
<kbd>enter</kbd>: fokusieren aufs Hauptpanel
</pre>
## Containers
<pre>
<kbd>[</kbd>: vorheriges Tab
<kbd>]</kbd>: nächstes Tab
<kbd>d</kbd>: entfernen
<kbd>s</kbd>: anhalten
<kbd>r</kbd>: neustarten
<kbd>a</kbd>: anbinden
<kbd>D</kbd>: entferne verlassene Container
<kbd>m</kbd>: zeige Protokolle
<kbd>c</kbd>: führe vordefinierten benutzerdefinierten Befehl aus
<kbd>enter</kbd>: fokusieren aufs Hauptpanel
</pre>
## Services
<pre>
<kbd>d</kbd>: entferne Container
<kbd>s</kbd>: anhalten
<kbd>r</kbd>: neustarten
<kbd>a</kbd>: anbinden
<kbd>m</kbd>: zeige Protokolle
<kbd>[</kbd>: vorheriges Tab
<kbd>]</kbd>: nächstes Tab
<kbd>R</kbd>: zeige Neustartoptionen
<kbd>c</kbd>: führe vordefinierten benutzerdefinierten Befehl aus
<kbd>enter</kbd>: fokusieren aufs Hauptpanel
</pre>
## Images
<pre>
<kbd>[</kbd>: vorheriges Tab
<kbd>]</kbd>: nächstes Tab
<kbd>d</kbd>: entferne Image
<kbd>D</kbd>: entferne unbenutzte Images
<kbd>enter</kbd>: fokusieren aufs Hauptpanel
</pre>
## Volumes
<pre>
<kbd>[</kbd>: vorheriges Tab
<kbd>]</kbd>: nächstes Tab
<kbd>d</kbd>: entferne Volumen
<kbd>D</kbd>: entferne unbenutzte Volumen
<kbd>enter</kbd>: fokusieren aufs Hauptpanel
</pre>
## Main
<pre>
<kbd>esc</kbd>: zurück
</pre>

View file

@ -56,17 +56,6 @@ func (app *App) Run() error {
return err
}
// Close closes any resources
func (app *App) Close() error {
for _, closer := range app.closers {
err := closer.Close()
if err != nil {
return err
}
}
return nil
}
type errorMapping struct {
originalError string
newError string

View file

@ -1,100 +0,0 @@
// +build !windows
package commands
import (
"bufio"
"bytes"
"os"
"strings"
"unicode/utf8"
"github.com/go-errors/errors"
"github.com/jesseduffield/pty"
"github.com/mgutz/str"
)
// RunCommandWithOutputLiveWrapper runs a command and return every word that gets written in stdout
// Output is a function that executes by every word that gets read by bufio
// As return of output you need to give a string that will be written to stdin
// NOTE: If the return data is empty it won't written anything to stdin
func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error {
splitCmd := str.ToArgv(command)
cmd := c.command(splitCmd[0], splitCmd[1:]...)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8")
var stderr bytes.Buffer
cmd.Stderr = &stderr
ptmx, err := pty.Start(cmd)
if err != nil {
return err
}
go func() {
scanner := bufio.NewScanner(ptmx)
scanner.Split(scanWordsWithNewLines)
for scanner.Scan() {
toOutput := strings.Trim(scanner.Text(), " ")
_, _ = ptmx.WriteString(output(toOutput))
}
}()
err = cmd.Wait()
ptmx.Close()
if err != nil {
return errors.New(stderr.String())
}
return nil
}
// scanWordsWithNewLines is a copy of bufio.ScanWords but this also captures new lines
// For specific comments about this function take a look at: bufio.ScanWords
func scanWordsWithNewLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
start := 0
for width := 0; start < len(data); start += width {
var r rune
r, width = utf8.DecodeRune(data[start:])
if !isSpace(r) {
break
}
}
for width, i := 0, start; i < len(data); i += width {
var r rune
r, width = utf8.DecodeRune(data[i:])
if isSpace(r) {
return i + width, data[start:i], nil
}
}
if atEOF && len(data) > start {
return len(data), data[start:], nil
}
return start, nil, nil
}
// isSpace is also copied from the bufio package and has been modified to also captures new lines
// For specific comments about this function take a look at: bufio.isSpace
func isSpace(r rune) bool {
if r <= '\u00FF' {
switch r {
case ' ', '\t', '\v', '\f':
return true
case '\u0085', '\u00A0':
return true
}
return false
}
if '\u2000' <= r && r <= '\u200a' {
return true
}
switch r {
case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
return true
}
return false
}

View file

@ -1,9 +0,0 @@
// +build windows
package commands
// RunCommandWithOutputLiveWrapper runs a command live but because of windows compatibility this command can't be ran there
// TODO: Remove this hack and replace it with a proper way to run commands live on windows
func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error {
return c.RunCommand(command)
}

View file

@ -68,7 +68,7 @@ func (l *Layer) GetDisplayStrings(isFocused bool) []string {
createdBy = utils.ColoredString(split[0], color.FgYellow) + " " + strings.Join(split[1:], " ")
}
createdBy = strings.ReplaceAll(createdBy, "\t", " ")
createdBy = strings.Replace(createdBy, "\t", " ", -1)
size := utils.FormatBinaryBytes(int(l.Size))
sizeColor := color.FgWhite

View file

@ -6,7 +6,6 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"sync"
"syscall"
@ -87,36 +86,6 @@ func (c *OSCommand) ExecutableFromString(commandStr string) *exec.Cmd {
return c.command(splitCmd[0], splitCmd[1:]...)
}
// RunCommandWithOutputLive runs RunCommandWithOutputLiveWrapper
func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) string) error {
return RunCommandWithOutputLiveWrapper(c, command, output)
}
// DetectUnamePass detect a username / password question in a command
// ask is a function that gets executen when this function detect you need to fillin a password
// The ask argument will be "username" or "password" and expects the user's password or username back
func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) error {
ttyText := ""
errMessage := c.RunCommandWithOutputLive(command, func(word string) string {
ttyText = ttyText + " " + word
prompts := map[string]string{
"password": `Password\s*for\s*'.+':`,
"username": `Username\s*for\s*'.+':`,
}
for askFor, pattern := range prompts {
if match, _ := regexp.MatchString(pattern, ttyText); match {
ttyText = ""
return ask(askFor)
}
}
return ""
})
return errMessage
}
// RunCommand runs a command and just returns the error
func (c *OSCommand) RunCommand(command string) error {
_, err := c.RunCommandWithOutput(command)

View file

@ -98,10 +98,6 @@ func (gui *Gui) onNewPopupPanel() {
}
}
func (gui *Gui) createLoaderPanel(g *gocui.Gui, currentView *gocui.View, prompt string) error {
return gui.createPopupPanel(g, currentView, "", prompt, true, nil, nil)
}
// it is very important that within this function we never include the original prompt in any error messages, because it may contain e.g. a user password
func (gui *Gui) createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
return gui.createPopupPanel(g, currentView, title, prompt, false, handleConfirm, handleClose)
@ -148,10 +144,6 @@ func (gui *Gui) setKeyBindings(g *gocui.Gui, handleConfirm, handleClose func(*go
return nil
}
func (gui *Gui) createMessagePanel(g *gocui.Gui, currentView *gocui.View, title, prompt string) error {
return gui.createPopupPanel(g, currentView, title, prompt, false, nil, nil)
}
// createSpecificErrorPanel allows you to create an error popup, specifying the
// view to be focused when the user closes the popup, and a boolean specifying
// whether we will log the error. If the message may include a user password,

View file

@ -177,10 +177,6 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
return gui, nil
}
func (gui *Gui) handleRefresh(g *gocui.Gui, v *gocui.View) error {
return gui.refreshSidePanels(g)
}
func max(a, b int) int {
if a > b {
return a

View file

@ -91,6 +91,10 @@ func (gui *Gui) handleExitMain(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) handleMainClick(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
currentView := gui.g.CurrentView()
v.ParentView = currentView

View file

@ -144,11 +144,6 @@ func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
return gui.newLineFocused(newView)
}
func (gui *Gui) resetOrigin(v *gocui.View) error {
_ = v.SetCursor(0, 0)
return v.SetOrigin(0, 0)
}
// if the cursor down past the last item, move it to the last line
func (gui *Gui) focusPoint(selectedX int, selectedY int, lineCount int, v *gocui.View) error {
if selectedY < 0 || selectedY > lineCount {
@ -317,28 +312,6 @@ func (gui *Gui) changeSelectedLine(line *int, total int, up bool) {
}
}
func (gui *Gui) refreshSelectedLine(line *int, total int) {
if *line == -1 && total > 0 {
*line = 0
} else if total-1 < *line {
*line = total - 1
}
}
func (gui *Gui) renderListPanel(v *gocui.View, items interface{}) error {
gui.g.Update(func(g *gocui.Gui) error {
isFocused := gui.g.CurrentView().Name() == v.Name()
list, err := utils.RenderList(items, utils.IsFocused(isFocused))
if err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
v.Clear()
fmt.Fprint(v, list)
return nil
})
return nil
}
func (gui *Gui) renderPanelOptions() error {
currentView := gui.g.CurrentView()
switch currentView.Name() {
@ -348,11 +321,6 @@ func (gui *Gui) renderPanelOptions() error {
return gui.renderGlobalOptions()
}
func (gui *Gui) handleFocusView(g *gocui.Gui, v *gocui.View) error {
_, err := gui.g.SetCurrentView(v.Name())
return err
}
func (gui *Gui) isPopupPanel(viewName string) bool {
return viewName == "confirmation" || viewName == "menu"
}

90
pkg/i18n/german.go Normal file
View file

@ -0,0 +1,90 @@
package i18n
func germanSet() TranslationSet {
return TranslationSet{
PruningStatus: "zerstören",
RemovingStatus: "entfernen",
RestartingStatus: "neustarten",
StoppingStatus: "anhalten",
RunningCustomCommandStatus: "führt benutzerdefinierten Befehl aus",
RunningSubprocess: "Unterprozess ausführen",
NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement",
ErrorOccurred: "Es ist ein Fehler aufgetreten! Bitte erstelle ein Issue hier: https://github.com/jesseduffield/lazydocker/issues",
ConnectionFailed: "Verbindung zum Docker Client fehlgeschlagen. Du musst ggf. den Docker Client neustarten.",
UnattachableContainerError: "Der Container bietet keine Unterstützung für das Anbinden. Du musst den Dienst entweder mit der '-it' Flagge benutzen oder `stdin_open: true, tty: true` in der docker-compose.yml Datei setzen.",
CannotAttachStoppedContainerError: "Du kannst keinen angehaltenen Container anbinden. Du musst ihn erst starten (was du tun kannst, indem du 'r' drückst), (ja, ich bin zu faul um das zu automatisieren) (aber ist schon cool, dass ich so eine Konversation durch eine Fehlermeldung mit dir führen kann)",
CannotAccessDockerSocketError: "Kann nicht auf den Socket zugreifen: unix:///var/run/docker.sock\nFühre lazydocker als root aus oder lese https://docs.docker.com/install/linux/linux-postinstall/",
Donate: "Spenden",
Confirm: "Bestätigen",
Return: "zurück",
FocusMain: "fokusieren aufs Hauptpanel",
Navigate: "navigieren",
Execute: "ausführen",
Close: "schließen",
Menu: "Menü",
Scroll: "scrollen",
OpenConfig: "öffne lazydocker Konfiguration",
EditConfig: "bearbeite lazydocker Konfiguration",
Cancel: "abbrechen",
Remove: "entfernen",
ForceRemove: "Entfernen erzwingen",
RemoveWithVolumes: "entferne mit Volumen",
RemoveService: "entferne Container",
Stop: "anhalten",
Restart: "neustarten",
Rebuild: "neubauen",
Recreate: "neuerstellen",
PreviousContext: "vorheriges Tab",
NextContext: "nächstes Tab",
Attach: "anbinden",
ViewLogs: "zeige Protokolle",
RemoveImage: "entferne Image",
RemoveVolume: "entferne Volumen",
RemoveWithoutPrune: "entfernen, ohne die unmarkierten Eltern zu entfernen",
PruneContainers: "entferne verlassene Container",
PruneVolumes: "entferne unbenutzte Volumen",
PruneImages: "entferne unbenutzte Images",
ViewRestartOptions: "zeige Neustartoptionen",
RunCustomCommand: "führe vordefinierten benutzerdefinierten Befehl aus",
AnonymousReportingTitle: "Mache dabei mit lazydocker besser zu machen",
AnonymousReportingPrompt: "Möchtest du anonymiserte Datensammlung erlauben, um zu helfen lazydocker zu erbessern? (enter/esc)",
GlobalTitle: "Global",
MainTitle: "Haupt",
StatusTitle: "Status",
ServicesTitle: "Dienste",
ContainersTitle: "Container",
StandaloneContainersTitle: "Alleinstehende Container",
ImagesTitle: "Images",
VolumesTitle: "Volumen",
CustomCommandTitle: "Benutzerdefinierter Befehl",
ErrorTitle: "Fehler",
LogsTitle: "Protokoll",
ConfigTitle: "Konfiguration",
DockerComposeConfigTitle: "Docker-Compose Konfiguration",
TopTitle: "Top",
StatsTitle: "Statistiken",
CreditsTitle: "Über Uns",
ContainerConfigTitle: "Container Konfiguration",
NoContainers: "Keine Container",
NoContainer: "Kein Container",
NoImages: "Keine Images",
NoVolumes: "Keine Volumen",
ConfirmQuit: "Bist du dir sicher, dass du verlassen möchtest?",
MustForceToRemoveContainer: "Du kannst keinen Container entfernen, der noch ausgeführt wird außer du erzwingst es. Möchtest du es erzwingen?",
NotEnoughSpace: "Nicht genug Platz um die Panele darzustellen",
ConfirmPruneImages: "Bist du dir sicher, dass du alle unbenutzten Images entfernen möchtest?",
ConfirmPruneContainers: "Bist du dir sicher, dass du alle angehaltenen Container entfernen möchtes?",
ConfirmPruneVolumes: "Bist du dir sicher, dass du alle unbenutzen Volumen entfernen möchtest?",
StopService: "Bist du dir sicher, dass du den Dienst dieses Containers anhalten möchtest? (enter/esc)",
StopContainer: "Bist du dir sicher, dass du den Container anhalten möchtest?",
PressEnterToReturn: "Drücke Eingabe um zu lazydocker zurück zu kehren. (Diese Nachfrage kann in Deiner Konfiguration deaktiviert werden, indem du folgenden Wert setzt: `gui.returnImmediately: true`)",
}
}

View file

@ -34,6 +34,10 @@ func NewTranslationSet(log *logrus.Entry) *TranslationSet {
mergo.Merge(&set, dutchSet(), mergo.WithOverride)
}
if strings.HasPrefix(userLang, "de") {
mergo.Merge(&set, germanSet(), mergo.WithOverride)
}
return &set
}

View file

@ -1,45 +0,0 @@
package test
import (
"fmt"
"os/exec"
"strings"
"testing"
"github.com/mgutz/str"
"github.com/stretchr/testify/assert"
)
// CommandSwapper takes a command, verifies that it is what it's expected to be
// and then returns a replacement command that will actually be called by the os
type CommandSwapper struct {
Expect string
Replace string
}
// SwapCommand verifies the command is what we expected, and swaps it out for a different command
func (i *CommandSwapper) SwapCommand(t *testing.T, cmd string, args []string) *exec.Cmd {
splitCmd := str.ToArgv(i.Expect)
assert.EqualValues(t, splitCmd[0], cmd, fmt.Sprintf("received command: %s %s", cmd, strings.Join(args, " ")))
if len(splitCmd) > 1 {
assert.EqualValues(t, splitCmd[1:], args, fmt.Sprintf("received command: %s %s", cmd, strings.Join(args, " ")))
}
splitCmd = str.ToArgv(i.Replace)
return exec.Command(splitCmd[0], splitCmd[1:]...)
}
// CreateMockCommand creates a command function that will verify its receiving the right sequence of commands from lazydocker
func CreateMockCommand(t *testing.T, swappers []*CommandSwapper) func(cmd string, args ...string) *exec.Cmd {
commandIndex := 0
return func(cmd string, args ...string) *exec.Cmd {
var command *exec.Cmd
if commandIndex > len(swappers)-1 {
assert.Fail(t, fmt.Sprintf("too many commands run. This command was (%s %s)", cmd, strings.Join(args, " ")))
}
command = swappers[commandIndex].SwapCommand(t, cmd, args)
commandIndex++
return command
}
}

View file

@ -2,7 +2,6 @@ package utils
import (
"bytes"
"encoding/json"
"fmt"
"html/template"
"math"
@ -59,15 +58,6 @@ func ColoredStringDirect(str string, colour *color.Color) string {
return colour.SprintFunc()(fmt.Sprint(str))
}
// TrimTrailingNewline - Trims the trailing newline
// TODO: replace with `chomp` after refactor
func TrimTrailingNewline(str string) string {
if strings.HasSuffix(str, "\n") {
return str[:len(str)-1]
}
return str
}
// NormalizeLinefeeds - Removes all Windows and Mac style line feeds
func NormalizeLinefeeds(str string) string {
str = strings.Replace(str, "\r\n", "\n", -1)
@ -92,14 +82,6 @@ func ResolvePlaceholderString(str string, arguments map[string]string) string {
return str
}
// Min returns the minimum of two integers
func Min(x, y int) int {
if x < y {
return x
}
return y
}
// Max returns the maximum of two integers
func Max(x, y int) int {
if x > y {
@ -240,42 +222,6 @@ func getDisplayStringArrays(displayables []Displayable, isFocused bool) [][]stri
return stringArrays
}
// IncludesString if the list contains the string
func IncludesString(list []string, a string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
// NextIndex returns the index of the element that comes after the given number
func NextIndex(numbers []int, currentNumber int) int {
for index, number := range numbers {
if number > currentNumber {
return index
}
}
return 0
}
// PrevIndex returns the index that comes before the given number, cycling if we reach the end
func PrevIndex(numbers []int, currentNumber int) int {
end := len(numbers) - 1
for i := end; i >= 0; i -= 1 {
if numbers[i] < currentNumber {
return i
}
}
return end
}
func AsJson(i interface{}) string {
bytes, _ := json.MarshalIndent(i, "", " ")
return string(bytes)
}
func FormatBinaryBytes(b int) string {
n := float64(b)
units := []string{"B", "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}

View file

@ -62,29 +62,6 @@ func TestWithPadding(t *testing.T) {
}
}
// TestTrimTrailingNewline is a function.
func TestTrimTrailingNewline(t *testing.T) {
type scenario struct {
str string
expected string
}
scenarios := []scenario{
{
"hello world !\n",
"hello world !",
},
{
"hello world !",
"hello world !",
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, TrimTrailingNewline(s.str))
}
}
// TestNormalizeLinefeeds is a function.
func TestNormalizeLinefeeds(t *testing.T) {
type scenario struct {
@ -403,173 +380,3 @@ func TestGetPadWidths(t *testing.T) {
assert.EqualValues(t, s.expected, getPadWidths(s.stringArrays))
}
}
// TestMin is a function.
func TestMin(t *testing.T) {
type scenario struct {
a int
b int
expected int
}
scenarios := []scenario{
{
1,
1,
1,
},
{
1,
2,
1,
},
{
2,
1,
1,
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, Min(s.a, s.b))
}
}
// TestIncludesString is a function.
func TestIncludesString(t *testing.T) {
type scenario struct {
list []string
element string
expected bool
}
scenarios := []scenario{
{
[]string{"a", "b"},
"a",
true,
},
{
[]string{"a", "b"},
"c",
false,
},
{
[]string{"a", "b"},
"",
false,
},
{
[]string{""},
"",
true,
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, IncludesString(s.list, s.element))
}
}
func TestNextIndex(t *testing.T) {
type scenario struct {
testName string
list []int
element int
expected int
}
scenarios := []scenario{
{
// I'm not really fussed about how it behaves here
"no elements",
[]int{},
1,
0,
},
{
"one element",
[]int{1},
1,
0,
},
{
"two elements",
[]int{1, 2},
1,
1,
},
{
"two elements, giving second one",
[]int{1, 2},
2,
0,
},
{
"three elements, giving second one",
[]int{1, 2, 3},
2,
2,
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
assert.EqualValues(t, s.expected, NextIndex(s.list, s.element))
})
}
}
func TestPrevIndex(t *testing.T) {
type scenario struct {
testName string
list []int
element int
expected int
}
scenarios := []scenario{
{
// I'm not really fussed about how it behaves here
"no elements",
[]int{},
1,
-1,
},
{
"one element",
[]int{1},
1,
0,
},
{
"two elements",
[]int{1, 2},
1,
1,
},
{
"three elements, giving second one",
[]int{1, 2, 3},
2,
0,
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
assert.EqualValues(t, s.expected, PrevIndex(s.list, s.element))
})
}
}
func TestAsJson(t *testing.T) {
type myStruct struct {
a string
}
output := AsJson(&myStruct{a: "foo"})
// no idea why this is returning empty hashes but it's works in the app ¯\_(ツ)_/¯
assert.EqualValues(t, "{}", output)
}