mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-07-01 00:54:58 +02:00
ensuring you can't accidentally forget to add a test to the tests list
This commit is contained in:
@ -37,7 +37,7 @@ If you find yourself doing something frequently in a test, consider making it a
|
|||||||
|
|
||||||
There are three ways to invoke a test:
|
There are three ways to invoke a test:
|
||||||
|
|
||||||
1. go run cmd/integration_test/main.go cli [<testname>...]
|
1. go run cmd/integration_test/main.go cli [<testname or testpath>...]
|
||||||
2. go run cmd/integration_test/main.go tui
|
2. go run cmd/integration_test/main.go tui
|
||||||
3. go test pkg/integration/clients/go_test.go
|
3. go test pkg/integration/clients/go_test.go
|
||||||
|
|
||||||
|
@ -44,10 +44,11 @@ func runAndPrintError(test *components.IntegrationTest, f func() error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getTestsToRun(testNames []string) []*components.IntegrationTest {
|
func getTestsToRun(testNames []string) []*components.IntegrationTest {
|
||||||
|
allIntegrationTests := tests.GetTests()
|
||||||
var testsToRun []*components.IntegrationTest
|
var testsToRun []*components.IntegrationTest
|
||||||
|
|
||||||
if len(testNames) == 0 {
|
if len(testNames) == 0 {
|
||||||
return tests.Tests
|
return allIntegrationTests
|
||||||
}
|
}
|
||||||
|
|
||||||
testNames = slices.Map(testNames, func(name string) string {
|
testNames = slices.Map(testNames, func(name string) string {
|
||||||
@ -61,7 +62,7 @@ func getTestsToRun(testNames []string) []*components.IntegrationTest {
|
|||||||
outer:
|
outer:
|
||||||
for _, testName := range testNames {
|
for _, testName := range testNames {
|
||||||
// check if our given test name actually exists
|
// check if our given test name actually exists
|
||||||
for _, test := range tests.Tests {
|
for _, test := range allIntegrationTests {
|
||||||
if test.Name() == testName {
|
if test.Name() == testName {
|
||||||
testsToRun = append(testsToRun, test)
|
testsToRun = append(testsToRun, test)
|
||||||
continue outer
|
continue outer
|
||||||
|
@ -29,7 +29,7 @@ func TestIntegration(t *testing.T) {
|
|||||||
testNumber := 0
|
testNumber := 0
|
||||||
|
|
||||||
err := components.RunTests(
|
err := components.RunTests(
|
||||||
tests.Tests,
|
tests.GetTests(),
|
||||||
t.Logf,
|
t.Logf,
|
||||||
runCmdHeadless,
|
runCmdHeadless,
|
||||||
func(test *components.IntegrationTest, f func() error) {
|
func(test *components.IntegrationTest, f func() error) {
|
||||||
|
@ -52,7 +52,8 @@ func getIntegrationTest() integrationTypes.IntegrationTest {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, candidateTest := range tests.Tests {
|
allTests := tests.GetTests()
|
||||||
|
for _, candidateTest := range allTests {
|
||||||
if candidateTest.Name() == integrationTestName {
|
if candidateTest.Name() == integrationTestName {
|
||||||
return candidateTest
|
return candidateTest
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ func RunTUI() {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
app.filteredTests = tests.Tests
|
app.filteredTests = app.allTests
|
||||||
app.renderTests()
|
app.renderTests()
|
||||||
app.editorView.TextArea.Clear()
|
app.editorView.TextArea.Clear()
|
||||||
app.editorView.Clear()
|
app.editorView.Clear()
|
||||||
@ -204,6 +204,7 @@ func RunTUI() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type app struct {
|
type app struct {
|
||||||
|
allTests []*components.IntegrationTest
|
||||||
filteredTests []*components.IntegrationTest
|
filteredTests []*components.IntegrationTest
|
||||||
itemIdx int
|
itemIdx int
|
||||||
testDir string
|
testDir string
|
||||||
@ -214,7 +215,7 @@ type app struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newApp(testDir string) *app {
|
func newApp(testDir string) *app {
|
||||||
return &app{testDir: testDir}
|
return &app{testDir: testDir, allTests: tests.GetTests()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *app) getCurrentTest() *components.IntegrationTest {
|
func (self *app) getCurrentTest() *components.IntegrationTest {
|
||||||
@ -226,7 +227,7 @@ func (self *app) getCurrentTest() *components.IntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *app) loadTests() {
|
func (self *app) loadTests() {
|
||||||
self.filteredTests = tests.Tests
|
self.filteredTests = self.allTests
|
||||||
|
|
||||||
self.adjustCursor()
|
self.adjustCursor()
|
||||||
}
|
}
|
||||||
@ -237,9 +238,9 @@ func (self *app) adjustCursor() {
|
|||||||
|
|
||||||
func (self *app) filterWithString(needle string) {
|
func (self *app) filterWithString(needle string) {
|
||||||
if needle == "" {
|
if needle == "" {
|
||||||
self.filteredTests = tests.Tests
|
self.filteredTests = self.allTests
|
||||||
} else {
|
} else {
|
||||||
self.filteredTests = slices.Filter(tests.Tests, func(test *components.IntegrationTest) bool {
|
self.filteredTests = slices.Filter(self.allTests, func(test *components.IntegrationTest) bool {
|
||||||
return strings.Contains(test.Name(), needle)
|
return strings.Contains(test.Name(), needle)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,12 @@ func NewAssert(gui integrationTypes.GuiDriver) *Assert {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// for making assertions on string values
|
// for making assertions on string values
|
||||||
type matcher[T any] struct {
|
type matcher struct {
|
||||||
testFn func(T) (bool, string)
|
testFn func(string) (bool, string)
|
||||||
prefix string
|
prefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *matcher[T]) test(value T) (bool, string) {
|
func (self *matcher) test(value string) (bool, string) {
|
||||||
ok, message := self.testFn(value)
|
ok, message := self.testFn(value)
|
||||||
if ok {
|
if ok {
|
||||||
return true, ""
|
return true, ""
|
||||||
@ -39,20 +39,20 @@ func (self *matcher[T]) test(value T) (bool, string) {
|
|||||||
return false, message
|
return false, message
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *matcher[T]) context(prefix string) *matcher[T] {
|
func (self *matcher) context(prefix string) *matcher {
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
|
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func Contains(target string) *matcher[string] {
|
func Contains(target string) *matcher {
|
||||||
return &matcher[string]{testFn: func(value string) (bool, string) {
|
return &matcher{testFn: func(value string) (bool, string) {
|
||||||
return strings.Contains(value, target), fmt.Sprintf("Expected '%s' to contain '%s'", value, target)
|
return strings.Contains(value, target), fmt.Sprintf("Expected '%s' to contain '%s'", value, target)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Equals[T constraints.Ordered](target T) *matcher[T] {
|
func Equals[T constraints.Ordered](target string) *matcher {
|
||||||
return &matcher[T]{testFn: func(value T) (bool, string) {
|
return &matcher{testFn: func(value string) (bool, string) {
|
||||||
return target == value, fmt.Sprintf("Expected '%T' to equal '%T'", value, target)
|
return target == value, fmt.Sprintf("Expected '%T' to equal '%T'", value, target)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ func (self *Assert) CommitCount(expectedCount int) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Assert) MatchHeadCommitMessage(matcher *matcher[string]) {
|
func (self *Assert) MatchHeadCommitMessage(matcher *matcher) {
|
||||||
self.assertWithRetries(func() (bool, string) {
|
self.assertWithRetries(func() (bool, string) {
|
||||||
return len(self.gui.Model().Commits) == 0, "Expected at least one commit to be present"
|
return len(self.gui.Model().Commits) == 0, "Expected at least one commit to be present"
|
||||||
})
|
})
|
||||||
@ -113,7 +113,7 @@ func (self *Assert) InListContext() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Assert) MatchSelectedLine(matcher *matcher[string]) {
|
func (self *Assert) MatchSelectedLine(matcher *matcher) {
|
||||||
self.matchString(matcher, "Unexpected selected line.",
|
self.matchString(matcher, "Unexpected selected line.",
|
||||||
func() string {
|
func() string {
|
||||||
return self.gui.CurrentContext().GetView().SelectedLine()
|
return self.gui.CurrentContext().GetView().SelectedLine()
|
||||||
@ -149,7 +149,7 @@ func (self *Assert) InMenu() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Assert) MatchCurrentViewTitle(matcher *matcher[string]) {
|
func (self *Assert) MatchCurrentViewTitle(matcher *matcher) {
|
||||||
self.matchString(matcher, "Unexpected current view title.",
|
self.matchString(matcher, "Unexpected current view title.",
|
||||||
func() string {
|
func() string {
|
||||||
return self.gui.CurrentContext().GetView().Title
|
return self.gui.CurrentContext().GetView().Title
|
||||||
@ -157,7 +157,7 @@ func (self *Assert) MatchCurrentViewTitle(matcher *matcher[string]) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Assert) MatchMainViewContent(matcher *matcher[string]) {
|
func (self *Assert) MatchMainViewContent(matcher *matcher) {
|
||||||
self.matchString(matcher, "Unexpected main view content.",
|
self.matchString(matcher, "Unexpected main view content.",
|
||||||
func() string {
|
func() string {
|
||||||
return self.gui.MainView().Buffer()
|
return self.gui.MainView().Buffer()
|
||||||
@ -165,7 +165,7 @@ func (self *Assert) MatchMainViewContent(matcher *matcher[string]) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Assert) MatchSecondaryViewContent(matcher *matcher[string]) {
|
func (self *Assert) MatchSecondaryViewContent(matcher *matcher) {
|
||||||
self.matchString(matcher, "Unexpected secondary view title.",
|
self.matchString(matcher, "Unexpected secondary view title.",
|
||||||
func() string {
|
func() string {
|
||||||
return self.gui.SecondaryView().Buffer()
|
return self.gui.SecondaryView().Buffer()
|
||||||
@ -173,7 +173,7 @@ func (self *Assert) MatchSecondaryViewContent(matcher *matcher[string]) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Assert) matchString(matcher *matcher[string], context string, getValue func() string) {
|
func (self *Assert) matchString(matcher *matcher, context string, getValue func() string) {
|
||||||
self.assertWithRetries(func() (bool, string) {
|
self.assertWithRetries(func() (bool, string) {
|
||||||
value := getValue()
|
value := getValue()
|
||||||
return matcher.context(context).test(value)
|
return matcher.context(context).test(value)
|
||||||
|
@ -53,7 +53,7 @@ func NewIntegrationTest(args NewIntegrationTestArgs) *IntegrationTest {
|
|||||||
if args.Description != unitTestDescription {
|
if args.Description != unitTestDescription {
|
||||||
// this panics if we're in a unit test for our integration tests,
|
// this panics if we're in a unit test for our integration tests,
|
||||||
// so we're using "test test" as a sentinel value
|
// so we're using "test test" as a sentinel value
|
||||||
name = testNameFromFilePath()
|
name = testNameFromCurrentFilePath()
|
||||||
}
|
}
|
||||||
|
|
||||||
return &IntegrationTest{
|
return &IntegrationTest{
|
||||||
@ -106,8 +106,12 @@ func (self *IntegrationTest) Run(gui integrationTypes.GuiDriver) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testNameFromFilePath() string {
|
func testNameFromCurrentFilePath() string {
|
||||||
path := utils.FilePath(3)
|
path := utils.FilePath(3)
|
||||||
|
return TestNameFromFilePath(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNameFromFilePath(path string) string {
|
||||||
name := strings.Split(path, "integration/tests/")[1]
|
name := strings.Split(path, "integration/tests/")[1]
|
||||||
|
|
||||||
return name[:len(name)-len(".go")]
|
return name[:len(name)-len(".go")]
|
||||||
|
@ -1,17 +1,25 @@
|
|||||||
package tests
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/generics/set"
|
||||||
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/lazygit/pkg/integration/components"
|
"github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/branch"
|
"github.com/jesseduffield/lazygit/pkg/integration/tests/branch"
|
||||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/commit"
|
"github.com/jesseduffield/lazygit/pkg/integration/tests/commit"
|
||||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/custom_commands"
|
"github.com/jesseduffield/lazygit/pkg/integration/tests/custom_commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/interactive_rebase"
|
"github.com/jesseduffield/lazygit/pkg/integration/tests/interactive_rebase"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Here is where we lists the actual tests that will run. When you create a new test,
|
// Here is where we lists the actual tests that will run. When you create a new test,
|
||||||
// be sure to add it to this list.
|
// be sure to add it to this list.
|
||||||
|
|
||||||
var Tests = []*components.IntegrationTest{
|
var tests = []*components.IntegrationTest{
|
||||||
commit.Commit,
|
commit.Commit,
|
||||||
commit.NewBranch,
|
commit.NewBranch,
|
||||||
branch.Suggestions,
|
branch.Suggestions,
|
||||||
@ -19,3 +27,47 @@ var Tests = []*components.IntegrationTest{
|
|||||||
custom_commands.Basic,
|
custom_commands.Basic,
|
||||||
custom_commands.MultiplePrompts,
|
custom_commands.MultiplePrompts,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetTests() []*components.IntegrationTest {
|
||||||
|
// first we ensure that each test in this directory has actually been added to the above list.
|
||||||
|
testCount := 0
|
||||||
|
|
||||||
|
testNamesSet := set.NewFromSlice(slices.Map(
|
||||||
|
tests,
|
||||||
|
func(test *components.IntegrationTest) string {
|
||||||
|
return test.Name()
|
||||||
|
},
|
||||||
|
))
|
||||||
|
|
||||||
|
missingTestNames := []string{}
|
||||||
|
|
||||||
|
if err := filepath.Walk(filepath.Join(utils.GetLazygitRootDirectory(), "pkg/integration/tests"), func(path string, info os.FileInfo, err error) error {
|
||||||
|
if !info.IsDir() && strings.HasSuffix(path, ".go") {
|
||||||
|
// ignoring this current file
|
||||||
|
if filepath.Base(path) == "tests.go" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
nameFromPath := components.TestNameFromFilePath(path)
|
||||||
|
if !testNamesSet.Includes(nameFromPath) {
|
||||||
|
missingTestNames = append(missingTestNames, nameFromPath)
|
||||||
|
}
|
||||||
|
testCount++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to walk tests: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(missingTestNames) > 0 {
|
||||||
|
panic(fmt.Sprintf("The following tests are missing from the list of tests: %s. You need to add them to `pkg/integration/tests/tests.go`.", strings.Join(missingTestNames, ", ")))
|
||||||
|
}
|
||||||
|
|
||||||
|
if testCount > len(tests) {
|
||||||
|
panic("you have not added all of the tests to the tests list in `pkg/integration/tests/tests.go`")
|
||||||
|
} else if testCount < len(tests) {
|
||||||
|
panic("There are more tests in `pkg/integration/tests/tests.go` than there are test files in the tests directory. Ensure that you only have one test per file and you haven't included the same test twice in the tests list.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return tests
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user