@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 CHANGED
@@ -43,20 +43,20 @@ tkit --version
43
43
 
44
44
  ## Configuration
45
45
 
46
- Create a `config.yml` file in your project root:
47
-
48
- ```yaml
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
- ## Documentation
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
- For full documentation, visit [TestFlowKit Documentation](https://testflowkit.github.io/testflowkit/testflowkit/).
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 : require("http");
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(` Platform: ${process.platform} (${PLATFORM_MAP[process.platform]})`);
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 bin directory
184
+ // Extract to temporary directory first
145
185
  console.log("Extracting...");
146
- await extractZip(zipBuffer, binDir);
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(binaryPath);
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 extraction");
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.0.0",
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.com/TestFlowKit/testflowkit#readme",
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
- };