k6-perf-reporter 1.6.0 → 1.6.1

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":";;;AAAA,2BAAmC;AAInC,MAAa,gBAAgB;IAC3B,MAAM,CAAC,IAAsB,EAAE,UAAmB;QAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAE7C,IAAI,UAAU,EAAE,CAAC;YACf,IAAA,kBAAa,EAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,mBAAmB,UAAU,EAAE,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxB,CAAC;IACH,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,kCAAkC,CAAC;QAC/C,QAAQ,IAAI,iBAAiB,IAAI,CAAC,KAAK,IAAI,CAAC;QAC5C,QAAQ,IAAI,gBAAgB,IAAI,CAAC,SAAS,IAAI,CAAC;QAC/C,QAAQ,IAAI,cAAc,IAAI,CAAC,OAAO,MAAM,CAAC;QAE7C,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,gBAAgB,CAAC;QAE/B,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;QAE/C,OAAO,IAAI,eAAe,MAAM,MAAM,CAAC;QACvC,OAAO,IAAI,sBAAsB,CAAC;QAClC,OAAO,IAAI,sBAAsB,CAAC;QAClC,OAAO,IAAI,kBAAkB,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5D,OAAO,IAAI,qBAAqB,YAAY,QAAQ,CAAC;QAErD,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,eAAe,CAAC,UAAmC;QACzD,IAAI,OAAO,GAAG,gBAAgB,CAAC;QAE/B,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,iBAAiB,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;QACxH,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,kBAAkB,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QAClG,CAAC;QAED,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAkC,CAAC;YAC/D,OAAO,IAAI,wBAAwB,QAAQ,CAAC,KAAK,KAAK,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QACxF,CAAC;QAED,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,UAAU,GAAG,UAAU,CAAC,UAAoC,CAAC;YACnE,OAAO,IAAI,qBAAqB,UAAU,CAAC,KAAK,KAAK,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QACzF,CAAC;QAED,IAAI,UAAU,CAAC,eAAe,EAAE,CAAC;YAC/B,MAAM,QAAQ,GAAG,UAAU,CAAC,eAAyC,CAAC;YACtE,OAAO,IAAI,4BAA4B,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC;QACrI,CAAC;QAED,IAAI,UAAU,CAAC,GAAG,EAAE,CAAC;YACnB,MAAM,GAAG,GAAG,UAAU,CAAC,GAA6B,CAAC;YACrD,OAAO,IAAI,cAAc,GAAG,CAAC,OAAO,SAAS,GAAG,CAAC,GAAG,SAAS,GAAG,CAAC,GAAG,KAAK,CAAC;QAC5E,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,4BAA4B,CAAC;gBACvC,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,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC;YACrD,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,oBAAoB,CAAC;gBAC/B,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,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC;YACrD,CAAC;QACH,CAAC;QAED,IAAI,UAAU,CAAC,eAAe,EAAE,CAAC;YAC/B,MAAM,eAAe,GAAG,UAAU,CAAC,eAA0C,CAAC;YAC9E,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAwH,CAAC;YAC1J,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpC,MAAM,IAAI,gCAAgC,CAAC;gBAC3C,MAAM,SAAS,GAAG;oBAChB,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC;oBAChE,GAAG,QAAQ,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,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;iBACnL,CAAC;gBACF,MAAM,IAAI,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC;YACrD,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,2BAA2B,CAAC;gBACtC,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,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC;YACrD,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,wBAAwB,CAAC;gBACnC,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,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC;YACrD,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,eAAe,CAAC,SAAqB;QAC3C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,SAAS,CAAC;QACrC,IAAI,QAAQ,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;QACnD,QAAQ,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC/D,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YACnB,QAAQ,IAAI,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC;IAClB,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;CACF;AAjOD,4CAiOC"}
@@ -8,6 +8,7 @@ export declare class SlackReporter {
8
8
  private generateSummary;
9
9
  private generateMetrics;
10
10
  private generateTables;
11
+ private tableToMarkdown;
11
12
  private formatDuration;
12
13
  private sendMessage;
13
14
  }
