Skip to content

Commit f7ddd5f

Browse files
committed
[feat] Parse K6 command output from stdout
- Ignore progress updates from stdout - Extract cloud test run urls from k6 command output
1 parent cd405e1 commit f7ddd5f

File tree

4 files changed

+158
-5
lines changed

4 files changed

+158
-5
lines changed

dist/index.js

+75-2
Original file line numberDiff line numberDiff line change
@@ -29899,7 +29899,8 @@ const core = __importStar(__nccwpck_require__(2186));
2989929899
const glob = __importStar(__nccwpck_require__(8090));
2990029900
const child_process_1 = __nccwpck_require__(2081);
2990129901
const fs = __importStar(__nccwpck_require__(5630));
29902-
var TEST_PIDS = [];
29902+
const k6OutputParser_1 = __nccwpck_require__(5035);
29903+
const TEST_PIDS = [], TEST_RESULT_URLS_MAP = {};
2990329904
run();
2990429905
/**
2990529906
* The main function for the action.
@@ -29913,7 +29914,7 @@ async function run() {
2991329914
const flags = core.getInput('flags', { required: false });
2991429915
const cloudRunLocally = core.getInput('cloud-run-locally', { required: false }) === 'true';
2991529916
const isCloud = await isCloudIntegrationEnabled();
29916-
const commands = testPaths.map(testPath => generateCommand(testPath));
29917+
const commands = testPaths.map(testPath => generateCommand(testPath)), TOTAL_TEST_RUNS = commands.length;
2991729918
if (commands.length === 0) {
2991829919
throw new Error('No test files found');
2991929920
}
@@ -30010,6 +30011,9 @@ async function run() {
3001030011
detached: true,
3001130012
env: process.env,
3001230013
});
30014+
// Parse k6 command output and extract test run URLs if running in cloud mode.
30015+
// Also, print the output to the console, excluding the progress lines.
30016+
child.stdout?.on('data', (data) => (0, k6OutputParser_1.parseK6Output)(data, isCloud ? TEST_RESULT_URLS_MAP : null, TOTAL_TEST_RUNS));
3001330017
return child;
3001430018
}
3001530019
}
@@ -30056,6 +30060,75 @@ function isDirectory(filepath) {
3005630060
}
3005730061

3005830062

30063+
/***/ }),
30064+
30065+
/***/ 5035:
30066+
/***/ ((__unused_webpack_module, exports) => {
30067+
30068+
"use strict";
30069+
30070+
Object.defineProperty(exports, "__esModule", ({ value: true }));
30071+
exports.parseK6Output = void 0;
30072+
const REGEX_EXPRESSIONS = {
30073+
scriptPath: /^\s*script:\s*(.+)$/m,
30074+
output: /^\s*output:\s*(.+)$/m,
30075+
runningIteration: /running \(\d+m\d+\.\d+s\), \d+\/\d+ VUs, \d+ complete and \d+ interrupted iterations/g,
30076+
runProgress: /\[ *(\d+)% *\] *\d+ VUs *\d+(\.\d+)?s\/\d+(\.\d+)?s/g
30077+
};
30078+
function extractTestRunUrl(data, testResultUrlsMap) {
30079+
/**
30080+
* This function extracts the script path and output URL from the k6 output.
30081+
* It then adds the script path and output URL to the testResultUrlsMap which is a reference to
30082+
* an object passed from the main function to store test run urls mapped to corresponding test script.
30083+
*
30084+
* @param {string} data - The k6 command output data as string
30085+
* @param {TestResultUrlsMap} testResultUrlsMap - The map containing the script path and output URL
30086+
*
30087+
* @returns {void}
30088+
*
30089+
*/
30090+
// Extracting the script path
30091+
const scriptMatch = data.match(REGEX_EXPRESSIONS.scriptPath);
30092+
const scriptPath = scriptMatch ? scriptMatch[1] : null;
30093+
// Extracting the output URL
30094+
const outputMatch = data.match(REGEX_EXPRESSIONS.output);
30095+
const output = outputMatch ? outputMatch[1] : null;
30096+
if (scriptPath && output) {
30097+
testResultUrlsMap[scriptPath] = output;
30098+
}
30099+
}
30100+
function parseK6Output(data, testResultUrlsMap, totalTestRuns) {
30101+
/*
30102+
* This function is responsible for parsing the output of the k6 command.
30103+
* It filters out the progress lines and logs the rest of the output.
30104+
* It also extracts the test run URLs from the output.
30105+
*
30106+
* @param {Buffer} data - The k6 command output data
30107+
* @param {TestResultUrlsMap | null} testResultUrlsMap - The map containing the script path and output URL. If null, the function will not extract test run URLs.
30108+
* @param {number} totalTestRuns - The total number of test runs. This is used to determine when all test run URLs have been extracted.
30109+
*
30110+
* @returns {void}
30111+
*/
30112+
const dataString = data.toString(), lines = dataString.split('\n');
30113+
// Extract test run URLs
30114+
if (testResultUrlsMap && Object.keys(testResultUrlsMap).length < totalTestRuns) {
30115+
extractTestRunUrl(dataString, testResultUrlsMap);
30116+
}
30117+
const filteredLines = lines.filter((line) => {
30118+
const isRegexMatch = REGEX_EXPRESSIONS.runningIteration.test(line) || REGEX_EXPRESSIONS.runProgress.test(line);
30119+
return !isRegexMatch;
30120+
});
30121+
if (filteredLines.length < lines.length) {
30122+
// ignore empty lines only when progress lines output was ignored.
30123+
if (filteredLines.join("") === "") {
30124+
return;
30125+
}
30126+
}
30127+
console.log(filteredLines.join('\n'));
30128+
}
30129+
exports.parseK6Output = parseK6Output;
30130+
30131+
3005930132
/***/ }),
3006030133

