@tng-sh/js 0.1.2 → 0.1.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/bin/tng.js +42 -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/index.d.ts +2 -0
- package/index.js +2 -1
- package/lib/generateTestsUi.js +57 -6
- 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.3');
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* @command init
|
|
@@ -210,10 +210,51 @@ program
|
|
|
210
210
|
.option('-f, --file <path>', 'JavaScript file path')
|
|
211
211
|
.option('-t, --type <type>', 'Component type (react_component, express_handler, etc) [required]')
|
|
212
212
|
.option('-a, --audit', 'Run audit mode instead of test generation')
|
|
213
|
+
.option('--trace', 'Run symbolic trace visualization')
|
|
213
214
|
.option('--json', 'Output results as JSON events (machine-readable)')
|
|
214
215
|
|
|
215
216
|
.action(async (options) => {
|
|
216
217
|
if (options.method && options.file) {
|
|
218
|
+
if (options.trace) {
|
|
219
|
+
const { getSymbolicTrace } = require('../index');
|
|
220
|
+
const GoUISession = require('../lib/goUiSession');
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const traceJson = getSymbolicTrace(
|
|
224
|
+
path.resolve(options.file),
|
|
225
|
+
options.method,
|
|
226
|
+
null
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
if (options.json) {
|
|
230
|
+
console.log(traceJson);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Create temp file for the trace
|
|
235
|
+
const tmpDir = require('os').tmpdir();
|
|
236
|
+
const tmpFile = path.join(tmpDir, `trace-${Date.now()}.json`);
|
|
237
|
+
fs.writeFileSync(tmpFile, traceJson);
|
|
238
|
+
|
|
239
|
+
// Launch Go UI
|
|
240
|
+
const session = new GoUISession();
|
|
241
|
+
const binaryPath = session._binaryPath;
|
|
242
|
+
|
|
243
|
+
const { spawnSync } = require('child_process');
|
|
244
|
+
spawnSync(binaryPath, ['trace-results', '--file', tmpFile], {
|
|
245
|
+
stdio: 'inherit'
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Cleanup
|
|
249
|
+
try { fs.unlinkSync(tmpFile); } catch (e) { }
|
|
250
|
+
|
|
251
|
+
} catch (e) {
|
|
252
|
+
console.log(chalk.red(`Trace failed: ${e.message}`));
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
217
258
|
if (!options.type && !options.audit) {
|
|
218
259
|
console.log(chalk.red('Error: --type <type> is required.'));
|
|
219
260
|
process.exit(1);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/index.d.ts
CHANGED
|
@@ -44,3 +44,5 @@ export declare function applyEditsAtomic(operationsJson: string): string
|
|
|
44
44
|
* Analyzes code, submits a job, and polls for the final generated test.
|
|
45
45
|
*/
|
|
46
46
|
export declare function generateTest(filePath: string, methodName: string, className: string | undefined | null, testType: string | undefined | null, configJson: string, callback: (...args: any[]) => any): string
|
|
47
|
+
/** Analyzes a method and generates a symbolic execution trace. */
|
|
48
|
+
export declare function getSymbolicTrace(filePath: string, methodName: string, className?: string | undefined | null): string
|
package/index.js
CHANGED
|
@@ -310,7 +310,7 @@ if (!nativeBinding) {
|
|
|
310
310
|
throw new Error(`Failed to load native binding`)
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
const { getFileOutline, getProjectMetadata, findCallSites, ping, submitJob, getUserStats, runAudit, applyEdit, applyEditsAtomic, generateTest } = nativeBinding
|
|
313
|
+
const { getFileOutline, getProjectMetadata, findCallSites, ping, submitJob, getUserStats, runAudit, applyEdit, applyEditsAtomic, generateTest, getSymbolicTrace } = nativeBinding
|
|
314
314
|
|
|
315
315
|
module.exports.getFileOutline = getFileOutline
|
|
316
316
|
module.exports.getProjectMetadata = getProjectMetadata
|
|
@@ -322,3 +322,4 @@ module.exports.runAudit = runAudit
|
|
|
322
322
|
module.exports.applyEdit = applyEdit
|
|
323
323
|
module.exports.applyEditsAtomic = applyEditsAtomic
|
|
324
324
|
module.exports.generateTest = generateTest
|
|
325
|
+
module.exports.getSymbolicTrace = getSymbolicTrace
|
package/lib/generateTestsUi.js
CHANGED
|
@@ -37,6 +37,9 @@ class GenerateTestsUI {
|
|
|
37
37
|
} else if (choice === 'xray') {
|
|
38
38
|
const result = await this._showFileSelection(false, true);
|
|
39
39
|
if (result === 'exit') return 'exit';
|
|
40
|
+
} else if (choice === 'trace') {
|
|
41
|
+
const result = await this._showFileSelection(false, false, true);
|
|
42
|
+
if (result === 'exit') return 'exit';
|
|
40
43
|
} else if (choice === 'stats') {
|
|
41
44
|
await this._showStats();
|
|
42
45
|
} else if (choice === 'about') {
|
|
@@ -98,7 +101,7 @@ class GenerateTestsUI {
|
|
|
98
101
|
});
|
|
99
102
|
}
|
|
100
103
|
|
|
101
|
-
async _showFileSelection(isAudit = false, isXray = false) {
|
|
104
|
+
async _showFileSelection(isAudit = false, isXray = false, isTrace = false) {
|
|
102
105
|
const files = await this._getUserFiles();
|
|
103
106
|
|
|
104
107
|
if (files.length === 0) {
|
|
@@ -112,19 +115,23 @@ class GenerateTestsUI {
|
|
|
112
115
|
path: path.dirname(file)
|
|
113
116
|
}));
|
|
114
117
|
|
|
115
|
-
|
|
118
|
+
let title = 'Select JavaScript File';
|
|
119
|
+
if (isAudit) title = 'Select JavaScript File to Audit';
|
|
120
|
+
else if (isXray) title = 'Select File for X-Ray';
|
|
121
|
+
else if (isTrace) title = 'Select File for Symbolic Trace';
|
|
122
|
+
|
|
116
123
|
const selectedName = this.goUiSession.showListView(title, items);
|
|
117
124
|
|
|
118
125
|
if (selectedName === 'back') return 'back';
|
|
119
126
|
if (!selectedName || selectedName === 'exit') return 'exit';
|
|
120
127
|
|
|
121
128
|
const selectedFile = path.resolve(cwd, selectedName);
|
|
122
|
-
const result = await this._showMethodsForFile(selectedFile, isAudit, isXray);
|
|
129
|
+
const result = await this._showMethodsForFile(selectedFile, isAudit, isXray, isTrace);
|
|
123
130
|
if (result === 'main_menu') return 'main_menu';
|
|
124
131
|
return result;
|
|
125
132
|
}
|
|
126
133
|
|
|
127
|
-
async _showMethodsForFile(filePath, isAudit = false, isXray = false) {
|
|
134
|
+
async _showMethodsForFile(filePath, isAudit = false, isXray = false, isTrace = false) {
|
|
128
135
|
let outline;
|
|
129
136
|
try {
|
|
130
137
|
const result = getFileOutline(filePath);
|
|
@@ -147,14 +154,23 @@ class GenerateTestsUI {
|
|
|
147
154
|
methodData: m
|
|
148
155
|
}));
|
|
149
156
|
|
|
150
|
-
|
|
157
|
+
let title = 'Select Method';
|
|
158
|
+
if (isAudit) title = `Select Method to Audit for ${fileName}`;
|
|
159
|
+
else if (isXray) title = `Select Method to X-Ray for ${fileName}`;
|
|
160
|
+
else if (isTrace) title = `Select Method to Trace for ${fileName}`;
|
|
161
|
+
|
|
151
162
|
const selectedDisplay = this.goUiSession.showListView(title, items);
|
|
152
163
|
|
|
153
|
-
if (selectedDisplay === 'back' || !selectedDisplay) return this._showFileSelection(isAudit, isXray);
|
|
164
|
+
if (selectedDisplay === 'back' || !selectedDisplay) return this._showFileSelection(isAudit, isXray, isTrace);
|
|
154
165
|
|
|
155
166
|
const selectedMethod = items.find(i => i.name === selectedDisplay)?.methodData;
|
|
156
167
|
|
|
157
168
|
if (selectedMethod) {
|
|
169
|
+
if (isTrace) {
|
|
170
|
+
await this._launchTrace(filePath, selectedMethod.name);
|
|
171
|
+
return this._showFileSelection(isAudit, isXray, isTrace);
|
|
172
|
+
}
|
|
173
|
+
|
|
158
174
|
if (isXray) {
|
|
159
175
|
const choice = await this._generateTestsForMethod(filePath, selectedMethod, 'visualize', false, true);
|
|
160
176
|
if (choice === 'main_menu') return 'main_menu';
|
|
@@ -657,6 +673,41 @@ class GenerateTestsUI {
|
|
|
657
673
|
return [];
|
|
658
674
|
}
|
|
659
675
|
}
|
|
676
|
+
|
|
677
|
+
async _launchTrace(filePath, methodName) {
|
|
678
|
+
const { getSymbolicTrace } = require('../index');
|
|
679
|
+
const fs = require('fs');
|
|
680
|
+
const path = require('path');
|
|
681
|
+
const chalk = require('chalk');
|
|
682
|
+
|
|
683
|
+
try {
|
|
684
|
+
// 1. Generate Trace (with Spinner)
|
|
685
|
+
const result = this.goUiSession.showSpinner(`Tracing ${methodName}...`, () => {
|
|
686
|
+
try {
|
|
687
|
+
const traceJson = getSymbolicTrace(filePath, methodName, null);
|
|
688
|
+
const tmpDir = require('os').tmpdir();
|
|
689
|
+
const f = path.join(tmpDir, `trace-${Date.now()}.json`);
|
|
690
|
+
fs.writeFileSync(f, traceJson);
|
|
691
|
+
return { success: true, file: f };
|
|
692
|
+
} catch (e) {
|
|
693
|
+
return { success: false, message: e.message };
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
if (result && result.success && result.file) {
|
|
698
|
+
// 2. Show Trace UI
|
|
699
|
+
const { spawnSync } = require('child_process');
|
|
700
|
+
spawnSync(this.goUiSession._binaryPath, ['trace-results', '--file', result.file], {
|
|
701
|
+
stdio: 'inherit'
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
try { fs.unlinkSync(result.file); } catch (e) { }
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
} catch (e) {
|
|
708
|
+
console.error(chalk.red(`Trace error: ${e.message}`));
|
|
709
|
+
}
|
|
710
|
+
}
|
|
660
711
|
}
|
|
661
712
|
|
|
662
713
|
module.exports = GenerateTestsUI;
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|