@scenetest/scenes 0.1.2 → 0.2.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/dist/__tests__/markdown-scene.test.js +134 -2
- package/dist/__tests__/markdown-scene.test.js.map +1 -1
- package/dist/__tests__/selectors.test.d.ts +2 -0
- package/dist/__tests__/selectors.test.d.ts.map +1 -0
- package/dist/__tests__/selectors.test.js +165 -0
- package/dist/__tests__/selectors.test.js.map +1 -0
- package/dist/__tests__/swarm.test.js +1 -0
- package/dist/__tests__/swarm.test.js.map +1 -1
- package/dist/__tests__/team-manager.test.d.ts +2 -0
- package/dist/__tests__/team-manager.test.d.ts.map +1 -0
- package/dist/__tests__/team-manager.test.js +180 -0
- package/dist/__tests__/team-manager.test.js.map +1 -0
- package/dist/__tests__/warmup.test.d.ts +2 -0
- package/dist/__tests__/warmup.test.d.ts.map +1 -0
- package/dist/__tests__/warmup.test.js +170 -0
- package/dist/__tests__/warmup.test.js.map +1 -0
- package/dist/cli.js +37 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +21 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +54 -4
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/markdown-scene.d.ts +2 -0
- package/dist/markdown-scene.d.ts.map +1 -1
- package/dist/markdown-scene.js +24 -7
- package/dist/markdown-scene.js.map +1 -1
- package/dist/prompt-generator.d.ts +59 -0
- package/dist/prompt-generator.d.ts.map +1 -0
- package/dist/prompt-generator.js +517 -0
- package/dist/prompt-generator.js.map +1 -0
- package/dist/reactive.d.ts +2 -1
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +22 -53
- package/dist/reactive.js.map +1 -1
- package/dist/runner.d.ts +8 -2
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +70 -3
- package/dist/runner.js.map +1 -1
- package/dist/scene.d.ts +8 -1
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +6 -15
- package/dist/scene.js.map +1 -1
- package/dist/selectors.d.ts.map +1 -1
- package/dist/selectors.js +8 -39
- package/dist/selectors.js.map +1 -1
- package/dist/swarm.d.ts +1 -1
- package/dist/swarm.d.ts.map +1 -1
- package/dist/swarm.js +10 -1
- package/dist/swarm.js.map +1 -1
- package/dist/team-manager.d.ts +28 -5
- package/dist/team-manager.d.ts.map +1 -1
- package/dist/team-manager.js +94 -15
- package/dist/team-manager.js.map +1 -1
- package/dist/types.d.ts +92 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/warmup.d.ts +61 -0
- package/dist/warmup.d.ts.map +1 -0
- package/dist/warmup.js +186 -0
- package/dist/warmup.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Prompt Generator for Team/Seed Data Creation
|
|
3
|
+
*
|
|
4
|
+
* Analyzes the user's project and generates context-rich prompts that can be
|
|
5
|
+
* fed to an LLM (Claude, GPT, etc.) to generate team configurations and
|
|
6
|
+
* corresponding seed data for scenetest.
|
|
7
|
+
*
|
|
8
|
+
* The generator scans for:
|
|
9
|
+
* - User/account models (TypeScript types, Prisma schema, etc.)
|
|
10
|
+
* - Database schemas
|
|
11
|
+
* - Existing seeds
|
|
12
|
+
* - Existing actor team files (scenetest/actors/)
|
|
13
|
+
* - Existing scene specs (scenetest/scenes/)
|
|
14
|
+
* - Auth-related files
|
|
15
|
+
*/
|
|
16
|
+
import fs from 'fs';
|
|
17
|
+
import path from 'path';
|
|
18
|
+
import { glob } from 'glob';
|
|
19
|
+
/**
|
|
20
|
+
* Gather project context for prompt generation.
|
|
21
|
+
*
|
|
22
|
+
* Scans the project for relevant files that an LLM would need to understand
|
|
23
|
+
* the application's user model, data layer, and existing test setup.
|
|
24
|
+
*/
|
|
25
|
+
export async function gatherProjectContext(configPath) {
|
|
26
|
+
const cwd = process.cwd();
|
|
27
|
+
const projectName = path.basename(cwd);
|
|
28
|
+
// Try to load package.json
|
|
29
|
+
let packageJson;
|
|
30
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
31
|
+
if (fs.existsSync(pkgPath)) {
|
|
32
|
+
try {
|
|
33
|
+
packageJson = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Ignore parse errors
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Find scenetest config (new location: scenetest/config.ts)
|
|
40
|
+
const configFile = await findFile(cwd, [
|
|
41
|
+
'scenetest/config.ts',
|
|
42
|
+
'scenetest/config.js',
|
|
43
|
+
'scenetest/config.mjs',
|
|
44
|
+
]);
|
|
45
|
+
// Find existing actor team files (new location: scenetest/actors/)
|
|
46
|
+
const existingActors = await findFilesMatching(cwd, [
|
|
47
|
+
'scenetest/actors/*.ts',
|
|
48
|
+
'scenetest/actors/*.js',
|
|
49
|
+
'scenetest/actors/*.mjs',
|
|
50
|
+
]);
|
|
51
|
+
// Find user models (common patterns)
|
|
52
|
+
const userModels = await findFilesMatching(cwd, [
|
|
53
|
+
'**/models/user*.ts',
|
|
54
|
+
'**/models/User*.ts',
|
|
55
|
+
'**/types/user*.ts',
|
|
56
|
+
'**/types/User*.ts',
|
|
57
|
+
'**/schema/user*.ts',
|
|
58
|
+
'**/schema/User*.ts',
|
|
59
|
+
'**/entities/user*.ts',
|
|
60
|
+
'**/entities/User*.ts',
|
|
61
|
+
'**/prisma/schema.prisma',
|
|
62
|
+
'**/drizzle/schema*.ts',
|
|
63
|
+
'**/db/schema*.ts',
|
|
64
|
+
]);
|
|
65
|
+
// Find existing seeds
|
|
66
|
+
const existingSeeds = await findFilesMatching(cwd, [
|
|
67
|
+
'**/seeds/**/*.ts',
|
|
68
|
+
'**/seeds/**/*.js',
|
|
69
|
+
'**/seed/**/*.ts',
|
|
70
|
+
'**/seed/**/*.js',
|
|
71
|
+
'**/prisma/seed.ts',
|
|
72
|
+
'**/prisma/seed.js',
|
|
73
|
+
'**/db/seed*.ts',
|
|
74
|
+
'**/db/seed*.js',
|
|
75
|
+
]);
|
|
76
|
+
// Find existing scenes (to understand what roles are used)
|
|
77
|
+
const existingScenes = await findFilesMatching(cwd, ['scenetest/scenes/**/*.spec.ts', 'scenetest/scenes/**/*.spec.md'], 5);
|
|
78
|
+
// Find auth-related files
|
|
79
|
+
const authPatterns = await findFilesMatching(cwd, [
|
|
80
|
+
'**/auth/**/*.ts',
|
|
81
|
+
'**/auth.ts',
|
|
82
|
+
'**/authentication*.ts',
|
|
83
|
+
'**/login*.ts',
|
|
84
|
+
'**/middleware/auth*.ts',
|
|
85
|
+
]);
|
|
86
|
+
// Find database schema files
|
|
87
|
+
const dbSchema = await findFilesMatching(cwd, [
|
|
88
|
+
'**/prisma/schema.prisma',
|
|
89
|
+
'**/drizzle/schema*.ts',
|
|
90
|
+
'**/drizzle/*.ts',
|
|
91
|
+
'**/db/schema*.ts',
|
|
92
|
+
'**/schema.sql',
|
|
93
|
+
'**/migrations/*.sql',
|
|
94
|
+
]);
|
|
95
|
+
return {
|
|
96
|
+
projectName,
|
|
97
|
+
packageJson,
|
|
98
|
+
userModels,
|
|
99
|
+
existingSeeds,
|
|
100
|
+
existingActors,
|
|
101
|
+
existingScenes,
|
|
102
|
+
authPatterns,
|
|
103
|
+
dbSchema,
|
|
104
|
+
configFile,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async function findFile(cwd, patterns) {
|
|
108
|
+
for (const pattern of patterns) {
|
|
109
|
+
const fullPath = path.join(cwd, pattern);
|
|
110
|
+
if (fs.existsSync(fullPath)) {
|
|
111
|
+
try {
|
|
112
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
113
|
+
return { path: pattern, content: truncateContent(content) };
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Ignore read errors
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
async function findFilesMatching(cwd, patterns, maxFiles = 10) {
|
|
123
|
+
const results = [];
|
|
124
|
+
const seen = new Set();
|
|
125
|
+
for (const pattern of patterns) {
|
|
126
|
+
if (results.length >= maxFiles)
|
|
127
|
+
break;
|
|
128
|
+
try {
|
|
129
|
+
const files = await glob(pattern, {
|
|
130
|
+
cwd,
|
|
131
|
+
ignore: ['**/node_modules/**', '**/dist/**', '**/.git/**'],
|
|
132
|
+
absolute: false,
|
|
133
|
+
});
|
|
134
|
+
for (const file of files) {
|
|
135
|
+
if (results.length >= maxFiles)
|
|
136
|
+
break;
|
|
137
|
+
if (seen.has(file))
|
|
138
|
+
continue;
|
|
139
|
+
seen.add(file);
|
|
140
|
+
const fullPath = path.join(cwd, file);
|
|
141
|
+
try {
|
|
142
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
143
|
+
results.push({ path: file, content: truncateContent(content) });
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// Ignore read errors
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Ignore glob errors
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return results;
|
|
155
|
+
}
|
|
156
|
+
function truncateContent(content, maxLines = 150) {
|
|
157
|
+
const lines = content.split('\n');
|
|
158
|
+
if (lines.length <= maxLines)
|
|
159
|
+
return content;
|
|
160
|
+
return lines.slice(0, maxLines).join('\n') + `\n\n... (${lines.length - maxLines} more lines)`;
|
|
161
|
+
}
|
|
162
|
+
// ─── Prompt generators ──────────────────────────────────────────────────────
|
|
163
|
+
/**
|
|
164
|
+
* Generate an LLM prompt for team/actor generation
|
|
165
|
+
*/
|
|
166
|
+
export function generateTeamsPrompt(ctx) {
|
|
167
|
+
const sections = [];
|
|
168
|
+
sections.push(`# Generate Scenetest Actor Teams
|
|
169
|
+
|
|
170
|
+
I need help creating scenetest team configurations for my project.
|
|
171
|
+
|
|
172
|
+
## Project: ${ctx.projectName}
|
|
173
|
+
`);
|
|
174
|
+
if (ctx.packageJson) {
|
|
175
|
+
const deps = {
|
|
176
|
+
...ctx.packageJson.dependencies,
|
|
177
|
+
...ctx.packageJson.devDependencies,
|
|
178
|
+
};
|
|
179
|
+
const relevantDeps = Object.keys(deps).filter((d) => d.includes('prisma') ||
|
|
180
|
+
d.includes('drizzle') ||
|
|
181
|
+
d.includes('sequelize') ||
|
|
182
|
+
d.includes('typeorm') ||
|
|
183
|
+
d.includes('mongoose') ||
|
|
184
|
+
d.includes('auth') ||
|
|
185
|
+
d.includes('passport') ||
|
|
186
|
+
d.includes('next-auth') ||
|
|
187
|
+
d.includes('lucia') ||
|
|
188
|
+
d.includes('clerk'));
|
|
189
|
+
if (relevantDeps.length > 0) {
|
|
190
|
+
sections.push(`**Relevant dependencies:** ${relevantDeps.join(', ')}\n`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (ctx.userModels.length > 0) {
|
|
194
|
+
sections.push(`## User/Account Models\n`);
|
|
195
|
+
for (const model of ctx.userModels) {
|
|
196
|
+
sections.push(`### ${model.path}\n\`\`\`\n${model.content}\n\`\`\`\n`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (ctx.dbSchema.length > 0) {
|
|
200
|
+
sections.push(`## Database Schema\n`);
|
|
201
|
+
for (const schema of ctx.dbSchema.slice(0, 3)) {
|
|
202
|
+
sections.push(`### ${schema.path}\n\`\`\`\n${schema.content}\n\`\`\`\n`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (ctx.existingActors.length > 0) {
|
|
206
|
+
sections.push(`## Existing Actor Teams (for reference)\n`);
|
|
207
|
+
for (const actor of ctx.existingActors) {
|
|
208
|
+
sections.push(`### ${actor.path}\n\`\`\`typescript\n${actor.content}\n\`\`\`\n`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (ctx.existingScenes.length > 0) {
|
|
212
|
+
sections.push(`## Existing Scenes (to understand what roles are used)\n`);
|
|
213
|
+
for (const scene of ctx.existingScenes.slice(0, 3)) {
|
|
214
|
+
sections.push(`### ${scene.path}\n\`\`\`\n${scene.content}\n\`\`\`\n`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
sections.push(`## Scenetest Team Format
|
|
218
|
+
|
|
219
|
+
Teams live in \`scenetest/actors/\`, one file per team. Each file exports a single team object mapping role names to actor configs.
|
|
220
|
+
|
|
221
|
+
### File structure
|
|
222
|
+
|
|
223
|
+
\`\`\`
|
|
224
|
+
your-project/
|
|
225
|
+
scenetest/
|
|
226
|
+
config.ts
|
|
227
|
+
actors/
|
|
228
|
+
team-maria.ts ← one team per file
|
|
229
|
+
team-john.ts
|
|
230
|
+
scenes/
|
|
231
|
+
...
|
|
232
|
+
\`\`\`
|
|
233
|
+
|
|
234
|
+
### Team file format
|
|
235
|
+
|
|
236
|
+
\`\`\`typescript
|
|
237
|
+
// scenetest/actors/team-maria.ts
|
|
238
|
+
import type { TeamConfig } from '@scenetest/scenes'
|
|
239
|
+
|
|
240
|
+
export default {
|
|
241
|
+
'primary-learner': {
|
|
242
|
+
key: 'maria-1', // Unique key (used for data-key matching in DOM)
|
|
243
|
+
email: 'maria@test.com',
|
|
244
|
+
password: 'test123',
|
|
245
|
+
nativeLanguage: 'english', // Custom fields are allowed
|
|
246
|
+
},
|
|
247
|
+
'existing-friend': {
|
|
248
|
+
key: 'carlos-1',
|
|
249
|
+
email: 'carlos@test.com',
|
|
250
|
+
password: 'test123',
|
|
251
|
+
},
|
|
252
|
+
'new-signup': {
|
|
253
|
+
key: 'fresh-a-1',
|
|
254
|
+
email: 'fresh-a@test.com',
|
|
255
|
+
password: 'willregister123',
|
|
256
|
+
},
|
|
257
|
+
} satisfies TeamConfig
|
|
258
|
+
\`\`\`
|
|
259
|
+
|
|
260
|
+
### Key requirements
|
|
261
|
+
|
|
262
|
+
- **Every team must have identical role names** — scenes call \`actor('role-name')\`, and that role must exist in every team
|
|
263
|
+
- **\`key\`** is the only required field on ActorConfig (used for data-key selector matching)
|
|
264
|
+
- Credentials (\`email\`, \`password\`, \`username\`) must match seed data users
|
|
265
|
+
- Relationships (friendships, org membership, etc.) between actors must exist in seed data
|
|
266
|
+
- Each team is a self-contained "world" — no cross-team references
|
|
267
|
+
- Use descriptive role names: \`primary-buyer\`, \`competing-seller\`, not \`user\`, \`admin\`
|
|
268
|
+
|
|
269
|
+
## What I Need
|
|
270
|
+
|
|
271
|
+
Generate actor team files for my project:
|
|
272
|
+
1. Identify the user types and relationships in my app
|
|
273
|
+
2. Create 2-3 team files in \`scenetest/actors/\`
|
|
274
|
+
3. Use descriptive, scenario-based role names
|
|
275
|
+
4. Include all credential fields my app needs
|
|
276
|
+
5. Note what relationships need to exist in seed data
|
|
277
|
+
`);
|
|
278
|
+
return sections.join('\n');
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Generate an LLM prompt for seed data generation
|
|
282
|
+
*/
|
|
283
|
+
export function generateSeedsPrompt(ctx) {
|
|
284
|
+
const sections = [];
|
|
285
|
+
sections.push(`# Generate Seed Data for Scenetest Teams
|
|
286
|
+
|
|
287
|
+
I need help creating seed data that matches my scenetest actor teams.
|
|
288
|
+
|
|
289
|
+
## Project: ${ctx.projectName}
|
|
290
|
+
`);
|
|
291
|
+
if (ctx.existingActors.length > 0) {
|
|
292
|
+
sections.push(`## Actor Team Files\n`);
|
|
293
|
+
for (const actor of ctx.existingActors) {
|
|
294
|
+
sections.push(`### ${actor.path}\n\`\`\`typescript\n${actor.content}\n\`\`\`\n`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
sections.push(`## Actor Teams
|
|
299
|
+
|
|
300
|
+
**No actor files found in \`scenetest/actors/\`.** Please either:
|
|
301
|
+
1. First generate team configurations (\`scenetest prompt teams\`)
|
|
302
|
+
2. Or paste your team files here
|
|
303
|
+
`);
|
|
304
|
+
}
|
|
305
|
+
if (ctx.dbSchema.length > 0) {
|
|
306
|
+
sections.push(`## Database Schema\n`);
|
|
307
|
+
for (const schema of ctx.dbSchema) {
|
|
308
|
+
sections.push(`### ${schema.path}\n\`\`\`\n${schema.content}\n\`\`\`\n`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (ctx.existingSeeds.length > 0) {
|
|
312
|
+
sections.push(`## Existing Seed Files (for style reference)\n`);
|
|
313
|
+
for (const seed of ctx.existingSeeds.slice(0, 3)) {
|
|
314
|
+
sections.push(`### ${seed.path}\n\`\`\`\n${seed.content}\n\`\`\`\n`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (ctx.authPatterns.length > 0) {
|
|
318
|
+
sections.push(`## Auth Implementation (for password hashing, etc.)\n`);
|
|
319
|
+
for (const auth of ctx.authPatterns.slice(0, 2)) {
|
|
320
|
+
sections.push(`### ${auth.path}\n\`\`\`\n${auth.content}\n\`\`\`\n`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
sections.push(`## What I Need
|
|
324
|
+
|
|
325
|
+
Generate seed data files that:
|
|
326
|
+
|
|
327
|
+
1. **Create users** matching every actor in every team file
|
|
328
|
+
2. **Establish relationships** between actors (friendships, org membership, etc.)
|
|
329
|
+
3. **Match my existing seed style** (if I have existing seeds above)
|
|
330
|
+
4. **Include supporting data** that tests might need (posts, products, etc.)
|
|
331
|
+
|
|
332
|
+
### Key requirements
|
|
333
|
+
|
|
334
|
+
- User credentials must match exactly: the email/password in actor files must work to log in
|
|
335
|
+
- Passwords must be hashed using my app's auth system
|
|
336
|
+
- Relationships assumed by scenes must be seeded (e.g., if a scene expects two actors to be friends, the friendship must exist)
|
|
337
|
+
- Each team's data is self-contained — all relationships hold within a single team
|
|
338
|
+
|
|
339
|
+
### Output format
|
|
340
|
+
|
|
341
|
+
Generate:
|
|
342
|
+
1. Seed data files in my project's format/style
|
|
343
|
+
2. A seed runner script if I don't have one
|
|
344
|
+
3. A list of all relationships established between actors
|
|
345
|
+
`);
|
|
346
|
+
return sections.join('\n');
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Generate a combined prompt for both teams and seeds
|
|
350
|
+
*/
|
|
351
|
+
export function generateCombinedPrompt(ctx) {
|
|
352
|
+
const sections = [];
|
|
353
|
+
sections.push(`# Generate Scenetest Teams and Seed Data
|
|
354
|
+
|
|
355
|
+
I'm setting up scenetest for my project and need both actor team files and matching seed data.
|
|
356
|
+
|
|
357
|
+
## Project: ${ctx.projectName}
|
|
358
|
+
`);
|
|
359
|
+
if (ctx.packageJson) {
|
|
360
|
+
const deps = {
|
|
361
|
+
...ctx.packageJson.dependencies,
|
|
362
|
+
...ctx.packageJson.devDependencies,
|
|
363
|
+
};
|
|
364
|
+
const relevantDeps = Object.keys(deps).filter((d) => d.includes('prisma') ||
|
|
365
|
+
d.includes('drizzle') ||
|
|
366
|
+
d.includes('sequelize') ||
|
|
367
|
+
d.includes('typeorm') ||
|
|
368
|
+
d.includes('mongoose') ||
|
|
369
|
+
d.includes('auth') ||
|
|
370
|
+
d.includes('passport') ||
|
|
371
|
+
d.includes('next-auth') ||
|
|
372
|
+
d.includes('lucia') ||
|
|
373
|
+
d.includes('clerk') ||
|
|
374
|
+
d.includes('react') ||
|
|
375
|
+
d.includes('vue') ||
|
|
376
|
+
d.includes('svelte') ||
|
|
377
|
+
d.includes('solid'));
|
|
378
|
+
if (relevantDeps.length > 0) {
|
|
379
|
+
sections.push(`**Tech stack:** ${relevantDeps.join(', ')}\n`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (ctx.userModels.length > 0) {
|
|
383
|
+
sections.push(`## User/Account Models\n`);
|
|
384
|
+
for (const model of ctx.userModels) {
|
|
385
|
+
sections.push(`### ${model.path}\n\`\`\`\n${model.content}\n\`\`\`\n`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (ctx.dbSchema.length > 0) {
|
|
389
|
+
sections.push(`## Database Schema\n`);
|
|
390
|
+
for (const schema of ctx.dbSchema.slice(0, 3)) {
|
|
391
|
+
sections.push(`### ${schema.path}\n\`\`\`\n${schema.content}\n\`\`\`\n`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (ctx.existingSeeds.length > 0) {
|
|
395
|
+
sections.push(`## Existing Seed Files (for style reference)\n`);
|
|
396
|
+
for (const seed of ctx.existingSeeds.slice(0, 2)) {
|
|
397
|
+
sections.push(`### ${seed.path}\n\`\`\`\n${seed.content}\n\`\`\`\n`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (ctx.existingScenes.length > 0) {
|
|
401
|
+
sections.push(`## Existing Scenes (to understand usage patterns)\n`);
|
|
402
|
+
for (const scene of ctx.existingScenes.slice(0, 2)) {
|
|
403
|
+
sections.push(`### ${scene.path}\n\`\`\`\n${scene.content}\n\`\`\`\n`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
sections.push(`## Scenetest Project Layout
|
|
407
|
+
|
|
408
|
+
\`\`\`
|
|
409
|
+
your-project/
|
|
410
|
+
scenetest/
|
|
411
|
+
config.ts ← project config
|
|
412
|
+
actors/
|
|
413
|
+
team-maria.ts ← one team per file
|
|
414
|
+
team-john.ts
|
|
415
|
+
scenes/
|
|
416
|
+
onboarding.spec.ts
|
|
417
|
+
social/
|
|
418
|
+
friend-request.spec.ts
|
|
419
|
+
\`\`\`
|
|
420
|
+
|
|
421
|
+
## Team File Format
|
|
422
|
+
|
|
423
|
+
Each file in \`scenetest/actors/\` exports one team:
|
|
424
|
+
|
|
425
|
+
\`\`\`typescript
|
|
426
|
+
// scenetest/actors/team-maria.ts
|
|
427
|
+
import type { TeamConfig } from '@scenetest/scenes'
|
|
428
|
+
|
|
429
|
+
export default {
|
|
430
|
+
'primary-learner': {
|
|
431
|
+
key: 'maria-1',
|
|
432
|
+
email: 'maria@test.com',
|
|
433
|
+
password: 'test123',
|
|
434
|
+
},
|
|
435
|
+
'existing-friend': {
|
|
436
|
+
key: 'carlos-1',
|
|
437
|
+
email: 'carlos@test.com',
|
|
438
|
+
password: 'test123',
|
|
439
|
+
},
|
|
440
|
+
} satisfies TeamConfig
|
|
441
|
+
\`\`\`
|
|
442
|
+
|
|
443
|
+
- \`key\` is the only required field (used for data-key selector matching)
|
|
444
|
+
- Every team must have the same role names
|
|
445
|
+
- Credentials must match seed data users
|
|
446
|
+
- Use descriptive role names: \`primary-buyer\`, not \`user\`
|
|
447
|
+
|
|
448
|
+
## What I Need
|
|
449
|
+
|
|
450
|
+
### Part 1: Actor Teams
|
|
451
|
+
|
|
452
|
+
Generate 2-3 team files for \`scenetest/actors/\`:
|
|
453
|
+
- Identify user types from my models/schema
|
|
454
|
+
- Choose descriptive, scenario-based role names
|
|
455
|
+
- Include credentials that match seed data
|
|
456
|
+
|
|
457
|
+
### Part 2: Seed Data
|
|
458
|
+
|
|
459
|
+
Generate seed files that:
|
|
460
|
+
- Create all users from the team files
|
|
461
|
+
- Establish relationships between actors
|
|
462
|
+
- Follow my existing seed patterns
|
|
463
|
+
- Include supporting data for tests
|
|
464
|
+
|
|
465
|
+
### Output
|
|
466
|
+
|
|
467
|
+
1. Complete team files (one per team)
|
|
468
|
+
2. Seed data files in my project's format
|
|
469
|
+
3. Explanation of each role and its purpose
|
|
470
|
+
4. List of relationships established in seeds
|
|
471
|
+
`);
|
|
472
|
+
return sections.join('\n');
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Generate the appropriate prompt based on type
|
|
476
|
+
*/
|
|
477
|
+
export function generatePrompt(ctx, type) {
|
|
478
|
+
switch (type) {
|
|
479
|
+
case 'teams':
|
|
480
|
+
return generateTeamsPrompt(ctx);
|
|
481
|
+
case 'seeds':
|
|
482
|
+
return generateSeedsPrompt(ctx);
|
|
483
|
+
case 'both':
|
|
484
|
+
return generateCombinedPrompt(ctx);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Format the prompt for CLI output with header/footer
|
|
489
|
+
*/
|
|
490
|
+
export function formatPromptOutput(prompt, type) {
|
|
491
|
+
const divider = '='.repeat(60);
|
|
492
|
+
const label = type === 'both' ? 'teams and seed data' : type;
|
|
493
|
+
return `
|
|
494
|
+
${divider}
|
|
495
|
+
SCENETEST PROMPT GENERATOR - ${type.toUpperCase()}
|
|
496
|
+
${divider}
|
|
497
|
+
|
|
498
|
+
Copy the prompt below and paste it into your preferred LLM
|
|
499
|
+
(Claude, ChatGPT, etc.) to generate your ${label}.
|
|
500
|
+
|
|
501
|
+
${divider}
|
|
502
|
+
|
|
503
|
+
${prompt}
|
|
504
|
+
|
|
505
|
+
${divider}
|
|
506
|
+
END OF PROMPT
|
|
507
|
+
${divider}
|
|
508
|
+
|
|
509
|
+
Tips:
|
|
510
|
+
- Review the prompt and add any missing context about your app
|
|
511
|
+
- Specify the number of teams you want (2-3 is typical)
|
|
512
|
+
- Mention any specific test scenarios you're planning
|
|
513
|
+
- After generating, audit alignment with: building-teams.md audit prompt
|
|
514
|
+
|
|
515
|
+
`;
|
|
516
|
+
}
|
|
517
|
+
//# sourceMappingURL=prompt-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-generator.js","sourceRoot":"","sources":["../src/prompt-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAqB3B;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,UAAmB;IAC5D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IACzB,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;IAEtC,2BAA2B;IAC3B,IAAI,WAAgD,CAAA;IACpD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAA;IAC9C,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;QAC7D,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,GAAG,EAAE;QACrC,qBAAqB;QACrB,qBAAqB;QACrB,sBAAsB;KACvB,CAAC,CAAA;IAEF,mEAAmE;IACnE,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE;QAClD,uBAAuB;QACvB,uBAAuB;QACvB,wBAAwB;KACzB,CAAC,CAAA;IAEF,qCAAqC;IACrC,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE;QAC9C,oBAAoB;QACpB,oBAAoB;QACpB,mBAAmB;QACnB,mBAAmB;QACnB,oBAAoB;QACpB,oBAAoB;QACpB,sBAAsB;QACtB,sBAAsB;QACtB,yBAAyB;QACzB,uBAAuB;QACvB,kBAAkB;KACnB,CAAC,CAAA;IAEF,sBAAsB;IACtB,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE;QACjD,kBAAkB;QAClB,kBAAkB;QAClB,iBAAiB;QACjB,iBAAiB;QACjB,mBAAmB;QACnB,mBAAmB;QACnB,gBAAgB;QAChB,gBAAgB;KACjB,CAAC,CAAA;IAEF,2DAA2D;IAC3D,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAC5C,GAAG,EACH,CAAC,+BAA+B,EAAE,+BAA+B,CAAC,EAClE,CAAC,CACF,CAAA;IAED,0BAA0B;IAC1B,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE;QAChD,iBAAiB;QACjB,YAAY;QACZ,uBAAuB;QACvB,cAAc;QACd,wBAAwB;KACzB,CAAC,CAAA;IAEF,6BAA6B;IAC7B,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE;QAC5C,yBAAyB;QACzB,uBAAuB;QACvB,iBAAiB;QACjB,kBAAkB;QAClB,eAAe;QACf,qBAAqB;KACtB,CAAC,CAAA;IAEF,OAAO;QACL,WAAW;QACX,WAAW;QACX,UAAU;QACV,aAAa;QACb,cAAc;QACd,cAAc;QACd,YAAY;QACZ,QAAQ;QACR,UAAU;KACX,CAAA;AACH,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,GAAW,EAAE,QAAkB;IACrD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QACxC,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;gBAClD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,CAAA;YAC7D,CAAC;YAAC,MAAM,CAAC;gBACP,qBAAqB;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,KAAK,UAAU,iBAAiB,CAC9B,GAAW,EACX,QAAkB,EAClB,QAAQ,GAAG,EAAE;IAEb,MAAM,OAAO,GAAkB,EAAE,CAAA;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAE9B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,CAAC,MAAM,IAAI,QAAQ;YAAE,MAAK;QAErC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE;gBAChC,GAAG;gBACH,MAAM,EAAE,CAAC,oBAAoB,EAAE,YAAY,EAAE,YAAY,CAAC;gBAC1D,QAAQ,EAAE,KAAK;aAChB,CAAC,CAAA;YAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,OAAO,CAAC,MAAM,IAAI,QAAQ;oBAAE,MAAK;gBACrC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,SAAQ;gBAC5B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;gBAEd,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;gBACrC,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;oBAClD,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;gBACjE,CAAC;gBAAC,MAAM,CAAC;oBACP,qBAAqB;gBACvB,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,OAAe,EAAE,QAAQ,GAAG,GAAG;IACtD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACjC,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,OAAO,CAAA;IAC5C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,MAAM,GAAG,QAAQ,cAAc,CAAA;AAChG,CAAC;AAED,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAkB;IACpD,MAAM,QAAQ,GAAa,EAAE,CAAA;IAE7B,QAAQ,CAAC,IAAI,CAAC;;;;cAIF,GAAG,CAAC,WAAW;CAC5B,CAAC,CAAA;IAEA,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG;YACX,GAAI,GAAG,CAAC,WAAW,CAAC,YAAmD;YACvE,GAAI,GAAG,CAAC,WAAW,CAAC,eAAsD;SAC3E,CAAA;QACD,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAC3C,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACpB,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;YACrB,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;YACvB,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;YACrB,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;YACtB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YAClB,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;YACtB,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;YACvB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;YACnB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CACtB,CAAA;QACD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,QAAQ,CAAC,IAAI,CAAC,8BAA8B,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC1E,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAA;QACzC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;YACnC,QAAQ,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,aAAa,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;QACxE,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAA;QACrC,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YAC9C,QAAQ,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,IAAI,aAAa,MAAM,CAAC,OAAO,YAAY,CAAC,CAAA;QAC1E,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAA;QAC1D,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;YACvC,QAAQ,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,uBAAuB,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;QAClF,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAA;QACzE,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACnD,QAAQ,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,aAAa,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;QACxE,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4Df,CAAC,CAAA;IAEA,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAkB;IACpD,MAAM,QAAQ,GAAa,EAAE,CAAA;IAE7B,QAAQ,CAAC,IAAI,CAAC;;;;cAIF,GAAG,CAAC,WAAW;CAC5B,CAAC,CAAA;IAEA,IAAI,GAAG,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAA;QACtC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;YACvC,QAAQ,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,uBAAuB,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;QAClF,CAAC;IACH,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,IAAI,CAAC;;;;;CAKjB,CAAC,CAAA;IACA,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAA;QACrC,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YAClC,QAAQ,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,IAAI,aAAa,MAAM,CAAC,OAAO,YAAY,CAAC,CAAA;QAC1E,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,QAAQ,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAA;QAC/D,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACjD,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,aAAa,IAAI,CAAC,OAAO,YAAY,CAAC,CAAA;QACtE,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAA;QACtE,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YAChD,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,aAAa,IAAI,CAAC,OAAO,YAAY,CAAC,CAAA;QACtE,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;CAsBf,CAAC,CAAA;IAEA,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAkB;IACvD,MAAM,QAAQ,GAAa,EAAE,CAAA;IAE7B,QAAQ,CAAC,IAAI,CAAC;;;;cAIF,GAAG,CAAC,WAAW;CAC5B,CAAC,CAAA;IAEA,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG;YACX,GAAI,GAAG,CAAC,WAAW,CAAC,YAAmD;YACvE,GAAI,GAAG,CAAC,WAAW,CAAC,eAAsD;SAC3E,CAAA;QACD,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAC3C,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACpB,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;YACrB,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;YACvB,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;YACrB,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;YACtB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YAClB,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;YACtB,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;YACvB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;YACnB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;YACnB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;YACnB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;YACjB,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACpB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CACtB,CAAA;QACD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,QAAQ,CAAC,IAAI,CAAC,mBAAmB,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC/D,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAA;QACzC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;YACnC,QAAQ,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,aAAa,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;QACxE,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAA;QACrC,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YAC9C,QAAQ,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,IAAI,aAAa,MAAM,CAAC,OAAO,YAAY,CAAC,CAAA;QAC1E,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,QAAQ,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAA;QAC/D,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACjD,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,aAAa,IAAI,CAAC,OAAO,YAAY,CAAC,CAAA;QACtE,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAA;QACpE,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACnD,QAAQ,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,aAAa,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;QACxE,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiEf,CAAC,CAAA;IAEA,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,GAAkB,EAAE,IAAgB;IACjE,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,OAAO;YACV,OAAO,mBAAmB,CAAC,GAAG,CAAC,CAAA;QACjC,KAAK,OAAO;YACV,OAAO,mBAAmB,CAAC,GAAG,CAAC,CAAA;QACjC,KAAK,MAAM;YACT,OAAO,sBAAsB,CAAC,GAAG,CAAC,CAAA;IACtC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,IAAgB;IACjE,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IAC9B,MAAM,KAAK,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAA;IAE5D,OAAO;EACP,OAAO;iCACwB,IAAI,CAAC,WAAW,EAAE;EACjD,OAAO;;;2CAGkC,KAAK;;EAE9C,OAAO;;EAEP,MAAM;;EAEN,OAAO;;EAEP,OAAO;;;;;;;;CAQR,CAAA;AACD,CAAC"}
|
package/dist/reactive.d.ts
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
* ```
|
|
47
47
|
*/
|
|
48
48
|
import type { Page } from 'playwright';
|
|
49
|
-
import type { ActorConfig, Selector, TimelineEntry, ScriptWarning, FlowFn, ConcurrentActorHandle } from './types.js';
|
|
49
|
+
import type { ActorConfig, Selector, TimelineEntry, ScriptWarning, FlowFn, SceneOptions, ConcurrentActorHandle } from './types.js';
|
|
50
50
|
import { MessageBus } from './message-bus.js';
|
|
51
51
|
/**
|
|
52
52
|
* Concurrent actor handle implementation (declarative / flow model).
|
|
@@ -275,4 +275,5 @@ export declare function drainAll(actors: ConcurrentActorHandleImpl[]): Promise<v
|
|
|
275
275
|
* ```
|
|
276
276
|
*/
|
|
277
277
|
export declare function flow(name: string, fn: FlowFn): void;
|
|
278
|
+
export declare function flow(name: string, options: SceneOptions, fn: FlowFn): void;
|
|
278
279
|
//# sourceMappingURL=reactive.d.ts.map
|
package/dist/reactive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../src/reactive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAW,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,aAAa,EACb,aAAa,EAEb,MAAM,EACN,qBAAqB,
|
|
1
|
+
{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../src/reactive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAW,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,aAAa,EACb,aAAa,EAEb,MAAM,EACN,YAAY,EACZ,qBAAqB,EAEtB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAwE7C;;;;;;GAMG;AACH,qBAAa,yBAA0B,YAAW,qBAAqB;IAiCnE,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,SAAS;IApCnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;IAEtB,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,mBAAmB,CAA2B;IAGtD,OAAO,CAAC,WAAW,CAAK;IAExB,OAAO,CAAC,cAAc,CAAe;IAErC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,YAAY,CAAC,CAAQ;IAE7B,4EAA4E;IAC5E,OAAO,CAAC,cAAc,CAAkD;IACxE,mDAAmD;IACnD,OAAO,CAAC,aAAa,CAAsC;gBAGzD,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,IAAI,GAAG,IAAI,EACT,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,aAAa,EAAE,EACzB,QAAQ,EAAE,aAAa,EAAE,EACzB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM;IAiB3B;;;OAGG;IACH,IAAI,IAAI,IAAI,IAAI,CAKf;IAED;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAK1B;;;OAGG;IACH,iBAAiB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,GAAG,IAAI;IAIrE;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAQxD,OAAO,CAAC,IAAI;IASZ,+BAA+B;IAC/B,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,4DAA4D;IAC5D,IAAI,OAAO,IAAI,OAAO,CAErB;IAMD,OAAO,KAAK,KAAK,GAEhB;IAED;;;;;;;OAOG;YACW,aAAa;IAmD3B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAUzB,cAAc,IAAI,IAAI;IA+BtB,GAAG,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAW7B,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAuBnC,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAShC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAW3B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAYlC,KAAK,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI;IAiBhC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAQjD,KAAK,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAQ/B,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAY/C,EAAE,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI;IAsB7B,IAAI,IAAI,IAAI;IAgBZ,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAMtB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM3B;;;;;OAKG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAU9B,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAU3C;;;;;;;;;;;;;;;;OAgBG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAUvB;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAqDpB;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAIjD;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,GAAG,IAAI;IAuB7D;;;OAGG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAS3B;;;;;;;;OAQG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA8D5B;;;;;OAKG;YACW,mBAAmB;CA+ElC;AAMD;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,MAAM,EAAE,yBAAyB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgCjF;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;AACpD,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA"}
|
package/dist/reactive.js
CHANGED
|
@@ -750,59 +750,12 @@ export async function drainAll(actors) {
|
|
|
750
750
|
throw (original ?? failures[0]).reason;
|
|
751
751
|
}
|
|
752
752
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
/**
|
|
757
|
-
* Define a reactive flow.
|
|
758
|
-
*
|
|
759
|
-
* Inside the flow function, actor DSL calls just queue actions — nothing
|
|
760
|
-
* executes. After the function returns, all actors drain their queues
|
|
761
|
-
* concurrently through the application.
|
|
762
|
-
*
|
|
763
|
-
* @example
|
|
764
|
-
* ```ts
|
|
765
|
-
* import { flow } from '@scenetest/scenes'
|
|
766
|
-
*
|
|
767
|
-
* flow('user updates profile', ({ actor }) => {
|
|
768
|
-
* const user = actor('user')
|
|
769
|
-
*
|
|
770
|
-
* user.openTo('/login')
|
|
771
|
-
* user
|
|
772
|
-
* .see('login-form')
|
|
773
|
-
* .typeInto('email', user.email!)
|
|
774
|
-
* .typeInto('password', user.password!)
|
|
775
|
-
* .click('submit')
|
|
776
|
-
*
|
|
777
|
-
* user.see('dashboard')
|
|
778
|
-
* user.openTo('/profile')
|
|
779
|
-
* user
|
|
780
|
-
* .see('profile-form')
|
|
781
|
-
* .typeInto('name-input', 'New Name')
|
|
782
|
-
* .click('save-button')
|
|
783
|
-
*
|
|
784
|
-
* user.seeText('New Name')
|
|
785
|
-
* })
|
|
786
|
-
* ```
|
|
787
|
-
*
|
|
788
|
-
* @example Multi-actor
|
|
789
|
-
* ```ts
|
|
790
|
-
* flow('two users chat', ({ actor }) => {
|
|
791
|
-
* const alice = actor('alice')
|
|
792
|
-
* const bob = actor('bob')
|
|
793
|
-
*
|
|
794
|
-
* alice.openTo('/chat')
|
|
795
|
-
* alice.see('message-input').typeInto('message-input', 'Hello!').click('send')
|
|
796
|
-
*
|
|
797
|
-
* bob.openTo('/chat')
|
|
798
|
-
* bob.seeText('Hello!')
|
|
799
|
-
* })
|
|
800
|
-
* ```
|
|
801
|
-
*/
|
|
802
|
-
export function flow(name, fn) {
|
|
753
|
+
export function flow(name, fnOrOptions, maybeFn) {
|
|
754
|
+
const fn = typeof fnOrOptions === 'function' ? fnOrOptions : maybeFn;
|
|
755
|
+
const options = typeof fnOrOptions === 'function' ? undefined : fnOrOptions;
|
|
803
756
|
// Register as a normal scene — the runner doesn't need to know it's
|
|
804
757
|
// reactive. The wrapping scene fn handles the three-phase execution.
|
|
805
|
-
|
|
758
|
+
const wrappedFn = async (context) => {
|
|
806
759
|
const session = getCurrentSession();
|
|
807
760
|
if (!session) {
|
|
808
761
|
throw new Error('flow() must be run inside the scene runner');
|
|
@@ -811,6 +764,12 @@ export function flow(name, fn) {
|
|
|
811
764
|
const actorRoles = [];
|
|
812
765
|
// Actor registry for [role.field] interpolation across actors
|
|
813
766
|
const actorRegistry = new Map();
|
|
767
|
+
// Build team metadata for [team.field] interpolation from tags
|
|
768
|
+
const teamMeta = session.meta;
|
|
769
|
+
const teamMetadataForInterpolation = {
|
|
770
|
+
...(teamMeta.tags ?? {}),
|
|
771
|
+
...(teamMeta.name ? { name: teamMeta.name } : {}),
|
|
772
|
+
};
|
|
814
773
|
const flowContext = {
|
|
815
774
|
actor: (role) => {
|
|
816
775
|
// Check if already created (re-referencing an actor)
|
|
@@ -829,6 +788,7 @@ export function flow(name, fn) {
|
|
|
829
788
|
return reactive;
|
|
830
789
|
},
|
|
831
790
|
teamIndex: context.teamIndex,
|
|
791
|
+
team: teamMeta,
|
|
832
792
|
};
|
|
833
793
|
// Phase 1: Declaration — user code queues actions, nothing executes.
|
|
834
794
|
// actor() is synchronous so the flow body can be sync too.
|
|
@@ -836,9 +796,12 @@ export function flow(name, fn) {
|
|
|
836
796
|
if (result && typeof result.then === 'function') {
|
|
837
797
|
await result;
|
|
838
798
|
}
|
|
839
|
-
// Set actor registry on all actors for
|
|
799
|
+
// Set actor registry and team metadata on all actors for interpolation
|
|
840
800
|
for (const actor of reactiveActors) {
|
|
841
801
|
actor._setActorRegistry(actorRegistry);
|
|
802
|
+
if (Object.keys(teamMetadataForInterpolation).length > 0) {
|
|
803
|
+
actor._setTeamMetadata(teamMetadataForInterpolation);
|
|
804
|
+
}
|
|
842
805
|
}
|
|
843
806
|
// Phase 2: Initialize — create browser contexts in parallel
|
|
844
807
|
await Promise.all(reactiveActors.map(async (actor, i) => {
|
|
@@ -847,6 +810,12 @@ export function flow(name, fn) {
|
|
|
847
810
|
}));
|
|
848
811
|
// Phase 3: Execution — all actors drain concurrently
|
|
849
812
|
await drainAll(reactiveActors);
|
|
850
|
-
}
|
|
813
|
+
};
|
|
814
|
+
if (options) {
|
|
815
|
+
scene(name, options, wrappedFn);
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
scene(name, wrappedFn);
|
|
819
|
+
}
|
|
851
820
|
}
|
|
852
821
|
//# sourceMappingURL=reactive.js.map
|