@7n/test 0.4.2 → 0.5.0
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 +13 -1
- package/package.json +2 -3
- package/src/coverage-per-file.mjs +49 -7
- package/src/fix-tests.mjs +5 -5
- package/src/run.mjs +9 -8
- package/types/index.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.0] - 2026-06-27
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- `measureCoveragePerFile` тепер повертає `{ files, failingTests }` — vitest coverage і JSON reporter запускаються в одному виклику. `run.mjs` перевіряє `failingTests` щоразу перед покриттям (не лише коли coverage порожня), автоматично викликає pi agent і повторює ітерацію при успішному виправленні.
|
|
8
|
+
|
|
9
|
+
## [0.4.3] - 2026-06-26
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Видалено зайвий бінарний аліас `7n-test` — залишено лише `test` для `npx @7n/test .`.
|
|
14
|
+
|
|
3
15
|
## [0.4.2] - 2026-06-26
|
|
4
16
|
|
|
5
17
|
### Changed
|
|
@@ -28,7 +40,7 @@
|
|
|
28
40
|
|
|
29
41
|
### Changed
|
|
30
42
|
|
|
31
|
-
- fix: unignore npm/src/coverage/ (was blocked by
|
|
43
|
+
- fix: unignore npm/src/coverage/ (was blocked by \*\*/coverage/ in .gitignore)
|
|
32
44
|
|
|
33
45
|
## [0.3.0] - 2026-06-26
|
|
34
46
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@7n/test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "CLI-утиліта @7n/test",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"7n",
|
|
@@ -18,8 +18,7 @@
|
|
|
18
18
|
"url": "git+https://github.com/nitra/7n-test.git"
|
|
19
19
|
},
|
|
20
20
|
"bin": {
|
|
21
|
-
"test": "bin/7n-test.js"
|
|
22
|
-
"7n-test": "bin/7n-test.js"
|
|
21
|
+
"test": "bin/7n-test.js"
|
|
23
22
|
},
|
|
24
23
|
"files": [
|
|
25
24
|
"bin",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-file coverage via vitest + lcov.
|
|
3
|
-
* Runs `bunx vitest run --coverage --coverage.reporter=lcov`
|
|
4
|
-
*
|
|
3
|
+
* Runs `bunx vitest run --coverage --coverage.reporter=lcov --reporter=json`
|
|
4
|
+
* in a single pass and returns both per-file coverage data and failing tests.
|
|
5
5
|
*/
|
|
6
6
|
import { spawnSync } from 'node:child_process'
|
|
7
7
|
import { existsSync, readFileSync } from 'node:fs'
|
|
@@ -11,6 +11,8 @@ import { join, relative } from 'node:path'
|
|
|
11
11
|
import { env } from 'node:process'
|
|
12
12
|
|
|
13
13
|
const TEST_FILE_RE = /\.(test|spec)\.[^.]+$|[/\\]tests?[/\\]/
|
|
14
|
+
const MAX_ERRORS_PER_FILE = 5
|
|
15
|
+
const MAX_ERROR_LINES = 10
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* @param {string} text lcov.info content
|
|
@@ -44,12 +46,43 @@ function parseLcovPerFile(text) {
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
/**
|
|
47
|
-
*
|
|
49
|
+
* @param {string} jsonPath path to vitest JSON results file
|
|
50
|
+
* @param {string} dir project root for relative paths
|
|
51
|
+
* @returns {Array<{file: string, errors: string[]}>}
|
|
52
|
+
*/
|
|
53
|
+
function parseFailingTests(jsonPath, dir) {
|
|
54
|
+
try {
|
|
55
|
+
const data = JSON.parse(readFileSync(jsonPath, 'utf8'))
|
|
56
|
+
return (data.testResults ?? [])
|
|
57
|
+
.filter(r => r.status === 'failed')
|
|
58
|
+
.map(r => ({
|
|
59
|
+
file: relative(dir, r.testFilePath),
|
|
60
|
+
errors: (r.assertionResults ?? [])
|
|
61
|
+
.filter(a => a.status === 'failed')
|
|
62
|
+
.slice(0, MAX_ERRORS_PER_FILE)
|
|
63
|
+
.map(a => {
|
|
64
|
+
const name = [...(a.ancestorTitles ?? []), a.title].join(' > ')
|
|
65
|
+
const msg = (a.failureMessages?.[0] ?? '').split('\n').slice(0, MAX_ERROR_LINES).join('\n')
|
|
66
|
+
return `${name}:\n${msg}`
|
|
67
|
+
})
|
|
68
|
+
}))
|
|
69
|
+
.filter(f => !f.file.startsWith('..') && f.errors.length > 0)
|
|
70
|
+
} catch {
|
|
71
|
+
return []
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Runs vitest coverage + JSON reporter in a single pass.
|
|
77
|
+
* Returns per-file coverage and any failing tests detected in the same run.
|
|
78
|
+
*
|
|
48
79
|
* @param {string} dir project root
|
|
49
|
-
* @returns {Promise<Array<{file: string, pct: number, linesFound: number, linesCovered: number}
|
|
80
|
+
* @returns {Promise<{files: Array<{file: string, pct: number, linesFound: number, linesCovered: number}>, failingTests: Array<{file: string, errors: string[]}>}>}
|
|
50
81
|
*/
|
|
51
82
|
export async function measureCoveragePerFile(dir) {
|
|
52
83
|
const lcovDir = await mkdtemp(join(tmpdir(), '7n-cov-'))
|
|
84
|
+
const jsonResultsFile = join(lcovDir, 'test-results.json')
|
|
85
|
+
|
|
53
86
|
try {
|
|
54
87
|
spawnSync(
|
|
55
88
|
'bunx',
|
|
@@ -59,16 +92,25 @@ export async function measureCoveragePerFile(dir) {
|
|
|
59
92
|
'--passWithNoTests',
|
|
60
93
|
'--coverage',
|
|
61
94
|
'--coverage.reporter=lcov',
|
|
62
|
-
`--coverage.reportsDirectory=${lcovDir}
|
|
95
|
+
`--coverage.reportsDirectory=${lcovDir}`,
|
|
96
|
+
'--reporter=verbose',
|
|
97
|
+
'--reporter=json',
|
|
98
|
+
`--outputFile=${jsonResultsFile}`
|
|
63
99
|
],
|
|
64
100
|
{ cwd: dir, stdio: 'inherit', env }
|
|
65
101
|
)
|
|
102
|
+
|
|
103
|
+
const failingTests = existsSync(jsonResultsFile) ? parseFailingTests(jsonResultsFile, dir) : []
|
|
104
|
+
|
|
66
105
|
const lcovPath = join(lcovDir, 'lcov.info')
|
|
67
|
-
if (!existsSync(lcovPath)) return []
|
|
106
|
+
if (!existsSync(lcovPath)) return { files: [], failingTests }
|
|
107
|
+
|
|
68
108
|
const allFiles = parseLcovPerFile(readFileSync(lcovPath, 'utf8'))
|
|
69
|
-
|
|
109
|
+
const files = allFiles
|
|
70
110
|
.map(f => ({ ...f, file: relative(dir, f.file) }))
|
|
71
111
|
.filter(f => !f.file.startsWith('..') && !TEST_FILE_RE.test(f.file))
|
|
112
|
+
|
|
113
|
+
return { files, failingTests }
|
|
72
114
|
} finally {
|
|
73
115
|
await rm(lcovDir, { recursive: true, force: true }).catch(() => {})
|
|
74
116
|
}
|
package/src/fix-tests.mjs
CHANGED
|
@@ -28,11 +28,11 @@ export async function getFailingTests(dir) {
|
|
|
28
28
|
const outputFile = join(tmpDir, 'results.json')
|
|
29
29
|
|
|
30
30
|
try {
|
|
31
|
-
spawnSync(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
)
|
|
31
|
+
spawnSync('bunx', ['vitest', 'run', '--reporter=json', `--outputFile=${outputFile}`, '--passWithNoTests'], {
|
|
32
|
+
cwd: dir,
|
|
33
|
+
stdio: 'inherit',
|
|
34
|
+
env
|
|
35
|
+
})
|
|
36
36
|
|
|
37
37
|
if (!existsSync(outputFile)) return []
|
|
38
38
|
|
package/src/run.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import { measureCoveragePerFile, getUncoveredFiles } from './coverage-per-file.mjs'
|
|
15
15
|
import { assessNeed } from './assess-need.mjs'
|
|
16
16
|
import { generateTests } from './gen-tests.mjs'
|
|
17
|
-
import { fixFailingTests
|
|
17
|
+
import { fixFailingTests } from './fix-tests.mjs'
|
|
18
18
|
import { runCoverageSteps } from './coverage/coverage.mjs'
|
|
19
19
|
import { withLock } from './scripts/utils/with-lock.mjs'
|
|
20
20
|
|
|
@@ -34,14 +34,15 @@ export async function runAutoTest(dir, opts = {}) {
|
|
|
34
34
|
for (let i = 1; i <= MAX_ITERATIONS; i++) {
|
|
35
35
|
console.log(`\n── Ітерація ${i}/${MAX_ITERATIONS}: coverage ──\n`)
|
|
36
36
|
|
|
37
|
-
const allFiles = await measureCoveragePerFile(dir)
|
|
37
|
+
const { files: allFiles, failingTests } = await measureCoveragePerFile(dir)
|
|
38
|
+
|
|
39
|
+
if (failingTests.length > 0) {
|
|
40
|
+
console.log(`\n── ${failingTests.length} тестів падають — виправляю (pi agent) ──\n`)
|
|
41
|
+
const { remaining } = await fixFailingTests(dir, { failures: failingTests })
|
|
42
|
+
if (remaining === 0) continue
|
|
43
|
+
}
|
|
44
|
+
|
|
38
45
|
if (allFiles.length === 0) {
|
|
39
|
-
const failures = await getFailingTests(dir)
|
|
40
|
-
if (failures.length > 0) {
|
|
41
|
-
console.log(`\n── ${failures.length} тестів падають — виправляю (pi agent) ──\n`)
|
|
42
|
-
const { remaining } = await fixFailingTests(dir, { failures })
|
|
43
|
-
if (remaining === 0) continue
|
|
44
|
-
}
|
|
45
46
|
console.log('⚠ Vitest coverage не повернула даних — перевір налаштування vitest.')
|
|
46
47
|
break
|
|
47
48
|
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|