faf-taf-git 2.4.0 → 2.4.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/README.md +1 -1
  3. package/RECEIPT-FORMAT.md +1 -1
  4. package/dist/cli.d.ts +5 -0
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +14 -1
  7. package/dist/cli.js.map +1 -1
  8. package/package.json +15 -2
  9. package/project.faf +32 -242
  10. package/.faf-dna +0 -95
  11. package/.fafignore +0 -41
  12. package/.github/dependabot.yml +0 -10
  13. package/.github/workflows/ci.yml +0 -53
  14. package/.github/workflows/claude-code-review.yml +0 -36
  15. package/.github/workflows/claude.yml +0 -39
  16. package/.github/workflows/homebrew.yml +0 -19
  17. package/.taf +0 -98
  18. package/CLAUDE.md +0 -38
  19. package/CODE_OF_CONDUCT.md +0 -21
  20. package/CONTRIBUTING.md +0 -289
  21. package/SECURITY.md +0 -19
  22. package/action.yml +0 -50
  23. package/agent.fafa +0 -53
  24. package/badge.svg +0 -21
  25. package/dist/bundle/index.js +0 -36554
  26. package/dist/index.js +0 -42827
  27. package/dist/index.js.map +0 -1
  28. package/dist/licenses.txt +0 -126
  29. package/dist/sourcemap-register.js +0 -1
  30. package/docs/RELEASE-NOTES.md +0 -200
  31. package/docs/USER-GUIDE.md +0 -301
  32. package/jest.config.js +0 -13
  33. package/src/badge.ts +0 -138
  34. package/src/cli.ts +0 -297
  35. package/src/index.ts +0 -201
  36. package/src/parsers/bun.ts +0 -103
  37. package/src/parsers/index.ts +0 -50
  38. package/src/parsers/jest.ts +0 -104
  39. package/src/parsers/junit.ts +0 -110
  40. package/src/parsers/vitest.ts +0 -65
  41. package/src/parsers/wjttc.ts +0 -160
  42. package/src/taf-core.ts +0 -139
  43. package/test-mcp.ts +0 -83
  44. package/test-standalone.ts +0 -87
  45. package/tests/action-bundle.test.ts +0 -34
  46. package/tests/action-parse-error.test.ts +0 -47
  47. package/tests/badge.test.ts +0 -260
  48. package/tests/bun-parser.test.ts +0 -173
  49. package/tests/jest-parser.test.ts +0 -122
  50. package/tests/junit-parser.test.ts +0 -109
  51. package/tests/vitest-parser.test.ts +0 -112
  52. package/tests/wjttc-parser.test.ts +0 -116
  53. package/tests/wjttc-taf.test.ts +0 -470
  54. package/tsconfig.json +0 -18
