mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-25 08:31:03 +00:00
Merge branch 'master' into added-dutch-translations
This commit is contained in:
commit
0b3eedc2e6
18 changed files with 610 additions and 1140 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
lazydocker*
|
||||
TODO.md
|
||||
Lazydocker.code-workspace
|
||||
Lazydocker.code-workspace
|
||||
.vscode
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
[](https://circleci.com/gh/jesseduffield/lazydocker) [](https://goreportcard.com/report/github.com/jesseduffield/lazydocker) [](https://golangci.com) [](http://godoc.org/github.com/jesseduffield/lazydocker) []()
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
[](https://join.slack.com/t/lazydocker/shared_invite/enQtNDE3MjIwNTYyMDA0LTM3Yjk3NzdiYzhhNTA1YjM4Y2M4MWNmNDBkOTI0YTE4YjQ1ZmI2YWRhZTgwNjg2YzhhYjg3NDBlMmQyMTI5N2M)
|
||||
[](https://join.slack.com/t/lazydocker/shared_invite/enQtNjgwMjc0Njk3MzgwLTM0NThlMTZiZmNkNWJkY2VlYWYwZmY1NWYyYWViZmE0ZTcxMWZjMTFjNTU1ZTEwMDBiNWIxZTIxYzkwNDgyY2M)
|
||||
|
||||
## Donate
|
||||
|
||||
|
|
|
|||
1086
coverage.txt
1086
coverage.txt
File diff suppressed because it is too large
Load diff
68
docs/keybindings/Keybindings_de.md
Normal file
68
docs/keybindings/Keybindings_de.md
Normal 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>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
90
pkg/i18n/german.go
Normal 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`)",
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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"}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue