@@ -161,21 +161,27 @@ func NewTestingWorkflow(baseName string, workflow BaseWorkflow, opts ...TestingW
161161// TestingWorkflowOption defines a function type for configuring TestingWorkflow instances.
162162type TestingWorkflowOption func (* TestingWorkflow )
163163
164- // WithOnlyOneJob keeps only the given job ID and its dependencies
165- // in the workflow, removing all other jobs .
166- // This can be used to run only a specific job for testing purposes .
167- func WithOnlyOneJob (t * testing.T , jobID string ) TestingWorkflowOption {
164+ // WithOnlyOneJob keeps only the given job ID, removing all other jobs.
165+ // If removeDependencies is true, it will also remove all dependencies of the given job .
166+ // You normally don't want to remove dependencies, otherwise the workflow might fail if it consumes the output of a dependency .
167+ func WithOnlyOneJob (t * testing.T , jobID string , removeDependencies bool ) TestingWorkflowOption {
168168 return func (twf * TestingWorkflow ) {
169169 onlyJob , ok := twf .BaseWorkflow .Jobs [jobID ]
170170 require .True (t , ok , fmt .Errorf ("job %q not found" , jobID ))
171171
172- // Remove all jobs except the given one and its dependencies
172+ // Remove all jobs
173173 for k := range twf .BaseWorkflow .Jobs {
174- if k == jobID || slices .Contains (onlyJob .Needs , k ) {
174+ // Do not remove the given job if it's a dependency and we don't want to remove dependencies
175+ if k == jobID || (slices .Contains (onlyJob .Needs , k ) && ! removeDependencies ) {
175176 continue
176177 }
177178 delete (twf .BaseWorkflow .Jobs , k )
178179 }
180+ // Remove all dependencies from the only job left in the workflow, otherwise it won't run
181+ // because it depends on a job that has been removed.
182+ if removeDependencies {
183+ onlyJob .Needs = nil
184+ }
179185 }
180186}
181187
@@ -209,6 +215,67 @@ func WithNoOpStep(t *testing.T, jobID, stepID string) TestingWorkflowOption {
209215 }
210216}
211217
218+ // InjectedStepsOptions defines options for injecting steps into a job via WithInjectedSteps.
219+ type InjectedStepsOptions struct {
220+ // Position indicates whether to inject the new steps before or after the injection step.
221+ Position InjectedStepsOptionsPosition
222+
223+ // InjectionStepID is the ID of the step where the new steps will be injected.
224+ // Either InjectionStepID or InjectionStepIndex must be set, but not both.
225+ InjectionStepID string
226+
227+ // InjectionStepIndex is the index of the step where the new steps will be injected.
228+ // You can use 0 to inject before the first step.
229+ // You can use -1 to inject after the last step.
230+ // Otherwise, provide a valid step index to inject before/after that step, depending on Position.
231+ // Either InjectionStepID or InjectionStepIndex must be set, but not both.
232+ InjectionStepIndex int
233+
234+ // Steps are the steps to be injected.
235+ Steps Steps
236+ }
237+
238+ // InjectedStepsOptionsPosition indicates the position where the new steps will be injected.
239+ type InjectedStepsOptionsPosition int
240+
241+ const (
242+ // InjectedStepsOptionsPositionBefore indicates that the new steps will be injected before the injection step.
243+ InjectedStepsOptionsPositionBefore InjectedStepsOptionsPosition = iota
244+
245+ // InjectedStepsOptionsPositionAfter indicates that the new steps will be injected after the injection step.
246+ InjectedStepsOptionsPositionAfter
247+ )
248+
249+ // WithInjectedSteps injects the given steps into the given job at the specified position
250+ // relative to the step identified by InjectionStepID or InjectionStepIndex (see InjectedStepsOptions).
251+ // This can be used to add custom steps for testing purposes.
252+ func WithInjectedSteps (t * testing.T , jobID string , opts InjectedStepsOptions ) TestingWorkflowOption {
253+ return func (twf * TestingWorkflow ) {
254+ job , ok := twf .BaseWorkflow .Jobs [jobID ]
255+ require .True (t , ok , fmt .Errorf ("job %q not found" , jobID ))
256+
257+ var injectionStepIndex int
258+ if opts .InjectionStepID != "" {
259+ injectionStepIndex = job .getStepIndex (opts .InjectionStepID )
260+ require .GreaterOrEqual (t , injectionStepIndex , 0 , "injection step with id %q not found" , opts .InjectionStepID )
261+ } else {
262+ injectionStepIndex = opts .InjectionStepIndex
263+ require .GreaterOrEqual (t , injectionStepIndex , - 1 , "injection step index is < -1. it should be -1 (for injecting at the end) or a valid index." )
264+ if injectionStepIndex == - 1 {
265+ injectionStepIndex = len (job .Steps ) - 1
266+ }
267+ require .Less (t , injectionStepIndex , len (job .Steps ), "injection step index %d out of bounds (steps length: %d)" , injectionStepIndex , len (job .Steps ))
268+ }
269+
270+ switch opts .Position {
271+ case InjectedStepsOptionsPositionBefore :
272+ job .Steps = append (job .Steps [:injectionStepIndex ], append (opts .Steps , job .Steps [injectionStepIndex :]... )... )
273+ case InjectedStepsOptionsPositionAfter :
274+ job .Steps = append (job .Steps [:injectionStepIndex + 1 ], append (opts .Steps , job .Steps [injectionStepIndex + 1 :]... )... )
275+ }
276+ }
277+ }
278+
212279// WithMatrix sets the matrix for the given job.
213280// This can be used to test workflows that use a dynamic matrix.
214281// act doesn't support dynamic matrix values, so this is a workaround to set the matrix for the given job.
0 commit comments