@@ -1,87 +0,0 @@
1
- /**
2
- * Standalone test script to simulate the action
3
- * Tests the action logic without GitHub Actions environment
4
- */
5
-
6
- import * as fs from 'fs';
7
- import * as path from 'path';
8
- import { parseJestOutput } from './src/parsers/jest';
9
- import { updateTafFile } from './src/taf-core';
10
-
11
- async function testAction() {
12
- console.log('Starting standalone action test...\n');
13
-
14
- // Simulate Jest output from faf-cli
15
- const jestOutput = `
16
- PASS src/commands/taf-init.test.ts
17
- PASS src/commands/taf-log.test.ts
18
- PASS src/commands/taf-validate.test.ts
19
- PASS src/commands/taf-stats.test.ts
20
-
21
- Test Suites: 18 passed, 18 total
22
- Tests: 173 passed, 173 total
23
- Snapshots: 0 total
24
- Time: 2.512 s
25
- Ran all test suites.
26
- `;
27
-
28
- console.log('1. Parsing Jest output...');
29
- const testResults = parseJestOutput(jestOutput);
30
-
31
- if (!testResults) {
32
- console.error('❌ Failed to parse Jest output');
33
- process.exit(1);
34
- }
35
-
36
- console.log('✅ Parsed successfully:');
37
- console.log(` - Total: ${testResults.total}`);
38
- console.log(` - Passed: ${testResults.passed}`);
39
- console.log(` - Failed: ${testResults.failed}`);
40
- console.log(` - Result: ${testResults.result}\n`);
41
-
42
- // Test with faf-cli .taf file
43
- const tafPath = path.join('/Users/wolfejam/FAF/cli', '.taf');
44
-
45
- console.log('2. Checking .taf file...');
46
- if (!fs.existsSync(tafPath)) {
47
- console.error('❌ No .taf file found at:', tafPath);
48
- process.exit(1);
49
- }
50
- console.log('✅ Found .taf file\n');
51
-
52
- console.log('3. Reading current .taf file...');
53
- const beforeContent = fs.readFileSync(tafPath, 'utf-8');
54
- const beforeLines = beforeContent.split('\n').filter(line => line.includes('timestamp')).length;
55
- console.log(`✅ Current test runs: ${beforeLines}\n`);
56
-
57
- console.log('4. Updating .taf file...');
58
- const updated = await updateTafFile(tafPath, testResults);
59
-
60
- if (!updated) {
61
- console.error('❌ Failed to update .taf file');
62
- process.exit(1);
63
- }
64
- console.log('✅ .taf file updated\n');
65
-
66
- console.log('5. Verifying update...');
67
- const afterContent = fs.readFileSync(tafPath, 'utf-8');
68
- const afterLines = afterContent.split('\n').filter(line => line.includes('timestamp')).length;
69
- console.log(`✅ Test runs after update: ${afterLines}`);
70
-
71
- if (afterLines !== beforeLines + 1) {
72
- console.error('❌ Expected one additional run');
73
- process.exit(1);
74
- }
75
-
76
- console.log('\n✅ Standalone test PASSED');
77
- console.log('\nThe action successfully:');
78
- console.log(' - Parsed Jest output (173/173 tests)');
79
- console.log(' - Updated .taf file');
80
- console.log(' - Added new test run entry');
81
- console.log(' - Maintained proper format');
82
- }
83
-
84
- testAction().catch(error => {
85
- console.error('❌ Test failed:', error);
86
- process.exit(1);
87
- });
@@ -1,34 +0,0 @@
1
- /**
2
- * The built Action bundle — dist/index.js, what action.yml runs — loads and
3
- * reaches the parse step under this Node (CI runs 24.x, the runtime
4
- * action.yml declares).
5
- *
6
- * An open-ended `overrides.undici: ">=6.28.0"` resolved undici 8 (Node >=
7
- * 22.19), which crashed the bundle at load on Node 20, before any input was
8
- * read. The src-level tests never executed the bundle, so nothing caught it.
9
- */
10
- import { spawnSync } from 'child_process';
11
- import * as fs from 'fs';
12
- import * as os from 'os';
13
- import * as path from 'path';
14
- import { UNPARSEABLE_OUTPUT } from '../src/parsers';
15
-
16
- describe('Action bundle (dist/index.js)', () => {
17
- it('loads and fails an unparseable test output with the parser list', () => {
18
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'taf-bundle-'));
19
- const file = path.join(dir, 'test-output.txt');
20
- fs.writeFileSync(file, 'no test summary here\n');
21
-
22
- // Actions pass inputs as INPUT_<NAME> env vars; stdout is captured here,
23
- // so the ::error:: line never reaches a real runner's log.
24
- const run = spawnSync(process.execPath, [path.join(__dirname, '..', 'dist', 'index.js')], {
25
- env: { ...process.env, 'INPUT_TEST-OUTPUT-FILE': file },
26
- encoding: 'utf-8',
27
- });
28
- fs.rmSync(dir, { recursive: true, force: true });
29
-
30
- expect(run.stderr).not.toMatch(/TypeError/);
31
- expect(run.stdout).toContain(`::error::${UNPARSEABLE_OUTPUT}`);
32
- expect(run.status).toBe(1);
33
- });
34
- });
@@ -1,47 +0,0 @@
1
- /**
2
- * The Action's parse error names every parser the dispatcher tries.
3
- *
4
- * It said "Supported: Jest, Vitest." while Bun (2.2.0) and WJTTC (2.3.0)
5
- * were already live. The CLI and the Action now build the message from the
6
- * same parser table that parseTestOutput walks.
7
- */
8
- import * as fs from 'fs';
9
- import * as os from 'os';
10
- import * as path from 'path';
11
-
12
- // Factory mocks: no real @actions/* module (or its undici network stack) loads.
13
- jest.mock('@actions/core', () => ({
14
- getInput: jest.fn(),
15
- info: jest.fn(),
16
- warning: jest.fn(),
17
- debug: jest.fn(),
18
- setOutput: jest.fn(),
19
- setFailed: jest.fn(),
20
- }));
21
- jest.mock('@actions/exec', () => ({ exec: jest.fn() }));
22
-
23
- import * as core from '@actions/core';
24
-
25
- describe('Action: unparseable test output', () => {
26
- it('fails with a message naming Bun, WJTTC, Jest and Vitest', () => {
27
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'taf-action-'));
28
- const file = path.join(dir, 'test-output.txt');
29
- fs.writeFileSync(file, 'no test summary here\n');
30
- (core.getInput as jest.Mock).mockImplementation((name: string) =>
31
- name === 'test-output-file' ? file : '',
32
- );
33
-
34
- // src/index.ts runs the Action on load; the parse failure is reached
35
- // before its first await, so setFailed has been called when this returns.
36
- jest.isolateModules(() => {
37
- require('../src/index');
38
- });
39
-
40
- const failures = (core.setFailed as jest.Mock).mock.calls.map((c) => String(c[0]));
41
- expect(failures).toHaveLength(1);
42
- for (const parser of ['Bun', 'WJTTC', 'Jest', 'Vitest']) {
43
- expect(failures[0]).toContain(parser);
44
- }
45
- fs.rmSync(dir, { recursive: true, force: true });
46
- });
47
- });
@@ -1,260 +0,0 @@
1
- /**
2
- * Badge Generator Tests
3
- *
4
- * Tests SVG output validity, .taf parsing, and edge cases.
5
- */
6
-
7
- import * as fs from 'fs';
8
- import * as path from 'path';
9
- import * as os from 'os';
10
- import { renderBadgeSvg, generateBadge } from '../src/badge';
11
-
12
- function createTempDir(prefix: string): string {
13
- return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
14
- }
15
-
16
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17
- // SVG RENDERING
18
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
19
-
20
- describe('SVG Rendering', () => {
21
- test('should produce valid SVG with label and value', () => {
22
- const svg = renderBadgeSvg('TAF', '10/10 passing', '#003366');
23
-
24
- expect(svg).toContain('<svg');
25
- expect(svg).toContain('</svg>');
26
- expect(svg).toContain('TAF');
27
- expect(svg).toContain('10/10 passing');
28
- });
29
-
30
- test('should include correct color', () => {
31
- const svg = renderBadgeSvg('TAF', '10/10 passing', '#003366');
32
-
33
- expect(svg).toContain('fill="#003366"');
34
- });
35
-
36
- test('should include accessibility attributes', () => {
37
- const svg = renderBadgeSvg('TAF', '5/5 passing', '#003366');
38
-
39
- expect(svg).toContain('role="img"');
40
- expect(svg).toContain('aria-label="TAF: 5/5 passing"');
41
- expect(svg).toContain('<title>TAF: 5/5 passing</title>');
42
- });
43
-
44
- test('should use Verdana font', () => {
45
- const svg = renderBadgeSvg('TAF', 'test', '#555');
46
-
47
- expect(svg).toContain('Verdana');
48
- });
49
-
50
- test('should have 20px height', () => {
51
- const svg = renderBadgeSvg('TAF', 'test', '#555');
52
-
53
- expect(svg).toContain('height="20"');
54
- });
55
-
56
- test('should have rounded corners via clipPath', () => {
57
- const svg = renderBadgeSvg('TAF', 'test', '#555');
58
-
59
- expect(svg).toContain('rx="3"');
60
- expect(svg).toContain('clip-path="url(#r)"');
61
- });
62
-
63
- test('should handle custom label', () => {
64
- const svg = renderBadgeSvg('tests', '99/100 passing', '#E32400');
65
-
66
- expect(svg).toContain('tests');
67
- expect(svg).toContain('99/100 passing');
68
- expect(svg).toContain('#E32400');
69
- });
70
- });
71
-
72
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
73
- // BADGE GENERATION FROM .taf
74
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
75
-
76
- describe('Badge Generation', () => {
77
- let tempDir: string;
78
-
79
- beforeEach(() => {
80
- tempDir = createTempDir('badge-test-');
81
- });
82
-
83
- afterEach(() => {
84
- fs.rmSync(tempDir, { recursive: true, force: true });
85
- });
86
-
87
- test('should generate navy badge when all tests pass', () => {
88
- const tafPath = path.join(tempDir, '.taf');
89
- fs.writeFileSync(tafPath, `format_version: 1.0.0
90
- project: test
91
- test_history:
92
- - timestamp: "2026-01-01T00:00:00Z"
93
- result: PASSED
94
- tests:
95
- total: 38
96
- passed: 38
97
- failed: 0
98
- `);
99
-
100
- const result = generateBadge({ tafPath });
101
-
102
- expect(result.success).toBe(true);
103
- expect(result.svg).toContain('38/38 passing');
104
- expect(result.svg).toContain('#003366');
105
- });
106
-
107
- test('should generate red badge when tests fail', () => {
108
- const tafPath = path.join(tempDir, '.taf');
109
- fs.writeFileSync(tafPath, `format_version: 1.0.0
110
- project: test
111
- test_history:
112
- - timestamp: "2026-01-01T00:00:00Z"
113
- result: FAILED
114
- tests:
115
- total: 38
116
- passed: 35
117
- failed: 3
118
- `);
119
-
120
- const result = generateBadge({ tafPath });
121
-
122
- expect(result.success).toBe(true);
123
- expect(result.svg).toContain('35/38 passing');
124
- expect(result.svg).toContain('#E32400');
125
- });
126
-
127
- test('should generate gray badge when .taf is missing', () => {
128
- const tafPath = path.join(tempDir, '.taf');
129
- // Don't create the file
130
-
131
- const result = generateBadge({ tafPath });
132
-
133
- expect(result.success).toBe(true);
134
- expect(result.svg).toContain('no data');
135
- expect(result.svg).toContain('#9F9F9F');
136
- });
137
-
138
- test('should generate gray badge when test_history is empty', () => {
139
- const tafPath = path.join(tempDir, '.taf');
140
- fs.writeFileSync(tafPath, `format_version: 1.0.0
141
- project: test
142
- test_history: []
143
- `);
144
-
145
- const result = generateBadge({ tafPath });
146
-
147
- expect(result.success).toBe(true);
148
- expect(result.svg).toContain('no data');
149
- expect(result.svg).toContain('#9F9F9F');
150
- });
151
-
152
- test('should use latest run from multiple entries', () => {
153
- const tafPath = path.join(tempDir, '.taf');
154
- fs.writeFileSync(tafPath, `format_version: 1.0.0
155
- project: test
156
- test_history:
157
- - timestamp: "2026-01-01T00:00:00Z"
158
- result: FAILED
159
- tests:
160
- total: 10
161
- passed: 7
162
- failed: 3
163
- - timestamp: "2026-01-02T00:00:00Z"
164
- result: PASSED
165
- tests:
166
- total: 10
167
- passed: 10
168
- failed: 0
169
- `);
170
-
171
- const result = generateBadge({ tafPath });
172
-
173
- expect(result.success).toBe(true);
174
- expect(result.svg).toContain('10/10 passing');
175
- expect(result.svg).toContain('#003366');
176
- });
177
-
178
- test('should use custom label', () => {
179
- const tafPath = path.join(tempDir, '.taf');
180
- fs.writeFileSync(tafPath, `format_version: 1.0.0
181
- project: test
182
- test_history:
183
- - timestamp: "2026-01-01T00:00:00Z"
184
- result: PASSED
185
- tests:
186
- total: 5
187
- passed: 5
188
- failed: 0
189
- `);
190
-
191
- const result = generateBadge({ tafPath, label: 'tests' });
192
-
193
- expect(result.success).toBe(true);
194
- expect(result.svg).toContain('tests');
195
- expect(result.svg).not.toContain('>TAF<');
196
- });
197
-
198
- test('should handle single test run', () => {
199
- const tafPath = path.join(tempDir, '.taf');
200
- fs.writeFileSync(tafPath, `format_version: 1.0.0
201
- project: test
202
- test_history:
203
- - timestamp: "2026-01-01T00:00:00Z"
204
- result: PASSED
205
- tests:
206
- total: 1
207
- passed: 1
208
- failed: 0
209
- `);
210
-
211
- const result = generateBadge({ tafPath });
212
-
213
- expect(result.success).toBe(true);
214
- expect(result.svg).toContain('1/1 passing');
215
- });
216
-
217
- test('should handle large test counts', () => {
218
- const tafPath = path.join(tempDir, '.taf');
219
- fs.writeFileSync(tafPath, `format_version: 1.0.0
220
- project: test
221
- test_history:
222
- - timestamp: "2026-01-01T00:00:00Z"
223
- result: PASSED
224
- tests:
225
- total: 10000
226
- passed: 10000
227
- failed: 0
228
- `);
229
-
230
- const result = generateBadge({ tafPath });
231
-
232
- expect(result.success).toBe(true);
233
- expect(result.svg).toContain('10000/10000 passing');
234
- });
235
-
236
- test('should handle invalid YAML gracefully', () => {
237
- const tafPath = path.join(tempDir, '.taf');
238
- fs.writeFileSync(tafPath, '{{{{ not yaml at all');
239
-
240
- const result = generateBadge({ tafPath });
241
-
242
- // Should either return no-data badge or error, but never throw
243
- expect(result).toBeDefined();
244
- });
245
-
246
- test('should handle .taf with missing tests field', () => {
247
- const tafPath = path.join(tempDir, '.taf');
248
- fs.writeFileSync(tafPath, `format_version: 1.0.0
249
- project: test
250
- test_history:
251
- - timestamp: "2026-01-01T00:00:00Z"
252
- result: PASSED
253
- `);
254
-
255
- const result = generateBadge({ tafPath });
256
-
257
- expect(result.success).toBe(true);
258
- expect(result.svg).toContain('no data');
259
- });
260
- });
@@ -1,173 +0,0 @@
1
- /**
2
- * Tests for Bun output parser
3
- */
4
-
5
- import { parseBunOutput } from '../src/parsers/bun';
6
-
7
- describe('Bun Parser', () => {
8
- describe('parseBunOutput', () => {
9
- it('should parse all passing tests (minimal output)', () => {
10
- const output = `
11
- 173 pass
12
- 0 fail
13
- 612 expect() calls
14
- Ran 173 tests across 8 files. [4.2s]
15
- `;
16
-
17
- const result = parseBunOutput(output);
18
-
19
- expect(result).not.toBeNull();
20
- expect(result?.total).toBe(173);
21
- expect(result?.passed).toBe(173);
22
- expect(result?.failed).toBe(0);
23
- expect(result?.skipped).toBeUndefined();
24
- expect(result?.result).toBe('PASSED');
25
- });
26
-
27
- it('should parse the grok-faf-mcp 1.4.9 shape (pass + skip + todo + fail=0)', () => {
28
- const output = `
29
- 232 pass
30
- 1 skip
31
- 1 todo
32
- 0 fail
33
- 848 expect() calls
34
- Ran 234 tests across 9 files. [21.53s]
35
- `;
36
-
37
- const result = parseBunOutput(output);
38
-
39
- expect(result).not.toBeNull();
40
- expect(result?.total).toBe(234);
41
- expect(result?.passed).toBe(232);
42
- expect(result?.failed).toBe(0);
43
- // skipped aggregates skip + todo (parity with vitest convention)
44
- expect(result?.skipped).toBe(2);
45
- expect(result?.result).toBe('PASSED');
46
- });
47
-
48
- it('should mark result FAILED when any tests fail', () => {
49
- const output = `
50
- 170 pass
51
- 3 fail
52
- 612 expect() calls
53
- Ran 173 tests across 8 files. [5.1s]
54
- `;
55
-
56
- const result = parseBunOutput(output);
57
-
58
- expect(result).not.toBeNull();
59
- expect(result?.total).toBe(173);
60
- expect(result?.passed).toBe(170);
61
- expect(result?.failed).toBe(3);
62
- expect(result?.result).toBe('FAILED');
63
- });
64
-
65
- it('should handle skip-only output', () => {
66
- const output = `
67
- 170 pass
68
- 3 skip
69
- 0 fail
70
- 612 expect() calls
71
- Ran 173 tests across 8 files. [5.0s]
72
- `;
73
-
74
- const result = parseBunOutput(output);
75
-
76
- expect(result?.total).toBe(173);
77
- expect(result?.passed).toBe(170);
78
- expect(result?.skipped).toBe(3);
79
- expect(result?.result).toBe('PASSED');
80
- });
81
-
82
- it('should strip ANSI color codes', () => {
83
- const output =
84
- '\x1b[32m 232 pass\x1b[0m\n' +
85
- '\x1b[33m 1 skip\x1b[0m\n' +
86
- '\x1b[33m 1 todo\x1b[0m\n' +
87
- '\x1b[31m 0 fail\x1b[0m\n' +
88
- ' 848 expect() calls\n' +
89
- 'Ran 234 tests across 9 files. [21.53s]\n';
90
-
91
- const result = parseBunOutput(output);
92
-
93
- expect(result?.total).toBe(234);
94
- expect(result?.passed).toBe(232);
95
- expect(result?.skipped).toBe(2);
96
- });
97
-
98
- it('should handle CRLF line endings', () => {
99
- const output =
100
- ' 173 pass\r\n 0 fail\r\nRan 173 tests across 8 files. [4.2s]\r\n';
101
-
102
- const result = parseBunOutput(output);
103
-
104
- expect(result?.total).toBe(173);
105
- expect(result?.passed).toBe(173);
106
- });
107
-
108
- it('should ignore an appended jest-shim "Tests:" line (legacy ci.yml conversion)', () => {
109
- // Some consumer CIs append a jest-shaped summary so the legacy jest
110
- // parser could read bun output. With bun support, the appended line
111
- // should be ignored — the canonical Ran-line wins.
112
- const output = `
113
- 232 pass
114
- 1 skip
115
- 1 todo
116
- 0 fail
117
- 848 expect() calls
118
- Ran 234 tests across 9 files. [21.53s]
119
- Tests: 232 passed, 232 total
120
- `;
121
-
122
- const result = parseBunOutput(output);
123
-
124
- // Truthful total from "Ran 234" — NOT 232 from the jest-shim line.
125
- expect(result?.total).toBe(234);
126
- expect(result?.passed).toBe(232);
127
- expect(result?.skipped).toBe(2);
128
- });
129
-
130
- it('should return null when no "Ran N tests" line is present (non-bun output)', () => {
131
- const output = `
132
- Test Suites: 18 passed, 18 total
133
- Tests: 173 passed, 173 total
134
- Snapshots: 0 total
135
- Time: 2.5s
136
- `;
137
-
138
- const result = parseBunOutput(output);
139
-
140
- // Defers to the next parser in the dispatcher chain.
141
- expect(result).toBeNull();
142
- });
143
-
144
- it('should return null on empty input', () => {
145
- expect(parseBunOutput('')).toBeNull();
146
- });
147
-
148
- it('should return null when "Ran 0 tests" (nothing actually ran)', () => {
149
- const output = `
150
- 0 pass
151
- 0 fail
152
- Ran 0 tests across 0 files. [0.1s]
153
- `;
154
-
155
- const result = parseBunOutput(output);
156
-
157
- expect(result).toBeNull();
158
- });
159
-
160
- it('should handle singular forms gracefully (1 test)', () => {
161
- const output = `
162
- 1 pass
163
- 0 fail
164
- Ran 1 test across 1 file. [0.5s]
165
- `;
166
-
167
- const result = parseBunOutput(output);
168
-
169
- expect(result?.total).toBe(1);
170
- expect(result?.passed).toBe(1);
171
- });
172
- });
173
- });