@wp-tester/results 0.0.1 → 0.0.3

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.
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Tests for StreamingReporter
3
+ */
4
+ import { describe, it, expect, beforeEach } from "vitest";
5
+ import { StreamingReporter } from "./streaming.js";
6
+ class MockWriter {
7
+ constructor() {
8
+ this.lines = [];
9
+ this.currentOutput = [];
10
+ }
11
+ write(text) {
12
+ // Handle ANSI clear codes
13
+ if (text === "\x1b[1A\x1b[2K") {
14
+ // Clear last line
15
+ this.currentOutput.pop();
16
+ }
17
+ else {
18
+ this.lines.push(text);
19
+ this.currentOutput.push(text);
20
+ }
21
+ }
22
+ writeLine(text) {
23
+ this.lines.push(text + "\n");
24
+ this.currentOutput.push(text + "\n");
25
+ }
26
+ getOutput() {
27
+ return this.lines.join("");
28
+ }
29
+ getCurrentOutput() {
30
+ return this.currentOutput.join("");
31
+ }
32
+ clear() {
33
+ this.lines = [];
34
+ this.currentOutput = [];
35
+ }
36
+ }
37
+ describe("StreamingReporter", () => {
38
+ let writer;
39
+ let reporter;
40
+ beforeEach(() => {
41
+ writer = new MockWriter();
42
+ reporter = new StreamingReporter({
43
+ writer,
44
+ showRunBoundaries: false, // Disable header for cleaner tests
45
+ showSummary: false, // Disable summary for cleaner tests
46
+ });
47
+ });
48
+ it("should handle a simple test pass", () => {
49
+ reporter.onEvent({ type: "run:start" });
50
+ reporter.onEvent({ type: "suite:start", name: "My Suite" });
51
+ reporter.onEvent({ type: "test:start", name: "test 1", suiteName: "My Suite" });
52
+ reporter.onEvent({ type: "test:pass", name: "test 1", duration: 100, suiteName: "My Suite" });
53
+ reporter.onEvent({ type: "suite:end", name: "My Suite" });
54
+ const output = writer.getOutput();
55
+ expect(output).toContain("My Suite");
56
+ expect(output).toContain("test 1");
57
+ expect(output).toContain("✓");
58
+ });
59
+ it("should handle parallel file execution without interference", () => {
60
+ reporter.onEvent({ type: "run:start" });
61
+ // Start two files in parallel
62
+ reporter.onEvent({ type: "file:start", fileId: "file1", fileName: "test1.spec.ts" });
63
+ reporter.onEvent({ type: "file:start", fileId: "file2", fileName: "test2.spec.ts" });
64
+ // File 1 starts a suite
65
+ reporter.onEvent({ type: "suite:start", name: "Suite 1", fileId: "file1" });
66
+ // File 2 starts a suite (parallel)
67
+ reporter.onEvent({ type: "suite:start", name: "Suite 2", fileId: "file2" });
68
+ // File 1 starts a test
69
+ reporter.onEvent({ type: "test:start", name: "test 1", suiteName: "Suite 1", fileId: "file1" });
70
+ // File 2 starts a test (parallel)
71
+ reporter.onEvent({ type: "test:start", name: "test 2", suiteName: "Suite 2", fileId: "file2" });
72
+ // File 1 test passes
73
+ reporter.onEvent({ type: "test:pass", name: "test 1", duration: 100, suiteName: "Suite 1", fileId: "file1" });
74
+ // File 2 test passes
75
+ reporter.onEvent({ type: "test:pass", name: "test 2", duration: 150, suiteName: "Suite 2", fileId: "file2" });
76
+ // End suites
77
+ reporter.onEvent({ type: "suite:end", name: "Suite 1", fileId: "file1" });
78
+ reporter.onEvent({ type: "suite:end", name: "Suite 2", fileId: "file2" });
79
+ // End files
80
+ reporter.onEvent({ type: "file:end", fileId: "file1" });
81
+ reporter.onEvent({ type: "file:end", fileId: "file2" });
82
+ const output = writer.getCurrentOutput();
83
+ // Both suites should be present
84
+ expect(output).toContain("Suite 1");
85
+ expect(output).toContain("Suite 2");
86
+ // Both tests should be present
87
+ expect(output).toContain("test 1");
88
+ expect(output).toContain("test 2");
89
+ // Both should show as passed
90
+ const passMarks = output.match(/✓/g);
91
+ expect(passMarks).toHaveLength(2);
92
+ // Verify counts
93
+ const counts = reporter.getCounts();
94
+ expect(counts.total).toBe(2);
95
+ expect(counts.passed).toBe(2);
96
+ expect(counts.failed).toBe(0);
97
+ });
98
+ it("should handle test failures with error messages", () => {
99
+ reporter.onEvent({ type: "run:start" });
100
+ reporter.onEvent({ type: "suite:start", name: "Suite" });
101
+ reporter.onEvent({ type: "test:start", name: "failing test", suiteName: "Suite" });
102
+ reporter.onEvent({
103
+ type: "test:fail",
104
+ name: "failing test",
105
+ duration: 50,
106
+ message: "Expected 1 to equal 2",
107
+ suiteName: "Suite"
108
+ });
109
+ reporter.onEvent({ type: "suite:end", name: "Suite" });
110
+ const output = writer.getOutput();
111
+ expect(output).toContain("failing test");
112
+ expect(output).toContain("✗");
113
+ expect(output).toContain("Expected 1 to equal 2");
114
+ const counts = reporter.getCounts();
115
+ expect(counts.failed).toBe(1);
116
+ });
117
+ it("should handle skipped tests", () => {
118
+ reporter.onEvent({ type: "run:start" });
119
+ reporter.onEvent({ type: "suite:start", name: "Suite" });
120
+ reporter.onEvent({
121
+ type: "test:skip",
122
+ name: "skipped test",
123
+ message: "Not implemented yet",
124
+ suiteName: "Suite"
125
+ });
126
+ reporter.onEvent({ type: "suite:end", name: "Suite" });
127
+ const output = writer.getOutput();
128
+ expect(output).toContain("skipped test");
129
+ expect(output).toContain("○");
130
+ expect(output).toContain("Not implemented yet");
131
+ const counts = reporter.getCounts();
132
+ expect(counts.skipped).toBe(1);
133
+ });
134
+ it("should generate correct CTRF report", () => {
135
+ reporter.onEvent({ type: "run:start", toolName: "test-runner" });
136
+ reporter.onEvent({ type: "suite:start", name: "Suite" });
137
+ reporter.onEvent({ type: "test:pass", name: "test 1", duration: 100, suiteName: "Suite" });
138
+ reporter.onEvent({ type: "test:fail", name: "test 2", duration: 50, message: "Error", suiteName: "Suite" });
139
+ reporter.onEvent({ type: "suite:end", name: "Suite" });
140
+ reporter.onEvent({ type: "run:end" });
141
+ const report = reporter.getReport();
142
+ expect(report.reportFormat).toBe("CTRF");
143
+ expect(report.results.tool.name).toBe("test-runner");
144
+ expect(report.results.summary.tests).toBe(2);
145
+ expect(report.results.summary.passed).toBe(1);
146
+ expect(report.results.summary.failed).toBe(1);
147
+ expect(report.results.tests).toHaveLength(2);
148
+ const passedTest = report.results.tests.find(t => t.status === "passed");
149
+ expect(passedTest?.name).toBe("Suite::test 1");
150
+ expect(passedTest?.duration).toBe(100);
151
+ const failedTest = report.results.tests.find(t => t.status === "failed");
152
+ expect(failedTest?.name).toBe("Suite::test 2");
153
+ expect(failedTest?.message).toBe("Error");
154
+ });
155
+ it("should handle interleaved parallel test events", () => {
156
+ reporter.onEvent({ type: "run:start" });
157
+ // Simulate realistic parallel execution with interleaved events
158
+ reporter.onEvent({ type: "file:start", fileId: "file1" });
159
+ reporter.onEvent({ type: "suite:start", name: "Suite A", fileId: "file1" });
160
+ reporter.onEvent({ type: "file:start", fileId: "file2" });
161
+ reporter.onEvent({ type: "test:start", name: "test A1", suiteName: "Suite A", fileId: "file1" });
162
+ reporter.onEvent({ type: "suite:start", name: "Suite B", fileId: "file2" });
163
+ reporter.onEvent({ type: "test:start", name: "test B1", suiteName: "Suite B", fileId: "file2" });
164
+ reporter.onEvent({ type: "test:pass", name: "test A1", duration: 100, suiteName: "Suite A", fileId: "file1" });
165
+ reporter.onEvent({ type: "test:start", name: "test A2", suiteName: "Suite A", fileId: "file1" });
166
+ reporter.onEvent({ type: "test:pass", name: "test B1", duration: 150, suiteName: "Suite B", fileId: "file2" });
167
+ reporter.onEvent({ type: "test:pass", name: "test A2", duration: 80, suiteName: "Suite A", fileId: "file1" });
168
+ reporter.onEvent({ type: "suite:end", name: "Suite A", fileId: "file1" });
169
+ reporter.onEvent({ type: "suite:end", name: "Suite B", fileId: "file2" });
170
+ reporter.onEvent({ type: "file:end", fileId: "file1" });
171
+ reporter.onEvent({ type: "file:end", fileId: "file2" });
172
+ const counts = reporter.getCounts();
173
+ expect(counts.total).toBe(3);
174
+ expect(counts.passed).toBe(3);
175
+ const output = writer.getOutput();
176
+ expect(output).toContain("Suite A");
177
+ expect(output).toContain("Suite B");
178
+ expect(output).toContain("test A1");
179
+ expect(output).toContain("test A2");
180
+ expect(output).toContain("test B1");
181
+ });
182
+ it("should handle nested suites", () => {
183
+ reporter.onEvent({ type: "run:start" });
184
+ reporter.onEvent({ type: "suite:start", name: "Outer Suite" });
185
+ reporter.onEvent({ type: "suite:start", name: "Inner Suite" });
186
+ reporter.onEvent({ type: "test:pass", name: "nested test", duration: 50, suiteName: "Inner Suite" });
187
+ reporter.onEvent({ type: "suite:end", name: "Inner Suite" });
188
+ reporter.onEvent({ type: "suite:end", name: "Outer Suite" });
189
+ const output = writer.getOutput();
190
+ expect(output).toContain("Outer Suite");
191
+ expect(output).toContain("Inner Suite");
192
+ expect(output).toContain("nested test");
193
+ });
194
+ it("should clean up running tests when suite ends", () => {
195
+ reporter.onEvent({ type: "run:start" });
196
+ reporter.onEvent({ type: "suite:start", name: "Suite" });
197
+ reporter.onEvent({ type: "test:start", name: "hanging test", suiteName: "Suite" });
198
+ // End suite without test completion - should clean up running test
199
+ reporter.onEvent({ type: "suite:end", name: "Suite" });
200
+ // Test should be marked as pending (not running)
201
+ const output = writer.getCurrentOutput();
202
+ expect(output).toContain("hanging test");
203
+ expect(output).toContain("○"); // pending status
204
+ });
205
+ it("should clean up all running tests when run ends", () => {
206
+ reporter.onEvent({ type: "run:start" });
207
+ reporter.onEvent({ type: "suite:start", name: "Suite" });
208
+ reporter.onEvent({ type: "test:start", name: "hanging test", suiteName: "Suite" });
209
+ // End run without completing test
210
+ reporter.onEvent({ type: "run:end" });
211
+ // Final output should not have any running tests
212
+ const output = writer.getCurrentOutput();
213
+ expect(output).toContain("hanging test");
214
+ expect(output).toContain("○"); // marked as pending, not running
215
+ });
216
+ });
217
+ //# sourceMappingURL=streaming.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming.spec.js","sourceRoot":"","sources":["../src/streaming.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Test Summary Output
3
+ *
4
+ * Provides formatted test summary output using clack/prompts for consistent CLI display.
5
+ */
6
+ import type { Summary } from "ctrf";
7
+ /**
8
+ * Print test summary to console
9
+ *
10
+ * @param summary - The test summary from a CTRF report
11
+ */
12
+ export declare function printSummary(summary: Summary): void;
13
+ //# sourceMappingURL=summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.d.ts","sourceRoot":"","sources":["../src/summary.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAapC;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqBnD"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Test Summary Output
3
+ *
4
+ * Provides formatted test summary output using clack/prompts for consistent CLI display.
5
+ */
6
+ import pc from "picocolors";
7
+ /**
8
+ * Format duration in human-readable format
9
+ */
10
+ function formatDuration(ms) {
11
+ if (ms < 1000) {
12
+ return `${Math.round(ms)}ms`;
13
+ }
14
+ return `${(ms / 1000).toFixed(2)}s`;
15
+ }
16
+ /**
17
+ * Print test summary to console
18
+ *
19
+ * @param summary - The test summary from a CTRF report
20
+ */
21
+ export function printSummary(summary) {
22
+ const duration = summary.stop - summary.start;
23
+ console.log("");
24
+ if (summary.passed > 0) {
25
+ console.log(pc.green(` ✓ ${summary.passed} passed`));
26
+ }
27
+ if (summary.failed > 0) {
28
+ console.log(pc.red(` ✗ ${summary.failed} failed`));
29
+ }
30
+ if (summary.skipped > 0) {
31
+ console.log(pc.yellow(` ○ ${summary.skipped} skipped`));
32
+ }
33
+ if (summary.pending > 0) {
34
+ console.log(pc.yellow(` ○ ${summary.pending} pending`));
35
+ }
36
+ console.log("");
37
+ console.log(pc.dim(` ${summary.tests} tests in ${formatDuration(duration)}`));
38
+ console.log("");
39
+ }
40
+ //# sourceMappingURL=summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.js","sourceRoot":"","sources":["../src/summary.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B;;GAEG;AACH,SAAS,cAAc,CAAC,EAAU;IAChC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;QACd,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC;IAC/B,CAAC;IACD,OAAO,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC;IAE9C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,MAAM,SAAS,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,OAAO,CAAC,MAAM,SAAS,CAAC,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,OAAO,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,OAAO,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,KAAK,aAAa,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * TeamCity Format Parser
3
+ *
4
+ * Parses PHPUnit's TeamCity output format for real-time test result streaming.
5
+ * TeamCity format is designed for streaming and emits structured messages
6
+ * as tests execute.
7
+ *
8
+ * Format reference:
9
+ * ##teamcity[testSuiteStarted name='SuiteName' ...]
10
+ * ##teamcity[testStarted name='testName' ...]
11
+ * ##teamcity[testFinished name='testName' duration='123']
12
+ * ##teamcity[testFailed name='testName' message='error' details='trace']
13
+ * ##teamcity[testIgnored name='testName' message='reason']
14
+ * ##teamcity[testSuiteFinished name='SuiteName']
15
+ */
16
+ import type { StreamEvent } from "./streaming.js";
17
+ import type { StreamingReporter } from "./streaming.js";
18
+ /**
19
+ * TeamCity stream parser that converts PHPUnit output to StreamEvents
20
+ */
21
+ export declare class TeamCityParser {
22
+ private buffer;
23
+ private currentSuite;
24
+ private testStartTimes;
25
+ private completedTests;
26
+ private reporter;
27
+ private onEvent;
28
+ /**
29
+ * Create a parser that sends events to a StreamingReporter
30
+ */
31
+ static withReporter(reporter: StreamingReporter): TeamCityParser;
32
+ /**
33
+ * Create a parser with a custom event handler
34
+ */
35
+ static withEventHandler(onEvent: (event: StreamEvent) => void): TeamCityParser;
36
+ /**
37
+ * Emit an event to the reporter or handler
38
+ */
39
+ private emit;
40
+ /**
41
+ * Process a chunk of data from PHPUnit output
42
+ * Handles partial lines by buffering
43
+ */
44
+ write(chunk: string): void;
45
+ /**
46
+ * Flush any remaining buffered data
47
+ */
48
+ flush(): void;
49
+ /**
50
+ * Process a single line of output
51
+ */
52
+ private processLine;
53
+ /**
54
+ * Handle a parsed TeamCity message
55
+ */
56
+ private handleMessage;
57
+ /**
58
+ * Reset the parser state
59
+ */
60
+ reset(): void;
61
+ }
62
+ /**
63
+ * Create a WritableStream that parses TeamCity format and emits events
64
+ */
65
+ export declare function createTeamCityParserStream(reporter: StreamingReporter): WritableStream<string>;
66
+ /**
67
+ * Parse TeamCity output and return events (for testing or one-shot parsing)
68
+ */
69
+ export declare function parseTeamCityOutput(output: string): StreamEvent[];
70
+ //# sourceMappingURL=teamcity-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teamcity-parser.d.ts","sourceRoot":"","sources":["../src/teamcity-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAwExD;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,OAAO,CAA+C;IAE9D;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,iBAAiB,GAAG,cAAc;IAMhE;;OAEG;IACH,MAAM,CAAC,gBAAgB,CACrB,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GACpC,cAAc;IAMjB;;OAEG;IACH,OAAO,CAAC,IAAI;IASZ;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAa1B;;OAEG;IACH,KAAK,IAAI,IAAI;IAOb;;OAEG;IACH,OAAO,CAAC,WAAW;IAcnB;;OAEG;IACH,OAAO,CAAC,aAAa;IA6FrB;;OAEG;IACH,KAAK,IAAI,IAAI;CAMd;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,iBAAiB,GAC1B,cAAc,CAAC,MAAM,CAAC,CAWxB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CAQjE"}
@@ -0,0 +1,256 @@
1
+ /**
2
+ * TeamCity Format Parser
3
+ *
4
+ * Parses PHPUnit's TeamCity output format for real-time test result streaming.
5
+ * TeamCity format is designed for streaming and emits structured messages
6
+ * as tests execute.
7
+ *
8
+ * Format reference:
9
+ * ##teamcity[testSuiteStarted name='SuiteName' ...]
10
+ * ##teamcity[testStarted name='testName' ...]
11
+ * ##teamcity[testFinished name='testName' duration='123']
12
+ * ##teamcity[testFailed name='testName' message='error' details='trace']
13
+ * ##teamcity[testIgnored name='testName' message='reason']
14
+ * ##teamcity[testSuiteFinished name='SuiteName']
15
+ */
16
+ /**
17
+ * Unescape TeamCity string values
18
+ * TeamCity uses special escaping for certain characters:
19
+ * - |' -> '
20
+ * - |n -> newline
21
+ * - |r -> carriage return
22
+ * - |[ -> [
23
+ * - |] -> ]
24
+ * - || -> |
25
+ */
26
+ function unescapeTeamCityValue(value) {
27
+ return value
28
+ .replace(/\|'/g, "'")
29
+ .replace(/\|n/g, "\n")
30
+ .replace(/\|r/g, "\r")
31
+ .replace(/\|\[/g, "[")
32
+ .replace(/\|\]/g, "]")
33
+ .replace(/\|\|/g, "|");
34
+ }
35
+ /**
36
+ * Parse a single TeamCity message line
37
+ */
38
+ function parseTeamCityLine(line) {
39
+ // Match the TeamCity format: ##teamcity[messageName attr1='value1' attr2='value2']
40
+ const match = line.match(/^##teamcity\[(\w+)\s*(.*)\]$/);
41
+ if (!match) {
42
+ return null;
43
+ }
44
+ const [, type, attributesStr] = match;
45
+ // Parse attributes (name='value' pairs)
46
+ // Values can contain escaped characters like |' (quote), |n (newline), || (pipe)
47
+ // The pattern matches: non-quote/non-pipe chars OR pipe followed by any char (or lookahead for quote)
48
+ // The lookahead (?=') handles edge case of pipes at end of value before closing quote
49
+ const attributes = {};
50
+ const attrRegex = /(\w+)='((?:[^'|]|\|(?:.|(?=')))*?)'/g;
51
+ let attrMatch;
52
+ while ((attrMatch = attrRegex.exec(attributesStr)) !== null) {
53
+ const [, key, value] = attrMatch;
54
+ attributes[key] = unescapeTeamCityValue(value);
55
+ }
56
+ return {
57
+ type: type,
58
+ attributes,
59
+ };
60
+ }
61
+ /**
62
+ * TeamCity stream parser that converts PHPUnit output to StreamEvents
63
+ */
64
+ export class TeamCityParser {
65
+ constructor() {
66
+ this.buffer = "";
67
+ this.currentSuite = null;
68
+ this.testStartTimes = new Map();
69
+ this.completedTests = new Set(); // Track tests that already reported result
70
+ this.reporter = null;
71
+ this.onEvent = null;
72
+ }
73
+ /**
74
+ * Create a parser that sends events to a StreamingReporter
75
+ */
76
+ static withReporter(reporter) {
77
+ const parser = new TeamCityParser();
78
+ parser.reporter = reporter;
79
+ return parser;
80
+ }
81
+ /**
82
+ * Create a parser with a custom event handler
83
+ */
84
+ static withEventHandler(onEvent) {
85
+ const parser = new TeamCityParser();
86
+ parser.onEvent = onEvent;
87
+ return parser;
88
+ }
89
+ /**
90
+ * Emit an event to the reporter or handler
91
+ */
92
+ emit(event) {
93
+ if (this.reporter) {
94
+ this.reporter.onEvent(event);
95
+ }
96
+ if (this.onEvent) {
97
+ this.onEvent(event);
98
+ }
99
+ }
100
+ /**
101
+ * Process a chunk of data from PHPUnit output
102
+ * Handles partial lines by buffering
103
+ */
104
+ write(chunk) {
105
+ this.buffer += chunk;
106
+ // Process complete lines
107
+ const lines = this.buffer.split("\n");
108
+ // Keep the last incomplete line in the buffer
109
+ this.buffer = lines.pop() || "";
110
+ for (const line of lines) {
111
+ this.processLine(line);
112
+ }
113
+ }
114
+ /**
115
+ * Flush any remaining buffered data
116
+ */
117
+ flush() {
118
+ if (this.buffer.trim()) {
119
+ this.processLine(this.buffer);
120
+ this.buffer = "";
121
+ }
122
+ }
123
+ /**
124
+ * Process a single line of output
125
+ */
126
+ processLine(line) {
127
+ const trimmed = line.trim();
128
+ if (!trimmed.startsWith("##teamcity[")) {
129
+ return;
130
+ }
131
+ const message = parseTeamCityLine(trimmed);
132
+ if (!message) {
133
+ return;
134
+ }
135
+ this.handleMessage(message);
136
+ }
137
+ /**
138
+ * Handle a parsed TeamCity message
139
+ */
140
+ handleMessage(message) {
141
+ const { type, attributes } = message;
142
+ const name = attributes.name || "Unknown";
143
+ switch (type) {
144
+ case "testSuiteStarted":
145
+ this.currentSuite = name;
146
+ this.emit({
147
+ type: "suite:start",
148
+ name,
149
+ });
150
+ break;
151
+ case "testSuiteFinished":
152
+ this.emit({
153
+ type: "suite:end",
154
+ name,
155
+ });
156
+ if (this.currentSuite === name) {
157
+ this.currentSuite = null;
158
+ }
159
+ break;
160
+ case "testStarted":
161
+ this.testStartTimes.set(name, Date.now());
162
+ this.emit({
163
+ type: "test:start",
164
+ name,
165
+ suiteName: this.currentSuite || undefined,
166
+ });
167
+ break;
168
+ case "testFinished": {
169
+ // In TeamCity format, testFailed/testIgnored is followed by testFinished
170
+ // Skip if we already reported this test as failed/skipped
171
+ if (this.completedTests.has(name)) {
172
+ this.completedTests.delete(name);
173
+ break;
174
+ }
175
+ const startTime = this.testStartTimes.get(name);
176
+ const duration = attributes.duration
177
+ ? parseInt(attributes.duration, 10)
178
+ : startTime
179
+ ? Date.now() - startTime
180
+ : 0;
181
+ this.testStartTimes.delete(name);
182
+ this.emit({
183
+ type: "test:pass",
184
+ name,
185
+ suiteName: this.currentSuite || undefined,
186
+ duration,
187
+ });
188
+ break;
189
+ }
190
+ case "testFailed": {
191
+ const startTime = this.testStartTimes.get(name);
192
+ const duration = attributes.duration
193
+ ? parseInt(attributes.duration, 10)
194
+ : startTime
195
+ ? Date.now() - startTime
196
+ : 0;
197
+ this.testStartTimes.delete(name);
198
+ this.completedTests.add(name); // Mark as completed to skip testFinished
199
+ this.emit({
200
+ type: "test:fail",
201
+ name,
202
+ suiteName: this.currentSuite || undefined,
203
+ duration,
204
+ message: attributes.message,
205
+ trace: attributes.details,
206
+ });
207
+ break;
208
+ }
209
+ case "testIgnored": {
210
+ this.testStartTimes.delete(name);
211
+ this.completedTests.add(name); // Mark as completed to skip testFinished
212
+ this.emit({
213
+ type: "test:skip",
214
+ name,
215
+ suiteName: this.currentSuite || undefined,
216
+ message: attributes.message,
217
+ });
218
+ break;
219
+ }
220
+ }
221
+ }
222
+ /**
223
+ * Reset the parser state
224
+ */
225
+ reset() {
226
+ this.buffer = "";
227
+ this.currentSuite = null;
228
+ this.testStartTimes.clear();
229
+ this.completedTests.clear();
230
+ }
231
+ }
232
+ /**
233
+ * Create a WritableStream that parses TeamCity format and emits events
234
+ */
235
+ export function createTeamCityParserStream(reporter) {
236
+ const parser = TeamCityParser.withReporter(reporter);
237
+ return new WritableStream({
238
+ write(chunk) {
239
+ parser.write(chunk);
240
+ },
241
+ close() {
242
+ parser.flush();
243
+ },
244
+ });
245
+ }
246
+ /**
247
+ * Parse TeamCity output and return events (for testing or one-shot parsing)
248
+ */
249
+ export function parseTeamCityOutput(output) {
250
+ const events = [];
251
+ const parser = TeamCityParser.withEventHandler((event) => events.push(event));
252
+ parser.write(output);
253
+ parser.flush();
254
+ return events;
255
+ }
256
+ //# sourceMappingURL=teamcity-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teamcity-parser.js","sourceRoot":"","sources":["../src/teamcity-parser.ts"],"names":[],"mappings":"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"}