github.com/saucelabs/saucectl@v0.175.1/internal/cmd/ini/initializer.go (about)

     1  package ini
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"sort"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/AlecAivazis/survey/v2"
    12  	"github.com/AlecAivazis/survey/v2/terminal"
    13  	"github.com/fatih/color"
    14  	"github.com/saucelabs/saucectl/internal/config"
    15  	"github.com/saucelabs/saucectl/internal/cypress"
    16  	"github.com/saucelabs/saucectl/internal/devices"
    17  	"github.com/saucelabs/saucectl/internal/espresso"
    18  	"github.com/saucelabs/saucectl/internal/framework"
    19  	"github.com/saucelabs/saucectl/internal/http"
    20  	"github.com/saucelabs/saucectl/internal/iam"
    21  	"github.com/saucelabs/saucectl/internal/imagerunner"
    22  	"github.com/saucelabs/saucectl/internal/msg"
    23  	"github.com/saucelabs/saucectl/internal/playwright"
    24  	"github.com/saucelabs/saucectl/internal/region"
    25  	"github.com/saucelabs/saucectl/internal/testcafe"
    26  	"github.com/saucelabs/saucectl/internal/vmd"
    27  	"github.com/saucelabs/saucectl/internal/xcuitest"
    28  	"github.com/spf13/pflag"
    29  )
    30  
    31  var androidDevicesPatterns = []string{
    32  	"Amazon Kindle Fire .*", "Google Pixel .*", "HTC .*", "Huawei .*",
    33  	"LG .*", "Motorola .*", "OnePlus .*", "Samsung .*", "Sony .*",
    34  }
    35  
    36  var iOSDevicesPatterns = []string{
    37  	"iPad .*",
    38  	"iPhone .*",
    39  }
    40  
    41  var fallbackAndroidVirtualDevices = []vmd.VirtualDevice{
    42  	{Name: "Android GoogleAPI Emulator", OSVersion: []string{"11.0", "10.0"}},
    43  }
    44  
    45  var fallbackIOSVirtualDevices = []vmd.VirtualDevice{
    46  	{Name: "iPhone Simulator", OSVersion: []string{"16.2"}},
    47  }
    48  
    49  type initializer struct {
    50  	stdio        terminal.Stdio
    51  	infoReader   framework.MetadataService
    52  	deviceReader devices.Reader
    53  	vmdReader    vmd.Reader
    54  	userService  iam.UserService
    55  	cfg          *initConfig
    56  }
    57  
    58  // newInitializer creates a new initializer instance.
    59  func newInitializer(stdio terminal.Stdio, creds iam.Credentials, cfg *initConfig) *initializer {
    60  	r := region.FromString(cfg.region)
    61  	tc := http.NewTestComposer(r.APIBaseURL(), creds, testComposerTimeout)
    62  	rc := http.NewRDCService(r.APIBaseURL(), creds.Username, creds.AccessKey, rdcTimeout, config.ArtifactDownload{})
    63  	rs := http.NewResto(r.APIBaseURL(), creds.Username, creds.AccessKey, restoTimeout)
    64  	us := http.NewUserService(r.APIBaseURL(), creds, 5*time.Second)
    65  
    66  	return &initializer{
    67  		stdio:        stdio,
    68  		infoReader:   &tc,
    69  		deviceReader: &rc,
    70  		vmdReader:    &rs,
    71  		userService:  &us,
    72  		cfg:          cfg,
    73  	}
    74  }
    75  
    76  func (ini *initializer) configure() error {
    77  	switch ini.cfg.frameworkName {
    78  	case cypress.Kind:
    79  		return ini.initializeCypress()
    80  	case playwright.Kind:
    81  		return ini.initializePlaywright()
    82  	case testcafe.Kind:
    83  		return ini.initializeTestcafe()
    84  	case espresso.Kind:
    85  		return ini.initializeEspresso()
    86  	case xcuitest.Kind:
    87  		return ini.initializeXCUITest()
    88  	case imagerunner.Kind:
    89  		return ini.initializeImageRunner()
    90  	default:
    91  		return fmt.Errorf("unsupported framework %q", ini.cfg.frameworkName)
    92  	}
    93  }
    94  
    95  func askCredentials(stdio terminal.Stdio) (iam.Credentials, error) {
    96  	creds := iam.Credentials{}
    97  	q := &survey.Input{Message: "Sauce Labs username:"}
    98  
    99  	err := survey.AskOne(q, &creds.Username,
   100  		survey.WithValidator(survey.Required),
   101  		survey.WithShowCursor(true),
   102  		survey.WithStdio(stdio.In, stdio.Out, stdio.Err))
   103  	if err != nil {
   104  		return creds, err
   105  	}
   106  
   107  	q = &survey.Input{Message: "Sauce Labs access key:"}
   108  	err = survey.AskOne(q, &creds.AccessKey,
   109  		survey.WithValidator(survey.Required),
   110  		survey.WithShowCursor(true),
   111  		survey.WithStdio(stdio.In, stdio.Out, stdio.Err))
   112  	if err != nil {
   113  		return creds, err
   114  	}
   115  	return creds, nil
   116  }
   117  
   118  func askRegion(stdio terminal.Stdio) (string, error) {
   119  	var r string
   120  	p := &survey.Select{
   121  		Message: "Select region:",
   122  		Options: []string{region.USWest1.String(), region.EUCentral1.String()},
   123  		Default: region.USWest1.String(),
   124  	}
   125  
   126  	err := survey.AskOne(p, &r, survey.WithStdio(stdio.In, stdio.Out, stdio.Err))
   127  	if err != nil {
   128  		return "", err
   129  	}
   130  	return r, nil
   131  }
   132  
   133  func (ini *initializer) checkCredentials(region string) error {
   134  	_, err := ini.infoReader.Frameworks(context.Background())
   135  	if err != nil && err.Error() == "unexpected status '401' from test-composer: Unauthorized\n" {
   136  		println()
   137  		color.HiRed("It appears that your credentials are incorrect.")
   138  		fmt.Printf("Use %s to update your account settings.\n", color.HiBlueString("saucectl configure"))
   139  		println()
   140  		return errors.New(msg.InvalidCredentials)
   141  	}
   142  	if err != nil && strings.Contains(err.Error(), "context deadline exceeded") {
   143  		println()
   144  		color.HiRed("saucectl cannot reach Sauce Labs infrastructure.")
   145  		fmt.Printf("Check your connection and that you can access %s.\n", color.HiBlueString("https://api.%s.saucelabs.com", region))
   146  		println()
   147  		return errors.New(msg.UnableToCheckCredentials)
   148  	}
   149  	return err
   150  }
   151  
   152  type completor func(string) []string
   153  
   154  /* When translation */
   155  var whenStrings = []string{
   156  	"when tests are failing",
   157  	"when tests are passing",
   158  	"never",
   159  	"always",
   160  }
   161  var mapWhen = map[string]config.When{
   162  	"when tests are failing": config.WhenFail,
   163  	"when tests are passing": config.WhenPass,
   164  	"never":                  config.WhenNever,
   165  	"always":                 config.WhenAlways,
   166  }
   167  
   168  func (ini *initializer) askDownloadWhen() error {
   169  	q := &survey.Select{
   170  		Message: "Download artifacts:",
   171  		Default: whenStrings[0],
   172  		Options: whenStrings,
   173  	}
   174  	q.WithStdio(ini.stdio)
   175  
   176  	var when string
   177  	err := survey.AskOne(q, &when,
   178  		survey.WithShowCursor(true),
   179  		survey.WithValidator(survey.Required),
   180  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err))
   181  	if err != nil {
   182  		return err
   183  	}
   184  	ini.cfg.artifactWhen = mapWhen[when]
   185  	return nil
   186  }
   187  
   188  func (ini *initializer) askDevice(suggestions []string) error {
   189  	q := &survey.Select{
   190  		Message: "Select device pattern:",
   191  		Options: suggestions,
   192  	}
   193  	return survey.AskOne(q, &ini.cfg.device.Name,
   194  		survey.WithShowCursor(true),
   195  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err),
   196  	)
   197  }
   198  
   199  // vmdToMaps returns a list of virtual devices, and a map containing all supported platform versions.
   200  func vmdToMaps(vmds []vmd.VirtualDevice) ([]string, map[string][]string) {
   201  	var vmdNames []string
   202  	vmdOSVersions := map[string][]string{}
   203  	for _, e := range vmds {
   204  		vmdNames = append(vmdNames, e.Name)
   205  		vmdOSVersions[e.Name] = e.OSVersion
   206  	}
   207  
   208  	sort.Strings(vmdNames)
   209  	for _, v := range vmdOSVersions {
   210  		sortVersions(v)
   211  	}
   212  	return vmdNames, vmdOSVersions
   213  }
   214  
   215  func (ini *initializer) askSimulator(vmds []vmd.VirtualDevice) error {
   216  	vmdNames, vmdOSVersions := vmdToMaps(vmds)
   217  
   218  	q := &survey.Select{
   219  		Message: "Select simulator:",
   220  		Options: vmdNames,
   221  	}
   222  	err := survey.AskOne(q, &ini.cfg.simulator.Name,
   223  		survey.WithShowCursor(true),
   224  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err))
   225  	if err != nil {
   226  		return err
   227  	}
   228  
   229  	q = &survey.Select{
   230  		Message: "Select platform version:",
   231  		Options: vmdOSVersions[ini.cfg.simulator.Name],
   232  	}
   233  	var simulatorVersion string
   234  	err = survey.AskOne(q, &simulatorVersion,
   235  		survey.WithShowCursor(true),
   236  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err))
   237  	ini.cfg.simulator.PlatformVersions = []string{simulatorVersion}
   238  	return err
   239  }
   240  
   241  func (ini *initializer) askEmulator(vmds []vmd.VirtualDevice) error {
   242  	vmdNames, vmdOSVersions := vmdToMaps(vmds)
   243  
   244  	q := &survey.Select{
   245  		Message: "Select emulator:",
   246  		Options: vmdNames,
   247  	}
   248  	err := survey.AskOne(q, &ini.cfg.emulator.Name,
   249  		survey.WithShowCursor(true),
   250  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err))
   251  	if err != nil {
   252  		return err
   253  	}
   254  
   255  	q = &survey.Select{
   256  		Message: "Select platform version:",
   257  		Options: vmdOSVersions[ini.cfg.emulator.Name],
   258  	}
   259  	var emulatorVersion string
   260  	err = survey.AskOne(q, &emulatorVersion,
   261  		survey.WithShowCursor(true),
   262  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err))
   263  	ini.cfg.emulator.PlatformVersions = []string{emulatorVersion}
   264  	return err
   265  }
   266  
   267  // metaToVersions returns a list of versions for a list of meta.
   268  func metaToVersions(metadatas []framework.Metadata) []string {
   269  	var versions []string
   270  	for _, v := range metadatas {
   271  		versions = append(versions, v.FrameworkVersion)
   272  	}
   273  	return versions
   274  }
   275  
   276  // metaToBrowsers returns a sorted list of browsers, and a map containing all supported platform those browsers.
   277  func metaToBrowsers(metadatas []framework.Metadata, frameworkName, frameworkVersion string) ([]string, map[string][]string) {
   278  	var browsers []string
   279  	platforms := map[string][]string{}
   280  
   281  	var platformsToMap []framework.Platform
   282  	for _, v := range metadatas {
   283  		if v.FrameworkVersion == frameworkVersion {
   284  			platformsToMap = v.Platforms
   285  		}
   286  	}
   287  
   288  	for _, p := range platformsToMap {
   289  		p.PlatformName = normalizePlatform(p.PlatformName)
   290  
   291  		if frameworkName == testcafe.Kind && p.PlatformName == "ios" {
   292  			continue
   293  		}
   294  		for _, browserName := range correctBrowsers(p.BrowserNames) {
   295  			if _, ok := platforms[browserName]; !ok {
   296  				browsers = append(browsers, browserName)
   297  				platforms[browserName] = []string{}
   298  			}
   299  			platforms[browserName] = append(platforms[browserName], p.PlatformName)
   300  		}
   301  	}
   302  
   303  	for _, v := range platforms {
   304  		sort.Strings(v)
   305  	}
   306  
   307  	sort.Strings(browsers)
   308  	return browsers, platforms
   309  }
   310  
   311  func normalizePlatform(platform string) string {
   312  	r := strings.NewReplacer("macos", "macOS", "windows", "Windows")
   313  	return r.Replace(platform)
   314  }
   315  
   316  func correctBrowsers(browsers []string) []string {
   317  	var cb []string
   318  	for _, browserName := range browsers {
   319  		cb = append(cb, correctBrowser(browserName))
   320  	}
   321  	return cb
   322  }
   323  
   324  func correctBrowser(browserName string) string {
   325  	switch browserName {
   326  	case "playwright-chromium":
   327  		return "chromium"
   328  	case "playwright-firefox":
   329  		return "firefox"
   330  	case "playwright-webkit":
   331  		return "webkit"
   332  	case "googlechrome":
   333  		return "chrome"
   334  	default:
   335  		return browserName
   336  	}
   337  }
   338  
   339  func (ini *initializer) askPlatform(metadatas []framework.Metadata) error {
   340  	browsers, platforms := metaToBrowsers(metadatas, ini.cfg.frameworkName, ini.cfg.frameworkVersion)
   341  
   342  	// Select browser
   343  	q := &survey.Select{
   344  		Message: "Select browser:",
   345  		Options: browsers,
   346  	}
   347  	err := survey.AskOne(q, &ini.cfg.browserName,
   348  		survey.WithShowCursor(true),
   349  		survey.WithValidator(survey.Required),
   350  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err))
   351  	if err != nil {
   352  		return err
   353  	}
   354  
   355  	q = &survey.Select{
   356  		Message: "Select platform:",
   357  		Options: platforms[ini.cfg.browserName],
   358  	}
   359  	err = survey.AskOne(q, &ini.cfg.platformName,
   360  		survey.WithShowCursor(true),
   361  		survey.WithValidator(survey.Required),
   362  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err))
   363  	if err != nil {
   364  		return err
   365  	}
   366  	return nil
   367  }
   368  
   369  func (ini *initializer) askVersion(metadatas []framework.Metadata) error {
   370  	versions := metaToVersions(metadatas)
   371  
   372  	q := &survey.Select{
   373  		Message: fmt.Sprintf("Select %s version:", ini.cfg.frameworkName),
   374  		Options: versions,
   375  	}
   376  
   377  	err := survey.AskOne(q, &ini.cfg.frameworkVersion,
   378  		survey.WithShowCursor(true),
   379  		survey.WithValidator(survey.Required),
   380  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err))
   381  	if err != nil {
   382  		return err
   383  	}
   384  	return nil
   385  }
   386  
   387  func (ini *initializer) askFile(message string, val survey.Validator, comp completor, targetValue *string) error {
   388  	q := &survey.Input{
   389  		Message: message,
   390  		Suggest: comp,
   391  	}
   392  
   393  	return survey.AskOne(q, targetValue,
   394  		survey.WithShowCursor(true),
   395  		survey.WithValidator(survey.Required),
   396  		survey.WithValidator(val),
   397  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err))
   398  }
   399  
   400  func (ini *initializer) askDockerImage(message string, val survey.Validator, targetValue *string) error {
   401  	q := &survey.Input{
   402  		Message: message,
   403  	}
   404  
   405  	return survey.AskOne(q, targetValue,
   406  		survey.WithShowCursor(true),
   407  		survey.WithValidator(survey.Required),
   408  		survey.WithValidator(val),
   409  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err))
   410  }
   411  
   412  var Workloads = []string{
   413  	"webdriver",
   414  	"other",
   415  }
   416  
   417  func (ini *initializer) askWorkload() error {
   418  	q := &survey.Select{
   419  		Message: "Set workload:",
   420  		Default: Workloads[0],
   421  		Options: Workloads,
   422  	}
   423  	q.WithStdio(ini.stdio)
   424  
   425  	var workload string
   426  	err := survey.AskOne(q, &workload,
   427  		survey.WithShowCursor(true),
   428  		survey.WithValidator(survey.Required),
   429  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err))
   430  	if err != nil {
   431  		return err
   432  	}
   433  	ini.cfg.workload = workload
   434  	return nil
   435  }
   436  
   437  func (ini *initializer) initializeCypress() error {
   438  	frameworkMetadatas, err := ini.infoReader.Versions(context.Background(), ini.cfg.frameworkName)
   439  	if err != nil {
   440  		return err
   441  	}
   442  
   443  	err = ini.askVersion(frameworkMetadatas)
   444  	if err != nil {
   445  		return err
   446  	}
   447  
   448  	err = ini.askFile(
   449  		"Cypress configuration file:",
   450  		frameworkExtValidator(ini.cfg.frameworkName, ini.cfg.frameworkVersion),
   451  		completeBasic,
   452  		&ini.cfg.cypressConfigFile,
   453  	)
   454  	if err != nil {
   455  		return err
   456  	}
   457  
   458  	err = ini.askPlatform(frameworkMetadatas)
   459  	if err != nil {
   460  		return err
   461  	}
   462  
   463  	return ini.askDownloadWhen()
   464  }
   465  
   466  func (ini *initializer) initializePlaywright() error {
   467  	frameworkMetadatas, err := ini.infoReader.Versions(context.Background(), ini.cfg.frameworkName)
   468  	if err != nil {
   469  		return err
   470  	}
   471  
   472  	err = ini.askVersion(frameworkMetadatas)
   473  	if err != nil {
   474  		return err
   475  	}
   476  
   477  	err = ini.askPlatform(frameworkMetadatas)
   478  	if err != nil {
   479  		return err
   480  	}
   481  
   482  	err = survey.AskOne(
   483  		&survey.Input{
   484  			Message: "Playwright project name. " +
   485  				"Leave empty if your configuration does not contain projects:",
   486  			Default: "",
   487  			Help:    "See https://playwright.dev/docs/test-projects",
   488  		},
   489  		&ini.cfg.playwrightProject,
   490  		survey.WithShowCursor(true),
   491  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err),
   492  	)
   493  	if err != nil {
   494  		return err
   495  	}
   496  
   497  	var pattern string
   498  	err = survey.AskOne(
   499  		&survey.Input{
   500  			Message: "Test file pattern to match against:",
   501  			Default: ".*.spec.js",
   502  			Help:    "See https://playwright.dev/docs/test-projects",
   503  		},
   504  		&pattern,
   505  		survey.WithShowCursor(true),
   506  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err),
   507  	)
   508  	if err != nil {
   509  		return err
   510  	}
   511  	ini.cfg.testMatch = []string{pattern}
   512  
   513  	return ini.askDownloadWhen()
   514  }
   515  
   516  func (ini *initializer) initializeTestcafe() error {
   517  	frameworkMetadatas, err := ini.infoReader.Versions(context.Background(), ini.cfg.frameworkName)
   518  	if err != nil {
   519  		return err
   520  	}
   521  
   522  	err = ini.askVersion(frameworkMetadatas)
   523  	if err != nil {
   524  		return err
   525  	}
   526  
   527  	err = ini.askPlatform(frameworkMetadatas)
   528  	if err != nil {
   529  		return err
   530  	}
   531  
   532  	err = ini.askDownloadWhen()
   533  	if err != nil {
   534  		return err
   535  	}
   536  	return nil
   537  }
   538  
   539  func (ini *initializer) initializeEspresso() error {
   540  	err := ini.askFile(
   541  		"Application to test:",
   542  		frameworkExtValidator(ini.cfg.frameworkName, ""),
   543  		completeBasic,
   544  		&ini.cfg.app,
   545  	)
   546  	if err != nil {
   547  		return err
   548  	}
   549  
   550  	err = ini.askFile(
   551  		"Test application:",
   552  		frameworkExtValidator(ini.cfg.frameworkName, ""),
   553  		completeBasic,
   554  		&ini.cfg.testApp,
   555  	)
   556  	if err != nil {
   557  		return err
   558  	}
   559  
   560  	err = ini.askDevice(androidDevicesPatterns)
   561  	if err != nil {
   562  		return err
   563  	}
   564  
   565  	virtualDevices, err := ini.vmdReader.GetVirtualDevices(context.Background(), vmd.AndroidEmulator)
   566  	if err != nil {
   567  		println()
   568  		color.HiRed("saucectl is unable to fetch the emulators list.")
   569  		fmt.Printf("You will be able to choose only in a subset of available emulators.\n")
   570  		fmt.Printf("To get the complete list, check your connection and try again.\n")
   571  		println()
   572  		virtualDevices = fallbackAndroidVirtualDevices
   573  	}
   574  
   575  	err = ini.askEmulator(virtualDevices)
   576  	if err != nil {
   577  		return err
   578  	}
   579  
   580  	err = ini.askDownloadWhen()
   581  	if err != nil {
   582  		return err
   583  	}
   584  	return nil
   585  }
   586  
   587  func (ini *initializer) initializeXCUITest() error {
   588  	q := &survey.Select{
   589  		Message: "Select target:",
   590  		Options: []string{
   591  			"Real Devices",
   592  			"Virtual Devices",
   593  		},
   594  	}
   595  
   596  	var target string
   597  	err := survey.AskOne(q, &target,
   598  		survey.WithShowCursor(true),
   599  		survey.WithStdio(ini.stdio.In, ini.stdio.Out, ini.stdio.Err),
   600  		survey.WithValidator(survey.Required),
   601  	)
   602  	if err != nil {
   603  		return err
   604  	}
   605  
   606  	if target == "Real Devices" {
   607  		err = ini.askDevice(iOSDevicesPatterns)
   608  		if err != nil {
   609  			return err
   610  		}
   611  		err = ini.askFile("Application to test:", extValidator([]string{".ipa", ".app"}), completeBasic, &ini.cfg.app)
   612  		if err != nil {
   613  			return err
   614  		}
   615  
   616  		err = ini.askFile("Test application:", extValidator([]string{".ipa", ".app"}), completeBasic, &ini.cfg.testApp)
   617  		if err != nil {
   618  			return err
   619  		}
   620  	} else if target == "Virtual Devices" {
   621  		virtualDevices, err := ini.vmdReader.GetVirtualDevices(context.Background(), vmd.IOSSimulator)
   622  		if err != nil {
   623  			println()
   624  			color.HiRed("saucectl is unable to fetch the simulators list.")
   625  			fmt.Println("You will be able to choose only in a subset of available simulators.")
   626  			fmt.Println("To get the complete list, check your connection and try again.")
   627  			println()
   628  			virtualDevices = fallbackIOSVirtualDevices
   629  		}
   630  
   631  		err = ini.askSimulator(virtualDevices)
   632  		if err != nil {
   633  			return err
   634  		}
   635  
   636  		err = ini.askFile("Application to test:", extValidator([]string{".zip", ".app"}), completeBasic, &ini.cfg.app)
   637  		if err != nil {
   638  			return err
   639  		}
   640  
   641  		err = ini.askFile("Test application:", extValidator([]string{".zip", ".app"}), completeBasic, &ini.cfg.testApp)
   642  		if err != nil {
   643  			return err
   644  		}
   645  	}
   646  
   647  	err = ini.askDownloadWhen()
   648  	if err != nil {
   649  		return err
   650  	}
   651  
   652  	return nil
   653  }
   654  
   655  func (ini *initializer) initializeImageRunner() error {
   656  	if err := ini.askDockerImage(
   657  		"Docker Image to use:",
   658  		dockerImageValidator(),
   659  		&ini.cfg.dockerImage,
   660  	); err != nil {
   661  		return err
   662  	}
   663  
   664  	if err := ini.askWorkload(); err != nil {
   665  		return err
   666  	}
   667  
   668  	return ini.askDownloadWhen()
   669  }
   670  
   671  func checkFrameworkVersion(metadatas []framework.Metadata, frameworkName, frameworkVersion string) error {
   672  	var supported []string
   673  	for _, fm := range metadatas {
   674  		if fm.FrameworkVersion == frameworkVersion {
   675  			return nil
   676  		}
   677  		supported = append(supported, fm.FrameworkVersion)
   678  	}
   679  	return fmt.Errorf("%s %s is not supported. Supported versions are: %s", frameworkName, frameworkVersion, strings.Join(supported, ", "))
   680  }
   681  
   682  func checkBrowserAndPlatform(metadatas []framework.Metadata, frameworkName, frameworkVersion, browserName, platformName string) error {
   683  	browsers, platforms := metaToBrowsers(metadatas, frameworkName, frameworkVersion)
   684  	if ok := sliceContainsString(browsers, browserName); !ok {
   685  		return fmt.Errorf("%s: unsupported browser. Supported browsers are: %s", browserName, strings.Join(browsers, ", "))
   686  	}
   687  	if ok := sliceContainsString(platforms[browserName], platformName); !ok {
   688  		return fmt.Errorf("%s: unsupported browser on %s", browserName, platformName)
   689  	}
   690  	return nil
   691  }
   692  
   693  func checkArtifactDownloadSetting(when string) (config.When, error) {
   694  	switch when {
   695  	case "pass":
   696  		return config.WhenPass, nil
   697  	case "fail":
   698  		return config.WhenFail, nil
   699  	case "always":
   700  		return config.WhenAlways, nil
   701  	case "never":
   702  		return config.WhenNever, nil
   703  	default:
   704  		return "", fmt.Errorf("%s: unknown download condition", when)
   705  	}
   706  }
   707  
   708  func checkEmulators(vmds []vmd.VirtualDevice, emulator config.Emulator) (config.Emulator, []error) {
   709  	var errs []error
   710  
   711  	d := vmd.VirtualDevice{}
   712  	for _, dev := range vmds {
   713  		if strings.EqualFold(dev.Name, emulator.Name) {
   714  			d = dev
   715  			break
   716  		}
   717  	}
   718  	if d.Name == "" {
   719  		return config.Emulator{}, []error{fmt.Errorf("emulator: %s does not exists", emulator.Name)}
   720  	}
   721  	for _, p := range emulator.PlatformVersions {
   722  		if !sliceContainsString(d.OSVersion, p) {
   723  			errs = append(errs, fmt.Errorf("emulator: %s does not support platform %s", emulator.Name, p))
   724  		}
   725  	}
   726  	if len(errs) > 0 {
   727  		return config.Emulator{}, errs
   728  	}
   729  	return config.Emulator{
   730  		Name:             d.Name,
   731  		PlatformVersions: emulator.PlatformVersions,
   732  		PlatformName:     emulator.PlatformName,
   733  		Orientation:      emulator.Orientation,
   734  	}, []error{}
   735  }
   736  
   737  func (ini *initializer) initializeBatchCypress() []error {
   738  	var errs []error
   739  
   740  	if ini.cfg.frameworkVersion == "" {
   741  		errs = append(errs, fmt.Errorf(msg.MissingFrameworkVersion, ini.cfg.frameworkName))
   742  	}
   743  	if ini.cfg.cypressConfigFile == "" {
   744  		errs = append(errs, errors.New(msg.MissingCypressConfig))
   745  	}
   746  	if ini.cfg.platformName == "" {
   747  		errs = append(errs, errors.New(msg.MissingPlatformName))
   748  	}
   749  	if ini.cfg.browserName == "" {
   750  		errs = append(errs, errors.New(msg.MissingBrowserName))
   751  	}
   752  
   753  	frameworkMetadatas, err := ini.infoReader.Versions(context.Background(), ini.cfg.frameworkName)
   754  	if err != nil {
   755  		errs = append(errs, err)
   756  		return errs
   757  	}
   758  
   759  	frameworkVersionSupported := true
   760  	if ini.cfg.frameworkVersion != "" {
   761  		if err = checkFrameworkVersion(frameworkMetadatas, ini.cfg.frameworkName, ini.cfg.frameworkVersion); err != nil {
   762  			errs = append(errs, err)
   763  			frameworkVersionSupported = false
   764  		}
   765  	}
   766  
   767  	if ini.cfg.cypressConfigFile != "" {
   768  		verifier := frameworkExtValidator(ini.cfg.frameworkName, "")
   769  		if err := verifier(ini.cfg.cypressConfigFile); err != nil {
   770  			errs = append(errs, err)
   771  		}
   772  	}
   773  
   774  	if frameworkVersionSupported && ini.cfg.platformName != "" && ini.cfg.browserName != "" {
   775  		ini.cfg.browserName = strings.ToLower(ini.cfg.browserName)
   776  		if err = checkBrowserAndPlatform(
   777  			frameworkMetadatas,
   778  			ini.cfg.frameworkName,
   779  			ini.cfg.frameworkVersion,
   780  			ini.cfg.browserName,
   781  			ini.cfg.platformName,
   782  		); err != nil {
   783  			errs = append(errs, err)
   784  		}
   785  	}
   786  
   787  	if ini.cfg.artifactWhenStr != "" {
   788  		ini.cfg.artifactWhenStr = strings.ToLower(ini.cfg.artifactWhenStr)
   789  		if ini.cfg.artifactWhen, err = checkArtifactDownloadSetting(ini.cfg.artifactWhenStr); err != nil {
   790  			errs = append(errs, err)
   791  		}
   792  	}
   793  	return errs
   794  }
   795  
   796  func (ini *initializer) initializeBatchEspresso(f *pflag.FlagSet) []error {
   797  	var errs []error
   798  	var err error
   799  
   800  	if ini.cfg.app == "" {
   801  		errs = append(errs, errors.New(msg.MissingApp))
   802  	}
   803  	if ini.cfg.testApp == "" {
   804  		errs = append(errs, errors.New(msg.MissingTestApp))
   805  	}
   806  	if !f.Changed("device") && !f.Changed("emulator") {
   807  		errs = append(errs, errors.New(msg.MissingDeviceOrEmulator))
   808  	}
   809  	if ini.cfg.artifactWhenStr != "" {
   810  		ini.cfg.artifactWhenStr = strings.ToLower(ini.cfg.artifactWhenStr)
   811  		if ini.cfg.artifactWhen, err = checkArtifactDownloadSetting(ini.cfg.artifactWhenStr); err != nil {
   812  			errs = append(errs, err)
   813  		}
   814  	}
   815  
   816  	if ini.cfg.app != "" {
   817  		verifier := frameworkExtValidator(ini.cfg.frameworkName, "")
   818  		if err = verifier(ini.cfg.app); err != nil {
   819  			errs = append(errs, fmt.Errorf("app: %s", err))
   820  		}
   821  	}
   822  	if ini.cfg.testApp != "" {
   823  		verifier := frameworkExtValidator(ini.cfg.frameworkName, "")
   824  		if err = verifier(ini.cfg.app); err != nil {
   825  			errs = append(errs, fmt.Errorf("testApp: %s", err))
   826  		}
   827  	}
   828  	if f.Changed("emulator") {
   829  		emulators, err := ini.vmdReader.GetVirtualDevices(context.Background(), vmd.AndroidEmulator)
   830  		if err != nil {
   831  			errs = append(errs, fmt.Errorf(""))
   832  		}
   833  		var lerrs []error
   834  		if ini.cfg.emulator, lerrs = checkEmulators(emulators, ini.cfg.emulatorFlag.Emulator); len(lerrs) > 0 {
   835  			errs = append(errs, lerrs...)
   836  		}
   837  	}
   838  	if f.Changed("device") {
   839  		ini.cfg.device = ini.cfg.deviceFlag.Device
   840  	}
   841  	return errs
   842  }
   843  
   844  func (ini *initializer) initializeBatchPlaywright() []error {
   845  	var errs []error
   846  
   847  	if ini.cfg.frameworkVersion == "" {
   848  		errs = append(errs, fmt.Errorf(msg.MissingFrameworkVersion, ini.cfg.frameworkName))
   849  	}
   850  	if ini.cfg.platformName == "" {
   851  		errs = append(errs, errors.New(msg.MissingPlatformName))
   852  	}
   853  	if ini.cfg.browserName == "" {
   854  		errs = append(errs, errors.New(msg.MissingBrowserName))
   855  	}
   856  
   857  	frameworkMetadatas, err := ini.infoReader.Versions(context.Background(), ini.cfg.frameworkName)
   858  	if err != nil {
   859  		errs = append(errs, err)
   860  		return errs
   861  	}
   862  
   863  	frameworkVersionSupported := true
   864  	if ini.cfg.frameworkVersion != "" {
   865  		if err = checkFrameworkVersion(frameworkMetadatas, ini.cfg.frameworkName, ini.cfg.frameworkVersion); err != nil {
   866  			errs = append(errs, err)
   867  			frameworkVersionSupported = false
   868  		}
   869  	}
   870  
   871  	if frameworkVersionSupported && ini.cfg.platformName != "" && ini.cfg.browserName != "" {
   872  		ini.cfg.browserName = strings.ToLower(ini.cfg.browserName)
   873  		if err = checkBrowserAndPlatform(
   874  			frameworkMetadatas,
   875  			ini.cfg.frameworkName,
   876  			ini.cfg.frameworkVersion,
   877  			ini.cfg.browserName,
   878  			ini.cfg.platformName,
   879  		); err != nil {
   880  			errs = append(errs, err)
   881  		}
   882  	}
   883  
   884  	if ini.cfg.artifactWhenStr != "" {
   885  		ini.cfg.artifactWhenStr = strings.ToLower(ini.cfg.artifactWhenStr)
   886  		if ini.cfg.artifactWhen, err = checkArtifactDownloadSetting(ini.cfg.artifactWhenStr); err != nil {
   887  			errs = append(errs, err)
   888  		}
   889  	}
   890  	return errs
   891  }
   892  
   893  func (ini *initializer) initializeBatchTestcafe() []error {
   894  	var errs []error
   895  
   896  	if ini.cfg.frameworkVersion == "" {
   897  		errs = append(errs, fmt.Errorf(msg.MissingFrameworkVersion, ini.cfg.frameworkName))
   898  	}
   899  	if ini.cfg.platformName == "" {
   900  		errs = append(errs, errors.New(msg.MissingPlatformName))
   901  	}
   902  	if ini.cfg.browserName == "" {
   903  		errs = append(errs, errors.New(msg.MissingBrowserName))
   904  	}
   905  
   906  	frameworkMetadatas, err := ini.infoReader.Versions(context.Background(), ini.cfg.frameworkName)
   907  	if err != nil {
   908  		errs = append(errs, err)
   909  		return errs
   910  	}
   911  
   912  	frameworkVersionSupported := true
   913  	if ini.cfg.frameworkVersion != "" {
   914  		if err = checkFrameworkVersion(frameworkMetadatas, ini.cfg.frameworkName, ini.cfg.frameworkVersion); err != nil {
   915  			errs = append(errs, err)
   916  			frameworkVersionSupported = false
   917  		}
   918  	}
   919  
   920  	if frameworkVersionSupported && ini.cfg.platformName != "" && ini.cfg.browserName != "" {
   921  		ini.cfg.browserName = strings.ToLower(ini.cfg.browserName)
   922  		if err = checkBrowserAndPlatform(
   923  			frameworkMetadatas,
   924  			ini.cfg.frameworkName,
   925  			ini.cfg.frameworkVersion,
   926  			ini.cfg.browserName,
   927  			ini.cfg.platformName,
   928  		); err != nil {
   929  			errs = append(errs, err)
   930  		}
   931  	}
   932  
   933  	if ini.cfg.artifactWhenStr != "" {
   934  		ini.cfg.artifactWhenStr = strings.ToLower(ini.cfg.artifactWhenStr)
   935  		if ini.cfg.artifactWhen, err = checkArtifactDownloadSetting(ini.cfg.artifactWhenStr); err != nil {
   936  			errs = append(errs, err)
   937  		}
   938  	}
   939  	return errs
   940  }
   941  
   942  func (ini *initializer) initializeBatchXcuitest(f *pflag.FlagSet) []error {
   943  	var errs []error
   944  	var err error
   945  
   946  	if ini.cfg.app == "" {
   947  		errs = append(errs, errors.New(msg.MissingApp))
   948  	}
   949  	if ini.cfg.testApp == "" {
   950  		errs = append(errs, errors.New(msg.MissingTestApp))
   951  	}
   952  	if !(f.Changed("simulator") || f.Changed("device")) {
   953  		errs = append(errs, errors.New(msg.MissingDeviceOrSimulator))
   954  	}
   955  	if ini.cfg.artifactWhenStr != "" {
   956  		ini.cfg.artifactWhenStr = strings.ToLower(ini.cfg.artifactWhenStr)
   957  		if ini.cfg.artifactWhen, err = checkArtifactDownloadSetting(ini.cfg.artifactWhenStr); err != nil {
   958  			errs = append(errs, err)
   959  		}
   960  	}
   961  	validExt := []string{".app"}
   962  	if f.Changed("simulator") {
   963  		validExt = append(validExt, ".zip")
   964  	} else {
   965  		validExt = append(validExt, ".ipa")
   966  	}
   967  	if ini.cfg.app != "" {
   968  		verifier := extValidator(validExt)
   969  		if err = verifier(ini.cfg.app); err != nil {
   970  			errs = append(errs, fmt.Errorf("app: %s", err))
   971  		}
   972  	}
   973  	if ini.cfg.testApp != "" {
   974  		verifier := extValidator(validExt)
   975  		if err = verifier(ini.cfg.app); err != nil {
   976  			errs = append(errs, fmt.Errorf("testApp: %s", err))
   977  		}
   978  	}
   979  	if f.Changed("device") {
   980  		ini.cfg.device = ini.cfg.deviceFlag.Device
   981  	}
   982  	if f.Changed("simulator") {
   983  		ini.cfg.simulator = ini.cfg.simulatorFlag.Simulator
   984  	}
   985  	return errs
   986  }
   987  
   988  func (ini *initializer) initializeBatchImageRunner() []error {
   989  	var errs []error
   990  	var err error
   991  
   992  	if ini.cfg.dockerImage == "" {
   993  		errs = append(errs, errors.New(msg.MissingDockerImage))
   994  	}
   995  	if ini.cfg.dockerImage != "" {
   996  		verifier := dockerImageValidator()
   997  		if err = verifier(ini.cfg.dockerImage); err != nil {
   998  			errs = append(errs, fmt.Errorf("dockerImage: %s", err))
   999  		}
  1000  	}
  1001  	if ini.cfg.artifactWhenStr != "" {
  1002  		ini.cfg.artifactWhenStr = strings.ToLower(ini.cfg.artifactWhenStr)
  1003  		if ini.cfg.artifactWhen, err = checkArtifactDownloadSetting(ini.cfg.artifactWhenStr); err != nil {
  1004  			errs = append(errs, err)
  1005  		}
  1006  	}
  1007  	return errs
  1008  }