k6-perf-reporter 1.6.0 → 1.6.2

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/README.md CHANGED
@@ -1,13 +1,14 @@
1
1
  # k6-perf-reporter
2
2
 
3
- A comprehensive reporting tool for k6 performance tests with InfluxDB 2 integration. Generates beautiful CLI, JSON, and Slack reports with key performance metrics.
3
+ A comprehensive reporting tool for k6 performance tests with InfluxDB 2 integration. Generates beautiful CLI, JSON, Markdown, and Slack reports with key performance metrics.
4
4
 
5
5
  ## Features
6
6
 
7
- - **Multiple Report Formats**: CLI output, JSON, and Slack integration
7
+ - **Multiple Report Formats**: CLI output, JSON, Markdown, and Slack integration
8
8
  - **InfluxDB 2 Integration**: Query test metrics directly from InfluxDB
9
9
  - **Key Metrics**: RPS, HTTP requests, checks, error rates, latencies, and more
10
10
  - **Real-time Slack Notifications**: Send formatted test reports directly to Slack channels
11
+ - **Markdown Reports**: Generate exportable markdown files for documentation and archiving
11
12
  - **CLI Tool**: Command-line interface for automated report generation
12
13
  - **Library Support**: Use as a TypeScript/JavaScript library in your own tools
13
14
 
@@ -138,6 +139,30 @@ Slack message includes:
138
139
  - Error requests
139
140
  - Error responses with error types
140
141
 
142
+ #### Markdown Report
143
+
144
+ Generate a markdown file for documentation, wikis, or archiving:
145
+
146
+ ```bash
147
+ npx tsx src/cli.ts generate \
148
+ --run-id 123456790121 \
149
+ --start-time "-1h" \
150
+ --format markdown \
151
+ --output report.md
152
+ ```
153
+
154
+ Markdown report includes:
155
+ - Formatted header with run ID, start/end times
156
+ - Summary with pass/fail status and key metrics table
157
+ - Metrics section with all performance data
158
+ - Tables for:
159
+ - Top slowest URLs
160
+ - RPS per URL
161
+ - Error requests
162
+ - Error responses with error types
163
+
164
+ See [example markdown report](examples/report.example.md) for output format.
165
+
141
166
  ### Command Options
142
167
 
143
168
  ```
@@ -145,8 +170,8 @@ Slack message includes:
145
170
  -st, --start-time <time> Start time (relative: -1h, -30m, or ISO 8601)
146
171
  -et, --end-time <time> End time (ISO 8601 format, defaults to now)
147
172
  -c, --config <path> Path to config file (default: .config.json)
148
- -f, --format <format> Output format: 'json', 'cli', or 'slack' (default: cli)
149
- -o, --output <path> Output file path (for json format)
173
+ -f, --format <format> Output format: 'json', 'cli', 'markdown', or 'slack' (default: cli)
174
+ -o, --output <path> Output file path (for json and markdown formats)
150
175
  ```
151
176
 
152
177
  ### Time Format Examples
@@ -248,6 +273,9 @@ npx tsx src/cli.ts generate --run-id 123456 -st -1h --format cli
248
273
  # Save as JSON
249
274
  npx tsx src/cli.ts generate --run-id 123456 -st -1h --format json -o report.json
250
275
 
276
+ # Save as Markdown
277
+ npx tsx src/cli.ts generate --run-id 123456 -st -1h --format markdown -o report.md
278
+
251
279
  # Send to Slack
252
280
  npx tsx src/cli.ts generate --run-id 123456 -st -1h --format slack
