@aimeloic/monkey-tester 2.0.2 → 2.0.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/index.js +52 -66
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -54,23 +54,49 @@ export function endtesterExpress() {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// =========================
|
|
57
|
-
//
|
|
57
|
+
// Context-Aware Static Fallbacks
|
|
58
|
+
// =========================
|
|
59
|
+
function getFallbackFieldsForPath(path) {
|
|
60
|
+
const lowerPath = path.toLowerCase();
|
|
61
|
+
let rawFields = [];
|
|
62
|
+
|
|
63
|
+
if (lowerPath.includes('login') || lowerPath.includes('auth') || lowerPath.includes('signin')) {
|
|
64
|
+
rawFields = ['email', 'password'];
|
|
65
|
+
} else if (lowerPath.includes('user') || lowerPath.includes('register') || lowerPath.includes('signup')) {
|
|
66
|
+
rawFields = ['username', 'email', 'password'];
|
|
67
|
+
} else if (lowerPath.includes('product')) {
|
|
68
|
+
rawFields = ['name', 'price', 'stock'];
|
|
69
|
+
} else {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return rawFields.map(field => ({
|
|
74
|
+
name: field,
|
|
75
|
+
label: field.charAt(0).toUpperCase() + field.slice(1),
|
|
76
|
+
type: detectInputType(field),
|
|
77
|
+
placeholder: `Enter ${field}`
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// =========================
|
|
82
|
+
// Extract req.body fields via Source Inspection
|
|
58
83
|
// =========================
|
|
59
84
|
function extractBodyFields(handler) {
|
|
60
85
|
try {
|
|
61
86
|
const source = handler.toString();
|
|
87
|
+
|
|
88
|
+
// If it's a bound handler or lacks source reference code text
|
|
89
|
+
if (!source || source.includes('[native code]')) return [];
|
|
62
90
|
|
|
63
|
-
// FIXED: The 's' flag allows matching across multiple lines (\n)
|
|
64
91
|
const regex = /(const|let|var)\s*\{\s*([^}]+)\s*\}\s*=\s*req\.body/gs;
|
|
65
92
|
const matches = [...source.matchAll(regex)];
|
|
66
93
|
const fields = [];
|
|
67
94
|
|
|
68
95
|
matches.forEach((match) => {
|
|
69
|
-
// Clean out formatting line-breaks, tabs, or rogue inner code comments
|
|
70
96
|
const cleanedVariablesBlock = match[2]
|
|
71
|
-
.replace(/\/\/.*$/gm, '')
|
|
72
|
-
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
73
|
-
.replace(/[\r\n\t]/g, ' ');
|
|
97
|
+
.replace(/\/\/.*$/gm, '')
|
|
98
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
99
|
+
.replace(/[\r\n\t]/g, ' ');
|
|
74
100
|
|
|
75
101
|
const variables = cleanedVariablesBlock
|
|
76
102
|
.split(',')
|
|
@@ -80,29 +106,22 @@ export function endtesterExpress() {
|
|
|
80
106
|
variables.forEach((field) => {
|
|
81
107
|
let realField = field;
|
|
82
108
|
|
|
83
|
-
// Support aliases (e.g., name: username)
|
|
84
109
|
if (field.includes(':')) {
|
|
85
110
|
realField = field.split(':')[0].trim();
|
|
86
111
|
}
|
|
87
112
|
|
|
88
|
-
// Remove defaults values (e.g., age = 0)
|
|
89
113
|
if (realField.includes('=')) {
|
|
90
114
|
realField = realField.split('=')[0].trim();
|
|
91
115
|
}
|
|
92
116
|
|
|
93
|
-
// Clean any trailing whitespace variations
|
|
94
117
|
realField = realField.trim();
|
|
95
118
|
|
|
96
|
-
const alreadyExists = fields.find(
|
|
97
|
-
f => f.name === realField
|
|
98
|
-
);
|
|
119
|
+
const alreadyExists = fields.find(f => f.name === realField);
|
|
99
120
|
|
|
100
121
|
if (!alreadyExists && realField) {
|
|
101
122
|
fields.push({
|
|
102
123
|
name: realField,
|
|
103
|
-
label:
|
|
104
|
-
realField.charAt(0).toUpperCase() +
|
|
105
|
-
realField.slice(1),
|
|
124
|
+
label: realField.charAt(0).toUpperCase() + realField.slice(1),
|
|
106
125
|
type: detectInputType(realField),
|
|
107
126
|
placeholder: `Enter ${realField}`
|
|
108
127
|
});
|
|
@@ -128,13 +147,8 @@ export function endtesterExpress() {
|
|
|
128
147
|
// ROUTES
|
|
129
148
|
// =========================
|
|
130
149
|
if (layer.route) {
|
|
131
|
-
const methods = Object.keys(
|
|
132
|
-
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
const path = (
|
|
136
|
-
prefix + layer.route.path
|
|
137
|
-
).replace(/\/+/g, '/');
|
|
150
|
+
const methods = Object.keys(layer.route.methods);
|
|
151
|
+
const path = (prefix + layer.route.path).replace(/\/+/g, '/');
|
|
138
152
|
|
|
139
153
|
if (path.includes('/api/tester')) {
|
|
140
154
|
return;
|
|
@@ -142,14 +156,8 @@ export function endtesterExpress() {
|
|
|
142
156
|
|
|
143
157
|
methods.forEach((method) => {
|
|
144
158
|
const httpMethod = method.toUpperCase();
|
|
159
|
+
const key = `${httpMethod.toLowerCase()}-` + path.replace(/[^a-zA-Z0-9]/g, '-');
|
|
145
160
|
|
|
146
|
-
const key =
|
|
147
|
-
`${httpMethod.toLowerCase()}-` +
|
|
148
|
-
path.replace(/[^a-zA-Z0-9]/g, '-');
|
|
149
|
-
|
|
150
|
-
// =========================
|
|
151
|
-
// PATH PARAMS
|
|
152
|
-
// =========================
|
|
153
161
|
const pathParams = layer.route.keys
|
|
154
162
|
? layer.route.keys.map((k) => ({
|
|
155
163
|
name: k.name,
|
|
@@ -159,37 +167,27 @@ export function endtesterExpress() {
|
|
|
159
167
|
: [];
|
|
160
168
|
|
|
161
169
|
// =========================
|
|
162
|
-
// BODY FIELDS
|
|
170
|
+
// BODY FIELDS COMPILING
|
|
163
171
|
// =========================
|
|
164
172
|
let bodyFields = [];
|
|
165
173
|
|
|
166
|
-
if (
|
|
167
|
-
['POST', 'PUT', 'PATCH'].includes(
|
|
168
|
-
httpMethod
|
|
169
|
-
)
|
|
170
|
-
) {
|
|
174
|
+
if (['POST', 'PUT', 'PATCH'].includes(httpMethod)) {
|
|
171
175
|
layer.route.stack.forEach((stackLayer) => {
|
|
172
|
-
if (
|
|
173
|
-
stackLayer.handle
|
|
174
|
-
typeof stackLayer.handle === 'function'
|
|
175
|
-
) {
|
|
176
|
-
const extractedFields =
|
|
177
|
-
extractBodyFields(
|
|
178
|
-
stackLayer.handle
|
|
179
|
-
);
|
|
180
|
-
|
|
176
|
+
if (stackLayer.handle && typeof stackLayer.handle === 'function') {
|
|
177
|
+
const extractedFields = extractBodyFields(stackLayer.handle);
|
|
181
178
|
bodyFields.push(...extractedFields);
|
|
182
179
|
}
|
|
183
180
|
});
|
|
184
181
|
|
|
185
|
-
//
|
|
182
|
+
// Deduplicate discovered elements
|
|
186
183
|
bodyFields = bodyFields.filter(
|
|
187
|
-
(field, index, self) =>
|
|
188
|
-
index ===
|
|
189
|
-
self.findIndex(
|
|
190
|
-
f => f.name === field.name
|
|
191
|
-
)
|
|
184
|
+
(field, index, self) => index === self.findIndex(f => f.name === field.name)
|
|
192
185
|
);
|
|
186
|
+
|
|
187
|
+
// CRITICAL FAILSAFE: If code reflection extracted nothing, apply smart path-based fallback fields
|
|
188
|
+
if (bodyFields.length === 0) {
|
|
189
|
+
bodyFields = getFallbackFieldsForPath(path);
|
|
190
|
+
}
|
|
193
191
|
}
|
|
194
192
|
|
|
195
193
|
detectedEndpoints[key] = {
|
|
@@ -219,10 +217,7 @@ export function endtesterExpress() {
|
|
|
219
217
|
.match(/^\/\^\\(.*?)\\\/\?/);
|
|
220
218
|
|
|
221
219
|
if (match && match[1]) {
|
|
222
|
-
routerPath = match[1].replace(
|
|
223
|
-
/\\/g,
|
|
224
|
-
''
|
|
225
|
-
);
|
|
220
|
+
routerPath = match[1].replace(/\\/g, '');
|
|
226
221
|
}
|
|
227
222
|
}
|
|
228
223
|
|
|
@@ -237,24 +232,15 @@ export function endtesterExpress() {
|
|
|
237
232
|
// =========================
|
|
238
233
|
// START PARSING
|
|
239
234
|
// =========================
|
|
240
|
-
if (
|
|
241
|
-
expressApp._router &&
|
|
242
|
-
expressApp._router.stack
|
|
243
|
-
) {
|
|
235
|
+
if (expressApp._router && expressApp._router.stack) {
|
|
244
236
|
parseStack(expressApp._router.stack);
|
|
245
237
|
}
|
|
246
238
|
|
|
247
239
|
// =========================
|
|
248
240
|
// RENDER HTML
|
|
249
241
|
// =========================
|
|
250
|
-
const fullHtml =
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
res.setHeader(
|
|
254
|
-
'Content-Type',
|
|
255
|
-
'text/html'
|
|
256
|
-
);
|
|
257
|
-
|
|
242
|
+
const fullHtml = getHtmlTemplate(detectedEndpoints);
|
|
243
|
+
res.setHeader('Content-Type', 'text/html');
|
|
258
244
|
return res.send(fullHtml);
|
|
259
245
|
};
|
|
260
246
|
}
|