@boyingliu01/xp-gate 0.12.10 → 0.12.12
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/adapters/typescript.sh +87 -10
- package/gate-3.sh +1 -1
- package/hooks/adapter-common.sh +13 -18
- package/hooks/pre-commit +166 -27
- package/hooks/pre-push +231 -18
- package/lib/__tests__/sprint-discovery.test.ts +5 -4
- package/lib/__tests__/sprint-status.test.ts +5 -4
- package/lib/__tests__/tsconfig.json +8 -0
- package/lib/init.js +24 -0
- package/mock-policy/AGENTS.md +3 -3
- package/mutation/AGENTS.md +3 -3
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +2 -2
- package/principles/AGENTS.md +3 -3
- package/principles/__tests__/baseline-storage.test.ts +3 -2
- package/principles/__tests__/baseline.test.ts +16 -14
- package/principles/__tests__/types.test.ts +1 -0
- package/principles/adapters/__tests__/base.test.ts +8 -8
- package/principles/adapters/__tests__/cpp.test.ts +26 -26
- package/principles/adapters/__tests__/dart.test.ts +11 -11
- package/principles/adapters/__tests__/go.test.ts +9 -9
- package/principles/adapters/__tests__/java.test.ts +9 -9
- package/principles/adapters/__tests__/kotlin.test.ts +10 -10
- package/principles/adapters/__tests__/objectivec.test.ts +27 -27
- package/principles/adapters/__tests__/python.test.ts +11 -11
- package/principles/adapters/__tests__/swift.test.ts +9 -9
- package/principles/adapters/__tests__/typescript.test.ts +13 -13
- package/principles/rules/__tests__/clean-code/large-file.test.ts +4 -3
- package/skills/delphi-review/AGENTS.md +3 -3
- package/skills/sprint-flow/AGENTS.md +3 -3
- package/skills/test-specification-alignment/AGENTS.md +3 -3
package/adapters/typescript.sh
CHANGED
|
@@ -3,25 +3,102 @@
|
|
|
3
3
|
# TypeScript adapter for quality gates
|
|
4
4
|
|
|
5
5
|
run_static_analysis() {
|
|
6
|
-
if command -v npx >/dev/null 2>&1; then
|
|
7
|
-
echo "Running TypeScript static analysis..."
|
|
8
|
-
npx tsc --noEmit
|
|
9
|
-
return $?
|
|
10
|
-
else
|
|
6
|
+
if ! command -v npx >/dev/null 2>&1; then
|
|
11
7
|
echo "npx not available, skipping TypeScript static analysis"
|
|
12
8
|
return 0
|
|
13
9
|
fi
|
|
10
|
+
|
|
11
|
+
local HAS_ERRORS=0
|
|
12
|
+
local TSC_VERSION
|
|
13
|
+
|
|
14
|
+
echo "Running TypeScript static analysis..."
|
|
15
|
+
npx tsc --noEmit
|
|
16
|
+
TSC_EXIT=$?
|
|
17
|
+
if [ $TSC_EXIT -ne 0 ]; then
|
|
18
|
+
HAS_ERRORS=1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Also check test files if tsconfig.json exists (see Issue #293).
|
|
22
|
+
# Many projects exclude __tests__/ from tsconfig.json, so test file type
|
|
23
|
+
# errors silently accumulate. We detect this and run a second tsc pass.
|
|
24
|
+
local HAS_TESTS=false
|
|
25
|
+
if [ -d "src/__tests__" ] || [ -d "src/tests" ] || [ -d "tests" ] || [ -d "__tests__" ]; then
|
|
26
|
+
HAS_TESTS=true
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
if [ "$HAS_TESTS" = true ] && [ -f "tsconfig.json" ]; then
|
|
30
|
+
# Check if tsconfig.json already includes test files
|
|
31
|
+
if npx tsc --noEmit --listFiles 2>/dev/null | grep -qE '__tests__/|\.test\.ts|\.spec\.ts'; then
|
|
32
|
+
: # Test files already checked — no additional pass needed
|
|
33
|
+
elif [ -f "tsconfig.tests.json" ]; then
|
|
34
|
+
echo "Checking test files with tsconfig.tests.json..."
|
|
35
|
+
npx tsc --noEmit --project tsconfig.tests.json
|
|
36
|
+
TSC_TEST_EXIT=$?
|
|
37
|
+
if [ $TSC_TEST_EXIT -ne 0 ]; then
|
|
38
|
+
HAS_ERRORS=1
|
|
39
|
+
fi
|
|
40
|
+
else
|
|
41
|
+
# Generate a temp tsconfig that includes test files for the additional pass
|
|
42
|
+
local TSC_TEST_EXIT=0
|
|
43
|
+
echo "Checking test files for type errors (generating temp tsconfig)..."
|
|
44
|
+
npx tsc --noEmit 2>/dev/null
|
|
45
|
+
# Create temp tsconfig extending the original but with test files included
|
|
46
|
+
local TEMP_TSCONFIG=".tsconfig.withtests.json"
|
|
47
|
+
if [ ! -f "$TEMP_TSCONFIG" ]; then
|
|
48
|
+
node -e "
|
|
49
|
+
const cfg = JSON.parse(require('fs').readFileSync('tsconfig.json','utf8'));
|
|
50
|
+
delete cfg.exclude;
|
|
51
|
+
cfg.include = cfg.include || ['src/**/*'];
|
|
52
|
+
cfg.include.push('src/**/__tests__/**','src/**/*.test.ts','src/**/*.spec.ts');
|
|
53
|
+
require('fs').writeFileSync('$TEMP_TSCONFIG', JSON.stringify(cfg, null, 2));
|
|
54
|
+
" 2>/dev/null
|
|
55
|
+
npx tsc --noEmit --project "$TEMP_TSCONFIG"
|
|
56
|
+
TSC_TEST_EXIT=$?
|
|
57
|
+
rm -f "$TEMP_TSCONFIG"
|
|
58
|
+
fi
|
|
59
|
+
if [ $TSC_TEST_EXIT -ne 0 ]; then
|
|
60
|
+
echo ""
|
|
61
|
+
echo "❌ BLOCKED - TYPE ERRORS in test files"
|
|
62
|
+
echo "Your project's tsconfig.json excludes test files from type checking."
|
|
63
|
+
echo "Fix the type errors above or add a tsconfig.tests.json to customise test file checking."
|
|
64
|
+
HAS_ERRORS=1
|
|
65
|
+
fi
|
|
66
|
+
fi
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
return $HAS_ERRORS
|
|
14
70
|
}
|
|
15
71
|
|
|
16
72
|
run_lint() {
|
|
17
|
-
if command -v npx >/dev/null 2>&1; then
|
|
18
|
-
echo "Running TypeScript linting..."
|
|
19
|
-
npx eslint . --ext .ts,.tsx
|
|
20
|
-
return $?
|
|
21
|
-
else
|
|
73
|
+
if ! command -v npx >/dev/null 2>&1; then
|
|
22
74
|
echo "npx not available, skipping TypeScript linting"
|
|
23
75
|
return 0
|
|
24
76
|
fi
|
|
77
|
+
|
|
78
|
+
# Prefer Biome if biome.json/biome.jsonc exists (covers lint + format in one pass)
|
|
79
|
+
if [ -f "biome.json" ] || [ -f "biome.jsonc" ]; then
|
|
80
|
+
echo "Running Biome linting..."
|
|
81
|
+
npx biome check . --no-errors-on-unmatched 2>&1 | head -50
|
|
82
|
+
local EXIT_CODE=$?
|
|
83
|
+
if [ $EXIT_CODE -ne 0 ]; then
|
|
84
|
+
echo ""
|
|
85
|
+
echo "❌ Biome check failed"
|
|
86
|
+
echo "Run 'npx biome check --write .' to auto-fix."
|
|
87
|
+
return $EXIT_CODE
|
|
88
|
+
fi
|
|
89
|
+
echo "✅ PASSED - Biome check."
|
|
90
|
+
return 0
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Fall back to ESLint if no Biome config exists
|
|
94
|
+
if [ -f ".eslintrc.json" ] || [ -f ".eslintrc.js" ] || [ -f ".eslintrc.cjs" ] || [ -f "eslint.config.js" ]; then
|
|
95
|
+
echo "Running ESLint linting..."
|
|
96
|
+
npx eslint . --ext .ts,.tsx
|
|
97
|
+
return $?
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
echo "ℹ️ No linter config found (biome.json or eslint config). Skipping lint."
|
|
101
|
+
return 0
|
|
25
102
|
}
|
|
26
103
|
|
|
27
104
|
run_tests() {
|
package/gate-3.sh
CHANGED
|
@@ -30,7 +30,7 @@ else
|
|
|
30
30
|
if [ -n "$LIZARD_CMD" ]; then
|
|
31
31
|
LIZARD_PATH=$(eval echo "$LIZARD_CMD")
|
|
32
32
|
|
|
33
|
-
# Get changed source files for complexity check
|
|
33
|
+
# Get changed source files for complexity check (includes test files)
|
|
34
34
|
CC_FILES=$(echo "$CHANGED_FILES" | grep -E '\.(ts|tsx|js|jsx|py|go|java|swift|cpp|c|hpp|h|m|mm|kt)$' || true)
|
|
35
35
|
|
|
36
36
|
if [ -n "$CC_FILES" ]; then
|
package/hooks/adapter-common.sh
CHANGED
|
@@ -192,26 +192,21 @@ detect_iac_project() {
|
|
|
192
192
|
|
|
193
193
|
# Stryker 9.x config files (new format, takes priority)
|
|
194
194
|
detect_mutation_testable() {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if command -v npx >/dev/null 2>&1 && npx --no-install stryker --version >/dev/null 2>&1; then
|
|
201
|
-
return 0
|
|
202
|
-
fi
|
|
195
|
+
# Check if @stryker-mutator/core is installed — no config file required.
|
|
196
|
+
# The StrykerRunner passes --mutate via CLI flags, so stryker can run
|
|
197
|
+
# without any config file present.
|
|
198
|
+
if [ -f "package.json" ] && grep -qE '"@stryker-mutator[^"]*"' package.json 2>/dev/null; then
|
|
199
|
+
return 0
|
|
203
200
|
fi
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
fi
|
|
201
|
+
if command -v npx >/dev/null 2>&1 && npx --no-install stryker --version >/dev/null 2>&1; then
|
|
202
|
+
return 0
|
|
203
|
+
fi
|
|
204
|
+
# Legacy config files (backwards compatibility — deprecated)
|
|
205
|
+
if [ -f "stryker.conf.json" ] || [ -f "stryker.prepush.conf.json" ] || \
|
|
206
|
+
[ -f "stryker.config.mjs" ] || [ -f "stryker.config.js" ] || \
|
|
207
|
+
[ -f "stryker.config.cjs" ] || [ -f "stryker.config.json" ]; then
|
|
208
|
+
return 0
|
|
213
209
|
fi
|
|
214
|
-
|
|
215
210
|
return 1
|
|
216
211
|
}
|
|
217
212
|
# Detect if a Python project has mutmut installed and configured
|
package/hooks/pre-commit
CHANGED
|
@@ -608,6 +608,44 @@ if [ "$PROJECT_LANG" = "documentation-only" ]; then
|
|
|
608
608
|
exit 1
|
|
609
609
|
fi
|
|
610
610
|
echo "✅ PASSED - TypeScript static analysis."
|
|
611
|
+
|
|
612
|
+
# Also check test files if they exist (Issue #293)
|
|
613
|
+
# Many projects exclude __tests__/ from tsconfig.json, so test file type
|
|
614
|
+
# errors silently accumulate.
|
|
615
|
+
if [ -d "src/__tests__" ] || [ -d "src/tests" ] || [ -d "tests" ] || [ -d "__tests__" ]; then
|
|
616
|
+
HAS_TESTS_IN_TSC=false
|
|
617
|
+
if npx tsc --noEmit --skipLibCheck --listFiles 2>/dev/null | grep -qE '__tests__/|\.test\.ts|\.spec\.ts'; then
|
|
618
|
+
HAS_TESTS_IN_TSC=true
|
|
619
|
+
fi
|
|
620
|
+
if [ "$HAS_TESTS_IN_TSC" = false ]; then
|
|
621
|
+
TSC_TEST_EXIT=0
|
|
622
|
+
if [ -f "tsconfig.tests.json" ]; then
|
|
623
|
+
echo "Checking test files with tsconfig.tests.json..."
|
|
624
|
+
npx tsc --noEmit --project tsconfig.tests.json 2>&1 | head -30
|
|
625
|
+
TSC_TEST_EXIT=$?
|
|
626
|
+
else
|
|
627
|
+
echo "Checking test files for type errors..."
|
|
628
|
+
TEMP_TSCONFIG=".tsconfig.withtests.json"
|
|
629
|
+
node -e "
|
|
630
|
+
const cfg = JSON.parse(require('fs').readFileSync('tsconfig.json','utf8'));
|
|
631
|
+
delete cfg.exclude;
|
|
632
|
+
cfg.include = cfg.include || ['src/**/*'];
|
|
633
|
+
cfg.include.push('src/**/__tests__/**','src/**/*.test.ts','src/**/*.spec.ts');
|
|
634
|
+
require('fs').writeFileSync('$TEMP_TSCONFIG', JSON.stringify(cfg, null, 2));
|
|
635
|
+
" 2>/dev/null
|
|
636
|
+
npx tsc --noEmit --project "$TEMP_TSCONFIG" --skipLibCheck 2>&1 | head -30
|
|
637
|
+
TSC_TEST_EXIT=$?
|
|
638
|
+
rm -f "$TEMP_TSCONFIG"
|
|
639
|
+
fi
|
|
640
|
+
if [ $TSC_TEST_EXIT -ne 0 ]; then
|
|
641
|
+
echo ""
|
|
642
|
+
echo "❌ BLOCKED - TYPE ERRORS in test files"
|
|
643
|
+
echo "Your project's tsconfig.json excludes test files from type checking."
|
|
644
|
+
echo "Add a tsconfig.tests.json to customise test file checking."
|
|
645
|
+
exit 1
|
|
646
|
+
fi
|
|
647
|
+
fi
|
|
648
|
+
fi
|
|
611
649
|
else
|
|
612
650
|
echo "ℹ️ SKIP - tsconfig.json not found (no TypeScript project config)"
|
|
613
651
|
fi
|
|
@@ -679,8 +717,20 @@ if [ "$PROJECT_LANG" = "documentation-only" ]; then
|
|
|
679
717
|
fi
|
|
680
718
|
fi
|
|
681
719
|
|
|
682
|
-
# ── Biome
|
|
683
|
-
if
|
|
720
|
+
# ── Biome lint/format check ──
|
|
721
|
+
if [ -f "biome.json" ] || [ -f "biome.jsonc" ]; then
|
|
722
|
+
echo "Running Biome lint/format check..."
|
|
723
|
+
if ! npx biome check --staged . 2>/dev/null; then
|
|
724
|
+
npx biome check . 2>&1 | head -50
|
|
725
|
+
echo ""
|
|
726
|
+
echo "❌ BLOCKED - Biome check failed"
|
|
727
|
+
echo "Fix the errors above before committing."
|
|
728
|
+
echo "Tip: Run 'npx biome check --write .' to auto-fix formatting and safe lint issues."
|
|
729
|
+
echo " Run 'npx biome check --write --unsafe .' to also fix unsafe lint issues."
|
|
730
|
+
exit 1
|
|
731
|
+
fi
|
|
732
|
+
echo "✅ PASSED - Biome check."
|
|
733
|
+
else
|
|
684
734
|
if [ -f "package.json" ] && command -v node &> /dev/null; then
|
|
685
735
|
HAS_BIOME=$(node -e "try{const p=JSON.parse(require('fs').readFileSync('package.json','utf8'));const d=p.devDependencies||{};const deps=p.dependencies||{};console.log((d['@biomejs/biome']||deps['@biomejs/biome'])?'yes':'no')}catch{console.log('no')}") && true
|
|
686
736
|
if [ "$HAS_BIOME" = "yes" ]; then
|
|
@@ -1221,13 +1271,6 @@ else
|
|
|
1221
1271
|
fi
|
|
1222
1272
|
done
|
|
1223
1273
|
|
|
1224
|
-
if [ "$TEST_FOUND" = false ] && [ "$ext" = "py" ]; then
|
|
1225
|
-
module_name="${filename//-/_}"
|
|
1226
|
-
if grep -rq "import $module_name\|from $module_name import" tests/ 2>/dev/null; then
|
|
1227
|
-
TEST_FOUND=true
|
|
1228
|
-
fi
|
|
1229
|
-
fi
|
|
1230
|
-
|
|
1231
1274
|
if [ "$TEST_FOUND" = false ]; then
|
|
1232
1275
|
echo "⚠️ TEST PAIRING WARNING: Source file without corresponding test: $src_file"
|
|
1233
1276
|
echo " Expected patterns for .${ext}: ${patterns[0]}, ${patterns[1]:-...}"
|
|
@@ -1299,28 +1342,117 @@ else
|
|
|
1299
1342
|
done
|
|
1300
1343
|
fi
|
|
1301
1344
|
|
|
1302
|
-
#
|
|
1303
|
-
#
|
|
1304
|
-
#
|
|
1345
|
+
# ============================================================================
|
|
1346
|
+
# Smart test selection (Issue #286): only run tests affected by changed files.
|
|
1347
|
+
# Reuses CHANGED_TEST_FILES already computed in Gate 5b (line 1244).
|
|
1348
|
+
# Full run fallback: >20 changed test files, or vitest not found.
|
|
1349
|
+
# ============================================================================
|
|
1305
1350
|
echo "Running tests..."
|
|
1306
1351
|
|
|
1307
1352
|
# Route to appropriate test runner via adapter
|
|
1308
1353
|
ADAPTER_FILE=$(resolve_adapter_path "$PROJECT_LANG")
|
|
1309
1354
|
if [ "${PROJECT_LANG}" != "unknown" ] && [ -n "$ADAPTER_FILE" ]; then
|
|
1310
1355
|
if source "$ADAPTER_FILE" 2>/dev/null; then
|
|
1311
|
-
# TypeScript:
|
|
1356
|
+
# TypeScript: vitest with smart test selection
|
|
1312
1357
|
if [ "$PROJECT_LANG" = "typescript" ] && [ -f "package.json" ]; then
|
|
1313
1358
|
if npx vitest --version >/dev/null 2>&1; then
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1359
|
+
# Count changed test files to decide strategy
|
|
1360
|
+
CHANGED_TEST_FILE_COUNT=$(echo "$CHANGED_TEST_FILES" | grep -c '.' || echo "0")
|
|
1361
|
+
|
|
1362
|
+
# Also get changed source files (non-test) for related-test lookup
|
|
1363
|
+
CHANGED_SRC_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(ts|tsx|js|jsx)$' | grep -v '\.test\.' | grep -v '\.spec\.' | grep -v '__tests__' | grep -v '\.d\.ts$' | grep -v '__snapshots__' || true)
|
|
1364
|
+
CHANGED_SRC_COUNT=$(echo "$CHANGED_SRC_FILES" | grep -c '.' || echo "0")
|
|
1365
|
+
|
|
1366
|
+
# Decide strategy
|
|
1367
|
+
if [ "$CHANGED_TEST_FILE_COUNT" -gt 20 ] 2>/dev/null; then
|
|
1368
|
+
# Too many test files changed → full run
|
|
1369
|
+
echo "Too many test files changed ($CHANGED_TEST_FILE_COUNT), running full test suite..."
|
|
1370
|
+
TESTS_OUTPUT=$(npx vitest run --coverage 2>&1)
|
|
1371
|
+
TESTS_EXIT_CODE=$?
|
|
1372
|
+
echo "$TESTS_OUTPUT" | head -60
|
|
1373
|
+
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
1374
|
+
echo ""
|
|
1375
|
+
echo "❌ BLOCKED - Tests FAILED"
|
|
1376
|
+
exit 1
|
|
1377
|
+
else
|
|
1378
|
+
echo "✅ PASSED - Full test suite passed (with coverage)."
|
|
1379
|
+
fi
|
|
1380
|
+
elif [ "$CHANGED_TEST_FILE_COUNT" -gt 0 ] 2>/dev/null; then
|
|
1381
|
+
# Run only changed test files with coverage
|
|
1382
|
+
SMART_TARGETS=$(echo "$CHANGED_TEST_FILES" | tr '\n' ' ')
|
|
1383
|
+
echo "Running ${CHANGED_TEST_FILE_COUNT} changed test file(s) with coverage..."
|
|
1384
|
+
echo " Target(s): $(echo "$CHANGED_TEST_FILES" | tr '\n' ' ')"
|
|
1385
|
+
TESTS_OUTPUT=$(npx vitest run --coverage $SMART_TARGETS 2>&1)
|
|
1386
|
+
TESTS_EXIT_CODE=$?
|
|
1387
|
+
echo "$TESTS_OUTPUT" | head -60
|
|
1388
|
+
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
1389
|
+
echo ""
|
|
1390
|
+
echo "❌ BLOCKED - Tests FAILED in changed test files"
|
|
1391
|
+
exit 1
|
|
1392
|
+
else
|
|
1393
|
+
echo "✅ PASSED - Changed test files passed (with coverage)."
|
|
1394
|
+
fi
|
|
1395
|
+
elif [ "$CHANGED_SRC_COUNT" -gt 0 ] 2>/dev/null; then
|
|
1396
|
+
# Source files changed but no test files → find related tests or run full
|
|
1397
|
+
echo "Source file(s) changed, finding related tests..."
|
|
1398
|
+
RELATED_TESTS=""
|
|
1399
|
+
for src_file in $CHANGED_SRC_FILES; do
|
|
1400
|
+
base="${src_file%.*}"
|
|
1401
|
+
filename="${base##*/}"
|
|
1402
|
+
dir="$(dirname "$src_file")"
|
|
1403
|
+
ext="${src_file##*.}"
|
|
1404
|
+
# Try common test file patterns
|
|
1405
|
+
for pattern in \
|
|
1406
|
+
"${base}.test.${ext}" \
|
|
1407
|
+
"${base}.spec.${ext}" \
|
|
1408
|
+
"${dir}/__tests__/${filename}.test.${ext}" \
|
|
1409
|
+
"${dir}/__tests__/${filename}.spec.${ext}" \
|
|
1410
|
+
"tests/${filename}.test.${ext}" \
|
|
1411
|
+
"tests/${filename}.spec.${ext}"; do
|
|
1412
|
+
if [ -f "$pattern" ]; then
|
|
1413
|
+
RELATED_TESTS="${RELATED_TESTS}${pattern} "
|
|
1414
|
+
fi
|
|
1415
|
+
done
|
|
1416
|
+
# Glob fallback for __tests__ dir patterns
|
|
1417
|
+
if [ -d "${dir}/__tests__" ]; then
|
|
1418
|
+
found_glob=$(find "${dir}/__tests__" -maxdepth 1 -name "${filename}.test.*" -o -name "${filename}.spec.*" 2>/dev/null | tr '\n' ' ')
|
|
1419
|
+
if [ -n "$found_glob" ]; then
|
|
1420
|
+
for f in $found_glob; do
|
|
1421
|
+
case " $RELATED_TESTS " in *" $f "*) ;; *) RELATED_TESTS="${RELATED_TESTS}${f} " ;; esac
|
|
1422
|
+
done
|
|
1423
|
+
fi
|
|
1424
|
+
fi
|
|
1425
|
+
done
|
|
1426
|
+
if [ -n "$RELATED_TESTS" ]; then
|
|
1427
|
+
echo " Found related test(s): $(echo "$RELATED_TESTS" | tr '\n' ' ')"
|
|
1428
|
+
TESTS_OUTPUT=$(npx vitest run --coverage $RELATED_TESTS 2>&1)
|
|
1429
|
+
TESTS_EXIT_CODE=$?
|
|
1430
|
+
echo "$TESTS_OUTPUT" | head -60
|
|
1431
|
+
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
1432
|
+
echo ""
|
|
1433
|
+
echo "❌ BLOCKED - Tests FAILED in related test files"
|
|
1434
|
+
exit 1
|
|
1435
|
+
else
|
|
1436
|
+
echo "✅ PASSED - Related test files passed (with coverage)."
|
|
1437
|
+
fi
|
|
1438
|
+
else
|
|
1439
|
+
# No related tests found → full run is safest
|
|
1440
|
+
echo " No related test files found, running full test suite..."
|
|
1441
|
+
TESTS_OUTPUT=$(npx vitest run --coverage 2>&1)
|
|
1442
|
+
TESTS_EXIT_CODE=$?
|
|
1443
|
+
echo "$TESTS_OUTPUT" | head -60
|
|
1444
|
+
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
1445
|
+
echo ""
|
|
1446
|
+
echo "❌ BLOCKED - Tests FAILED"
|
|
1447
|
+
exit 1
|
|
1448
|
+
else
|
|
1449
|
+
echo "✅ PASSED - Full test suite passed (with coverage)."
|
|
1450
|
+
fi
|
|
1451
|
+
fi
|
|
1322
1452
|
else
|
|
1323
|
-
|
|
1453
|
+
# No test files AND no source files changed → skip
|
|
1454
|
+
echo "✅ PASSED - No test or source files changed, skipping test run."
|
|
1455
|
+
TESTS_EXIT_CODE=0
|
|
1324
1456
|
fi
|
|
1325
1457
|
else
|
|
1326
1458
|
# Fallback: run_tests without coverage
|
|
@@ -1360,11 +1492,18 @@ else
|
|
|
1360
1492
|
if source "$ADAPTER_FILE" 2>/dev/null; then
|
|
1361
1493
|
case "$PROJECT_LANG" in
|
|
1362
1494
|
"typescript")
|
|
1363
|
-
# Coverage
|
|
1364
|
-
#
|
|
1365
|
-
#
|
|
1366
|
-
|
|
1367
|
-
|
|
1495
|
+
# Coverage collected during the test section above.
|
|
1496
|
+
# Smart selection (Issue #286): only runs changed/related tests with --coverage,
|
|
1497
|
+
# producing coverage/coverage-summary.json for the tested subset.
|
|
1498
|
+
# Stage 2 enforcement reads global coverage from the subset run.
|
|
1499
|
+
# If no tests were run (TESTS_EXIT_CODE=0 skip path), warn instead of blocking.
|
|
1500
|
+
if [ -f "coverage/coverage-summary.json" ]; then
|
|
1501
|
+
echo "TypeScript coverage collected during test run."
|
|
1502
|
+
COV_EXIT=0
|
|
1503
|
+
else
|
|
1504
|
+
echo "ℹ️ No coverage data produced — test run was skipped (no TS changes)."
|
|
1505
|
+
COV_EXIT=0
|
|
1506
|
+
fi
|
|
1368
1507
|
;;
|
|
1369
1508
|
"python")
|
|
1370
1509
|
if command -v pytest &> /dev/null && command -v coverage &> /dev/null; then
|