253
281
  ```
@@ -319,6 +347,7 @@ src/
319
347
  ├── reporters/
320
348
  │ ├── cli-reporter.ts # CLI table formatting
321
349
  │ ├── json-reporter.ts # JSON export
350
+ │ ├── markdown-reporter.ts # Markdown export
322
351
  │ ├── slack-reporter.ts # Slack integration
323
352
  │ └── index.ts # Reporter exports
324
353
  ├── cli.ts # Command-line interface
package/dist/cli.js CHANGED
@@ -17,7 +17,7 @@ function main() {
17
17
  .option("-st, --start-time <time>", "Start time in ISO 8601 format or relative like '-1h'")
18
18
  .option("-et, --end-time <time>", "End time in ISO 8601 format (defaults to now)")
19
19
  .option("-c, --config <path>", "Path to config file", ".config.json")
20
- .option("-f, --format <format>", "Output format: 'json', 'cli', or 'slack'", "cli")
20
+ .option("-f, --format <format>", "Output format: 'json', 'cli', 'markdown', or 'slack'", "cli")
21
21
  .option("-o, --output <path>", "Output file path (for json format)")
22
22
  .action(async (options) => {
23
23
  try {
@@ -28,6 +28,10 @@ function main() {
28
28
  const jsonReporter = new reporters_1.JsonReporter();
29
29
  jsonReporter.report(report, options.output);
30
30
  }
31
+ else if (options.format === "markdown") {
32
+ const markdownReporter = new reporters_1.MarkdownReporter();
33
+ markdownReporter.report(report, options.output);
34
+ }
31
35
  else if (options.format === "slack") {
32
36
  const slackConfig = config_1.Config.getInstance(options.config).getSlackConfig();
33
37
  if (!slackConfig) {
@@ -69,8 +73,8 @@ OPTIONS:
69
73
  -st, --start-time Start time (relative: -1h, -30m, or ISO 8601)
70
74
  -et, --end-time End time (ISO 8601 format, defaults to now)
71
75
  -c, --config Path to config file (default: .config.json)
72
- -f, --format Output format: 'json', 'cli', or 'slack' (default: cli)
73
- -o, --output Output file path (for json format)
76
+ -f, --format Output format: 'json', 'cli', 'markdown', or 'slack' (default: cli)
77
+ -o, --output Output file path (for json and markdown formats)
74
78
  -h, --help Show command help
75
79
  -V, --version Show version
76
80
 
@@ -88,13 +92,16 @@ EXAMPLES:
88
92
  4. Save JSON report to file:
89
93
  k6-reporter generate --run-id 123456790121 --format json -o report.json
90
94
 
91
- 5. Send report to Slack:
95
+ 5. Generate Markdown report and save to file:
96
+ k6-reporter generate --run-id 123456790121 --format markdown -o report.md
97
+
98
+ 6. Send report to Slack:
92
99
  SLACK_TOKEN=xoxb-... k6-reporter generate --run-id 123456790121 --format slack
93
100
 
94
- 6. Use custom config file:
101
+ 7. Use custom config file:
95
102
  k6-reporter generate --run-id 123456790121 -c /path/to/config.json
96
103
 
97
- 7. Get help for generate command:
104
+ 8. Get help for generate command:
98
105
  k6-reporter generate --help
99
106
 
100
107
  TIME FORMAT:
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AACpC,qCAAkC;AAClC,qDAAiD;AACjD,2CAAuE;AAEvE,SAAS,IAAI;IACX,mBAAO;SACJ,IAAI,CAAC,aAAa,CAAC;SACnB,WAAW,CAAC,uDAAuD,CAAC;SACpE,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,mBAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,kCAAkC,CAAC;SAC/C,cAAc,CAAC,eAAe,EAAE,gBAAgB,CAAC;SACjD,MAAM,CAAC,0BAA0B,EAAE,sDAAsD,CAAC;SAC1F,MAAM,CAAC,wBAAwB,EAAE,+CAA+C,CAAC;SACjF,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,EAAE,cAAc,CAAC;SACpE,MAAM,CAAC,uBAAuB,EAAE,0CAA0C,EAAE,KAAK,CAAC;SAClF,MAAM,CAAC,qBAAqB,EAAE,oCAAoC,CAAC;SACnE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACxB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,eAAM,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAC;YACpE,MAAM,SAAS,GAAG,IAAI,8BAAa,CAAC,MAAM,CAAC,CAAC;YAC5C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CACpC,OAAO,CAAC,KAAK,EACb,OAAO,CAAC,SAAS,IAAI,KAAK,EAC1B,OAAO,CAAC,OAAO,IAAI,OAAO,CAC3B,CAAC;YAEF,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC9B,MAAM,YAAY,GAAG,IAAI,wBAAY,EAAE,CAAC;gBACxC,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;YAC9C,CAAC;iBAAM,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;gBACtC,MAAM,WAAW,GAAG,eAAM,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,CAAC;gBACxE,IAAI,CAAC,WAAW,EAAE,CAAC;oBACjB,MAAM,IAAI,KAAK,CAAC,+FAA+F,CAAC,CAAC;gBACnH,CAAC;gBACD,MAAM,aAAa,GAAG,IAAI,yBAAa,CAAC,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;gBAChF,MAAM,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACnC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;YACtC,CAAC;iBAAM,CAAC;gBACN,MAAM,WAAW,GAAG,IAAI,uBAAW,EAAE,CAAC;gBACtC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,mBAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,wBAAwB,CAAC;SACrC,MAAM,CAAC,GAAG,EAAE;QACX,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmEX,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,mBAAO,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC;AAED,IAAI,EAAE,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  export { JsonReporter } from "./json-reporter";
2
2
  export { CliReporter } from "./cli-reporter";
3
3
  export { SlackReporter } from "./slack-reporter";
4
+ export { MarkdownReporter } from "./markdown-reporter";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reporters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reporters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SlackReporter = exports.CliReporter = exports.JsonReporter = void 0;
3
+ exports.MarkdownReporter = exports.SlackReporter = exports.CliReporter = exports.JsonReporter = void 0;
4
4
  var json_reporter_1 = require("./json-reporter");
5
5
  Object.defineProperty(exports, "JsonReporter", { enumerable: true, get: function () { return json_reporter_1.JsonReporter; } });
6
6
  var cli_reporter_1 = require("./cli-reporter");
7
7
  Object.defineProperty(exports, "CliReporter", { enumerable: true, get: function () { return cli_reporter_1.CliReporter; } });
8
8
  var slack_reporter_1 = require("./slack-reporter");
9
9
  Object.defineProperty(exports, "SlackReporter", { enumerable: true, get: function () { return slack_reporter_1.SlackReporter; } });
10
+ var markdown_reporter_1 = require("./markdown-reporter");
11
+ Object.defineProperty(exports, "MarkdownReporter", { enumerable: true, get: function () { return markdown_reporter_1.MarkdownReporter; } });
10
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/reporters/index.ts"],"names":[],"mappings":";;;AAAA,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AACrB,+CAA6C;AAApC,2GAAA,WAAW,OAAA;AACpB,mDAAiD;AAAxC,+GAAA,aAAa,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/reporters/index.ts"],"names":[],"mappings":";;;AAAA,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AACrB,+CAA6C;AAApC,2GAAA,WAAW,OAAA;AACpB,mDAAiD;AAAxC,+GAAA,aAAa,OAAA;AACtB,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA"}
@@ -0,0 +1,11 @@
1
+ import { ReporterResponse } from "../data-collector";
2
+ export declare class MarkdownReporter {
3
+ report(data: ReporterResponse, outputPath?: string): void;
4
+ private generateMarkdown;
5
+ private generateSummary;
6
+ private generateMetrics;
7
+ private generateTables;
8
+ private tableToMarkdown;
9
+ private formatDuration;
10
+ }
11
+ //# sourceMappingURL=markdown-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-reporter.d.ts","sourceRoot":"","sources":["../../src/reporters/markdown-reporter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,qBAAa,gBAAgB;IAC3B,MAAM,CAAC,IAAI,EAAE,gBAAgB,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAWzD,OAAO,CAAC,gBAAgB;IAsBxB,OAAO,CAAC,eAAe;IA4BvB,OAAO,CAAC,eAAe;IAyCvB,OAAO,CAAC,cAAc;IAkGtB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,cAAc;CASvB"}
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MarkdownReporter = void 0;
4
+ const fs_1 = require("fs");
5
+ class MarkdownReporter {
6
+ report(data, outputPath) {
7
+ const markdown = this.generateMarkdown(data);
8
+ if (outputPath) {
9
+ (0, fs_1.writeFileSync)(outputPath, markdown);
10
+ console.log(`Report saved to ${outputPath}`);
11
+ }
12
+ else {
13
+ console.log(markdown);
14
+ }
15
+ }
16
+ generateMarkdown(data) {
17
+ const reportData = data.data;
18
+ let markdown = "";
19
+ // Header
20
+ markdown += `# k6 Performance Test Report\n\n`;
21
+ markdown += `- **Run ID:** ${data.runId}\n`;
22
+ markdown += `- **Start:** ${data.startTime}\n`;
23
+ markdown += `- **End:** ${data.endTime}\n\n`;
24
+ // Summary section
25
+ markdown += this.generateSummary(reportData);
26
+ // Main metrics
27
+ markdown += this.generateMetrics(reportData);
28
+ // Tables
29
+ markdown += this.generateTables(reportData);
30
+ return markdown;
31
+ }
32
+ generateSummary(reportData) {
33
+ let summary = "## Summary\n\n";
34
+ let errorPercent = 0;
35
+ let failedChecks = 0;
36
+ if (reportData.httpReqFailed) {
37
+ const failed = reportData.httpReqFailed;
38
+ errorPercent = typeof failed.failureRate === "number" ? failed.failureRate : 0;
39
+ }
40
+ if (reportData.checks) {
41
+ const checks = reportData.checks;
42
+ failedChecks = typeof checks.fails === "number" ? checks.fails : 0;
43
+ }
44
+ const isSuccess = errorPercent < 1 && failedChecks === 0;
45
+ const status = isSuccess ? "✓ PASS" : "✗ FAIL";
46
+ summary += `**Status:** ${status}\n\n`;
47
+ summary += `| Metric | Value |\n`;
48
+ summary += `|--------|-------|\n`;
49
+ summary += `| Error Rate | ${errorPercent.toFixed(2)}% |\n`;
50
+ summary += `| Failed Checks | ${failedChecks} |\n\n`;
51
+ return summary;
52
+ }
53
+ generateMetrics(reportData) {
54
+ let metrics = "## Metrics\n\n";
55
+ if (reportData.checks) {
56
+ const checks = reportData.checks;
57
+ const passRate = typeof checks.passRate === "number" ? checks.passRate : 0;
58
+ metrics += `- **Checks:** ${passRate.toFixed(2)}% (${checks.passes} ✓${checks.fails ? `, ${checks.fails} ✗` : ""})\n`;
59
+ }
60
+ if (reportData.rpsAggregated) {
61
+ const rpsAgg = reportData.rpsAggregated;
62
+ const avg = typeof rpsAgg.avg === "number" ? rpsAgg.avg : 0;
63
+ const p95 = typeof rpsAgg.p95 === "number" ? rpsAgg.p95 : 0;
64
+ const max = typeof rpsAgg.max === "number" ? rpsAgg.max : 0;
65
+ metrics += `- **RPS:** avg=${avg.toFixed(2)}, p(95)=${p95.toFixed(2)}, max=${max.toFixed(2)}\n`;
66
+ }
67
+ if (reportData.httpReqs) {
68
+ const httpReqs = reportData.httpReqs;
69
+ metrics += `- **HTTP Requests:** ${httpReqs.total} (${httpReqs.rate.toFixed(6)}/s)\n`;
70
+ }
71
+ if (reportData.iterations) {
72
+ const iterations = reportData.iterations;
73
+ metrics += `- **Iterations:** ${iterations.total} (${iterations.rate.toFixed(6)}/s)\n`;
74
+ }
75
+ if (reportData.httpReqDuration) {
76
+ const duration = reportData.httpReqDuration;
77
+ metrics += `- **HTTP Duration:** avg=${this.formatDuration(duration.avg || 0)}, p(95)=${this.formatDuration(duration.p95 || 0)}\n`;
78
+ }
79
+ if (reportData.vus) {
80
+ const vus = reportData.vus;
81
+ metrics += `- **VUs:** ${vus.current} (min=${vus.min}, max=${vus.max})\n`;
82
+ }
83
+ metrics += "\n";
84
+ return metrics;
85
+ }
86
+ generateTables(reportData) {
87
+ let tables = "";
88
+ if (reportData.topSlowUrls) {
89
+ const topSlowUrls = reportData.topSlowUrls;
90
+ const urls = topSlowUrls.urls;
91
+ if (urls && urls.length > 0) {
92
+ tables += "## Top 10 Slowest URLs\n\n";
93
+ const tableData = [
94
+ ["#", "Method", "URL", "p(95)"],
95
+ ...urls.map((u, i) => [String(i + 1), u.method, u.url, this.formatDuration(u.p95Duration)]),
96
+ ];
97
+ tables += this.tableToMarkdown(tableData) + "\n\n";
98
+ }
99
+ }
100
+ if (reportData.rpsPerUrl) {
101
+ const rpsPerUrl = reportData.rpsPerUrl;
102
+ const urls = rpsPerUrl.urls;
103
+ if (urls && urls.length > 0) {
104
+ tables += "## RPS per URL\n\n";
105
+ const tableData = [
106
+ ["#", "Method", "URL", "Count", "avg", "p(95)", "max"],
107
+ ...urls.map((u, i) => [String(i + 1), u.method, u.url, String(u.count), u.rps.avg.toFixed(2), u.rps.p95.toFixed(2), u.rps.max.toFixed(2)]),
108
+ ];
109
+ tables += this.tableToMarkdown(tableData) + "\n\n";
110
+ }
111
+ }
112
+ if (reportData.successRequests) {
113
+ const successRequests = reportData.successRequests;
114
+ const requests = successRequests.requests;
115
+ if (requests && requests.length > 0) {
116
+ tables += "## Top Successful Requests\n\n";
117
+ const tableData = [
118
+ ["#", "Method", "URL", "Status", "Count", "Min", "Avg", "p(95)"],
119
+ ...requests.map((r, i) => [String(i + 1), r.method, r.url, String(r.status), String(r.count), this.formatDuration(r.min), this.formatDuration(r.avg), this.formatDuration(r.p95)]),
120
+ ];
121
+ tables += this.tableToMarkdown(tableData) + "\n\n";
122
+ }
123
+ }
124
+ if (reportData.errorRequests) {
125
+ const errorRequests = reportData.errorRequests;
126
+ const errors = errorRequests.errors;
127
+ if (errors && errors.length > 0) {
128
+ tables += "## Top Error Requests\n\n";
129
+ const tableData = [
130
+ ["#", "Method", "URL", "Code", "Count"],
131
+ ...errors.map((e, i) => [String(i + 1), e.method, e.url, String(e.status), String(e.count)]),
132
+ ];
133
+ tables += this.tableToMarkdown(tableData) + "\n\n";
134
+ }
135
+ }
136
+ if (reportData.errorResponsesText) {
137
+ const errorResponsesText = reportData.errorResponsesText;
138
+ const responses = errorResponsesText.responses;
139
+ if (responses && responses.length > 0) {
140
+ const groupedErrors = new Map();
141
+ responses.forEach((r) => {
142
+ const key = `${r.method}|${r.url}|${r.status}|${r.error}`;
143
+ if (groupedErrors.has(key)) {
144
+ const entry = groupedErrors.get(key);
145
+ entry.count++;
146
+ }
147
+ else {
148
+ groupedErrors.set(key, {
149
+ method: r.method,
150
+ url: r.url,
151
+ status: r.status,
152
+ error: r.error || "",
153
+ count: 1,
154
+ });
155
+ }
156
+ });
157
+ tables += "## Error Responses\n\n";
158
+ const tableData = [
159
+ ["#", "Method", "URL", "Status", "Error", "Count"],
160
+ ...Array.from(groupedErrors.values()).map((r, i) => {
161
+ let url = r.url;
162
+ try {
163
+ const urlObj = new URL(url);
164
+ url = urlObj.pathname;
165
+ }
166
+ catch {
167
+ // If not a full URL, use as-is
168
+ }
169
+ return [String(i + 1), r.method, url, String(r.status), r.error, String(r.count)];
170
+ }),
171
+ ];
172
+ tables += this.tableToMarkdown(tableData) + "\n\n";
173
+ }
174
+ }
175
+ return tables;
176
+ }
177
+ tableToMarkdown(tableData) {
178
+ if (tableData.length === 0) {
179
+ return "";
180
+ }
181
+ const [headers, ...rows] = tableData;
182
+ let markdown = "| " + headers.join(" | ") + " |\n";
183
+ markdown += "|" + headers.map(() => " --- ").join("|") + "|\n";
184
+ rows.forEach((row) => {
185
+ markdown += "| " + row.join(" | ") + " |\n";
186
+ });
187
+ return markdown;
188
+ }
189
+ formatDuration(ms) {
190
+ if (ms < 1) {
191
+ return `${(ms * 1000).toFixed(2)}µs`;
192
+ }
193
+ if (ms < 1000) {
194
+ return `${ms.toFixed(2)}ms`;
195
+ }
196
+ return `${(ms / 1000).toFixed(2)}s`;
197
+ }
198
+ }
199
+ exports.MarkdownReporter = MarkdownReporter;
200
+ //# sourceMappingURL=markdown-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-reporter.js","sourceRoot":"","sources":["../../src/reporters/markdown-reporter.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,10 @@ export declare class SlackReporter {
8
8
  private generateSummary;