@@ -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;AAErD,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,eAAe;IAevB,OAAO,CAAC,cAAc;YAUR,WAAW;CAmB1B"}
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SlackReporter = void 0;
4
4
  const web_api_1 = require("@slack/web-api");
5
- const table_1 = require("table");
6
5
  class SlackReporter {
7
6
  constructor(token, channel) {
8
7
  this.client = new web_api_1.WebClient(token);
@@ -19,9 +18,10 @@ class SlackReporter {
19
18
  const reportData = data.data;
20
19
  let markdown = "";
21
20
  // Header
22
- markdown += `\`\`\`\nRun ID: ${data.runId}\n`;
23
- markdown += `Start: ${data.startTime}\n`;
24
- markdown += `End: ${data.endTime}\n\`\`\`\n\n`;
21
+ markdown += `*k6 Performance Test Report*\n`;
22
+ markdown += `• *Run ID:* ${data.runId}\n`;
23
+ markdown += `• *Start:* ${data.startTime}\n`;
24
+ markdown += `• *End:* ${data.endTime}\n\n`;
25
25
  // Summary section
26
26
  markdown += this.generateSummary(reportData);
27
27
  // Main metrics
@@ -31,7 +31,7 @@ class SlackReporter {
31
31
  return markdown;
32
32
  }
33
33
  generateSummary(reportData) {
34
- let summary = "";
34
+ let summary = "*Summary*\n";
35
35
  let errorPercent = 0;
36
36
  let failedChecks = 0;
37
37
  if (reportData.httpReqFailed) {
@@ -45,7 +45,7 @@ class SlackReporter {
45
45
  const isSuccess = errorPercent < 1 && failedChecks === 0;
46
46
  const status = isSuccess ? "✓ PASS" : "✗ FAIL";
47
47
  const statusEmoji = isSuccess ? "✅" : "❌";
48
- summary += `${statusEmoji} *${status}*\n`;
48
+ summary += `${statusEmoji} *${status}*\n\n`;
49
49
  summary += `• Error Rate: ${errorPercent.toFixed(2)}%\n`;
50
50
  summary += `• Failed Checks: ${failedChecks}\n\n`;
51
51
  return summary;
@@ -89,39 +89,48 @@ class SlackReporter {
89
89
  const topSlowUrls = reportData.topSlowUrls;
90
90
  const urls = topSlowUrls.urls;
91
91
  if (urls && urls.length > 0) {
92
- tables += "*Top 10 Slowest URLs:*\n```\n";
92
+ tables += "*Top 10 Slowest URLs*\n\n";
93
93
  const tableData = [
94
94
  ["#", "Method", "URL", "p(95)"],
95
95
  ...urls.map((u, i) => [String(i + 1), u.method, u.url, this.formatDuration(u.p95Duration)]),
96
96
  ];
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";
97
+ tables += this.tableToMarkdown(tableData) + "\n\n";
99
98
  }
100
99
  }
101
100
  if (reportData.rpsPerUrl) {
102
101
  const rpsPerUrl = reportData.rpsPerUrl;
103
102
  const urls = rpsPerUrl.urls;
104
103
  if (urls && urls.length > 0) {
105
- tables += "*RPS per URL:*\n```\n";
104
+ tables += "*RPS per URL*\n\n";
106
105
  const tableData = [
107
106
  ["#", "Method", "URL", "Count", "avg", "p(95)", "max"],
108
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)]),
109
108
  ];
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";
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";
112
122
  }
113
123
  }
114
124
  if (reportData.errorRequests) {
115
125
  const errorRequests = reportData.errorRequests;
116
126
  const errors = errorRequests.errors;
117
127
  if (errors && errors.length > 0) {
118
- tables += "*Top Error Requests:*\n```\n";
128
+ tables += "*Top Error Requests*\n\n";
119
129
  const tableData = [
120
130
  ["#", "Method", "URL", "Code", "Count"],
121
131
  ...errors.map((e, i) => [String(i + 1), e.method, e.url, String(e.status), String(e.count)]),
122
132
  ];
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";
133
+ tables += this.tableToMarkdown(tableData) + "\n\n";
125
134
  }
126
135
  }
127
136
  if (reportData.errorResponsesText) {
@@ -145,7 +154,7 @@ class SlackReporter {
145
154
  });
146
155
  }
147
156
  });
