github.com/saucelabs/saucectl@v0.175.1/internal/cmd/run/cucumber.go (about)

     1  package run
     2  
     3  import (
     4  	"os"
     5  
     6  	"github.com/rs/zerolog/log"
     7  	"github.com/saucelabs/saucectl/internal/ci"
     8  	cmds "github.com/saucelabs/saucectl/internal/cmd"
     9  	"github.com/saucelabs/saucectl/internal/config"
    10  	"github.com/saucelabs/saucectl/internal/cucumber"
    11  	"github.com/saucelabs/saucectl/internal/flags"
    12  	"github.com/saucelabs/saucectl/internal/framework"
    13  	"github.com/saucelabs/saucectl/internal/http"
    14  	"github.com/saucelabs/saucectl/internal/region"
    15  	"github.com/saucelabs/saucectl/internal/report/captor"
    16  	"github.com/saucelabs/saucectl/internal/saucecloud"
    17  	"github.com/saucelabs/saucectl/internal/saucecloud/retry"
    18  	"github.com/saucelabs/saucectl/internal/segment"
    19  	"github.com/saucelabs/saucectl/internal/usage"
    20  	"github.com/saucelabs/saucectl/internal/viper"
    21  	"github.com/spf13/cobra"
    22  	"github.com/spf13/pflag"
    23  	"golang.org/x/text/cases"
    24  	"golang.org/x/text/language"
    25  )
    26  
    27  // NewCucumberCmd creates the 'run' command for cucumber.
    28  func NewCucumberCmd() *cobra.Command {
    29  	sc := flags.SnakeCharmer{Fmap: map[string]*pflag.Flag{}}
    30  
    31  	cmd := &cobra.Command{
    32  		Use:              "cucumberjs",
    33  		Short:            "Run Cucumber test",
    34  		SilenceUsage:     true,
    35  		Hidden:           true,
    36  		TraverseChildren: true,
    37  		PreRunE: func(cmd *cobra.Command, args []string) error {
    38  			sc.BindAll()
    39  			return preRun()
    40  		},
    41  		Run: func(cmd *cobra.Command, args []string) {
    42  			// Test patterns are passed in via positional args.
    43  			viper.Set("suite::options::paths", args)
    44  
    45  			exitCode, err := runCucumber(cmd, true)
    46  			if err != nil {
    47  				log.Err(err).Msg("failed to execute run command")
    48  			}
    49  			os.Exit(exitCode)
    50  		},
    51  	}
    52  
    53  	sc.Fset = cmd.Flags()
    54  
    55  	sc.String("name", "suite::name", "", "Set the name of the job as it will appear on Sauce Labs.")
    56  	sc.String("platformName", "suite::platformName", "", "Run against this platform.")
    57  
    58  	// Cucumber
    59  	sc.String("scenario-name", "suite::options::name", "", "Regular expressions of which scenario names should match one of to be run")
    60  	sc.StringSlice("paths", "suite::options::paths", []string{}, "Paths to where the feature files are, using glob pattern")
    61  	sc.StringSlice("excludedTestFiles", "suite::options::excludedTestFiles", []string{}, "Exclude test files to skip the tests, using glob pattern")
    62  	sc.Bool("backtrace", "suite::options::backtrace", false, "Show the full backtrace for errors")
    63  	sc.StringSlice("require", "suite::options::require", []string{}, "Paths to where your support code is, for CommonJS.")
    64  	sc.StringSlice("import", "suite::options::import", []string{}, "Paths to where your support code is, for ESM")
    65  	sc.StringSlice("scenario-tags", "suite::options::tags", []string{}, "Tag expression to filter which scenarios should be run")
    66  	sc.StringSlice("format", "suite::options::format", []string{}, "Name/path and (optionally) output file path of each formatter to use")
    67  	sc.Int("parallel", "suite::options::parallel", 0, "Run tests in parallel with the given number of worker processes, default: 0")
    68  	sc.Int("passThreshold", "suite::passThreshold", 1, "The minimum number of successful attempts for a suite to be considered as 'passed'.")
    69  
    70  	return cmd
    71  }
    72  
    73  func runCucumber(cmd *cobra.Command, isCLIDriven bool) (int, error) {
    74  	if !isCLIDriven {
    75  		config.ValidateSchema(gFlags.cfgFilePath)
    76  	}
    77  
    78  	p, err := cucumber.FromFile(gFlags.cfgFilePath)
    79  	if err != nil {
    80  		return 1, err
    81  	}
    82  
    83  	p.CLIFlags = flags.CaptureCommandLineFlags(cmd.Flags())
    84  
    85  	if err := applyCucumberFlags(&p); err != nil {
    86  		return 1, err
    87  	}
    88  
    89  	cucumber.SetDefaults(&p)
    90  
    91  	if err := cucumber.Validate(&p); err != nil {
    92  		return 1, err
    93  	}
    94  
    95  	regio := region.FromString(p.Sauce.Region)
    96  	if !gFlags.noAutoTagging {
    97  		p.Sauce.Metadata.Tags = append(p.Sauce.Metadata.Tags, ci.GetTags()...)
    98  	}
    99  
   100  	tracker := segment.DefaultTracker
   101  	if regio == region.Staging {
   102  		tracker.Enabled = false
   103  	}
   104  
   105  	go func() {
   106  		props := usage.Properties{}
   107  		props.SetFramework("playwright-cucumberjs").SetFVersion(p.Playwright.Version).SetFlags(cmd.Flags()).SetSauceConfig(p.Sauce).
   108  			SetArtifacts(p.Artifacts).SetNPM(p.Npm).SetNumSuites(len(p.Suites)).SetJobs(captor.Default.TestResults).
   109  			SetSlack(p.Notifications.Slack).SetSharding(cucumber.IsSharded(p.Suites)).SetLaunchOrder(p.Sauce.LaunchOrder).
   110  			SetSmartRetry(p.IsSmartRetried()).SetReporters(p.Reporters)
   111  		tracker.Collect(cases.Title(language.English).String(cmds.FullName(cmd)), props)
   112  		_ = tracker.Close()
   113  	}()
   114  
   115  	cleanupArtifacts(p.Artifacts)
   116  
   117  	creds := regio.Credentials()
   118  
   119  	restoClient := http.NewResto(regio.APIBaseURL(), creds.Username, creds.AccessKey, 0)
   120  	restoClient.ArtifactConfig = p.Artifacts.Download
   121  	testcompClient := http.NewTestComposer(regio.APIBaseURL(), creds, testComposerTimeout)
   122  	webdriverClient := http.NewWebdriver(regio.WebDriverBaseURL(), creds, webdriverTimeout)
   123  	appsClient := *http.NewAppStore(regio.APIBaseURL(), creds.Username, creds.AccessKey, gFlags.appStoreTimeout)
   124  	rdcClient := http.NewRDCService(regio.APIBaseURL(), creds.Username, creds.AccessKey, rdcTimeout, config.ArtifactDownload{})
   125  	insightsClient := http.NewInsightsService(regio.APIBaseURL(), creds, insightsTimeout)
   126  	iamClient := http.NewUserService(regio.APIBaseURL(), creds, iamTimeout)
   127  
   128  	log.Info().Msg("Running Playwright-Cucumberjs in Sauce Labs")
   129  	r := saucecloud.CucumberRunner{
   130  		Project: p,
   131  		CloudRunner: saucecloud.CloudRunner{
   132  			ProjectUploader: &appsClient,
   133  			JobService: saucecloud.JobService{
   134  				VDCStarter:    &webdriverClient,
   135  				RDCStarter:    &rdcClient,
   136  				VDCReader:     &restoClient,
   137  				RDCReader:     &rdcClient,
   138  				VDCWriter:     &testcompClient,
   139  				VDCStopper:    &restoClient,
   140  				RDCStopper:    &rdcClient,
   141  				VDCDownloader: &restoClient,
   142  			},
   143  			TunnelService:   &restoClient,
   144  			MetadataService: &testcompClient,
   145  			InsightsService: &insightsClient,
   146  			UserService:     &iamClient,
   147  			BuildService:    &restoClient,
   148  			Region:          regio,
   149  			ShowConsoleLog:  p.ShowConsoleLog,
   150  			Reporters: createReporters(p.Reporters, p.Notifications, p.Sauce.Metadata, &testcompClient, &restoClient,
   151  				"cucumber", "sauce", gFlags.async),
   152  			Async:                  gFlags.async,
   153  			FailFast:               gFlags.failFast,
   154  			MetadataSearchStrategy: framework.NewSearchStrategy(p.Playwright.Version, p.RootDir),
   155  			NPMDependencies:        p.Npm.Dependencies,
   156  			Retrier: &retry.SauceReportRetrier{
   157  				VDCReader:       &restoClient,
   158  				ProjectUploader: &appsClient,
   159  				Project:         &p,
   160  			},
   161  		},
   162  	}
   163  
   164  	p.Npm.Packages = cleanPlaywrightPackages(p.Npm, p.Playwright.Version)
   165  	return r.RunProject()
   166  }
   167  
   168  func applyCucumberFlags(p *cucumber.Project) error {
   169  	if gFlags.selectedSuite != "" {
   170  		if err := cucumber.FilterSuites(p, gFlags.selectedSuite); err != nil {
   171  			return err
   172  		}
   173  	}
   174  
   175  	// Use the adhoc suite instead, if one is provided
   176  	if p.Suite.Name != "" {
   177  		p.Suites = []cucumber.Suite{p.Suite}
   178  	}
   179  
   180  	return nil
   181  }