Skip to content

Commit

Permalink
Add demo test variant
Browse files Browse the repository at this point in the history
We're piggybacking on our existing integration test framework to record  demos that we can include in our docs
  • Loading branch information
jesseduffield committed Jul 31, 2023
1 parent 71d2fd3 commit 0386a9d
Show file tree
Hide file tree
Showing 26 changed files with 478 additions and 10 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ test/results/**
oryxBuildBinary
__debug_bin

.worktrees
.worktrees
demo.yml
2 changes: 2 additions & 0 deletions demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This directory contains stuff for recording lazygit demos.

109 changes: 109 additions & 0 deletions demo/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Specify a command to be executed
# like `/bin/bash -l`, `ls`, or any other commands
# the default is bash for Linux
# or powershell.exe for Windows
command: echo "YOU NEED TO SPECIFY YOUR OWN COMMAND WITH THE -d ARG"

# Specify the current working directory path
# the default is the current working directory path
cwd: null

# Export additional ENV variables
env:
recording: true

# Explicitly set the number of columns
# or use `auto` to take the current
# number of columns of your shell
cols: 120 # 100

# Explicitly set the number of rows
# or use `auto` to take the current
# number of rows of your shell
rows: 35 # 30

# Amount of times to repeat GIF
# If value is -1, play once
# If value is 0, loop indefinitely
# If value is a positive number, loop n times
repeat: 0

# Quality
# 1 - 100
# Higher quality seems to make no difference, but running it through
# gifsicle ends up with a much better compressed version.
quality: 100

# Delay between frames in ms
# If the value is `auto` use the actual recording delays
frameDelay: auto

# Maximum delay between frames in ms
# Ignored if the `frameDelay` isn't set to `auto`
# Set to `auto` to prevent limiting the max idle time
maxIdleTime: 2000

# The surrounding frame box
# The `type` can be null, window, floating, or solid`
# To hide the title use the value null
# Don't forget to add a backgroundColor style with a null as type
frameBox:
type: floating
title: Lazygit
style:
border: 0px black solid
backgroundColor: "#1d1d1d"
margin: -5px

# Add a watermark image to the rendered gif
# You need to specify an absolute path for
# the image on your machine or a URL, and you can also
# add your own CSS styles
watermark:
imagePath: null
style:
position: absolute
right: 15px
bottom: 15px
width: 100px
opacity: 0.9

# Cursor style can be one of
# `block`, `underline`, or `bar`
cursorStyle: block

# Font family
# You can use any font that is installed on your machine
# in CSS-like syntax
fontFamily: "DejaVuSansMono Nerd Font"

# The size of the font
fontSize: 8

# The height of lines
lineHeight: 1

# The spacing between letters
letterSpacing: 0

# Theme
theme:
background: "transparent"
foreground: "#dddad6"
cursor: "#c7c7c7"
black: "#7a7a7a"
red: "#fc4384"
green: "#b3e33b"
yellow: "#ffa727"
blue: "#102895"
magenta: "#c930c7"
cyan: "#00c5c7"
white: "#c7c7c7"
brightBlack: "#676767"
brightRed: "#ff7fac"
brightGreen: "#c8ed71"
brightYellow: "#ebdf86"
brightBlue: "#6871ff"
brightMagenta: "#ff76ff"
brightCyan: "#5ffdff"
brightWhite: "#fffefe"
33 changes: 33 additions & 0 deletions demo/record_demo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/sh

TEST=$1

set -e

if [ -z "$TEST" ]
then
echo "Usage: $0 <test>"
exit 1
fi

if ! command -v terminalizer &> /dev/null
then
echo "terminalizer could not be found"
echo "Install it with: npm install -g terminalizer"
exit 1
fi

if ! command -v "gifsicle" &> /dev/null
then
echo "gifsicle could not be found"
echo "Install it with: npm install -g gifsicle"
exit 1
fi

go generate pkg/integration/tests/tests.go

terminalizer -c demo/config.yml record --skip-sharing -d "go run cmd/integration_test/main.go cli --slow $TEST" demo
terminalizer render demo -o demo/demo.gif
gifsicle --colors 256 --use-col=web -O3 < demo/demo.gif > demo/demo-compressed.gif

echo "Demo recorded to demo/demo-compressed.gif"
5 changes: 4 additions & 1 deletion pkg/gui/controllers/helpers/refresh_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,10 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) error {

wg := sync.WaitGroup{}
refresh := func(name string, f func()) {
if options.Mode == types.ASYNC {
// if we're in a demo we don't want any async refreshes because
// everything happens fast and it's better to have everything update
// in the one frame
if !self.c.InDemo() && options.Mode == types.ASYNC {
self.c.OnWorker(func(t gocui.Task) {
f()
})
Expand Down
10 changes: 8 additions & 2 deletions pkg/gui/controllers/helpers/window_arrangement_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,18 @@ func (self *WindowArrangementHelper) infoSectionChildren(informationStr string,
appStatusBox.Weight = 1
} else {
optionsBox.Weight = 1
appStatusBox.Size = runewidth.StringWidth(INFO_SECTION_PADDING) + runewidth.StringWidth(appStatus)
if self.c.InDemo() {
// app status appears very briefly in demos and dislodges the caption,
// so better not to show it at all
appStatusBox.Size = 0
} else {
appStatusBox.Size = runewidth.StringWidth(INFO_SECTION_PADDING) + runewidth.StringWidth(appStatus)
}
}

result := []*boxlayout.Box{appStatusBox, optionsBox}

if self.c.UserConfig.Gui.ShowBottomLine || self.modeHelper.IsAnyModeActive() {
if (!self.c.InDemo() && self.c.UserConfig.Gui.ShowBottomLine) || self.modeHelper.IsAnyModeActive() {
result = append(result, &boxlayout.Box{
Window: "information",
// unlike appStatus, informationStr has various colors so we need to decolorise before taking the length
Expand Down
20 changes: 20 additions & 0 deletions pkg/gui/global_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strings"

"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
Expand Down Expand Up @@ -137,3 +138,22 @@ func (gui *Gui) handleCopySelectedSideContextItemToClipboard() error {

return nil
}

func (gui *Gui) setCaption(caption string) {
gui.Views.Options.FgColor = gocui.ColorWhite
gui.Views.Options.FgColor |= gocui.AttrBold
gui.Views.Options.SetContent(captionPrefix + " " + style.FgCyan.SetBold().Sprint(caption))
gui.c.Render()
}

var captionPrefix = ""

func (gui *Gui) setCaptionPrefix(prefix string) {
gui.Views.Options.FgColor = gocui.ColorWhite
gui.Views.Options.FgColor |= gocui.AttrBold

captionPrefix = prefix

gui.Views.Options.SetContent(prefix)
gui.c.Render()
}
1 change: 1 addition & 0 deletions pkg/gui/gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ func NewGui(
func(message string) { gui.helpers.AppStatus.Toast(message) },
func() string { return gui.Views.Confirmation.TextArea.GetContent() },
func(f func(gocui.Task)) { gui.c.OnWorker(f) },
func() bool { return gui.c.InDemo() },
)

guiCommon := &guiCommon{gui: gui, IPopupHandler: gui.PopupHandler}
Expand Down
4 changes: 4 additions & 0 deletions pkg/gui/gui_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,7 @@ func (self *guiCommon) AfterLayout(f func() error) {
self.gui.c.Log.Error("afterLayoutFuncs channel is full, skipping function")
}
}

func (self *guiCommon) InDemo() bool {
return self.gui.integrationTest != nil && self.gui.integrationTest.IsDemo()
}
16 changes: 15 additions & 1 deletion pkg/gui/gui_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ func (self *GuiDriver) PressKey(keyStr string) {
0,
)

// wait until lazygit is idle (i.e. all processing is done) before continuing
self.waitTillIdle()
}

// wait until lazygit is idle (i.e. all processing is done) before continuing
func (self *GuiDriver) waitTillIdle() {
<-self.isIdleChan
}

Expand Down Expand Up @@ -111,3 +115,13 @@ func (self *GuiDriver) View(viewName string) *gocui.View {
}
return view
}

func (self *GuiDriver) SetCaption(caption string) {
self.gui.setCaption(caption)
self.waitTillIdle()
}

func (self *GuiDriver) SetCaptionPrefix(prefix string) {
self.gui.setCaptionPrefix(prefix)
self.waitTillIdle()
}
4 changes: 4 additions & 0 deletions pkg/gui/options_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ type OptionsMapMgr struct {
}

func (gui *Gui) renderContextOptionsMap(c types.Context) {
// In demos, we render our own content to this view
if gui.integrationTest != nil && gui.integrationTest.IsDemo() {
return
}
mgr := OptionsMapMgr{c: gui.c}
mgr.renderContextOptionsMap(c)
}
Expand Down
9 changes: 9 additions & 0 deletions pkg/gui/popup/popup_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package popup
import (
"context"
"strings"
"time"

"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/common"
Expand All @@ -25,6 +26,7 @@ type PopupHandler struct {
toastFn func(message string)
getPromptInputFn func() string
onWorker func(func(gocui.Task))
inDemo func() bool
}

var _ types.IPopupHandler = &PopupHandler{}
Expand All @@ -40,6 +42,7 @@ func NewPopupHandler(
toastFn func(message string),
getPromptInputFn func() string,
onWorker func(func(gocui.Task)),
inDemo func() bool,
) *PopupHandler {
return &PopupHandler{
Common: common,
Expand All @@ -53,6 +56,7 @@ func NewPopupHandler(
toastFn: toastFn,
getPromptInputFn: getPromptInputFn,
onWorker: onWorker,
inDemo: inDemo,
}
}

Expand Down Expand Up @@ -144,6 +148,11 @@ func (self *PopupHandler) WithLoaderPanel(message string, f func(gocui.Task) err
}

self.onWorker(func(task gocui.Task) {
// emulating a delay due to network latency
if self.inDemo() {
time.Sleep(500 * time.Millisecond)
}

if err := f(task); err != nil {
self.Log.Error(err)
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/gui/types/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ type IGuiCommon interface {

// hopefully we can remove this once we've moved all our keybinding stuff out of the gui god struct.
GetInitialKeybindingsWithCustomCommands() ([]*Binding, []*gocui.ViewMouseBinding)

// Returns true if we're in a demo recording/playback
InDemo() bool
}

type IModeMgr interface {
Expand Down
6 changes: 6 additions & 0 deletions pkg/integration/clients/go_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ func TestIntegration(t *testing.T) {
return
}

// not running demoes right now. Arguably we should, but we'd need to
// strip away any artificial lag they use.
if test.IsDemo() {
return
}

t.Run(test.Name(), func(t *testing.T) {
t.Parallel()
err := f()
Expand Down
2 changes: 1 addition & 1 deletion pkg/integration/clients/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (

// This program lets you run integration tests from a TUI. See pkg/integration/README.md for more info.

var SLOW_KEY_PRESS_DELAY = 300
var SLOW_KEY_PRESS_DELAY = 600

func RunTUI() {
rootDir := utils.GetLazyRootDirectory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func (self *CommitDescriptionPanelDriver) SwitchToSummary() *CommitMessagePanelD
}

func (self *CommitDescriptionPanelDriver) AddNewline() *CommitDescriptionPanelDriver {
self.t.press(self.t.keys.Universal.Confirm)
self.t.pressFast(self.t.keys.Universal.Confirm)
return self
}

Expand Down
Loading

0 comments on commit 0386a9d

Please sign in to comment.