diff --git a/js/modules/k6/execution/execution.go b/js/modules/k6/execution/execution.go index c01fb05a5c0..a70bfbe7fa4 100644 --- a/js/modules/k6/execution/execution.go +++ b/js/modules/k6/execution/execution.go @@ -24,6 +24,9 @@ import ( "context" "errors" + "github.com/dop251/goja" + + "go.k6.io/k6/js/common" "go.k6.io/k6/lib" ) @@ -36,61 +39,130 @@ func New() *Execution { } // GetVUStats returns information about the currently executing VU. -func (e *Execution) GetVUStats(ctx context.Context) (map[string]interface{}, error) { +func (e *Execution) GetVUStats(ctx context.Context) (goja.Value, error) { vuState := lib.GetState(ctx) if vuState == nil { return nil, errors.New("getting VU information in the init context is not supported") } + rt := common.GetRuntime(ctx) + if rt == nil { + return nil, errors.New("goja runtime is nil in context") + } + scID, _ := vuState.GetScenarioVUID() - out := map[string]interface{}{ - "id": vuState.Vu, - "idScenario": scID, - "iteration": vuState.Iteration, - "iterationScenario": vuState.GetScenarioVUIter(), + stats := map[string]interface{}{ + "id": vuState.Vu, + "idScenario": scID, + "iteration": vuState.Iteration, + "iterationScenario": func() goja.Value { + return rt.ToValue(vuState.GetScenarioVUIter()) + }, + } + + obj, err := newLazyJSObject(rt, stats) + if err != nil { + return nil, err } - return out, nil + return obj, nil } // GetScenarioStats returns information about the currently executing scenario. -func (e *Execution) GetScenarioStats(ctx context.Context) (map[string]interface{}, error) { +func (e *Execution) GetScenarioStats(ctx context.Context) (goja.Value, error) { ss := lib.GetScenarioState(ctx) if ss == nil { return nil, errors.New("getting scenario information in the init context is not supported") } - progress, _ := ss.ProgressFn() - out := map[string]interface{}{ + rt := common.GetRuntime(ctx) + if rt == nil { + return nil, errors.New("goja runtime is nil in context") + } + + stats := map[string]interface{}{ "name": ss.Name, "executor": ss.Executor, "startTime": ss.StartTime, - "progress": progress, - "iteration": ss.GetIter(), + "progress": func() goja.Value { + p, _ := ss.ProgressFn() + return rt.ToValue(p) + }, + "iteration": func() goja.Value { + return rt.ToValue(ss.GetIter()) + }, } + if ss.GetGlobalIter != nil { - out["iterationGlobal"] = ss.GetGlobalIter() + stats["iterationGlobal"] = func() goja.Value { + return rt.ToValue(ss.GetGlobalIter()) + } + } + + obj, err := newLazyJSObject(rt, stats) + if err != nil { + return nil, err } - return out, nil + return obj, nil } // GetTestStats returns global test information. -func (e *Execution) GetTestStats(ctx context.Context) (map[string]interface{}, error) { +func (e *Execution) GetTestStats(ctx context.Context) (goja.Value, error) { es := lib.GetExecutionState(ctx) if es == nil { return nil, errors.New("getting test information in the init context is not supported") } - out := map[string]interface{}{ + rt := common.GetRuntime(ctx) + if rt == nil { + return nil, errors.New("goja runtime is nil in context") + } + + stats := map[string]interface{}{ // XXX: For consistency, should this be startTime instead, or startTime // in ScenarioStats be converted to duration? - "duration": es.GetCurrentTestRunDuration().String(), - "iterationsCompleted": es.GetFullIterationCount(), - "iterationsInterrupted": es.GetPartialIterationCount(), - "vusActive": es.GetCurrentlyActiveVUsCount(), - "vusMax": es.GetInitializedVUsCount(), + "duration": func() goja.Value { + return rt.ToValue(es.GetCurrentTestRunDuration().String()) + }, + "iterationsCompleted": func() goja.Value { + return rt.ToValue(es.GetFullIterationCount()) + }, + "iterationsInterrupted": func() goja.Value { + return rt.ToValue(es.GetPartialIterationCount()) + }, + "vusActive": func() goja.Value { + return rt.ToValue(es.GetCurrentlyActiveVUsCount()) + }, + "vusMax": func() goja.Value { + return rt.ToValue(es.GetInitializedVUsCount()) + }, + } + + obj, err := newLazyJSObject(rt, stats) + if err != nil { + return nil, err + } + + return obj, nil +} + +func newLazyJSObject(rt *goja.Runtime, data map[string]interface{}) (goja.Value, error) { + obj := rt.NewObject() + + for k, v := range data { + if val, ok := v.(func() goja.Value); ok { + if err := obj.DefineAccessorProperty(k, rt.ToValue(val), + nil, goja.FLAG_FALSE, goja.FLAG_TRUE); err != nil { + return nil, err + } + } else { + if err := obj.DefineDataProperty(k, rt.ToValue(v), + goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_TRUE); err != nil { + return nil, err + } + } } - return out, nil + return obj, nil }