autotel-subscribers 52.0.0 → 54.0.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 +8 -8
- package/dist/factories.cjs +2 -144
- package/dist/factories.d.cts +2 -80
- package/dist/factories.d.ts +2 -80
- package/dist/factories.js +1 -143
- package/dist/mock-event-subscriber-BKa77OVK.js +144 -0
- package/dist/mock-event-subscriber-BOYF83Kq.cjs +150 -0
- package/dist/mock-event-subscriber-CfAyAiQn.d.cts +81 -0
- package/dist/mock-event-subscriber-CfAyAiQn.d.ts +81 -0
- package/dist/testing.cjs +421 -0
- package/dist/testing.d.cts +154 -0
- package/dist/testing.d.ts +154 -0
- package/dist/testing.js +416 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -151,12 +151,12 @@ const events = new Event('my-app', {
|
|
|
151
151
|
### Test Your Adapter
|
|
152
152
|
|
|
153
153
|
```typescript
|
|
154
|
-
import {
|
|
154
|
+
import { SubscriberTestHarness } from 'autotel-subscribers/testing';
|
|
155
155
|
|
|
156
|
-
const harness = new
|
|
156
|
+
const harness = new SubscriberTestHarness(new MySubscriber('test-key'));
|
|
157
157
|
const results = await harness.runAll();
|
|
158
158
|
|
|
159
|
-
|
|
159
|
+
SubscriberTestHarness.printResults(results);
|
|
160
160
|
// All tests passed! Your adapter is ready to use.
|
|
161
161
|
```
|
|
162
162
|
|
|
@@ -619,14 +619,14 @@ const subscriber = applyMiddleware(adapter, [
|
|
|
619
619
|
|
|
620
620
|
## Testing Custom Subscribers
|
|
621
621
|
|
|
622
|
-
###
|
|
622
|
+
### SubscriberTestHarness
|
|
623
623
|
|
|
624
624
|
Validate your adapter works correctly:
|
|
625
625
|
|
|
626
626
|
```typescript
|
|
627
|
-
import {
|
|
627
|
+
import { SubscriberTestHarness } from 'autotel-subscribers/testing';
|
|
628
628
|
|
|
629
|
-
const harness = new
|
|
629
|
+
const harness = new SubscriberTestHarness(new MySubscriber());
|
|
630
630
|
const results = await harness.runAll();
|
|
631
631
|
|
|
632
632
|
if (results.passed) {
|
|
@@ -636,7 +636,7 @@ if (results.passed) {
|
|
|
636
636
|
}
|
|
637
637
|
|
|
638
638
|
// Or use the built-in printer
|
|
639
|
-
|
|
639
|
+
SubscriberTestHarness.printResults(results);
|
|
640
640
|
```
|
|
641
641
|
|
|
642
642
|
Tests include:
|
|
@@ -699,7 +699,7 @@ import {
|
|
|
699
699
|
|
|
700
700
|
// Testing utilities
|
|
701
701
|
import {
|
|
702
|
-
|
|
702
|
+
SubscriberTestHarness,
|
|
703
703
|
MockWebhookServer,
|
|
704
704
|
MockEventSubscriber,
|
|
705
705
|
} from 'autotel-subscribers/testing';
|
package/dist/factories.cjs
CHANGED
|
@@ -4,150 +4,8 @@ const require_segment = require('./segment.cjs');
|
|
|
4
4
|
const require_amplitude = require('./amplitude.cjs');
|
|
5
5
|
const require_slack = require('./slack.cjs');
|
|
6
6
|
const require_webhook = require('./webhook.cjs');
|
|
7
|
+
const require_mock_event_subscriber = require('./mock-event-subscriber-BOYF83Kq.cjs');
|
|
7
8
|
|
|
8
|
-
//#region src/mock-event-subscriber.ts
|
|
9
|
-
/**
|
|
10
|
-
* Mock subscriber for testing
|
|
11
|
-
*
|
|
12
|
-
* Captures all events calls in memory for test assertions.
|
|
13
|
-
* Does not make any real API calls.
|
|
14
|
-
*/
|
|
15
|
-
var MockEventSubscriber = class {
|
|
16
|
-
name = "MockEventSubscriber";
|
|
17
|
-
version = "1.0.0";
|
|
18
|
-
/**
|
|
19
|
-
* All captured events
|
|
20
|
-
*/
|
|
21
|
-
events = [];
|
|
22
|
-
/**
|
|
23
|
-
* Track if shutdown was called
|
|
24
|
-
*/
|
|
25
|
-
shutdownCalled = false;
|
|
26
|
-
async trackEvent(name, attributes) {
|
|
27
|
-
this.events.push({
|
|
28
|
-
type: "event",
|
|
29
|
-
name,
|
|
30
|
-
attributes,
|
|
31
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
async trackFunnelStep(funnelName, step, attributes) {
|
|
35
|
-
this.events.push({
|
|
36
|
-
type: "funnel",
|
|
37
|
-
name: `${funnelName}.${step}`,
|
|
38
|
-
funnel: funnelName,
|
|
39
|
-
step,
|
|
40
|
-
attributes,
|
|
41
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
async trackOutcome(operationName, outcome, attributes) {
|
|
45
|
-
this.events.push({
|
|
46
|
-
type: "outcome",
|
|
47
|
-
name: `${operationName}.${outcome}`,
|
|
48
|
-
operation: operationName,
|
|
49
|
-
outcome,
|
|
50
|
-
attributes,
|
|
51
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
async trackValue(name, value, attributes) {
|
|
55
|
-
this.events.push({
|
|
56
|
-
type: "value",
|
|
57
|
-
name,
|
|
58
|
-
value,
|
|
59
|
-
attributes,
|
|
60
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
async shutdown() {
|
|
64
|
-
this.shutdownCalled = true;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Reset captured events (useful between tests)
|
|
68
|
-
*/
|
|
69
|
-
reset() {
|
|
70
|
-
this.events = [];
|
|
71
|
-
this.shutdownCalled = false;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Get events by type
|
|
75
|
-
*/
|
|
76
|
-
getEventsByType(type) {
|
|
77
|
-
return this.events.filter((e) => e.type === type);
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Get events by name
|
|
81
|
-
*/
|
|
82
|
-
getEventsByName(name) {
|
|
83
|
-
return this.events.filter((e) => e.name === name);
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Get funnel events for a specific funnel
|
|
87
|
-
*/
|
|
88
|
-
getFunnelEvents(funnelName) {
|
|
89
|
-
return this.events.filter((e) => e.type === "funnel" && e.funnel === funnelName);
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Get outcome events for a specific operation
|
|
93
|
-
*/
|
|
94
|
-
getOutcomeEvents(operationName) {
|
|
95
|
-
return this.events.filter((e) => e.type === "outcome" && e.operation === operationName);
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Get value events by metric name
|
|
99
|
-
*/
|
|
100
|
-
getValueEvents(metricName) {
|
|
101
|
-
return this.events.filter((e) => e.type === "value" && e.name === metricName);
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Assert that an event was tracked
|
|
105
|
-
*/
|
|
106
|
-
assertEventTracked(name, attributes) {
|
|
107
|
-
const events = this.getEventsByName(name);
|
|
108
|
-
if (events.length === 0) throw new Error(`No events found with name: ${name}`);
|
|
109
|
-
if (attributes) {
|
|
110
|
-
if (!events.find((event) => {
|
|
111
|
-
if (!event.attributes) return false;
|
|
112
|
-
return Object.entries(attributes).every(([key, value]) => event.attributes[key] === value);
|
|
113
|
-
})) throw new Error(`Event "${name}" found, but no matching attributes: ${JSON.stringify(attributes)}`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Assert that a funnel step was tracked
|
|
118
|
-
*/
|
|
119
|
-
assertFunnelStepTracked(funnelName, step, attributes) {
|
|
120
|
-
const matchingEvent = this.getFunnelEvents(funnelName).find((e) => e.step === step);
|
|
121
|
-
if (!matchingEvent) throw new Error(`Funnel "${funnelName}" step "${step}" was not tracked`);
|
|
122
|
-
if (attributes) {
|
|
123
|
-
if (!Object.entries(attributes).every(([key, value]) => matchingEvent.attributes?.[key] === value)) throw new Error(`Funnel step tracked, but attributes don't match: ${JSON.stringify(attributes)}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Assert that an outcome was tracked
|
|
128
|
-
*/
|
|
129
|
-
assertOutcomeTracked(operationName, outcome, attributes) {
|
|
130
|
-
const matchingEvent = this.getOutcomeEvents(operationName).find((e) => e.outcome === outcome);
|
|
131
|
-
if (!matchingEvent) throw new Error(`Outcome "${operationName}.${outcome}" was not tracked`);
|
|
132
|
-
if (attributes) {
|
|
133
|
-
if (!Object.entries(attributes).every(([key, value]) => matchingEvent.attributes?.[key] === value)) throw new Error(`Outcome tracked, but attributes don't match: ${JSON.stringify(attributes)}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Pretty print all captured events (useful for debugging)
|
|
138
|
-
*/
|
|
139
|
-
printEvents() {
|
|
140
|
-
console.log(`\n[${this.name}] Captured ${this.events.length} events:\n`);
|
|
141
|
-
for (const [index, event] of this.events.entries()) {
|
|
142
|
-
console.log(`${index + 1}. [${event.type}] ${event.name}`);
|
|
143
|
-
if (event.attributes) console.log(` Attributes:`, event.attributes);
|
|
144
|
-
if (event.value !== void 0) console.log(` Value: ${event.value}`);
|
|
145
|
-
console.log(` Timestamp: ${event.timestamp}\n`);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
//#endregion
|
|
151
9
|
//#region src/factories.ts
|
|
152
10
|
/**
|
|
153
11
|
* Factory functions for creating events subscribers
|
|
@@ -188,7 +46,7 @@ function createSlackSubscriber(config) {
|
|
|
188
46
|
}
|
|
189
47
|
/** Create a mock events subscriber for testing */
|
|
190
48
|
function createMockSubscriber() {
|
|
191
|
-
return new MockEventSubscriber();
|
|
49
|
+
return new require_mock_event_subscriber.MockEventSubscriber();
|
|
192
50
|
}
|
|
193
51
|
function backoffDelay(attempt, initialMs, maxMs) {
|
|
194
52
|
return Math.min(maxMs, initialMs * 2 ** (attempt - 1));
|
package/dist/factories.d.cts
CHANGED
|
@@ -1,88 +1,10 @@
|
|
|
1
1
|
import { AmplitudeConfig } from "./amplitude.cjs";
|
|
2
|
+
import { t as MockEventSubscriber } from "./mock-event-subscriber-CfAyAiQn.cjs";
|
|
2
3
|
import { MixpanelConfig } from "./mixpanel.cjs";
|
|
3
4
|
import { SegmentConfig } from "./segment.cjs";
|
|
4
5
|
import { WebhookConfig } from "./webhook.cjs";
|
|
5
6
|
import { SlackSubscriberConfig } from "./slack.cjs";
|
|
6
|
-
import {
|
|
7
|
-
//#region src/mock-event-subscriber.d.ts
|
|
8
|
-
/**
|
|
9
|
-
* Captured event data
|
|
10
|
-
*/
|
|
11
|
-
interface CapturedEvent {
|
|
12
|
-
type: 'event' | 'funnel' | 'outcome' | 'value';
|
|
13
|
-
name: string;
|
|
14
|
-
attributes?: EventAttributes;
|
|
15
|
-
funnel?: string;
|
|
16
|
-
step?: FunnelStatus;
|
|
17
|
-
operation?: string;
|
|
18
|
-
outcome?: OutcomeStatus;
|
|
19
|
-
value?: number;
|
|
20
|
-
timestamp: string;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Mock subscriber for testing
|
|
24
|
-
*
|
|
25
|
-
* Captures all events calls in memory for test assertions.
|
|
26
|
-
* Does not make any real API calls.
|
|
27
|
-
*/
|
|
28
|
-
declare class MockEventSubscriber implements EventSubscriber {
|
|
29
|
-
readonly name = "MockEventSubscriber";
|
|
30
|
-
readonly version = "1.0.0";
|
|
31
|
-
/**
|
|
32
|
-
* All captured events
|
|
33
|
-
*/
|
|
34
|
-
events: CapturedEvent[];
|
|
35
|
-
/**
|
|
36
|
-
* Track if shutdown was called
|
|
37
|
-
*/
|
|
38
|
-
shutdownCalled: boolean;
|
|
39
|
-
trackEvent(name: string, attributes?: EventAttributes): Promise<void>;
|
|
40
|
-
trackFunnelStep(funnelName: string, step: FunnelStatus, attributes?: EventAttributes): Promise<void>;
|
|
41
|
-
trackOutcome(operationName: string, outcome: OutcomeStatus, attributes?: EventAttributes): Promise<void>;
|
|
42
|
-
trackValue(name: string, value: number, attributes?: EventAttributes): Promise<void>;
|
|
43
|
-
shutdown(): Promise<void>;
|
|
44
|
-
/**
|
|
45
|
-
* Reset captured events (useful between tests)
|
|
46
|
-
*/
|
|
47
|
-
reset(): void;
|
|
48
|
-
/**
|
|
49
|
-
* Get events by type
|
|
50
|
-
*/
|
|
51
|
-
getEventsByType(type: 'event' | 'funnel' | 'outcome' | 'value'): CapturedEvent[];
|
|
52
|
-
/**
|
|
53
|
-
* Get events by name
|
|
54
|
-
*/
|
|
55
|
-
getEventsByName(name: string): CapturedEvent[];
|
|
56
|
-
/**
|
|
57
|
-
* Get funnel events for a specific funnel
|
|
58
|
-
*/
|
|
59
|
-
getFunnelEvents(funnelName: string): CapturedEvent[];
|
|
60
|
-
/**
|
|
61
|
-
* Get outcome events for a specific operation
|
|
62
|
-
*/
|
|
63
|
-
getOutcomeEvents(operationName: string): CapturedEvent[];
|
|
64
|
-
/**
|
|
65
|
-
* Get value events by metric name
|
|
66
|
-
*/
|
|
67
|
-
getValueEvents(metricName: string): CapturedEvent[];
|
|
68
|
-
/**
|
|
69
|
-
* Assert that an event was tracked
|
|
70
|
-
*/
|
|
71
|
-
assertEventTracked(name: string, attributes?: Partial<EventAttributes>): void;
|
|
72
|
-
/**
|
|
73
|
-
* Assert that a funnel step was tracked
|
|
74
|
-
*/
|
|
75
|
-
assertFunnelStepTracked(funnelName: string, step: FunnelStatus, attributes?: Partial<EventAttributes>): void;
|
|
76
|
-
/**
|
|
77
|
-
* Assert that an outcome was tracked
|
|
78
|
-
*/
|
|
79
|
-
assertOutcomeTracked(operationName: string, outcome: OutcomeStatus, attributes?: Partial<EventAttributes>): void;
|
|
80
|
-
/**
|
|
81
|
-
* Pretty print all captured events (useful for debugging)
|
|
82
|
-
*/
|
|
83
|
-
printEvents(): void;
|
|
84
|
-
}
|
|
85
|
-
//#endregion
|
|
7
|
+
import { EventSubscriber } from "autotel/event-subscriber";
|
|
86
8
|
//#region src/factories.d.ts
|
|
87
9
|
/** Create a Mixpanel events subscriber */
|
|
88
10
|
declare function createMixpanelSubscriber(config: {
|
package/dist/factories.d.ts
CHANGED
|
@@ -1,88 +1,10 @@
|
|
|
1
1
|
import { AmplitudeConfig } from "./amplitude.js";
|
|
2
|
+
import { t as MockEventSubscriber } from "./mock-event-subscriber-CfAyAiQn.js";
|
|
2
3
|
import { MixpanelConfig } from "./mixpanel.js";
|
|
3
4
|
import { SegmentConfig } from "./segment.js";
|
|
4
5
|
import { WebhookConfig } from "./webhook.js";
|
|
5
6
|
import { SlackSubscriberConfig } from "./slack.js";
|
|
6
|
-
import {
|
|
7
|
-
//#region src/mock-event-subscriber.d.ts
|
|
8
|
-
/**
|
|
9
|
-
* Captured event data
|
|
10
|
-
*/
|
|
11
|
-
interface CapturedEvent {
|
|
12
|
-
type: 'event' | 'funnel' | 'outcome' | 'value';
|
|
13
|
-
name: string;
|
|
14
|
-
attributes?: EventAttributes;
|
|
15
|
-
funnel?: string;
|
|
16
|
-
step?: FunnelStatus;
|
|
17
|
-
operation?: string;
|
|
18
|
-
outcome?: OutcomeStatus;
|
|
19
|
-
value?: number;
|
|
20
|
-
timestamp: string;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Mock subscriber for testing
|
|
24
|
-
*
|
|
25
|
-
* Captures all events calls in memory for test assertions.
|
|
26
|
-
* Does not make any real API calls.
|
|
27
|
-
*/
|
|
28
|
-
declare class MockEventSubscriber implements EventSubscriber {
|
|
29
|
-
readonly name = "MockEventSubscriber";
|
|
30
|
-
readonly version = "1.0.0";
|
|
31
|
-
/**
|
|
32
|
-
* All captured events
|
|
33
|
-
*/
|
|
34
|
-
events: CapturedEvent[];
|
|
35
|
-
/**
|
|
36
|
-
* Track if shutdown was called
|
|
37
|
-
*/
|
|
38
|
-
shutdownCalled: boolean;
|
|
39
|
-
trackEvent(name: string, attributes?: EventAttributes): Promise<void>;
|
|
40
|
-
trackFunnelStep(funnelName: string, step: FunnelStatus, attributes?: EventAttributes): Promise<void>;
|
|
41
|
-
trackOutcome(operationName: string, outcome: OutcomeStatus, attributes?: EventAttributes): Promise<void>;
|
|
42
|
-
trackValue(name: string, value: number, attributes?: EventAttributes): Promise<void>;
|
|
43
|
-
shutdown(): Promise<void>;
|
|
44
|
-
/**
|
|
45
|
-
* Reset captured events (useful between tests)
|
|
46
|
-
*/
|
|
47
|
-
reset(): void;
|
|
48
|
-
/**
|
|
49
|
-
* Get events by type
|
|
50
|
-
*/
|
|
51
|
-
getEventsByType(type: 'event' | 'funnel' | 'outcome' | 'value'): CapturedEvent[];
|
|
52
|
-
/**
|
|
53
|
-
* Get events by name
|
|
54
|
-
*/
|
|
55
|
-
getEventsByName(name: string): CapturedEvent[];
|
|
56
|
-
/**
|
|
57
|
-
* Get funnel events for a specific funnel
|
|
58
|
-
*/
|
|
59
|
-
getFunnelEvents(funnelName: string): CapturedEvent[];
|
|
60
|
-
/**
|
|
61
|
-
* Get outcome events for a specific operation
|
|
62
|
-
*/
|
|
63
|
-
getOutcomeEvents(operationName: string): CapturedEvent[];
|
|
64
|
-
/**
|
|
65
|
-
* Get value events by metric name
|
|
66
|
-
*/
|
|
67
|
-
getValueEvents(metricName: string): CapturedEvent[];
|
|
68
|
-
/**
|
|
69
|
-
* Assert that an event was tracked
|
|
70
|
-
*/
|
|
71
|
-
assertEventTracked(name: string, attributes?: Partial<EventAttributes>): void;
|
|
72
|
-
/**
|
|
73
|
-
* Assert that a funnel step was tracked
|
|
74
|
-
*/
|
|
75
|
-
assertFunnelStepTracked(funnelName: string, step: FunnelStatus, attributes?: Partial<EventAttributes>): void;
|
|
76
|
-
/**
|
|
77
|
-
* Assert that an outcome was tracked
|
|
78
|
-
*/
|
|
79
|
-
assertOutcomeTracked(operationName: string, outcome: OutcomeStatus, attributes?: Partial<EventAttributes>): void;
|
|
80
|
-
/**
|
|
81
|
-
* Pretty print all captured events (useful for debugging)
|
|
82
|
-
*/
|
|
83
|
-
printEvents(): void;
|
|
84
|
-
}
|
|
85
|
-
//#endregion
|
|
7
|
+
import { EventSubscriber } from "autotel/event-subscriber";
|
|
86
8
|
//#region src/factories.d.ts
|
|
87
9
|
/** Create a Mixpanel events subscriber */
|
|
88
10
|
declare function createMixpanelSubscriber(config: {
|
package/dist/factories.js
CHANGED
|
@@ -3,150 +3,8 @@ import { SegmentSubscriber } from "./segment.js";
|
|
|
3
3
|
import { AmplitudeSubscriber } from "./amplitude.js";
|
|
4
4
|
import { SlackSubscriber } from "./slack.js";
|
|
5
5
|
import { WebhookSubscriber } from "./webhook.js";
|
|
6
|
+
import { t as MockEventSubscriber } from "./mock-event-subscriber-BKa77OVK.js";
|
|
6
7
|
|
|
7
|
-
//#region src/mock-event-subscriber.ts
|
|
8
|
-
/**
|
|
9
|
-
* Mock subscriber for testing
|
|
10
|
-
*
|
|
11
|
-
* Captures all events calls in memory for test assertions.
|
|
12
|
-
* Does not make any real API calls.
|
|
13
|
-
*/
|
|
14
|
-
var MockEventSubscriber = class {
|
|
15
|
-
name = "MockEventSubscriber";
|
|
16
|
-
version = "1.0.0";
|
|
17
|
-
/**
|
|
18
|
-
* All captured events
|
|
19
|
-
*/
|
|
20
|
-
events = [];
|
|
21
|
-
/**
|
|
22
|
-
* Track if shutdown was called
|
|
23
|
-
*/
|
|
24
|
-
shutdownCalled = false;
|
|
25
|
-
async trackEvent(name, attributes) {
|
|
26
|
-
this.events.push({
|
|
27
|
-
type: "event",
|
|
28
|
-
name,
|
|
29
|
-
attributes,
|
|
30
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
async trackFunnelStep(funnelName, step, attributes) {
|
|
34
|
-
this.events.push({
|
|
35
|
-
type: "funnel",
|
|
36
|
-
name: `${funnelName}.${step}`,
|
|
37
|
-
funnel: funnelName,
|
|
38
|
-
step,
|
|
39
|
-
attributes,
|
|
40
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
async trackOutcome(operationName, outcome, attributes) {
|
|
44
|
-
this.events.push({
|
|
45
|
-
type: "outcome",
|
|
46
|
-
name: `${operationName}.${outcome}`,
|
|
47
|
-
operation: operationName,
|
|
48
|
-
outcome,
|
|
49
|
-
attributes,
|
|
50
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
async trackValue(name, value, attributes) {
|
|
54
|
-
this.events.push({
|
|
55
|
-
type: "value",
|
|
56
|
-
name,
|
|
57
|
-
value,
|
|
58
|
-
attributes,
|
|
59
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
async shutdown() {
|
|
63
|
-
this.shutdownCalled = true;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Reset captured events (useful between tests)
|
|
67
|
-
*/
|
|
68
|
-
reset() {
|
|
69
|
-
this.events = [];
|
|
70
|
-
this.shutdownCalled = false;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Get events by type
|
|
74
|
-
*/
|
|
75
|
-
getEventsByType(type) {
|
|
76
|
-
return this.events.filter((e) => e.type === type);
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Get events by name
|
|
80
|
-
*/
|
|
81
|
-
getEventsByName(name) {
|
|
82
|
-
return this.events.filter((e) => e.name === name);
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Get funnel events for a specific funnel
|
|
86
|
-
*/
|
|
87
|
-
getFunnelEvents(funnelName) {
|
|
88
|
-
return this.events.filter((e) => e.type === "funnel" && e.funnel === funnelName);
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Get outcome events for a specific operation
|
|
92
|
-
*/
|
|
93
|
-
getOutcomeEvents(operationName) {
|
|
94
|
-
return this.events.filter((e) => e.type === "outcome" && e.operation === operationName);
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Get value events by metric name
|
|
98
|
-
*/
|
|
99
|
-
getValueEvents(metricName) {
|
|
100
|
-
return this.events.filter((e) => e.type === "value" && e.name === metricName);
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Assert that an event was tracked
|
|
104
|
-
*/
|
|
105
|
-
assertEventTracked(name, attributes) {
|
|
106
|
-
const events = this.getEventsByName(name);
|
|
107
|
-
if (events.length === 0) throw new Error(`No events found with name: ${name}`);
|
|
108
|
-
if (attributes) {
|
|
109
|
-
if (!events.find((event) => {
|
|
110
|
-
if (!event.attributes) return false;
|
|
111
|
-
return Object.entries(attributes).every(([key, value]) => event.attributes[key] === value);
|
|
112
|
-
})) throw new Error(`Event "${name}" found, but no matching attributes: ${JSON.stringify(attributes)}`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Assert that a funnel step was tracked
|
|
117
|
-
*/
|
|
118
|
-
assertFunnelStepTracked(funnelName, step, attributes) {
|
|
119
|
-
const matchingEvent = this.getFunnelEvents(funnelName).find((e) => e.step === step);
|
|
120
|
-
if (!matchingEvent) throw new Error(`Funnel "${funnelName}" step "${step}" was not tracked`);
|
|
121
|
-
if (attributes) {
|
|
122
|
-
if (!Object.entries(attributes).every(([key, value]) => matchingEvent.attributes?.[key] === value)) throw new Error(`Funnel step tracked, but attributes don't match: ${JSON.stringify(attributes)}`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Assert that an outcome was tracked
|
|
127
|
-
*/
|
|
128
|
-
assertOutcomeTracked(operationName, outcome, attributes) {
|
|
129
|
-
const matchingEvent = this.getOutcomeEvents(operationName).find((e) => e.outcome === outcome);
|
|
130
|
-
if (!matchingEvent) throw new Error(`Outcome "${operationName}.${outcome}" was not tracked`);
|
|
131
|
-
if (attributes) {
|
|
132
|
-
if (!Object.entries(attributes).every(([key, value]) => matchingEvent.attributes?.[key] === value)) throw new Error(`Outcome tracked, but attributes don't match: ${JSON.stringify(attributes)}`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Pretty print all captured events (useful for debugging)
|
|
137
|
-
*/
|
|
138
|
-
printEvents() {
|
|
139
|
-
console.log(`\n[${this.name}] Captured ${this.events.length} events:\n`);
|
|
140
|
-
for (const [index, event] of this.events.entries()) {
|
|
141
|
-
console.log(`${index + 1}. [${event.type}] ${event.name}`);
|
|
142
|
-
if (event.attributes) console.log(` Attributes:`, event.attributes);
|
|
143
|
-
if (event.value !== void 0) console.log(` Value: ${event.value}`);
|
|
144
|
-
console.log(` Timestamp: ${event.timestamp}\n`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
//#endregion
|
|
150
8
|
//#region src/factories.ts
|
|
151
9
|
/**
|
|
152
10
|
* Factory functions for creating events subscribers
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
//#region src/mock-event-subscriber.ts
|
|
2
|
+
/**
|
|
3
|
+
* Mock subscriber for testing
|
|
4
|
+
*
|
|
5
|
+
* Captures all events calls in memory for test assertions.
|
|
6
|
+
* Does not make any real API calls.
|
|
7
|
+
*/
|
|
8
|
+
var MockEventSubscriber = class {
|
|
9
|
+
name = "MockEventSubscriber";
|
|
10
|
+
version = "1.0.0";
|
|
11
|
+
/**
|
|
12
|
+
* All captured events
|
|
13
|
+
*/
|
|
14
|
+
events = [];
|
|
15
|
+
/**
|
|
16
|
+
* Track if shutdown was called
|
|
17
|
+
*/
|
|
18
|
+
shutdownCalled = false;
|
|
19
|
+
async trackEvent(name, attributes) {
|
|
20
|
+
this.events.push({
|
|
21
|
+
type: "event",
|
|
22
|
+
name,
|
|
23
|
+
attributes,
|
|
24
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async trackFunnelStep(funnelName, step, attributes) {
|
|
28
|
+
this.events.push({
|
|
29
|
+
type: "funnel",
|
|
30
|
+
name: `${funnelName}.${step}`,
|
|
31
|
+
funnel: funnelName,
|
|
32
|
+
step,
|
|
33
|
+
attributes,
|
|
34
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async trackOutcome(operationName, outcome, attributes) {
|
|
38
|
+
this.events.push({
|
|
39
|
+
type: "outcome",
|
|
40
|
+
name: `${operationName}.${outcome}`,
|
|
41
|
+
operation: operationName,
|
|
42
|
+
outcome,
|
|
43
|
+
attributes,
|
|
44
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async trackValue(name, value, attributes) {
|
|
48
|
+
this.events.push({
|
|
49
|
+
type: "value",
|
|
50
|
+
name,
|
|
51
|
+
value,
|
|
52
|
+
attributes,
|
|
53
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async shutdown() {
|
|
57
|
+
this.shutdownCalled = true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Reset captured events (useful between tests)
|
|
61
|
+
*/
|
|
62
|
+
reset() {
|
|
63
|
+
this.events = [];
|
|
64
|
+
this.shutdownCalled = false;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get events by type
|
|
68
|
+
*/
|
|
69
|
+
getEventsByType(type) {
|
|
70
|
+
return this.events.filter((e) => e.type === type);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get events by name
|
|
74
|
+
*/
|
|
75
|
+
getEventsByName(name) {
|
|
76
|
+
return this.events.filter((e) => e.name === name);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get funnel events for a specific funnel
|
|
80
|
+
*/
|
|
81
|
+
getFunnelEvents(funnelName) {
|
|
82
|
+
return this.events.filter((e) => e.type === "funnel" && e.funnel === funnelName);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get outcome events for a specific operation
|
|
86
|
+
*/
|
|
87
|
+
getOutcomeEvents(operationName) {
|
|
88
|
+
return this.events.filter((e) => e.type === "outcome" && e.operation === operationName);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get value events by metric name
|
|
92
|
+
*/
|
|
93
|
+
getValueEvents(metricName) {
|
|
94
|
+
return this.events.filter((e) => e.type === "value" && e.name === metricName);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Assert that an event was tracked
|
|
98
|
+
*/
|
|
99
|
+
assertEventTracked(name, attributes) {
|
|
100
|
+
const events = this.getEventsByName(name);
|
|
101
|
+
if (events.length === 0) throw new Error(`No events found with name: ${name}`);
|
|
102
|
+
if (attributes) {
|
|
103
|
+
if (!events.find((event) => {
|
|
104
|
+
if (!event.attributes) return false;
|
|
105
|
+
return Object.entries(attributes).every(([key, value]) => event.attributes[key] === value);
|
|
106
|
+
})) throw new Error(`Event "${name}" found, but no matching attributes: ${JSON.stringify(attributes)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Assert that a funnel step was tracked
|
|
111
|
+
*/
|
|
112
|
+
assertFunnelStepTracked(funnelName, step, attributes) {
|
|
113
|
+
const matchingEvent = this.getFunnelEvents(funnelName).find((e) => e.step === step);
|
|
114
|
+
if (!matchingEvent) throw new Error(`Funnel "${funnelName}" step "${step}" was not tracked`);
|
|
115
|
+
if (attributes) {
|
|
116
|
+
if (!Object.entries(attributes).every(([key, value]) => matchingEvent.attributes?.[key] === value)) throw new Error(`Funnel step tracked, but attributes don't match: ${JSON.stringify(attributes)}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Assert that an outcome was tracked
|
|
121
|
+
*/
|
|
122
|
+
assertOutcomeTracked(operationName, outcome, attributes) {
|
|
123
|
+
const matchingEvent = this.getOutcomeEvents(operationName).find((e) => e.outcome === outcome);
|
|
124
|
+
if (!matchingEvent) throw new Error(`Outcome "${operationName}.${outcome}" was not tracked`);
|
|
125
|
+
if (attributes) {
|
|
126
|
+
if (!Object.entries(attributes).every(([key, value]) => matchingEvent.attributes?.[key] === value)) throw new Error(`Outcome tracked, but attributes don't match: ${JSON.stringify(attributes)}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Pretty print all captured events (useful for debugging)
|
|
131
|
+
*/
|
|
132
|
+
printEvents() {
|
|
133
|
+
console.log(`\n[${this.name}] Captured ${this.events.length} events:\n`);
|
|
134
|
+
for (const [index, event] of this.events.entries()) {
|
|
135
|
+
console.log(`${index + 1}. [${event.type}] ${event.name}`);
|
|
136
|
+
if (event.attributes) console.log(` Attributes:`, event.attributes);
|
|
137
|
+
if (event.value !== void 0) console.log(` Value: ${event.value}`);
|
|
138
|
+
console.log(` Timestamp: ${event.timestamp}\n`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
//#endregion
|
|
144
|
+
export { MockEventSubscriber as t };
|