9
9
  private generateMetrics;
10
10
  private generateTables;
11
+ private formatTable;
11
12
  private formatDuration;
13
+ private sendMessages;
14
+ private splitIntoMessages;
12
15
  private sendMessage;
13
16
  }
14
17
  //# sourceMappingURL=slack-reporter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"slack-reporter.d.ts","sourceRoot":"","sources":["../../src/reporters/slack-reporter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,OAAO,CAAS;gBAEZ,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAQpC,MAAM,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnD,OAAO,CAAC,gBAAgB;IAqBxB,OAAO,CAAC,eAAe;IA2BvB,OAAO,CAAC,eAAe;IAyCvB,OAAO,CAAC,cAAc;IAyFtB,OAAO,CAAC,cAAc;YAUR,WAAW;CAmB1B"}
1
+ {"version":3,"file":"slack-reporter.d.ts","sourceRoot":"","sources":["../../src/reporters/slack-reporter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,OAAO,CAAS;gBAEZ,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAQpC,MAAM,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnD,OAAO,CAAC,gBAAgB;IAsBxB,OAAO,CAAC,eAAe;IA2BvB,OAAO,CAAC,eAAe;IAyCvB,OAAO,CAAC,cAAc;IAkGtB,OAAO,CAAC,WAAW;IAkCnB,OAAO,CAAC,cAAc;YAUR,YAAY;IAS1B,OAAO,CAAC,iBAAiB;YAuBX,WAAW;CAmB1B"}
@@ -13,15 +13,16 @@ class SlackReporter {
13
13
  }
14
14
  async report(data) {
15
15
  const markdown = this.generateMarkdown(data);
16
- await this.sendMessage(markdown);
16
+ await this.sendMessages(markdown);
17
17
  }
18
18
  generateMarkdown(data) {
19
19
  const reportData = data.data;
20
20
  let markdown = "";
21
21
  // Header
22
- markdown += `\`\`\`\nRun ID: ${data.runId}\n`;
23
- markdown += `Start: ${data.startTime}\n`;
24
- markdown += `End: ${data.endTime}\n\`\`\`\n\n`;
22
+ markdown += `*k6 Performance Test Report*\n`;
23
+ markdown += `• *Run ID:* ${data.runId}\n`;
24
+ markdown += `• *Start:* ${data.startTime}\n`;
25
+ markdown += `• *End:* ${data.endTime}\n\n`;
25
26
  // Summary section
26
27
  markdown += this.generateSummary(reportData);
27
28
  // Main metrics
@@ -31,7 +32,7 @@ class SlackReporter {
31
32
  return markdown;
32
33
  }
33
34
  generateSummary(reportData) {
34
- let summary = "";
35
+ let summary = "*Summary*\n";
35
36
  let errorPercent = 0;
36
37
  let failedChecks = 0;
37
38
  if (reportData.httpReqFailed) {
@@ -45,7 +46,7 @@ class SlackReporter {
45
46
  const isSuccess = errorPercent < 1 && failedChecks === 0;
46
47
  const status = isSuccess ? "✓ PASS" : "✗ FAIL";
47
48
  const statusEmoji = isSuccess ? "✅" : "❌";
48
- summary += `${statusEmoji} *${status}*\n`;
49
+ summary += `${statusEmoji} *${status}*\n\n`;
49
50
  summary += `• Error Rate: ${errorPercent.toFixed(2)}%\n`;
50
51
  summary += `• Failed Checks: ${failedChecks}\n\n`;
51
52
  return summary;
@@ -89,39 +90,48 @@ class SlackReporter {
89
90
  const topSlowUrls = reportData.topSlowUrls;
90
91
  const urls = topSlowUrls.urls;
91
92
  if (urls && urls.length > 0) {
92
- tables += "*Top 10 Slowest URLs:*\n```\n";
93
+ tables += "*Top 10 Slowest URLs*\n";
93
94
  const tableData = [
94
95
  ["#", "Method", "URL", "p(95)"],
95
96
  ...urls.map((u, i) => [String(i + 1), u.method, u.url, this.formatDuration(u.p95Duration)]),
96
97
  ];
97
- tables += (0, table_1.table)(tableData, { border: { topBody: "", topJoin: "", topLeft: "", topRight: "", bottomBody: "", bottomJoin: "", bottomLeft: "", bottomRight: "", bodyLeft: "", bodyRight: "", bodyJoin: "", joinBody: "─", joinLeft: "", joinRight: "", joinJoin: "" }, drawHorizontalLine: (index) => index === 1, columns: { 0: { alignment: "left" }, 1: { alignment: "left" }, 2: { alignment: "left" }, 3: { alignment: "left" } } });
98
- tables += "```\n\n";
98
+ tables += "```\n" + this.formatTable(tableData) + "```\n\n";
99
99
  }
100
100
  }
101
101
  if (reportData.rpsPerUrl) {
102
102
  const rpsPerUrl = reportData.rpsPerUrl;
103
103
  const urls = rpsPerUrl.urls;
104
104
  if (urls && urls.length > 0) {
105
- tables += "*RPS per URL:*\n```\n";
105
+ tables += "*RPS per URL*\n";
106
106
  const tableData = [
107
107
  ["#", "Method", "URL", "Count", "avg", "p(95)", "max"],
108
108
  ...urls.map((u, i) => [String(i + 1), u.method, u.url, String(u.count), u.rps.avg.toFixed(2), u.rps.p95.toFixed(2), u.rps.max.toFixed(2)]),
109
109
  ];
110
- tables += (0, table_1.table)(tableData, { border: { topBody: "", topJoin: "", topLeft: "", topRight: "", bottomBody: "", bottomJoin: "", bottomLeft: "", bottomRight: "", bodyLeft: "", bodyRight: "", bodyJoin: "", joinBody: "─", joinLeft: "", joinRight: "", joinJoin: "" }, drawHorizontalLine: (index) => index === 1, columns: { 0: { alignment: "left" }, 1: { alignment: "left" }, 2: { alignment: "left" }, 3: { alignment: "left" }, 4: { alignment: "left" }, 5: { alignment: "left" }, 6: { alignment: "left" } } });
111
- tables += "```\n\n";
110
+ tables += "```\n" + this.formatTable(tableData) + "```\n\n";
111
+ }
112
+ }
113
+ if (reportData.successRequests) {
114
+ const successRequests = reportData.successRequests;
115
+ const requests = successRequests.requests;
116
+ if (requests && requests.length > 0) {
117
+ tables += "*Top Successful Requests*\n";
118
+ const tableData = [
119
+ ["#", "Method", "URL", "Status", "Count", "Min", "Avg", "p(95)"],
120
+ ...requests.map((r, i) => [String(i + 1), r.method, r.url, String(r.status), String(r.count), this.formatDuration(r.min), this.formatDuration(r.avg), this.formatDuration(r.p95)]),
121
+ ];
122
+ tables += "```\n" + this.formatTable(tableData) + "```\n\n";
112
123
  }
113
124
  }
114
125
  if (reportData.errorRequests) {
115
126
  const errorRequests = reportData.errorRequests;
116
127
  const errors = errorRequests.errors;
117
128
  if (errors && errors.length > 0) {
118
- tables += "*Top Error Requests:*\n```\n";
129
+ tables += "*Top Error Requests*\n";
119
130
  const tableData = [
120
131
  ["#", "Method", "URL", "Code", "Count"],
121
132
  ...errors.map((e, i) => [String(i + 1), e.method, e.url, String(e.status), String(e.count)]),
122
133
  ];
123
- tables += (0, table_1.table)(tableData, { border: { topBody: "", topJoin: "", topLeft: "", topRight: "", bottomBody: "", bottomJoin: "", bottomLeft: "", bottomRight: "", bodyLeft: "", bodyRight: "", bodyJoin: "", joinBody: "─", joinLeft: "", joinRight: "", joinJoin: "" }, drawHorizontalLine: (index) => index === 1, columns: { 0: { alignment: "left" }, 1: { alignment: "left" }, 2: { alignment: "left" }, 3: { alignment: "left" }, 4: { alignment: "left" } } });
124
- tables += "```\n\n";
134
+ tables += "```\n" + this.formatTable(tableData) + "```\n\n";
125
135
  }
126
136
  }
127
137
  if (reportData.errorResponsesText) {
@@ -145,7 +155,7 @@ class SlackReporter {
145
155
  });
146
156
  }
147
157
  });
148
- tables += "*Error Responses:*\n```\n";
158
+ tables += "*Error Responses*\n";
149
159
  const tableData = [
150
160
  ["#", "Method", "URL", "Status", "Error", "Count"],
151
161
  ...Array.from(groupedErrors.values()).map((r, i) => {
@@ -160,12 +170,42 @@ class SlackReporter {
160
170
  return [String(i + 1), r.method, url, String(r.status), r.error, String(r.count)];
161
171
  }),
162
172
  ];
163
- tables += (0, table_1.table)(tableData, { border: { topBody: "", topJoin: "", topLeft: "", topRight: "", bottomBody: "", bottomJoin: "", bottomLeft: "", bottomRight: "", bodyLeft: "", bodyRight: "", bodyJoin: "", joinBody: "─", joinLeft: "", joinRight: "", joinJoin: "" }, drawHorizontalLine: (index) => index === 1, columns: { 0: { alignment: "left" }, 1: { alignment: "left" }, 2: { alignment: "left" }, 3: { alignment: "left" }, 4: { alignment: "left" }, 5: { alignment: "left" } } });
164
- tables += "```\n\n";
173
+ tables += "```\n" + this.formatTable(tableData) + "```\n\n";
165
174
  }
166
175
  }
167
176
  return tables;
168
177
  }
