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.
- package/CHANGELOG.md +23 -1
- package/README.md +1 -1
- package/RECEIPT-FORMAT.md +1 -1
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -1
- package/dist/cli.js.map +1 -1
- package/package.json +15 -2
- package/project.faf +32 -242
- package/.faf-dna +0 -95
- package/.fafignore +0 -41
- package/.github/dependabot.yml +0 -10
- package/.github/workflows/ci.yml +0 -53
- package/.github/workflows/claude-code-review.yml +0 -36
- package/.github/workflows/claude.yml +0 -39
- package/.github/workflows/homebrew.yml +0 -19
- package/.taf +0 -98
- package/CLAUDE.md +0 -38
- package/CODE_OF_CONDUCT.md +0 -21
- package/CONTRIBUTING.md +0 -289
- package/SECURITY.md +0 -19
- package/action.yml +0 -50
- package/agent.fafa +0 -53
- package/badge.svg +0 -21
- package/dist/bundle/index.js +0 -36554
- package/dist/index.js +0 -42827
- package/dist/index.js.map +0 -1
- package/dist/licenses.txt +0 -126
- package/dist/sourcemap-register.js +0 -1
- package/docs/RELEASE-NOTES.md +0 -200
- package/docs/USER-GUIDE.md +0 -301
- package/jest.config.js +0 -13
- package/src/badge.ts +0 -138
- package/src/cli.ts +0 -297
- package/src/index.ts +0 -201
- package/src/parsers/bun.ts +0 -103
- package/src/parsers/index.ts +0 -50
- package/src/parsers/jest.ts +0 -104
- package/src/parsers/junit.ts +0 -110
- package/src/parsers/vitest.ts +0 -65
- package/src/parsers/wjttc.ts +0 -160
- package/src/taf-core.ts +0 -139
- package/test-mcp.ts +0 -83
- package/test-standalone.ts +0 -87
- package/tests/action-bundle.test.ts +0 -34
- package/tests/action-parse-error.test.ts +0 -47
- package/tests/badge.test.ts +0 -260
- package/tests/bun-parser.test.ts +0 -173
- package/tests/jest-parser.test.ts +0 -122
- package/tests/junit-parser.test.ts +0 -109
- package/tests/vitest-parser.test.ts +0 -112
- package/tests/wjttc-parser.test.ts +0 -116
- package/tests/wjttc-taf.test.ts +0 -470
- package/tsconfig.json +0 -18
package/test-standalone.ts
DELETED
|
@@ -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
|
-
});
|
package/tests/badge.test.ts
DELETED
|
@@ -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
|
-
});
|
package/tests/bun-parser.test.ts
DELETED
|
@@ -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
|
-
});
|