3006130134
/***/ 9491:

src/index.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import * as core from '@actions/core';
22
import * as glob from '@actions/glob';
33
import { spawn } from 'child_process';
44
import * as fs from 'fs-extra';
5+
import { parseK6Output } from './k6OutputParser';
56

6-
var TEST_PIDS: number[] = [];
7+
const TEST_PIDS: number[] = [],
8+
TEST_RESULT_URLS_MAP: { [key: string]: string } = {};
79

810
run()
911

@@ -21,7 +23,8 @@ export async function run(): Promise<void> {
2123

2224
const isCloud = await isCloudIntegrationEnabled()
2325

24-
const commands = testPaths.map(testPath => generateCommand(testPath))
26+
const commands = testPaths.map(testPath => generateCommand(testPath)),
27+
TOTAL_TEST_RUNS = commands.length;
2528
if (commands.length === 0) {
2629
throw new Error('No test files found')
2730
}
@@ -119,7 +122,10 @@ export async function run(): Promise<void> {
119122
detached: true,
120123
env: process.env,
121124
});
122-
125+
// Parse k6 command output and extract test run URLs if running in cloud mode.
126+
// Also, print the output to the console, excluding the progress lines.
127+
child.stdout?.on('data', (data) => parseK6Output(data, isCloud ? TEST_RESULT_URLS_MAP : null, TOTAL_TEST_RUNS));
128+
123129
return child;
124130
}
125131
} catch (error) {

src/k6OutputParser.ts

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { TestResultUrlsMap } from './types';
2+
3+
const REGEX_EXPRESSIONS = {
4+
scriptPath: /^\s*script:\s*(.+)$/m,
5+
output: /^\s*output:\s*(.+)$/m,
6+
runningIteration: /running \(\d+m\d+\.\d+s\), \d+\/\d+ VUs, \d+ complete and \d+ interrupted iterations/g,
7+
runProgress: /\[ *(\d+)% *\] *\d+ VUs *\d+(\.\d+)?s\/\d+(\.\d+)?s/g
8+
};
9+
10+
11+
function extractTestRunUrl(data: string, testResultUrlsMap: TestResultUrlsMap): void {
12+
/**
13+
* This function extracts the script path and output URL from the k6 output.
14+
* It then adds the script path and output URL to the testResultUrlsMap which is a reference to
15+
* an object passed from the main function to store test run urls mapped to corresponding test script.
16+
*
17+
* @param {string} data - The k6 command output data as string
18+
* @param {TestResultUrlsMap} testResultUrlsMap - The map containing the script path and output URL
19+
*
20+
* @returns {void}
21+
*
22+
*/
23+
24+
// Extracting the script path
25+
const scriptMatch = data.match(REGEX_EXPRESSIONS.scriptPath);
26+
const scriptPath = scriptMatch ? scriptMatch[1] : null;
27+
28+
// Extracting the output URL
29+
const outputMatch = data.match(REGEX_EXPRESSIONS.output);
30+
const output = outputMatch ? outputMatch[1] : null;
31+
32+
if (scriptPath && output) {
33+
testResultUrlsMap[scriptPath] = output;
34+
}
35+
}
36+
37+
38+
export function parseK6Output(data: Buffer, testResultUrlsMap: TestResultUrlsMap | null, totalTestRuns: number): void {
39+
/*
40+
* This function is responsible for parsing the output of the k6 command.
41+
* It filters out the progress lines and logs the rest of the output.
42+
* It also extracts the test run URLs from the output.
43+
*
44+
* @param {Buffer} data - The k6 command output data
45+
* @param {TestResultUrlsMap | null} testResultUrlsMap - The map containing the script path and output URL. If null, the function will not extract test run URLs.
46+
* @param {number} totalTestRuns - The total number of test runs. This is used to determine when all test run URLs have been extracted.
47+
*
48+
* @returns {void}
49+
*/
50+
51+
const dataString = data.toString(),
52+
lines = dataString.split('\n');
53+
54+
// Extract test run URLs
55+
if (testResultUrlsMap && Object.keys(testResultUrlsMap).length < totalTestRuns) {
56+
extractTestRunUrl(dataString, testResultUrlsMap);
57+
}
58+
59+
const filteredLines = lines.filter((line) => {
60+
const isRegexMatch = REGEX_EXPRESSIONS.runningIteration.test(line) || REGEX_EXPRESSIONS.runProgress.test(line);
61+
return !isRegexMatch;
62+
});
63+
64+
if (filteredLines.length < lines.length) {
65+
// ignore empty lines only when progress lines output was ignored.
66+
if (filteredLines.join("") === "") {
67+
return;
68+
}
69+
}
70+
console.log(filteredLines.join('\n'))
71+
}

src/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export type TestResultUrlsMap = {
2+
[key: string]: string;
3+
};

0 commit comments

Comments
 (0)