diff --git a/cmd/record.go b/cmd/record.go index 7767e7d..53d7a93 100644 --- a/cmd/record.go +++ b/cmd/record.go @@ -11,13 +11,15 @@ import ( ) func newRecordCommand() *cobra.Command { - return &cobra.Command{ + var save bool + + cmd := &cobra.Command{ Use: "record ", Short: "Record a new CLI scenario", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { path := filepath.Clean(args[0]) - createdPath, err := mire.Record(path) + createdPath, err := mire.Record(path, mire.RecordOptions{Save: save}) if err != nil { if errors.Is(err, mire.ErrRecordingDiscarded) { return nil @@ -30,4 +32,8 @@ func newRecordCommand() *cobra.Command { return nil }, } + + cmd.Flags().BoolVar(&save, "save", false, "Save the recording without prompting") + + return cmd } diff --git a/cmd/root_test.go b/cmd/root_test.go index 7f0362d..0c3ea03 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -130,6 +130,48 @@ func TestRunRecord(t *testing.T) { }) } +func TestRunRecordSaveFlagSkipsPrompt(t *testing.T) { + testutil.AddFakeRecordDependencies(t, "bwrap", "bash") + + root := t.TempDir() + wantDir := filepath.Join(root, "e2e") + + testutil.WithWorkingDir(t, root, func() struct{} { + initStdout, initStderr := testutil.CaptureOutput(t, func() { + if got := Run([]string{"init"}); got != 0 { + t.Fatalf("Run() code = %d, want %d", got, 0) + } + }) + if initStdout != prefixed("Done initialising...\n") { + t.Fatalf("stdout = %q, want %q", initStdout, prefixed("Done initialising...\n")) + } + if initStderr != "" { + t.Fatalf("stderr = %q, want empty", initStderr) + } + + stdout, stderr := testutil.CapturePromptedOutput(t, "exit\n", "", "", func() { + if got := Run([]string{"record", "--save", "suite/spec"}); got != 0 { + t.Fatalf("Run() code = %d, want %d", got, 0) + } + }) + + createdPath := filepath.Join(wantDir, "suite", "spec") + if !strings.Contains(stdout, createdPath) { + t.Fatalf("stdout = %q, want created path", stdout) + } + if strings.Contains(stderr, "Save recording?") { + t.Fatalf("stderr = %q, want save prompt omitted", stderr) + } + + for _, name := range []string{"in", "out"} { + if _, err := os.Stat(filepath.Join(createdPath, name)); err != nil { + t.Fatalf("Stat(%q) error = %v", filepath.Join(createdPath, name), err) + } + } + return struct{}{} + }) +} + func TestRunRewrite(t *testing.T) { testutil.AddFakeRecordDependencies(t, "bwrap", "bash") diff --git a/internal/mire/record.go b/internal/mire/record.go index a012bdb..de0bff6 100644 --- a/internal/mire/record.go +++ b/internal/mire/record.go @@ -5,9 +5,13 @@ import ( "os" ) +type RecordOptions struct { + Save bool +} + // Record creates the requested scenario path under the resolved test directory // and records an interactive shell session into in/out fixtures when saved. -func Record(path string) (string, error) { +func Record(path string, opts RecordOptions) (string, error) { root, err := currentProjectRoot() if err != nil { return "", err @@ -46,7 +50,7 @@ func Record(path string) (string, error) { in: os.Stdin, out: os.Stdout, err: os.Stderr, - }, cfg.Sandbox, cfg.Mounts, cfg.Paths, setupScripts); err != nil { + }, cfg.Sandbox, cfg.Mounts, cfg.Paths, setupScripts, opts.Save); err != nil { return "", err } diff --git a/internal/mire/record_session.go b/internal/mire/record_session.go index 7334b7d..123ea09 100644 --- a/internal/mire/record_session.go +++ b/internal/mire/record_session.go @@ -18,7 +18,7 @@ type recordIO struct { err io.Writer } -func recordScenario(target, shellPath string, rio recordIO, sandboxConfig map[string]string, mounts, paths, setupScripts []string) error { +func recordScenario(target, shellPath string, rio recordIO, sandboxConfig map[string]string, mounts, paths, setupScripts []string, save bool) error { rawIn, rawOut, cleanup, err := newRecordFiles() if err != nil { return err @@ -45,12 +45,14 @@ func recordScenario(target, shellPath string, rio recordIO, sandboxConfig map[st // as an error runRecordSession(target, rawIn, rawOut, shellPath, sandbox, rio, sandboxConfig, mounts, paths, setupScripts) - save, err := confirmRecordSave(rio) - if err != nil { - return err - } if !save { - return ErrRecordingDiscarded + save, err = confirmRecordSave(rio) + if err != nil { + return err + } + if !save { + return ErrRecordingDiscarded + } } recordedIn, recordedOut, err := loadRecordedFixtures(rawIn, rawOut) diff --git a/internal/mire/record_test.go b/internal/mire/record_test.go index 0ec5a28..cce4bcf 100644 --- a/internal/mire/record_test.go +++ b/internal/mire/record_test.go @@ -22,7 +22,7 @@ func TestRecordCreatesRelativePath(t *testing.T) { var got string testutil.WithWorkingDir(t, root, func() struct{} { testutil.CapturePromptedOutput(t, "exit\n", "Save recording?", "y\n", func() { - path, err := Record(filepath.Join("a", "b", "c") + string(os.PathSeparator)) + path, err := Record(filepath.Join("a", "b", "c")+string(os.PathSeparator), RecordOptions{}) if err != nil { t.Fatalf("Record() error = %v", err) } @@ -61,7 +61,7 @@ func TestRecordReturnsDiscardedErrorWhenSaveDeclined(t *testing.T) { target := filepath.Join(testDir, "a", "b", "c") testutil.MustMkdirAll(t, target) return withRecordStreams(t, "exit\nn\n", func(rio recordIO) error { - return recordScenario(target, recordShellPath(testDir), rio, defaultSandboxConfig(), nil, nil, nil) + return recordScenario(target, recordShellPath(testDir), rio, defaultSandboxConfig(), nil, nil, nil, false) }) }) @@ -88,7 +88,7 @@ func TestRecordReturnsDiscardedErrorWhenOverwriteDeclined(t *testing.T) { err := testutil.WithWorkingDir(t, root, func() error { return withRecordStreams(t, "n\n", func(rio recordIO) error { - return recordScenario(target, recordShellPath(testDir), rio, defaultSandboxConfig(), nil, nil, nil) + return recordScenario(target, recordShellPath(testDir), rio, defaultSandboxConfig(), nil, nil, nil, false) }) }) @@ -281,7 +281,7 @@ func TestRecordScenarioUsesDeterministicSandbox(t *testing.T) { "y\n", func(rio recordIO) error { rio.out = ioDiscard{} - return recordScenario(target, recordShellPath(testDir), rio, sandboxConfig, nil, nil, nil) + return recordScenario(target, recordShellPath(testDir), rio, sandboxConfig, nil, nil, nil, false) }, ) }) @@ -377,7 +377,7 @@ func TestRecordScenarioStripsInterruptsFromSavedFixtures(t *testing.T) { in: reader, out: ioDiscard{}, err: errWriter, - }, defaultSandboxConfig(), nil, nil, nil) + }, defaultSandboxConfig(), nil, nil, nil, false) if writeErr := <-writeDone; writeErr != nil { t.Fatalf("write record input: %v", writeErr) } @@ -412,7 +412,7 @@ func TestRecordFailsWhenRecorderShellMissing(t *testing.T) { target := filepath.Join(testDir, "suite", "spec") err := testutil.WithWorkingDir(t, root, func() error { - _, err := Record(filepath.Join("suite", "spec")) + _, err := Record(filepath.Join("suite", "spec"), RecordOptions{}) return err }) if err == nil {