148
- tables += "*Error Responses:*\n```\n";
157
+ tables += "*Error Responses*\n\n";
149
158
  const tableData = [
150
159
  ["#", "Method", "URL", "Status", "Error", "Count"],
151
160
  ...Array.from(groupedErrors.values()).map((r, i) => {
@@ -160,12 +169,23 @@ class SlackReporter {
160
169
  return [String(i + 1), r.method, url, String(r.status), r.error, String(r.count)];
161
170
  }),
162
171
  ];
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";
172
+ tables += this.tableToMarkdown(tableData) + "\n\n";
165
173
  }
166
174
  }
167
175
  return tables;
168
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
+ }
169
189
  formatDuration(ms) {
170
190
  if (ms < 1) {
171
191
  return `${(ms * 1000).toFixed(2)}µs`;
@@ -1 +1 @@
1
- {"version":3,"file":"slack-reporter.js","sourceRoot":"","sources":["../../src/reporters/slack-reporter.ts"],"names":[],"mappings":"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"}
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.1",
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
+ }
@@ -1,6 +1,5 @@
1
1
  import { WebClient } from "@slack/web-api";
2
2
  import { ReporterResponse } from "../data-collector";
3
- import { table } from "table";
4
3
 
5
4
  export class SlackReporter {
6
5
  private client: WebClient;
@@ -24,9 +23,10 @@ export class SlackReporter {
24
23
  let markdown = "";
25
24
 
26
25
  // Header
27
- markdown += `\`\`\`\nRun ID: ${data.runId}\n`;
28
- markdown += `Start: ${data.startTime}\n`;
29
- markdown += `End: ${data.endTime}\n\`\`\`\n\n`;
26
+ markdown += `*k6 Performance Test Report*\n`;
27
+ markdown += `• *Run ID:* ${data.runId}\n`;
28
+ markdown += `• *Start:* ${data.startTime}\n`;
29
+ markdown += `• *End:* ${data.endTime}\n\n`;
30
30
 
31
31
  // Summary section
32
32
  markdown += this.generateSummary(reportData);
@@ -41,7 +41,7 @@ export class SlackReporter {
41
41
  }
42
42
 
43
43
  private generateSummary(reportData: Record<string, unknown>): string {
44
- let summary = "";
44
+ let summary = "*Summary*\n";
45
45
 
46
46
  let errorPercent = 0;
47
47
  let failedChecks = 0;
@@ -60,7 +60,7 @@ export class SlackReporter {
60
60
  const status = isSuccess ? "✓ PASS" : "✗ FAIL";
61
61
  const statusEmoji = isSuccess ? "✅" : "❌";
62
62
 
63
- summary += `${statusEmoji} *${status}*\n`;
63
+ summary += `${statusEmoji} *${status}*\n\n`;
64
64
  summary += `• Error Rate: ${errorPercent.toFixed(2)}%\n`;
65
65
  summary += `• Failed Checks: ${failedChecks}\n\n`;
66
66
 
@@ -115,13 +115,12 @@ export class SlackReporter {
115
115
  const topSlowUrls = reportData.topSlowUrls as Record<string, unknown>;
116
116
  const urls = topSlowUrls.urls as Array<{ method: string; url: string; p95Duration: number }>;
117
117
  if (urls && urls.length > 0) {
118
- tables += "*Top 10 Slowest URLs:*\n```\n";
118
+ tables += "*Top 10 Slowest URLs*\n\n";
119
119
  const tableData = [
120
120
  ["#", "Method", "URL", "p(95)"],
121
121
  ...urls.map((u, i) => [String(i + 1), u.method, u.url, this.formatDuration(u.p95Duration)]),
122
122
  ];
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";
123
+ tables += this.tableToMarkdown(tableData) + "\n\n";
125
124
  }
126
125
  }
127
126
 
@@ -129,13 +128,25 @@ export class SlackReporter {
129
128
  const rpsPerUrl = reportData.rpsPerUrl as Record<string, unknown>;
130
129
  const urls = rpsPerUrl.urls as Array<{ method: string; url: string; count: number; rps: { avg: number; p95: number; max: number } }>;
131
130
  if (urls && urls.length > 0) {
132
- tables += "*RPS per URL:*\n```\n";
131
+ tables += "*RPS per URL*\n\n";
133
132
  const tableData = [
134
133
  ["#", "Method", "URL", "Count", "avg", "p(95)", "max"],
135
134
  ...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
135
  ];
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";
136
+ tables += this.tableToMarkdown(tableData) + "\n\n";
137
+ }
138
+ }
139
+
140
+ if (reportData.successRequests) {
141
+ const successRequests = reportData.successRequests as Record<string, unknown>;
142
+ const requests = successRequests.requests as Array<{ method: string; url: string; status: number; count: number; min: number; avg: number; p95: number }>;
143
+ if (requests && requests.length > 0) {
144
+ tables += "*Top Successful Requests*\n\n";
145
+ const tableData = [
146
+ ["#", "Method", "URL", "Status", "Count", "Min", "Avg", "p(95)"],
147
+ ...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)]),
148
+ ];
149
+ tables += this.tableToMarkdown(tableData) + "\n\n";
139
150
  }
140
151
  }
141
152
 
@@ -143,13 +154,12 @@ export class SlackReporter {
143
154
  const errorRequests = reportData.errorRequests as Record<string, unknown>;
144
155
  const errors = errorRequests.errors as Array<{ method: string; url: string; status: number; count: number }>;
145
156
  if (errors && errors.length > 0) {
146
- tables += "*Top Error Requests:*\n```\n";
157
+ tables += "*Top Error Requests*\n\n";
147
158
  const tableData = [
148
159
  ["#", "Method", "URL", "Code", "Count"],
149
160
  ...errors.map((e, i) => [String(i + 1), e.method, e.url, String(e.status), String(e.count)]),
150
161
  ];
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";
162
+ tables += this.tableToMarkdown(tableData) + "\n\n";
153
163
  }
154
164
  }
155
165
 
@@ -175,7 +185,7 @@ export class SlackReporter {
175
185
  }
176
186
  });
