@testflowkit/cli 3.0.0 → 3.1.0
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/README.md +12 -12
- package/lib/install.js +133 -41
- package/package.json +6 -3
- package/lib/index.js +0 -48
package/README.md
CHANGED
|
@@ -43,20 +43,20 @@ tkit --version
|
|
|
43
43
|
|
|
44
44
|
## Configuration
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
mode: frontend
|
|
50
|
-
browser:
|
|
51
|
-
type: chromium
|
|
52
|
-
headless: true
|
|
53
|
-
features:
|
|
54
|
-
- path: ./features
|
|
55
|
-
```
|
|
46
|
+
The `tkit init` command generates a `config.yml` file in your project root. You can edit it to match your needs.
|
|
47
|
+
|
|
48
|
+
## 📚 Documentation
|
|
56
49
|
|
|
57
|
-
|
|
50
|
+
For comprehensive documentation, guides, examples, and best practices, please visit the **[TestFlowKit Web Documentation](https://testflowkit.github.io/testflowkit/)** on GitHub Pages.
|
|
58
51
|
|
|
59
|
-
|
|
52
|
+
The documentation includes:
|
|
53
|
+
- Getting Started Guide
|
|
54
|
+
- Configuration Options
|
|
55
|
+
- Step Definitions & Sentences
|
|
56
|
+
- Variable System
|
|
57
|
+
- API Testing
|
|
58
|
+
- Advanced Features
|
|
59
|
+
- FAQ & Troubleshooting
|
|
60
60
|
|
|
61
61
|
## Supported Platforms
|
|
62
62
|
|
package/lib/install.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const https = require("https");
|
|
4
|
+
const http = require("http");
|
|
5
|
+
|
|
4
6
|
const fs = require("fs");
|
|
5
7
|
const path = require("path");
|
|
8
|
+
const os = require("os");
|
|
6
9
|
const AdmZip = require("adm-zip");
|
|
7
|
-
|
|
8
10
|
const PACKAGE_JSON = require("../package.json");
|
|
9
11
|
const VERSION = PACKAGE_JSON.version;
|
|
10
12
|
const BINARY_NAME = "tkit";
|
|
@@ -58,35 +60,9 @@ function downloadFile(url, maxRedirects = 5) {
|
|
|
58
60
|
return;
|
|
59
61
|
}
|
|
60
62
|
|
|
61
|
-
const protocol = url.startsWith("https") ? https :
|
|
62
|
-
|
|
63
|
-
protocol
|
|
64
|
-
.get(url, (response) => {
|
|
65
|
-
// Handle redirects (GitHub releases use redirects)
|
|
66
|
-
if (
|
|
67
|
-
response.statusCode >= 300 &&
|
|
68
|
-
response.statusCode < 400 &&
|
|
69
|
-
response.headers.location
|
|
70
|
-
) {
|
|
71
|
-
downloadFile(response.headers.location, maxRedirects - 1)
|
|
72
|
-
.then(resolve)
|
|
73
|
-
.catch(reject);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (response.statusCode !== 200) {
|
|
78
|
-
reject(
|
|
79
|
-
new Error(`Failed to download: HTTP ${response.statusCode}`)
|
|
80
|
-
);
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const chunks = [];
|
|
85
|
-
response.on("data", (chunk) => chunks.push(chunk));
|
|
86
|
-
response.on("end", () => resolve(Buffer.concat(chunks)));
|
|
87
|
-
response.on("error", reject);
|
|
88
|
-
})
|
|
89
|
-
.on("error", reject);
|
|
63
|
+
const protocol = url.startsWith("https") ? https : http;
|
|
64
|
+
|
|
65
|
+
protocol.get(url, (response) => handleDownloadResponse(response, resolve, reject, maxRedirects)).on("error", reject);
|
|
90
66
|
});
|
|
91
67
|
}
|
|
92
68
|
|
|
@@ -102,6 +78,62 @@ async function extractZip(zipBuffer, destDir) {
|
|
|
102
78
|
}
|
|
103
79
|
}
|
|
104
80
|
|
|
81
|
+
/**
|
|
82
|
+
*
|
|
83
|
+
* @param {http.IncomingMessage} response
|
|
84
|
+
* @returns
|
|
85
|
+
*/
|
|
86
|
+
function handleDownloadResponse(response, resolve, reject, maxRedirects) {
|
|
87
|
+
// Handle redirects (GitHub releases use redirects)
|
|
88
|
+
const mustHandleRedirects =
|
|
89
|
+
response.statusCode >= 300 &&
|
|
90
|
+
response.statusCode < 400 &&
|
|
91
|
+
response.headers.location;
|
|
92
|
+
|
|
93
|
+
if (mustHandleRedirects) {
|
|
94
|
+
downloadFile(response.headers.location, maxRedirects - 1)
|
|
95
|
+
.then(resolve)
|
|
96
|
+
.catch(reject);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (response.statusCode !== 200) {
|
|
101
|
+
reject(new Error(`Failed to download: HTTP ${response.statusCode}`));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const chunks = [];
|
|
106
|
+
const totalBytes = parseInt(response.headers["content-length"] || "0", 10);
|
|
107
|
+
let downloadedBytes = 0;
|
|
108
|
+
let nextLogPercent = 0;
|
|
109
|
+
|
|
110
|
+
if (totalBytes > 0) {
|
|
111
|
+
console.log("Download progress: 0%");
|
|
112
|
+
nextLogPercent = 10;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
response.on("data", (chunk) => {
|
|
116
|
+
chunks.push(chunk);
|
|
117
|
+
downloadedBytes += chunk.length;
|
|
118
|
+
|
|
119
|
+
if (totalBytes > 0) {
|
|
120
|
+
const percent = Math.floor((downloadedBytes / totalBytes) * 100);
|
|
121
|
+
while (percent >= nextLogPercent && nextLogPercent <= 100) {
|
|
122
|
+
console.log(`Download progress: ${nextLogPercent}%`);
|
|
123
|
+
nextLogPercent += 10;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
response.on("end", () => {
|
|
129
|
+
if (totalBytes <= 0 || nextLogPercent <= 100) {
|
|
130
|
+
console.log("Download progress: 100%");
|
|
131
|
+
}
|
|
132
|
+
resolve(Buffer.concat(chunks));
|
|
133
|
+
});
|
|
134
|
+
response.on("error", reject);
|
|
135
|
+
}
|
|
136
|
+
|
|
105
137
|
/**
|
|
106
138
|
* Make the binary executable (Unix only)
|
|
107
139
|
*/
|
|
@@ -111,6 +143,7 @@ function makeExecutable(filePath) {
|
|
|
111
143
|
}
|
|
112
144
|
}
|
|
113
145
|
|
|
146
|
+
|
|
114
147
|
/**
|
|
115
148
|
* Main installation function
|
|
116
149
|
*/
|
|
@@ -118,12 +151,7 @@ async function install() {
|
|
|
118
151
|
const binDir = path.join(__dirname, "..", "cli");
|
|
119
152
|
const binaryName = getBinaryName();
|
|
120
153
|
const binaryPath = path.join(binDir, binaryName);
|
|
121
|
-
|
|
122
|
-
// Check if binary already exists
|
|
123
|
-
if (fs.existsSync(binaryPath)) {
|
|
124
|
-
console.log(`✓ ${BINARY_NAME} binary already exists`);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
154
|
+
const binaryExists = fs.existsSync(binaryPath);
|
|
127
155
|
|
|
128
156
|
// Ensure bin directory exists
|
|
129
157
|
if (!fs.existsSync(binDir)) {
|
|
@@ -132,29 +160,69 @@ async function install() {
|
|
|
132
160
|
|
|
133
161
|
const downloadUrl = getDownloadUrl();
|
|
134
162
|
console.log(`Downloading ${BINARY_NAME} v${VERSION}...`);
|
|
135
|
-
console.log(
|
|
163
|
+
console.log(
|
|
164
|
+
` Platform: ${process.platform} (${PLATFORM_MAP[process.platform]})`,
|
|
165
|
+
);
|
|
136
166
|
console.log(` Architecture: ${process.arch} (${ARCH_MAP[process.arch]})`);
|
|
137
167
|
console.log(` URL: ${downloadUrl}`);
|
|
138
168
|
|
|
169
|
+
if (binaryExists) {
|
|
170
|
+
console.log(
|
|
171
|
+
`ℹ Existing ${BINARY_NAME} binary found, downloading new version...`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let tempDir;
|
|
139
176
|
try {
|
|
177
|
+
// Create temporary directory in system temp location
|
|
178
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `${BINARY_NAME}-`));
|
|
179
|
+
|
|
140
180
|
// Download the zip file
|
|
141
181
|
const zipBuffer = await downloadFile(downloadUrl);
|
|
142
182
|
console.log(`Downloaded ${(zipBuffer.length / 1024 / 1024).toFixed(2)} MB`);
|
|
143
183
|
|
|
144
|
-
// Extract to
|
|
184
|
+
// Extract to temporary directory first
|
|
145
185
|
console.log("Extracting...");
|
|
146
|
-
await extractZip(zipBuffer,
|
|
186
|
+
await extractZip(zipBuffer, tempDir);
|
|
187
|
+
|
|
188
|
+
// Get the extracted binary path from temp directory
|
|
189
|
+
const tempBinaryPath = path.join(tempDir, binaryName);
|
|
190
|
+
|
|
191
|
+
if (!fs.existsSync(tempBinaryPath)) {
|
|
192
|
+
throw new Error("Binary not found after extraction");
|
|
193
|
+
}
|
|
147
194
|
|
|
148
195
|
// Make executable
|
|
149
|
-
makeExecutable(
|
|
196
|
+
makeExecutable(tempBinaryPath);
|
|
197
|
+
|
|
198
|
+
// Remove old binary if it exists, then move new binary
|
|
199
|
+
if (binaryExists) {
|
|
200
|
+
try {
|
|
201
|
+
removeExistingBinary(binaryPath);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
// If removal fails, clean up temp and rethrow
|
|
204
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Move new binary from temp to final location
|
|
210
|
+
fs.renameSync(tempBinaryPath, binaryPath);
|
|
211
|
+
|
|
212
|
+
// Clean up temporary directory
|
|
213
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
150
214
|
|
|
151
215
|
// Verify installation
|
|
152
216
|
if (fs.existsSync(binaryPath)) {
|
|
153
217
|
console.log(`✓ Successfully installed ${BINARY_NAME} v${VERSION}`);
|
|
154
218
|
} else {
|
|
155
|
-
throw new Error("Binary not found after
|
|
219
|
+
throw new Error("Binary not found after installation");
|
|
156
220
|
}
|
|
157
221
|
} catch (error) {
|
|
222
|
+
// Clean up temp directory on error
|
|
223
|
+
if (tempDir && fs.existsSync(tempDir)) {
|
|
224
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
225
|
+
}
|
|
158
226
|
console.error(`✗ Failed to install ${BINARY_NAME}: ${error.message}`);
|
|
159
227
|
console.error("");
|
|
160
228
|
console.error("You can manually download the binary from:");
|
|
@@ -166,5 +234,29 @@ async function install() {
|
|
|
166
234
|
}
|
|
167
235
|
}
|
|
168
236
|
|
|
237
|
+
function removeExistingBinary(binaryPath) {
|
|
238
|
+
try {
|
|
239
|
+
fs.unlinkSync(binaryPath);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error(
|
|
242
|
+
`✗ Failed to remove existing ${BINARY_NAME} binary: ${error.message}`,
|
|
243
|
+
);
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
|
|
169
249
|
// Run installation
|
|
170
250
|
install();
|
|
251
|
+
|
|
252
|
+
// Export functions for testing
|
|
253
|
+
module.exports = {
|
|
254
|
+
install,
|
|
255
|
+
downloadFile,
|
|
256
|
+
extractZip,
|
|
257
|
+
makeExecutable,
|
|
258
|
+
removeExistingBinary,
|
|
259
|
+
getBinaryName,
|
|
260
|
+
getDownloadUrl,
|
|
261
|
+
handleDownloadResponse,
|
|
262
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testflowkit/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
"tkit": "bin/cli.js"
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
|
-
"postinstall": "node ./lib/install.js"
|
|
13
|
+
"postinstall": "node -e \"process.env.CI || require('./lib/install.js')\"",
|
|
14
|
+
"test": "node --test ./lib/install.*.test.js",
|
|
15
|
+
"test:unit": "node --test ./lib/install.test.js",
|
|
16
|
+
"test:integration": "node ./lib/install.integration.test.js"
|
|
14
17
|
},
|
|
15
18
|
"repository": {
|
|
16
19
|
"type": "git",
|
|
@@ -32,7 +35,7 @@
|
|
|
32
35
|
"bugs": {
|
|
33
36
|
"url": "https://github.com/TestFlowKit/testflowkit/issues"
|
|
34
37
|
},
|
|
35
|
-
"homepage": "https://github.
|
|
38
|
+
"homepage": "https://testflowkit.github.io/testflowkit",
|
|
36
39
|
"os": [
|
|
37
40
|
"darwin",
|
|
38
41
|
"linux",
|
package/lib/index.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @testflowkit/cli
|
|
3
|
-
*
|
|
4
|
-
* This package provides the TestFlowKit CLI (tkit) for running
|
|
5
|
-
* end-to-end tests using Gherkin syntax.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* npx @testflowkit/cli run
|
|
9
|
-
* npx @testflowkit/cli init
|
|
10
|
-
* npx @testflowkit/cli validate
|
|
11
|
-
* npx @testflowkit/cli --version
|
|
12
|
-
*
|
|
13
|
-
* For programmatic usage, you can get the path to the binary:
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const path = require("path");
|
|
17
|
-
const fs = require("fs");
|
|
18
|
-
|
|
19
|
-
const BINARY_NAME = process.platform === "win32" ? "tkit.exe" : "tkit";
|
|
20
|
-
const binaryPath = path.join(__dirname, "..", "cli", BINARY_NAME);
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Get the path to the tkit binary
|
|
24
|
-
* @returns {string} Absolute path to the tkit binary
|
|
25
|
-
* @throws {Error} If the binary is not found
|
|
26
|
-
*/
|
|
27
|
-
function getBinaryPath() {
|
|
28
|
-
if (!fs.existsSync(binaryPath)) {
|
|
29
|
-
throw new Error(
|
|
30
|
-
`tkit binary not found. Please reinstall @testflowkit/cli`
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
return binaryPath;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Check if the binary is installed
|
|
38
|
-
* @returns {boolean} True if the binary exists
|
|
39
|
-
*/
|
|
40
|
-
function isInstalled() {
|
|
41
|
-
return fs.existsSync(binaryPath);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
module.exports = {
|
|
45
|
-
getBinaryPath,
|
|
46
|
-
isInstalled,
|
|
47
|
-
binaryPath,
|
|
48
|
-
};
|