diff --git a/api/saucectl.schema.json b/api/saucectl.schema.json index 6a92503f7..d78fbcb20 100644 --- a/api/saucectl.schema.json +++ b/api/saucectl.schema.json @@ -2314,6 +2314,10 @@ "description": "The set of one or more versions of the device platform on which to run the test suite.", "type": "array", "minItems": 1 + }, + "armRequired": { + "description": "If set to true, the simulator will run on an ARM-based Mac. If set to false, the simulator will run on an Intel-based Mac.", + "type": "boolean" } }, "required": [ diff --git a/api/v1alpha/framework/xcuitest.schema.json b/api/v1alpha/framework/xcuitest.schema.json index 73351cf1d..1d7fccee6 100644 --- a/api/v1alpha/framework/xcuitest.schema.json +++ b/api/v1alpha/framework/xcuitest.schema.json @@ -157,6 +157,10 @@ "description": "The set of one or more versions of the device platform on which to run the test suite.", "type": "array", "minItems": 1 + }, + "armRequired": { + "description": "If set to true, the simulator will run on an ARM-based Mac. If set to false, the simulator will run on an Intel-based Mac.", + "type": "boolean" } }, "required": [ diff --git a/internal/config/config.go b/internal/config/config.go index d745e8c99..e57a552a7 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -71,6 +71,7 @@ type VirtualDevice struct { PlatformName string `yaml:"platformName,omitempty" json:"platformName"` Orientation string `yaml:"orientation,omitempty" json:"orientation,omitempty"` PlatformVersions []string `yaml:"platformVersions,omitempty" json:"platformVersions,omitempty"` + ARMRequired bool `yaml:"armRequired,omitempty" json:"armRequired,omitempty"` } const ( diff --git a/internal/flags/simulator.go b/internal/flags/simulator.go index 7ee6ca295..7e2c90ba8 100644 --- a/internal/flags/simulator.go +++ b/internal/flags/simulator.go @@ -4,6 +4,7 @@ import ( "encoding/csv" "errors" "fmt" + "strconv" "strings" "github.com/saucelabs/saucectl/internal/config" @@ -16,8 +17,8 @@ type Simulator struct { Changed bool } -// String returns a string represenation of the simulator. -func (e Simulator) String() string { +// String returns a string representation of the simulator. +func (e *Simulator) String() string { if !e.Changed { return "" } @@ -54,6 +55,11 @@ func (e *Simulator) Set(s string) error { e.Orientation = val case "platformVersion": e.PlatformVersions = []string{val} + case "armRequired": + e.ARMRequired, err = strconv.ParseBool(val) + if err != nil { + return err + } } } @@ -61,6 +67,6 @@ func (e *Simulator) Set(s string) error { } // Type returns the value type. -func (e Simulator) Type() string { +func (e *Simulator) Type() string { return "simulator" } diff --git a/internal/http/webdriver.go b/internal/http/webdriver.go index 70120891a..fb360f821 100644 --- a/internal/http/webdriver.go +++ b/internal/http/webdriver.go @@ -60,10 +60,13 @@ type SauceOpts struct { TunnelIdentifier string `json:"tunnelIdentifier,omitempty"` TunnelParent string `json:"parentTunnel,omitempty"` // note that 'parentTunnel` is backwards, because that's the way sauce likes it ScreenResolution string `json:"screen_resolution,omitempty"` - SauceCloudNode string `json:"_sauceCloudNode,omitempty"` UserAgent string `json:"user_agent,omitempty"` TimeZone string `json:"timeZone,omitempty"` Visibility string `json:"public,omitempty"` + + // VMD specific settings. + + ARMRequired bool `json:"armRequired,omitempty"` } type env struct { @@ -135,7 +138,6 @@ func (c *Webdriver) StartJob(ctx context.Context, opts job.StartOptions) (jobID TunnelIdentifier: opts.Tunnel.ID, TunnelParent: opts.Tunnel.Parent, ScreenResolution: opts.ScreenResolution, - SauceCloudNode: opts.Experiments["_sauceCloudNode"], TestName: opts.Name, BuildName: opts.Build, Tags: opts.Tags, @@ -152,6 +154,7 @@ func (c *Webdriver) StartJob(ctx context.Context, opts job.StartOptions) (jobID MaxDuration: 10800, TimeZone: opts.TimeZone, Visibility: opts.Visibility, + ARMRequired: opts.ARMRequired, }, DeviceName: opts.DeviceName, DeviceOrientation: opts.DeviceOrientation, diff --git a/internal/job/starter.go b/internal/job/starter.go index 68edef852..65882a492 100644 --- a/internal/job/starter.go +++ b/internal/job/starter.go @@ -16,56 +16,84 @@ type StartOptions struct { PrevAttempts []report.Attempt `json:"-"` // Timeout is used for local/per-suite timeout. - Timeout time.Duration `json:"-"` - - User string `json:"username"` - AccessKey string `json:"accessKey"` - App string `json:"app,omitempty"` - TestApp string `json:"testApp,omitempty"` - Suite string `json:"suite,omitempty"` - OtherApps []string `json:"otherApps,omitempty"` - Framework string `json:"framework,omitempty"` - ConfigFilePath string `json:"-"` - CLIFlags map[string]interface{} `json:"-"` + Timeout time.Duration `json:"-"` + StartTime time.Time `json:"startTime,omitempty"` + + User string `json:"username"` + AccessKey string `json:"accessKey"` + + App string `json:"app,omitempty"` + OtherApps []string `json:"otherApps,omitempty"` - // FrameworkVersion contains the targeted version of the framework - // It should not be confused with automation tool (like jest/folio). - // This is currently supported only for frameworks available on Sauce Cloud: - // Currently supported: Cypress. + Suite string `json:"suite,omitempty"` + + // FrameworkVersion contains the targeted version of the framework. + // It should not be confused with RunnerVersion. FrameworkVersion string `json:"frameworkVersion,omitempty"` + Framework string `json:"framework,omitempty"` + + PlatformName string `json:"platformName,omitempty"` + PlatformVersion string `json:"platformVersion,omitempty"` + + Tunnel TunnelOptions `json:"tunnel,omitempty"` + + Experiments map[string]string `json:"experiments,omitempty"` + + // Job Metadata. + + Name string `json:"name,omitempty"` + Build string `json:"build,omitempty"` + Tags []string `json:"tags,omitempty"` + + // Job Access Control. + + Visibility string `json:"public,omitempty"` + + // Thresholds & Retries. - Attempt int `json:"-"` - CurrentPassCount int `json:"-"` - BrowserName string `json:"browserName,omitempty"` - BrowserVersion string `json:"browserVersion,omitempty"` - PlatformName string `json:"platformName,omitempty"` - PlatformVersion string `json:"platformVersion,omitempty"` - DeviceID string `json:"deviceId,omitempty"` + Attempt int `json:"-"` + CurrentPassCount int `json:"-"` + PassThreshold int `json:"-"` + + Retries int `json:"-"` + SmartRetry SmartRetry `json:"-"` + + // Cypress & Playwright & TestCafe only. + + BrowserName string `json:"browserName,omitempty"` + BrowserVersion string `json:"browserVersion,omitempty"` + TimeZone string `json:"timeZone,omitempty"` + RunnerVersion string `json:"runnerVersion,omitempty"` + ScreenResolution string `json:"screenResolution,omitempty"` + + // RDC & VMD only. + + TestApp string `json:"testApp,omitempty"` DeviceName string `json:"deviceName,omitempty"` DeviceOrientation string `json:"deviceOrientation"` - DevicePrivateOnly bool `json:"devicePrivateOnly,omitempty"` - DeviceType string `json:"deviceType,omitempty"` - DeviceHasCarrier bool `json:"deviceHasCarrier,omitempty"` - RealDevice bool `json:"realDevice,omitempty"` - Name string `json:"name,omitempty"` - Build string `json:"build,omitempty"` - Tags []string `json:"tags,omitempty"` - Tunnel TunnelOptions `json:"tunnel,omitempty"` - ScreenResolution string `json:"screenResolution,omitempty"` - Retries int `json:"-"` - PassThreshold int `json:"-"` - SmartRetry SmartRetry `json:"-"` - RunnerVersion string `json:"runnerVersion,omitempty"` - Experiments map[string]string `json:"experiments,omitempty"` TestOptions map[string]interface{} `json:"testOptions,omitempty"` - TestsToRun []string `json:"testsToRun,omitempty"` - TestsToSkip []string `json:"testsToSkip,omitempty"` - StartTime time.Time `json:"startTime,omitempty"` - AppSettings AppSettings `json:"appSettings,omitempty"` - RealDeviceKind string `json:"realDeviceKind,omitempty"` - TimeZone string `json:"timeZone,omitempty"` - Visibility string `json:"public,omitempty"` - Env map[string]string `json:"-"` + + // RDC only. + + AppSettings AppSettings `json:"appSettings,omitempty"` + DeviceID string `json:"deviceId,omitempty"` + DeviceHasCarrier bool `json:"deviceHasCarrier,omitempty"` + DevicePrivateOnly bool `json:"devicePrivateOnly,omitempty"` + DeviceType string `json:"deviceType,omitempty"` + RealDevice bool `json:"realDevice,omitempty"` + TestsToRun []string `json:"testsToRun,omitempty"` + TestsToSkip []string `json:"testsToSkip,omitempty"` + RealDeviceKind string `json:"realDeviceKind,omitempty"` + + // VMD specific settings. + + ARMRequired bool `json:"armRequired,omitempty"` + Env map[string]string `json:"-"` + + // CLI. + + ConfigFilePath string `json:"-"` + CLIFlags map[string]interface{} `json:"-"` } // AppSettings represents app settings for real device diff --git a/internal/saucecloud/cloud.go b/internal/saucecloud/cloud.go index a4c00ac86..0dddf7db4 100644 --- a/internal/saucecloud/cloud.go +++ b/internal/saucecloud/cloud.go @@ -22,7 +22,6 @@ import ( "github.com/saucelabs/saucectl/internal/apps" "github.com/saucelabs/saucectl/internal/build" "github.com/saucelabs/saucectl/internal/config" - "github.com/saucelabs/saucectl/internal/espresso" "github.com/saucelabs/saucectl/internal/framework" "github.com/saucelabs/saucectl/internal/hashio" "github.com/saucelabs/saucectl/internal/iam" @@ -284,11 +283,6 @@ func (r *CloudRunner) runJob(opts job.StartOptions) (j job.Job, skipped bool, er return job.Job{}, r.interrupted, fmt.Errorf("failed to retrieve job status for suite %s: %s", opts.DisplayName, err.Error()) } - // Enrich RDC data - if opts.RealDevice { - enrichRDCReport(&j, opts) - } - // Check timeout if j.TimedOut { log.Error(). @@ -312,21 +306,6 @@ func (r *CloudRunner) runJob(opts job.StartOptions) (j job.Job, skipped bool, er return j, false, nil } -// enrichRDCReport added the fields from the opts as the API does not provides it. -func enrichRDCReport(j *job.Job, opts job.StartOptions) { - switch opts.Framework { - case "espresso": - j.OS = espresso.Android - } - - if opts.DeviceID != "" { - j.DeviceName = opts.DeviceID - } else { - j.DeviceName = opts.DeviceName - j.OSVersion = opts.PlatformVersion - } -} - func (r *CloudRunner) runJobs(jobOpts chan job.StartOptions, results chan<- result) { for opts := range jobOpts { start := time.Now() diff --git a/internal/saucecloud/espresso.go b/internal/saucecloud/espresso.go index 52a1b3a9e..d3b308067 100644 --- a/internal/saucecloud/espresso.go +++ b/internal/saucecloud/espresso.go @@ -23,6 +23,7 @@ type deviceConfig struct { hasCarrier bool deviceType string privateOnly bool + armRequired bool } // EspressoRunner represents the Sauce Labs cloud implementation for cypress. @@ -156,6 +157,7 @@ func enumerateDevices(devices []config.Device, virtualDevices []config.VirtualDe platformName: e.PlatformName, platformVersion: p, orientation: e.Orientation, + armRequired: e.ARMRequired, }) } } diff --git a/internal/saucecloud/xcuitest.go b/internal/saucecloud/xcuitest.go index 50cc79c89..fb4de66c4 100644 --- a/internal/saucecloud/xcuitest.go +++ b/internal/saucecloud/xcuitest.go @@ -185,7 +185,10 @@ func (r *XcuitestRunner) runSuites() bool { go func() { for _, s := range suites { for _, d := range enumerateDevices(s.Devices, s.Simulators) { - log.Debug().Str("suite", s.Name).Str("deviceName", d.name).Str("deviceID", d.ID).Str("platformVersion", d.platformVersion).Msg("Starting job") + log.Debug().Str("suite", s.Name). + Str("deviceName", d.name).Str("deviceID", d.ID). + Str("platformVersion", d.platformVersion). + Msg("Starting job") r.startJob(jobOpts, s.App, s.TestApp, s.OtherApps, s, d) } } @@ -235,7 +238,8 @@ func (r *XcuitestRunner) startJob(jobOpts chan<- job.StartOptions, appFileID, te DevicePrivateOnly: d.privateOnly, // VMD specific settings - Env: s.Env, + Env: s.Env, + ARMRequired: d.armRequired, // Overwrite device settings RealDeviceKind: strings.ToLower(xcuitest.IOS),