@autometa/events 0.3.2 → 1.0.0-rc.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
@@ -1,3 +1,29 @@
1
- # Gherkin
1
+ # @autometa/events (v1 draft)
2
2
 
3
- Gherkin implementation for @autometa.
3
+ Autometa's event bus coordinates feature, rule, scenario, example, background, step, and hook lifecycles during a test run. The v1 rewrite introduces a richer taxonomy so runners can react to each Gherkin stage explicitly and attach structured context to their hooks.
4
+
5
+ ## Hook Vocabulary
6
+
7
+ `HookKind` now exposes per-stage before/after hooks in addition to runner-level setup/teardown:
8
+
9
+ - `beforeFeature` / `afterFeature`
10
+ - `beforeRule` / `afterRule`
11
+ - `beforeScenario` / `afterScenario`
12
+ - `beforeScenarioOutline` / `afterScenarioOutline`
13
+ - `beforeExample` / `afterExample`
14
+ - `beforeBackground` / `afterBackground`
15
+ - `beforeStep` / `afterStep`
16
+ - `setup` / `teardown`
17
+ - `custom`
18
+
19
+ Each hook descriptor can include the feature, rule, scenario, outline, example, background, or step it targets so downstream tooling can scope side effects precisely.
20
+
21
+ ## Event Payloads
22
+
23
+ Lifecycle events follow the same granularity (`feature.*`, `rule.*`, `scenario.*`, `scenarioOutline.*`, `example.*`, `background.*`, `step.*`). They carry the originating pickles, references, and metadata required to trace execution across async boundaries.
24
+
25
+ The dispatcher guarantees in-order delivery. Emitters construct canonical payloads with generated IDs and timestamps so subscribers always receive comparable shapes regardless of the underlying runner.
26
+
27
+ ## Migration Notes
28
+
29
+ Legacy `beforeEach`/`afterEach` and `beforeAll`/`afterAll` handlers must be remapped to the explicit stage hooks above. Packages awaiting migration (e.g. `@autometa/scopes`, `@autometa/test-builder`) should budget time to adopt the new event names when they move onto the v1 stack.
package/dist/bus.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { EventDispatcher } from "./dispatcher.js";
2
+ import { EventEmitter } from "./emitter.js";
3
+ export interface EventBus {
4
+ readonly dispatcher: EventDispatcher;
5
+ readonly emitter: EventEmitter;
6
+ }
7
+ export declare function getEventBus(): EventBus;
8
+ export declare function getEventDispatcher(): EventDispatcher;
9
+ export declare function getEventEmitter(): EventEmitter;
10
+ export declare function resetEventBus(): void;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Event dispatcher for Autometa lifecycle events.
3
+ */
4
+ import { type IContainer } from "@autometa/injection";
5
+ import type { EventSubscriber, TestEvent } from "./types.js";
6
+ export interface EventDispatcherOptions {
7
+ /**
8
+ * Optional container to use for resolving subscribers. If omitted a
9
+ * standalone container is created per dispatcher instance.
10
+ */
11
+ container?: IContainer;
12
+ }
13
+ export declare const EventDispatcherToken: import("@autometa/injection").Token<EventDispatcher>;
14
+ export declare class EventDispatcher {
15
+ private readonly container;
16
+ private readonly subscribers;
17
+ private sequence;
18
+ constructor(container?: IContainer);
19
+ static create(options?: EventDispatcherOptions): EventDispatcher;
20
+ /**
21
+ * Subscribe to events of a particular type.
22
+ */
23
+ subscribe<T extends TestEvent>(type: T["type"], subscriber: EventSubscriber<T>): () => void;
24
+ /**
25
+ * Publish an event to all registered subscribers. Subscribers are awaited in
26
+ * series to preserve ordering guarantees; callers can fork when concurrency is
27
+ * desired.
28
+ */
29
+ dispatch<T extends TestEvent>(event: T): Promise<void>;
30
+ clear(): void;
31
+ }
@@ -0,0 +1,97 @@
1
+ import type { SimpleExampleGroup, SimplePickle, SimplePickleFeatureRef, SimplePickleRuleRef, SimplePickleScenarioRef, SimplePickleStep, SimpleScenario, SimpleScenarioOutline } from "@autometa/gherkin";
2
+ import { EventDispatcher } from "./dispatcher.js";
3
+ import type { HookDescriptor } from "./hooks.js";
4
+ import type { ErrorEvent } from "./types.js";
5
+ import type { TestStatus } from "./status.js";
6
+ export interface EventEmitterOptions {
7
+ createId?: () => string;
8
+ now?: () => number;
9
+ }
10
+ interface BaseEmitOptions {
11
+ id?: string;
12
+ timestamp?: number;
13
+ metadata?: Record<string, unknown>;
14
+ }
15
+ export interface FeatureLifecycleOptions extends BaseEmitOptions {
16
+ feature: SimplePickleFeatureRef;
17
+ }
18
+ export interface RuleLifecycleOptions extends FeatureLifecycleOptions {
19
+ rule: SimplePickleRuleRef;
20
+ }
21
+ export interface ScenarioLifecycleOptions extends BaseEmitOptions {
22
+ feature: SimplePickleFeatureRef;
23
+ scenario: SimplePickleScenarioRef;
24
+ pickle: SimplePickle;
25
+ rule?: SimplePickleRuleRef;
26
+ }
27
+ export interface ScenarioOutlineLifecycleOptions extends BaseEmitOptions {
28
+ feature: SimplePickleFeatureRef;
29
+ scenarioOutline: SimpleScenarioOutline;
30
+ rule?: SimplePickleRuleRef;
31
+ }
32
+ export interface ExampleLifecycleOptions extends BaseEmitOptions {
33
+ feature: SimplePickleFeatureRef;
34
+ scenarioOutline: SimpleScenarioOutline;
35
+ example: SimpleExampleGroup;
36
+ pickle: SimplePickle;
37
+ rule?: SimplePickleRuleRef;
38
+ }
39
+ export interface BackgroundLifecycleOptions extends BaseEmitOptions {
40
+ feature: SimplePickleFeatureRef;
41
+ background: SimpleScenario;
42
+ pickle: SimplePickle;
43
+ rule?: SimplePickleRuleRef;
44
+ }
45
+ export interface StepLifecycleOptions extends ScenarioLifecycleOptions {
46
+ step: SimplePickleStep;
47
+ }
48
+ export interface HookEmitOptions extends BaseEmitOptions {
49
+ hook: HookDescriptor;
50
+ }
51
+ export interface StatusEmitOptions extends BaseEmitOptions {
52
+ status: TestStatus;
53
+ previousStatus?: TestStatus;
54
+ feature?: SimplePickleFeatureRef;
55
+ rule?: SimplePickleRuleRef;
56
+ scenario?: SimplePickleScenarioRef;
57
+ scenarioOutline?: SimpleScenarioOutline;
58
+ example?: SimpleExampleGroup;
59
+ pickle?: SimplePickle;
60
+ }
61
+ export interface ErrorEmitOptions extends BaseEmitOptions {
62
+ error: unknown;
63
+ phase: ErrorEvent["phase"];
64
+ feature?: SimplePickleFeatureRef;
65
+ rule?: SimplePickleRuleRef;
66
+ scenario?: SimplePickleScenarioRef;
67
+ scenarioOutline?: SimpleScenarioOutline;
68
+ example?: SimpleExampleGroup;
69
+ background?: SimpleScenario;
70
+ pickle?: SimplePickle;
71
+ }
72
+ export declare class EventEmitter {
73
+ private readonly dispatcher;
74
+ private readonly createId;
75
+ private readonly now;
76
+ constructor(dispatcher: EventDispatcher, options?: EventEmitterOptions);
77
+ featureStarted(options: FeatureLifecycleOptions): Promise<void>;
78
+ featureCompleted(options: FeatureLifecycleOptions): Promise<void>;
79
+ ruleStarted(options: RuleLifecycleOptions): Promise<void>;
80
+ ruleCompleted(options: RuleLifecycleOptions): Promise<void>;
81
+ scenarioStarted(options: ScenarioLifecycleOptions): Promise<void>;
82
+ scenarioCompleted(options: ScenarioLifecycleOptions): Promise<void>;
83
+ scenarioOutlineStarted(options: ScenarioOutlineLifecycleOptions): Promise<void>;
84
+ scenarioOutlineCompleted(options: ScenarioOutlineLifecycleOptions): Promise<void>;
85
+ exampleStarted(options: ExampleLifecycleOptions): Promise<void>;
86
+ exampleCompleted(options: ExampleLifecycleOptions): Promise<void>;
87
+ backgroundStarted(options: BackgroundLifecycleOptions): Promise<void>;
88
+ backgroundCompleted(options: BackgroundLifecycleOptions): Promise<void>;
89
+ stepStarted(options: StepLifecycleOptions): Promise<void>;
90
+ stepCompleted(options: StepLifecycleOptions): Promise<void>;
91
+ hookStarted(options: HookEmitOptions): Promise<void>;
92
+ hookCompleted(options: HookEmitOptions): Promise<void>;
93
+ statusChanged(options: StatusEmitOptions): Promise<void>;
94
+ errorRaised(options: ErrorEmitOptions): Promise<void>;
95
+ private base;
96
+ }
97
+ export {};
@@ -0,0 +1,41 @@
1
+ import { SimplePickle, SimplePickleFeatureRef, SimplePickleRuleRef, SimplePickleScenarioRef, SimplePickleStep, SimpleScenarioOutline, SimpleExampleGroup, SimpleScenario } from "@autometa/gherkin";
2
+ /** Canonical hook kinds recognised by the Autometa runner. */
3
+ export declare const HookKind: {
4
+ readonly BEFORE_FEATURE: "beforeFeature";
5
+ readonly AFTER_FEATURE: "afterFeature";
6
+ readonly BEFORE_RULE: "beforeRule";
7
+ readonly AFTER_RULE: "afterRule";
8
+ readonly BEFORE_SCENARIO: "beforeScenario";
9
+ readonly AFTER_SCENARIO: "afterScenario";
10
+ readonly BEFORE_SCENARIO_OUTLINE: "beforeScenarioOutline";
11
+ readonly AFTER_SCENARIO_OUTLINE: "afterScenarioOutline";
12
+ readonly BEFORE_EXAMPLE: "beforeExample";
13
+ readonly AFTER_EXAMPLE: "afterExample";
14
+ readonly BEFORE_BACKGROUND: "beforeBackground";
15
+ readonly AFTER_BACKGROUND: "afterBackground";
16
+ readonly BEFORE_STEP: "beforeStep";
17
+ readonly AFTER_STEP: "afterStep";
18
+ readonly SETUP: "setup";
19
+ readonly TEARDOWN: "teardown";
20
+ readonly CUSTOM: "custom";
21
+ };
22
+ export type HookKind = (typeof HookKind)[keyof typeof HookKind];
23
+ export interface HookDescriptor {
24
+ /** Categorises when the hook is executed. */
25
+ kind: HookKind;
26
+ /** Optional human friendly name for diagnostics. */
27
+ name?: string;
28
+ /** Path or identifier that produced the hook for richer reporting. */
29
+ source?: string;
30
+ /** Stage-specific context for the hook, when available. */
31
+ feature?: SimplePickleFeatureRef;
32
+ rule?: SimplePickleRuleRef;
33
+ scenario?: SimplePickleScenarioRef;
34
+ scenarioOutline?: SimpleScenarioOutline;
35
+ example?: SimpleExampleGroup;
36
+ background?: SimpleScenario;
37
+ step?: SimplePickleStep;
38
+ pickle?: SimplePickle;
39
+ /** Arbitrary metadata emitted by the hook author. */
40
+ metadata?: Record<string, unknown>;
41
+ }
package/dist/index.cjs ADDED
@@ -0,0 +1,424 @@
1
+ 'use strict';
2
+
3
+ var injection = require('@autometa/injection');
4
+
5
+ // src/dispatcher.ts
6
+ var EventDispatcherToken = injection.createToken(
7
+ "@autometa/events/dispatcher"
8
+ );
9
+ var EventDispatcher = class _EventDispatcher {
10
+ constructor(container = injection.createContainer()) {
11
+ this.container = container;
12
+ this.subscribers = /* @__PURE__ */ new Map();
13
+ this.sequence = 0;
14
+ }
15
+ static create(options = {}) {
16
+ const container = options.container ?? injection.createContainer();
17
+ if (!container.isRegistered(EventDispatcherToken)) {
18
+ container.registerValue(
19
+ EventDispatcherToken,
20
+ new _EventDispatcher(container),
21
+ { scope: injection.Scope.SINGLETON }
22
+ );
23
+ }
24
+ return container.resolve(EventDispatcherToken);
25
+ }
26
+ /**
27
+ * Subscribe to events of a particular type.
28
+ */
29
+ subscribe(type, subscriber) {
30
+ const set = this.subscribers.get(type) ?? /* @__PURE__ */ new Set();
31
+ set.add(subscriber);
32
+ this.subscribers.set(type, set);
33
+ return () => {
34
+ set.delete(subscriber);
35
+ if (set.size === 0) {
36
+ this.subscribers.delete(type);
37
+ }
38
+ };
39
+ }
40
+ /**
41
+ * Publish an event to all registered subscribers. Subscribers are awaited in
42
+ * series to preserve ordering guarantees; callers can fork when concurrency is
43
+ * desired.
44
+ */
45
+ async dispatch(event) {
46
+ const envelope = {
47
+ sequence: ++this.sequence,
48
+ event
49
+ };
50
+ const listeners = this.subscribers.get(event.type);
51
+ if (!listeners || listeners.size === 0) {
52
+ return;
53
+ }
54
+ for (const listener of listeners) {
55
+ await listener(envelope);
56
+ }
57
+ }
58
+ clear() {
59
+ this.subscribers.clear();
60
+ this.sequence = 0;
61
+ }
62
+ };
63
+
64
+ // src/emitter.ts
65
+ var defaultIdFactory = () => {
66
+ const crypto = globalThis.crypto;
67
+ if (crypto && typeof crypto.randomUUID === "function") {
68
+ return crypto.randomUUID();
69
+ }
70
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
71
+ };
72
+ var EventEmitter = class {
73
+ constructor(dispatcher, options = {}) {
74
+ this.dispatcher = dispatcher;
75
+ this.createId = options.createId ?? defaultIdFactory;
76
+ this.now = options.now ?? Date.now;
77
+ }
78
+ async featureStarted(options) {
79
+ const event = {
80
+ type: "feature.started",
81
+ feature: options.feature,
82
+ ...this.base(options)
83
+ };
84
+ await this.dispatcher.dispatch(event);
85
+ }
86
+ async featureCompleted(options) {
87
+ const event = {
88
+ type: "feature.completed",
89
+ feature: options.feature,
90
+ ...this.base(options)
91
+ };
92
+ await this.dispatcher.dispatch(event);
93
+ }
94
+ async ruleStarted(options) {
95
+ const event = {
96
+ type: "rule.started",
97
+ feature: options.feature,
98
+ rule: options.rule,
99
+ ...this.base(options)
100
+ };
101
+ await this.dispatcher.dispatch(event);
102
+ }
103
+ async ruleCompleted(options) {
104
+ const event = {
105
+ type: "rule.completed",
106
+ feature: options.feature,
107
+ rule: options.rule,
108
+ ...this.base(options)
109
+ };
110
+ await this.dispatcher.dispatch(event);
111
+ }
112
+ async scenarioStarted(options) {
113
+ const event = {
114
+ type: "scenario.started",
115
+ feature: options.feature,
116
+ scenario: options.scenario,
117
+ pickle: options.pickle,
118
+ ...options.rule ? { rule: options.rule } : {},
119
+ ...this.base(options)
120
+ };
121
+ await this.dispatcher.dispatch(event);
122
+ }
123
+ async scenarioCompleted(options) {
124
+ const event = {
125
+ type: "scenario.completed",
126
+ feature: options.feature,
127
+ scenario: options.scenario,
128
+ pickle: options.pickle,
129
+ ...options.rule ? { rule: options.rule } : {},
130
+ ...this.base(options)
131
+ };
132
+ await this.dispatcher.dispatch(event);
133
+ }
134
+ async scenarioOutlineStarted(options) {
135
+ const event = {
136
+ type: "scenarioOutline.started",
137
+ feature: options.feature,
138
+ scenarioOutline: options.scenarioOutline,
139
+ ...options.rule ? { rule: options.rule } : {},
140
+ ...this.base(options)
141
+ };
142
+ await this.dispatcher.dispatch(event);
143
+ }
144
+ async scenarioOutlineCompleted(options) {
145
+ const event = {
146
+ type: "scenarioOutline.completed",
147
+ feature: options.feature,
148
+ scenarioOutline: options.scenarioOutline,
149
+ ...options.rule ? { rule: options.rule } : {},
150
+ ...this.base(options)
151
+ };
152
+ await this.dispatcher.dispatch(event);
153
+ }
154
+ async exampleStarted(options) {
155
+ const event = {
156
+ type: "example.started",
157
+ feature: options.feature,
158
+ scenarioOutline: options.scenarioOutline,
159
+ example: options.example,
160
+ pickle: options.pickle,
161
+ ...options.rule ? { rule: options.rule } : {},
162
+ ...this.base(options)
163
+ };
164
+ await this.dispatcher.dispatch(event);
165
+ }
166
+ async exampleCompleted(options) {
167
+ const event = {
168
+ type: "example.completed",
169
+ feature: options.feature,
170
+ scenarioOutline: options.scenarioOutline,
171
+ example: options.example,
172
+ pickle: options.pickle,
173
+ ...options.rule ? { rule: options.rule } : {},
174
+ ...this.base(options)
175
+ };
176
+ await this.dispatcher.dispatch(event);
177
+ }
178
+ async backgroundStarted(options) {
179
+ const event = {
180
+ type: "background.started",
181
+ feature: options.feature,
182
+ background: options.background,
183
+ pickle: options.pickle,
184
+ ...options.rule ? { rule: options.rule } : {},
185
+ ...this.base(options)
186
+ };
187
+ await this.dispatcher.dispatch(event);
188
+ }
189
+ async backgroundCompleted(options) {
190
+ const event = {
191
+ type: "background.completed",
192
+ feature: options.feature,
193
+ background: options.background,
194
+ pickle: options.pickle,
195
+ ...options.rule ? { rule: options.rule } : {},
196
+ ...this.base(options)
197
+ };
198
+ await this.dispatcher.dispatch(event);
199
+ }
200
+ async stepStarted(options) {
201
+ const event = {
202
+ type: "step.started",
203
+ feature: options.feature,
204
+ scenario: options.scenario,
205
+ step: options.step,
206
+ pickle: options.pickle,
207
+ ...options.rule ? { rule: options.rule } : {},
208
+ ...this.base(options)
209
+ };
210
+ await this.dispatcher.dispatch(event);
211
+ }
212
+ async stepCompleted(options) {
213
+ const event = {
214
+ type: "step.completed",
215
+ feature: options.feature,
216
+ scenario: options.scenario,
217
+ step: options.step,
218
+ pickle: options.pickle,
219
+ ...options.rule ? { rule: options.rule } : {},
220
+ ...this.base(options)
221
+ };
222
+ await this.dispatcher.dispatch(event);
223
+ }
224
+ async hookStarted(options) {
225
+ const event = {
226
+ type: "hook.started",
227
+ hook: options.hook,
228
+ ...options.hook.feature ? { feature: options.hook.feature } : {},
229
+ ...options.hook.rule ? { rule: options.hook.rule } : {},
230
+ ...options.hook.scenario ? { scenario: options.hook.scenario } : {},
231
+ ...options.hook.scenarioOutline ? { scenarioOutline: options.hook.scenarioOutline } : {},
232
+ ...options.hook.example ? { example: options.hook.example } : {},
233
+ ...options.hook.background ? { background: options.hook.background } : {},
234
+ ...options.hook.step ? { step: options.hook.step } : {},
235
+ ...options.hook.pickle ? { pickle: options.hook.pickle } : {},
236
+ ...this.base(options)
237
+ };
238
+ await this.dispatcher.dispatch(event);
239
+ }
240
+ async hookCompleted(options) {
241
+ const event = {
242
+ type: "hook.completed",
243
+ hook: options.hook,
244
+ ...options.hook.feature ? { feature: options.hook.feature } : {},
245
+ ...options.hook.rule ? { rule: options.hook.rule } : {},
246
+ ...options.hook.scenario ? { scenario: options.hook.scenario } : {},
247
+ ...options.hook.scenarioOutline ? { scenarioOutline: options.hook.scenarioOutline } : {},
248
+ ...options.hook.example ? { example: options.hook.example } : {},
249
+ ...options.hook.background ? { background: options.hook.background } : {},
250
+ ...options.hook.step ? { step: options.hook.step } : {},
251
+ ...options.hook.pickle ? { pickle: options.hook.pickle } : {},
252
+ ...this.base(options)
253
+ };
254
+ await this.dispatcher.dispatch(event);
255
+ }
256
+ async statusChanged(options) {
257
+ const event = {
258
+ type: "status.changed",
259
+ status: options.status,
260
+ ...options.previousStatus ? { previousStatus: options.previousStatus } : {},
261
+ ...options.feature ? { feature: options.feature } : {},
262
+ ...options.rule ? { rule: options.rule } : {},
263
+ ...options.scenario ? { scenario: options.scenario } : {},
264
+ ...options.scenarioOutline ? { scenarioOutline: options.scenarioOutline } : {},
265
+ ...options.example ? { example: options.example } : {},
266
+ ...options.pickle ? { pickle: options.pickle } : {},
267
+ ...this.base(options)
268
+ };
269
+ await this.dispatcher.dispatch(event);
270
+ }
271
+ async errorRaised(options) {
272
+ const event = {
273
+ type: "error",
274
+ error: options.error,
275
+ phase: options.phase,
276
+ ...options.feature ? { feature: options.feature } : {},
277
+ ...options.rule ? { rule: options.rule } : {},
278
+ ...options.scenario ? { scenario: options.scenario } : {},
279
+ ...options.scenarioOutline ? { scenarioOutline: options.scenarioOutline } : {},
280
+ ...options.example ? { example: options.example } : {},
281
+ ...options.background ? { background: options.background } : {},
282
+ ...options.pickle ? { pickle: options.pickle } : {},
283
+ ...this.base(options)
284
+ };
285
+ await this.dispatcher.dispatch(event);
286
+ }
287
+ base(options) {
288
+ const base = {
289
+ id: options.id ?? this.createId(),
290
+ timestamp: options.timestamp ?? this.now()
291
+ };
292
+ if (options.metadata !== void 0) {
293
+ base.metadata = options.metadata;
294
+ }
295
+ return base;
296
+ }
297
+ };
298
+
299
+ // src/bus.ts
300
+ var BUS_KEY = Symbol.for("@autometa/events/bus");
301
+ function getEventBus() {
302
+ const store = globalThis;
303
+ const existing = store[BUS_KEY];
304
+ if (existing) {
305
+ return existing;
306
+ }
307
+ const dispatcher = EventDispatcher.create();
308
+ const emitter = new EventEmitter(dispatcher);
309
+ const bus = { dispatcher, emitter };
310
+ store[BUS_KEY] = bus;
311
+ return bus;
312
+ }
313
+ function getEventDispatcher() {
314
+ return getEventBus().dispatcher;
315
+ }
316
+ function getEventEmitter() {
317
+ return getEventBus().emitter;
318
+ }
319
+ function resetEventBus() {
320
+ const store = globalThis;
321
+ const existing = store[BUS_KEY];
322
+ if (existing) {
323
+ existing.dispatcher.clear();
324
+ }
325
+ store[BUS_KEY] = void 0;
326
+ }
327
+
328
+ // src/hooks.ts
329
+ var HookKind = {
330
+ BEFORE_FEATURE: "beforeFeature",
331
+ AFTER_FEATURE: "afterFeature",
332
+ BEFORE_RULE: "beforeRule",
333
+ AFTER_RULE: "afterRule",
334
+ BEFORE_SCENARIO: "beforeScenario",
335
+ AFTER_SCENARIO: "afterScenario",
336
+ BEFORE_SCENARIO_OUTLINE: "beforeScenarioOutline",
337
+ AFTER_SCENARIO_OUTLINE: "afterScenarioOutline",
338
+ BEFORE_EXAMPLE: "beforeExample",
339
+ AFTER_EXAMPLE: "afterExample",
340
+ BEFORE_BACKGROUND: "beforeBackground",
341
+ AFTER_BACKGROUND: "afterBackground",
342
+ BEFORE_STEP: "beforeStep",
343
+ AFTER_STEP: "afterStep",
344
+ SETUP: "setup",
345
+ TEARDOWN: "teardown",
346
+ CUSTOM: "custom"
347
+ };
348
+
349
+ // src/listener.ts
350
+ function registerTestListener(listener, options = {}) {
351
+ const dispatcher = options.dispatcher ?? getEventDispatcher();
352
+ const unsubscribers = [];
353
+ const subscribe = (type, handler) => {
354
+ if (!handler && !listener.onEvent) {
355
+ return;
356
+ }
357
+ unsubscribers.push(
358
+ dispatcher.subscribe(type, async (envelope) => {
359
+ if (listener.onEvent) {
360
+ await listener.onEvent(envelope);
361
+ }
362
+ if (handler) {
363
+ await handler(envelope);
364
+ }
365
+ })
366
+ );
367
+ };
368
+ subscribe("feature.started", listener.onFeatureStarted);
369
+ subscribe("feature.completed", listener.onFeatureCompleted);
370
+ subscribe("rule.started", listener.onRuleStarted);
371
+ subscribe("rule.completed", listener.onRuleCompleted);
372
+ subscribe("scenario.started", listener.onScenarioStarted);
373
+ subscribe("scenario.completed", listener.onScenarioCompleted);
374
+ subscribe(
375
+ "scenarioOutline.started",
376
+ listener.onScenarioOutlineStarted
377
+ );
378
+ subscribe(
379
+ "scenarioOutline.completed",
380
+ listener.onScenarioOutlineCompleted
381
+ );
382
+ subscribe("example.started", listener.onExampleStarted);
383
+ subscribe("example.completed", listener.onExampleCompleted);
384
+ subscribe("background.started", listener.onBackgroundStarted);
385
+ subscribe("background.completed", listener.onBackgroundCompleted);
386
+ subscribe("step.started", listener.onStepStarted);
387
+ subscribe("step.completed", listener.onStepCompleted);
388
+ subscribe("hook.started", listener.onHookStarted);
389
+ subscribe("hook.completed", listener.onHookCompleted);
390
+ subscribe("status.changed", listener.onStatusChanged);
391
+ subscribe("error", listener.onError);
392
+ return () => {
393
+ for (const unsubscribe of unsubscribers) {
394
+ unsubscribe();
395
+ }
396
+ };
397
+ }
398
+
399
+ // src/status.ts
400
+ var TestStatus = {
401
+ IDLE: "idle",
402
+ RUNNING: "running",
403
+ PASSED: "passed",
404
+ FAILED: "failed",
405
+ SKIPPED: "skipped",
406
+ BROKEN: "broken"
407
+ };
408
+ function isTerminalStatus(status) {
409
+ return status === TestStatus.PASSED || status === TestStatus.FAILED || status === TestStatus.SKIPPED || status === TestStatus.BROKEN;
410
+ }
411
+
412
+ exports.EventDispatcher = EventDispatcher;
413
+ exports.EventDispatcherToken = EventDispatcherToken;
414
+ exports.EventEmitter = EventEmitter;
415
+ exports.HookKind = HookKind;
416
+ exports.TestStatus = TestStatus;
417
+ exports.getEventBus = getEventBus;
418
+ exports.getEventDispatcher = getEventDispatcher;
419
+ exports.getEventEmitter = getEventEmitter;
420
+ exports.isTerminalStatus = isTerminalStatus;
421
+ exports.registerTestListener = registerTestListener;
422
+ exports.resetEventBus = resetEventBus;
423
+ //# sourceMappingURL=out.js.map
424
+ //# sourceMappingURL=index.cjs.map