@scenetest/scenes 0.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.
Files changed (90) hide show
  1. package/LICENSE +21 -0
  2. package/dist/__tests__/devices.test.d.ts +2 -0
  3. package/dist/__tests__/devices.test.d.ts.map +1 -0
  4. package/dist/__tests__/devices.test.js +117 -0
  5. package/dist/__tests__/devices.test.js.map +1 -0
  6. package/dist/__tests__/dsl.test.d.ts +2 -0
  7. package/dist/__tests__/dsl.test.d.ts.map +1 -0
  8. package/dist/__tests__/dsl.test.js +385 -0
  9. package/dist/__tests__/dsl.test.js.map +1 -0
  10. package/dist/__tests__/markdown-scene.test.d.ts +2 -0
  11. package/dist/__tests__/markdown-scene.test.d.ts.map +1 -0
  12. package/dist/__tests__/markdown-scene.test.js +508 -0
  13. package/dist/__tests__/markdown-scene.test.js.map +1 -0
  14. package/dist/__tests__/reactive.test.d.ts +2 -0
  15. package/dist/__tests__/reactive.test.d.ts.map +1 -0
  16. package/dist/__tests__/reactive.test.js +383 -0
  17. package/dist/__tests__/reactive.test.js.map +1 -0
  18. package/dist/__tests__/swarm.test.d.ts +2 -0
  19. package/dist/__tests__/swarm.test.d.ts.map +1 -0
  20. package/dist/__tests__/swarm.test.js +214 -0
  21. package/dist/__tests__/swarm.test.js.map +1 -0
  22. package/dist/actor.d.ts +104 -0
  23. package/dist/actor.d.ts.map +1 -0
  24. package/dist/actor.js +527 -0
  25. package/dist/actor.js.map +1 -0
  26. package/dist/cli.d.ts +3 -0
  27. package/dist/cli.d.ts.map +1 -0
  28. package/dist/cli.js +273 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/config.d.ts +21 -0
  31. package/dist/config.d.ts.map +1 -0
  32. package/dist/config.js +120 -0
  33. package/dist/config.js.map +1 -0
  34. package/dist/devices.d.ts +55 -0
  35. package/dist/devices.d.ts.map +1 -0
  36. package/dist/devices.js +167 -0
  37. package/dist/devices.js.map +1 -0
  38. package/dist/dsl.d.ts +99 -0
  39. package/dist/dsl.d.ts.map +1 -0
  40. package/dist/dsl.js +247 -0
  41. package/dist/dsl.js.map +1 -0
  42. package/dist/index.d.ts +13 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +16 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/init.d.ts +9 -0
  47. package/dist/init.d.ts.map +1 -0
  48. package/dist/init.js +27 -0
  49. package/dist/init.js.map +1 -0
  50. package/dist/loader.d.ts +2 -0
  51. package/dist/loader.d.ts.map +1 -0
  52. package/dist/loader.js +10 -0
  53. package/dist/loader.js.map +1 -0
  54. package/dist/markdown-scene.d.ts +120 -0
  55. package/dist/markdown-scene.d.ts.map +1 -0
  56. package/dist/markdown-scene.js +452 -0
  57. package/dist/markdown-scene.js.map +1 -0
  58. package/dist/message-bus.d.ts +31 -0
  59. package/dist/message-bus.d.ts.map +1 -0
  60. package/dist/message-bus.js +74 -0
  61. package/dist/message-bus.js.map +1 -0
  62. package/dist/reactive.d.ts +267 -0
  63. package/dist/reactive.d.ts.map +1 -0
  64. package/dist/reactive.js +779 -0
  65. package/dist/reactive.js.map +1 -0
  66. package/dist/runner.d.ts +51 -0
  67. package/dist/runner.d.ts.map +1 -0
  68. package/dist/runner.js +306 -0
  69. package/dist/runner.js.map +1 -0
  70. package/dist/scene.d.ts +40 -0
  71. package/dist/scene.d.ts.map +1 -0
  72. package/dist/scene.js +110 -0
  73. package/dist/scene.js.map +1 -0
  74. package/dist/selectors.d.ts +57 -0
  75. package/dist/selectors.d.ts.map +1 -0
  76. package/dist/selectors.js +193 -0
  77. package/dist/selectors.js.map +1 -0
  78. package/dist/swarm.d.ts +64 -0
  79. package/dist/swarm.d.ts.map +1 -0
  80. package/dist/swarm.js +306 -0
  81. package/dist/swarm.js.map +1 -0
  82. package/dist/team-manager.d.ts +120 -0
  83. package/dist/team-manager.d.ts.map +1 -0
  84. package/dist/team-manager.js +267 -0
  85. package/dist/team-manager.js.map +1 -0
  86. package/dist/types.d.ts +653 -0
  87. package/dist/types.d.ts.map +1 -0
  88. package/dist/types.js +2 -0
  89. package/dist/types.js.map +1 -0
  90. package/package.json +61 -0
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Global alias registry
3
+ */
4
+ let globalAliases = {};
5
+ /**
6
+ * Set the global selector aliases
7
+ */
8
+ export function setAliases(aliases) {
9
+ globalAliases = aliases;
10
+ }
11
+ /**
12
+ * Get the current aliases
13
+ */
14
+ export function getAliases() {
15
+ return globalAliases;
16
+ }
17
+ /**
18
+ * Clear all aliases
19
+ */
20
+ export function clearAliases() {
21
+ globalAliases = {};
22
+ }
23
+ /**
24
+ * Build CSS selector for a single token.
25
+ *
26
+ * Resolution order:
27
+ * 1. aria-label - Accessibility label (encouraged for interactive elements)
28
+ * 2. id - DOM id attribute
29
+ * 3. data-testid - Explicit test identifier
30
+ * 4. data-name - Custom name attribute
31
+ * 5. data-key - Key for list items
32
+ * 6. name - Form element name
33
+ */
34
+ function buildTokenSelector(token) {
35
+ return [
36
+ `[aria-label="${token}"]`,
37
+ `[id="${token}"]`,
38
+ `[data-testid="${token}"]`,
39
+ `[data-name="${token}"]`,
40
+ `[data-key="${token}"]`,
41
+ `[name="${token}"]`,
42
+ ].join(', ');
43
+ }
44
+ /**
45
+ * Resolve a single token to a locator.
46
+ * Handles aliases (~) and explicit aria-labels (@).
47
+ */
48
+ function resolveToken(base, token) {
49
+ // Check if it's an alias (starts with ~)
50
+ if (token.startsWith('~')) {
51
+ const aliasName = token.slice(1);
52
+ const aliasValue = globalAliases[aliasName];
53
+ if (!aliasValue) {
54
+ throw new Error(`Unknown alias: ${aliasName}`);
55
+ }
56
+ return base.locator(aliasValue);
57
+ }
58
+ // Check if it's explicitly an aria-label (starts with @)
59
+ if (token.startsWith('@')) {
60
+ const label = token.slice(1);
61
+ return base.locator(`[aria-label="${label}"]`);
62
+ }
63
+ // Default: try all attribute types
64
+ return base.locator(buildTokenSelector(token)).first();
65
+ }
66
+ /**
67
+ * Resolve a space-separated selector string.
68
+ *
69
+ * Algorithm:
70
+ * 1. Split into tokens
71
+ * 2. For each token, find matching element
72
+ * 3. After matching, check if SAME element has data-key matching NEXT token
73
+ * - If yes: consume that token (it's a key match), continue with token after
74
+ * - If no: descend into children for next token
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * // Simple
79
+ * resolveSelector(page, 'button')
80
+ *
81
+ * // Nested
82
+ * resolveSelector(page, 'modal form submit-button')
83
+ *
84
+ * // With implicit key matching
85
+ * resolveSelector(page, 'playlist-row 12345 like-button')
86
+ * // Finds playlist-row, checks if it has data-key="12345",
87
+ * // if yes stays on same element, then finds like-button child
88
+ * ```
89
+ */
90
+ export function resolveSelector(base, selector) {
91
+ const tokens = selector.trim().split(/\s+/);
92
+ if (tokens.length === 0) {
93
+ throw new Error('Empty selector');
94
+ }
95
+ let locator = resolveToken(base, tokens[0]);
96
+ let i = 1;
97
+ while (i < tokens.length) {
98
+ const nextToken = tokens[i];
99
+ // Check if current element has data-key matching the next token
100
+ // This allows 'playlist-row 12345' to match a single element with both attributes
101
+ const withKey = locator.filter({ has: base.locator(`[data-key="${nextToken}"]`) });
102
+ // We need to check if the current locator itself has the data-key
103
+ // Use a more direct approach: check if locator[data-key=nextToken] exists
104
+ const sameElementWithKey = locator.locator(`xpath=self::*[@data-key="${nextToken}"]`);
105
+ // Try to resolve: does the current element have this data-key?
106
+ // We'll build a locator that checks, and use it in the chain
107
+ // Since we can't do async checks here, we use a filter approach
108
+ // Actually, let's use a simpler approach:
109
+ // Build a locator that tries both interpretations and takes the first match
110
+ // But that's complex. Let's just always check for key on same element first.
111
+ // Simpler: use locator chaining with 'or'
112
+ // Option 1: Same element has data-key=nextToken (consume token, stay on element)
113
+ // Option 2: Child matches nextToken (descend)
114
+ // We'll prioritize: if current element has data-key matching next token,
115
+ // treat it as a key match and skip to the token after
116
+ // This is done by checking self::*[@data-key=...]
117
+ // For now, use a practical approach: try to find child, but also support
118
+ // the key-on-same-element pattern by including data-key in the resolution
119
+ // Actually the cleanest way: always descend, but data-key IS in the attribute list
120
+ // So 'playlist-row 12345' will:
121
+ // 1. Find playlist-row (matches data-name or similar)
122
+ // 2. Look for '12345' in children - but wait, it might be on same element!
123
+ // The user's algorithm says: after matching token1, check if THAT SAME ELEMENT
124
+ // has data-key=token2. If yes, stay on same element and consume token2.
125
+ // We need to do this check. Let's restructure:
126
+ // Check if current locator's element has data-key = nextToken
127
+ // We can do this with a self:: xpath or by checking the attribute
128
+ // Build combined locator:
129
+ // Either: current element has data-key=nextToken (stay, consume)
130
+ // Or: find child matching nextToken (descend)
131
+ // Use Playwright's .or() to combine
132
+ const stayOnCurrent = locator.locator(`xpath=self::*[@data-key="${nextToken}"]`);
133
+ const descendToChild = locator.locator(buildTokenSelector(nextToken)).first();
134
+ // Prefer staying on current if it has the key, otherwise descend
135
+ locator = stayOnCurrent.or(descendToChild).first();
136
+ i++;
137
+ }
138
+ return locator;
139
+ }
140
+ /**
141
+ * Get debugging info about what a selector would match.
142
+ * Useful for the debug selector explorer.
143
+ */
144
+ export async function explainSelector(page, selector) {
145
+ const locator = resolveSelector(page, selector);
146
+ const count = await locator.count();
147
+ const matches = [];
148
+ // Get info about matching elements
149
+ for (let i = 0; i < Math.min(count, 5); i++) {
150
+ const element = locator.nth(i);
151
+ const tag = await element.evaluate((el) => el.tagName.toLowerCase());
152
+ const text = (await element.textContent()) || '';
153
+ const attributes = await element.evaluate((el) => {
154
+ const attrs = {};
155
+ for (const attr of el.attributes) {
156
+ attrs[attr.name] = attr.value;
157
+ }
158
+ return attrs;
159
+ });
160
+ matches.push({ tag, attributes, text: text.slice(0, 100) });
161
+ }
162
+ // Generate suggestions if not found
163
+ const suggestions = [];
164
+ if (count === 0) {
165
+ const firstToken = selector.trim().split(/\s+/)[0];
166
+ // Look for similar elements
167
+ const similarByAriaLabel = await page.locator(`[aria-label*="${firstToken}"]`).all();
168
+ for (const el of similarByAriaLabel.slice(0, 3)) {
169
+ const label = await el.getAttribute('aria-label');
170
+ if (label)
171
+ suggestions.push(label);
172
+ }
173
+ const similarByTestId = await page.locator(`[data-testid*="${firstToken}"]`).all();
174
+ for (const el of similarByTestId.slice(0, 3)) {
175
+ const testid = await el.getAttribute('data-testid');
176
+ if (testid)
177
+ suggestions.push(testid);
178
+ }
179
+ const similarByName = await page.locator(`[data-name*="${firstToken}"]`).all();
180
+ for (const el of similarByName.slice(0, 3)) {
181
+ const name = await el.getAttribute('data-name');
182
+ if (name)
183
+ suggestions.push(name);
184
+ }
185
+ }
186
+ return {
187
+ found: count > 0,
188
+ count,
189
+ matches,
190
+ suggestions: [...new Set(suggestions)],
191
+ };
192
+ }
193
+ //# sourceMappingURL=selectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectors.js","sourceRoot":"","sources":["../src/selectors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import type { SwarmConfig, SwarmReport, RunReport, RegisteredScene } from './types.js';
2
+ import type { TeamManager } from './team-manager.js';
3
+ /**
4
+ * Resolve partial swarm config with defaults
5
+ */
6
+ export declare function resolveSwarmConfig(config?: SwarmConfig): Required<SwarmConfig>;
7
+ /**
8
+ * Tracks failure history across runs for a scene.
9
+ * Used to determine whether swarm mode should be triggered.
10
+ */
11
+ interface SceneFailureRecord {
12
+ /** Scene name */
13
+ name: string;
14
+ /** Recent run outcomes (true = passed, false = failed). Most recent last. */
15
+ history: boolean[];
16
+ /** Count of consecutive failures at the tail of history */
17
+ consecutiveFailures: number;
18
+ }
19
+ /**
20
+ * Evaluates whether swarm mode should be auto-triggered based on
21
+ * recent run history. Tracks failures across runs and checks whether
22
+ * any scene has exceeded the failure threshold within the window.
23
+ */
24
+ export declare class SwarmTrigger {
25
+ private records;
26
+ private config;
27
+ constructor(config?: SwarmConfig);
28
+ /**
29
+ * Record the results of a run. Call this after each normal run completes.
30
+ */
31
+ recordRun(report: RunReport): void;
32
+ /**
33
+ * Check whether swarm mode should be triggered.
34
+ * Returns the names of scenes that have exceeded the failure threshold.
35
+ */
36
+ shouldTrigger(): string[];
37
+ /**
38
+ * Get the failure record for a scene
39
+ */
40
+ getRecord(name: string): SceneFailureRecord | undefined;
41
+ /**
42
+ * Clear all tracking data
43
+ */
44
+ clear(): void;
45
+ }
46
+ /**
47
+ * Run swarm mode: execute scenes across ALL teams with multiple repeats.
48
+ *
49
+ * This is the diagnostic mode that runs when failures persist beyond
50
+ * the threshold. It tests every scene against every team multiple times
51
+ * to classify failures as broken, flaky, or seed-data edge cases.
52
+ *
53
+ * @param scenes - Scenes to swarm (all registered, or just the triggering ones)
54
+ * @param teamManager - The team manager with all teams
55
+ * @param config - Swarm configuration
56
+ * @param timeout - Scene timeout
57
+ * @param actionTimeout - Per-action timeout
58
+ * @param warnAfter - Warn threshold
59
+ * @param baseUrl - Application base URL
60
+ * @param trigger - Whether this was auto-triggered or manually requested
61
+ */
62
+ export declare function runSwarm(scenes: RegisteredScene[], teamManager: TeamManager, config: SwarmConfig | undefined, timeout: number, actionTimeout: number, warnAfter: number, baseUrl: string | undefined, trigger: 'auto' | 'manual'): Promise<SwarmReport>;
63
+ export {};
64
+ //# sourceMappingURL=swarm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swarm.d.ts","sourceRoot":"","sources":["../src/swarm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EAIX,SAAS,EAET,eAAe,EAChB,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAcpD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,CAE9E;AAID;;;GAGG;AACH,UAAU,kBAAkB;IAC1B,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,6EAA6E;IAC7E,OAAO,EAAE,OAAO,EAAE,CAAA;IAClB,2DAA2D;IAC3D,mBAAmB,EAAE,MAAM,CAAA;CAC5B;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAwC;IACvD,OAAO,CAAC,MAAM,CAAuB;gBAEzB,MAAM,CAAC,EAAE,WAAW;IAIhC;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IA4BlC;;;OAGG;IACH,aAAa,IAAI,MAAM,EAAE;IAYzB;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAIvD;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAID;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,eAAe,EAAE,EACzB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,MAAM,GAAG,QAAQ,GACzB,OAAO,CAAC,WAAW,CAAC,CAiJtB"}
package/dist/swarm.js ADDED
@@ -0,0 +1,306 @@
1
+ import { runScene } from './scene.js';
2
+ /**
3
+ * Default swarm configuration values
4
+ */
5
+ const swarmDefaults = {
6
+ failureThreshold: 5,
7
+ windowSize: 3,
8
+ concurrency: Infinity,
9
+ repeats: 3,
10
+ auto: true,
11
+ };
12
+ /**
13
+ * Resolve partial swarm config with defaults
14
+ */
15
+ export function resolveSwarmConfig(config) {
16
+ return { ...swarmDefaults, ...config };
17
+ }
18
+ /**
19
+ * Evaluates whether swarm mode should be auto-triggered based on
20
+ * recent run history. Tracks failures across runs and checks whether
21
+ * any scene has exceeded the failure threshold within the window.
22
+ */
23
+ export class SwarmTrigger {
24
+ records = new Map();
25
+ config;
26
+ constructor(config) {
27
+ this.config = resolveSwarmConfig(config);
28
+ }
29
+ /**
30
+ * Record the results of a run. Call this after each normal run completes.
31
+ */
32
+ recordRun(report) {
33
+ for (const scene of report.scenes) {
34
+ const passed = scene.status === 'completed' && scene.assertions.every((a) => a.result);
35
+ let record = this.records.get(scene.name);
36
+ if (!record) {
37
+ record = { name: scene.name, history: [], consecutiveFailures: 0 };
38
+ this.records.set(scene.name, record);
39
+ }
40
+ record.history.push(passed);
41
+ // Trim history to window size
42
+ if (record.history.length > this.config.windowSize) {
43
+ record.history = record.history.slice(-this.config.windowSize);
44
+ }
45
+ // Count consecutive failures from the end
46
+ record.consecutiveFailures = 0;
47
+ for (let i = record.history.length - 1; i >= 0; i--) {
48
+ if (!record.history[i]) {
49
+ record.consecutiveFailures++;
50
+ }
51
+ else {
52
+ break;
53
+ }
54
+ }
55
+ }
56
+ }
57
+ /**
58
+ * Check whether swarm mode should be triggered.
59
+ * Returns the names of scenes that have exceeded the failure threshold.
60
+ */
61
+ shouldTrigger() {
62
+ if (!this.config.auto)
63
+ return [];
64
+ const triggering = [];
65
+ for (const [name, record] of this.records) {
66
+ if (record.consecutiveFailures >= this.config.failureThreshold) {
67
+ triggering.push(name);
68
+ }
69
+ }
70
+ return triggering;
71
+ }
72
+ /**
73
+ * Get the failure record for a scene
74
+ */
75
+ getRecord(name) {
76
+ return this.records.get(name);
77
+ }
78
+ /**
79
+ * Clear all tracking data
80
+ */
81
+ clear() {
82
+ this.records.clear();
83
+ }
84
+ }
85
+ // ─── Swarm Execution ─────────────────────────────────────────────────
86
+ /**
87
+ * Run swarm mode: execute scenes across ALL teams with multiple repeats.
88
+ *
89
+ * This is the diagnostic mode that runs when failures persist beyond
90
+ * the threshold. It tests every scene against every team multiple times
91
+ * to classify failures as broken, flaky, or seed-data edge cases.
92
+ *
93
+ * @param scenes - Scenes to swarm (all registered, or just the triggering ones)
94
+ * @param teamManager - The team manager with all teams
95
+ * @param config - Swarm configuration
96
+ * @param timeout - Scene timeout
97
+ * @param actionTimeout - Per-action timeout
98
+ * @param warnAfter - Warn threshold
99
+ * @param baseUrl - Application base URL
100
+ * @param trigger - Whether this was auto-triggered or manually requested
101
+ */
102
+ export async function runSwarm(scenes, teamManager, config, timeout, actionTimeout, warnAfter, baseUrl, trigger) {
103
+ const resolved = resolveSwarmConfig(config);
104
+ const totalTeams = teamManager.totalCount;
105
+ const concurrency = Math.min(resolved.concurrency, totalTeams);
106
+ console.log('\n' + '='.repeat(50));
107
+ console.log(' SWARM MODE');
108
+ console.log('='.repeat(50));
109
+ console.log(` Trigger: ${trigger}`);
110
+ console.log(` Scenes: ${scenes.length}`);
111
+ console.log(` Teams: ${totalTeams}`);
112
+ console.log(` Repeats: ${resolved.repeats}`);
113
+ console.log(` Concurrency: ${concurrency === Infinity ? 'unlimited' : concurrency}`);
114
+ console.log('='.repeat(50) + '\n');
115
+ const results = [];
116
+ for (const scene of scenes) {
117
+ console.log(` Swarming: ${scene.name}`);
118
+ const details = [];
119
+ const tasks = [];
120
+ for (let repeat = 0; repeat < resolved.repeats; repeat++) {
121
+ for (let teamIndex = 0; teamIndex < totalTeams; teamIndex++) {
122
+ tasks.push({ teamIndex, repeat });
123
+ }
124
+ }
125
+ // Execute with concurrency throttle
126
+ const semaphore = new Semaphore(concurrency);
127
+ const taskPromises = tasks.map(async (task) => {
128
+ await semaphore.acquire();
129
+ try {
130
+ // Wait for this specific team to be available
131
+ // (in swarm mode we bypass the normal acquire flow and
132
+ // coordinate at the task level)
133
+ while (true) {
134
+ const acquired = teamManager.acquire();
135
+ if (acquired === task.teamIndex)
136
+ break;
137
+ // We got a different team, release it
138
+ if (acquired !== null)
139
+ teamManager.release(acquired);
140
+ await new Promise((r) => setTimeout(r, 50));
141
+ }
142
+ try {
143
+ const session = await teamManager.createSession(task.teamIndex, actionTimeout, warnAfter, baseUrl);
144
+ try {
145
+ const report = await runScene(scene, session, timeout);
146
+ const detail = {
147
+ teamIndex: task.teamIndex,
148
+ repeat: task.repeat,
149
+ status: report.status,
150
+ duration: report.duration,
151
+ assertionsFailed: report.assertions.filter((a) => !a.result).length,
152
+ assertionsTotal: report.assertions.length,
153
+ error: report.error,
154
+ actors: {},
155
+ };
156
+ // Capture device info per actor
157
+ for (const [role] of session.getActors()) {
158
+ const device = session.getActorDevice(role);
159
+ detail.actors[role] = { device: device?.name };
160
+ }
161
+ details.push(detail);
162
+ const icon = report.status === 'completed' ? '.' : 'F';
163
+ process.stdout.write(icon);
164
+ }
165
+ finally {
166
+ await session.close();
167
+ }
168
+ }
169
+ finally {
170
+ teamManager.release(task.teamIndex);
171
+ }
172
+ }
173
+ finally {
174
+ semaphore.release();
175
+ }
176
+ });
177
+ await Promise.all(taskPromises);
178
+ process.stdout.write('\n');
179
+ // Classify this scene
180
+ const passed = details.filter((d) => d.status === 'completed' && d.assertionsFailed === 0);
181
+ const failed = details.filter((d) => d.status !== 'completed' || d.assertionsFailed > 0);
182
+ const failingTeamSet = new Set(failed.map((d) => d.teamIndex));
183
+ const passingTeamSet = new Set(passed.map((d) => d.teamIndex));
184
+ const classification = classifyScene(details, totalTeams);
185
+ const result = {
186
+ name: scene.name,
187
+ file: scene.file,
188
+ runs: details.length,
189
+ passed: passed.length,
190
+ failed: failed.length,
191
+ failingTeams: [...failingTeamSet],
192
+ passingTeams: [...passingTeamSet],
193
+ classification,
194
+ details,
195
+ };
196
+ results.push(result);
197
+ console.log(` ${classificationLabel(classification)} (${passed.length}/${details.length} passed)`);
198
+ if (classification === 'seed-data-edge-case') {
199
+ console.log(` Failing teams: ${[...failingTeamSet].join(', ')}`);
200
+ }
201
+ }
202
+ // Build summary
203
+ const summary = {
204
+ broken: results.filter((r) => r.classification === 'broken').length,
205
+ flaky: results.filter((r) => r.classification === 'flaky').length,
206
+ seedDataEdgeCase: results.filter((r) => r.classification === 'seed-data-edge-case').length,
207
+ healthy: results.filter((r) => r.classification === 'healthy').length,
208
+ };
209
+ console.log('\n' + '-'.repeat(50));
210
+ console.log(' Swarm Results');
211
+ console.log('-'.repeat(50));
212
+ if (summary.broken > 0)
213
+ console.log(` BROKEN: ${summary.broken} scene(s)`);
214
+ if (summary.flaky > 0)
215
+ console.log(` FLAKY: ${summary.flaky} scene(s)`);
216
+ if (summary.seedDataEdgeCase > 0)
217
+ console.log(` SEED DATA EDGE: ${summary.seedDataEdgeCase} scene(s)`);
218
+ if (summary.healthy > 0)
219
+ console.log(` HEALTHY: ${summary.healthy} scene(s)`);
220
+ console.log('-'.repeat(50) + '\n');
221
+ return { trigger, results, summary };
222
+ }
223
+ // ─── Classification ──────────────────────────────────────────────────
224
+ /**
225
+ * Classify a scene based on swarm run details.
226
+ *
227
+ * Logic:
228
+ * - If ALL runs failed → broken
229
+ * - If ALL runs passed → healthy
230
+ * - If failures are concentrated on specific teams (and other teams always pass)
231
+ * → seed-data-edge-case
232
+ * - Otherwise → flaky
233
+ */
234
+ function classifyScene(details, totalTeams) {
235
+ const allFailed = details.every((d) => d.status !== 'completed' || d.assertionsFailed > 0);
236
+ const allPassed = details.every((d) => d.status === 'completed' && d.assertionsFailed === 0);
237
+ if (allPassed)
238
+ return 'healthy';
239
+ if (allFailed)
240
+ return 'broken';
241
+ // Check if failures are team-specific
242
+ // Group by team and check if some teams always fail while others always pass
243
+ const teamResults = new Map();
244
+ for (const detail of details) {
245
+ const entry = teamResults.get(detail.teamIndex) ?? { passed: 0, failed: 0 };
246
+ if (detail.status === 'completed' && detail.assertionsFailed === 0) {
247
+ entry.passed++;
248
+ }
249
+ else {
250
+ entry.failed++;
251
+ }
252
+ teamResults.set(detail.teamIndex, entry);
253
+ }
254
+ // A team "always fails" if it never passed
255
+ // A team "always passes" if it never failed
256
+ const alwaysFailing = [...teamResults.entries()].filter(([, r]) => r.passed === 0);
257
+ const alwaysPassing = [...teamResults.entries()].filter(([, r]) => r.failed === 0);
258
+ // If there are teams that always fail AND teams that always pass,
259
+ // this looks like a seed-data edge case
260
+ if (alwaysFailing.length > 0 && alwaysPassing.length > 0) {
261
+ return 'seed-data-edge-case';
262
+ }
263
+ // Otherwise it's flaky — failures are spread across teams
264
+ return 'flaky';
265
+ }
266
+ /**
267
+ * Human-readable label for a classification
268
+ */
269
+ function classificationLabel(c) {
270
+ switch (c) {
271
+ case 'broken': return 'BROKEN - fails consistently';
272
+ case 'flaky': return 'FLAKY - intermittent failures';
273
+ case 'seed-data-edge-case': return 'SEED DATA EDGE CASE - team-specific failures';
274
+ case 'healthy': return 'HEALTHY - passes consistently';
275
+ }
276
+ }
277
+ // ─── Concurrency Control ─────────────────────────────────────────────
278
+ /**
279
+ * Simple counting semaphore for throttling concurrent work.
280
+ */
281
+ class Semaphore {
282
+ count;
283
+ waiting = [];
284
+ constructor(max) {
285
+ this.count = max === Infinity ? Number.MAX_SAFE_INTEGER : max;
286
+ }
287
+ async acquire() {
288
+ if (this.count > 0) {
289
+ this.count--;
290
+ return;
291
+ }
292
+ return new Promise((resolve) => {
293
+ this.waiting.push(resolve);
294
+ });
295
+ }
296
+ release() {
297
+ const next = this.waiting.shift();
298
+ if (next) {
299
+ next();
300
+ }
301
+ else {
302
+ this.count++;
303
+ }
304
+ }
305
+ }
306
+ //# sourceMappingURL=swarm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swarm.js","sourceRoot":"","sources":["../src/swarm.ts"],"names":[],"mappings":"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"}