@testsmith/perfornium 0.6.4 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/cli.js +16 -1
- package/dist/cli/commands/distributed.js +2 -2
- package/dist/cli/commands/report.js +2 -2
- package/dist/cli/commands/run.js +2 -0
- package/dist/config/parser.js +2 -2
- package/dist/config/types/global-config.d.ts +82 -2
- package/dist/config/types/scenario-config.d.ts +2 -2
- package/dist/config/types/step-types.d.ts +1 -1
- package/dist/core/data/data-manager.d.ts +70 -0
- package/dist/core/data/data-manager.js +186 -0
- package/dist/core/data/data-provider.d.ts +85 -0
- package/dist/core/data/data-provider.js +468 -0
- package/dist/core/data/index.d.ts +8 -0
- package/dist/core/data/index.js +13 -0
- package/dist/core/execution/check-evaluator.d.ts +10 -0
- package/dist/core/execution/check-evaluator.js +79 -0
- package/dist/core/execution/data-extractor.d.ts +6 -0
- package/dist/core/execution/data-extractor.js +70 -0
- package/dist/core/execution/index.d.ts +3 -0
- package/dist/core/execution/index.js +9 -0
- package/dist/core/execution/json-payload-processor.d.ts +7 -0
- package/dist/core/execution/json-payload-processor.js +140 -0
- package/dist/core/factories/index.d.ts +2 -0
- package/dist/core/factories/index.js +7 -0
- package/dist/core/factories/output-handler-factory.d.ts +10 -0
- package/dist/core/factories/output-handler-factory.js +91 -0
- package/dist/core/factories/protocol-handler-factory.d.ts +12 -0
- package/dist/core/factories/protocol-handler-factory.js +96 -0
- package/dist/core/index.d.ts +3 -2
- package/dist/core/index.js +8 -3
- package/dist/core/reporting/dashboard-reporter.d.ts +17 -0
- package/dist/core/reporting/dashboard-reporter.js +127 -0
- package/dist/core/reporting/index.d.ts +1 -0
- package/dist/core/reporting/index.js +5 -0
- package/dist/core/step-executor.d.ts +6 -20
- package/dist/core/step-executor.js +72 -366
- package/dist/core/strategies/index.d.ts +2 -0
- package/dist/core/strategies/index.js +7 -0
- package/dist/core/strategies/scenario-selector.d.ts +13 -0
- package/dist/core/strategies/scenario-selector.js +37 -0
- package/dist/core/strategies/think-time-strategy.d.ts +15 -0
- package/dist/core/strategies/think-time-strategy.js +71 -0
- package/dist/core/test-runner.d.ts +4 -11
- package/dist/core/test-runner.js +105 -312
- package/dist/core/virtual-user.d.ts +7 -37
- package/dist/core/virtual-user.js +29 -269
- package/dist/dashboard/routes/api.d.ts +64 -0
- package/dist/dashboard/routes/api.js +569 -0
- package/dist/dashboard/routes/index.d.ts +2 -0
- package/dist/dashboard/routes/index.js +7 -0
- package/dist/dashboard/routes/static.d.ts +6 -0
- package/dist/dashboard/routes/static.js +76 -0
- package/dist/dashboard/server.d.ts +8 -84
- package/dist/dashboard/server.js +76 -2007
- package/dist/dashboard/services/file-scanner.d.ts +7 -0
- package/dist/dashboard/services/file-scanner.js +114 -0
- package/dist/dashboard/services/index.d.ts +5 -0
- package/dist/dashboard/services/index.js +13 -0
- package/dist/dashboard/services/influxdb-service.d.ts +41 -0
- package/dist/dashboard/services/influxdb-service.js +329 -0
- package/dist/dashboard/services/metrics-parser.d.ts +12 -0
- package/dist/dashboard/services/metrics-parser.js +209 -0
- package/dist/dashboard/services/results-manager.d.ts +17 -0
- package/dist/dashboard/services/results-manager.js +311 -0
- package/dist/dashboard/services/test-executor.d.ts +41 -0
- package/dist/dashboard/services/test-executor.js +250 -0
- package/dist/dashboard/services/workers-manager.d.ts +13 -0
- package/dist/dashboard/services/workers-manager.js +81 -0
- package/dist/dashboard/templates/index.html +122 -0
- package/dist/dashboard/templates/scripts/main.js +3280 -0
- package/dist/dashboard/templates/styles.css +402 -0
- package/dist/dashboard/types.d.ts +168 -0
- package/dist/dashboard/types.js +2 -0
- package/dist/distributed/result-aggregator.js +1 -3
- package/dist/metrics/batch/batch-processor.d.ts +27 -0
- package/dist/metrics/batch/batch-processor.js +85 -0
- package/dist/metrics/batch/index.d.ts +1 -0
- package/dist/metrics/batch/index.js +5 -0
- package/dist/metrics/collector.d.ts +46 -45
- package/dist/metrics/collector.js +179 -640
- package/dist/metrics/core/error-tracker.d.ts +9 -0
- package/dist/metrics/core/error-tracker.js +52 -0
- package/dist/metrics/core/index.d.ts +3 -0
- package/dist/metrics/core/index.js +9 -0
- package/dist/metrics/core/result-storage.d.ts +19 -0
- package/dist/metrics/core/result-storage.js +56 -0
- package/dist/metrics/core/statistics-engine.d.ts +27 -0
- package/dist/metrics/core/statistics-engine.js +91 -0
- package/dist/metrics/output/file-writer.d.ts +19 -0
- package/dist/metrics/output/file-writer.js +129 -0
- package/dist/metrics/output/index.d.ts +2 -0
- package/dist/metrics/output/index.js +10 -0
- package/dist/metrics/output/influxdb-writer.d.ts +89 -0
- package/dist/metrics/output/influxdb-writer.js +404 -0
- package/dist/metrics/realtime/dispatcher.d.ts +18 -0
- package/dist/metrics/realtime/dispatcher.js +45 -0
- package/dist/metrics/realtime/endpoints/graphite.d.ts +3 -0
- package/dist/metrics/realtime/endpoints/graphite.js +61 -0
- package/dist/metrics/realtime/endpoints/influxdb.d.ts +3 -0
- package/dist/metrics/realtime/endpoints/influxdb.js +35 -0
- package/dist/metrics/realtime/endpoints/webhook.d.ts +3 -0
- package/dist/metrics/realtime/endpoints/webhook.js +22 -0
- package/dist/metrics/realtime/endpoints/websocket.d.ts +3 -0
- package/dist/metrics/realtime/endpoints/websocket.js +25 -0
- package/dist/metrics/realtime/index.d.ts +5 -0
- package/dist/metrics/realtime/index.js +13 -0
- package/dist/metrics/reporting/index.d.ts +3 -0
- package/dist/metrics/reporting/index.js +9 -0
- package/dist/metrics/reporting/step-statistics.d.ts +6 -0
- package/dist/metrics/reporting/step-statistics.js +59 -0
- package/dist/metrics/reporting/summary-generator.d.ts +16 -0
- package/dist/metrics/reporting/summary-generator.js +46 -0
- package/dist/metrics/reporting/timeline-calculator.d.ts +7 -0
- package/dist/metrics/reporting/timeline-calculator.js +86 -0
- package/dist/metrics/types.d.ts +58 -0
- package/dist/outputs/csv.d.ts +2 -0
- package/dist/outputs/csv.js +21 -2
- package/dist/outputs/json.js +6 -2
- package/dist/protocols/rest/handler.d.ts +4 -53
- package/dist/protocols/rest/handler.js +73 -454
- package/dist/protocols/rest/request/auth-handler.d.ts +4 -0
- package/dist/protocols/rest/request/auth-handler.js +30 -0
- package/dist/protocols/rest/request/body-processor.d.ts +11 -0
- package/dist/protocols/rest/request/body-processor.js +62 -0
- package/dist/protocols/rest/request/index.d.ts +2 -0
- package/dist/protocols/rest/request/index.js +7 -0
- package/dist/protocols/rest/response/checks.d.ts +6 -0
- package/dist/protocols/rest/response/checks.js +71 -0
- package/dist/protocols/rest/response/index.d.ts +2 -0
- package/dist/protocols/rest/response/index.js +7 -0
- package/dist/protocols/rest/response/size-calculator.d.ts +12 -0
- package/dist/protocols/rest/response/size-calculator.js +64 -0
- package/dist/protocols/web/browser/highlight.d.ts +7 -0
- package/dist/protocols/web/browser/highlight.js +47 -0
- package/dist/protocols/web/browser/index.d.ts +4 -0
- package/dist/protocols/web/browser/index.js +11 -0
- package/dist/protocols/web/browser/manager.d.ts +20 -0
- package/dist/protocols/web/browser/manager.js +189 -0
- package/dist/protocols/web/browser/screenshot.d.ts +8 -0
- package/dist/protocols/web/browser/screenshot.js +69 -0
- package/dist/protocols/web/browser/storage.d.ts +5 -0
- package/dist/protocols/web/browser/storage.js +45 -0
- package/dist/protocols/web/commands/index.d.ts +5 -0
- package/dist/protocols/web/commands/index.js +11 -0
- package/dist/protocols/web/commands/interaction.d.ts +13 -0
- package/dist/protocols/web/commands/interaction.js +68 -0
- package/dist/protocols/web/commands/measurement.d.ts +16 -0
- package/dist/protocols/web/commands/measurement.js +33 -0
- package/dist/protocols/web/commands/navigation.d.ts +11 -0
- package/dist/protocols/web/commands/navigation.js +43 -0
- package/dist/protocols/web/commands/types.d.ts +12 -0
- package/dist/protocols/web/commands/types.js +2 -0
- package/dist/protocols/web/commands/verification.d.ts +12 -0
- package/dist/protocols/web/commands/verification.js +118 -0
- package/dist/protocols/web/handler.d.ts +19 -30
- package/dist/protocols/web/handler.js +164 -651
- package/dist/protocols/web/network/capture.d.ts +19 -0
- package/dist/protocols/web/network/capture.js +225 -0
- package/dist/protocols/web/network/filters.d.ts +5 -0
- package/dist/protocols/web/network/filters.js +49 -0
- package/dist/protocols/web/network/index.d.ts +4 -0
- package/dist/protocols/web/network/index.js +9 -0
- package/dist/protocols/web/network/types.d.ts +13 -0
- package/dist/protocols/web/network/types.js +2 -0
- package/dist/protocols/web/network/utils.d.ts +8 -0
- package/dist/protocols/web/network/utils.js +29 -0
- package/dist/recorder/continue-recorder.d.ts +11 -0
- package/dist/recorder/continue-recorder.js +872 -0
- package/dist/reporting/chart-data/index.d.ts +5 -0
- package/dist/reporting/chart-data/index.js +13 -0
- package/dist/reporting/chart-data/network.d.ts +25 -0
- package/dist/reporting/chart-data/network.js +78 -0
- package/dist/reporting/chart-data/scenario.d.ts +37 -0
- package/dist/reporting/chart-data/scenario.js +76 -0
- package/dist/reporting/chart-data/step-statistics.d.ts +24 -0
- package/dist/reporting/chart-data/step-statistics.js +94 -0
- package/dist/reporting/chart-data/throughput.d.ts +16 -0
- package/dist/reporting/chart-data/throughput.js +24 -0
- package/dist/reporting/chart-data/timeline.d.ts +17 -0
- package/dist/reporting/chart-data/timeline.js +46 -0
- package/dist/reporting/handlebars-helpers.d.ts +1 -0
- package/dist/reporting/handlebars-helpers.js +63 -0
- package/dist/reporting/{enhanced-html-generator.d.ts → html-generator.d.ts} +1 -1
- package/dist/reporting/{enhanced-html-generator.js → html-generator.js} +10 -7
- package/dist/reporting/templates/{enhanced-report.hbs → report.hbs} +9 -9
- package/dist/utils/data-utils.d.ts +17 -0
- package/dist/utils/data-utils.js +129 -0
- package/dist/utils/template.js +2 -2
- package/package.json +5 -2
- package/dist/core/csv-data-provider.d.ts +0 -47
- package/dist/core/csv-data-provider.js +0 -265
- package/dist/reporting/generator.d.ts +0 -42
- package/dist/reporting/generator.js +0 -1217
- package/dist/reporting/templates/html.hbs +0 -2453
|
@@ -1,37 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.StepExecutor = void 0;
|
|
37
4
|
const hooks_manager_1 = require("./hooks-manager");
|
|
@@ -41,11 +8,16 @@ const rendezvous_1 = require("./rendezvous");
|
|
|
41
8
|
const time_1 = require("../utils/time");
|
|
42
9
|
const template_1 = require("../utils/template");
|
|
43
10
|
const logger_1 = require("../utils/logger");
|
|
44
|
-
const
|
|
45
|
-
const
|
|
11
|
+
const check_evaluator_1 = require("./execution/check-evaluator");
|
|
12
|
+
const data_extractor_1 = require("./execution/data-extractor");
|
|
13
|
+
const json_payload_processor_1 = require("./execution/json-payload-processor");
|
|
46
14
|
class StepExecutor {
|
|
47
15
|
constructor(handlers, testName = 'Load Test') {
|
|
48
16
|
this.templateProcessor = new template_1.TemplateProcessor();
|
|
17
|
+
// Extracted modules
|
|
18
|
+
this.checkEvaluator = new check_evaluator_1.CheckEvaluator();
|
|
19
|
+
this.dataExtractor = new data_extractor_1.DataExtractor();
|
|
20
|
+
this.jsonPayloadProcessor = new json_payload_processor_1.JSONPayloadProcessor();
|
|
49
21
|
this.handlers = handlers;
|
|
50
22
|
this.testName = testName;
|
|
51
23
|
// Register this instance with ScriptExecutor for step execution in hooks
|
|
@@ -63,7 +35,6 @@ class StepExecutor {
|
|
|
63
35
|
if (stepHooksManager) {
|
|
64
36
|
try {
|
|
65
37
|
const beforeStepResult = await stepHooksManager.executeBeforeStep(context.variables, context.extracted_data, context.csv_data);
|
|
66
|
-
// Merge any variables returned by beforeStep hook
|
|
67
38
|
if (beforeStepResult?.variables) {
|
|
68
39
|
Object.assign(context.variables, beforeStepResult.variables);
|
|
69
40
|
logger_1.logger.debug(`Hook VU${context.vu_id}: beforeStep hook set variables: ${Object.keys(beforeStepResult.variables).join(', ')}`);
|
|
@@ -75,11 +46,9 @@ class StepExecutor {
|
|
|
75
46
|
}
|
|
76
47
|
let testResult;
|
|
77
48
|
try {
|
|
78
|
-
// Execute the actual step
|
|
79
49
|
testResult = await this.executeStepInternal(step, context, scenarioName, startTime);
|
|
80
50
|
}
|
|
81
51
|
catch (error) {
|
|
82
|
-
// Execute onStepError hook
|
|
83
52
|
if (stepHooksManager) {
|
|
84
53
|
try {
|
|
85
54
|
await stepHooksManager.executeOnStepError(error, context.variables, context.extracted_data, context.csv_data);
|
|
@@ -88,31 +57,12 @@ class StepExecutor {
|
|
|
88
57
|
logger_1.logger.error(`VU ${context.vu_id} onStepError hook failed:`, hookError);
|
|
89
58
|
}
|
|
90
59
|
}
|
|
91
|
-
|
|
92
|
-
const stepName = step.name || `${step.type}_${context.iteration}`;
|
|
93
|
-
const iteration = context.iteration || 1;
|
|
94
|
-
const threadName = `${iteration}. ${stepName} ${context.vu_id}-${iteration}`;
|
|
95
|
-
testResult = {
|
|
96
|
-
id: `${context.vu_id}-${Date.now()}`,
|
|
97
|
-
vu_id: context.vu_id,
|
|
98
|
-
iteration: context.iteration,
|
|
99
|
-
scenario: scenarioName,
|
|
100
|
-
action: step.name || step.type || 'rest',
|
|
101
|
-
step_name: stepName,
|
|
102
|
-
thread_name: threadName,
|
|
103
|
-
timestamp: startTime,
|
|
104
|
-
duration: Date.now() - startTime,
|
|
105
|
-
success: false,
|
|
106
|
-
error: error.message,
|
|
107
|
-
shouldRecord: true
|
|
108
|
-
};
|
|
60
|
+
testResult = this.createErrorResult(step, context, scenarioName, startTime, error);
|
|
109
61
|
}
|
|
110
62
|
finally {
|
|
111
|
-
// Execute teardownStep hook (only if testResult was created)
|
|
112
63
|
if (stepHooksManager && testResult) {
|
|
113
64
|
try {
|
|
114
65
|
const teardownResult = await stepHooksManager.executeTeardownStep(context.variables, context.extracted_data, context.csv_data, testResult);
|
|
115
|
-
// Merge any variables returned by teardownStep hook
|
|
116
66
|
if (teardownResult?.variables) {
|
|
117
67
|
Object.assign(context.variables, teardownResult.variables);
|
|
118
68
|
}
|
|
@@ -122,40 +72,20 @@ class StepExecutor {
|
|
|
122
72
|
}
|
|
123
73
|
}
|
|
124
74
|
}
|
|
125
|
-
// This should never happen, but TypeScript needs the guarantee
|
|
126
75
|
if (!testResult) {
|
|
127
|
-
|
|
128
|
-
const iteration = context.iteration || 1;
|
|
129
|
-
const threadName = `${iteration}. ${stepName} ${context.vu_id}-${iteration}`;
|
|
130
|
-
testResult = {
|
|
131
|
-
id: `${context.vu_id}-${Date.now()}`,
|
|
132
|
-
vu_id: context.vu_id,
|
|
133
|
-
iteration: context.iteration,
|
|
134
|
-
scenario: scenarioName,
|
|
135
|
-
action: step.name || step.type || 'rest',
|
|
136
|
-
step_name: stepName,
|
|
137
|
-
thread_name: threadName,
|
|
138
|
-
timestamp: startTime,
|
|
139
|
-
duration: Date.now() - startTime,
|
|
140
|
-
success: false,
|
|
141
|
-
error: 'Unknown error occurred',
|
|
142
|
-
shouldRecord: true
|
|
143
|
-
};
|
|
76
|
+
testResult = this.createErrorResult(step, context, scenarioName, startTime, new Error('Unknown error occurred'));
|
|
144
77
|
}
|
|
145
|
-
// Evaluate thresholds
|
|
78
|
+
// Evaluate thresholds
|
|
146
79
|
if (step.thresholds && step.thresholds.length > 0) {
|
|
147
80
|
try {
|
|
148
81
|
const evaluationResult = threshold_evaluator_1.ThresholdEvaluator.evaluate(step.thresholds, testResult, stepName);
|
|
149
82
|
if (!evaluationResult.passed) {
|
|
150
|
-
// Add threshold failures to test result
|
|
151
83
|
testResult.threshold_failures = evaluationResult.failures;
|
|
152
|
-
// Execute threshold actions (may throw errors for fail actions)
|
|
153
84
|
await threshold_evaluator_1.ThresholdEvaluator.executeThresholdActions(evaluationResult, stepName);
|
|
154
85
|
}
|
|
155
86
|
}
|
|
156
87
|
catch (error) {
|
|
157
88
|
logger_1.logger.error(`Threshold evaluation failed for step ${stepName}:`, error);
|
|
158
|
-
// If threshold action is to fail, we re-throw the error
|
|
159
89
|
if (error instanceof Error && error.message.includes('threshold violation')) {
|
|
160
90
|
throw error;
|
|
161
91
|
}
|
|
@@ -163,27 +93,30 @@ class StepExecutor {
|
|
|
163
93
|
}
|
|
164
94
|
return testResult;
|
|
165
95
|
}
|
|
166
|
-
|
|
96
|
+
createErrorResult(step, context, scenarioName, startTime, error) {
|
|
97
|
+
const stepName = step.name || `${step.type}_${context.iteration}`;
|
|
98
|
+
const iteration = context.iteration || 1;
|
|
99
|
+
const threadName = `${iteration}. ${stepName} ${context.vu_id}-${iteration}`;
|
|
100
|
+
return {
|
|
101
|
+
id: `${context.vu_id}-${Date.now()}`,
|
|
102
|
+
vu_id: context.vu_id,
|
|
103
|
+
iteration: context.iteration,
|
|
104
|
+
scenario: scenarioName,
|
|
105
|
+
action: step.name || step.type || 'rest',
|
|
106
|
+
step_name: stepName,
|
|
107
|
+
thread_name: threadName,
|
|
108
|
+
timestamp: startTime,
|
|
109
|
+
duration: Date.now() - startTime,
|
|
110
|
+
success: false,
|
|
111
|
+
error: error.message,
|
|
112
|
+
shouldRecord: true
|
|
113
|
+
};
|
|
114
|
+
}
|
|
167
115
|
async executeStepInternal(step, context, scenarioName, startTime) {
|
|
168
116
|
const processedStep = this.processTemplate(step, context);
|
|
169
117
|
// Check condition if specified
|
|
170
118
|
if (step.condition && !this.evaluateCondition(step.condition, context)) {
|
|
171
|
-
|
|
172
|
-
const iteration = context.iteration || 1;
|
|
173
|
-
const threadName = `${iteration}. ${stepName} ${context.vu_id}-${iteration}`;
|
|
174
|
-
return {
|
|
175
|
-
id: `${context.vu_id}-${Date.now()}`,
|
|
176
|
-
vu_id: context.vu_id,
|
|
177
|
-
iteration: context.iteration,
|
|
178
|
-
scenario: scenarioName,
|
|
179
|
-
action: step.name || step.type || 'rest',
|
|
180
|
-
step_name: stepName,
|
|
181
|
-
thread_name: threadName,
|
|
182
|
-
timestamp: startTime,
|
|
183
|
-
duration: 0,
|
|
184
|
-
success: true,
|
|
185
|
-
custom_metrics: { skipped: true }
|
|
186
|
-
};
|
|
119
|
+
return this.createSkippedResult(step, context, scenarioName, startTime);
|
|
187
120
|
}
|
|
188
121
|
let result;
|
|
189
122
|
const stepType = step.type || 'rest';
|
|
@@ -213,15 +146,46 @@ class StepExecutor {
|
|
|
213
146
|
default:
|
|
214
147
|
throw new Error(`Unsupported step type: ${step.type}`);
|
|
215
148
|
}
|
|
216
|
-
|
|
217
|
-
//
|
|
149
|
+
const testResult = this.buildTestResult(step, context, scenarioName, startTime, result);
|
|
150
|
+
// Run checks if configured
|
|
151
|
+
if ('checks' in step && step.checks) {
|
|
152
|
+
const checkResults = await this.checkEvaluator.runChecks(step.checks, result, context);
|
|
153
|
+
if (!checkResults.passed) {
|
|
154
|
+
testResult.success = false;
|
|
155
|
+
testResult.error = checkResults.errors.join('; ');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Extract data if configured
|
|
159
|
+
if ('extract' in step && step.extract) {
|
|
160
|
+
await this.dataExtractor.extractData(step.extract, result, context);
|
|
161
|
+
}
|
|
162
|
+
return testResult;
|
|
163
|
+
}
|
|
164
|
+
createSkippedResult(step, context, scenarioName, startTime) {
|
|
165
|
+
const stepName = step.name || `${step.type}_${context.iteration}`;
|
|
166
|
+
const iteration = context.iteration || 1;
|
|
167
|
+
const threadName = `${iteration}. ${stepName} ${context.vu_id}-${iteration}`;
|
|
168
|
+
return {
|
|
169
|
+
id: `${context.vu_id}-${Date.now()}`,
|
|
170
|
+
vu_id: context.vu_id,
|
|
171
|
+
iteration: context.iteration,
|
|
172
|
+
scenario: scenarioName,
|
|
173
|
+
action: step.name || step.type || 'rest',
|
|
174
|
+
step_name: stepName,
|
|
175
|
+
thread_name: threadName,
|
|
176
|
+
timestamp: startTime,
|
|
177
|
+
duration: 0,
|
|
178
|
+
success: true,
|
|
179
|
+
custom_metrics: { skipped: true }
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
buildTestResult(step, context, scenarioName, startTime, result) {
|
|
218
183
|
const totalElapsed = Date.now() - startTime;
|
|
219
184
|
const duration = result.response_time !== undefined ? result.response_time : totalElapsed;
|
|
220
185
|
const stepName = step.name || `${step.type}_${context.iteration}`;
|
|
221
|
-
// Generate JMeter-style thread name: "iteration. step_name vu_id-iteration"
|
|
222
186
|
const iteration = context.iteration || 1;
|
|
223
187
|
const threadName = `${iteration}. ${stepName} ${context.vu_id}-${iteration}`;
|
|
224
|
-
|
|
188
|
+
return {
|
|
225
189
|
id: `${context.vu_id}-${Date.now()}`,
|
|
226
190
|
vu_id: context.vu_id,
|
|
227
191
|
iteration: context.iteration,
|
|
@@ -231,7 +195,7 @@ class StepExecutor {
|
|
|
231
195
|
thread_name: threadName,
|
|
232
196
|
timestamp: startTime,
|
|
233
197
|
duration,
|
|
234
|
-
response_time: duration,
|
|
198
|
+
response_time: duration,
|
|
235
199
|
success: result.success,
|
|
236
200
|
status: result.status,
|
|
237
201
|
status_text: result.status_text,
|
|
@@ -246,11 +210,9 @@ class StepExecutor {
|
|
|
246
210
|
response_body: result.response_body,
|
|
247
211
|
custom_metrics: result.custom_metrics,
|
|
248
212
|
shouldRecord: result.shouldRecord !== undefined ? result.shouldRecord : this.shouldRecordStep(step, true),
|
|
249
|
-
// JMeter-style timing breakdown
|
|
250
213
|
sample_start: result.sample_start,
|
|
251
214
|
connect_time: result.connect_time,
|
|
252
215
|
latency: result.latency,
|
|
253
|
-
// JMeter-style size breakdown
|
|
254
216
|
sent_bytes: result.sent_bytes,
|
|
255
217
|
headers_size_sent: result.headers_size_sent,
|
|
256
218
|
body_size_sent: result.body_size_sent,
|
|
@@ -258,29 +220,10 @@ class StepExecutor {
|
|
|
258
220
|
body_size_received: result.body_size_received,
|
|
259
221
|
data_type: result.data_type,
|
|
260
222
|
};
|
|
261
|
-
// Run checks if configured
|
|
262
|
-
if ('checks' in step && step.checks) {
|
|
263
|
-
const checkResults = await this.runChecks(step.checks, result, context);
|
|
264
|
-
if (!checkResults.passed) {
|
|
265
|
-
testResult.success = false;
|
|
266
|
-
testResult.error = `Check failed: ${checkResults.errors.join(', ')}`;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
// Extract data if configured
|
|
270
|
-
if ('extract' in step && step.extract) {
|
|
271
|
-
await this.extractData(step.extract, result, context);
|
|
272
|
-
}
|
|
273
|
-
return testResult;
|
|
274
223
|
}
|
|
275
224
|
shouldRecordStep(step, success) {
|
|
276
|
-
// Always record errors
|
|
277
225
|
if (!success)
|
|
278
226
|
return true;
|
|
279
|
-
// For web steps, only record meaningful performance measurements:
|
|
280
|
-
// - Verifications (verify_*) - time for elements/text to appear (measures app responsiveness)
|
|
281
|
-
// - Waits (wait_for_*) - time for conditions to be met
|
|
282
|
-
// - Performance measurements (measure_*, performance_audit)
|
|
283
|
-
// NOT recorded: goto, click, fill, press, select, hover, screenshot (navigation/interactions)
|
|
284
227
|
if (step.type === 'web' && step.action) {
|
|
285
228
|
const measurableCommands = [
|
|
286
229
|
'verify_exists', 'verify_visible', 'verify_text', 'verify_contains', 'verify_not_exists',
|
|
@@ -296,117 +239,9 @@ class StepExecutor {
|
|
|
296
239
|
if (!handler) {
|
|
297
240
|
throw new Error('REST handler not available');
|
|
298
241
|
}
|
|
299
|
-
|
|
300
|
-
const processedStep = this.processJsonFile(step, context);
|
|
242
|
+
const processedStep = this.jsonPayloadProcessor.processJsonFile(step, context);
|
|
301
243
|
return handler.execute(processedStep, context);
|
|
302
244
|
}
|
|
303
|
-
/**
|
|
304
|
-
* Load JSON payload from file and apply overrides
|
|
305
|
-
* Supports dot notation for nested paths in overrides
|
|
306
|
-
*/
|
|
307
|
-
processJsonFile(step, context) {
|
|
308
|
-
if (!step.jsonFile) {
|
|
309
|
-
return step;
|
|
310
|
-
}
|
|
311
|
-
// Resolve file path relative to CWD
|
|
312
|
-
const filePath = path.resolve(process.cwd(), step.jsonFile);
|
|
313
|
-
if (!fs.existsSync(filePath)) {
|
|
314
|
-
throw new Error(`JSON payload file not found: ${step.jsonFile}`);
|
|
315
|
-
}
|
|
316
|
-
// Load and parse JSON file
|
|
317
|
-
let payload;
|
|
318
|
-
try {
|
|
319
|
-
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
320
|
-
payload = JSON.parse(fileContent);
|
|
321
|
-
}
|
|
322
|
-
catch (error) {
|
|
323
|
-
throw new Error(`Failed to parse JSON file ${step.jsonFile}: ${error.message}`);
|
|
324
|
-
}
|
|
325
|
-
// Apply overrides if specified
|
|
326
|
-
if (step.overrides) {
|
|
327
|
-
payload = this.applyOverrides(payload, step.overrides, context);
|
|
328
|
-
}
|
|
329
|
-
// Return new step with json property set (removing jsonFile and overrides)
|
|
330
|
-
const { jsonFile, overrides, ...restOfStep } = step;
|
|
331
|
-
return {
|
|
332
|
-
...restOfStep,
|
|
333
|
-
json: payload
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Apply overrides to a JSON object using dot notation for nested paths
|
|
338
|
-
* Override values are processed through the template processor
|
|
339
|
-
*/
|
|
340
|
-
applyOverrides(obj, overrides, context) {
|
|
341
|
-
// Deep clone the object to avoid mutating the original
|
|
342
|
-
const result = JSON.parse(JSON.stringify(obj));
|
|
343
|
-
const contextData = {
|
|
344
|
-
__VU: context.vu_id,
|
|
345
|
-
__ITER: context.iteration,
|
|
346
|
-
vu_id: context.vu_id,
|
|
347
|
-
iteration: context.iteration,
|
|
348
|
-
variables: context.variables || {},
|
|
349
|
-
extracted_data: context.extracted_data || {},
|
|
350
|
-
...context.variables,
|
|
351
|
-
...context.extracted_data
|
|
352
|
-
};
|
|
353
|
-
for (const [path, value] of Object.entries(overrides)) {
|
|
354
|
-
// Process template expressions in override values
|
|
355
|
-
let processedValue = value;
|
|
356
|
-
if (typeof value === 'string') {
|
|
357
|
-
processedValue = this.templateProcessor.process(value, contextData);
|
|
358
|
-
// Try to parse as JSON if it looks like a number, boolean, or object
|
|
359
|
-
if (processedValue === 'true')
|
|
360
|
-
processedValue = true;
|
|
361
|
-
else if (processedValue === 'false')
|
|
362
|
-
processedValue = false;
|
|
363
|
-
else if (!isNaN(Number(processedValue)) && processedValue !== '') {
|
|
364
|
-
processedValue = Number(processedValue);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
this.setNestedValue(result, path, processedValue);
|
|
368
|
-
}
|
|
369
|
-
return result;
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Set a value at a nested path using dot notation
|
|
373
|
-
* Example: setNestedValue(obj, 'user.profile.name', 'John')
|
|
374
|
-
*/
|
|
375
|
-
setNestedValue(obj, path, value) {
|
|
376
|
-
const keys = path.split('.');
|
|
377
|
-
let current = obj;
|
|
378
|
-
for (let i = 0; i < keys.length - 1; i++) {
|
|
379
|
-
const key = keys[i];
|
|
380
|
-
// Handle array indices like 'items[0]'
|
|
381
|
-
const arrayMatch = key.match(/^(.+)\[(\d+)]$/);
|
|
382
|
-
if (arrayMatch) {
|
|
383
|
-
const [, prop, index] = arrayMatch;
|
|
384
|
-
if (!current[prop])
|
|
385
|
-
current[prop] = [];
|
|
386
|
-
if (!current[prop][parseInt(index)])
|
|
387
|
-
current[prop][parseInt(index)] = {};
|
|
388
|
-
current = current[prop][parseInt(index)];
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
if (!current[key] || typeof current[key] !== 'object') {
|
|
392
|
-
current[key] = {};
|
|
393
|
-
}
|
|
394
|
-
current = current[key];
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
const lastKey = keys[keys.length - 1];
|
|
398
|
-
// Handle array index in last key
|
|
399
|
-
const arrayMatch = lastKey.match(/^(.+)\[(\d+)]$/);
|
|
400
|
-
if (arrayMatch) {
|
|
401
|
-
const [, prop, index] = arrayMatch;
|
|
402
|
-
if (!current[prop])
|
|
403
|
-
current[prop] = [];
|
|
404
|
-
current[prop][parseInt(index)] = value;
|
|
405
|
-
}
|
|
406
|
-
else {
|
|
407
|
-
current[lastKey] = value;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
245
|
async executeSOAPStep(step, context) {
|
|
411
246
|
const handler = this.handlers.get('soap');
|
|
412
247
|
if (!handler) {
|
|
@@ -450,8 +285,7 @@ class StepExecutor {
|
|
|
450
285
|
}
|
|
451
286
|
async executeRendezvousStep(step, context) {
|
|
452
287
|
const rendezvousManager = rendezvous_1.RendezvousManager.getInstance();
|
|
453
|
-
|
|
454
|
-
let timeoutMs = 30000; // Default 30 seconds
|
|
288
|
+
let timeoutMs = 30000;
|
|
455
289
|
if (step.timeout !== undefined) {
|
|
456
290
|
if (typeof step.timeout === 'number') {
|
|
457
291
|
timeoutMs = step.timeout;
|
|
@@ -482,7 +316,7 @@ class StepExecutor {
|
|
|
482
316
|
rendezvous_reason: result.reason,
|
|
483
317
|
rendezvous_vu_count: result.vuCount
|
|
484
318
|
},
|
|
485
|
-
shouldRecord: true
|
|
319
|
+
shouldRecord: true
|
|
486
320
|
};
|
|
487
321
|
}
|
|
488
322
|
catch (error) {
|
|
@@ -497,18 +331,15 @@ class StepExecutor {
|
|
|
497
331
|
}
|
|
498
332
|
}
|
|
499
333
|
async executeScriptStep(step, context) {
|
|
500
|
-
logger_1.logger.info(
|
|
334
|
+
logger_1.logger.info(`Executing script step: file=${step.file}, function=${step.function}`);
|
|
501
335
|
const { file, function: funcName, params, returns, timeout = 30000 } = step;
|
|
502
336
|
const path = require('path');
|
|
503
337
|
const fs = require('fs');
|
|
504
338
|
try {
|
|
505
|
-
// Resolve file path relative to current working directory
|
|
506
339
|
const filePath = path.resolve(process.cwd(), file);
|
|
507
|
-
// Load the module (supports both .ts and .js)
|
|
508
340
|
let module;
|
|
509
341
|
try {
|
|
510
342
|
if (filePath.endsWith('.ts')) {
|
|
511
|
-
// Transpile TypeScript on the fly using esbuild
|
|
512
343
|
const esbuild = require('esbuild');
|
|
513
344
|
const source = fs.readFileSync(filePath, 'utf-8');
|
|
514
345
|
const result = esbuild.transformSync(source, {
|
|
@@ -516,11 +347,9 @@ class StepExecutor {
|
|
|
516
347
|
format: 'cjs',
|
|
517
348
|
target: 'node18'
|
|
518
349
|
});
|
|
519
|
-
// Create a temporary module from the transpiled code
|
|
520
350
|
const Module = require('module');
|
|
521
351
|
const tempModule = new Module(filePath);
|
|
522
352
|
tempModule.filename = filePath;
|
|
523
|
-
// Include both script dir and cwd node_modules for dependency resolution
|
|
524
353
|
const scriptPaths = Module._nodeModulePaths(path.dirname(filePath));
|
|
525
354
|
const cwdPaths = Module._nodeModulePaths(process.cwd());
|
|
526
355
|
tempModule.paths = [...new Set([...scriptPaths, ...cwdPaths])];
|
|
@@ -528,7 +357,6 @@ class StepExecutor {
|
|
|
528
357
|
module = tempModule.exports;
|
|
529
358
|
}
|
|
530
359
|
else {
|
|
531
|
-
// For JS files, clear require cache and load directly
|
|
532
360
|
delete require.cache[require.resolve(filePath)];
|
|
533
361
|
module = require(filePath);
|
|
534
362
|
}
|
|
@@ -536,12 +364,10 @@ class StepExecutor {
|
|
|
536
364
|
catch (loadError) {
|
|
537
365
|
throw new Error(`Failed to load script file '${file}': ${loadError.message}`);
|
|
538
366
|
}
|
|
539
|
-
// Get the function from the module
|
|
540
367
|
const fn = module[funcName] || module.default?.[funcName];
|
|
541
368
|
if (typeof fn !== 'function') {
|
|
542
369
|
throw new Error(`Function '${funcName}' not found in '${file}'`);
|
|
543
370
|
}
|
|
544
|
-
// Build parameters with context available
|
|
545
371
|
const execParams = {
|
|
546
372
|
...params,
|
|
547
373
|
__context: context,
|
|
@@ -550,14 +376,12 @@ class StepExecutor {
|
|
|
550
376
|
__vu_id: context.vu_id,
|
|
551
377
|
__iteration: context.iteration
|
|
552
378
|
};
|
|
553
|
-
// Execute the function with timeout
|
|
554
379
|
const timeoutPromise = new Promise((_, reject) => {
|
|
555
380
|
setTimeout(() => reject(new Error(`Script execution timeout (${timeout}ms)`)), timeout);
|
|
556
381
|
});
|
|
557
382
|
const resultPromise = Promise.resolve(fn(execParams));
|
|
558
383
|
const result = await Promise.race([resultPromise, timeoutPromise]);
|
|
559
384
|
logger_1.logger.debug(`Script ${funcName} returned: ${JSON.stringify(result)}`);
|
|
560
|
-
// Store return value if specified
|
|
561
385
|
if (returns && result !== undefined) {
|
|
562
386
|
context.extracted_data[returns] = result;
|
|
563
387
|
}
|
|
@@ -616,138 +440,20 @@ class StepExecutor {
|
|
|
616
440
|
};
|
|
617
441
|
logger_1.logger.debug(`StepExecutor processing template for VU${context.vu_id} Iter${context.iteration}`);
|
|
618
442
|
logger_1.logger.debug(`Extracted data keys: ${Object.keys(context.extracted_data || {}).join(', ') || '(none)'}`);
|
|
619
|
-
logger_1.logger.debug(`Context data keys at top level: ${Object.keys(contextData).join(', ')}`);
|
|
620
443
|
const stepStr = JSON.stringify(step);
|
|
621
|
-
logger_1.logger.debug(`Original step JSON: ${stepStr}`);
|
|
622
444
|
const processed = this.templateProcessor.process(stepStr, contextData);
|
|
623
|
-
logger_1.logger.debug(`Raw processed result: ${processed}`);
|
|
624
|
-
logger_1.logger.debug(`Processed result type: ${typeof processed}`);
|
|
625
|
-
let processedStep;
|
|
626
445
|
try {
|
|
627
446
|
if (typeof processed === 'string') {
|
|
628
|
-
|
|
447
|
+
return JSON.parse(processed);
|
|
629
448
|
}
|
|
630
449
|
else {
|
|
631
|
-
|
|
450
|
+
return processed;
|
|
632
451
|
}
|
|
633
452
|
}
|
|
634
453
|
catch (error) {
|
|
635
454
|
logger_1.logger.error(`JSON parsing failed in StepExecutor`);
|
|
636
|
-
logger_1.logger.error(`Processed content (first 500 chars): ${processed.substring(0, 500)}`);
|
|
637
|
-
logger_1.logger.error(`Error: ${error}`);
|
|
638
455
|
throw new Error(`Failed to parse processed step JSON: ${error}`);
|
|
639
456
|
}
|
|
640
|
-
logger_1.logger.debug(`Successfully parsed step: ${JSON.stringify(processedStep)}`);
|
|
641
|
-
return processedStep;
|
|
642
|
-
}
|
|
643
|
-
async runChecks(checks, result, context) {
|
|
644
|
-
const errors = [];
|
|
645
|
-
for (const check of checks) {
|
|
646
|
-
try {
|
|
647
|
-
let passed = false;
|
|
648
|
-
switch (check.type) {
|
|
649
|
-
case 'status':
|
|
650
|
-
passed = result.status === check.value;
|
|
651
|
-
break;
|
|
652
|
-
case 'response_time':
|
|
653
|
-
const threshold = typeof check.value === 'string'
|
|
654
|
-
? (0, time_1.parseTime)(check.value.replace(/[<>]/g, ''))
|
|
655
|
-
: check.value;
|
|
656
|
-
passed = (result.duration || 0) < threshold;
|
|
657
|
-
break;
|
|
658
|
-
case 'json_path':
|
|
659
|
-
const value = this.getJsonPath(result.data, check.value);
|
|
660
|
-
passed = value !== undefined && value !== null;
|
|
661
|
-
break;
|
|
662
|
-
case 'text_contains':
|
|
663
|
-
const text = typeof result.data === 'string' ? result.data : JSON.stringify(result.data);
|
|
664
|
-
passed = text.includes(check.value);
|
|
665
|
-
break;
|
|
666
|
-
case 'custom':
|
|
667
|
-
passed = await this.checkCustom(check.script, result, context);
|
|
668
|
-
break;
|
|
669
|
-
}
|
|
670
|
-
if (!passed) {
|
|
671
|
-
errors.push(check.description || `Check failed: ${check.type}`);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
catch (error) {
|
|
675
|
-
errors.push(`Check error: ${error}`);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
return { passed: errors.length === 0, errors };
|
|
679
|
-
}
|
|
680
|
-
async checkCustom(script, result, context) {
|
|
681
|
-
try {
|
|
682
|
-
const fn = new Function('result', 'context', `return ${script}`);
|
|
683
|
-
return !!fn(result, context);
|
|
684
|
-
}
|
|
685
|
-
catch (error) {
|
|
686
|
-
return false;
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
async extractData(extractors, result, context) {
|
|
690
|
-
for (const extractor of extractors) {
|
|
691
|
-
try {
|
|
692
|
-
let value;
|
|
693
|
-
// Normalize type: accept both "jsonpath" and "json_path"
|
|
694
|
-
const extractType = (extractor.type || 'jsonpath').toLowerCase().replace('_', '');
|
|
695
|
-
// Normalize expression: accept both "path" and "expression"
|
|
696
|
-
const expression = extractor.expression || extractor.path;
|
|
697
|
-
switch (extractType) {
|
|
698
|
-
case 'jsonpath':
|
|
699
|
-
value = this.getJsonPath(result.data, expression);
|
|
700
|
-
break;
|
|
701
|
-
case 'regex':
|
|
702
|
-
const match = String(result.data).match(new RegExp(expression));
|
|
703
|
-
value = match ? (match[1] || match[0]) : null;
|
|
704
|
-
break;
|
|
705
|
-
case 'header':
|
|
706
|
-
value = result.headers?.[expression.toLowerCase()];
|
|
707
|
-
break;
|
|
708
|
-
case 'custom':
|
|
709
|
-
value = await this.extractCustom(extractor.script, result, context);
|
|
710
|
-
break;
|
|
711
|
-
default:
|
|
712
|
-
// Default to jsonpath if type not recognized but path/expression provided
|
|
713
|
-
if (expression) {
|
|
714
|
-
value = this.getJsonPath(result.data, expression);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
if (value !== null && value !== undefined) {
|
|
718
|
-
context.extracted_data[extractor.name] = value;
|
|
719
|
-
logger_1.logger.debug(`Extracted ${extractor.name} = ${JSON.stringify(value)}`);
|
|
720
|
-
}
|
|
721
|
-
else if (extractor.default !== undefined) {
|
|
722
|
-
context.extracted_data[extractor.name] = extractor.default;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
catch (error) {
|
|
726
|
-
logger_1.logger.debug(`Extraction failed for ${extractor.name}: ${error}`);
|
|
727
|
-
if (extractor.default !== undefined) {
|
|
728
|
-
context.extracted_data[extractor.name] = extractor.default;
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
async extractCustom(script, result, context) {
|
|
734
|
-
try {
|
|
735
|
-
const fn = new Function('result', 'context', `return ${script}`);
|
|
736
|
-
return fn(result, context);
|
|
737
|
-
}
|
|
738
|
-
catch (error) {
|
|
739
|
-
return null;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
getJsonPath(obj, path) {
|
|
743
|
-
const keys = path.replace(/^\$\./, '').split('.');
|
|
744
|
-
return keys.reduce((current, key) => {
|
|
745
|
-
if (key.includes('[') && key.includes(']')) {
|
|
746
|
-
const [prop, index] = key.split(/[\[\]]/);
|
|
747
|
-
return current && current[prop] && current[prop][parseInt(index)];
|
|
748
|
-
}
|
|
749
|
-
return current && current[key];
|
|
750
|
-
}, typeof obj === 'string' ? JSON.parse(obj) : obj);
|
|
751
457
|
}
|
|
752
458
|
}
|
|
753
459
|
exports.StepExecutor = StepExecutor;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScenarioSelector = exports.ThinkTimeStrategy = void 0;
|
|
4
|
+
var think_time_strategy_1 = require("./think-time-strategy");
|
|
5
|
+
Object.defineProperty(exports, "ThinkTimeStrategy", { enumerable: true, get: function () { return think_time_strategy_1.ThinkTimeStrategy; } });
|
|
6
|
+
var scenario_selector_1 = require("./scenario-selector");
|
|
7
|
+
Object.defineProperty(exports, "ScenarioSelector", { enumerable: true, get: function () { return scenario_selector_1.ScenarioSelector; } });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Scenario } from '../../config/types/hooks';
|
|
2
|
+
export declare class ScenarioSelector {
|
|
3
|
+
/**
|
|
4
|
+
* Select scenarios based on weights using proportional distribution.
|
|
5
|
+
*
|
|
6
|
+
* Weights determine the probability of a scenario being selected:
|
|
7
|
+
* - scenario1 (weight: 50) + scenario2 (weight: 25) + scenario3 (weight: 25) = 100
|
|
8
|
+
* - 50% of VUs will run scenario1, 25% scenario2, 25% scenario3
|
|
9
|
+
*
|
|
10
|
+
* If weights don't sum to 100, they are normalized proportionally.
|
|
11
|
+
*/
|
|
12
|
+
selectScenarios(scenarios: Scenario[]): Scenario[];
|
|
13
|
+
}
|