@tng-sh/js 0.1.1 → 0.1.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/bin/tng.js +60 -1
- package/binaries/go-ui-darwin-amd64 +0 -0
- package/binaries/go-ui-darwin-arm64 +0 -0
- package/binaries/go-ui-linux-amd64 +0 -0
- package/binaries/go-ui-linux-arm64 +0 -0
- package/lib/generateTestsUi.js +95 -10
- package/package.json +1 -1
- package/tng_sh_js.darwin-arm64.node +0 -0
- package/tng_sh_js.darwin-x64.node +0 -0
- package/tng_sh_js.linux-arm64-gnu.node +0 -0
- package/tng_sh_js.linux-x64-gnu.node +0 -0
package/bin/tng.js
CHANGED
|
@@ -28,7 +28,7 @@ process.on('uncaughtException', (err) => {
|
|
|
28
28
|
program
|
|
29
29
|
.name('tng')
|
|
30
30
|
.description('TNG - Automated Test Generation, and audit generation for JavaScript')
|
|
31
|
-
.version('0.1.
|
|
31
|
+
.version('0.1.2');
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* @command init
|
|
@@ -143,6 +143,65 @@ program
|
|
|
143
143
|
launchInteractive();
|
|
144
144
|
});
|
|
145
145
|
|
|
146
|
+
/**
|
|
147
|
+
* @command xray
|
|
148
|
+
* Generate X-Ray (Mermaid Visualization)
|
|
149
|
+
*/
|
|
150
|
+
program
|
|
151
|
+
.command('xray')
|
|
152
|
+
.alias('x')
|
|
153
|
+
.description('Generate X-Ray visualization')
|
|
154
|
+
.option('-f, --file <path>', 'Input file path')
|
|
155
|
+
.option('-m, --method <name>', 'Method name to visualize')
|
|
156
|
+
.action(async (options) => {
|
|
157
|
+
if (!options.file || !options.method) {
|
|
158
|
+
console.log(chalk.red('Error: Both --file and --method are required for X-Ray.'));
|
|
159
|
+
console.log(chalk.yellow('Usage: tng xray -f <file> -m <method>'));
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
console.log(chalk.blue(`🔍 Generating X-Ray for ${options.method} in ${options.file}...`));
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const config = loadConfig();
|
|
167
|
+
|
|
168
|
+
// Re-use logic similar to _handleXrayFlow but tailored for CLI non-interactive output
|
|
169
|
+
const { generateTest } = require('../index');
|
|
170
|
+
|
|
171
|
+
// We use a custom callback to show progress
|
|
172
|
+
const callback = (msg, percent) => {
|
|
173
|
+
if (percent % 20 === 0) console.log(chalk.dim(`[${percent}%] ${msg}`));
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// We pass 'visualize' as the test type
|
|
177
|
+
const resultJson = generateTest(
|
|
178
|
+
path.resolve(options.file),
|
|
179
|
+
options.method,
|
|
180
|
+
null,
|
|
181
|
+
'visualize',
|
|
182
|
+
JSON.stringify(config),
|
|
183
|
+
callback
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
let mermaidCode = "";
|
|
187
|
+
try {
|
|
188
|
+
const parsed = JSON.parse(resultJson);
|
|
189
|
+
mermaidCode = parsed.mermaid_code || resultJson;
|
|
190
|
+
} catch (e) {
|
|
191
|
+
mermaidCode = resultJson;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log(chalk.bold('\n--- X-Ray Result (Mermaid.js) ---\n'));
|
|
195
|
+
console.log(chalk.blue(mermaidCode));
|
|
196
|
+
console.log(chalk.bold('\n---------------------------------\n'));
|
|
197
|
+
console.log(chalk.green('✓ Copy the code above and paste into https://mermaid.live'));
|
|
198
|
+
|
|
199
|
+
} catch (e) {
|
|
200
|
+
console.log(chalk.red(`Error: ${e.message}`));
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
146
205
|
/**
|
|
147
206
|
* Main command handler (handles -f and -m)
|
|
148
207
|
*/
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/generateTestsUi.js
CHANGED
|
@@ -34,6 +34,9 @@ class GenerateTestsUI {
|
|
|
34
34
|
} else if (choice === 'audit') {
|
|
35
35
|
const result = await this._showFileSelection(true);
|
|
36
36
|
if (result === 'exit') return 'exit';
|
|
37
|
+
} else if (choice === 'xray') {
|
|
38
|
+
const result = await this._showFileSelection(false, true);
|
|
39
|
+
if (result === 'exit') return 'exit';
|
|
37
40
|
} else if (choice === 'stats') {
|
|
38
41
|
await this._showStats();
|
|
39
42
|
} else if (choice === 'about') {
|
|
@@ -95,7 +98,7 @@ class GenerateTestsUI {
|
|
|
95
98
|
});
|
|
96
99
|
}
|
|
97
100
|
|
|
98
|
-
async _showFileSelection(isAudit = false) {
|
|
101
|
+
async _showFileSelection(isAudit = false, isXray = false) {
|
|
99
102
|
const files = await this._getUserFiles();
|
|
100
103
|
|
|
101
104
|
if (files.length === 0) {
|
|
@@ -109,19 +112,19 @@ class GenerateTestsUI {
|
|
|
109
112
|
path: path.dirname(file)
|
|
110
113
|
}));
|
|
111
114
|
|
|
112
|
-
const title = isAudit ? 'Select JavaScript File to Audit' : 'Select JavaScript File';
|
|
115
|
+
const title = isXray ? 'Select File for X-Ray' : (isAudit ? 'Select JavaScript File to Audit' : 'Select JavaScript File');
|
|
113
116
|
const selectedName = this.goUiSession.showListView(title, items);
|
|
114
117
|
|
|
115
118
|
if (selectedName === 'back') return 'back';
|
|
116
119
|
if (!selectedName || selectedName === 'exit') return 'exit';
|
|
117
120
|
|
|
118
121
|
const selectedFile = path.resolve(cwd, selectedName);
|
|
119
|
-
const result = await this._showMethodsForFile(selectedFile, isAudit);
|
|
122
|
+
const result = await this._showMethodsForFile(selectedFile, isAudit, isXray);
|
|
120
123
|
if (result === 'main_menu') return 'main_menu';
|
|
121
124
|
return result;
|
|
122
125
|
}
|
|
123
126
|
|
|
124
|
-
async _showMethodsForFile(filePath, isAudit = false) {
|
|
127
|
+
async _showMethodsForFile(filePath, isAudit = false, isXray = false) {
|
|
125
128
|
let outline;
|
|
126
129
|
try {
|
|
127
130
|
const result = getFileOutline(filePath);
|
|
@@ -144,33 +147,39 @@ class GenerateTestsUI {
|
|
|
144
147
|
methodData: m
|
|
145
148
|
}));
|
|
146
149
|
|
|
147
|
-
const title = isAudit ? `Select Method to Audit for ${fileName}` : `Select Method for ${fileName}
|
|
150
|
+
const title = isXray ? `Select Method to X-Ray for ${fileName}` : (isAudit ? `Select Method to Audit for ${fileName}` : `Select Method for ${fileName}`);
|
|
148
151
|
const selectedDisplay = this.goUiSession.showListView(title, items);
|
|
149
152
|
|
|
150
|
-
if (selectedDisplay === 'back' || !selectedDisplay) return this._showFileSelection(isAudit);
|
|
153
|
+
if (selectedDisplay === 'back' || !selectedDisplay) return this._showFileSelection(isAudit, isXray);
|
|
151
154
|
|
|
152
155
|
const selectedMethod = items.find(i => i.name === selectedDisplay)?.methodData;
|
|
153
156
|
|
|
154
157
|
if (selectedMethod) {
|
|
158
|
+
if (isXray) {
|
|
159
|
+
const choice = await this._generateTestsForMethod(filePath, selectedMethod, 'visualize', false, true);
|
|
160
|
+
if (choice === 'main_menu') return 'main_menu';
|
|
161
|
+
return this._showFileSelection(isAudit, isXray);
|
|
162
|
+
}
|
|
163
|
+
|
|
155
164
|
const testType = this.goUiSession.showJsTestMenu();
|
|
156
|
-
if (testType === 'back') return this._showFileSelection(isAudit);
|
|
165
|
+
if (testType === 'back') return this._showFileSelection(isAudit, isXray);
|
|
157
166
|
|
|
158
167
|
const finalType = testType === 'auto' ? null : testType;
|
|
159
168
|
const choice = await this._generateTestsForMethod(filePath, selectedMethod, finalType, isAudit);
|
|
160
169
|
|
|
161
170
|
if (isAudit) {
|
|
162
171
|
if (choice === 'main_menu') return 'main_menu';
|
|
163
|
-
return this._showFileSelection(isAudit);
|
|
172
|
+
return this._showFileSelection(isAudit, isXray);
|
|
164
173
|
}
|
|
165
174
|
|
|
166
175
|
if (choice && choice.file_path && !choice.error) {
|
|
167
176
|
this._showPostGenerationMenu(choice);
|
|
168
177
|
}
|
|
169
178
|
}
|
|
170
|
-
return this._showFileSelection(isAudit);
|
|
179
|
+
return this._showFileSelection(isAudit, isXray);
|
|
171
180
|
}
|
|
172
181
|
|
|
173
|
-
async _generateTestsForMethod(filePath, method, testType, isAudit = false) {
|
|
182
|
+
async _generateTestsForMethod(filePath, method, testType, isAudit = false, isXray = false) {
|
|
174
183
|
if (!this._hasApiKey()) {
|
|
175
184
|
return { error: 'No API key' };
|
|
176
185
|
}
|
|
@@ -179,6 +188,10 @@ class GenerateTestsUI {
|
|
|
179
188
|
return this._handleAuditFlow(filePath, method, testType);
|
|
180
189
|
}
|
|
181
190
|
|
|
191
|
+
if (isXray) {
|
|
192
|
+
return this._handleXrayFlow(filePath, method);
|
|
193
|
+
}
|
|
194
|
+
|
|
182
195
|
const fileName = path.basename(filePath);
|
|
183
196
|
const displayName = method.class_name ? `${method.class_name}#${method.name}` : `${fileName}#${method.name}`;
|
|
184
197
|
return this._handleTestGenerationFlow(filePath, method, testType, displayName);
|
|
@@ -395,6 +408,78 @@ class GenerateTestsUI {
|
|
|
395
408
|
return uiResult || null;
|
|
396
409
|
}
|
|
397
410
|
|
|
411
|
+
async _handleXrayFlow(filePath, method) {
|
|
412
|
+
const actionName = 'Generating X-Ray for';
|
|
413
|
+
const displayName = method.name;
|
|
414
|
+
|
|
415
|
+
const progressHandler = async (progress) => {
|
|
416
|
+
progress.update('Analyzing method structure...');
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
const config = loadConfig();
|
|
420
|
+
// Override test_type to 'visualize' which behaves like test generation but returns Mermaid code
|
|
421
|
+
const resultJson = generateTest(
|
|
422
|
+
filePath,
|
|
423
|
+
method.name,
|
|
424
|
+
method.class_name || null,
|
|
425
|
+
'visualize',
|
|
426
|
+
JSON.stringify(config),
|
|
427
|
+
(msg, percent) => {
|
|
428
|
+
// Simple progress updates
|
|
429
|
+
progress.update(msg, { percent });
|
|
430
|
+
}
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
// The result is expected to be a JSON string containing the mermaid definition
|
|
434
|
+
// But generateTest returns the full file content usually.
|
|
435
|
+
// We need to clarify if Backend returns just Mermaid or a file.
|
|
436
|
+
// Assuming Backend returns JSON with { "visualize_result": "mermaid code..." } or similar
|
|
437
|
+
// or if it returns raw text, we handle it.
|
|
438
|
+
|
|
439
|
+
// For 'visualize' type, existing backend generates a file or JSON.
|
|
440
|
+
// We should parse it.
|
|
441
|
+
let mermaidCode = "";
|
|
442
|
+
try {
|
|
443
|
+
const parsed = JSON.parse(resultJson);
|
|
444
|
+
if (parsed.mermaid_code) mermaidCode = parsed.mermaid_code;
|
|
445
|
+
else mermaidCode = resultJson; // Fallback
|
|
446
|
+
} catch (e) {
|
|
447
|
+
mermaidCode = resultJson;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
success: true,
|
|
452
|
+
mermaidCode: mermaidCode
|
|
453
|
+
};
|
|
454
|
+
} catch (e) {
|
|
455
|
+
progress.error(`Failed to generate X-Ray: ${e.message}`);
|
|
456
|
+
return { error: e.message };
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const result = await this.goUiSession.showProgress(`${actionName} ${displayName}`, progressHandler);
|
|
461
|
+
|
|
462
|
+
if (result && result.mermaidCode) {
|
|
463
|
+
console.log(chalk.bold('\n--- X-Ray Result (Mermaid.js) ---\n'));
|
|
464
|
+
// Simple highlighting for terminal
|
|
465
|
+
console.log(chalk.blue(result.mermaidCode));
|
|
466
|
+
console.log(chalk.bold('\n---------------------------------\n'));
|
|
467
|
+
|
|
468
|
+
// Prompt user action
|
|
469
|
+
this._copyToClipboard(result.mermaidCode);
|
|
470
|
+
console.log(chalk.green('✓ Copied to clipboard! Paste into https://mermaid.live'));
|
|
471
|
+
|
|
472
|
+
// Wait for user confirmation to return
|
|
473
|
+
const { execSync } = require('child_process');
|
|
474
|
+
try {
|
|
475
|
+
// Just a pause hack if we want, but showListView might be better.
|
|
476
|
+
// For now, let's just return.
|
|
477
|
+
} catch (e) { }
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return result;
|
|
481
|
+
}
|
|
482
|
+
|
|
398
483
|
_updateProgress(progress, msg, percent) {
|
|
399
484
|
const agentMap = {
|
|
400
485
|
'context_agent_status': { label: 'Context Builder', step: 1 },
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|