177
187
 
178
- tables += "*Error Responses:*\n```\n";
188
+ tables += "*Error Responses*\n\n";
179
189
  const tableData = [
180
190
  ["#", "Method", "URL", "Status", "Error", "Count"],
181
191
  ...Array.from(groupedErrors.values()).map((r, i) => {
@@ -189,14 +199,28 @@ export class SlackReporter {
189
199
  return [String(i + 1), r.method, url, String(r.status), r.error, String(r.count)];
190
200
  }),
191
201
  ];
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";
202
+ tables += this.tableToMarkdown(tableData) + "\n\n";
194
203
  }
195
204
  }
196
205
 
197
206
  return tables;
198
207
  }
199
208
 
209
+ private tableToMarkdown(tableData: string[][]): string {
210
+ if (tableData.length === 0) {
211
+ return "";
212
+ }
213
+
214
+ const [headers, ...rows] = tableData;
215
+ let markdown = "| " + headers.join(" | ") + " |\n";
216
+ markdown += "|" + headers.map(() => " --- ").join("|") + "|\n";
217
+ rows.forEach((row) => {
218
+ markdown += "| " + row.join(" | ") + " |\n";
219
+ });
220
+
221
+ return markdown;
222
+ }
223
+
200
224
  private formatDuration(ms: number): string {
201
225
  if (ms < 1) {
202
226
  return `${(ms * 1000).toFixed(2)}µs`;