@aiready/testability 0.6.22 → 0.6.23

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/src/analyzer.ts CHANGED
@@ -3,11 +3,12 @@ import {
3
3
  calculateTestabilityIndex,
4
4
  Severity,
5
5
  IssueType,
6
- emitProgress,
6
+ runBatchAnalysis,
7
7
  getParser,
8
+ isTestFile,
9
+ detectTestFramework,
8
10
  } from '@aiready/core';
9
- import { readFileSync, existsSync } from 'fs';
10
- import { join } from 'path';
11
+ import { readFileSync } from 'fs';
11
12
  import type {
12
13
  TestabilityOptions,
13
14
  TestabilityIssue,
@@ -89,62 +90,9 @@ async function analyzeFileTestability(filePath: string): Promise<FileAnalysis> {
89
90
  return result;
90
91
  }
91
92
 
92
- // ---------------------------------------------------------------------------
93
- // Test framework detection
94
- // ---------------------------------------------------------------------------
95
-
96
- function detectTestFramework(rootDir: string): boolean {
97
- // Check common manifest files
98
- const manifests = [
99
- {
100
- file: 'package.json',
101
- deps: ['jest', 'vitest', 'mocha', 'mocha', 'jasmine', 'ava', 'tap'],
102
- },
103
- { file: 'requirements.txt', deps: ['pytest', 'unittest', 'nose'] },
104
- { file: 'pyproject.toml', deps: ['pytest'] },
105
- { file: 'pom.xml', deps: ['junit', 'testng'] },
106
- { file: 'build.gradle', deps: ['junit', 'testng'] },
107
- { file: 'go.mod', deps: ['testing'] }, // go testing is built-in
108
- ];
109
-
110
- for (const m of manifests) {
111
- const p = join(rootDir, m.file);
112
- if (existsSync(p)) {
113
- if (m.file === 'go.mod') return true; // built-in
114
- try {
115
- const content = readFileSync(p, 'utf-8');
116
- if (m.deps.some((d) => content.includes(d))) return true;
117
- } catch {
118
- // Ignore file read errors
119
- }
120
- }
121
- }
122
- return false;
123
- }
124
-
125
- // ---------------------------------------------------------------------------
126
93
  // Main analyzer
127
94
  // ---------------------------------------------------------------------------
128
95
 
129
- const TEST_PATTERNS = [
130
- /\.(test|spec)\.(ts|tsx|js|jsx)$/,
131
- /_test\.go$/,
132
- /test_.*\.py$/,
133
- /.*_test\.py$/,
134
- /.*Test\.java$/,
135
- /.*Tests\.cs$/,
136
- /__tests__\//,
137
- /\/tests?\//,
138
- /\/e2e\//,
139
- /\/fixtures\//,
140
- ];
141
-
142
- function isTestFile(filePath: string, extra?: string[]): boolean {
143
- if (TEST_PATTERNS.some((p) => p.test(filePath))) return true;
144
- if (extra) return extra.some((p) => filePath.includes(p));
145
- return false;
146
- }
147
-
148
96
  export async function analyzeTestability(
149
97
  options: TestabilityOptions
150
98
  ): Promise<TestabilityReport> {
@@ -177,29 +125,28 @@ export async function analyzeTestability(
177
125
  totalFunctions: number;
178
126
  }> = [];
179
127
 
180
- let processed = 0;
181
- for (const f of sourceFiles) {
182
- processed++;
183
- emitProgress(
184
- processed,
185
- sourceFiles.length,
186
- 'testability',
187
- 'analyzing files',
188
- options.onProgress
189
- );
190
-
191
- const a = await analyzeFileTestability(f);
192
- for (const key of Object.keys(aggregated) as Array<keyof FileAnalysis>) {
193
- aggregated[key] += a[key];
194
- }
195
-
196
- // Collect file-level data
197
- fileDetails.push({
128
+ await runBatchAnalysis(
129
+ sourceFiles,
130
+ 'analyzing files',
131
+ 'testability',
132
+ options.onProgress,
133
+ async (f: string) => ({
198
134
  filePath: f,
199
- pureFunctions: a.pureFunctions,
200
- totalFunctions: a.totalFunctions,
201
- });
202
- }
135
+ analysis: await analyzeFileTestability(f),
136
+ }),
137
+ (result: { filePath: string; analysis: FileAnalysis }) => {
138
+ const a = result.analysis;
139
+ for (const key of Object.keys(aggregated) as Array<keyof FileAnalysis>) {
140
+ aggregated[key] += a[key];
141
+ }
142
+ // Collect file-level data
143
+ fileDetails.push({
144
+ filePath: result.filePath,
145
+ pureFunctions: a.pureFunctions,
146
+ totalFunctions: a.totalFunctions,
147
+ });
148
+ }
149
+ );
203
150
 
204
151
  const hasTestFramework = detectTestFramework(options.rootDir);
205
152
 
package/src/cli.ts CHANGED
@@ -88,28 +88,28 @@ EXAMPLES:
88
88
  } else {
89
89
  displayStandardConsoleReport({
90
90
  title: '🧪 Testability Analysis',
91
- score: scoring.summary.score,
92
- rating: scoring.summary.rating,
91
+ score: scoring.score,
92
+ rating: scoring.rating || report.summary.rating,
93
93
  dimensions: [
94
94
  {
95
95
  name: 'Test Coverage',
96
- value: scoring.summary.dimensions.testCoverageRatio,
96
+ value: report.summary.dimensions.testCoverageRatio,
97
97
  },
98
98
  {
99
99
  name: 'Function Purity',
100
- value: scoring.summary.dimensions.purityScore,
100
+ value: report.summary.dimensions.purityScore,
101
101
  },
102
102
  {
103
103
  name: 'Dependency Injection',
104
- value: scoring.summary.dimensions.dependencyInjectionScore,
104
+ value: report.summary.dimensions.dependencyInjectionScore,
105
105
  },
106
106
  {
107
107
  name: 'Interface Focus',
108
- value: scoring.summary.dimensions.interfaceFocusScore,
108
+ value: report.summary.dimensions.interfaceFocusScore,
109
109
  },
110
110
  {
111
111
  name: 'Observability',
112
- value: scoring.summary.dimensions.observabilityScore,
112
+ value: report.summary.dimensions.observabilityScore,
113
113
  },
114
114
  ],
115
115
  stats: [
@@ -117,7 +117,7 @@ EXAMPLES:
117
117
  { label: 'Test Files', value: report.rawData.testFiles },
118
118
  {
119
119
  label: 'Coverage Ratio',
120
- value: Math.round(scoring.summary.coverageRatio * 100) + '%',
120
+ value: Math.round(report.summary.coverageRatio * 100) + '%',
121
121
  },
122
122
  ],
123
123
  issues: report.issues,