mirror of
https://github.com/httprunner/httprunner.git
synced 2026-06-26 01:51:29 +08:00
refactor: TestCase/TestCaseDef/StepConfig models
This commit is contained in:
@@ -357,7 +357,7 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
|
||||
startTime := time.Now()
|
||||
for _, step := range testcase.TestSteps {
|
||||
// parse step struct
|
||||
err = sessionRunner.parseStepStruct(step)
|
||||
err = sessionRunner.parseStep(step)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("parse step struct failed")
|
||||
}
|
||||
@@ -406,8 +406,9 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
|
||||
if stepResult.StepType == stepTypeTransaction {
|
||||
// transaction
|
||||
// FIXME: support nested transactions
|
||||
if step.Struct().Transaction.Type == transactionEnd { // only record when transaction ends
|
||||
b.RecordTransaction(step.Struct().Transaction.Name, transactionSuccess, stepResult.Elapsed, 0)
|
||||
stepTransaction := step.(*StepTransaction)
|
||||
if stepTransaction.Transaction.Type == transactionEnd { // only record when transaction ends
|
||||
b.RecordTransaction(stepTransaction.Name(), transactionSuccess, stepResult.Elapsed, 0)
|
||||
transactionSuccess = true // reset flag for next transaction
|
||||
}
|
||||
} else if stepResult.StepType == stepTypeRendezvous {
|
||||
|
||||
@@ -16,8 +16,7 @@ func NewConfig(name string) *TConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// TConfig represents config data structure for testcase.
|
||||
// Each testcase should contain one config part.
|
||||
// define struct for testcase config
|
||||
type TConfig struct {
|
||||
Name string `json:"name" yaml:"name"` // required
|
||||
Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"`
|
||||
|
||||
@@ -1 +1 @@
|
||||
v5.0.0+2411072100
|
||||
v5.0.0+2411092015
|
||||
|
||||
@@ -51,8 +51,8 @@ func TestLoadTestCases(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadCase(t *testing.T) {
|
||||
tcJSON := &TestCase{}
|
||||
tcYAML := &TestCase{}
|
||||
tcJSON := &TestCaseDef{}
|
||||
tcYAML := &TestCaseDef{}
|
||||
err := LoadFileObject(demoTestCaseWithPluginJSONPath, tcJSON)
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal()
|
||||
@@ -68,10 +68,10 @@ func TestLoadCase(t *testing.T) {
|
||||
if !assert.Equal(t, tcJSON.Config.BaseURL, tcYAML.Config.BaseURL) {
|
||||
t.Fatal()
|
||||
}
|
||||
if !assert.Equal(t, tcJSON.Steps[1].Name, tcYAML.Steps[1].Name) {
|
||||
if !assert.Equal(t, tcJSON.Steps[1].StepName, tcYAML.Steps[1].StepName) {
|
||||
t.Fatal()
|
||||
}
|
||||
if !assert.Equal(t, tcJSON.Steps[1].Request, tcYAML.Steps[1].Request) {
|
||||
if !assert.Equal(t, tcJSON.Steps[1].Request, tcJSON.Steps[1].Request) {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
|
||||
70
hrp/models.go
Normal file
70
hrp/models.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package hrp
|
||||
|
||||
// define struct for testcase
|
||||
type TestCaseDef struct {
|
||||
Config *TConfig `json:"config" yaml:"config"`
|
||||
Steps []*TStep `json:"teststeps" yaml:"teststeps"`
|
||||
}
|
||||
|
||||
type StepConfig struct {
|
||||
StepName string `json:"name" yaml:"name"` // required
|
||||
Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"`
|
||||
SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"`
|
||||
TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"`
|
||||
Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"`
|
||||
Validators []interface{} `json:"validate,omitempty" yaml:"validate,omitempty"`
|
||||
StepExport []string `json:"export,omitempty" yaml:"export,omitempty"`
|
||||
Loops int `json:"loops,omitempty" yaml:"loops,omitempty"`
|
||||
IgnorePopup bool `json:"ignore_popup,omitempty" yaml:"ignore_popup,omitempty"`
|
||||
}
|
||||
|
||||
// define struct for teststep
|
||||
type TStep struct {
|
||||
StepConfig `json:",inline" yaml:",inline"`
|
||||
Request *Request `json:"request,omitempty" yaml:"request,omitempty"`
|
||||
API interface{} `json:"api,omitempty" yaml:"api,omitempty"` // *APIPath or *API
|
||||
TestCase interface{} `json:"testcase,omitempty" yaml:"testcase,omitempty"` // *TestCasePath or *TestCase
|
||||
Transaction *Transaction `json:"transaction,omitempty" yaml:"transaction,omitempty"`
|
||||
Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"`
|
||||
ThinkTime *ThinkTime `json:"think_time,omitempty" yaml:"think_time,omitempty"`
|
||||
WebSocket *WebSocketAction `json:"websocket,omitempty" yaml:"websocket,omitempty"`
|
||||
Android *MobileUI `json:"android,omitempty" yaml:"android,omitempty"`
|
||||
Harmony *MobileUI `json:"harmony,omitempty" yaml:"harmony,omitempty"`
|
||||
IOS *MobileUI `json:"ios,omitempty" yaml:"ios,omitempty"`
|
||||
Shell *Shell `json:"shell,omitempty" yaml:"shell,omitempty"`
|
||||
}
|
||||
|
||||
// one step contains one or multiple actions
|
||||
type ActionResult struct {
|
||||
Name string `json:"name"` // action name
|
||||
StartTime int64 `json:"start_time"` // action start time
|
||||
Elapsed int64 `json:"elapsed_ms"` // action elapsed time(ms)
|
||||
Error error `json:"error"` // action execution result
|
||||
}
|
||||
|
||||
// one testcase contains one or multiple steps
|
||||
type StepResult struct {
|
||||
Name string `json:"name" yaml:"name"` // step name
|
||||
Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty"` // step identifier
|
||||
StartTime int64 `json:"start_time" yaml:"time"` // step start time
|
||||
StepType StepType `json:"step_type" yaml:"step_type"` // step type, testcase/request/transaction/rendezvous
|
||||
Success bool `json:"success" yaml:"success"` // step execution result
|
||||
Elapsed int64 `json:"elapsed_ms" yaml:"elapsed_ms"` // step execution time in millisecond(ms)
|
||||
HttpStat map[string]int64 `json:"httpstat,omitempty" yaml:"httpstat,omitempty"` // httpstat in millisecond(ms)
|
||||
Data interface{} `json:"data,omitempty" yaml:"data,omitempty"` // step data
|
||||
ContentSize int64 `json:"content_size" yaml:"content_size"` // response body length
|
||||
ExportVars map[string]interface{} `json:"export_vars,omitempty" yaml:"export_vars,omitempty"` // extract variables
|
||||
Actions []*ActionResult `json:"actions,omitempty" yaml:"actions,omitempty"` // store action execution info
|
||||
Attachments interface{} `json:"attachments,omitempty" yaml:"attachments,omitempty"` // store extra step information, such as error message or screenshots
|
||||
}
|
||||
|
||||
// IStep represents interface for all types for teststeps, includes:
|
||||
// StepRequest, StepRequestWithOptionalArgs, StepRequestValidation, StepRequestExtraction,
|
||||
// StepTestCaseWithOptionalArgs,
|
||||
// StepTransaction, StepRendezvous, StepWebSocket.
|
||||
type IStep interface {
|
||||
Name() string
|
||||
Type() StepType
|
||||
Config() *StepConfig
|
||||
Run(*SessionRunner) (*StepResult, error)
|
||||
}
|
||||
@@ -94,12 +94,12 @@ func init() {
|
||||
}
|
||||
|
||||
// LoadCurlCase loads testcase from one or more curl commands in .txt file
|
||||
func LoadCurlCase(path string) (*hrp.TestCase, error) {
|
||||
func LoadCurlCase(path string) (*hrp.TestCaseDef, error) {
|
||||
cmds, err := readFileLines(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tCase := &hrp.TestCase{
|
||||
tCase := &hrp.TestCaseDef{
|
||||
Config: &hrp.TConfig{
|
||||
Name: "testcase converted from curl command",
|
||||
},
|
||||
@@ -307,7 +307,7 @@ type stepFromCurl struct {
|
||||
}
|
||||
|
||||
func (s *stepFromCurl) makeRequestName(c CaseCurl) error {
|
||||
s.Name = c.Get(originCmdKey, 0)
|
||||
s.StepName = c.Get(originCmdKey, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestLoadCurlCase(t *testing.T) {
|
||||
}
|
||||
|
||||
// curl httpbin.org
|
||||
if !assert.Equal(t, "curl httpbin.org", tCase.Steps[0].Name) {
|
||||
if !assert.Equal(t, "curl httpbin.org", tCase.Steps[0].StepName) {
|
||||
t.Fatal()
|
||||
}
|
||||
if !assert.EqualValues(t, "GET", tCase.Steps[0].Request.Method) {
|
||||
|
||||
@@ -357,7 +357,7 @@ type TestResult struct {
|
||||
|
||||
// ==================== model definition ends here ====================
|
||||
|
||||
func LoadHARCase(path string) (*hrp.TestCase, error) {
|
||||
func LoadHARCase(path string) (*hrp.TestCaseDef, error) {
|
||||
// load har file
|
||||
caseHAR, err := loadCaseHAR(path)
|
||||
if err != nil {
|
||||
@@ -381,13 +381,13 @@ func loadCaseHAR(path string) (*CaseHar, error) {
|
||||
}
|
||||
|
||||
// convert CaseHar to TestCase format
|
||||
func (c *CaseHar) ToTestCase() (*hrp.TestCase, error) {
|
||||
func (c *CaseHar) ToTestCase() (*hrp.TestCaseDef, error) {
|
||||
teststeps, err := c.prepareTestSteps()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tCase := &hrp.TestCase{
|
||||
tCase := &hrp.TestCaseDef{
|
||||
Config: c.prepareConfig(),
|
||||
Steps: teststeps,
|
||||
}
|
||||
@@ -424,8 +424,10 @@ func (c *CaseHar) prepareTestStep(entry *Entry) (*hrp.TStep, error) {
|
||||
|
||||
step := &stepFromHAR{
|
||||
TStep: hrp.TStep{
|
||||
Request: &hrp.Request{},
|
||||
Validators: make([]interface{}, 0),
|
||||
Request: &hrp.Request{},
|
||||
StepConfig: hrp.StepConfig{
|
||||
Validators: make([]interface{}, 0),
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := step.makeRequestMethod(entry); err != nil {
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"github.com/httprunner/httprunner/v4/hrp"
|
||||
)
|
||||
|
||||
func LoadJSONCase(path string) (*hrp.TestCase, error) {
|
||||
func LoadJSONCase(path string) (*hrp.TestCaseDef, error) {
|
||||
log.Info().Str("path", path).Msg("load json case file")
|
||||
caseJSON := new(hrp.TestCase)
|
||||
caseJSON := new(hrp.TestCaseDef)
|
||||
err := hrp.LoadFileObject(path, caseJSON)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "load json file failed")
|
||||
|
||||
@@ -111,7 +111,7 @@ var contentTypeMap = map[string]string{
|
||||
"xml": "application/xml",
|
||||
}
|
||||
|
||||
func LoadPostmanCase(path string) (*hrp.TestCase, error) {
|
||||
func LoadPostmanCase(path string) (*hrp.TestCaseDef, error) {
|
||||
log.Info().Str("path", path).Msg("load postman case file")
|
||||
casePostman, err := loadCasePostman(path)
|
||||
if err != nil {
|
||||
@@ -135,12 +135,12 @@ func loadCasePostman(path string) (*CasePostman, error) {
|
||||
return casePostman, nil
|
||||
}
|
||||
|
||||
func (c *CasePostman) ToTestCase() (*hrp.TestCase, error) {
|
||||
func (c *CasePostman) ToTestCase() (*hrp.TestCaseDef, error) {
|
||||
teststeps, err := c.prepareTestSteps()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tCase := &hrp.TestCase{
|
||||
tCase := &hrp.TestCaseDef{
|
||||
Config: c.prepareConfig(),
|
||||
Steps: teststeps,
|
||||
}
|
||||
@@ -199,8 +199,10 @@ func (c *CasePostman) prepareTestStep(item *TItem) (*hrp.TStep, error) {
|
||||
|
||||
step := &stepFromPostman{
|
||||
TStep: hrp.TStep{
|
||||
Request: &hrp.Request{},
|
||||
Validators: make([]interface{}, 0),
|
||||
Request: &hrp.Request{},
|
||||
StepConfig: hrp.StepConfig{
|
||||
Validators: make([]interface{}, 0),
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := step.makeRequestName(item); err != nil {
|
||||
@@ -233,7 +235,7 @@ type stepFromPostman struct {
|
||||
|
||||
// makeRequestName indicates the step name the same as item name
|
||||
func (s *stepFromPostman) makeRequestName(item *TItem) error {
|
||||
s.Name = item.Name
|
||||
s.StepName = item.Name
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/httprunner/httprunner/v4/hrp"
|
||||
)
|
||||
|
||||
func LoadSwaggerCase(path string) (*hrp.TestCase, error) {
|
||||
func LoadSwaggerCase(path string) (*hrp.TestCaseDef, error) {
|
||||
// load swagger file
|
||||
caseSwagger := new(spec.Swagger)
|
||||
err := hrp.LoadFileObject(path, caseSwagger)
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"github.com/httprunner/httprunner/v4/hrp"
|
||||
)
|
||||
|
||||
func LoadYAMLCase(path string) (*hrp.TestCase, error) {
|
||||
func LoadYAMLCase(path string) (*hrp.TestCaseDef, error) {
|
||||
// load yaml case file
|
||||
caseJSON := new(hrp.TestCase)
|
||||
caseJSON := new(hrp.TestCaseDef)
|
||||
err := hrp.LoadFileObject(path, caseJSON)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "load yaml file failed")
|
||||
|
||||
@@ -114,7 +114,7 @@ type TCaseConverter struct {
|
||||
fromFile string
|
||||
profilePath string
|
||||
outputDir string
|
||||
tCase *hrp.TestCase
|
||||
tCase *hrp.TestCaseDef
|
||||
}
|
||||
|
||||
// LoadCase loads source file and convert to TCase type
|
||||
|
||||
@@ -60,7 +60,7 @@ func TestLoadHARWithProfileOverride(t *testing.T) {
|
||||
|
||||
func TestMakeRequestWithProfile(t *testing.T) {
|
||||
caseConverter := &TCaseConverter{
|
||||
tCase: &hrp.TestCase{
|
||||
tCase: &hrp.TestCaseDef{
|
||||
Steps: []*hrp.TStep{
|
||||
{
|
||||
Request: &hrp.Request{
|
||||
@@ -101,7 +101,7 @@ func TestMakeRequestWithProfile(t *testing.T) {
|
||||
|
||||
func TestMakeRequestWithProfileOverride(t *testing.T) {
|
||||
caseConverter := &TCaseConverter{
|
||||
tCase: &hrp.TestCase{
|
||||
tCase: &hrp.TestCaseDef{
|
||||
Steps: []*hrp.TStep{
|
||||
{
|
||||
Request: &hrp.Request{
|
||||
|
||||
@@ -32,19 +32,6 @@ const (
|
||||
ACTION_SetIme ActionMethod = "set_ime"
|
||||
ACTION_GetSource ActionMethod = "get_source"
|
||||
|
||||
// UI validation
|
||||
// selectors
|
||||
SelectorName string = "ui_name"
|
||||
SelectorLabel string = "ui_label"
|
||||
SelectorOCR string = "ui_ocr"
|
||||
SelectorImage string = "ui_image"
|
||||
SelectorForegroundApp string = "ui_foreground_app"
|
||||
// assertions
|
||||
AssertionEqual string = "equal"
|
||||
AssertionNotEqual string = "not_equal"
|
||||
AssertionExists string = "exists"
|
||||
AssertionNotExists string = "not_exists"
|
||||
|
||||
// UI handling
|
||||
ACTION_Home ActionMethod = "home"
|
||||
ACTION_TapXY ActionMethod = "tap_xy"
|
||||
@@ -69,6 +56,21 @@ const (
|
||||
ACTION_DownloadApp ActionMethod = "download_app"
|
||||
)
|
||||
|
||||
const (
|
||||
// UI validation
|
||||
// selectors
|
||||
SelectorName string = "ui_name"
|
||||
SelectorLabel string = "ui_label"
|
||||
SelectorOCR string = "ui_ocr"
|
||||
SelectorImage string = "ui_image"
|
||||
SelectorForegroundApp string = "ui_foreground_app"
|
||||
// assertions
|
||||
AssertionEqual string = "equal"
|
||||
AssertionNotEqual string = "not_equal"
|
||||
AssertionExists string = "exists"
|
||||
AssertionNotExists string = "not_exists"
|
||||
)
|
||||
|
||||
type MobileAction struct {
|
||||
Method ActionMethod `json:"method,omitempty" yaml:"method,omitempty"`
|
||||
Params interface{} `json:"params,omitempty" yaml:"params,omitempty"`
|
||||
@@ -741,7 +743,7 @@ func (dExt *DriverExt) DoAction(action MobileAction) (err error) {
|
||||
case ACTION_ClosePopups:
|
||||
return dExt.ClosePopupsHandler()
|
||||
case ACTION_EndToEndDelay:
|
||||
dExt.CollectEndToEndDelay(action.GetOptions()...)
|
||||
CollectEndToEndDelay(dExt, action.GetOptions()...)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -476,7 +476,7 @@ func WithDriverPlugin(plugin funplugin.IPlugin) DriverOption {
|
||||
}
|
||||
}
|
||||
|
||||
// current implemeted device: IOSDevice, AndroidDevice
|
||||
// current implemeted device: IOSDevice, AndroidDevice, HarmonyDevice
|
||||
type IDevice interface {
|
||||
Init() error // init android device
|
||||
UUID() string // ios udid or android serial
|
||||
|
||||
@@ -28,7 +28,7 @@ type EndToEndDelay struct {
|
||||
Timelines []timeLog `json:"timelines"`
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) CollectEndToEndDelay(options ...ActionOption) {
|
||||
func CollectEndToEndDelay(dExt *DriverExt, options ...ActionOption) {
|
||||
dataOptions := NewActionOptions(options...)
|
||||
startTime := time.Now()
|
||||
|
||||
|
||||
@@ -600,7 +600,7 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) (summary *TestCa
|
||||
return summary, errors.Wrap(code.InterruptError, "session runner interrupted")
|
||||
default:
|
||||
// parse step struct
|
||||
err = r.parseStepStruct(step)
|
||||
err = r.parseStep(step)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("parse step struct failed")
|
||||
if r.caseRunner.hrpRunner.failfast {
|
||||
@@ -614,7 +614,7 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) (summary *TestCa
|
||||
stepStartTime := time.Now()
|
||||
|
||||
// run times of step
|
||||
loopTimes := step.Struct().Loops
|
||||
loopTimes := step.Config().Loops
|
||||
if loopTimes < 0 {
|
||||
log.Warn().Int("loops", loopTimes).Msg("loop times should be positive, set to 1")
|
||||
loopTimes = 1
|
||||
@@ -681,12 +681,12 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) (summary *TestCa
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
func (r *SessionRunner) parseStepStruct(step IStep) error {
|
||||
stepStruct := step.Struct()
|
||||
func (r *SessionRunner) parseStep(step IStep) error {
|
||||
stepConfig := step.Config()
|
||||
|
||||
// update step variables: merges step variables with config variables and session variables
|
||||
// variables priority: step variables > session variables (extracted variables from previous steps)
|
||||
overrideVars := mergeVariables(stepStruct.Variables, r.sessionVariables)
|
||||
overrideVars := mergeVariables(stepConfig.Variables, r.sessionVariables)
|
||||
// step variables > testcase config variables
|
||||
overrideVars = mergeVariables(overrideVars, r.caseRunner.Config.Variables)
|
||||
|
||||
@@ -697,19 +697,19 @@ func (r *SessionRunner) parseStepStruct(step IStep) error {
|
||||
Err(err).Msg("parse step variables failed")
|
||||
return errors.Wrap(err, "parse step variables failed")
|
||||
}
|
||||
stepStruct.Variables = parsedVariables
|
||||
stepConfig.Variables = parsedVariables
|
||||
|
||||
// parse step name
|
||||
parsedName, err := r.caseRunner.parser.ParseString(
|
||||
stepStruct.Name, stepStruct.Variables)
|
||||
stepConfig.StepName, stepConfig.Variables)
|
||||
if err != nil {
|
||||
parsedName = step.Name()
|
||||
}
|
||||
stepStruct.Name = convertString(parsedName)
|
||||
stepConfig.StepName = convertString(parsedName)
|
||||
|
||||
// parse step validators
|
||||
var parsedValidators []interface{}
|
||||
for _, iValidator := range stepStruct.Validators {
|
||||
for _, iValidator := range stepConfig.Validators {
|
||||
validator, ok := iValidator.(Validator)
|
||||
if !ok {
|
||||
return errors.New("validator type error")
|
||||
@@ -725,13 +725,13 @@ func (r *SessionRunner) parseStepStruct(step IStep) error {
|
||||
|
||||
// parse validator expect
|
||||
validator.Expect, err = r.caseRunner.parser.Parse(
|
||||
validator.Expect, stepStruct.Variables)
|
||||
validator.Expect, stepConfig.Variables)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse validator expect")
|
||||
}
|
||||
parsedValidators = append(parsedValidators, validator)
|
||||
}
|
||||
stepStruct.Validators = parsedValidators
|
||||
stepConfig.Validators = parsedValidators
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -250,15 +250,15 @@ func TestSessionRunner(t *testing.T) {
|
||||
caseRunner, _ := NewRunner(t).NewCaseRunner(testcase)
|
||||
sessionRunner := caseRunner.NewSession()
|
||||
step := testcase.TestSteps[0]
|
||||
if !assert.Equal(t, step.Struct().Variables["varFoo"], "${max($a, $b)}") {
|
||||
if !assert.Equal(t, step.Config().Variables["varFoo"], "${max($a, $b)}") {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
err := sessionRunner.parseStepStruct(step)
|
||||
err := sessionRunner.parseStep(step)
|
||||
if err != nil {
|
||||
t.Fatal()
|
||||
}
|
||||
if !assert.Equal(t, step.Struct().Variables["varFoo"], 34.5) {
|
||||
if !assert.Equal(t, step.Config().Variables["varFoo"], 34.5) {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
|
||||
60
hrp/step.go
60
hrp/step.go
@@ -18,63 +18,3 @@ const (
|
||||
stepTypeSuffixExtraction StepType = "_extraction"
|
||||
stepTypeSuffixValidation StepType = "_validation"
|
||||
)
|
||||
|
||||
// one step contains one or multiple actions
|
||||
type ActionResult struct {
|
||||
Name string `json:"name"` // action name
|
||||
StartTime int64 `json:"start_time"` // action start time
|
||||
Elapsed int64 `json:"elapsed_ms"` // action elapsed time(ms)
|
||||
Error error `json:"error"` // action execution result
|
||||
}
|
||||
|
||||
// one testcase contains one or multiple steps
|
||||
type StepResult struct {
|
||||
Name string `json:"name" yaml:"name"` // step name
|
||||
Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty"` // step identifier
|
||||
StartTime int64 `json:"start_time" yaml:"time"` // step start time
|
||||
StepType StepType `json:"step_type" yaml:"step_type"` // step type, testcase/request/transaction/rendezvous
|
||||
Success bool `json:"success" yaml:"success"` // step execution result
|
||||
Elapsed int64 `json:"elapsed_ms" yaml:"elapsed_ms"` // step execution time in millisecond(ms)
|
||||
HttpStat map[string]int64 `json:"httpstat,omitempty" yaml:"httpstat,omitempty"` // httpstat in millisecond(ms)
|
||||
Data interface{} `json:"data,omitempty" yaml:"data,omitempty"` // step data
|
||||
ContentSize int64 `json:"content_size" yaml:"content_size"` // response body length
|
||||
ExportVars map[string]interface{} `json:"export_vars,omitempty" yaml:"export_vars,omitempty"` // extract variables
|
||||
Actions []*ActionResult `json:"actions,omitempty" yaml:"actions,omitempty"` // store action execution info
|
||||
Attachments interface{} `json:"attachments,omitempty" yaml:"attachments,omitempty"` // store extra step information, such as error message or screenshots
|
||||
}
|
||||
|
||||
// TStep represents teststep data structure.
|
||||
// Each step maybe three different types: make one request or reference another api/testcase.
|
||||
type TStep struct {
|
||||
Name string `json:"name" yaml:"name"` // required
|
||||
Request *Request `json:"request,omitempty" yaml:"request,omitempty"`
|
||||
API interface{} `json:"api,omitempty" yaml:"api,omitempty"` // *APIPath or *API
|
||||
TestCase interface{} `json:"testcase,omitempty" yaml:"testcase,omitempty"` // *TestCasePath or *TestCase
|
||||
Transaction *Transaction `json:"transaction,omitempty" yaml:"transaction,omitempty"`
|
||||
Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"`
|
||||
ThinkTime *ThinkTime `json:"think_time,omitempty" yaml:"think_time,omitempty"`
|
||||
WebSocket *WebSocketAction `json:"websocket,omitempty" yaml:"websocket,omitempty"`
|
||||
Android *MobileUI `json:"android,omitempty" yaml:"android,omitempty"`
|
||||
Harmony *MobileUI `json:"harmony,omitempty" yaml:"harmony,omitempty"`
|
||||
IOS *MobileUI `json:"ios,omitempty" yaml:"ios,omitempty"`
|
||||
Shell *Shell `json:"shell,omitempty" yaml:"shell,omitempty"`
|
||||
Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"`
|
||||
SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"`
|
||||
TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"`
|
||||
Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"`
|
||||
Validators []interface{} `json:"validate,omitempty" yaml:"validate,omitempty"`
|
||||
Export []string `json:"export,omitempty" yaml:"export,omitempty"`
|
||||
Loops int `json:"loops,omitempty" yaml:"loops,omitempty"`
|
||||
IgnorePopup bool `json:"ignore_popup,omitempty" yaml:"ignore_popup,omitempty"`
|
||||
}
|
||||
|
||||
// IStep represents interface for all types for teststeps, includes:
|
||||
// StepRequest, StepRequestWithOptionalArgs, StepRequestValidation, StepRequestExtraction,
|
||||
// StepTestCaseWithOptionalArgs,
|
||||
// StepTransaction, StepRendezvous, StepWebSocket.
|
||||
type IStep interface {
|
||||
Name() string
|
||||
Type() StepType
|
||||
Struct() *TStep
|
||||
Run(*SessionRunner) (*StepResult, error)
|
||||
}
|
||||
|
||||
@@ -59,29 +59,30 @@ func (path *APIPath) ToAPI() (*API, error) {
|
||||
|
||||
// StepAPIWithOptionalArgs implements IStep interface.
|
||||
type StepAPIWithOptionalArgs struct {
|
||||
step *TStep
|
||||
StepConfig
|
||||
API interface{} `json:"api,omitempty" yaml:"api,omitempty"` // *APIPath or *API
|
||||
}
|
||||
|
||||
// TeardownHook adds a teardown hook for current teststep.
|
||||
func (s *StepAPIWithOptionalArgs) TeardownHook(hook string) *StepAPIWithOptionalArgs {
|
||||
s.step.TeardownHooks = append(s.step.TeardownHooks, hook)
|
||||
s.TeardownHooks = append(s.TeardownHooks, hook)
|
||||
return s
|
||||
}
|
||||
|
||||
// Export specifies variable names to export from referenced api for current step.
|
||||
func (s *StepAPIWithOptionalArgs) Export(names ...string) *StepAPIWithOptionalArgs {
|
||||
api, ok := s.step.API.(*API)
|
||||
api, ok := s.API.(*API)
|
||||
if ok {
|
||||
s.step.Export = append(api.Export, names...)
|
||||
s.StepExport = append(api.Export, names...)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepAPIWithOptionalArgs) Name() string {
|
||||
if s.step.Name != "" {
|
||||
return s.step.Name
|
||||
if s.StepName != "" {
|
||||
return s.StepName
|
||||
}
|
||||
api, ok := s.step.API.(*API)
|
||||
api, ok := s.API.(*API)
|
||||
if ok {
|
||||
return api.Name
|
||||
}
|
||||
@@ -92,8 +93,8 @@ func (s *StepAPIWithOptionalArgs) Type() StepType {
|
||||
return stepTypeAPI
|
||||
}
|
||||
|
||||
func (s *StepAPIWithOptionalArgs) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepAPIWithOptionalArgs) Config() *StepConfig {
|
||||
return &s.StepConfig
|
||||
}
|
||||
|
||||
func (s *StepAPIWithOptionalArgs) Run(r *SessionRunner) (stepResult *StepResult, err error) {
|
||||
@@ -101,10 +102,10 @@ func (s *StepAPIWithOptionalArgs) Run(r *SessionRunner) (stepResult *StepResult,
|
||||
stepResult.StepType = stepTypeAPI
|
||||
}()
|
||||
// extend request with referenced API
|
||||
api, _ := s.step.API.(*API)
|
||||
step := &TStep{}
|
||||
api, _ := s.API.(*API)
|
||||
step := &StepRequestWithOptionalArgs{}
|
||||
// deep copy step to avoid data racing
|
||||
if err = copier.Copy(step, s.step); err != nil {
|
||||
if err = copier.Copy(step, s.StepConfig); err != nil {
|
||||
log.Error().Err(err).Msg("copy step failed")
|
||||
return
|
||||
}
|
||||
@@ -114,10 +115,10 @@ func (s *StepAPIWithOptionalArgs) Run(r *SessionRunner) (stepResult *StepResult,
|
||||
}
|
||||
|
||||
// extend teststep with api, teststep will merge and override referenced api
|
||||
func extendWithAPI(testStep *TStep, overriddenStep *API) {
|
||||
func extendWithAPI(testStep *StepRequestWithOptionalArgs, overriddenStep *API) {
|
||||
// override api name
|
||||
if testStep.Name == "" {
|
||||
testStep.Name = overriddenStep.Name
|
||||
if testStep.StepName == "" {
|
||||
testStep.StepName = overriddenStep.Name
|
||||
}
|
||||
// merge & override request
|
||||
testStep.Request = overriddenStep.Request
|
||||
@@ -128,7 +129,7 @@ func extendWithAPI(testStep *TStep, overriddenStep *API) {
|
||||
// merge & override variables
|
||||
testStep.Variables = mergeVariables(testStep.Variables, overriddenStep.Variables)
|
||||
// merge & override extractors
|
||||
testStep.Extract = mergeMap(testStep.Extract, overriddenStep.Extract)
|
||||
testStep.StepConfig.Extract = mergeMap(testStep.StepConfig.Extract, overriddenStep.Extract)
|
||||
// merge & override validators
|
||||
testStep.Validators = mergeValidators(testStep.Validators, overriddenStep.Validators)
|
||||
// merge & override setupHooks
|
||||
|
||||
@@ -63,6 +63,7 @@ func initUIClient(serial, osType string) (client *uixt.DriverExt, err error) {
|
||||
}
|
||||
|
||||
type MobileUI struct {
|
||||
OSType string `json:"-" yaml:"-"` // ios or harmony or android
|
||||
Serial string `json:"serial,omitempty" yaml:"serial,omitempty"` // android serial or ios udid
|
||||
uixt.MobileAction `yaml:",inline"`
|
||||
Actions []uixt.MobileAction `json:"actions,omitempty" yaml:"actions,omitempty"`
|
||||
@@ -70,22 +71,35 @@ type MobileUI struct {
|
||||
|
||||
// StepMobile implements IStep interface.
|
||||
type StepMobile struct {
|
||||
step *TStep
|
||||
StepConfig
|
||||
stepObj *MobileUI
|
||||
Android *MobileUI `json:"android,omitempty" yaml:"android,omitempty"`
|
||||
Harmony *MobileUI `json:"harmony,omitempty" yaml:"harmony,omitempty"`
|
||||
IOS *MobileUI `json:"ios,omitempty" yaml:"ios,omitempty"`
|
||||
}
|
||||
|
||||
// uniform interface for all types of mobile systems
|
||||
func (s *StepMobile) obj() *MobileUI {
|
||||
if s.step.IOS != nil {
|
||||
return s.step.IOS
|
||||
} else if s.step.Harmony != nil {
|
||||
return s.step.Harmony
|
||||
if s.stepObj != nil {
|
||||
return s.stepObj
|
||||
}
|
||||
return s.step.Android
|
||||
if s.IOS != nil {
|
||||
s.stepObj = s.IOS
|
||||
s.stepObj.OSType = string(stepTypeIOS)
|
||||
return s.stepObj
|
||||
} else if s.Harmony != nil {
|
||||
s.stepObj = s.Harmony
|
||||
s.stepObj.OSType = string(stepTypeHarmony)
|
||||
return s.stepObj
|
||||
}
|
||||
s.stepObj = s.Android
|
||||
s.stepObj.OSType = string(stepTypeAndroid)
|
||||
return s.stepObj
|
||||
}
|
||||
|
||||
func (s *StepMobile) Serial(serial string) *StepMobile {
|
||||
s.obj().Serial = serial
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) InstallApp(path string) *StepMobile {
|
||||
@@ -117,7 +131,7 @@ func (s *StepMobile) Home() *StepMobile {
|
||||
Method: uixt.ACTION_Home,
|
||||
Params: nil,
|
||||
})
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
// TapXY taps the point {X,Y}, X & Y is percentage of coordinates
|
||||
@@ -129,7 +143,7 @@ func (s *StepMobile) TapXY(x, y float64, options ...uixt.ActionOption) *StepMobi
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
// TapAbsXY taps the point {X,Y}, X & Y is absolute coordinates
|
||||
@@ -141,7 +155,7 @@ func (s *StepMobile) TapAbsXY(x, y float64, options ...uixt.ActionOption) *StepM
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
// Tap taps on the target element
|
||||
@@ -153,7 +167,7 @@ func (s *StepMobile) Tap(params string, options ...uixt.ActionOption) *StepMobil
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
// TapByOCR taps on the target element by OCR recognition
|
||||
@@ -165,7 +179,7 @@ func (s *StepMobile) TapByOCR(ocrText string, options ...uixt.ActionOption) *Ste
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
// TapByCV taps on the target element by CV recognition
|
||||
@@ -177,7 +191,7 @@ func (s *StepMobile) TapByCV(imagePath string, options ...uixt.ActionOption) *St
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
// TapByUITypes taps on the target element specified by uiTypes, the higher the uiTypes, the higher the priority
|
||||
@@ -188,7 +202,7 @@ func (s *StepMobile) TapByUITypes(options ...uixt.ActionOption) *StepMobile {
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
// DoubleTapXY double taps the point {X,Y}, X & Y is percentage of coordinates
|
||||
@@ -198,7 +212,7 @@ func (s *StepMobile) DoubleTapXY(x, y float64, options ...uixt.ActionOption) *St
|
||||
Params: []float64{x, y},
|
||||
Options: uixt.NewActionOptions(options...),
|
||||
})
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) DoubleTap(params string, options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -209,7 +223,7 @@ func (s *StepMobile) DoubleTap(params string, options ...uixt.ActionOption) *Ste
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) Back(options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -220,7 +234,7 @@ func (s *StepMobile) Back(options ...uixt.ActionOption) *StepMobile {
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) Swipe(sx, sy, ex, ey float64, options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -231,7 +245,7 @@ func (s *StepMobile) Swipe(sx, sy, ex, ey float64, options ...uixt.ActionOption)
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) SwipeUp(options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -242,7 +256,7 @@ func (s *StepMobile) SwipeUp(options ...uixt.ActionOption) *StepMobile {
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) SwipeDown(options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -253,7 +267,7 @@ func (s *StepMobile) SwipeDown(options ...uixt.ActionOption) *StepMobile {
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) SwipeLeft(options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -264,7 +278,7 @@ func (s *StepMobile) SwipeLeft(options ...uixt.ActionOption) *StepMobile {
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) SwipeRight(options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -275,7 +289,7 @@ func (s *StepMobile) SwipeRight(options ...uixt.ActionOption) *StepMobile {
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) SwipeToTapApp(appName string, options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -286,7 +300,7 @@ func (s *StepMobile) SwipeToTapApp(appName string, options ...uixt.ActionOption)
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) SwipeToTapText(text string, options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -297,7 +311,7 @@ func (s *StepMobile) SwipeToTapText(text string, options ...uixt.ActionOption) *
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) SwipeToTapTexts(texts interface{}, options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -308,7 +322,7 @@ func (s *StepMobile) SwipeToTapTexts(texts interface{}, options ...uixt.ActionOp
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) Input(text string, options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -319,7 +333,7 @@ func (s *StepMobile) Input(text string, options ...uixt.ActionOption) *StepMobil
|
||||
}
|
||||
|
||||
s.obj().Actions = append(s.obj().Actions, action)
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
// Sleep specify sleep seconds after last action
|
||||
@@ -329,7 +343,7 @@ func (s *StepMobile) Sleep(n float64) *StepMobile {
|
||||
Params: n,
|
||||
Options: nil,
|
||||
})
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
// SleepRandom specify random sleeping seconds after last action
|
||||
@@ -342,7 +356,7 @@ func (s *StepMobile) SleepRandom(params ...float64) *StepMobile {
|
||||
Params: params,
|
||||
Options: nil,
|
||||
})
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) EndToEndDelay(options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -351,7 +365,7 @@ func (s *StepMobile) EndToEndDelay(options ...uixt.ActionOption) *StepMobile {
|
||||
Params: nil,
|
||||
Options: uixt.NewActionOptions(options...),
|
||||
})
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) ScreenShot(options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -360,7 +374,7 @@ func (s *StepMobile) ScreenShot(options ...uixt.ActionOption) *StepMobile {
|
||||
Params: nil,
|
||||
Options: uixt.NewActionOptions(options...),
|
||||
})
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) StartCamera() *StepMobile {
|
||||
@@ -369,7 +383,7 @@ func (s *StepMobile) StartCamera() *StepMobile {
|
||||
Params: nil,
|
||||
Options: nil,
|
||||
})
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) StopCamera() *StepMobile {
|
||||
@@ -378,7 +392,7 @@ func (s *StepMobile) StopCamera() *StepMobile {
|
||||
Params: nil,
|
||||
Options: nil,
|
||||
})
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) ClosePopups(options ...uixt.ActionOption) *StepMobile {
|
||||
@@ -387,40 +401,37 @@ func (s *StepMobile) ClosePopups(options ...uixt.ActionOption) *StepMobile {
|
||||
Params: nil,
|
||||
Options: uixt.NewActionOptions(options...),
|
||||
})
|
||||
return &StepMobile{step: s.step}
|
||||
return s
|
||||
}
|
||||
|
||||
// Validate switches to step validation.
|
||||
func (s *StepMobile) Validate() *StepMobileUIValidation {
|
||||
return &StepMobileUIValidation{
|
||||
step: s.step,
|
||||
StepMobile: s,
|
||||
Validators: make([]interface{}, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepMobile) Name() string {
|
||||
return s.step.Name
|
||||
return s.StepName
|
||||
}
|
||||
|
||||
func (s *StepMobile) Type() StepType {
|
||||
if s.step.Android != nil {
|
||||
return stepTypeAndroid
|
||||
} else if s.step.Harmony != nil {
|
||||
return stepTypeHarmony
|
||||
}
|
||||
return stepTypeIOS
|
||||
return StepType(s.obj().OSType)
|
||||
}
|
||||
|
||||
func (s *StepMobile) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepMobile) Config() *StepConfig {
|
||||
return &s.StepConfig
|
||||
}
|
||||
|
||||
func (s *StepMobile) Run(r *SessionRunner) (*StepResult, error) {
|
||||
return runStepMobileUI(r, s.step)
|
||||
return runStepMobileUI(r, s)
|
||||
}
|
||||
|
||||
// StepMobileUIValidation implements IStep interface.
|
||||
type StepMobileUIValidation struct {
|
||||
step *TStep
|
||||
*StepMobile
|
||||
Validators []interface{} `json:"validate,omitempty" yaml:"validate,omitempty"`
|
||||
}
|
||||
|
||||
func (s *StepMobileUIValidation) AssertNameExists(expectedName string, msg ...string) *StepMobileUIValidation {
|
||||
@@ -434,7 +445,7 @@ func (s *StepMobileUIValidation) AssertNameExists(expectedName string, msg ...st
|
||||
} else {
|
||||
v.Message = fmt.Sprintf("attribute name [%s] not found", expectedName)
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -449,7 +460,7 @@ func (s *StepMobileUIValidation) AssertNameNotExists(expectedName string, msg ..
|
||||
} else {
|
||||
v.Message = fmt.Sprintf("attribute name [%s] should not exist", expectedName)
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -464,7 +475,7 @@ func (s *StepMobileUIValidation) AssertLabelExists(expectedLabel string, msg ...
|
||||
} else {
|
||||
v.Message = fmt.Sprintf("attribute label [%s] not found", expectedLabel)
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -479,7 +490,7 @@ func (s *StepMobileUIValidation) AssertLabelNotExists(expectedLabel string, msg
|
||||
} else {
|
||||
v.Message = fmt.Sprintf("attribute label [%s] should not exist", expectedLabel)
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -494,7 +505,7 @@ func (s *StepMobileUIValidation) AssertOCRExists(expectedText string, msg ...str
|
||||
} else {
|
||||
v.Message = fmt.Sprintf("ocr text [%s] not found", expectedText)
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -509,7 +520,7 @@ func (s *StepMobileUIValidation) AssertOCRNotExists(expectedText string, msg ...
|
||||
} else {
|
||||
v.Message = fmt.Sprintf("ocr text [%s] should not exist", expectedText)
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -524,7 +535,7 @@ func (s *StepMobileUIValidation) AssertImageExists(expectedImagePath string, msg
|
||||
} else {
|
||||
v.Message = fmt.Sprintf("cv image [%s] not found", expectedImagePath)
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -539,7 +550,7 @@ func (s *StepMobileUIValidation) AssertImageNotExists(expectedImagePath string,
|
||||
} else {
|
||||
v.Message = fmt.Sprintf("cv image [%s] should not exist", expectedImagePath)
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -554,7 +565,7 @@ func (s *StepMobileUIValidation) AssertAppInForeground(packageName string, msg .
|
||||
} else {
|
||||
v.Message = fmt.Sprintf("app [%s] should be in foreground", packageName)
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -569,49 +580,53 @@ func (s *StepMobileUIValidation) AssertAppNotInForeground(packageName string, ms
|
||||
} else {
|
||||
v.Message = fmt.Sprintf("app [%s] should not be in foreground", packageName)
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobileUIValidation) Name() string {
|
||||
return s.step.Name
|
||||
return s.StepName
|
||||
}
|
||||
|
||||
func (s *StepMobileUIValidation) Type() StepType {
|
||||
if s.step.Android != nil {
|
||||
return stepTypeAndroid + stepTypeSuffixValidation
|
||||
}
|
||||
return stepTypeIOS + stepTypeSuffixValidation
|
||||
return s.StepMobile.Type() + stepTypeSuffixValidation
|
||||
}
|
||||
|
||||
func (s *StepMobileUIValidation) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepMobileUIValidation) Config() *StepConfig {
|
||||
return &StepConfig{
|
||||
StepName: s.StepName,
|
||||
Variables: s.Variables,
|
||||
Validators: s.Validators,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepMobileUIValidation) Run(r *SessionRunner) (*StepResult, error) {
|
||||
return runStepMobileUI(r, s.step)
|
||||
return runStepMobileUI(r, s)
|
||||
}
|
||||
|
||||
func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err error) {
|
||||
var osType string
|
||||
func runStepMobileUI(s *SessionRunner, step IStep) (stepResult *StepResult, err error) {
|
||||
var stepVariables map[string]interface{}
|
||||
var stepValidators []interface{}
|
||||
var ignorePopup bool
|
||||
|
||||
var mobileStep *MobileUI
|
||||
if step.IOS != nil {
|
||||
// ios step
|
||||
osType = "ios"
|
||||
mobileStep = step.IOS
|
||||
} else if step.Harmony != nil {
|
||||
// harmony step
|
||||
osType = "harmony"
|
||||
mobileStep = step.Harmony
|
||||
} else {
|
||||
// android step
|
||||
osType = "android"
|
||||
mobileStep = step.Android
|
||||
switch stepMobile := step.(type) {
|
||||
case *StepMobile:
|
||||
mobileStep = stepMobile.obj()
|
||||
stepVariables = stepMobile.Variables
|
||||
ignorePopup = stepMobile.IgnorePopup
|
||||
case *StepMobileUIValidation:
|
||||
mobileStep = stepMobile.obj()
|
||||
stepVariables = stepMobile.Variables
|
||||
stepValidators = stepMobile.Validators
|
||||
ignorePopup = stepMobile.StepMobile.IgnorePopup
|
||||
default:
|
||||
return nil, errors.New("invalid mobile UI step type")
|
||||
}
|
||||
|
||||
// report GA event
|
||||
go sdk.SendGA4Event("hrp_run_ui", map[string]interface{}{
|
||||
"osType": osType,
|
||||
"osType": mobileStep.OSType,
|
||||
})
|
||||
|
||||
identifier := mobileStep.Identifier
|
||||
@@ -628,15 +643,15 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err
|
||||
}
|
||||
|
||||
stepResult = &StepResult{
|
||||
Name: step.Name,
|
||||
Name: step.Name(),
|
||||
Identifier: identifier,
|
||||
StepType: StepType(osType),
|
||||
StepType: step.Type(),
|
||||
Success: false,
|
||||
ContentSize: 0,
|
||||
}
|
||||
|
||||
// init wda/uia driver
|
||||
uiDriver, err := initUIClient(mobileStep.Serial, osType)
|
||||
uiDriver, err := initUIClient(mobileStep.Serial, mobileStep.OSType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -655,9 +670,9 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err
|
||||
}
|
||||
|
||||
// automatic handling of pop-up windows on each step finished
|
||||
if !step.IgnorePopup && !s.IgnorePopup() {
|
||||
if !ignorePopup && !s.IgnorePopup() {
|
||||
if err2 := uiDriver.ClosePopupsHandler(); err2 != nil {
|
||||
log.Error().Err(err2).Str("step", step.Name).Msg("auto handle popup failed")
|
||||
log.Error().Err(err2).Str("step", step.Name()).Msg("auto handle popup failed")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,7 +707,7 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err
|
||||
log.Warn().Msg("interrupted in mobile UI runner")
|
||||
return stepResult, errors.Wrap(code.InterruptError, "mobile UI runner interrupted")
|
||||
default:
|
||||
if action.Params, err = s.caseRunner.parser.Parse(action.Params, step.Variables); err != nil {
|
||||
if action.Params, err = s.caseRunner.parser.Parse(action.Params, stepVariables); err != nil {
|
||||
if !code.IsErrorPredefined(err) {
|
||||
err = errors.Wrap(code.ParseError,
|
||||
fmt.Sprintf("parse action params failed: %v", err))
|
||||
@@ -709,7 +724,7 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err
|
||||
}
|
||||
|
||||
// validate
|
||||
validateResults, err := validateUI(uiDriver, step.Validators)
|
||||
validateResults, err := validateUI(uiDriver, stepValidators)
|
||||
if err != nil {
|
||||
if !code.IsErrorPredefined(err) {
|
||||
err = errors.Wrap(code.MobileUIValidationError, err.Error())
|
||||
|
||||
@@ -10,26 +10,30 @@ import (
|
||||
|
||||
// StepRendezvous implements IStep interface.
|
||||
type StepRendezvous struct {
|
||||
step *TStep
|
||||
StepConfig
|
||||
Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"`
|
||||
}
|
||||
|
||||
func (s *StepRendezvous) Name() string {
|
||||
if s.step.Name != "" {
|
||||
return s.step.Name
|
||||
if s.StepName != "" {
|
||||
return s.StepName
|
||||
}
|
||||
return s.step.Rendezvous.Name
|
||||
return s.Rendezvous.Name
|
||||
}
|
||||
|
||||
func (s *StepRendezvous) Type() StepType {
|
||||
return stepTypeRendezvous
|
||||
}
|
||||
|
||||
func (s *StepRendezvous) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepRendezvous) Config() *StepConfig {
|
||||
return &StepConfig{
|
||||
StepName: s.StepName,
|
||||
Variables: s.Variables,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepRendezvous) Run(r *SessionRunner) (*StepResult, error) {
|
||||
rendezvous := s.step.Rendezvous
|
||||
rendezvous := s.Rendezvous
|
||||
log.Info().
|
||||
Str("name", rendezvous.Name).
|
||||
Float32("percent", rendezvous.Percent).
|
||||
@@ -70,8 +74,11 @@ func (s *StepRendezvous) Run(r *SessionRunner) (*StepResult, error) {
|
||||
}
|
||||
|
||||
func isPreRendezvousAllReleased(rendezvous *Rendezvous, testCase *TestCase) bool {
|
||||
for _, step := range testCase.Steps {
|
||||
preRendezvous := step.Rendezvous
|
||||
for _, step := range testCase.TestSteps {
|
||||
if step.Type() != stepTypeRendezvous {
|
||||
continue
|
||||
}
|
||||
preRendezvous := step.(*StepRendezvous).Rendezvous
|
||||
if preRendezvous == nil {
|
||||
continue
|
||||
}
|
||||
@@ -88,19 +95,19 @@ func isPreRendezvousAllReleased(rendezvous *Rendezvous, testCase *TestCase) bool
|
||||
|
||||
// WithUserNumber sets the user number needed to release the current rendezvous
|
||||
func (s *StepRendezvous) WithUserNumber(number int64) *StepRendezvous {
|
||||
s.step.Rendezvous.Number = number
|
||||
s.Rendezvous.Number = number
|
||||
return s
|
||||
}
|
||||
|
||||
// WithUserPercent sets the user percent needed to release the current rendezvous
|
||||
func (s *StepRendezvous) WithUserPercent(percent float32) *StepRendezvous {
|
||||
s.step.Rendezvous.Percent = percent
|
||||
s.Rendezvous.Percent = percent
|
||||
return s
|
||||
}
|
||||
|
||||
// WithTimeout sets the timeout of duration between each user arriving at the current rendezvous
|
||||
func (s *StepRendezvous) WithTimeout(timeout int64) *StepRendezvous {
|
||||
s.step.Rendezvous.Timeout = timeout
|
||||
s.Rendezvous.Timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -157,7 +164,7 @@ func (r *Rendezvous) setReleased() {
|
||||
func initRendezvous(testcase *TestCase, total int64) []*Rendezvous {
|
||||
var rendezvousList []*Rendezvous
|
||||
for _, s := range testcase.TestSteps {
|
||||
step := s.Struct()
|
||||
step := s.(*StepRendezvous)
|
||||
if step.Rendezvous == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ func (r *requestBuilder) prepareBody(stepVariables map[string]interface{}) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func initUpload(step *TStep) {
|
||||
func initUpload(step *StepRequestWithOptionalArgs) {
|
||||
if step.Request.Headers == nil {
|
||||
step.Request.Headers = make(map[string]string)
|
||||
}
|
||||
@@ -259,7 +259,7 @@ func initUpload(step *TStep) {
|
||||
step.Request.Body = "$m_encoder"
|
||||
}
|
||||
|
||||
func prepareUpload(parser *Parser, step *TStep, stepVariables map[string]interface{}) (err error) {
|
||||
func prepareUpload(parser *Parser, step *StepRequestWithOptionalArgs, stepVariables map[string]interface{}) (err error) {
|
||||
if len(step.Request.Upload) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -276,9 +276,10 @@ func prepareUpload(parser *Parser, step *TStep, stepVariables map[string]interfa
|
||||
return
|
||||
}
|
||||
|
||||
func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err error) {
|
||||
func runStepRequest(r *SessionRunner, step IStep) (stepResult *StepResult, err error) {
|
||||
stepRequest := step.(*StepRequestWithOptionalArgs)
|
||||
stepResult = &StepResult{
|
||||
Name: step.Name,
|
||||
Name: stepRequest.StepName,
|
||||
StepType: stepTypeRequest,
|
||||
Success: false,
|
||||
ContentSize: 0,
|
||||
@@ -291,7 +292,7 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
|
||||
}
|
||||
}()
|
||||
|
||||
err = prepareUpload(r.caseRunner.parser, step, step.Variables)
|
||||
err = prepareUpload(r.caseRunner.parser, stepRequest, stepRequest.Variables)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -300,32 +301,32 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
|
||||
parser := r.caseRunner.parser
|
||||
config := r.caseRunner.Config
|
||||
|
||||
rb := newRequestBuilder(parser, config, step.Request)
|
||||
rb.req.Method = strings.ToUpper(string(step.Request.Method))
|
||||
rb := newRequestBuilder(parser, config, stepRequest.Request)
|
||||
rb.req.Method = strings.ToUpper(string(stepRequest.Request.Method))
|
||||
|
||||
err = rb.prepareUrlParams(step.Variables)
|
||||
err = rb.prepareUrlParams(stepRequest.Variables)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = rb.prepareHeaders(step.Variables)
|
||||
err = rb.prepareHeaders(stepRequest.Variables)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = rb.prepareBody(step.Variables)
|
||||
err = rb.prepareBody(stepRequest.Variables)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// add request object to step variables, could be used in setup hooks
|
||||
step.Variables["hrp_step_name"] = step.Name
|
||||
step.Variables["hrp_step_request"] = rb.requestMap
|
||||
step.Variables["request"] = rb.requestMap // setup hooks compatible with v3
|
||||
stepRequest.Variables["hrp_step_name"] = step.Name
|
||||
stepRequest.Variables["hrp_step_request"] = rb.requestMap
|
||||
stepRequest.Variables["request"] = rb.requestMap // setup hooks compatible with v3
|
||||
|
||||
// deal with setup hooks
|
||||
for _, setupHook := range step.SetupHooks {
|
||||
_, err := parser.Parse(setupHook, step.Variables)
|
||||
for _, setupHook := range stepRequest.SetupHooks {
|
||||
_, err := parser.Parse(setupHook, stepRequest.Variables)
|
||||
if err != nil {
|
||||
return stepResult, errors.Wrap(err, "run setup hooks failed")
|
||||
}
|
||||
@@ -347,15 +348,15 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
|
||||
|
||||
// select HTTP client
|
||||
var client *http.Client
|
||||
if step.Request.HTTP2 {
|
||||
if stepRequest.Request.HTTP2 {
|
||||
client = r.caseRunner.hrpRunner.http2Client
|
||||
} else {
|
||||
client = r.caseRunner.hrpRunner.httpClient
|
||||
}
|
||||
|
||||
// set step timeout
|
||||
if step.Request.Timeout != 0 {
|
||||
client.Timeout = time.Duration(step.Request.Timeout*1000) * time.Millisecond
|
||||
if stepRequest.Request.Timeout != 0 {
|
||||
client.Timeout = time.Duration(stepRequest.Request.Timeout*1000) * time.Millisecond
|
||||
}
|
||||
|
||||
// do request action
|
||||
@@ -398,12 +399,12 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
|
||||
}
|
||||
|
||||
// add response object to step variables, could be used in teardown hooks
|
||||
step.Variables["hrp_step_response"] = respObj.respObjMeta
|
||||
step.Variables["response"] = respObj.respObjMeta
|
||||
stepRequest.Variables["hrp_step_response"] = respObj.respObjMeta
|
||||
stepRequest.Variables["response"] = respObj.respObjMeta
|
||||
|
||||
// deal with teardown hooks
|
||||
for _, teardownHook := range step.TeardownHooks {
|
||||
_, err := parser.Parse(teardownHook, step.Variables)
|
||||
for _, teardownHook := range stepRequest.TeardownHooks {
|
||||
_, err := parser.Parse(teardownHook, stepRequest.Variables)
|
||||
if err != nil {
|
||||
return stepResult, errors.Wrap(err, "run teardown hooks failed")
|
||||
}
|
||||
@@ -413,15 +414,15 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
|
||||
sessionData.ReqResps.Response = builtin.FormatResponse(respObj.respObjMeta)
|
||||
|
||||
// extract variables from response
|
||||
extractors := step.Extract
|
||||
extractMapping := respObj.Extract(extractors, step.Variables)
|
||||
extractors := stepRequest.StepRequest.Extract
|
||||
extractMapping := respObj.Extract(extractors, stepRequest.Variables)
|
||||
stepResult.ExportVars = extractMapping
|
||||
|
||||
// override step variables with extracted variables
|
||||
step.Variables = mergeVariables(step.Variables, extractMapping)
|
||||
stepRequest.Variables = mergeVariables(stepRequest.Variables, extractMapping)
|
||||
|
||||
// validate response
|
||||
err = respObj.Validate(step.Validators, step.Variables)
|
||||
err = respObj.Validate(stepRequest.Validators, stepRequest.Variables)
|
||||
sessionData.Validators = respObj.validationResults
|
||||
if err == nil {
|
||||
sessionData.Success = true
|
||||
@@ -521,32 +522,33 @@ func shouldPrintBody(contentType string) bool {
|
||||
// NewStep returns a new constructed teststep with specified step name.
|
||||
func NewStep(name string) *StepRequest {
|
||||
return &StepRequest{
|
||||
step: &TStep{
|
||||
Name: name,
|
||||
StepConfig: StepConfig{
|
||||
StepName: name,
|
||||
Variables: make(map[string]interface{}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type StepRequest struct {
|
||||
step *TStep
|
||||
StepConfig
|
||||
Request *Request `json:"request,omitempty" yaml:"request,omitempty"`
|
||||
}
|
||||
|
||||
// WithVariables sets variables for current teststep.
|
||||
func (s *StepRequest) WithVariables(variables map[string]interface{}) *StepRequest {
|
||||
s.step.Variables = variables
|
||||
s.Variables = variables
|
||||
return s
|
||||
}
|
||||
|
||||
// SetupHook adds a setup hook for current teststep.
|
||||
func (s *StepRequest) SetupHook(hook string) *StepRequest {
|
||||
s.step.SetupHooks = append(s.step.SetupHooks, hook)
|
||||
s.SetupHooks = append(s.SetupHooks, hook)
|
||||
return s
|
||||
}
|
||||
|
||||
// HTTP2 enables HTTP/2 protocol
|
||||
func (s *StepRequest) HTTP2() *StepRequest {
|
||||
s.step.Request = &Request{
|
||||
s.Request = &Request{
|
||||
HTTP2: true,
|
||||
}
|
||||
return s
|
||||
@@ -554,250 +556,248 @@ func (s *StepRequest) HTTP2() *StepRequest {
|
||||
|
||||
// Loop specify running times for the current step
|
||||
func (s *StepRequest) Loop(times int) *StepRequest {
|
||||
s.step.Loops = times
|
||||
s.Loops = times
|
||||
return s
|
||||
}
|
||||
|
||||
// GET makes a HTTP GET request.
|
||||
func (s *StepRequest) GET(url string) *StepRequestWithOptionalArgs {
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpGET
|
||||
s.step.Request.URL = url
|
||||
if s.Request != nil {
|
||||
s.Request.Method = httpGET
|
||||
s.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
s.Request = &Request{
|
||||
Method: httpGET,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
StepRequest: s,
|
||||
}
|
||||
}
|
||||
|
||||
// HEAD makes a HTTP HEAD request.
|
||||
func (s *StepRequest) HEAD(url string) *StepRequestWithOptionalArgs {
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpHEAD
|
||||
s.step.Request.URL = url
|
||||
if s.Request != nil {
|
||||
s.Request.Method = httpHEAD
|
||||
s.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
s.Request = &Request{
|
||||
Method: httpHEAD,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
StepRequest: s,
|
||||
}
|
||||
}
|
||||
|
||||
// POST makes a HTTP POST request.
|
||||
func (s *StepRequest) POST(url string) *StepRequestWithOptionalArgs {
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpPOST
|
||||
s.step.Request.URL = url
|
||||
if s.Request != nil {
|
||||
s.Request.Method = httpPOST
|
||||
s.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
s.Request = &Request{
|
||||
Method: httpPOST,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
StepRequest: s,
|
||||
}
|
||||
}
|
||||
|
||||
// PUT makes a HTTP PUT request.
|
||||
func (s *StepRequest) PUT(url string) *StepRequestWithOptionalArgs {
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpPUT
|
||||
s.step.Request.URL = url
|
||||
if s.Request != nil {
|
||||
s.Request.Method = httpPUT
|
||||
s.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
s.Request = &Request{
|
||||
Method: httpPUT,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
StepRequest: s,
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE makes a HTTP DELETE request.
|
||||
func (s *StepRequest) DELETE(url string) *StepRequestWithOptionalArgs {
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpDELETE
|
||||
s.step.Request.URL = url
|
||||
if s.Request != nil {
|
||||
s.Request.Method = httpDELETE
|
||||
s.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
s.Request = &Request{
|
||||
Method: httpDELETE,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
StepRequest: s,
|
||||
}
|
||||
}
|
||||
|
||||
// OPTIONS makes a HTTP OPTIONS request.
|
||||
func (s *StepRequest) OPTIONS(url string) *StepRequestWithOptionalArgs {
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpOPTIONS
|
||||
s.step.Request.URL = url
|
||||
if s.Request != nil {
|
||||
s.Request.Method = httpOPTIONS
|
||||
s.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
s.Request = &Request{
|
||||
Method: httpOPTIONS,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
StepRequest: s,
|
||||
}
|
||||
}
|
||||
|
||||
// PATCH makes a HTTP PATCH request.
|
||||
func (s *StepRequest) PATCH(url string) *StepRequestWithOptionalArgs {
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpPATCH
|
||||
s.step.Request.URL = url
|
||||
if s.Request != nil {
|
||||
s.Request.Method = httpPATCH
|
||||
s.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
s.Request = &Request{
|
||||
Method: httpPATCH,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
StepRequest: s,
|
||||
}
|
||||
}
|
||||
|
||||
// CallRefCase calls a referenced testcase.
|
||||
func (s *StepRequest) CallRefCase(tc ITestCase) *StepTestCaseWithOptionalArgs {
|
||||
var err error
|
||||
s.step.TestCase, err = tc.GetTestCase()
|
||||
testCase, err := tc.GetTestCase()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to load testcase")
|
||||
os.Exit(code.GetErrorCode(err))
|
||||
}
|
||||
return &StepTestCaseWithOptionalArgs{
|
||||
step: s.step,
|
||||
StepConfig: s.StepConfig,
|
||||
TestCase: testCase,
|
||||
}
|
||||
}
|
||||
|
||||
// CallRefAPI calls a referenced api.
|
||||
func (s *StepRequest) CallRefAPI(api IAPI) *StepAPIWithOptionalArgs {
|
||||
var err error
|
||||
s.step.API, err = api.ToAPI()
|
||||
api, err := api.ToAPI()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to load api")
|
||||
os.Exit(code.GetErrorCode(err))
|
||||
}
|
||||
return &StepAPIWithOptionalArgs{
|
||||
step: s.step,
|
||||
StepConfig: s.StepConfig,
|
||||
API: api,
|
||||
}
|
||||
}
|
||||
|
||||
// StartTransaction starts a transaction.
|
||||
func (s *StepRequest) StartTransaction(name string) *StepTransaction {
|
||||
s.step.Transaction = &Transaction{
|
||||
Name: name,
|
||||
Type: transactionStart,
|
||||
}
|
||||
return &StepTransaction{
|
||||
step: s.step,
|
||||
StepConfig: s.StepConfig,
|
||||
Transaction: &Transaction{
|
||||
Name: name,
|
||||
Type: transactionStart,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// EndTransaction ends a transaction.
|
||||
func (s *StepRequest) EndTransaction(name string) *StepTransaction {
|
||||
s.step.Transaction = &Transaction{
|
||||
Name: name,
|
||||
Type: transactionEnd,
|
||||
}
|
||||
return &StepTransaction{
|
||||
step: s.step,
|
||||
StepConfig: s.StepConfig,
|
||||
Transaction: &Transaction{
|
||||
Name: name,
|
||||
Type: transactionEnd,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SetThinkTime sets think time.
|
||||
func (s *StepRequest) SetThinkTime(time float64) *StepThinkTime {
|
||||
s.step.ThinkTime = &ThinkTime{
|
||||
Time: time,
|
||||
}
|
||||
return &StepThinkTime{
|
||||
step: s.step,
|
||||
StepConfig: s.StepConfig,
|
||||
ThinkTime: &ThinkTime{
|
||||
Time: time,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SetRendezvous creates a new rendezvous
|
||||
func (s *StepRequest) SetRendezvous(name string) *StepRendezvous {
|
||||
s.step.Rendezvous = &Rendezvous{
|
||||
Name: name,
|
||||
}
|
||||
return &StepRendezvous{
|
||||
step: s.step,
|
||||
Rendezvous: &Rendezvous{
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WebSocket creates a new websocket action
|
||||
func (s *StepRequest) WebSocket() *StepWebSocket {
|
||||
s.step.WebSocket = &WebSocketAction{}
|
||||
return &StepWebSocket{
|
||||
step: s.step,
|
||||
StepConfig: s.StepConfig,
|
||||
WebSocket: &WebSocketAction{},
|
||||
}
|
||||
}
|
||||
|
||||
// Android creates a new android action
|
||||
func (s *StepRequest) Android() *StepMobile {
|
||||
s.step.Android = &MobileUI{}
|
||||
return &StepMobile{
|
||||
step: s.step,
|
||||
StepConfig: s.StepConfig,
|
||||
Android: &MobileUI{},
|
||||
}
|
||||
}
|
||||
|
||||
// IOS creates a new ios action
|
||||
func (s *StepRequest) IOS() *StepMobile {
|
||||
s.step.IOS = &MobileUI{}
|
||||
return &StepMobile{
|
||||
step: s.step,
|
||||
StepConfig: s.StepConfig,
|
||||
IOS: &MobileUI{},
|
||||
}
|
||||
}
|
||||
|
||||
// Harmony creates a new harmony action
|
||||
func (s *StepRequest) Harmony() *StepMobile {
|
||||
s.step.Harmony = &MobileUI{}
|
||||
return &StepMobile{
|
||||
step: s.step,
|
||||
StepConfig: s.StepConfig,
|
||||
Harmony: &MobileUI{},
|
||||
}
|
||||
}
|
||||
|
||||
// Shell creates a new shell action
|
||||
func (s *StepRequest) Shell(content string) *StepShell {
|
||||
s.step.Shell = &Shell{
|
||||
String: content,
|
||||
ExpectExitCode: 0,
|
||||
}
|
||||
|
||||
return &StepShell{
|
||||
step: s.step,
|
||||
StepConfig: s.StepConfig,
|
||||
Shell: &Shell{
|
||||
String: content,
|
||||
ExpectExitCode: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// StepRequestWithOptionalArgs implements IStep interface.
|
||||
type StepRequestWithOptionalArgs struct {
|
||||
step *TStep
|
||||
*StepRequest
|
||||
}
|
||||
|
||||
// SetVerify sets whether to verify SSL for current HTTP request.
|
||||
func (s *StepRequestWithOptionalArgs) SetVerify(verify bool) *StepRequestWithOptionalArgs {
|
||||
log.Info().Bool("verify", verify).Msg("set step request verify")
|
||||
s.step.Request.Verify = verify
|
||||
s.Request.Verify = verify
|
||||
return s
|
||||
}
|
||||
|
||||
// SetTimeout sets timeout for current HTTP request.
|
||||
func (s *StepRequestWithOptionalArgs) SetTimeout(timeout time.Duration) *StepRequestWithOptionalArgs {
|
||||
log.Info().Float64("timeout(seconds)", timeout.Seconds()).Msg("set step request timeout")
|
||||
s.step.Request.Timeout = timeout.Seconds()
|
||||
s.Request.Timeout = timeout.Seconds()
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -811,7 +811,7 @@ func (s *StepRequestWithOptionalArgs) SetProxies(proxies map[string]string) *Ste
|
||||
// SetAllowRedirects sets whether to allow redirects for current HTTP request.
|
||||
func (s *StepRequestWithOptionalArgs) SetAllowRedirects(allowRedirects bool) *StepRequestWithOptionalArgs {
|
||||
log.Info().Bool("allowRedirects", allowRedirects).Msg("set step request allowRedirects")
|
||||
s.step.Request.AllowRedirects = allowRedirects
|
||||
s.Request.AllowRedirects = allowRedirects
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -824,154 +824,142 @@ func (s *StepRequestWithOptionalArgs) SetAuth(auth map[string]string) *StepReque
|
||||
|
||||
// WithParams sets HTTP request params for current step.
|
||||
func (s *StepRequestWithOptionalArgs) WithParams(params map[string]interface{}) *StepRequestWithOptionalArgs {
|
||||
s.step.Request.Params = params
|
||||
s.Request.Params = params
|
||||
return s
|
||||
}
|
||||
|
||||
// WithHeaders sets HTTP request headers for current step.
|
||||
func (s *StepRequestWithOptionalArgs) WithHeaders(headers map[string]string) *StepRequestWithOptionalArgs {
|
||||
s.step.Request.Headers = headers
|
||||
s.Request.Headers = headers
|
||||
return s
|
||||
}
|
||||
|
||||
// WithCookies sets HTTP request cookies for current step.
|
||||
func (s *StepRequestWithOptionalArgs) WithCookies(cookies map[string]string) *StepRequestWithOptionalArgs {
|
||||
s.step.Request.Cookies = cookies
|
||||
s.Request.Cookies = cookies
|
||||
return s
|
||||
}
|
||||
|
||||
// WithBody sets HTTP request body for current step.
|
||||
func (s *StepRequestWithOptionalArgs) WithBody(body interface{}) *StepRequestWithOptionalArgs {
|
||||
s.step.Request.Body = body
|
||||
s.Request.Body = body
|
||||
return s
|
||||
}
|
||||
|
||||
// WithUpload sets HTTP request body for uploading file(s).
|
||||
func (s *StepRequestWithOptionalArgs) WithUpload(upload map[string]interface{}) *StepRequestWithOptionalArgs {
|
||||
// init upload
|
||||
initUpload(s.step)
|
||||
s.step.Request.Upload = upload
|
||||
initUpload(s)
|
||||
s.Request.Upload = upload
|
||||
return s
|
||||
}
|
||||
|
||||
// TeardownHook adds a teardown hook for current teststep.
|
||||
func (s *StepRequestWithOptionalArgs) TeardownHook(hook string) *StepRequestWithOptionalArgs {
|
||||
s.step.TeardownHooks = append(s.step.TeardownHooks, hook)
|
||||
s.TeardownHooks = append(s.TeardownHooks, hook)
|
||||
return s
|
||||
}
|
||||
|
||||
// Validate switches to step validation.
|
||||
func (s *StepRequestWithOptionalArgs) Validate() *StepRequestValidation {
|
||||
return &StepRequestValidation{
|
||||
step: s.step,
|
||||
StepRequestWithOptionalArgs: s,
|
||||
}
|
||||
}
|
||||
|
||||
// Extract switches to step extraction.
|
||||
func (s *StepRequestWithOptionalArgs) Extract() *StepRequestExtraction {
|
||||
s.step.Extract = make(map[string]string)
|
||||
s.StepConfig.Extract = make(map[string]string)
|
||||
return &StepRequestExtraction{
|
||||
step: s.step,
|
||||
StepRequestWithOptionalArgs: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepRequestWithOptionalArgs) Name() string {
|
||||
if s.step.Name != "" {
|
||||
return s.step.Name
|
||||
if s.StepName != "" {
|
||||
return s.StepName
|
||||
}
|
||||
return fmt.Sprintf("%v %s", s.step.Request.Method, s.step.Request.URL)
|
||||
return fmt.Sprintf("%v %s", s.Request.Method, s.Request.URL)
|
||||
}
|
||||
|
||||
func (s *StepRequestWithOptionalArgs) Type() StepType {
|
||||
return StepType(fmt.Sprintf("request-%v", s.step.Request.Method))
|
||||
return StepType(fmt.Sprintf("request-%v", s.Request.Method))
|
||||
}
|
||||
|
||||
func (s *StepRequestWithOptionalArgs) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepRequestWithOptionalArgs) Config() *StepConfig {
|
||||
return &s.StepConfig
|
||||
}
|
||||
|
||||
func (s *StepRequestWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error) {
|
||||
return runStepRequest(r, s.step)
|
||||
return runStepRequest(r, s)
|
||||
}
|
||||
|
||||
// StepRequestExtraction implements IStep interface.
|
||||
type StepRequestExtraction struct {
|
||||
step *TStep
|
||||
*StepRequestWithOptionalArgs
|
||||
}
|
||||
|
||||
// WithJmesPath sets the JMESPath expression to extract from the response.
|
||||
func (s *StepRequestExtraction) WithJmesPath(jmesPath string, varName string) *StepRequestExtraction {
|
||||
s.step.Extract[varName] = jmesPath
|
||||
s.StepConfig.Extract[varName] = jmesPath
|
||||
return s
|
||||
}
|
||||
|
||||
// Validate switches to step validation.
|
||||
func (s *StepRequestExtraction) Validate() *StepRequestValidation {
|
||||
return &StepRequestValidation{
|
||||
step: s.step,
|
||||
StepRequestWithOptionalArgs: &StepRequestWithOptionalArgs{
|
||||
StepRequest: &StepRequest{
|
||||
StepConfig: s.StepConfig,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepRequestExtraction) Name() string {
|
||||
return s.step.Name
|
||||
return s.StepName
|
||||
}
|
||||
|
||||
func (s *StepRequestExtraction) Type() StepType {
|
||||
var stepType StepType
|
||||
if s.step.WebSocket != nil {
|
||||
stepType = StepType(fmt.Sprintf("websocket-%v", s.step.WebSocket.Type))
|
||||
} else {
|
||||
stepType = StepType(fmt.Sprintf("request-%v", s.step.Request.Method))
|
||||
}
|
||||
stepType := StepType(fmt.Sprintf("request-%v", s.Request.Method))
|
||||
return stepType + stepTypeSuffixExtraction
|
||||
}
|
||||
|
||||
func (s *StepRequestExtraction) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepRequestExtraction) Struct() *StepConfig {
|
||||
return &s.StepConfig
|
||||
}
|
||||
|
||||
func (s *StepRequestExtraction) Run(r *SessionRunner) (*StepResult, error) {
|
||||
if s.step.Request != nil {
|
||||
return runStepRequest(r, s.step)
|
||||
}
|
||||
if s.step.WebSocket != nil {
|
||||
return runStepWebSocket(r, s.step)
|
||||
if s.StepRequestWithOptionalArgs != nil {
|
||||
return runStepRequest(r, s.StepRequestWithOptionalArgs)
|
||||
}
|
||||
return nil, errors.New("unexpected protocol type")
|
||||
}
|
||||
|
||||
// StepRequestValidation implements IStep interface.
|
||||
type StepRequestValidation struct {
|
||||
step *TStep
|
||||
*StepRequestWithOptionalArgs
|
||||
}
|
||||
|
||||
func (s *StepRequestValidation) Name() string {
|
||||
if s.step.Name != "" {
|
||||
return s.step.Name
|
||||
if s.StepName != "" {
|
||||
return s.StepName
|
||||
}
|
||||
return fmt.Sprintf("%s %s", s.step.Request.Method, s.step.Request.URL)
|
||||
return fmt.Sprintf("%s %s", s.Request.Method, s.Request.URL)
|
||||
}
|
||||
|
||||
func (s *StepRequestValidation) Type() StepType {
|
||||
var stepType StepType
|
||||
if s.step.WebSocket != nil {
|
||||
stepType = StepType(fmt.Sprintf("websocket-%v", s.step.WebSocket.Type))
|
||||
} else {
|
||||
stepType = StepType(fmt.Sprintf("request-%v", s.step.Request.Method))
|
||||
}
|
||||
stepType := StepType(fmt.Sprintf("request-%v", s.Request.Method))
|
||||
return stepType + stepTypeSuffixValidation
|
||||
}
|
||||
|
||||
func (s *StepRequestValidation) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepRequestValidation) Config() *StepConfig {
|
||||
return &s.StepConfig
|
||||
}
|
||||
|
||||
func (s *StepRequestValidation) Run(r *SessionRunner) (*StepResult, error) {
|
||||
if s.step.Request != nil {
|
||||
return runStepRequest(r, s.step)
|
||||
}
|
||||
if s.step.WebSocket != nil {
|
||||
return runStepWebSocket(r, s.step)
|
||||
if s.StepRequestWithOptionalArgs != nil {
|
||||
return runStepRequest(r, s.StepRequestWithOptionalArgs)
|
||||
}
|
||||
return nil, errors.New("unexpected protocol type")
|
||||
}
|
||||
@@ -983,7 +971,7 @@ func (s *StepRequestValidation) AssertEqual(jmesPath string, expected interface{
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -994,7 +982,7 @@ func (s *StepRequestValidation) AssertGreater(jmesPath string, expected interfac
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1005,7 +993,7 @@ func (s *StepRequestValidation) AssertLess(jmesPath string, expected interface{}
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1016,7 +1004,7 @@ func (s *StepRequestValidation) AssertGreaterOrEqual(jmesPath string, expected i
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1027,7 +1015,7 @@ func (s *StepRequestValidation) AssertLessOrEqual(jmesPath string, expected inte
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1038,7 +1026,7 @@ func (s *StepRequestValidation) AssertNotEqual(jmesPath string, expected interfa
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1049,7 +1037,7 @@ func (s *StepRequestValidation) AssertContains(jmesPath string, expected interfa
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1060,7 +1048,7 @@ func (s *StepRequestValidation) AssertTypeMatch(jmesPath string, expected interf
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1071,7 +1059,7 @@ func (s *StepRequestValidation) AssertRegexp(jmesPath string, expected interface
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1082,7 +1070,7 @@ func (s *StepRequestValidation) AssertStartsWith(jmesPath string, expected inter
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1093,7 +1081,7 @@ func (s *StepRequestValidation) AssertEndsWith(jmesPath string, expected interfa
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1104,7 +1092,7 @@ func (s *StepRequestValidation) AssertLengthEqual(jmesPath string, expected inte
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1115,7 +1103,7 @@ func (s *StepRequestValidation) AssertContainedBy(jmesPath string, expected inte
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1126,7 +1114,7 @@ func (s *StepRequestValidation) AssertLengthLessThan(jmesPath string, expected i
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1137,7 +1125,7 @@ func (s *StepRequestValidation) AssertStringEqual(jmesPath string, expected inte
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1148,7 +1136,7 @@ func (s *StepRequestValidation) AssertEqualFold(jmesPath string, expected interf
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1159,7 +1147,7 @@ func (s *StepRequestValidation) AssertLengthLessOrEquals(jmesPath string, expect
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1170,7 +1158,7 @@ func (s *StepRequestValidation) AssertLengthGreaterThan(jmesPath string, expecte
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1181,7 +1169,7 @@ func (s *StepRequestValidation) AssertLengthGreaterOrEquals(jmesPath string, exp
|
||||
Expect: expected,
|
||||
Message: msg,
|
||||
}
|
||||
s.step.Validators = append(s.step.Validators, v)
|
||||
s.Validators = append(s.Validators, v)
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ var (
|
||||
)
|
||||
|
||||
func TestRunRequestGetToStruct(t *testing.T) {
|
||||
tStep := stepGET.step
|
||||
tStep := stepGET
|
||||
if tStep.Request.Method != httpGET {
|
||||
t.Fatalf("tStep.Request.Method != GET")
|
||||
}
|
||||
@@ -52,7 +52,7 @@ func TestRunRequestGetToStruct(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunRequestPostDataToStruct(t *testing.T) {
|
||||
tStep := stepPOSTData.step
|
||||
tStep := stepPOSTData
|
||||
if tStep.Request.Method != httpPOST {
|
||||
t.Fatalf("tStep.Request.Method != POST")
|
||||
}
|
||||
|
||||
@@ -17,68 +17,86 @@ type Shell struct {
|
||||
|
||||
// StepShell implements IStep interface.
|
||||
type StepShell struct {
|
||||
step *TStep
|
||||
StepConfig
|
||||
Shell *Shell `json:"shell,omitempty" yaml:"shell,omitempty"`
|
||||
}
|
||||
|
||||
func (s *StepShell) Name() string {
|
||||
return s.step.Name
|
||||
return s.StepName
|
||||
}
|
||||
|
||||
func (s *StepShell) Type() StepType {
|
||||
return stepTypeShell
|
||||
}
|
||||
|
||||
func (s *StepShell) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepShell) Config() *StepConfig {
|
||||
return &StepConfig{
|
||||
StepName: s.StepName,
|
||||
Variables: s.Variables,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepShell) Run(r *SessionRunner) (*StepResult, error) {
|
||||
return runStepShell(r, s.step)
|
||||
return runStepShell(r, s)
|
||||
}
|
||||
|
||||
// Validate switches to step validation.
|
||||
func (s *StepShell) Validate() *StepShellValidation {
|
||||
return &StepShellValidation{
|
||||
step: s.step,
|
||||
StepConfig: s.StepConfig,
|
||||
Shell: s.Shell,
|
||||
}
|
||||
}
|
||||
|
||||
// StepShellValidation implements IStep interface.
|
||||
type StepShellValidation struct {
|
||||
step *TStep
|
||||
StepConfig
|
||||
Shell *Shell `json:"shell,omitempty" yaml:"shell,omitempty"`
|
||||
}
|
||||
|
||||
func (s *StepShellValidation) Name() string {
|
||||
return s.step.Name
|
||||
return s.StepName
|
||||
}
|
||||
|
||||
func (s *StepShellValidation) Type() StepType {
|
||||
return stepTypeShell + stepTypeSuffixValidation
|
||||
}
|
||||
|
||||
func (s *StepShellValidation) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepShellValidation) Config() *StepConfig {
|
||||
return &StepConfig{
|
||||
StepName: s.StepName,
|
||||
Variables: s.Variables,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepShellValidation) Run(r *SessionRunner) (*StepResult, error) {
|
||||
return runStepShell(r, s.step)
|
||||
return runStepShell(r, s)
|
||||
}
|
||||
|
||||
func (s *StepShellValidation) AssertExitCode(expected int) *StepShellValidation {
|
||||
s.step.Shell.ExpectExitCode = expected
|
||||
s.Shell.ExpectExitCode = expected
|
||||
return s
|
||||
}
|
||||
|
||||
func runStepShell(r *SessionRunner, step *TStep) (stepResult *StepResult, err error) {
|
||||
shell := step.Shell
|
||||
func runStepShell(r *SessionRunner, step IStep) (stepResult *StepResult, err error) {
|
||||
var shell *Shell
|
||||
switch stepShell := step.(type) {
|
||||
case *StepShell:
|
||||
shell = stepShell.Shell
|
||||
case *StepShellValidation:
|
||||
shell = stepShell.Shell
|
||||
default:
|
||||
return nil, errors.New("invalid shell step type")
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Str("name", step.Name).
|
||||
Str("name", step.Name()).
|
||||
Str("type", string(stepTypeShell)).
|
||||
Str("content", shell.String).
|
||||
Msg("run shell string")
|
||||
|
||||
stepResult = &StepResult{
|
||||
Name: step.Name,
|
||||
Name: step.Name(),
|
||||
StepType: stepTypeShell,
|
||||
Success: false,
|
||||
Elapsed: 0,
|
||||
|
||||
@@ -10,26 +10,27 @@ import (
|
||||
|
||||
// StepTestCaseWithOptionalArgs implements IStep interface.
|
||||
type StepTestCaseWithOptionalArgs struct {
|
||||
step *TStep
|
||||
StepConfig
|
||||
TestCase interface{} `json:"testcase,omitempty" yaml:"testcase,omitempty"` // *TestCasePath or *TestCase
|
||||
}
|
||||
|
||||
// TeardownHook adds a teardown hook for current teststep.
|
||||
func (s *StepTestCaseWithOptionalArgs) TeardownHook(hook string) *StepTestCaseWithOptionalArgs {
|
||||
s.step.TeardownHooks = append(s.step.TeardownHooks, hook)
|
||||
s.TeardownHooks = append(s.TeardownHooks, hook)
|
||||
return s
|
||||
}
|
||||
|
||||
// Export specifies variable names to export from referenced testcase for current step.
|
||||
func (s *StepTestCaseWithOptionalArgs) Export(names ...string) *StepTestCaseWithOptionalArgs {
|
||||
s.step.Export = append(s.step.Export, names...)
|
||||
s.StepExport = append(s.StepExport, names...)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepTestCaseWithOptionalArgs) Name() string {
|
||||
if s.step.Name != "" {
|
||||
return s.step.Name
|
||||
if s.StepName != "" {
|
||||
return s.StepName
|
||||
}
|
||||
ts, ok := s.step.TestCase.(*TestCase)
|
||||
ts, ok := s.TestCase.(*TestCase)
|
||||
if ok {
|
||||
return ts.Config.Name
|
||||
}
|
||||
@@ -40,13 +41,13 @@ func (s *StepTestCaseWithOptionalArgs) Type() StepType {
|
||||
return stepTypeTestCase
|
||||
}
|
||||
|
||||
func (s *StepTestCaseWithOptionalArgs) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepTestCaseWithOptionalArgs) Config() *StepConfig {
|
||||
return &s.StepConfig
|
||||
}
|
||||
|
||||
func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (stepResult *StepResult, err error) {
|
||||
stepResult = &StepResult{
|
||||
Name: s.step.Name,
|
||||
Name: s.StepName,
|
||||
StepType: stepTypeTestCase,
|
||||
Success: false,
|
||||
}
|
||||
@@ -58,7 +59,7 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (stepResult *StepRe
|
||||
}
|
||||
}()
|
||||
|
||||
stepTestCase := s.step.TestCase.(*TestCase)
|
||||
stepTestCase := s.TestCase.(*TestCase)
|
||||
|
||||
// copy testcase to avoid data racing
|
||||
copiedTestCase := &TestCase{}
|
||||
@@ -69,11 +70,11 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (stepResult *StepRe
|
||||
|
||||
// override testcase config
|
||||
// override testcase name
|
||||
if s.step.Name != "" {
|
||||
copiedTestCase.Config.Name = s.step.Name
|
||||
if s.StepName != "" {
|
||||
copiedTestCase.Config.Name = s.StepName
|
||||
}
|
||||
// merge & override extractors
|
||||
copiedTestCase.Config.Export = mergeSlices(s.step.Export, copiedTestCase.Config.Export)
|
||||
copiedTestCase.Config.Export = mergeSlices(s.StepExport, copiedTestCase.Config.Export)
|
||||
|
||||
caseRunner, err := r.caseRunner.hrpRunner.NewCaseRunner(*copiedTestCase)
|
||||
if err != nil {
|
||||
@@ -85,7 +86,7 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (stepResult *StepRe
|
||||
start := time.Now()
|
||||
var summary *TestCaseSummary
|
||||
// run referenced testcase with step variables
|
||||
summary, err = sessionRunner.Start(s.step.Variables)
|
||||
summary, err = sessionRunner.Start(s.Variables)
|
||||
stepResult.Elapsed = time.Since(start).Milliseconds()
|
||||
|
||||
// update step names
|
||||
|
||||
@@ -14,27 +14,28 @@ type ThinkTime struct {
|
||||
|
||||
// StepThinkTime implements IStep interface.
|
||||
type StepThinkTime struct {
|
||||
step *TStep
|
||||
StepConfig
|
||||
ThinkTime *ThinkTime `json:"think_time,omitempty" yaml:"think_time,omitempty"`
|
||||
}
|
||||
|
||||
func (s *StepThinkTime) Name() string {
|
||||
return s.step.Name
|
||||
return s.StepName
|
||||
}
|
||||
|
||||
func (s *StepThinkTime) Type() StepType {
|
||||
return stepTypeThinkTime
|
||||
}
|
||||
|
||||
func (s *StepThinkTime) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepThinkTime) Config() *StepConfig {
|
||||
return &s.StepConfig
|
||||
}
|
||||
|
||||
func (s *StepThinkTime) Run(r *SessionRunner) (*StepResult, error) {
|
||||
thinkTime := s.step.ThinkTime
|
||||
thinkTime := s.ThinkTime
|
||||
log.Info().Float64("time", thinkTime.Time).Msg("think time")
|
||||
|
||||
stepResult := &StepResult{
|
||||
Name: s.step.Name,
|
||||
Name: s.StepName,
|
||||
StepType: stepTypeThinkTime,
|
||||
Success: true,
|
||||
}
|
||||
|
||||
@@ -21,26 +21,27 @@ const (
|
||||
|
||||
// StepTransaction implements IStep interface.
|
||||
type StepTransaction struct {
|
||||
step *TStep
|
||||
StepConfig
|
||||
Transaction *Transaction `json:"transaction,omitempty" yaml:"transaction,omitempty"`
|
||||
}
|
||||
|
||||
func (s *StepTransaction) Name() string {
|
||||
if s.step.Name != "" {
|
||||
return s.step.Name
|
||||
if s.StepName != "" {
|
||||
return s.StepName
|
||||
}
|
||||
return fmt.Sprintf("transaction %s %s", s.step.Transaction.Name, s.step.Transaction.Type)
|
||||
return fmt.Sprintf("transaction %s %s", s.Transaction.Name, s.Transaction.Type)
|
||||
}
|
||||
|
||||
func (s *StepTransaction) Type() StepType {
|
||||
return stepTypeTransaction
|
||||
}
|
||||
|
||||
func (s *StepTransaction) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepTransaction) Config() *StepConfig {
|
||||
return &s.StepConfig
|
||||
}
|
||||
|
||||
func (s *StepTransaction) Run(r *SessionRunner) (*StepResult, error) {
|
||||
transaction := s.step.Transaction
|
||||
transaction := s.Transaction
|
||||
log.Info().
|
||||
Str("name", transaction.Name).
|
||||
Str("type", string(transaction.Type)).
|
||||
|
||||
@@ -120,26 +120,27 @@ func (wsConfig *WebSocketConfig) checkWebSocket() {
|
||||
|
||||
// StepWebSocket implements IStep interface.
|
||||
type StepWebSocket struct {
|
||||
step *TStep
|
||||
StepConfig
|
||||
WebSocket *WebSocketAction `json:"websocket,omitempty" yaml:"websocket,omitempty"`
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) Name() string {
|
||||
if s.step.Name != "" {
|
||||
return s.step.Name
|
||||
if s.StepName != "" {
|
||||
return s.StepName
|
||||
}
|
||||
return fmt.Sprintf("%s %s", s.step.WebSocket.Type, s.step.WebSocket.URL)
|
||||
return fmt.Sprintf("%s %s", s.WebSocket.Type, s.WebSocket.URL)
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) Type() StepType {
|
||||
return StepType(fmt.Sprintf("websocket-%v", s.step.WebSocket.Type))
|
||||
return StepType(fmt.Sprintf("websocket-%v", s.WebSocket.Type))
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) Struct() *TStep {
|
||||
return s.step
|
||||
func (s *StepWebSocket) Config() *StepConfig {
|
||||
return &s.StepConfig
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) Run(r *SessionRunner) (*StepResult, error) {
|
||||
return runStepWebSocket(r, s.step)
|
||||
return runStepWebSocket(r, s)
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) withUrl(url ...string) *StepWebSocket {
|
||||
@@ -149,87 +150,95 @@ func (s *StepWebSocket) withUrl(url ...string) *StepWebSocket {
|
||||
if len(url) > 1 {
|
||||
log.Warn().Msg("too many WebSocket step URL specified, using first URL")
|
||||
}
|
||||
s.step.WebSocket.URL = url[0]
|
||||
s.WebSocket.URL = url[0]
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) OpenConnection(url ...string) *StepWebSocket {
|
||||
s.step.WebSocket.Type = wsOpen
|
||||
s.WebSocket.Type = wsOpen
|
||||
return s.withUrl(url...)
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) PingPong(url ...string) *StepWebSocket {
|
||||
s.step.WebSocket.Type = wsPing
|
||||
s.WebSocket.Type = wsPing
|
||||
return s.withUrl(url...)
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) WriteAndRead(url ...string) *StepWebSocket {
|
||||
s.step.WebSocket.Type = wsWriteAndRead
|
||||
s.WebSocket.Type = wsWriteAndRead
|
||||
return s.withUrl(url...)
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) Read(url ...string) *StepWebSocket {
|
||||
s.step.WebSocket.Type = wsRead
|
||||
s.WebSocket.Type = wsRead
|
||||
return s.withUrl(url...)
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) Write(url ...string) *StepWebSocket {
|
||||
s.step.WebSocket.Type = wsWrite
|
||||
s.WebSocket.Type = wsWrite
|
||||
return s.withUrl(url...)
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) CloseConnection(url ...string) *StepWebSocket {
|
||||
s.step.WebSocket.Type = wsClose
|
||||
s.WebSocket.Type = wsClose
|
||||
return s.withUrl(url...)
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) WithParams(params map[string]interface{}) *StepWebSocket {
|
||||
s.step.WebSocket.Params = params
|
||||
s.WebSocket.Params = params
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) WithHeaders(headers map[string]string) *StepWebSocket {
|
||||
s.step.WebSocket.Headers = headers
|
||||
s.WebSocket.Headers = headers
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) NewConnection() *StepWebSocket {
|
||||
s.step.WebSocket.NewConnection = true
|
||||
s.WebSocket.NewConnection = true
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) WithTextMessage(message interface{}) *StepWebSocket {
|
||||
s.step.WebSocket.TextMessage = message
|
||||
s.WebSocket.TextMessage = message
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) WithBinaryMessage(message interface{}) *StepWebSocket {
|
||||
s.step.WebSocket.BinaryMessage = message
|
||||
s.WebSocket.BinaryMessage = message
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) WithTimeout(timeout int64) *StepWebSocket {
|
||||
s.step.WebSocket.Timeout = timeout
|
||||
s.WebSocket.Timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepWebSocket) WithCloseStatus(closeStatus int64) *StepWebSocket {
|
||||
s.step.WebSocket.CloseStatusCode = closeStatus
|
||||
s.WebSocket.CloseStatusCode = closeStatus
|
||||
return s
|
||||
}
|
||||
|
||||
// Validate switches to step validation.
|
||||
func (s *StepWebSocket) Validate() *StepRequestValidation {
|
||||
return &StepRequestValidation{
|
||||
step: s.step,
|
||||
StepRequestWithOptionalArgs: &StepRequestWithOptionalArgs{
|
||||
StepRequest: &StepRequest{
|
||||
StepConfig: s.StepConfig,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Extract switches to step extraction.
|
||||
func (s *StepWebSocket) Extract() *StepRequestExtraction {
|
||||
s.step.Extract = make(map[string]string)
|
||||
s.StepConfig.Extract = make(map[string]string)
|
||||
return &StepRequestExtraction{
|
||||
step: s.step,
|
||||
StepRequestWithOptionalArgs: &StepRequestWithOptionalArgs{
|
||||
StepRequest: &StepRequest{
|
||||
StepConfig: s.StepConfig,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,9 +269,12 @@ func (w *WebSocketAction) GetCloseStatusCode() int64 {
|
||||
return w.CloseStatusCode
|
||||
}
|
||||
|
||||
func runStepWebSocket(r *SessionRunner, step *TStep) (stepResult *StepResult, err error) {
|
||||
func runStepWebSocket(r *SessionRunner, step IStep) (stepResult *StepResult, err error) {
|
||||
stepWebSocket := step.(*StepWebSocket)
|
||||
webSocket := stepWebSocket.WebSocket
|
||||
variables := stepWebSocket.Variables
|
||||
stepResult = &StepResult{
|
||||
Name: step.Name,
|
||||
Name: step.Name(),
|
||||
StepType: stepTypeWebSocket,
|
||||
Success: false,
|
||||
ContentSize: 0,
|
||||
@@ -280,18 +292,18 @@ func runStepWebSocket(r *SessionRunner, step *TStep) (stepResult *StepResult, er
|
||||
config := r.caseRunner.Config
|
||||
|
||||
dummyReq := &Request{
|
||||
URL: step.WebSocket.URL,
|
||||
Params: step.WebSocket.Params,
|
||||
Headers: step.WebSocket.Headers,
|
||||
URL: webSocket.URL,
|
||||
Params: webSocket.Params,
|
||||
Headers: webSocket.Headers,
|
||||
}
|
||||
rb := newRequestBuilder(parser, config, dummyReq)
|
||||
|
||||
err = rb.prepareUrlParams(step.Variables)
|
||||
err = rb.prepareUrlParams(variables)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = rb.prepareHeaders(step.Variables)
|
||||
err = rb.prepareHeaders(variables)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -299,12 +311,12 @@ func runStepWebSocket(r *SessionRunner, step *TStep) (stepResult *StepResult, er
|
||||
parsedHeader := rb.req.Header
|
||||
|
||||
// add request object to step variables, could be used in setup hooks
|
||||
step.Variables["hrp_step_name"] = step.Name
|
||||
step.Variables["hrp_step_request"] = rb.requestMap
|
||||
variables["hrp_step_name"] = step.Name
|
||||
variables["hrp_step_request"] = rb.requestMap
|
||||
|
||||
// deal with setup hooks
|
||||
for _, setupHook := range step.SetupHooks {
|
||||
_, err = parser.Parse(setupHook, step.Variables)
|
||||
for _, setupHook := range stepWebSocket.SetupHooks {
|
||||
_, err = parser.Parse(setupHook, variables)
|
||||
if err != nil {
|
||||
return stepResult, errors.Wrap(err, "run setup hooks failed")
|
||||
}
|
||||
@@ -315,26 +327,26 @@ func runStepWebSocket(r *SessionRunner, step *TStep) (stepResult *StepResult, er
|
||||
|
||||
// do websocket action
|
||||
if r.caseRunner.hrpRunner.requestsLogOn {
|
||||
fmt.Printf("-------------------- websocket action: %v --------------------\n", step.WebSocket.Type.toString())
|
||||
fmt.Printf("-------------------- websocket action: %v --------------------\n", webSocket.Type.toString())
|
||||
}
|
||||
switch step.WebSocket.Type {
|
||||
switch webSocket.Type {
|
||||
case wsOpen:
|
||||
log.Info().Int64("timeout(ms)", step.WebSocket.GetTimeout()).Str("url", parsedURL).Msg("open websocket connection")
|
||||
log.Info().Int64("timeout(ms)", webSocket.GetTimeout()).Str("url", parsedURL).Msg("open websocket connection")
|
||||
// use the current websocket connection if existed
|
||||
if getWsClient(r, parsedURL) != nil {
|
||||
break
|
||||
}
|
||||
resp, err = openWithTimeout(parsedURL, parsedHeader, r, step)
|
||||
resp, err = openWithTimeout(parsedURL, parsedHeader, r, stepWebSocket)
|
||||
if err != nil {
|
||||
return stepResult, errors.Wrap(err, "open connection failed")
|
||||
}
|
||||
case wsPing:
|
||||
log.Info().Int64("timeout(ms)", step.WebSocket.GetTimeout()).Str("url", parsedURL).Msg("send ping and expect pong")
|
||||
err = writeWebSocket(parsedURL, r, step, step.Variables)
|
||||
log.Info().Int64("timeout(ms)", webSocket.GetTimeout()).Str("url", parsedURL).Msg("send ping and expect pong")
|
||||
err = writeWebSocket(parsedURL, r, stepWebSocket, stepWebSocket.Variables)
|
||||
if err != nil {
|
||||
return stepResult, errors.Wrap(err, "send ping message failed")
|
||||
}
|
||||
timer := time.NewTimer(time.Duration(step.WebSocket.GetTimeout()) * time.Millisecond)
|
||||
timer := time.NewTimer(time.Duration(webSocket.GetTimeout()) * time.Millisecond)
|
||||
// asynchronous receiving pong message with timeout
|
||||
go func() {
|
||||
select {
|
||||
@@ -347,35 +359,35 @@ func runStepWebSocket(r *SessionRunner, step *TStep) (stepResult *StepResult, er
|
||||
}
|
||||
}()
|
||||
case wsWriteAndRead:
|
||||
log.Info().Int64("timeout(ms)", step.WebSocket.GetTimeout()).Str("url", parsedURL).Msg("write a message and read response")
|
||||
err = writeWebSocket(parsedURL, r, step, step.Variables)
|
||||
log.Info().Int64("timeout(ms)", webSocket.GetTimeout()).Str("url", parsedURL).Msg("write a message and read response")
|
||||
err = writeWebSocket(parsedURL, r, stepWebSocket, variables)
|
||||
if err != nil {
|
||||
return stepResult, errors.Wrap(err, "write message failed")
|
||||
}
|
||||
resp, err = readMessageWithTimeout(parsedURL, r, step)
|
||||
resp, err = readMessageWithTimeout(parsedURL, r, stepWebSocket)
|
||||
if err != nil {
|
||||
return stepResult, errors.Wrap(err, "read message failed")
|
||||
}
|
||||
case wsRead:
|
||||
log.Info().Int64("timeout(ms)", step.WebSocket.GetTimeout()).Str("url", parsedURL).Msg("read only")
|
||||
resp, err = readMessageWithTimeout(parsedURL, r, step)
|
||||
log.Info().Int64("timeout(ms)", webSocket.GetTimeout()).Str("url", parsedURL).Msg("read only")
|
||||
resp, err = readMessageWithTimeout(parsedURL, r, stepWebSocket)
|
||||
if err != nil {
|
||||
return stepResult, errors.Wrap(err, "read message failed")
|
||||
}
|
||||
case wsWrite:
|
||||
log.Info().Str("url", parsedURL).Msg("write only")
|
||||
err = writeWebSocket(parsedURL, r, step, step.Variables)
|
||||
err = writeWebSocket(parsedURL, r, stepWebSocket, variables)
|
||||
if err != nil {
|
||||
return stepResult, errors.Wrap(err, "write message failed")
|
||||
}
|
||||
case wsClose:
|
||||
log.Info().Int64("timeout(ms)", step.WebSocket.GetTimeout()).Str("url", parsedURL).Msg("close webSocket connection")
|
||||
resp, err = closeWithTimeout(parsedURL, r, step, step.Variables)
|
||||
log.Info().Int64("timeout(ms)", webSocket.GetTimeout()).Str("url", parsedURL).Msg("close webSocket connection")
|
||||
resp, err = closeWithTimeout(parsedURL, r, stepWebSocket, variables)
|
||||
if err != nil {
|
||||
return stepResult, errors.Wrap(err, "close connection failed")
|
||||
}
|
||||
default:
|
||||
return stepResult, errors.Errorf("unexpected websocket frame type: %v", step.WebSocket.Type)
|
||||
return stepResult, errors.Errorf("unexpected websocket frame type: %v", webSocket.Type)
|
||||
}
|
||||
if r.caseRunner.hrpRunner.requestsLogOn {
|
||||
err = printWebSocketResponse(resp)
|
||||
@@ -393,12 +405,12 @@ func runStepWebSocket(r *SessionRunner, step *TStep) (stepResult *StepResult, er
|
||||
|
||||
if respObj != nil {
|
||||
// add response object to step variables, could be used in teardown hooks
|
||||
step.Variables["hrp_step_response"] = respObj.respObjMeta
|
||||
variables["hrp_step_response"] = respObj.respObjMeta
|
||||
}
|
||||
|
||||
// deal with teardown hooks
|
||||
for _, teardownHook := range step.TeardownHooks {
|
||||
_, err = parser.Parse(teardownHook, step.Variables)
|
||||
for _, teardownHook := range stepWebSocket.TeardownHooks {
|
||||
_, err = parser.Parse(teardownHook, variables)
|
||||
if err != nil {
|
||||
return stepResult, errors.Wrap(err, "run teardown hooks failed")
|
||||
}
|
||||
@@ -409,15 +421,15 @@ func runStepWebSocket(r *SessionRunner, step *TStep) (stepResult *StepResult, er
|
||||
sessionData.ReqResps.Response = builtin.FormatResponse(respObj.respObjMeta)
|
||||
|
||||
// extract variables from response
|
||||
extractors := step.Extract
|
||||
extractMapping := respObj.Extract(extractors, step.Variables)
|
||||
extractors := stepWebSocket.StepConfig.Extract
|
||||
extractMapping := respObj.Extract(extractors, variables)
|
||||
stepResult.ExportVars = extractMapping
|
||||
|
||||
// override step variables with extracted variables
|
||||
step.Variables = mergeVariables(step.Variables, extractMapping)
|
||||
variables = mergeVariables(variables, extractMapping)
|
||||
|
||||
// validate response
|
||||
err = respObj.Validate(step.Validators, step.Variables)
|
||||
err = respObj.Validate(stepWebSocket.Validators, variables)
|
||||
sessionData.Validators = respObj.validationResults
|
||||
if err == nil {
|
||||
sessionData.Success = true
|
||||
@@ -471,7 +483,7 @@ func printWebSocketResponse(resp interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func openWithTimeout(urlStr string, requestHeader http.Header, r *SessionRunner, step *TStep) (*http.Response, error) {
|
||||
func openWithTimeout(urlStr string, requestHeader http.Header, r *SessionRunner, step *StepWebSocket) (*http.Response, error) {
|
||||
openResponseChan := make(chan *http.Response)
|
||||
errorChan := make(chan error)
|
||||
go func() {
|
||||
@@ -519,7 +531,7 @@ func openWithTimeout(urlStr string, requestHeader http.Header, r *SessionRunner,
|
||||
}
|
||||
}
|
||||
|
||||
func readMessageWithTimeout(urlString string, r *SessionRunner, step *TStep) (*wsReadRespObject, error) {
|
||||
func readMessageWithTimeout(urlString string, r *SessionRunner, step *StepWebSocket) (*wsReadRespObject, error) {
|
||||
wsConn := getWsClient(r, urlString)
|
||||
if wsConn == nil {
|
||||
return nil, errors.New("try to use existing connection, but there is no connection")
|
||||
@@ -549,7 +561,7 @@ func readMessageWithTimeout(urlString string, r *SessionRunner, step *TStep) (*w
|
||||
}
|
||||
}
|
||||
|
||||
func writeWebSocket(urlString string, r *SessionRunner, step *TStep, stepVariables map[string]interface{}) error {
|
||||
func writeWebSocket(urlString string, r *SessionRunner, step *StepWebSocket, stepVariables map[string]interface{}) error {
|
||||
wsConn := getWsClient(r, urlString)
|
||||
if wsConn == nil {
|
||||
return errors.New("try to use existing connection, but there is no connection")
|
||||
@@ -583,7 +595,7 @@ func writeWebSocket(urlString string, r *SessionRunner, step *TStep, stepVariabl
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeWithType(c *websocket.Conn, step *TStep, messageType int, message interface{}) error {
|
||||
func writeWithType(c *websocket.Conn, step *StepWebSocket, messageType int, message interface{}) error {
|
||||
if message == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -603,7 +615,7 @@ func writeWithType(c *websocket.Conn, step *TStep, messageType int, message inte
|
||||
}
|
||||
}
|
||||
|
||||
func writeWithAction(c *websocket.Conn, step *TStep, messageType int, message []byte) error {
|
||||
func writeWithAction(c *websocket.Conn, step *StepWebSocket, messageType int, message []byte) error {
|
||||
switch step.WebSocket.Type {
|
||||
case wsPing:
|
||||
return c.WriteControl(websocket.PingMessage, message, time.Now().Add(defaultWriteWait))
|
||||
@@ -615,7 +627,7 @@ func writeWithAction(c *websocket.Conn, step *TStep, messageType int, message []
|
||||
}
|
||||
}
|
||||
|
||||
func closeWithTimeout(urlString string, r *SessionRunner, step *TStep, stepVariables map[string]interface{}) (*wsCloseRespObject, error) {
|
||||
func closeWithTimeout(urlString string, r *SessionRunner, step *StepWebSocket, stepVariables map[string]interface{}) (*wsCloseRespObject, error) {
|
||||
wsConn := getWsClient(r, urlString)
|
||||
if wsConn == nil {
|
||||
return nil, errors.New("no connection needs to be closed")
|
||||
|
||||
@@ -25,7 +25,7 @@ type TestCasePath string
|
||||
|
||||
// GetTestCase loads testcase path and convert to *TestCase
|
||||
func (path *TestCasePath) GetTestCase() (*TestCase, error) {
|
||||
tc := &TestCase{}
|
||||
tc := &TestCaseDef{}
|
||||
casePath := string(*path)
|
||||
err := LoadFileObject(casePath, tc)
|
||||
if err != nil {
|
||||
@@ -47,8 +47,7 @@ func (path *TestCasePath) GetTestCase() (*TestCase, error) {
|
||||
// TestCase implements ITestCase interface.
|
||||
type TestCase struct {
|
||||
Config *TConfig `json:"config" yaml:"config"`
|
||||
Steps []*TStep `json:"teststeps" yaml:"teststeps"`
|
||||
TestSteps []IStep `json:"-" yaml:"-"`
|
||||
TestSteps []IStep `json:"teststeps" yaml:"teststeps"`
|
||||
}
|
||||
|
||||
func (tc *TestCase) GetTestCase() (*TestCase, error) {
|
||||
@@ -56,7 +55,7 @@ func (tc *TestCase) GetTestCase() (*TestCase, error) {
|
||||
}
|
||||
|
||||
// MakeCompat converts TestCase compatible with Golang engine style
|
||||
func (tc *TestCase) MakeCompat() (err error) {
|
||||
func (tc *TestCaseDef) MakeCompat() (err error) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
err = fmt.Errorf("[MakeCompat] convert compat testcase error: %v", p)
|
||||
@@ -86,7 +85,6 @@ func (tc *TestCase) MakeCompat() (err error) {
|
||||
}
|
||||
|
||||
func (tc *TestCase) Dump2JSON(targetPath string) error {
|
||||
tc.loadTSteps()
|
||||
err := builtin.Dump2JSON(tc, targetPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "dump testcase to json failed")
|
||||
@@ -95,7 +93,6 @@ func (tc *TestCase) Dump2JSON(targetPath string) error {
|
||||
}
|
||||
|
||||
func (tc *TestCase) Dump2YAML(targetPath string) error {
|
||||
tc.loadTSteps()
|
||||
err := builtin.Dump2YAML(tc, targetPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "dump testcase to yaml failed")
|
||||
@@ -103,21 +100,8 @@ func (tc *TestCase) Dump2YAML(targetPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadTSteps loads TSteps structs from TestSteps([]IStep)
|
||||
func (tc *TestCase) loadTSteps() {
|
||||
tc.Steps = make([]*TStep, 0)
|
||||
for _, step := range tc.TestSteps {
|
||||
if step.Type() == stepTypeTestCase {
|
||||
if testcase, ok := step.Struct().TestCase.(*TestCase); ok {
|
||||
step.Struct().TestCase = testcase
|
||||
}
|
||||
}
|
||||
tc.Steps = append(tc.Steps, step.Struct())
|
||||
}
|
||||
}
|
||||
|
||||
// loadTSteps loads TestSteps([]IStep) from TSteps structs
|
||||
func (tc *TestCase) loadISteps() (*TestCase, error) {
|
||||
// loadISteps loads TestSteps([]IStep) from TSteps structs
|
||||
func (tc *TestCaseDef) loadISteps() (*TestCase, error) {
|
||||
testCase := &TestCase{
|
||||
Config: tc.Config,
|
||||
}
|
||||
@@ -187,7 +171,8 @@ func (tc *TestCase) loadISteps() (*TestCase, error) {
|
||||
fmt.Sprintf("failed to handle referenced API, got %v", step.TestCase))
|
||||
}
|
||||
testCase.TestSteps = append(testCase.TestSteps, &StepAPIWithOptionalArgs{
|
||||
step: step,
|
||||
StepConfig: step.StepConfig,
|
||||
API: step.API,
|
||||
})
|
||||
} else if step.TestCase != nil {
|
||||
casePath, ok := step.TestCase.(string)
|
||||
@@ -210,7 +195,7 @@ func (tc *TestCase) loadISteps() (*TestCase, error) {
|
||||
return nil, errors.Wrap(code.InvalidCaseError,
|
||||
fmt.Sprintf("referenced testcase should be map or path(string), got %v", step.TestCase))
|
||||
}
|
||||
tCase := &TestCase{}
|
||||
tCase := &TestCaseDef{}
|
||||
err = mapstructure.Decode(testCaseMap, tCase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -227,47 +212,60 @@ func (tc *TestCase) loadISteps() (*TestCase, error) {
|
||||
fmt.Sprintf("failed to handle referenced testcase, got %v", step.TestCase))
|
||||
}
|
||||
testCase.TestSteps = append(testCase.TestSteps, &StepTestCaseWithOptionalArgs{
|
||||
step: step,
|
||||
StepConfig: step.StepConfig,
|
||||
TestCase: step.TestCase,
|
||||
})
|
||||
} else if step.ThinkTime != nil {
|
||||
testCase.TestSteps = append(testCase.TestSteps, &StepThinkTime{
|
||||
step: step,
|
||||
StepConfig: step.StepConfig,
|
||||
ThinkTime: step.ThinkTime,
|
||||
})
|
||||
} else if step.Request != nil {
|
||||
stepRequest := &StepRequestWithOptionalArgs{
|
||||
StepRequest: &StepRequest{
|
||||
StepConfig: step.StepConfig,
|
||||
Request: step.Request,
|
||||
},
|
||||
}
|
||||
// init upload
|
||||
if len(step.Request.Upload) != 0 {
|
||||
initUpload(step)
|
||||
initUpload(stepRequest)
|
||||
}
|
||||
testCase.TestSteps = append(testCase.TestSteps, &StepRequestWithOptionalArgs{
|
||||
step: step,
|
||||
})
|
||||
testCase.TestSteps = append(testCase.TestSteps, stepRequest)
|
||||
} else if step.Transaction != nil {
|
||||
testCase.TestSteps = append(testCase.TestSteps, &StepTransaction{
|
||||
step: step,
|
||||
StepConfig: step.StepConfig,
|
||||
Transaction: step.Transaction,
|
||||
})
|
||||
} else if step.Rendezvous != nil {
|
||||
testCase.TestSteps = append(testCase.TestSteps, &StepRendezvous{
|
||||
step: step,
|
||||
StepConfig: step.StepConfig,
|
||||
Rendezvous: step.Rendezvous,
|
||||
})
|
||||
} else if step.WebSocket != nil {
|
||||
testCase.TestSteps = append(testCase.TestSteps, &StepWebSocket{
|
||||
step: step,
|
||||
StepConfig: step.StepConfig,
|
||||
WebSocket: step.WebSocket,
|
||||
})
|
||||
} else if step.IOS != nil {
|
||||
testCase.TestSteps = append(testCase.TestSteps, &StepMobile{
|
||||
step: step,
|
||||
StepConfig: step.StepConfig,
|
||||
IOS: step.IOS,
|
||||
})
|
||||
} else if step.Harmony != nil {
|
||||
testCase.TestSteps = append(testCase.TestSteps, &StepMobile{
|
||||
step: step,
|
||||
StepConfig: step.StepConfig,
|
||||
Harmony: step.Harmony,
|
||||
})
|
||||
} else if step.Android != nil {
|
||||
testCase.TestSteps = append(testCase.TestSteps, &StepMobile{
|
||||
step: step,
|
||||
StepConfig: step.StepConfig,
|
||||
Android: step.Android,
|
||||
})
|
||||
} else if step.Shell != nil {
|
||||
testCase.TestSteps = append(testCase.TestSteps, &StepShell{
|
||||
step: step,
|
||||
StepConfig: step.StepConfig,
|
||||
Shell: step.Shell,
|
||||
})
|
||||
} else {
|
||||
log.Warn().Interface("step", step).Msg("[convertTestCase] unexpected step")
|
||||
|
||||
Reference in New Issue
Block a user