178
+ formatTable(tableData) {
179
+ if (tableData.length === 0) {
180
+ return "";
181
+ }
182
+ const columnCount = tableData[0].length;
183
+ const columnConfig = {};
184
+ for (let i = 0; i < columnCount; i++) {
185
+ columnConfig[i] = { alignment: "left" };
186
+ }
187
+ return (0, table_1.table)(tableData, {
188
+ border: {
189
+ topBody: "─",
190
+ topJoin: "┬",
191
+ topLeft: "┌",
192
+ topRight: "┐",
193
+ bottomBody: "─",
194
+ bottomJoin: "┴",
195
+ bottomLeft: "└",
196
+ bottomRight: "┘",
197
+ bodyLeft: "│",
198
+ bodyRight: "│",
199
+ bodyJoin: "│",
200
+ joinBody: "─",
201
+ joinLeft: "├",
202
+ joinRight: "┤",
203
+ joinJoin: "┼",
204
+ },
205
+ drawHorizontalLine: (index) => index === 1,
206
+ columns: columnConfig,
207
+ });
208
+ }
169
209
  formatDuration(ms) {
170
210
  if (ms < 1) {
171
211
  return `${(ms * 1000).toFixed(2)}µs`;
@@ -175,6 +215,33 @@ class SlackReporter {
175
215
  }
176
216
  return `${(ms / 1000).toFixed(2)}s`;
177
217
  }
218
+ async sendMessages(markdown) {
219
+ const MAX_BLOCK_LENGTH = 3000;
220
+ const messages = this.splitIntoMessages(markdown, MAX_BLOCK_LENGTH);
221
+ for (const message of messages) {
222
+ await this.sendMessage(message);
223
+ }
224
+ }
225
+ splitIntoMessages(text, maxLength) {
226
+ const messages = [];
227
+ const sections = text.split(/(\n\n)/);
228
+ let currentMessage = "";
229
+ for (const section of sections) {
230
+ if ((currentMessage + section).length <= maxLength) {
231
+ currentMessage += section;
232
+ }
233
+ else {
234
+ if (currentMessage) {
235
+ messages.push(currentMessage);
236
+ }
237
+ currentMessage = section;
238
+ }
239
+ }
240
+ if (currentMessage) {
241
+ messages.push(currentMessage);
242
+ }
243
+ return messages;
244
+ }
178
245
  async sendMessage(markdown) {
179
246
  try {
180
247
  await this.client.chat.postMessage({
@@ -1 +1 @@
1
- {"version":3,"file":"slack-reporter.js","sourceRoot":"","sources":["../../src/reporters/slack-reporter.ts"],"names":[],"mappings":";;;AAAA,4CAA2C;AAE3C,iCAA8B;AAE9B,MAAa,aAAa;IAIxB,YAAY,KAAa,EAAE,OAAe;QACxC,IAAI,CAAC,MAAM,GAAG,IAAI,mBAAS,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAsB;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAEO,gBAAgB,CAAC,IAAsB;QAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,IAA+B,CAAC;QACxD,IAAI,QAAQ,GAAG,EAAE,CAAC;QAElB,SAAS;QACT,QAAQ,IAAI,mBAAmB,IAAI,CAAC,KAAK,IAAI,CAAC;QAC9C,QAAQ,IAAI,UAAU,IAAI,CAAC,SAAS,IAAI,CAAC;QACzC,QAAQ,IAAI,QAAQ,IAAI,CAAC,OAAO,cAAc,CAAC;QAE/C,kBAAkB;QAClB,QAAQ,IAAI,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QAE7C,eAAe;QACf,QAAQ,IAAI,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QAE7C,SAAS;QACT,QAAQ,IAAI,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAE5C,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,eAAe,CAAC,UAAmC;QACzD,IAAI,OAAO,GAAG,EAAE,CAAC;QAEjB,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,IAAI,YAAY,GAAG,CAAC,CAAC;QAErB,IAAI,UAAU,CAAC,aAAa,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAG,UAAU,CAAC,aAAuC,CAAC;YAClE,YAAY,GAAG,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;QACjF,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,UAAU,CAAC,MAAgC,CAAC;YAC3D,YAAY,GAAG,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,SAAS,GAAG,YAAY,GAAG,CAAC,IAAI,YAAY,KAAK,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC/C,MAAM,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QAE1C,OAAO,IAAI,GAAG,WAAW,KAAK,MAAM,KAAK,CAAC;QAC1C,OAAO,IAAI,iBAAiB,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;QACzD,OAAO,IAAI,oBAAoB,YAAY,MAAM,CAAC;QAElD,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,eAAe,CAAC,UAAmC;QACzD,IAAI,OAAO,GAAG,cAAc,CAAC;QAE7B,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,UAAU,CAAC,MAAgC,CAAC;YAC3D,MAAM,QAAQ,GAAG,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3E,OAAO,IAAI,aAAa,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC;QACpH,CAAC;QAED,IAAI,UAAU,CAAC,aAAa,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAG,UAAU,CAAC,aAAwC,CAAC;YACnE,MAAM,GAAG,GAAG,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5D,MAAM,GAAG,GAAG,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5D,MAAM,GAAG,GAAG,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5D,OAAO,IAAI,cAAc,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9F,CAAC;QAED,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAkC,CAAC;YAC/D,OAAO,IAAI,oBAAoB,QAAQ,CAAC,KAAK,KAAK,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QACpF,CAAC;QAED,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,UAAU,GAAG,UAAU,CAAC,UAAoC,CAAC;YACnE,OAAO,IAAI,iBAAiB,UAAU,CAAC,KAAK,KAAK,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QACrF,CAAC;QAED,IAAI,UAAU,CAAC,eAAe,EAAE,CAAC;YAC/B,MAAM,QAAQ,GAAG,UAAU,CAAC,eAAyC,CAAC;YACtE,OAAO,IAAI,wBAAwB,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC;QACjI,CAAC;QAED,IAAI,UAAU,CAAC,GAAG,EAAE,CAAC;YACnB,MAAM,GAAG,GAAG,UAAU,CAAC,GAA6B,CAAC;YACrD,OAAO,IAAI,UAAU,GAAG,CAAC,OAAO,SAAS,GAAG,CAAC,GAAG,SAAS,GAAG,CAAC,GAAG,KAAK,CAAC;QACxE,CAAC;QAED,OAAO,IAAI,IAAI,CAAC;QAChB,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,cAAc,CAAC,UAAmC;QACxD,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;YAC3B,MAAM,WAAW,GAAG,UAAU,CAAC,WAAsC,CAAC;YACtE,MAAM,IAAI,GAAG,WAAW,CAAC,IAAmE,CAAC;YAC7F,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5B,MAAM,IAAI,+BAA+B,CAAC;gBAC1C,MAAM,SAAS,GAAG;oBAChB,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC;oBAC/B,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;iBAC5F,CAAC;gBACF,MAAM,IAAI,IAAA,aAAK,EAAC,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,kBAAkB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;gBACxa,MAAM,IAAI,SAAS,CAAC;YACtB,CAAC;QACH,CAAC;QAED,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,UAAU,CAAC,SAAoC,CAAC;YAClE,MAAM,IAAI,GAAG,SAAS,CAAC,IAA6G,CAAC;YACrI,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5B,MAAM,IAAI,uBAAuB,CAAC;gBAClC,MAAM,SAAS,GAAG;oBAChB,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC;oBACtD,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;iBAC3I,CAAC;gBACF,MAAM,IAAI,IAAA,aAAK,EAAC,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,kBAAkB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;gBACtf,MAAM,IAAI,SAAS,CAAC;YACtB,CAAC;QACH,CAAC;QAED,IAAI,UAAU,CAAC,aAAa,EAAE,CAAC;YAC7B,MAAM,aAAa,GAAG,UAAU,CAAC,aAAwC,CAAC;YAC1E,MAAM,MAAM,GAAG,aAAa,CAAC,MAA+E,CAAC;YAC7G,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,8BAA8B,CAAC;gBACzC,MAAM,SAAS,GAAG;oBAChB,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC;oBACvC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;iBAC7F,CAAC;gBACF,MAAM,IAAI,IAAA,aAAK,EAAC,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,kBAAkB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;gBAClc,MAAM,IAAI,SAAS,CAAC;YACtB,CAAC;QACH,CAAC;QAED,IAAI,UAAU,CAAC,kBAAkB,EAAE,CAAC;YAClC,MAAM,kBAAkB,GAAG,UAAU,CAAC,kBAA6C,CAAC;YACpF,MAAM,SAAS,GAAG,kBAAkB,CAAC,SAAkF,CAAC;YACxH,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAyF,CAAC;gBAEvH,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;oBACtB,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;oBAC1D,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;wBAC3B,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;wBACtC,KAAK,CAAC,KAAK,EAAE,CAAC;oBAChB,CAAC;yBAAM,CAAC;wBACN,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE;4BACrB,MAAM,EAAE,CAAC,CAAC,MAAM;4BAChB,GAAG,EAAE,CAAC,CAAC,GAAG;4BACV,MAAM,EAAE,CAAC,CAAC,MAAM;4BAChB,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE;4BACpB,KAAK,EAAE,CAAC;yBACT,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC,CAAC,CAAC;gBAEH,MAAM,IAAI,2BAA2B,CAAC;gBACtC,MAAM,SAAS,GAAG;oBAChB,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC;oBAClD,GAAG,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;wBACjD,IAAI,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;wBAChB,IAAI,CAAC;4BACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;4BAC5B,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC;wBACxB,CAAC;wBAAC,MAAM,CAAC;4BACP,+BAA+B;wBACjC,CAAC;wBACD,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;oBACpF,CAAC,CAAC;iBACH,CAAC;gBACF,MAAM,IAAI,IAAA,aAAK,EAAC,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,kBAAkB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC5d,MAAM,IAAI,SAAS,CAAC;YACtB,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,cAAc,CAAC,EAAU;QAC/B,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;YACX,OAAO,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QACvC,CAAC;QACD,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;YACd,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9B,CAAC;QACD,OAAO,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACtC,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,QAAgB;QACxC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;gBACjC,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,IAAI,EAAE,4BAA4B;gBAClC,MAAM,EAAE;oBACN;wBACE,IAAI,EAAE,SAAS;wBACf,IAAI,EAAE;4BACJ,IAAI,EAAE,QAAQ;4BACd,IAAI,EAAE,QAAQ;yBACf;qBACF;iBACF;aACF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,iCAAiC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC7G,CAAC;IACH,CAAC;CACF;AAhOD,sCAgOC"}
1
+ {"version":3,"file":"slack-reporter.js","sourceRoot":"","sources":["../../src/reporters/slack-reporter.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "k6-perf-reporter",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "Reporting tool for k6 performance tests with InfluxDB 2 integration",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
package/src/cli.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  import { program } from "commander";
4
4
  import { Config } from "./config";
5
5
  import { DataCollector } from "./data-collector";
6
- import { JsonReporter, CliReporter, SlackReporter } from "./reporters";
6
+ import { JsonReporter, CliReporter, SlackReporter, MarkdownReporter } from "./reporters";
7
7
 
8
8
  function main(): void {
9
9
  program
@@ -18,7 +18,7 @@ function main(): void {
18
18
  .option("-st, --start-time <time>", "Start time in ISO 8601 format or relative like '-1h'")
19
19
  .option("-et, --end-time <time>", "End time in ISO 8601 format (defaults to now)")
20
20
  .option("-c, --config <path>", "Path to config file", ".config.json")
21
- .option("-f, --format <format>", "Output format: 'json', 'cli', or 'slack'", "cli")
21
+ .option("-f, --format <format>", "Output format: 'json', 'cli', 'markdown', or 'slack'", "cli")
22
22
  .option("-o, --output <path>", "Output file path (for json format)")
23
23
  .action(async (options) => {
24
24
  try {
@@ -33,6 +33,9 @@ function main(): void {
33
33
  if (options.format === "json") {
34
34
  const jsonReporter = new JsonReporter();
35
35
  jsonReporter.report(report, options.output);
36
+ } else if (options.format === "markdown") {
37
+ const markdownReporter = new MarkdownReporter();
38
+ markdownReporter.report(report, options.output);
36
39
  } else if (options.format === "slack") {
37
40
  const slackConfig = Config.getInstance(options.config).getSlackConfig();
38
41
  if (!slackConfig) {
@@ -73,8 +76,8 @@ OPTIONS:
73
76
  -st, --start-time Start time (relative: -1h, -30m, or ISO 8601)
74
77
  -et, --end-time End time (ISO 8601 format, defaults to now)
75
78
  -c, --config Path to config file (default: .config.json)
76
- -f, --format Output format: 'json', 'cli', or 'slack' (default: cli)
77
- -o, --output Output file path (for json format)
79
+ -f, --format Output format: 'json', 'cli', 'markdown', or 'slack' (default: cli)
80
+ -o, --output Output file path (for json and markdown formats)
78
81
  -h, --help Show command help
79
82
  -V, --version Show version
80
83
 
@@ -92,13 +95,16 @@ EXAMPLES:
92
95
  4. Save JSON report to file:
93
96
  k6-reporter generate --run-id 123456790121 --format json -o report.json
94
97
 
95
- 5. Send report to Slack:
98
+ 5. Generate Markdown report and save to file:
99
+ k6-reporter generate --run-id 123456790121 --format markdown -o report.md
100
+
101
+ 6. Send report to Slack:
96
102
  SLACK_TOKEN=xoxb-... k6-reporter generate --run-id 123456790121 --format slack
97
103
 
98
- 6. Use custom config file:
104
+ 7. Use custom config file:
99
105
  k6-reporter generate --run-id 123456790121 -c /path/to/config.json
100
106
 
101
- 7. Get help for generate command:
107
+ 8. Get help for generate command:
102
108
  k6-reporter generate --help
103
109
 
104
110
  TIME FORMAT:
@@ -1,3 +1,4 @@
1
1
  export { JsonReporter } from "./json-reporter";
2
2
  export { CliReporter } from "./cli-reporter";
3
3
  export { SlackReporter } from "./slack-reporter";
4
+ export { MarkdownReporter } from "./markdown-reporter";
@@ -0,0 +1,230 @@
1
+ import { writeFileSync } from "fs";
2
+ import { ReporterResponse } from "../data-collector";
3
+ import { table } from "table";
4
+
5
+ export class MarkdownReporter {
6
+ report(data: ReporterResponse, outputPath?: string): void {
7
+ const markdown = this.generateMarkdown(data);
8
+
9
+ if (outputPath) {
10
+ writeFileSync(outputPath, markdown);
11
+ console.log(`Report saved to ${outputPath}`);
12
+ } else {
13
+ console.log(markdown);
14
+ }
15
+ }
16
+
17
+ private generateMarkdown(data: ReporterResponse): string {
18
+ const reportData = data.data as Record<string, unknown>;
19
+ let markdown = "";
20
+
21
+ // Header
22
+ markdown += `# k6 Performance Test Report\n\n`;
23
+ markdown += `- **Run ID:** ${data.runId}\n`;
24
+ markdown += `- **Start:** ${data.startTime}\n`;
25
+ markdown += `- **End:** ${data.endTime}\n\n`;
26
+
27
+ // Summary section
28
+ markdown += this.generateSummary(reportData);
29
+
30
+ // Main metrics
31
+ markdown += this.generateMetrics(reportData);
32
+
33
+ // Tables
34
+ markdown += this.generateTables(reportData);
35
+
36
+ return markdown;
37
+ }
38
+
39
+ private generateSummary(reportData: Record<string, unknown>): string {
40
+ let summary = "## Summary\n\n";
41
+
42
+ let errorPercent = 0;
43
+ let failedChecks = 0;
44
+
45
+ if (reportData.httpReqFailed) {
46
+ const failed = reportData.httpReqFailed as Record<string, number>;
47
+ errorPercent = typeof failed.failureRate === "number" ? failed.failureRate : 0;
48
+ }
49
+
50
+ if (reportData.checks) {
51
+ const checks = reportData.checks as Record<string, number>;
52
+ failedChecks = typeof checks.fails === "number" ? checks.fails : 0;
53
+ }
54
+
55
+ const isSuccess = errorPercent < 1 && failedChecks === 0;
56
+ const status = isSuccess ? "✓ PASS" : "✗ FAIL";
57
+
58
+ summary += `**Status:** ${status}\n\n`;
59
+ summary += `| Metric | Value |\n`;
60
+ summary += `|--------|-------|\n`;
61
+ summary += `| Error Rate | ${errorPercent.toFixed(2)}% |\n`;
62
+ summary += `| Failed Checks | ${failedChecks} |\n\n`;
63
+
64
+ return summary;
65
+ }
66
+
67
+ private generateMetrics(reportData: Record<string, unknown>): string {
68
+ let metrics = "## Metrics\n\n";
69
+
70
+ if (reportData.checks) {
71
+ const checks = reportData.checks as Record<string, number>;
72
+ const passRate = typeof checks.passRate === "number" ? checks.passRate : 0;
73
+ metrics += `- **Checks:** ${passRate.toFixed(2)}% (${checks.passes} ✓${checks.fails ? `, ${checks.fails} ✗` : ""})\n`;
74
+ }
75
+
76
+ if (reportData.rpsAggregated) {
77
+ const rpsAgg = reportData.rpsAggregated as Record<string, unknown>;
78
+ const avg = typeof rpsAgg.avg === "number" ? rpsAgg.avg : 0;
79
+ const p95 = typeof rpsAgg.p95 === "number" ? rpsAgg.p95 : 0;
80
+ const max = typeof rpsAgg.max === "number" ? rpsAgg.max : 0;
81
+ metrics += `- **RPS:** avg=${avg.toFixed(2)}, p(95)=${p95.toFixed(2)}, max=${max.toFixed(2)}\n`;
82
+ }
83
+
84
+ if (reportData.httpReqs) {
85
+ const httpReqs = reportData.httpReqs as Record<string, number>;
86
+ metrics += `- **HTTP Requests:** ${httpReqs.total} (${httpReqs.rate.toFixed(6)}/s)\n`;
87
+ }
88
+
89
+ if (reportData.iterations) {
90
+ const iterations = reportData.iterations as Record<string, number>;
91
+ metrics += `- **Iterations:** ${iterations.total} (${iterations.rate.toFixed(6)}/s)\n`;
92
+ }
93
+
94
+ if (reportData.httpReqDuration) {
95
+ const duration = reportData.httpReqDuration as Record<string, number>;
96
+ metrics += `- **HTTP Duration:** avg=${this.formatDuration(duration.avg || 0)}, p(95)=${this.formatDuration(duration.p95 || 0)}\n`;
97
+ }
98
+
99
+ if (reportData.vus) {
100
+ const vus = reportData.vus as Record<string, number>;
101
+ metrics += `- **VUs:** ${vus.current} (min=${vus.min}, max=${vus.max})\n`;
102
+ }
103
+
104
+ metrics += "\n";
105
+ return metrics;
106
+ }
107
+
108
+ private generateTables(reportData: Record<string, unknown>): string {
109
+ let tables = "";
110
+
111
+ if (reportData.topSlowUrls) {
112
+ const topSlowUrls = reportData.topSlowUrls as Record<string, unknown>;
113
+ const urls = topSlowUrls.urls as Array<{ method: string; url: string; p95Duration: number }>;
114
+ if (urls && urls.length > 0) {
115
+ tables += "## Top 10 Slowest URLs\n\n";
116
+ const tableData = [
117
+ ["#", "Method", "URL", "p(95)"],
118
+ ...urls.map((u, i) => [String(i + 1), u.method, u.url, this.formatDuration(u.p95Duration)]),
119
+ ];
120
+ tables += this.tableToMarkdown(tableData) + "\n\n";
121
+ }
122
+ }
123
+
124
+ if (reportData.rpsPerUrl) {
125
+ const rpsPerUrl = reportData.rpsPerUrl as Record<string, unknown>;
126
+ const urls = rpsPerUrl.urls as Array<{ method: string; url: string; count: number; rps: { avg: number; p95: number; max: number } }>;
127
+ if (urls && urls.length > 0) {
128
+ tables += "## RPS per URL\n\n";
129
+ const tableData = [
130
+ ["#", "Method", "URL", "Count", "avg", "p(95)", "max"],
131
+ ...urls.map((u, i) => [String(i + 1), u.method, u.url, String(u.count), u.rps.avg.toFixed(2), u.rps.p95.toFixed(2), u.rps.max.toFixed(2)]),
132
+ ];
133
+ tables += this.tableToMarkdown(tableData) + "\n\n";
134
+ }
135
+ }
136
+
137
+ if (reportData.successRequests) {
138
+ const successRequests = reportData.successRequests as Record<string, unknown>;
139
+ const requests = successRequests.requests as Array<{ method: string; url: string; status: number; count: number; min: number; avg: number; p95: number }>;
140
+ if (requests && requests.length > 0) {
141
+ tables += "## Top Successful Requests\n\n";
142
+ const tableData = [
143
+ ["#", "Method", "URL", "Status", "Count", "Min", "Avg", "p(95)"],
144
+ ...requests.map((r, i) => [String(i + 1), r.method, r.url, String(r.status), String(r.count), this.formatDuration(r.min), this.formatDuration(r.avg), this.formatDuration(r.p95)]),
145
+ ];
146
+ tables += this.tableToMarkdown(tableData) + "\n\n";
147
+ }
148
+ }
149
+
150
+ if (reportData.errorRequests) {
151
+ const errorRequests = reportData.errorRequests as Record<string, unknown>;
152
+ const errors = errorRequests.errors as Array<{ method: string; url: string; status: number; count: number }>;
153
+ if (errors && errors.length > 0) {
154
+ tables += "## Top Error Requests\n\n";
155
+ const tableData = [
156
+ ["#", "Method", "URL", "Code", "Count"],
157
+ ...errors.map((e, i) => [String(i + 1), e.method, e.url, String(e.status), String(e.count)]),
158
+ ];
159
+ tables += this.tableToMarkdown(tableData) + "\n\n";
160
+ }
161
+ }
162
+
163
+ if (reportData.errorResponsesText) {
164
+ const errorResponsesText = reportData.errorResponsesText as Record<string, unknown>;
165
+ const responses = errorResponsesText.responses as Array<{ url: string; method: string; status: number; error: string }>;
166
+ if (responses && responses.length > 0) {
167
+ const groupedErrors = new Map<string, { method: string; url: string; status: number; error: string; count: number }>();
168
+
169
+ responses.forEach((r) => {
170
+ const key = `${r.method}|${r.url}|${r.status}|${r.error}`;
171
+ if (groupedErrors.has(key)) {
172
+ const entry = groupedErrors.get(key)!;
173
+ entry.count++;
174
+ } else {
175
+ groupedErrors.set(key, {
176
+ method: r.method,
177
+ url: r.url,
178
+ status: r.status,
179
+ error: r.error || "",
180
+ count: 1,
181
+ });
182
+ }
183
+ });
184
+
185
+ tables += "## Error Responses\n\n";
186
+ const tableData = [
187
+ ["#", "Method", "URL", "Status", "Error", "Count"],
188
+ ...Array.from(groupedErrors.values()).map((r, i) => {
189
+ let url = r.url;
190
+ try {
191
+ const urlObj = new URL(url);
192
+ url = urlObj.pathname;
193
+ } catch {
194
+ // If not a full URL, use as-is
195
+ }
196
+ return [String(i + 1), r.method, url, String(r.status), r.error, String(r.count)];
197
+ }),
198
+ ];
199
+ tables += this.tableToMarkdown(tableData) + "\n\n";
200
+ }
201
+ }
202
+
203
+ return tables;
204
+ }
205
+
206
+ private tableToMarkdown(tableData: string[][]): string {
207
+ if (tableData.length === 0) {
208
+ return "";
209
+ }
210
+
211
+ const [headers, ...rows] = tableData;
212
+ let markdown = "| " + headers.join(" | ") + " |\n";
213
+ markdown += "|" + headers.map(() => " --- ").join("|") + "|\n";
214
+ rows.forEach((row) => {
215
+ markdown += "| " + row.join(" | ") + " |\n";
216
+ });
217
+
218
+ return markdown;
219
+ }
220
+
221
+ private formatDuration(ms: number): string {
222
+ if (ms < 1) {
223
+ return `${(ms * 1000).toFixed(2)}µs`;
224
+ }
225
+ if (ms < 1000) {
226
+ return `${ms.toFixed(2)}ms`;
227
+ }
228
+ return `${(ms / 1000).toFixed(2)}s`;
229
+ }
230
+ }
@@ -16,7 +16,7 @@ export class SlackReporter {
16
16
 
17
17
  async report(data: ReporterResponse): Promise<void> {
18
18
  const markdown = this.generateMarkdown(data);
19
- await this.sendMessage(markdown);
19
+ await this.sendMessages(markdown);
20
20
  }
21
21
 
22
22
  private generateMarkdown(data: ReporterResponse): string {
@@ -24,9 +24,10 @@ export class SlackReporter {
24
24
  let markdown = "";
25
25
 
26
26
  // Header
27
- markdown += `\`\`\`\nRun ID: ${data.runId}\n`;
28
- markdown += `Start: ${data.startTime}\n`;
29
- markdown += `End: ${data.endTime}\n\`\`\`\n\n`;
27
+ markdown += `*k6 Performance Test Report*\n`;
28
+ markdown += `• *Run ID:* ${data.runId}\n`;
29
+ markdown += `• *Start:* ${data.startTime}\n`;
30
+ markdown += `• *End:* ${data.endTime}\n\n`;
30
31
 
31
32
  // Summary section
32
33
  markdown += this.generateSummary(reportData);
@@ -41,7 +42,7 @@ export class SlackReporter {
41
42
  }
42
43
 
43
44
  private generateSummary(reportData: Record<string, unknown>): string {
44
- let summary = "";
45
+ let summary = "*Summary*\n";
45
46
 
46
47
  let errorPercent = 0;
47
48
  let failedChecks = 0;
@@ -60,7 +61,7 @@ export class SlackReporter {
60
61
  const status = isSuccess ? "✓ PASS" : "✗ FAIL";
61
62
  const statusEmoji = isSuccess ? "✅" : "❌";
62
63
 
63
- summary += `${statusEmoji} *${status}*\n`;
64
+ summary += `${statusEmoji} *${status}*\n\n`;
64
65
  summary += `• Error Rate: ${errorPercent.toFixed(2)}%\n`;
65
66
  summary += `• Failed Checks: ${failedChecks}\n\n`;
66
67
 
@@ -115,13 +116,12 @@ export class SlackReporter {
115
116
  const topSlowUrls = reportData.topSlowUrls as Record<string, unknown>;
116
117
  const urls = topSlowUrls.urls as Array<{ method: string; url: string; p95Duration: number }>;
117
118
  if (urls && urls.length > 0) {
118
- tables += "*Top 10 Slowest URLs:*\n```\n";
119
+ tables += "*Top 10 Slowest URLs*\n";
119
120
  const tableData = [
120
121
  ["#", "Method", "URL", "p(95)"],
121
122
  ...urls.map((u, i) => [String(i + 1), u.method, u.url, this.formatDuration(u.p95Duration)]),
122
123
  ];
123
- tables += table(tableData, { border: { topBody: "", topJoin: "", topLeft: "", topRight: "", bottomBody: "", bottomJoin: "", bottomLeft: "", bottomRight: "", bodyLeft: "", bodyRight: "", bodyJoin: "", joinBody: "─", joinLeft: "", joinRight: "", joinJoin: "" }, drawHorizontalLine: (index) => index === 1, columns: { 0: { alignment: "left" }, 1: { alignment: "left" }, 2: { alignment: "left" }, 3: { alignment: "left" } } });
124
- tables += "```\n\n";
124
+ tables += "```\n" + this.formatTable(tableData) + "```\n\n";
125
125
  }
126
126
  }
127
127
 
@@ -129,13 +129,25 @@ export class SlackReporter {
129
129
  const rpsPerUrl = reportData.rpsPerUrl as Record<string, unknown>;
130
130
  const urls = rpsPerUrl.urls as Array<{ method: string; url: string; count: number; rps: { avg: number; p95: number; max: number } }>;
131
131
  if (urls && urls.length > 0) {
132
- tables += "*RPS per URL:*\n```\n";
132
+ tables += "*RPS per URL*\n";
133
133
  const tableData = [
134
134
  ["#", "Method", "URL", "Count", "avg", "p(95)", "max"],
135
135
  ...urls.map((u, i) => [String(i + 1), u.method, u.url, String(u.count), u.rps.avg.toFixed(2), u.rps.p95.toFixed(2), u.rps.max.toFixed(2)]),
136
136
  ];
137
- tables += table(tableData, { border: { topBody: "", topJoin: "", topLeft: "", topRight: "", bottomBody: "", bottomJoin: "", bottomLeft: "", bottomRight: "", bodyLeft: "", bodyRight: "", bodyJoin: "", joinBody: "─", joinLeft: "", joinRight: "", joinJoin: "" }, drawHorizontalLine: (index) => index === 1, columns: { 0: { alignment: "left" }, 1: { alignment: "left" }, 2: { alignment: "left" }, 3: { alignment: "left" }, 4: { alignment: "left" }, 5: { alignment: "left" }, 6: { alignment: "left" } } });
138
- tables += "```\n\n";
137
+ tables += "```\n" + this.formatTable(tableData) + "```\n\n";
138
+ }
139
+ }
140
+
141
+ if (reportData.successRequests) {
142
+ const successRequests = reportData.successRequests as Record<string, unknown>;
143
+ const requests = successRequests.requests as Array<{ method: string; url: string; status: number; count: number; min: number; avg: number; p95: number }>;
144
+ if (requests && requests.length > 0) {
145
+ tables += "*Top Successful Requests*\n";
146
+ const tableData = [
147
+ ["#", "Method", "URL", "Status", "Count", "Min", "Avg", "p(95)"],
148
+ ...requests.map((r, i) => [String(i + 1), r.method, r.url, String(r.status), String(r.count), this.formatDuration(r.min), this.formatDuration(r.avg), this.formatDuration(r.p95)]),
149
+ ];
150
+ tables += "```\n" + this.formatTable(tableData) + "```\n\n";
139
151
  }
140
152
  }
141
153
 
@@ -143,13 +155,12 @@ export class SlackReporter {
143
155
  const errorRequests = reportData.errorRequests as Record<string, unknown>;
144
156
  const errors = errorRequests.errors as Array<{ method: string; url: string; status: number; count: number }>;
145
157
  if (errors && errors.length > 0) {
146
- tables += "*Top Error Requests:*\n```\n";
158
+ tables += "*Top Error Requests*\n";
147
159
  const tableData = [
148
160
  ["#", "Method", "URL", "Code", "Count"],
149
161
  ...errors.map((e, i) => [String(i + 1), e.method, e.url, String(e.status), String(e.count)]),
150
162
  ];
151
- tables += table(tableData, { border: { topBody: "", topJoin: "", topLeft: "", topRight: "", bottomBody: "", bottomJoin: "", bottomLeft: "", bottomRight: "", bodyLeft: "", bodyRight: "", bodyJoin: "", joinBody: "─", joinLeft: "", joinRight: "", joinJoin: "" }, drawHorizontalLine: (index) => index === 1, columns: { 0: { alignment: "left" }, 1: { alignment: "left" }, 2: { alignment: "left" }, 3: { alignment: "left" }, 4: { alignment: "left" } } });
152
- tables += "```\n\n";
163
+ tables += "```\n" + this.formatTable(tableData) + "```\n\n";
153
164
  }
154
165
  }
155
166
 
@@ -175,7 +186,7 @@ export class SlackReporter {
175
186
  }
176
187
  });
177
188
 
178
- tables += "*Error Responses:*\n```\n";
189
+ tables += "*Error Responses*\n";
179
190
  const tableData = [
180
191
  ["#", "Method", "URL", "Status", "Error", "Count"],
181
192
  ...Array.from(groupedErrors.values()).map((r, i) => {
@@ -189,14 +200,47 @@ export class SlackReporter {
189
200
  return [String(i + 1), r.method, url, String(r.status), r.error, String(r.count)];
190
201
  }),
191
202
  ];
192
- tables += table(tableData, { border: { topBody: "", topJoin: "", topLeft: "", topRight: "", bottomBody: "", bottomJoin: "", bottomLeft: "", bottomRight: "", bodyLeft: "", bodyRight: "", bodyJoin: "", joinBody: "─", joinLeft: "", joinRight: "", joinJoin: "" }, drawHorizontalLine: (index) => index === 1, columns: { 0: { alignment: "left" }, 1: { alignment: "left" }, 2: { alignment: "left" }, 3: { alignment: "left" }, 4: { alignment: "left" }, 5: { alignment: "left" } } });
193
- tables += "```\n\n";
203
+ tables += "```\n" + this.formatTable(tableData) + "```\n\n";
194
204
  }
195
205
  }
196
206
 
197
207
  return tables;
198
208
  }
199
209
 
210
+ private formatTable(tableData: string[][]): string {
211
+ if (tableData.length === 0) {
212
+ return "";
213
+ }
214
+
215
+ const columnCount = tableData[0].length;
216
+ const columnConfig: Record<number, { alignment: "left" | "right" | "center" }> = {};
217
+ for (let i = 0; i < columnCount; i++) {
218
+ columnConfig[i] = { alignment: "left" };
219
+ }
220
+
221
+ return table(tableData, {
222
+ border: {
223
+ topBody: "─",
224
+ topJoin: "┬",
225
+ topLeft: "┌",
226
+ topRight: "┐",
227
+ bottomBody: "─",
228
+ bottomJoin: "┴",
229
+ bottomLeft: "└",
230
+ bottomRight: "┘",
231
+ bodyLeft: "│",
232
+ bodyRight: "│",
233
+ bodyJoin: "│",
234
+ joinBody: "─",
235
+ joinLeft: "├",
236
+ joinRight: "┤",
237
+ joinJoin: "┼",
238
+ },
239
+ drawHorizontalLine: (index) => index === 1,
240
+ columns: columnConfig,
241
+ });
242
+ }
243
+
200
244
  private formatDuration(ms: number): string {
201
245
  if (ms < 1) {
202
246
  return `${(ms * 1000).toFixed(2)}µs`;
@@ -207,6 +251,38 @@ export class SlackReporter {
207
251
  return `${(ms / 1000).toFixed(2)}s`;
208
252
  }
209
253
 
254
+ private async sendMessages(markdown: string): Promise<void> {
255
+ const MAX_BLOCK_LENGTH = 3000;
256
+ const messages = this.splitIntoMessages(markdown, MAX_BLOCK_LENGTH);
257
+
258
+ for (const message of messages) {
259
+ await this.sendMessage(message);
260
+ }
261
+ }
262
+
263
+ private splitIntoMessages(text: string, maxLength: number): string[] {
264
+ const messages: string[] = [];
265
+ const sections = text.split(/(\n\n)/);
266
+ let currentMessage = "";
267
+
268
+ for (const section of sections) {
269
+ if ((currentMessage + section).length <= maxLength) {
270
+ currentMessage += section;
271
+ } else {
272
+ if (currentMessage) {
273
+ messages.push(currentMessage);
274
+ }
275
+ currentMessage = section;
276
+ }
277
+ }
278
+
279
+ if (currentMessage) {
280
+ messages.push(currentMessage);
281
+ }
282
+
283
+ return messages;
284
+ }
285
+
210
286
  private async sendMessage(markdown: string): Promise<void> {
211
287
  try {
212
288
  await this.client.chat.postMessage({