@7n/test 0.10.2 → 0.10.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.
- package/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/coverage-per-file.mjs +29 -0
- package/src/docs/coverage-per-file.md +3 -1
- package/src/docs/run.md +6 -5
- package/src/run.mjs +18 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.10.3] - 2026-07-03
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- COVERAGE.md тепер записується і при --no-mutation: береться per-file line coverage з останньої ітерації циклу тестування (без мутаційних даних) замість повного пропуску запису звіту.
|
|
8
|
+
|
|
3
9
|
## [0.10.2] - 2026-07-02
|
|
4
10
|
|
|
5
11
|
### Fixed
|
package/package.json
CHANGED
|
@@ -148,6 +148,35 @@ export function getUncoveredFiles(files, threshold = 80) {
|
|
|
148
148
|
return files.filter(f => f.pct < threshold)
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Рендерить per-file line coverage як Markdown-таблицю без мутаційних даних.
|
|
153
|
+
* Використовується для `COVERAGE.md`, коли мутаційне тестування пропущено
|
|
154
|
+
* (`--no-mutation`) і провайдери `.n-cursor.json#rules` не викликались.
|
|
155
|
+
* @param {Array<{file: string, pct: number, linesFound: number, linesCovered: number}>} files per-file coverage rows
|
|
156
|
+
* @returns {string} Markdown з заголовком `# Coverage`
|
|
157
|
+
*/
|
|
158
|
+
export function renderPerFileMarkdown(files) {
|
|
159
|
+
const sorted = files.toSorted((a, b) => a.pct - b.pct || a.file.localeCompare(b.file))
|
|
160
|
+
const lines = [
|
|
161
|
+
'# Coverage',
|
|
162
|
+
'',
|
|
163
|
+
'_Мутаційне тестування пропущено (`--no-mutation`) — лише per-file покриття рядків._',
|
|
164
|
+
'',
|
|
165
|
+
'| Файл | Рядки | % |',
|
|
166
|
+
'| --- | --- | --- |'
|
|
167
|
+
]
|
|
168
|
+
let coveredTotal = 0
|
|
169
|
+
let foundTotal = 0
|
|
170
|
+
for (const f of sorted) {
|
|
171
|
+
lines.push(`| ${f.file} | ${f.linesCovered}/${f.linesFound} | ${f.pct.toFixed(2)}% |`)
|
|
172
|
+
coveredTotal += f.linesCovered
|
|
173
|
+
foundTotal += f.linesFound
|
|
174
|
+
}
|
|
175
|
+
const totalPct = foundTotal === 0 ? 100 : (coveredTotal / foundTotal) * 100
|
|
176
|
+
lines.push(`| **Разом** | ${coveredTotal}/${foundTotal} | ${totalPct.toFixed(2)}% |`)
|
|
177
|
+
return `${lines.join('\n')}\n`
|
|
178
|
+
}
|
|
179
|
+
|
|
151
180
|
const SOURCE_EXT_RE = /\.(mjs|js|ts|vue|py)$/
|
|
152
181
|
const IGNORE_DIRS = new Set([
|
|
153
182
|
'node_modules',
|
|
@@ -3,7 +3,7 @@ type: JS Module
|
|
|
3
3
|
title: coverage-per-file.mjs
|
|
4
4
|
resource: npm/src/coverage-per-file.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: edc508cb
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
issues: judge:inaccurate:0.98
|
|
@@ -20,12 +20,14 @@ docgen:
|
|
|
20
20
|
measureCoveragePerFile запускає Vitest з генерацією покриття та звітністю у форматі JSON у єдиному проході, повертаючи дані про покриття по файлах та список тестів, що не пройшли. При цьому ігноруються папки та файли, такі як node_modules та .git, і винятки у процесі вимірювання обробляються, не викликаючи збоїв.
|
|
21
21
|
getUncoveredFiles відфільтровує файли, покриття яких нижче заданого порогового значення.
|
|
22
22
|
findSourceFiles рекурсивно знаходить усі файли вихідного коду в заданому каталозі, виключаючи тестові файли та директорії, визначені як ігнорувані.
|
|
23
|
+
renderPerFileMarkdown рендерить per-file покриття рядків (без мутаційних даних) як Markdown-таблицю з підсумковим рядком «Разом», відсортовану за зростанням відсотка покриття.
|
|
23
24
|
|
|
24
25
|
## Публічний API
|
|
25
26
|
|
|
26
27
|
- measureCoveragePerFile — Вимірює покриття коду за файлами, збираючи результати покриття Vitest та логуючи провалені тести за один прохід.
|
|
27
28
|
- getUncoveredFiles — Повертає список файлів, які не досягли встановленого порогу покриття.
|
|
28
29
|
- findSourceFiles — Рекурсивно шукає файли вихідного коду в директорії, ігноруючи тестові файли та проігноровані каталоги, використовуючи це для ініціалізації у разі відсутності даних про покриття.
|
|
30
|
+
- renderPerFileMarkdown — Форматує масив per-file coverage рядків у Markdown-документ `# Coverage` для запису в `COVERAGE.md`, коли мутаційне тестування пропущено.
|
|
29
31
|
|
|
30
32
|
## Гарантії поведінки
|
|
31
33
|
|
package/src/docs/run.md
CHANGED
|
@@ -3,7 +3,7 @@ type: JS Module
|
|
|
3
3
|
title: run.mjs
|
|
4
4
|
resource: npm/src/run.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: f71ad579
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
issues: judge:inaccurate:0.95
|
|
@@ -12,21 +12,22 @@ docgen:
|
|
|
12
12
|
|
|
13
13
|
## Огляд
|
|
14
14
|
|
|
15
|
-
Main auto-test loop забезпечує автоматизований цикл тестування з метою підвищення покриття коду. У першій фазі відбувається вимірювання покриття коду по файлах через vitest+lcov. Якщо покриття нижче заданого порогу, генеруються тести для відповідних файлів. Процес повторюється, доки покриття не досягне максимуму або не буде досягнуто обмежень ітерацій. Друга фаза включає мутаційне тестування, яке проводиться з використанням існуючих провайдерів покриття, залежність від .n-cursor.json. Після виявлення виживших мутантів, pi agent автоматично їх виправляє. Функція runAutoTest перехоплює можливі помилки, запобігаючи виняткам на виході, але не виконує кешування.
|
|
15
|
+
Main auto-test loop забезпечує автоматизований цикл тестування з метою підвищення покриття коду. У першій фазі відбувається вимірювання покриття коду по файлах через vitest+lcov. Якщо покриття нижче заданого порогу, генеруються тести для відповідних файлів. Процес повторюється, доки покриття не досягне максимуму або не буде досягнуто обмежень ітерацій. Друга фаза включає мутаційне тестування, яке проводиться з використанням існуючих провайдерів покриття, залежність від .n-cursor.json. Після виявлення виживших мутантів, pi agent автоматично їх виправляє. З опцією `noMutation` друга фаза повністю пропускається, а натомість `COVERAGE.md` записується з per-file покриттям, отриманим в останній ітерації першої фази. Функція runAutoTest перехоплює можливі помилки, запобігаючи виняткам на виході, але не виконує кешування.
|
|
16
16
|
|
|
17
17
|
## Поведінка
|
|
18
18
|
|
|
19
19
|
Поведінка
|
|
20
20
|
|
|
21
21
|
1. Викликається `runAutoTest` для запуску циклу автоматичного тестування.
|
|
22
|
-
2. У циклі до п'яти ітерацій вимірюється покриття кожної функції у
|
|
22
|
+
2. У циклі до п'яти ітерацій вимірюється покриття кожної функції у проекті; результат останньої ітерації зберігається для можливого запису в `COVERAGE.md`.
|
|
23
23
|
3. Якщо існують падіння тестів, вони автоматично виправляються через агент, і цикл продовжується.
|
|
24
24
|
4. Якщо немає тестів, відбувається запуск процесу Bootstrap: скануються вихідні файли, класифікуються, і для тих, що потребують тестів, генеруються початкові тести.
|
|
25
25
|
5. Обчислюється загальне покриття. Якщо воно досягло встановленого порогу, цикл зупиняється.
|
|
26
26
|
6. Якщо покриття не покращилося порівняно з попередньою ітерацією, цикл зупиняється.
|
|
27
27
|
7. Якщо покриття недостатнє, генеруються тести для некритичних файлів і цикл повторюється.
|
|
28
|
-
8.
|
|
29
|
-
9.
|
|
28
|
+
8. Якщо передано опцію `noMutation`, мутаційне тестування пропускається: `COVERAGE.md` записується з per-file покриттям останньої ітерації (без мутаційних даних), або лишається незаписаним, якщо даних покриття не отримано.
|
|
29
|
+
9. Інакше запускається мутаційне тестування, яке залежить від конфігурації в `.n-cursor.json`.
|
|
30
|
+
10. У разі успішного виживання мутантів, виконується повторний запуск покриття для перевірки.
|
|
30
31
|
|
|
31
32
|
## Гарантії поведінки
|
|
32
33
|
|
package/src/run.mjs
CHANGED
|
@@ -7,17 +7,24 @@
|
|
|
7
7
|
* 3. Bootstrap: when no tests exist, scan sources and quickClassify locally.
|
|
8
8
|
* 4. Repeat until coverage maxes out or no improvement.
|
|
9
9
|
*
|
|
10
|
-
* Phase 2
|
|
10
|
+
* Phase 2 (skipped entirely with --no-mutation, which instead writes
|
|
11
|
+
* COVERAGE.md from the last Phase 1 per-file measurement):
|
|
11
12
|
* 5. Mutation testing via existing coverage providers (requires .n-cursor.json#rules).
|
|
12
13
|
* 6. Auto-fix survived mutants via pi agent.
|
|
13
14
|
*/
|
|
14
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
measureCoveragePerFile,
|
|
17
|
+
getUncoveredFiles,
|
|
18
|
+
findSourceFiles,
|
|
19
|
+
renderPerFileMarkdown
|
|
20
|
+
} from './coverage-per-file.mjs'
|
|
15
21
|
import { quickClassify } from './assess-need.mjs'
|
|
16
22
|
import { generateTests } from './gen-tests.mjs'
|
|
17
23
|
import { fixFailingTests } from './fix-tests.mjs'
|
|
18
24
|
import { runCoverageSteps } from './coverage/coverage.mjs'
|
|
19
25
|
import { withLock } from './scripts/utils/with-lock.mjs'
|
|
20
26
|
import { readFileSync } from 'node:fs'
|
|
27
|
+
import { writeFile } from 'node:fs/promises'
|
|
21
28
|
import { join } from 'node:path'
|
|
22
29
|
|
|
23
30
|
const MAX_ITERATIONS = 5
|
|
@@ -25,18 +32,20 @@ const COVERAGE_THRESHOLD = 80
|
|
|
25
32
|
|
|
26
33
|
/**
|
|
27
34
|
* @param {string} dir absolute path to project root
|
|
28
|
-
* @param {{ noMutation?: boolean }} [opts]
|
|
35
|
+
* @param {{ noMutation?: boolean }} [opts] опції запуску
|
|
29
36
|
* @returns {Promise<number>} exit code
|
|
30
37
|
*/
|
|
31
38
|
export async function runAutoTest(dir, opts = {}) {
|
|
32
39
|
console.log(`\n📁 ${dir}\n`)
|
|
33
40
|
|
|
34
41
|
let prevUncoveredCount = Infinity
|
|
42
|
+
let lastFiles = []
|
|
35
43
|
|
|
36
44
|
for (let i = 1; i <= MAX_ITERATIONS; i++) {
|
|
37
45
|
console.log(`\n── Ітерація ${i}/${MAX_ITERATIONS}: coverage ──\n`)
|
|
38
46
|
|
|
39
47
|
const { files: allFiles, failingTests } = await measureCoveragePerFile(dir)
|
|
48
|
+
lastFiles = allFiles
|
|
40
49
|
|
|
41
50
|
if (failingTests.length > 0) {
|
|
42
51
|
console.log(`\n── ${failingTests.length} тестів падають — виправляю (pi agent) ──\n`)
|
|
@@ -107,6 +116,12 @@ export async function runAutoTest(dir, opts = {}) {
|
|
|
107
116
|
|
|
108
117
|
if (opts.noMutation) {
|
|
109
118
|
console.log('\n── Мутаційне тестування пропущено (--no-mutation) ──\n')
|
|
119
|
+
if (lastFiles.length > 0) {
|
|
120
|
+
await writeFile(join(dir, 'COVERAGE.md'), renderPerFileMarkdown(lastFiles), 'utf8')
|
|
121
|
+
console.log('✓ COVERAGE.md (тільки per-file покриття, без мутантів)')
|
|
122
|
+
} else {
|
|
123
|
+
console.log('⚠ Немає даних покриття — COVERAGE.md не записано.')
|
|
124
|
+
}
|
|
110
125
|
return 0
|
|
111
126
|
}
|
|
112
127
|
|