diff --git a/cmd/job.go b/cmd/job.go index 00e0341..50e4dbc 100644 --- a/cmd/job.go +++ b/cmd/job.go @@ -12,5 +12,5 @@ var jobCmd = &cobra.Command{ func init() { rootCmd.AddCommand(jobCmd) - jobCmd.AddCommand(jobListCmd, jobCreateCmd, jobCancelCmd) + jobCmd.AddCommand(jobListCmd, JobCreateCmd, jobCancelCmd) } diff --git a/cmd/jobcreate.go b/cmd/jobcreate.go index 255360e..7f74478 100644 --- a/cmd/jobcreate.go +++ b/cmd/jobcreate.go @@ -15,7 +15,7 @@ import ( "github.com/renderinc/cli/pkg/tui/views" ) -var jobCreateCmd = &cobra.Command{ +var JobCreateCmd = &cobra.Command{ Use: "create [serviceID]", Short: "Create a new job for a service", Args: cobra.MaximumNArgs(1), @@ -24,10 +24,10 @@ var jobCreateCmd = &cobra.Command{ var InteractiveJobCreate = func(ctx context.Context, input *views.JobCreateInput, breadcrumb string) tea.Cmd { return command.AddToStackFunc( ctx, - jobCreateCmd, + JobCreateCmd, breadcrumb, input, - views.NewJobCreateView(ctx, input, jobCreateCmd, func(j *clientjob.Job) tea.Cmd { + views.NewJobCreateView(ctx, input, JobCreateCmd, views.CreateJob, func(j *clientjob.Job) tea.Cmd { return InteractiveLogs(ctx, views.LogInput{ ResourceIDs: []string{j.Id}, Tail: true, @@ -45,7 +45,9 @@ func interactiveJobCreate(cmd *cobra.Command, input *views.JobCreateInput) tea.C "Create Job", input, views.NewServiceList(ctx, views.ServiceInput{ - Types: []client.ServiceType{client.WebService, client.BackgroundWorker, client.PrivateService, client.CronJob}, + Types: []client.ServiceType{ + client.WebService, client.BackgroundWorker, client.PrivateService, client.CronJob, + }, }, func(ctx context.Context, r resource.Resource) tea.Cmd { input.ServiceID = r.ID() return InteractiveJobCreate(ctx, input, resource.BreadcrumbForResource(r)) @@ -62,7 +64,7 @@ func interactiveJobCreate(cmd *cobra.Command, input *views.JobCreateInput) tea.C } func init() { - jobCreateCmd.RunE = func(cmd *cobra.Command, args []string) error { + JobCreateCmd.RunE = func(cmd *cobra.Command, args []string) error { var input views.JobCreateInput err := command.ParseCommand(cmd, args, &input) @@ -84,6 +86,6 @@ func init() { return nil } - jobCreateCmd.Flags().String("start-command", "", "The command to run for the job") - jobCreateCmd.Flags().String("plan-id", "", "The plan ID for the job (optional)") + JobCreateCmd.Flags().String("start-command", "", "The command to run for the job") + JobCreateCmd.Flags().String("plan-id", "", "The plan ID for the job (optional)") } diff --git a/pkg/command/form.go b/pkg/command/form.go index 97b518f..1503b08 100644 --- a/pkg/command/form.go +++ b/pkg/command/form.go @@ -64,6 +64,7 @@ func FormValuesFromStruct(v any) FormValues { case reflect.Ptr: if elemField.IsNil() { formValues[cliTag] = NewStringFormValue("") + break } switch field.Type.Elem().Kind() { diff --git a/pkg/tui/views/jobcreate.go b/pkg/tui/views/jobcreate.go index 7764d1e..7b4dc60 100644 --- a/pkg/tui/views/jobcreate.go +++ b/pkg/tui/views/jobcreate.go @@ -40,14 +40,27 @@ type JobCreateView struct { formAction *tui.FormWithAction[*clientjob.Job] } -func NewJobCreateView(ctx context.Context, input *JobCreateInput, cobraCmd *cobra.Command, action func(j *clientjob.Job) tea.Cmd) *JobCreateView { - form, _ := command.HuhForm(cobraCmd, input) +func NewJobCreateView( + ctx context.Context, + input *JobCreateInput, + cobraCmd *cobra.Command, + createJob func(ctx context.Context, input JobCreateInput) (*clientjob.Job, error), + action func(j *clientjob.Job) tea.Cmd, +) *JobCreateView { + form, values := command.HuhForm(cobraCmd, input) return &JobCreateView{ formAction: tui.NewFormWithAction( tui.NewFormAction( action, - command.LoadCmd(ctx, CreateJob, *input), + func() tea.Msg { + var createJobInput JobCreateInput + err := command.StructFromFormValues(values, &createJobInput) + if err != nil { + return tui.ErrorMsg{Err: err} + } + return command.LoadCmd(ctx, createJob, createJobInput)() + }, ), form, ), diff --git a/pkg/tui/views/jobcreate_test.go b/pkg/tui/views/jobcreate_test.go new file mode 100644 index 0000000..8d4c0b8 --- /dev/null +++ b/pkg/tui/views/jobcreate_test.go @@ -0,0 +1,50 @@ +package views_test + +import ( + "context" + "testing" + "time" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/x/exp/teatest" + "github.com/renderinc/cli/cmd" + clientjob "github.com/renderinc/cli/pkg/client/jobs" + "github.com/renderinc/cli/pkg/tui/testhelper" + "github.com/renderinc/cli/pkg/tui/views" + "github.com/stretchr/testify/require" +) + +func TestJobCreate(t *testing.T) { + ctx := context.Background() + + input := views.JobCreateInput{ + ServiceID: "service-id", + } + + var createJobInput views.JobCreateInput + + createJob := func(ctx context.Context, input views.JobCreateInput) (*clientjob.Job, error) { + createJobInput = input + return &clientjob.Job{Id: "foo"}, nil + } + + action := func(j *clientjob.Job) tea.Cmd { + return nil + } + + m := views.NewJobCreateView(ctx, &input, cmd.JobCreateCmd, createJob, action) + tm := teatest.NewTestModel(t, testhelper.Stackify(m)) + + tm.Send(tea.WindowSizeMsg{Width: 80, Height: 80}) + + // Add start command + tm.Send(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("echo 'hello world'")}) + tm.Send(tea.KeyMsg{Type: tea.KeyEnter}) + + require.Eventually(t, func() bool { + return createJobInput.StartCommand != nil && *createJobInput.StartCommand == "echo 'hello world'" + }, time.Second, time.Millisecond*10) + + err := tm.Quit() + require.NoError(t, err) +}