@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
package/dist/cli/cli.js
CHANGED
|
@@ -10,6 +10,7 @@ const init_1 = require("./commands/init");
|
|
|
10
10
|
const mock_1 = require("./commands/mock");
|
|
11
11
|
const dashboard_1 = require("./commands/dashboard");
|
|
12
12
|
const native_recorder_1 = require("../recorder/native-recorder");
|
|
13
|
+
const continue_recorder_1 = require("../recorder/continue-recorder");
|
|
13
14
|
const distributed_1 = require("./commands/distributed");
|
|
14
15
|
// Add new import commands
|
|
15
16
|
const import_1 = require("./commands/import");
|
|
@@ -77,13 +78,27 @@ program
|
|
|
77
78
|
program
|
|
78
79
|
.command('record')
|
|
79
80
|
.description('Record web interactions for test creation (Ctrl+W to add wait points)')
|
|
80
|
-
.argument('
|
|
81
|
+
.argument('[url]', 'Starting URL for recording (required unless using --continue)')
|
|
82
|
+
.option('-c, --continue <file>', 'Continue recording from last step of existing scenario file')
|
|
81
83
|
.option('-o, --output <file>', 'Output file for recorded scenario')
|
|
82
84
|
.option('--viewport <viewport>', 'Browser viewport size (e.g., 1920x1080)')
|
|
83
85
|
.option('--base-url <url>', 'Base URL to relativize recorded URLs')
|
|
84
86
|
.option('-b, --browser <browser>', 'Browser to use: chromium, chrome, msedge, firefox, webkit', 'chromium')
|
|
85
87
|
.option('-f, --format <format>', 'Output format: yaml, json, or typescript', 'yaml')
|
|
86
88
|
.action(async (url, options) => {
|
|
89
|
+
// Handle --continue mode
|
|
90
|
+
if (options.continue) {
|
|
91
|
+
await (0, continue_recorder_1.startContinueRecording)(options.continue, {
|
|
92
|
+
format: options.format,
|
|
93
|
+
browser: options.browser
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// Normal recording mode requires URL
|
|
98
|
+
if (!url) {
|
|
99
|
+
console.error('Error: URL is required for recording. Use --continue <file> to continue from an existing scenario.');
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
87
102
|
// Auto-determine file extension if output not specified
|
|
88
103
|
// Save to tests/web directory by default
|
|
89
104
|
if (!options.output) {
|
|
@@ -105,8 +105,8 @@ async function distributedCommand(configPath, options) {
|
|
|
105
105
|
// Generate report if requested
|
|
106
106
|
if (options.report || testConfig.report?.generate) {
|
|
107
107
|
try {
|
|
108
|
-
const {
|
|
109
|
-
const generator = new
|
|
108
|
+
const { HTMLReportGenerator } = await Promise.resolve().then(() => __importStar(require('../../reporting/html-generator')));
|
|
109
|
+
const generator = new HTMLReportGenerator();
|
|
110
110
|
const timestamp = timestamp_helper_1.TimestampHelper.getTimestamp('file');
|
|
111
111
|
const reportFilename = `distributed-report-${timestamp}.html`;
|
|
112
112
|
// Always use timestamped filename for distributed reports in reports folder
|
|
@@ -36,7 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.reportCommand = reportCommand;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
|
-
const
|
|
39
|
+
const html_generator_1 = require("../../reporting/html-generator");
|
|
40
40
|
const logger_1 = require("../../utils/logger");
|
|
41
41
|
async function reportCommand(resultsPath, options) {
|
|
42
42
|
try {
|
|
@@ -54,7 +54,7 @@ async function reportCommand(resultsPath, options) {
|
|
|
54
54
|
logger_1.logger.error('❌ Only JSON results files are currently supported');
|
|
55
55
|
process.exit(1);
|
|
56
56
|
}
|
|
57
|
-
const generator = new
|
|
57
|
+
const generator = new html_generator_1.HTMLReportGenerator();
|
|
58
58
|
const outputPath = options.output || 'report.html';
|
|
59
59
|
await generator.generate({
|
|
60
60
|
testName: options.title || resultsData.testName || 'Performance Test',
|
package/dist/cli/commands/run.js
CHANGED
package/dist/config/parser.js
CHANGED
|
@@ -39,7 +39,7 @@ const YAML = __importStar(require("yaml"));
|
|
|
39
39
|
const fs = __importStar(require("fs"));
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const template_1 = require("../utils/template");
|
|
42
|
-
const
|
|
42
|
+
const data_1 = require("../core/data");
|
|
43
43
|
const logger_1 = require("../utils/logger");
|
|
44
44
|
class ConfigParser {
|
|
45
45
|
constructor() {
|
|
@@ -263,7 +263,7 @@ class ConfigParser {
|
|
|
263
263
|
const configStr = JSON.stringify(config);
|
|
264
264
|
const hasTemplates = configStr.includes('{{template:') || configStr.includes('{{csv:');
|
|
265
265
|
if (hasCSVScenarios || hasTemplates) {
|
|
266
|
-
|
|
266
|
+
data_1.DataProvider.setBaseDir(baseDir);
|
|
267
267
|
template_1.TemplateProcessor.setBaseDir(baseDir);
|
|
268
268
|
logger_1.logger.debug(`Set base directory for CSV/templates: ${baseDir}`);
|
|
269
269
|
}
|
|
@@ -12,6 +12,24 @@ export interface GlobalConfig {
|
|
|
12
12
|
csv_data?: GlobalCSVConfig;
|
|
13
13
|
/** CSV data access mode: next (round-robin), unique (exhaustible), random */
|
|
14
14
|
csv_mode?: 'next' | 'unique' | 'random';
|
|
15
|
+
/** InfluxDB configuration for storing test metrics (optional) */
|
|
16
|
+
influxdb?: InfluxDBConfig;
|
|
17
|
+
}
|
|
18
|
+
export interface InfluxDBConfig {
|
|
19
|
+
/** Enable InfluxDB storage for test metrics */
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
/** InfluxDB server URL (default: http://localhost:8086 or INFLUXDB_URL env var) */
|
|
22
|
+
url?: string;
|
|
23
|
+
/** InfluxDB authentication token (default: INFLUXDB_TOKEN env var) */
|
|
24
|
+
token?: string;
|
|
25
|
+
/** InfluxDB organization (default: 'perfornium' or INFLUXDB_ORG env var) */
|
|
26
|
+
org?: string;
|
|
27
|
+
/** InfluxDB bucket name (default: 'metrics' or INFLUXDB_BUCKET env var) */
|
|
28
|
+
bucket?: string;
|
|
29
|
+
/** Write batch size (default: 100) */
|
|
30
|
+
batch_size?: number;
|
|
31
|
+
/** Flush interval in ms (default: 1000) */
|
|
32
|
+
flush_interval?: number;
|
|
15
33
|
}
|
|
16
34
|
export interface GlobalCSVConfig {
|
|
17
35
|
/** Path to the CSV file */
|
|
@@ -28,12 +46,52 @@ export interface GlobalCSVConfig {
|
|
|
28
46
|
columns?: string[];
|
|
29
47
|
/** Filter expression (e.g., "status=active") */
|
|
30
48
|
filter?: string;
|
|
31
|
-
/** Shuffle data randomly */
|
|
49
|
+
/** Shuffle data randomly (alias for distribution.order = 'random') */
|
|
32
50
|
randomize?: boolean;
|
|
33
|
-
/** Restart from beginning when data is exhausted (default: true) */
|
|
51
|
+
/** Restart from beginning when data is exhausted (default: true) - legacy, use distribution.on_exhausted */
|
|
34
52
|
cycleOnExhaustion?: boolean;
|
|
35
53
|
/** Map CSV column names to custom variable names: { "csv_column": "variable_name" } */
|
|
36
54
|
variables?: Record<string, string>;
|
|
55
|
+
/**
|
|
56
|
+
* Value Change Policy - determines when a new value is fetched
|
|
57
|
+
* - each_use: New value for every request/step (default for unique scope)
|
|
58
|
+
* - each_iteration: New value at the start of each iteration (default for global/local scope)
|
|
59
|
+
* - each_vu: Same value for all iterations of a VU (sticky per VU)
|
|
60
|
+
*/
|
|
61
|
+
change_policy?: 'each_use' | 'each_iteration' | 'each_vu';
|
|
62
|
+
/**
|
|
63
|
+
* Value Distribution Policy - comprehensive control over data distribution
|
|
64
|
+
*/
|
|
65
|
+
distribution?: DataDistributionPolicy;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Value Distribution Policy
|
|
69
|
+
* Controls how data values are distributed across virtual users
|
|
70
|
+
*/
|
|
71
|
+
export interface DataDistributionPolicy {
|
|
72
|
+
/**
|
|
73
|
+
* Scope - determines value sharing between VUs
|
|
74
|
+
* - local: Each VU has its own copy of the data (values can repeat across VUs)
|
|
75
|
+
* - global: Values are shared across all VUs (round-robin distribution)
|
|
76
|
+
* - unique: Global scope with exclusive locking - a value can only be used by one VU at a time,
|
|
77
|
+
* returned to the pool when the VU's iteration/use completes
|
|
78
|
+
*/
|
|
79
|
+
scope?: 'local' | 'global' | 'unique';
|
|
80
|
+
/**
|
|
81
|
+
* Order - how values are selected from the data
|
|
82
|
+
* - sequential: Values are taken in the order they appear in the file
|
|
83
|
+
* - random: Values are taken in random order
|
|
84
|
+
* - any: Best-effort sequential (more efficient, may vary under high concurrency)
|
|
85
|
+
*/
|
|
86
|
+
order?: 'sequential' | 'random' | 'any';
|
|
87
|
+
/**
|
|
88
|
+
* On Exhausted - what happens when all values have been used
|
|
89
|
+
* - cycle: Start over from the beginning (default)
|
|
90
|
+
* - stop_vu: Stop the current VU (it won't get more data)
|
|
91
|
+
* - stop_test: Stop the entire test
|
|
92
|
+
* - no_value: Return undefined/empty for subsequent requests
|
|
93
|
+
*/
|
|
94
|
+
on_exhausted?: 'cycle' | 'stop_vu' | 'stop_test' | 'no_value';
|
|
37
95
|
}
|
|
38
96
|
export interface FakerConfig {
|
|
39
97
|
locale?: string;
|
|
@@ -52,6 +110,28 @@ export interface BrowserConfig {
|
|
|
52
110
|
clear_storage?: boolean | ClearStorageConfig;
|
|
53
111
|
/** Capture screenshot on test failure */
|
|
54
112
|
screenshot_on_failure?: boolean | ScreenshotConfig;
|
|
113
|
+
/** Capture HTTP network calls during web tests */
|
|
114
|
+
network_capture?: NetworkCaptureConfig;
|
|
115
|
+
}
|
|
116
|
+
export interface NetworkCaptureConfig {
|
|
117
|
+
/** Enable network call capturing */
|
|
118
|
+
enabled: boolean;
|
|
119
|
+
/** Glob patterns for URLs to capture - if empty, captures all */
|
|
120
|
+
include_patterns?: string[];
|
|
121
|
+
/** Glob patterns for URLs to exclude */
|
|
122
|
+
exclude_patterns?: string[];
|
|
123
|
+
/** Capture request body (default: false) */
|
|
124
|
+
capture_request_body?: boolean;
|
|
125
|
+
/** Capture response body (default: false) */
|
|
126
|
+
capture_response_body?: boolean;
|
|
127
|
+
/** Maximum body size to capture in bytes (default: 10240 = 10KB) */
|
|
128
|
+
max_body_size?: number;
|
|
129
|
+
/** Only capture bodies with these content types */
|
|
130
|
+
content_type_filters?: string[];
|
|
131
|
+
/** Store network calls in TestResult.custom_metrics (default: true) */
|
|
132
|
+
store_inline?: boolean;
|
|
133
|
+
/** Emit NETWORK events for dashboard live updates (default: true) */
|
|
134
|
+
store_separate?: boolean;
|
|
55
135
|
}
|
|
56
136
|
export interface ScreenshotConfig {
|
|
57
137
|
enabled: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { CSVDataConfig } from '../../core/csv-data-provider';
|
|
2
1
|
import { Step } from './step-types';
|
|
3
2
|
import { ScenarioHooks, LoopHooks } from './hooks';
|
|
3
|
+
import { GlobalCSVConfig } from './global-config';
|
|
4
4
|
export interface Scenario {
|
|
5
5
|
name: string;
|
|
6
6
|
description?: string;
|
|
@@ -11,7 +11,7 @@ export interface Scenario {
|
|
|
11
11
|
teardown?: string;
|
|
12
12
|
variables?: Record<string, any>;
|
|
13
13
|
steps: Step[];
|
|
14
|
-
csv_data?:
|
|
14
|
+
csv_data?: GlobalCSVConfig;
|
|
15
15
|
csv_mode?: 'next' | 'unique' | 'random';
|
|
16
16
|
condition?: string;
|
|
17
17
|
enabled?: boolean;
|
|
@@ -97,7 +97,7 @@ export interface RendezvousStep extends BaseStep {
|
|
|
97
97
|
export interface WebAction {
|
|
98
98
|
name?: string;
|
|
99
99
|
expected_text?: string;
|
|
100
|
-
command: 'goto' | 'click' | 'fill' | 'press' | 'select' | 'hover' | 'screenshot' | 'wait_for_selector' | 'wait_for_text' | 'verify_text' | 'verify_contains' | 'verify_not_exists' | 'verify_exists' | 'verify_visible' | 'evaluate' | 'measure_web_vitals' | 'measure_verification' | 'performance_audit' | 'accessibility_audit' | 'wait_for_load_state' | 'network_idle' | 'dom_ready';
|
|
100
|
+
command: 'goto' | 'click' | 'fill' | 'press' | 'select' | 'hover' | 'screenshot' | 'wait_for_selector' | 'wait_for_text' | 'verify_text' | 'verify_contains' | 'verify_not_exists' | 'verify_exists' | 'verify_visible' | 'verify_value' | 'evaluate' | 'measure_web_vitals' | 'measure_verification' | 'performance_audit' | 'accessibility_audit' | 'wait_for_load_state' | 'network_idle' | 'dom_ready';
|
|
101
101
|
selector?: string;
|
|
102
102
|
url?: string;
|
|
103
103
|
value?: string | string[];
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Scenario } from '../../config/types/hooks';
|
|
2
|
+
import { GlobalCSVConfig } from '../../config/types/global-config';
|
|
3
|
+
import { DataRow } from './data-provider';
|
|
4
|
+
export interface DataOptions {
|
|
5
|
+
config: GlobalCSVConfig;
|
|
6
|
+
/** Legacy mode option - maps to distribution.order */
|
|
7
|
+
mode?: 'next' | 'unique' | 'random';
|
|
8
|
+
}
|
|
9
|
+
export interface DataContext {
|
|
10
|
+
csv_data?: DataRow;
|
|
11
|
+
global_csv_data?: DataRow;
|
|
12
|
+
variables: Record<string, any>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Data Manager for VU data lifecycle:
|
|
16
|
+
* - Manages global and scenario-specific data providers
|
|
17
|
+
* - Handles iteration start/end for checkout/checkin
|
|
18
|
+
* - Supports change policies (each_use, each_iteration, each_vu)
|
|
19
|
+
* - Supports distribution scopes (local, global, unique)
|
|
20
|
+
* - Supports exhaustion policies (cycle, stop_vu, stop_test, no_value)
|
|
21
|
+
*/
|
|
22
|
+
export declare class DataManager {
|
|
23
|
+
private vuId;
|
|
24
|
+
private currentIteration;
|
|
25
|
+
private globalProvider?;
|
|
26
|
+
private scenarioProviders;
|
|
27
|
+
private shouldStopVU;
|
|
28
|
+
private shouldStopTest;
|
|
29
|
+
constructor(vuId: number, globalData?: DataOptions);
|
|
30
|
+
/**
|
|
31
|
+
* Map legacy mode to new distribution config
|
|
32
|
+
*/
|
|
33
|
+
private mapLegacyMode;
|
|
34
|
+
/**
|
|
35
|
+
* Initialize providers for scenarios
|
|
36
|
+
*/
|
|
37
|
+
initializeForScenarios(scenarios: Scenario[]): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Called at the start of each iteration
|
|
40
|
+
*/
|
|
41
|
+
startIteration(iteration: number): void;
|
|
42
|
+
/**
|
|
43
|
+
* Called at the end of each iteration - releases any checked-out rows
|
|
44
|
+
*/
|
|
45
|
+
endIteration(iteration: number): void;
|
|
46
|
+
/**
|
|
47
|
+
* Load global CSV data into context
|
|
48
|
+
*/
|
|
49
|
+
loadGlobalData(context: DataContext): Promise<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* Load scenario-specific CSV data into context
|
|
52
|
+
*/
|
|
53
|
+
loadScenarioData(scenario: Scenario, context: DataContext): Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Handle exhaustion result and set stop flags
|
|
56
|
+
*/
|
|
57
|
+
private handleExhaustionResult;
|
|
58
|
+
/**
|
|
59
|
+
* Check if VU should stop due to data exhaustion
|
|
60
|
+
*/
|
|
61
|
+
shouldStop(): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Check if test should stop
|
|
64
|
+
*/
|
|
65
|
+
shouldStopEntireTest(): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Get status for debugging
|
|
68
|
+
*/
|
|
69
|
+
getStatus(): Record<string, any>;
|
|
70
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DataManager = void 0;
|
|
4
|
+
const data_provider_1 = require("./data-provider");
|
|
5
|
+
const logger_1 = require("../../utils/logger");
|
|
6
|
+
/**
|
|
7
|
+
* Data Manager for VU data lifecycle:
|
|
8
|
+
* - Manages global and scenario-specific data providers
|
|
9
|
+
* - Handles iteration start/end for checkout/checkin
|
|
10
|
+
* - Supports change policies (each_use, each_iteration, each_vu)
|
|
11
|
+
* - Supports distribution scopes (local, global, unique)
|
|
12
|
+
* - Supports exhaustion policies (cycle, stop_vu, stop_test, no_value)
|
|
13
|
+
*/
|
|
14
|
+
class DataManager {
|
|
15
|
+
constructor(vuId, globalData) {
|
|
16
|
+
this.currentIteration = 0;
|
|
17
|
+
this.scenarioProviders = new Map();
|
|
18
|
+
this.shouldStopVU = false;
|
|
19
|
+
this.shouldStopTest = false;
|
|
20
|
+
this.vuId = vuId;
|
|
21
|
+
if (globalData?.config) {
|
|
22
|
+
// Apply legacy mode mapping if no distribution is set
|
|
23
|
+
const config = { ...globalData.config };
|
|
24
|
+
if (globalData.mode && !config.distribution) {
|
|
25
|
+
config.distribution = this.mapLegacyMode(globalData.mode);
|
|
26
|
+
logger_1.logger.debug(`VU${vuId}: Mapped legacy mode '${globalData.mode}' to distribution`);
|
|
27
|
+
}
|
|
28
|
+
this.globalProvider = data_provider_1.DataProvider.getInstance(config);
|
|
29
|
+
logger_1.logger.debug(`VU${vuId}: Global data provider configured`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Map legacy mode to new distribution config
|
|
34
|
+
*/
|
|
35
|
+
mapLegacyMode(mode) {
|
|
36
|
+
switch (mode) {
|
|
37
|
+
case 'unique':
|
|
38
|
+
return { scope: 'unique', order: 'sequential', on_exhausted: 'stop_vu' };
|
|
39
|
+
case 'random':
|
|
40
|
+
return { scope: 'global', order: 'random', on_exhausted: 'cycle' };
|
|
41
|
+
case 'next':
|
|
42
|
+
default:
|
|
43
|
+
return { scope: 'global', order: 'sequential', on_exhausted: 'cycle' };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Initialize providers for scenarios
|
|
48
|
+
*/
|
|
49
|
+
async initializeForScenarios(scenarios) {
|
|
50
|
+
const csvScenarios = scenarios.filter(s => s.csv_data);
|
|
51
|
+
for (const scenario of csvScenarios) {
|
|
52
|
+
const csvConfig = scenario.csv_data;
|
|
53
|
+
if (csvConfig) {
|
|
54
|
+
try {
|
|
55
|
+
const provider = data_provider_1.DataProvider.getInstance(csvConfig);
|
|
56
|
+
await provider.loadData();
|
|
57
|
+
this.scenarioProviders.set(scenario.name, provider);
|
|
58
|
+
logger_1.logger.debug(`VU${this.vuId}: Initialized data provider for scenario "${scenario.name}"`);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
logger_1.logger.warn(`VU${this.vuId}: Failed to initialize data for scenario "${scenario.name}":`, error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Called at the start of each iteration
|
|
68
|
+
*/
|
|
69
|
+
startIteration(iteration) {
|
|
70
|
+
this.currentIteration = iteration;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Called at the end of each iteration - releases any checked-out rows
|
|
74
|
+
*/
|
|
75
|
+
endIteration(iteration) {
|
|
76
|
+
if (this.globalProvider) {
|
|
77
|
+
this.globalProvider.releaseRow(this.vuId, iteration);
|
|
78
|
+
}
|
|
79
|
+
for (const provider of this.scenarioProviders.values()) {
|
|
80
|
+
provider.releaseRow(this.vuId, iteration);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Load global CSV data into context
|
|
85
|
+
*/
|
|
86
|
+
async loadGlobalData(context) {
|
|
87
|
+
if (!this.globalProvider) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
if (this.shouldStopVU || this.shouldStopTest) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const result = await this.globalProvider.getRow(this.vuId, this.currentIteration);
|
|
95
|
+
if (result.row) {
|
|
96
|
+
context.global_csv_data = result.row;
|
|
97
|
+
for (const [key, value] of Object.entries(result.row)) {
|
|
98
|
+
context.variables[key] = value;
|
|
99
|
+
logger_1.logger.debug(`VU${this.vuId}: Set global data variable: ${key} = ${value}`);
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
return this.handleExhaustionResult(result);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
logger_1.logger.warn(`VU${this.vuId}: Failed to load global data:`, error);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Load scenario-specific CSV data into context
|
|
112
|
+
*/
|
|
113
|
+
async loadScenarioData(scenario, context) {
|
|
114
|
+
const provider = this.scenarioProviders.get(scenario.name);
|
|
115
|
+
if (!provider) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (this.shouldStopVU || this.shouldStopTest) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const result = await provider.getRow(this.vuId, this.currentIteration);
|
|
123
|
+
if (result.row) {
|
|
124
|
+
context.csv_data = result.row;
|
|
125
|
+
for (const [key, value] of Object.entries(result.row)) {
|
|
126
|
+
if (!(key in context.variables)) {
|
|
127
|
+
context.variables[key] = value;
|
|
128
|
+
logger_1.logger.debug(`VU${this.vuId}: Set scenario data variable: ${key} = ${value}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
return this.handleExhaustionResult(result);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
logger_1.logger.warn(`VU${this.vuId}: Failed to load scenario data:`, error);
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Handle exhaustion result and set stop flags
|
|
142
|
+
*/
|
|
143
|
+
handleExhaustionResult(result) {
|
|
144
|
+
if (result.action === 'stop_test') {
|
|
145
|
+
this.shouldStopTest = true;
|
|
146
|
+
throw new Error('CSV_DATA_EXHAUSTED_STOP_TEST');
|
|
147
|
+
}
|
|
148
|
+
if (result.action === 'stop_vu') {
|
|
149
|
+
this.shouldStopVU = true;
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
// no_value: continue but with null data
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Check if VU should stop due to data exhaustion
|
|
157
|
+
*/
|
|
158
|
+
shouldStop() {
|
|
159
|
+
return this.shouldStopVU || this.shouldStopTest;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Check if test should stop
|
|
163
|
+
*/
|
|
164
|
+
shouldStopEntireTest() {
|
|
165
|
+
return this.shouldStopTest;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get status for debugging
|
|
169
|
+
*/
|
|
170
|
+
getStatus() {
|
|
171
|
+
const status = {
|
|
172
|
+
vuId: this.vuId,
|
|
173
|
+
iteration: this.currentIteration,
|
|
174
|
+
shouldStopVU: this.shouldStopVU,
|
|
175
|
+
shouldStopTest: this.shouldStopTest
|
|
176
|
+
};
|
|
177
|
+
if (this.globalProvider) {
|
|
178
|
+
status.globalProvider = this.globalProvider.getStatus();
|
|
179
|
+
}
|
|
180
|
+
for (const [name, provider] of this.scenarioProviders) {
|
|
181
|
+
status[`scenario_${name}`] = provider.getStatus();
|
|
182
|
+
}
|
|
183
|
+
return status;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
exports.DataManager = DataManager;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { GlobalCSVConfig } from '../../config/types/global-config';
|
|
2
|
+
export interface DataRow {
|
|
3
|
+
[key: string]: string | number | boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface CheckedOutRow {
|
|
6
|
+
row: DataRow;
|
|
7
|
+
vuId: number;
|
|
8
|
+
iteration: number;
|
|
9
|
+
checkoutTime: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Result from getting a data row
|
|
13
|
+
*/
|
|
14
|
+
export interface DataResult {
|
|
15
|
+
row: DataRow | null;
|
|
16
|
+
exhausted: boolean;
|
|
17
|
+
action?: 'stop_vu' | 'stop_test' | 'no_value';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Data Provider with full support for:
|
|
21
|
+
* - Simple API: getNextRow, getUniqueRow, getRandomRow (for templates)
|
|
22
|
+
* - Advanced API: getRow with distribution/change policies (for VUs)
|
|
23
|
+
* - Value Change Policy (each_use, each_iteration, each_vu)
|
|
24
|
+
* - Value Distribution (scope: local/global/unique, order: sequential/random/any)
|
|
25
|
+
* - Exhaustion Policy (cycle, stop_vu, stop_test, no_value)
|
|
26
|
+
*/
|
|
27
|
+
export declare class DataProvider {
|
|
28
|
+
private static instances;
|
|
29
|
+
private config;
|
|
30
|
+
private filePath;
|
|
31
|
+
private data;
|
|
32
|
+
private originalData;
|
|
33
|
+
private isLoaded;
|
|
34
|
+
private globalIndex;
|
|
35
|
+
private checkedOutRows;
|
|
36
|
+
private availableIndices;
|
|
37
|
+
private vuCache;
|
|
38
|
+
private iterationCache;
|
|
39
|
+
private isExhausted;
|
|
40
|
+
private exhaustedVUs;
|
|
41
|
+
private accessCount;
|
|
42
|
+
private constructor();
|
|
43
|
+
static getInstance(config: GlobalCSVConfig): DataProvider;
|
|
44
|
+
static setBaseDir(dir: string): void;
|
|
45
|
+
static clearInstances(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Get next row in sequence (cycles by default)
|
|
48
|
+
*/
|
|
49
|
+
getNextRow(vuId: number): Promise<DataRow | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Get unique row (each row used once, then cycles or exhausts)
|
|
52
|
+
*/
|
|
53
|
+
getUniqueRow(vuId: number): Promise<DataRow | null>;
|
|
54
|
+
/**
|
|
55
|
+
* Get random row
|
|
56
|
+
*/
|
|
57
|
+
getRandomRow(vuId?: number): Promise<DataRow | null>;
|
|
58
|
+
/**
|
|
59
|
+
* Get a data row for a VU with full policy support
|
|
60
|
+
*/
|
|
61
|
+
getRow(vuId: number, iteration?: number): Promise<DataResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Release a checked-out row back to the pool (for unique scope)
|
|
64
|
+
*/
|
|
65
|
+
releaseRow(vuId: number, iteration: number): void;
|
|
66
|
+
private getDistribution;
|
|
67
|
+
private getChangePolicy;
|
|
68
|
+
loadData(): Promise<void>;
|
|
69
|
+
private getNewRow;
|
|
70
|
+
private getLocalRow;
|
|
71
|
+
private getGlobalRow;
|
|
72
|
+
private getUniqueRowAdvanced;
|
|
73
|
+
private handleExhaustion;
|
|
74
|
+
private filterData;
|
|
75
|
+
/**
|
|
76
|
+
* Get current status for debugging
|
|
77
|
+
*/
|
|
78
|
+
getStatus(): {
|
|
79
|
+
totalRows: number;
|
|
80
|
+
availableRows: number;
|
|
81
|
+
checkedOutRows: number;
|
|
82
|
+
exhausted: boolean;
|
|
83
|
+
exhaustedVUs: number[];
|
|
84
|
+
};
|
|
85
|
+
}
|