@testsmith/api-spector 0.3.3 → 0.3.5
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/cli.js +180 -39
- package/out/main/index.js +65 -12
- package/out/main/runner.js +1 -1
- package/out/renderer/assets/{index-vRsURTUY.js → index-BFxRlU1d.js} +836 -870
- package/out/renderer/assets/index-DuMAjIiI.css +2 -0
- package/out/renderer/index.html +2 -2
- package/package.json +1 -1
- package/readme.md +3 -0
- package/out/renderer/assets/index-CjvjKHtF.css +0 -2
package/bin/cli.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const os = require('os')
|
|
4
6
|
const path = require('path')
|
|
5
|
-
const { spawn } = require('child_process')
|
|
7
|
+
const { spawn, spawnSync } = require('child_process')
|
|
6
8
|
|
|
7
9
|
const [, , cmd = 'ui', ...rest] = process.argv
|
|
8
10
|
|
|
@@ -61,23 +63,140 @@ if (!command) {
|
|
|
61
63
|
process.exit(1)
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
// ─── Electron binary self-repair ─────────────────────────────────────────────
|
|
67
|
+
//
|
|
68
|
+
// `require('electron')` throws when the postinstall didn't download the
|
|
69
|
+
// platform binary. On corporate machines the ~100 MB zip often IS fully
|
|
70
|
+
// downloaded into electron's cache — it's the extraction into node_modules
|
|
71
|
+
// that got interrupted (antivirus, killed install, …). In that case we can
|
|
72
|
+
// repair the install ourselves, using the OS's own unzip tooling (which is
|
|
73
|
+
// not affected by whatever broke Node's extractor), and launch anyway.
|
|
74
|
+
|
|
75
|
+
// Mirrors getPlatformPath() in electron's install.js — path.txt must contain
|
|
76
|
+
// exactly this value.
|
|
77
|
+
function electronPlatformPath() {
|
|
78
|
+
switch (process.platform) {
|
|
79
|
+
case 'win32': return 'electron.exe'
|
|
80
|
+
case 'darwin':
|
|
81
|
+
case 'mas': return 'Electron.app/Contents/MacOS/Electron'
|
|
82
|
+
default: return 'electron'
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Default cache roots used by @electron/get, per OS. `electron_config_cache`
|
|
87
|
+
// overrides them (same variable electron's own installer respects).
|
|
88
|
+
function electronCacheDirs() {
|
|
89
|
+
if (process.env.electron_config_cache) return [process.env.electron_config_cache]
|
|
90
|
+
const home = os.homedir()
|
|
91
|
+
if (process.platform === 'win32') {
|
|
92
|
+
return [path.join(process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'electron', 'Cache')]
|
|
93
|
+
}
|
|
94
|
+
if (process.platform === 'darwin') {
|
|
95
|
+
return [path.join(home, 'Library', 'Caches', 'electron')]
|
|
96
|
+
}
|
|
97
|
+
return [path.join(process.env.XDG_CACHE_HOME || path.join(home, '.cache'), 'electron')]
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Find a fully-downloaded electron zip for this version/platform/arch in the
|
|
101
|
+
// cache. Entries live in hash-named subdirectories; a real zip is >20 MB —
|
|
102
|
+
// anything smaller is a truncated download or a proxy's HTML block page.
|
|
103
|
+
function findCachedElectronZip(version) {
|
|
104
|
+
const wanted = `electron-v${version}-${process.platform}-${process.arch}.zip`
|
|
105
|
+
for (const root of electronCacheDirs()) {
|
|
106
|
+
let entries
|
|
107
|
+
try { entries = fs.readdirSync(root) } catch { continue }
|
|
108
|
+
for (const entry of ['', ...entries]) {
|
|
109
|
+
const candidate = path.join(root, entry, wanted)
|
|
110
|
+
try {
|
|
111
|
+
if (fs.statSync(candidate).size > 20 * 1024 * 1024) return candidate
|
|
112
|
+
} catch { /* not there — keep looking */ }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Extract with OS-native tools: PowerShell on Windows, ditto on macOS (it
|
|
119
|
+
// preserves the symlinks inside Electron.app, plain unzip does not), unzip on
|
|
120
|
+
// Linux. Deliberately NOT extract-zip — when we get here, that path already
|
|
121
|
+
// failed once on this machine.
|
|
122
|
+
function extractZipNative(zip, destDir) {
|
|
123
|
+
let r
|
|
124
|
+
if (process.platform === 'win32') {
|
|
125
|
+
r = spawnSync('powershell.exe', [
|
|
126
|
+
'-NoProfile', '-NonInteractive', '-Command',
|
|
127
|
+
`Expand-Archive -LiteralPath "${zip}" -DestinationPath "${destDir}" -Force`,
|
|
128
|
+
], { stdio: 'ignore' })
|
|
129
|
+
} else if (process.platform === 'darwin') {
|
|
130
|
+
r = spawnSync('ditto', ['-x', '-k', zip, destDir], { stdio: 'ignore' })
|
|
131
|
+
} else {
|
|
132
|
+
r = spawnSync('unzip', ['-o', '-q', zip, '-d', destDir], { stdio: 'ignore' })
|
|
133
|
+
}
|
|
134
|
+
return Boolean(r && r.status === 0)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Attempt to rebuild node_modules/electron/dist from a cached zip.
|
|
138
|
+
// Returns 'repaired', 'no-zip', or a { zip } object when extraction failed.
|
|
139
|
+
function tryRepairElectron() {
|
|
140
|
+
let pkgPath
|
|
141
|
+
try { pkgPath = require.resolve('electron/package.json') } catch { return 'no-zip' }
|
|
142
|
+
const electronDir = path.dirname(pkgPath)
|
|
143
|
+
const version = require(pkgPath).version
|
|
144
|
+
const zip = findCachedElectronZip(version)
|
|
145
|
+
if (!zip) return 'no-zip'
|
|
146
|
+
|
|
147
|
+
console.error(` Electron ${version} was already downloaded — repairing the`)
|
|
148
|
+
console.error(' installation from the local cache...')
|
|
149
|
+
const distDir = path.join(electronDir, 'dist')
|
|
150
|
+
try { fs.rmSync(distDir, { recursive: true, force: true }) } catch { /* best effort */ }
|
|
151
|
+
if (!extractZipNative(zip, distDir) || !fs.existsSync(path.join(distDir, electronPlatformPath()))) {
|
|
152
|
+
return { zip }
|
|
153
|
+
}
|
|
154
|
+
fs.writeFileSync(path.join(electronDir, 'path.txt'), electronPlatformPath())
|
|
155
|
+
console.error(' Repaired.')
|
|
156
|
+
console.error('')
|
|
157
|
+
return 'repaired'
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Resolve the electron executable, classifying the failure modes.
|
|
161
|
+
function loadElectron() {
|
|
71
162
|
try {
|
|
72
|
-
electron = require('electron')
|
|
163
|
+
const electron = require('electron')
|
|
164
|
+
// path.txt can exist while dist/ is incomplete (interrupted extraction) —
|
|
165
|
+
// require() succeeds but points at a binary that isn't there.
|
|
166
|
+
if (typeof electron === 'string' && !fs.existsSync(electron)) {
|
|
167
|
+
return { status: 'binary-missing' }
|
|
168
|
+
}
|
|
169
|
+
return { status: 'ok', electron }
|
|
73
170
|
} catch (err) {
|
|
74
171
|
const msg = err && err.message ? err.message : String(err)
|
|
75
|
-
|
|
76
|
-
|
|
172
|
+
if (/Cannot find module 'electron'/i.test(msg)) return { status: 'not-installed' }
|
|
173
|
+
if (/Electron failed to install correctly/i.test(msg)) return { status: 'binary-missing' }
|
|
174
|
+
return { status: 'error', message: msg }
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const TROUBLESHOOTING_URL =
|
|
179
|
+
'https://github.com/testsmith-io/api-spector/blob/main/docs/getting-started/troubleshooting.md'
|
|
180
|
+
|
|
181
|
+
// ui: spawn electron with the app dir
|
|
182
|
+
if (command.runner === 'electron') {
|
|
183
|
+
let loaded = loadElectron()
|
|
184
|
+
let failedZip = null
|
|
185
|
+
|
|
186
|
+
if (loaded.status === 'binary-missing') {
|
|
187
|
+
const repair = tryRepairElectron()
|
|
188
|
+
if (repair === 'repaired') {
|
|
189
|
+
loaded = loadElectron()
|
|
190
|
+
} else if (repair && repair.zip) {
|
|
191
|
+
failedZip = repair.zip
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (loaded.status !== 'ok') {
|
|
77
196
|
console.error('')
|
|
78
197
|
console.error(' API Spector — failed to launch the UI.')
|
|
79
198
|
console.error('')
|
|
80
|
-
if (
|
|
199
|
+
if (loaded.status === 'not-installed') {
|
|
81
200
|
console.error(' The electron package is not installed alongside API Spector.')
|
|
82
201
|
console.error(' Versions 0.3.1 and 0.3.2 shipped without it by mistake.')
|
|
83
202
|
console.error('')
|
|
@@ -89,39 +208,61 @@ if (command.runner === 'electron') {
|
|
|
89
208
|
console.error('')
|
|
90
209
|
console.error(' 2. Or keep this version and install electron yourself:')
|
|
91
210
|
console.error(' npm install -D electron@31')
|
|
211
|
+
} else if (loaded.status === 'binary-missing') {
|
|
212
|
+
let electronDir = null
|
|
213
|
+
let version = '<version>'
|
|
214
|
+
try {
|
|
215
|
+
const pkgPath = require.resolve('electron/package.json')
|
|
216
|
+
electronDir = path.dirname(pkgPath)
|
|
217
|
+
version = require(pkgPath).version
|
|
218
|
+
} catch { /* keep placeholders */ }
|
|
219
|
+
const zipName = `electron-v${version}-${process.platform}-${process.arch}.zip`
|
|
220
|
+
|
|
221
|
+
if (failedZip) {
|
|
222
|
+
console.error(' Electron\'s binary is missing. A downloaded copy exists at')
|
|
223
|
+
console.error(` ${failedZip}`)
|
|
224
|
+
console.error(' but it could not be extracted — the file may be corrupt (delete')
|
|
225
|
+
console.error(' it and reinstall), or antivirus is blocking the extraction.')
|
|
226
|
+
} else {
|
|
227
|
+
console.error(' Electron is installed, but its platform binary is missing and no')
|
|
228
|
+
console.error(' usable download was found in the local cache. The download during')
|
|
229
|
+
console.error(' `npm install` was probably blocked.')
|
|
230
|
+
console.error('')
|
|
231
|
+
console.error(' Common causes on corporate machines:')
|
|
232
|
+
console.error('')
|
|
233
|
+
console.error(' - Proxy blocks github.com downloads. Note: npm\'s proxy settings')
|
|
234
|
+
console.error(' do NOT apply to electron\'s downloader — it needs:')
|
|
235
|
+
console.error(' ELECTRON_GET_USE_PROXY=1')
|
|
236
|
+
console.error(' GLOBAL_AGENT_HTTPS_PROXY=http://your-proxy:port')
|
|
237
|
+
console.error(' then: npm install -D @testsmith/api-spector --force')
|
|
238
|
+
console.error('')
|
|
239
|
+
console.error(' - TLS-intercepting proxy (certificate errors): point Node at')
|
|
240
|
+
console.error(' your corporate root CA:')
|
|
241
|
+
console.error(' NODE_EXTRA_CA_CERTS=/path/to/corporate-root-ca.pem')
|
|
242
|
+
console.error('')
|
|
243
|
+
console.error(' - ELECTRON_SKIP_BINARY_DOWNLOAD=1 set machine-wide (some IT')
|
|
244
|
+
console.error(' images do this) — unset it and reinstall.')
|
|
245
|
+
}
|
|
92
246
|
console.error('')
|
|
93
|
-
console.error('
|
|
94
|
-
console.error(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
console.error(
|
|
98
|
-
console.error(
|
|
99
|
-
console.error(' firewall blocking github.com / electronjs.org).')
|
|
100
|
-
console.error('')
|
|
101
|
-
console.error(' Fix options (try in order):')
|
|
102
|
-
console.error('')
|
|
103
|
-
console.error(' 1. Reinstall and force the postinstall script to run:')
|
|
104
|
-
console.error(' npm install -D @testsmith/api-spector --force')
|
|
105
|
-
console.error(' (use -g instead of -D if you installed globally)')
|
|
106
|
-
console.error('')
|
|
107
|
-
console.error(' 2. Behind a proxy? Set npm + electron mirrors and reinstall:')
|
|
108
|
-
console.error(' npm config set proxy http://your-proxy:port')
|
|
109
|
-
console.error(' npm config set https-proxy http://your-proxy:port')
|
|
110
|
-
console.error(' set ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/')
|
|
111
|
-
console.error(' npm install -D @testsmith/api-spector --force')
|
|
112
|
-
console.error('')
|
|
113
|
-
console.error(' 3. Re-run electron\'s postinstall manually:')
|
|
114
|
-
console.error(` cd "${path.join(installDir, 'node_modules', 'electron')}"`)
|
|
115
|
-
console.error(' node install.js')
|
|
116
|
-
console.error('')
|
|
117
|
-
console.error(' CLI subcommands (run / mock / record / contract / wsdl) do not')
|
|
118
|
-
console.error(' need the UI binary and should work even while this is broken.')
|
|
247
|
+
console.error(' Manual fix (works without any of the above): download')
|
|
248
|
+
console.error(` https://github.com/electron/electron/releases/download/v${version}/${zipName}`)
|
|
249
|
+
console.error(' in a browser, extract ALL of it into:')
|
|
250
|
+
console.error(` ${electronDir ? path.join(electronDir, 'dist') : '<node_modules>/electron/dist'}`)
|
|
251
|
+
console.error(` and create a file "path.txt" next to "dist" containing exactly:`)
|
|
252
|
+
console.error(` ${electronPlatformPath()}`)
|
|
119
253
|
} else {
|
|
120
|
-
console.error(` ${
|
|
254
|
+
console.error(` ${loaded.message}`)
|
|
121
255
|
}
|
|
122
256
|
console.error('')
|
|
257
|
+
console.error(' CLI subcommands (run / mock / record / contract / wsdl) do not')
|
|
258
|
+
console.error(' need the UI binary and work even while this is broken.')
|
|
259
|
+
console.error('')
|
|
260
|
+
console.error(` Full troubleshooting guide: ${TROUBLESHOOTING_URL}`)
|
|
261
|
+
console.error('')
|
|
123
262
|
process.exit(1)
|
|
124
263
|
}
|
|
264
|
+
|
|
265
|
+
const electron = loaded.electron
|
|
125
266
|
const appDir = path.join(__dirname, '..')
|
|
126
267
|
// Forward the user's cwd so the main process can decide whether to open a
|
|
127
268
|
// workspace in this folder, or fall through to the welcome screen. Without
|
package/out/main/index.js
CHANGED
|
@@ -1015,7 +1015,7 @@ function buildAuth(security, securitySchemes) {
|
|
|
1015
1015
|
}
|
|
1016
1016
|
return { type: "none" };
|
|
1017
1017
|
}
|
|
1018
|
-
const HTTP_METHODS$1 = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
1018
|
+
const HTTP_METHODS$1 = ["get", "post", "put", "patch", "delete", "head", "options", "query"];
|
|
1019
1019
|
function buildCollection(spec) {
|
|
1020
1020
|
const info = spec.info ?? {};
|
|
1021
1021
|
const servers = spec.servers ?? [{}];
|
|
@@ -1262,7 +1262,7 @@ function parseKv(blockContent) {
|
|
|
1262
1262
|
}
|
|
1263
1263
|
return result;
|
|
1264
1264
|
}
|
|
1265
|
-
const HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
1265
|
+
const HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options", "query"];
|
|
1266
1266
|
function parseBruFile(content, fileName) {
|
|
1267
1267
|
const meta = parseKv(extractBlock(content, "meta"));
|
|
1268
1268
|
let method = "GET";
|
|
@@ -1375,7 +1375,7 @@ async function importBruno(filePath) {
|
|
|
1375
1375
|
requests
|
|
1376
1376
|
};
|
|
1377
1377
|
}
|
|
1378
|
-
const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
1378
|
+
const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "QUERY"];
|
|
1379
1379
|
const HTTP_TO_SPECTOR = {
|
|
1380
1380
|
$guid: "$uuid",
|
|
1381
1381
|
$randomInt: "$randomInt",
|
|
@@ -1734,6 +1734,13 @@ function renderTree(paths) {
|
|
|
1734
1734
|
}
|
|
1735
1735
|
return [".", ...render(root)].join("\n");
|
|
1736
1736
|
}
|
|
1737
|
+
const PLAYWRIGHT_VERBS = ["get", "post", "put", "patch", "delete", "head"];
|
|
1738
|
+
const SUPERTEST_VERBS = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
1739
|
+
function restAssuredCall(method, pathArg) {
|
|
1740
|
+
const verbs = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
1741
|
+
return verbs.includes(method) ? `.${method}(${pathArg})` : `.request("${method.toUpperCase()}", ${pathArg})`;
|
|
1742
|
+
}
|
|
1743
|
+
const ROBOT_REQUESTS_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
1737
1744
|
function safeName(name) {
|
|
1738
1745
|
return name.replace(/[^\w\s]/g, " ").split(/\s+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
1739
1746
|
}
|
|
@@ -1818,6 +1825,12 @@ function buildKeywordsFile(collection, varMap, nameMap, hookExtractedVars) {
|
|
|
1818
1825
|
const method = req.method.charAt(0) + req.method.slice(1).toLowerCase();
|
|
1819
1826
|
lines.push(kwName);
|
|
1820
1827
|
lines.push(` [Documentation] Hook: ${req.hookType} — ${req.name}`);
|
|
1828
|
+
if (!ROBOT_REQUESTS_METHODS.includes(req.method)) {
|
|
1829
|
+
lines.push(` Log ${req.method} is not supported by robotframework-requests — hook skipped WARN`);
|
|
1830
|
+
lines.push(` RETURN \${None}`);
|
|
1831
|
+
lines.push("");
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1821
1834
|
const { body } = req;
|
|
1822
1835
|
if (hasBody(req) && body.mode === "json" && body.json) {
|
|
1823
1836
|
const bodyPairs = jsonToRfDictPairs(body.json, varMap);
|
|
@@ -1853,6 +1866,12 @@ function buildKeywordsFile(collection, varMap, nameMap, hookExtractedVars) {
|
|
|
1853
1866
|
const url = interpolate(req.url, varMap);
|
|
1854
1867
|
lines.push(kwName);
|
|
1855
1868
|
lines.push(` [Documentation] ${req.description || req.name}`);
|
|
1869
|
+
if (!ROBOT_REQUESTS_METHODS.includes(req.method)) {
|
|
1870
|
+
lines.push(` Log ${req.method} is not supported by robotframework-requests — request skipped WARN`);
|
|
1871
|
+
lines.push(` RETURN \${None}`);
|
|
1872
|
+
lines.push("");
|
|
1873
|
+
continue;
|
|
1874
|
+
}
|
|
1856
1875
|
const inherited = requestCollection.resolveInheritedAuthAndHeaders(reqId, collection);
|
|
1857
1876
|
const effectiveAuth = resolveEffectiveAuth(req, inherited);
|
|
1858
1877
|
const allHeaders = mergeHeaders(req, inherited);
|
|
@@ -2084,12 +2103,15 @@ function buildHookLines$1(req, sharedVars) {
|
|
|
2084
2103
|
} catch {
|
|
2085
2104
|
}
|
|
2086
2105
|
}
|
|
2106
|
+
const nativeVerb = PLAYWRIGHT_VERBS.includes(method);
|
|
2107
|
+
if (!nativeVerb) optionParts.unshift(`method: '${req.method}'`);
|
|
2087
2108
|
const opts = optionParts.length ? `, { ${optionParts.join(", ")} }` : "";
|
|
2109
|
+
const hookCall = nativeVerb ? `request.${method}(${pathExpr}${opts})` : `request.fetch(${pathExpr}${opts})`;
|
|
2088
2110
|
const lines = [];
|
|
2089
2111
|
lines.push(` // ${req.name}`);
|
|
2090
2112
|
const parsed = parsePostScript(req.postRequestScript);
|
|
2091
2113
|
if (parsed.extractions.length > 0) {
|
|
2092
|
-
lines.push(` const hookResponse = await
|
|
2114
|
+
lines.push(` const hookResponse = await ${hookCall};`);
|
|
2093
2115
|
lines.push(` const hookJson = await hookResponse.json();`);
|
|
2094
2116
|
for (const e of parsed.extractions) {
|
|
2095
2117
|
const jsonPath = e.accessor.replace(/^json\.?/, "");
|
|
@@ -2099,7 +2121,7 @@ function buildHookLines$1(req, sharedVars) {
|
|
|
2099
2121
|
lines.push(` ${varName} = String(${expr});`);
|
|
2100
2122
|
}
|
|
2101
2123
|
} else {
|
|
2102
|
-
lines.push(` await
|
|
2124
|
+
lines.push(` await ${hookCall};`);
|
|
2103
2125
|
}
|
|
2104
2126
|
return lines;
|
|
2105
2127
|
}
|
|
@@ -2199,13 +2221,16 @@ ${lines.join("\n")}
|
|
|
2199
2221
|
optionParts.push(` data: \`${interpolateEnvVars(req.body.json, sharedVars)}\``);
|
|
2200
2222
|
}
|
|
2201
2223
|
}
|
|
2224
|
+
const nativeVerb = PLAYWRIGHT_VERBS.includes(method);
|
|
2225
|
+
if (!nativeVerb) optionParts.unshift(` method: '${req.method}'`);
|
|
2202
2226
|
const optionsStr = optionParts.length ? `, {
|
|
2203
2227
|
${optionParts.join(",\n")},
|
|
2204
2228
|
}` : "";
|
|
2229
|
+
const callExpr = nativeVerb ? `request.${method}(${pathExpr}${optionsStr})` : `request.fetch(${pathExpr}${optionsStr})`;
|
|
2205
2230
|
const parsed = parsePostScript(req.postRequestScript);
|
|
2206
2231
|
const lines = [
|
|
2207
2232
|
` test('${testName}', async ({ request }) => {`,
|
|
2208
|
-
` const response = await
|
|
2233
|
+
` const response = await ${callExpr};`
|
|
2209
2234
|
];
|
|
2210
2235
|
const needsJson = parsed.assertions.some((a) => a.accessor.startsWith("json")) || parsed.extractions.length > 0;
|
|
2211
2236
|
if (needsJson) {
|
|
@@ -2364,11 +2389,14 @@ function buildHookLines(req, sharedVars) {
|
|
|
2364
2389
|
} catch {
|
|
2365
2390
|
}
|
|
2366
2391
|
}
|
|
2392
|
+
const nativeVerb = PLAYWRIGHT_VERBS.includes(method);
|
|
2393
|
+
if (!nativeVerb) optParts.unshift(`method: '${req.method}'`);
|
|
2367
2394
|
const opts = optParts.length ? `, { ${optParts.join(", ")} }` : "";
|
|
2395
|
+
const hookCall = nativeVerb ? `request.${method}(${pathExpr}${opts})` : `request.fetch(${pathExpr}${opts})`;
|
|
2368
2396
|
const lines = [` // ${req.name}`];
|
|
2369
2397
|
const parsed = parsePostScript(req.postRequestScript);
|
|
2370
2398
|
if (parsed.extractions.length > 0) {
|
|
2371
|
-
lines.push(` const hookResponse = await
|
|
2399
|
+
lines.push(` const hookResponse = await ${hookCall};`);
|
|
2372
2400
|
lines.push(` const hookJson = await hookResponse.json();`);
|
|
2373
2401
|
for (const e of parsed.extractions) {
|
|
2374
2402
|
const jp = e.accessor.replace(/^json\.?/, "");
|
|
@@ -2378,7 +2406,7 @@ function buildHookLines(req, sharedVars) {
|
|
|
2378
2406
|
lines.push(` ${varName} = String(${expr});`);
|
|
2379
2407
|
}
|
|
2380
2408
|
} else {
|
|
2381
|
-
lines.push(` await
|
|
2409
|
+
lines.push(` await ${hookCall};`);
|
|
2382
2410
|
}
|
|
2383
2411
|
return lines;
|
|
2384
2412
|
}
|
|
@@ -2476,13 +2504,16 @@ ${lines.join("\n")}
|
|
|
2476
2504
|
optionParts.push(` data: \`${interpolateEnvVars(req.body.json, sharedVars)}\``);
|
|
2477
2505
|
}
|
|
2478
2506
|
}
|
|
2507
|
+
const nativeVerb = PLAYWRIGHT_VERBS.includes(method);
|
|
2508
|
+
if (!nativeVerb) optionParts.unshift(` method: '${req.method}'`);
|
|
2479
2509
|
const optionsStr = optionParts.length ? `, {
|
|
2480
2510
|
${optionParts.join(",\n")},
|
|
2481
2511
|
}` : "";
|
|
2512
|
+
const callExpr = nativeVerb ? `request.${method}(${pathExpr}${optionsStr})` : `request.fetch(${pathExpr}${optionsStr})`;
|
|
2482
2513
|
const parsed = parsePostScript(req.postRequestScript);
|
|
2483
2514
|
const lines = [
|
|
2484
2515
|
` test('${testName}', async ({ request }) => {`,
|
|
2485
|
-
` const response = await
|
|
2516
|
+
` const response = await ${callExpr};`
|
|
2486
2517
|
];
|
|
2487
2518
|
const needsJson = parsed.assertions.some((a) => a.accessor.startsWith("json")) || parsed.extractions.length > 0;
|
|
2488
2519
|
if (needsJson) {
|
|
@@ -2643,6 +2674,10 @@ function buildTestFile$1(folderName, folder, collection) {
|
|
|
2643
2674
|
function buildSupertestHookLines(h) {
|
|
2644
2675
|
const lines = [` // ${h.name}`];
|
|
2645
2676
|
const method = h.method.toLowerCase();
|
|
2677
|
+
if (!SUPERTEST_VERBS.includes(method)) {
|
|
2678
|
+
lines.push(` // ${h.method} (RFC 10008) is not supported by supertest -- hook skipped`);
|
|
2679
|
+
return lines;
|
|
2680
|
+
}
|
|
2646
2681
|
const path2 = h.url.replace(/^https?:\/\/[^/]+/, "") || "/";
|
|
2647
2682
|
const parsed = parsePostScript(h.postRequestScript);
|
|
2648
2683
|
if (parsed.extractions.length > 0) {
|
|
@@ -2687,6 +2722,13 @@ ${lines.join("\n")}
|
|
|
2687
2722
|
const allHeaders = mergeHeaders(req, inherited);
|
|
2688
2723
|
const enabledParams = req.params.filter((p) => p.enabled && p.key);
|
|
2689
2724
|
const lines = [];
|
|
2725
|
+
if (!SUPERTEST_VERBS.includes(method)) {
|
|
2726
|
+
lines.push(` // ${req.method} (RFC 10008) is not supported by supertest`);
|
|
2727
|
+
lines.push(` it.skip('${nameMap.get(reqId)} [${req.method} unsupported]', () => {});`);
|
|
2728
|
+
lines.push("");
|
|
2729
|
+
tests.push(...lines);
|
|
2730
|
+
continue;
|
|
2731
|
+
}
|
|
2690
2732
|
lines.push(` it('${nameMap.get(reqId)}', async () => {`);
|
|
2691
2733
|
lines.push(` const res = await api`);
|
|
2692
2734
|
lines.push(` .${method}(\`${path2}\`)`);
|
|
@@ -2896,6 +2938,10 @@ function buildTestFile(folderName, folder, collection) {
|
|
|
2896
2938
|
function buildJsHookLines(h) {
|
|
2897
2939
|
const lines = [` // ${h.name}`];
|
|
2898
2940
|
const method = h.method.toLowerCase();
|
|
2941
|
+
if (!SUPERTEST_VERBS.includes(method)) {
|
|
2942
|
+
lines.push(` // ${h.method} (RFC 10008) is not supported by supertest -- hook skipped`);
|
|
2943
|
+
return lines;
|
|
2944
|
+
}
|
|
2899
2945
|
const path2 = h.url.replace(/^https?:\/\/[^/]+/, "") || "/";
|
|
2900
2946
|
const parsed = parsePostScript(h.postRequestScript);
|
|
2901
2947
|
if (parsed.extractions.length > 0) {
|
|
@@ -2940,6 +2986,13 @@ ${lines.join("\n")}
|
|
|
2940
2986
|
const allHeaders = mergeHeaders(req, inherited);
|
|
2941
2987
|
const enabledParams = req.params.filter((p) => p.enabled && p.key);
|
|
2942
2988
|
const lines = [];
|
|
2989
|
+
if (!SUPERTEST_VERBS.includes(method)) {
|
|
2990
|
+
lines.push(` // ${req.method} (RFC 10008) is not supported by supertest`);
|
|
2991
|
+
lines.push(` it.skip('${nameMap.get(reqId)} [${req.method} unsupported]', () => {});`);
|
|
2992
|
+
lines.push("");
|
|
2993
|
+
tests.push(...lines);
|
|
2994
|
+
continue;
|
|
2995
|
+
}
|
|
2943
2996
|
lines.push(` it('${nameMap.get(reqId)}', async () => {`);
|
|
2944
2997
|
lines.push(` const res = await api`);
|
|
2945
2998
|
lines.push(` .${method}(\`${path2}\`)`);
|
|
@@ -3234,7 +3287,7 @@ function buildTestClass(folderName, folder, collection) {
|
|
|
3234
3287
|
const escaped = h.body.json.replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
3235
3288
|
lines.push(` .body("${escaped}")`);
|
|
3236
3289
|
}
|
|
3237
|
-
lines.push(` .when()
|
|
3290
|
+
lines.push(` .when()${restAssuredCall(method, `"${path2}"`)};`);
|
|
3238
3291
|
for (const e of parsed.extractions) {
|
|
3239
3292
|
const jp = accessorToJsonPath(e.accessor);
|
|
3240
3293
|
const varName = toEnvVar(e.varName);
|
|
@@ -3242,7 +3295,7 @@ function buildTestClass(folderName, folder, collection) {
|
|
|
3242
3295
|
lines.push(` ${varName} = hookResponse.jsonPath().getString("${jp}");`);
|
|
3243
3296
|
}
|
|
3244
3297
|
} else {
|
|
3245
|
-
lines.push(` given().spec(requestSpec).when()
|
|
3298
|
+
lines.push(` given().spec(requestSpec).when()${restAssuredCall(method, `"${path2}"`)};`);
|
|
3246
3299
|
}
|
|
3247
3300
|
return lines;
|
|
3248
3301
|
}
|
|
@@ -3318,7 +3371,7 @@ ${lines.join("\n")}
|
|
|
3318
3371
|
}
|
|
3319
3372
|
}
|
|
3320
3373
|
lines.push(` .when()`);
|
|
3321
|
-
lines.push(`
|
|
3374
|
+
lines.push(` ${restAssuredCall(method, javaPath)}`);
|
|
3322
3375
|
lines.push(` .then()`);
|
|
3323
3376
|
const parsed = parsePostScript(req.postRequestScript);
|
|
3324
3377
|
if (parsed.assertions.length > 0) {
|
package/out/main/runner.js
CHANGED
|
@@ -393,7 +393,7 @@ async function main() {
|
|
|
393
393
|
if (envName && !env) {
|
|
394
394
|
console.warn(cliCommon.color(`Warning: environment "${envName}" not found. Running without environment.`, cliCommon.C.yellow));
|
|
395
395
|
}
|
|
396
|
-
const version = `v${"0.3.
|
|
396
|
+
const version = `v${"0.3.5"}`;
|
|
397
397
|
console.log("");
|
|
398
398
|
console.log(cliCommon.color(" API Test Runner" + (version ? ` ${version}` : ""), cliCommon.C.bold, cliCommon.C.white));
|
|
399
399
|
console.log(cliCommon.color(` Workspace: ${wsPath}`, cliCommon.C.gray));
|