testronaut 1.0.10 β 1.0.12
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 +21 -1
- package/bin/cli.js +133 -4
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -4,6 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## π Join the Mission Control Community
|
|
8
|
+
|
|
9
|
+
Got questions, ideas, or want to share your missions?
|
|
10
|
+
Join our Discord to connect with other Testronauts, get support, and help shape the frameworkβs future.
|
|
11
|
+
|
|
12
|
+
[](https://discord.gg/pBfdef92ba)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
7
16
|
## π Features
|
|
8
17
|
|
|
9
18
|
- Run real-world UI flows using missions written as plain strings or functions
|
|
@@ -145,4 +154,15 @@ Now you can use testronaut in any local project.
|
|
|
145
154
|
### π License
|
|
146
155
|
MIT
|
|
147
156
|
|
|
148
|
-
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## β Support the Mission
|
|
161
|
+
π€ Built with β€οΈ by [Shane Fast](https://github.com/scfast)
|
|
162
|
+
|
|
163
|
+
If Testronaut has helped you save time or improve your testing, consider fueling the mission!
|
|
164
|
+
Your support helps cover hosting, development time, and the occasional coffee needed to debug at 2am.
|
|
165
|
+
|
|
166
|
+
[](https://buymeacoffee.com/testronaut)
|
|
167
|
+
|
|
168
|
+
---
|
package/bin/cli.js
CHANGED
|
@@ -5,6 +5,8 @@ import { fileURLToPath } from 'url';
|
|
|
5
5
|
import { initializeTestronautProject } from './init.js';
|
|
6
6
|
import { createWelcomeMission } from './createWelcomeMission.js';
|
|
7
7
|
import { generateHtmlReport } from '../tools/generateHtmlReport.js';
|
|
8
|
+
import inquirer from 'inquirer';
|
|
9
|
+
import fetch from 'node-fetch';
|
|
8
10
|
|
|
9
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
12
|
|
|
@@ -21,6 +23,8 @@ const HELP_TEXT = `
|
|
|
21
23
|
Usage:
|
|
22
24
|
npx testronaut Run all missions in the ./missions directory
|
|
23
25
|
npx testronaut <file> Run a specific mission file (e.g., login.mission.js)
|
|
26
|
+
npx testronaut login Log in and store session token
|
|
27
|
+
npx testronaut upload Upload the most recent report JSON
|
|
24
28
|
|
|
25
29
|
Options:
|
|
26
30
|
--init Scaffold project folders and a welcome mission
|
|
@@ -28,11 +32,11 @@ Options:
|
|
|
28
32
|
|
|
29
33
|
Examples:
|
|
30
34
|
npx testronaut
|
|
31
|
-
npx testronaut login
|
|
35
|
+
npx testronaut login
|
|
36
|
+
npx testronaut upload
|
|
32
37
|
npx testronaut --init
|
|
33
38
|
`;
|
|
34
39
|
|
|
35
|
-
|
|
36
40
|
if (args.includes('--init')) {
|
|
37
41
|
await initializeTestronautProject();
|
|
38
42
|
await createWelcomeMission();
|
|
@@ -44,6 +48,18 @@ if (args.includes('--help')) {
|
|
|
44
48
|
process.exit(0);
|
|
45
49
|
}
|
|
46
50
|
|
|
51
|
+
// Handle the login command
|
|
52
|
+
if (args.includes('login')) {
|
|
53
|
+
await handleLogin();
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Handle the upload command
|
|
58
|
+
if (args.includes('upload')) {
|
|
59
|
+
await uploadReport();
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
47
63
|
if (!fs.existsSync(missionsDir)) {
|
|
48
64
|
console.error('β No `missions` directory found.');
|
|
49
65
|
process.exit(1);
|
|
@@ -91,7 +107,6 @@ const flatMissions = allResults.flatMap(entry => {
|
|
|
91
107
|
}));
|
|
92
108
|
});
|
|
93
109
|
|
|
94
|
-
|
|
95
110
|
const report = {
|
|
96
111
|
runId,
|
|
97
112
|
startTime: startTime.toISOString(),
|
|
@@ -107,4 +122,118 @@ const report = {
|
|
|
107
122
|
const outputDir = './missions/mission_reports';
|
|
108
123
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
109
124
|
fs.writeFileSync(`${outputDir}/${runId}.json`, JSON.stringify(report, null, 2));
|
|
110
|
-
generateHtmlReport(report, `${outputDir}/${runId}.html`);
|
|
125
|
+
generateHtmlReport(report, `${outputDir}/${runId}.html`);
|
|
126
|
+
|
|
127
|
+
// Function to handle login and store session token
|
|
128
|
+
async function handleLogin() {
|
|
129
|
+
// Check if API key is provided as an argument
|
|
130
|
+
let apiKey = args.find(arg => arg.startsWith('--apiKey='))?.split('=')[1];
|
|
131
|
+
|
|
132
|
+
// If API key is not provided, prompt the user for it
|
|
133
|
+
if (!apiKey) {
|
|
134
|
+
const responses = await inquirer.prompt([
|
|
135
|
+
{
|
|
136
|
+
type: 'password',
|
|
137
|
+
name: 'apiKey',
|
|
138
|
+
message: 'Enter your API key:',
|
|
139
|
+
mask: '*'
|
|
140
|
+
}
|
|
141
|
+
]);
|
|
142
|
+
apiKey = responses.apiKey;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Create URLSearchParams to send as x-www-form-urlencoded
|
|
146
|
+
const formData = new URLSearchParams();
|
|
147
|
+
formData.append('apiKey', apiKey);
|
|
148
|
+
console.log('π Authenticating with API key...');
|
|
149
|
+
|
|
150
|
+
// Call your authentication endpoint to get a session token
|
|
151
|
+
try {
|
|
152
|
+
const response = await fetch('http://testronaut.app/api/user/cli', { // Replace with your actual URL
|
|
153
|
+
method: 'POST',
|
|
154
|
+
headers: {
|
|
155
|
+
'Content-Type': 'application/json',
|
|
156
|
+
},
|
|
157
|
+
body: formData.toString(),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const data = await response.json();
|
|
161
|
+
|
|
162
|
+
if (data.error) {
|
|
163
|
+
console.error('β Authentication failed:', data.error);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Define the config path
|
|
168
|
+
const configPath = path.resolve(process.cwd(), 'testronaut-config.json');
|
|
169
|
+
|
|
170
|
+
// Read the existing config file
|
|
171
|
+
let config = {};
|
|
172
|
+
if (fs.existsSync(configPath)) {
|
|
173
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Overwrite or add the sessionToken field
|
|
177
|
+
config.sessionToken = data.sessionToken;
|
|
178
|
+
|
|
179
|
+
// Write the updated config back to testronaut-config.json
|
|
180
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
|
|
181
|
+
console.log('β
Login successful! Session token saved to testronaut-config.json.');
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.error('β Error during login:', error);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Upload the most recent report
|
|
189
|
+
async function uploadReport() {
|
|
190
|
+
const configPath = path.resolve(process.cwd(), 'testronaut-config.json');
|
|
191
|
+
if (!fs.existsSync(configPath)) {
|
|
192
|
+
console.error('β Configuration file not found.');
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
197
|
+
const sessionToken = config.sessionToken;
|
|
198
|
+
|
|
199
|
+
if (!sessionToken) {
|
|
200
|
+
console.error('β No session token found.');
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Get the most recent report from the 'missions/mission_reports/' directory
|
|
205
|
+
const reportDir = path.resolve(process.cwd(), 'missions/mission_reports');
|
|
206
|
+
const files = fs.readdirSync(reportDir).filter(file => file.endsWith('.json'));
|
|
207
|
+
|
|
208
|
+
if (files.length === 0) {
|
|
209
|
+
console.error('β No report files found.');
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Sort files by timestamp (most recent first)
|
|
214
|
+
files.sort((a, b) => parseInt(b.split('_')[1]) - parseInt(a.split('_')[1]));
|
|
215
|
+
const latestReport = path.join(reportDir, files[0]);
|
|
216
|
+
|
|
217
|
+
// Read the content of the most recent report
|
|
218
|
+
const reportData = fs.readFileSync(latestReport, 'utf8');
|
|
219
|
+
|
|
220
|
+
// Upload the report using the session token
|
|
221
|
+
try {
|
|
222
|
+
const response = await fetch(`http://testronaut.app/api/user/cli/${sessionToken}`, { // Replace with your API endpoint
|
|
223
|
+
method: 'POST',
|
|
224
|
+
headers: {
|
|
225
|
+
'Content-Type': 'application/json',
|
|
226
|
+
},
|
|
227
|
+
body: reportData, // Sending the report JSON as the request body
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const data = await response.json();
|
|
231
|
+
if (data.error) {
|
|
232
|
+
console.error('β Failed to upload the report:', data.error);
|
|
233
|
+
} else {
|
|
234
|
+
console.log('β
Report uploaded successfully!');
|
|
235
|
+
}
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error('β Error during upload:', error);
|
|
238
|
+
}
|
|
239
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testronaut",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"main": "./runner/testronaut.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"testronaut": "./bin/cli.js"
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
"@dqbd/tiktoken": "^1.0.21",
|
|
23
23
|
"cheerio": "^1.1.0",
|
|
24
24
|
"dotenv": "^17.2.0",
|
|
25
|
+
"inquirer": "^12.9.1",
|
|
26
|
+
"node-fetch": "^3.3.2",
|
|
25
27
|
"openai": "^5.10.2",
|
|
26
28
|
"playwright": "^1.54.1",
|
|
27
29
|
"prompts": "^2.4.2"
|