wavesurfer.js 7.11.1 → 7.12.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 +1 -1
- package/dist/__tests__/memory-leaks.test.d.ts +7 -0
- package/dist/__tests__/memory-leaks.test.js +184 -0
- package/dist/__tests__/player.test.js +107 -0
- package/dist/__tests__/renderer-utils.test.js +55 -2
- package/dist/draggable.d.ts +4 -0
- package/dist/draggable.js +4 -0
- package/dist/player.d.ts +21 -0
- package/dist/player.js +86 -0
- package/dist/plugins/envelope.cjs +1 -1
- package/dist/plugins/envelope.esm.js +1 -1
- package/dist/plugins/envelope.js +1 -1
- package/dist/plugins/envelope.min.js +1 -1
- package/dist/plugins/hover.cjs +1 -1
- package/dist/plugins/hover.d.ts +0 -3
- package/dist/plugins/hover.esm.js +1 -1
- package/dist/plugins/hover.js +1 -1
- package/dist/plugins/hover.min.js +1 -1
- package/dist/plugins/minimap.cjs +1 -1
- package/dist/plugins/minimap.esm.js +1 -1
- package/dist/plugins/minimap.js +1 -1
- package/dist/plugins/minimap.min.js +1 -1
- package/dist/plugins/record.cjs +1 -1
- package/dist/plugins/record.esm.js +1 -1
- package/dist/plugins/record.js +1 -1
- package/dist/plugins/record.min.js +1 -1
- package/dist/plugins/regions.cjs +1 -1
- package/dist/plugins/regions.esm.js +1 -1
- package/dist/plugins/regions.js +1 -1
- package/dist/plugins/regions.min.js +1 -1
- package/dist/plugins/timeline.cjs +1 -1
- package/dist/plugins/timeline.d.ts +1 -1
- package/dist/plugins/timeline.esm.js +1 -1
- package/dist/plugins/timeline.js +1 -1
- package/dist/plugins/timeline.min.js +1 -1
- package/dist/plugins/zoom.cjs +1 -1
- package/dist/plugins/zoom.esm.js +1 -1
- package/dist/plugins/zoom.js +1 -1
- package/dist/plugins/zoom.min.js +1 -1
- package/dist/reactive/__tests__/drag-stream.test.d.ts +1 -0
- package/dist/reactive/__tests__/drag-stream.test.js +199 -0
- package/dist/reactive/__tests__/event-stream-emitter.test.d.ts +1 -0
- package/dist/reactive/__tests__/event-stream-emitter.test.js +230 -0
- package/dist/reactive/__tests__/event-streams.test.d.ts +1 -0
- package/dist/reactive/__tests__/event-streams.test.js +278 -0
- package/dist/reactive/__tests__/media-event-bridge.test.d.ts +1 -0
- package/dist/reactive/__tests__/media-event-bridge.test.js +202 -0
- package/dist/reactive/__tests__/render-scheduler.test.d.ts +1 -0
- package/dist/reactive/__tests__/render-scheduler.test.js +216 -0
- package/dist/reactive/__tests__/scroll-stream.test.d.ts +1 -0
- package/dist/reactive/__tests__/scroll-stream.test.js +191 -0
- package/dist/reactive/__tests__/state-event-emitter.test.d.ts +1 -0
- package/dist/reactive/__tests__/state-event-emitter.test.js +234 -0
- package/dist/reactive/__tests__/store.test.d.ts +1 -0
- package/dist/reactive/__tests__/store.test.js +271 -0
- package/dist/reactive/drag-stream.d.ts +48 -0
- package/dist/reactive/drag-stream.js +146 -0
- package/dist/reactive/event-stream-emitter.d.ts +104 -0
- package/dist/reactive/event-stream-emitter.js +151 -0
- package/dist/reactive/event-streams.d.ts +65 -0
- package/dist/reactive/event-streams.js +146 -0
- package/dist/reactive/media-event-bridge.d.ts +51 -0
- package/dist/reactive/media-event-bridge.js +152 -0
- package/dist/reactive/render-scheduler.d.ts +43 -0
- package/dist/reactive/render-scheduler.js +75 -0
- package/dist/reactive/scroll-stream.d.ts +93 -0
- package/dist/reactive/scroll-stream.js +131 -0
- package/dist/reactive/state-event-emitter.d.ts +105 -0
- package/dist/reactive/state-event-emitter.js +213 -0
- package/dist/reactive/store.d.ts +66 -0
- package/dist/reactive/store.js +115 -0
- package/dist/renderer-utils.d.ts +12 -3
- package/dist/renderer-utils.js +33 -13
- package/dist/renderer.d.ts +2 -1
- package/dist/renderer.js +44 -42
- package/dist/state/__tests__/wavesurfer-state.test.d.ts +1 -0
- package/dist/state/__tests__/wavesurfer-state.test.js +268 -0
- package/dist/state/wavesurfer-state.d.ts +90 -0
- package/dist/state/wavesurfer-state.js +124 -0
- package/dist/types.d.ts +165 -1
- package/dist/wavesurfer.cjs +1 -1
- package/dist/wavesurfer.d.ts +16 -0
- package/dist/wavesurfer.esm.js +1 -1
- package/dist/wavesurfer.js +32 -0
- package/dist/wavesurfer.min.js +1 -1
- package/package.json +2 -3
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { createDragStream } from '../drag-stream';
|
|
2
|
+
describe('createDragStream', () => {
|
|
3
|
+
beforeAll(() => {
|
|
4
|
+
// Mock matchMedia
|
|
5
|
+
Object.defineProperty(window, 'matchMedia', {
|
|
6
|
+
writable: true,
|
|
7
|
+
value: jest.fn().mockReturnValue({
|
|
8
|
+
matches: false,
|
|
9
|
+
addListener: jest.fn(),
|
|
10
|
+
removeListener: jest.fn(),
|
|
11
|
+
}),
|
|
12
|
+
});
|
|
13
|
+
// Polyfill PointerEvent for jsdom
|
|
14
|
+
if (typeof window.PointerEvent === 'undefined') {
|
|
15
|
+
class FakePointerEvent extends MouseEvent {
|
|
16
|
+
constructor(type, props) {
|
|
17
|
+
super(type, props);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// @ts-expect-error - Polyfill PointerEvent for jsdom test environment
|
|
21
|
+
window.PointerEvent = FakePointerEvent;
|
|
22
|
+
// @ts-expect-error - Polyfill PointerEvent for jsdom test environment
|
|
23
|
+
global.PointerEvent = FakePointerEvent;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
let element;
|
|
27
|
+
let events;
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
element = document.createElement('div');
|
|
30
|
+
document.body.appendChild(element);
|
|
31
|
+
element.getBoundingClientRect = jest.fn(() => ({
|
|
32
|
+
left: 0,
|
|
33
|
+
top: 0,
|
|
34
|
+
right: 100,
|
|
35
|
+
bottom: 100,
|
|
36
|
+
width: 100,
|
|
37
|
+
height: 100,
|
|
38
|
+
x: 0,
|
|
39
|
+
y: 0,
|
|
40
|
+
toJSON: () => ({}),
|
|
41
|
+
}));
|
|
42
|
+
events = [];
|
|
43
|
+
});
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
document.body.removeChild(element);
|
|
46
|
+
});
|
|
47
|
+
it('should create a drag signal', () => {
|
|
48
|
+
const { signal, cleanup } = createDragStream(element);
|
|
49
|
+
expect(signal).toBeDefined();
|
|
50
|
+
expect(signal.value).toBeNull();
|
|
51
|
+
cleanup();
|
|
52
|
+
});
|
|
53
|
+
it('should emit start event on drag', () => {
|
|
54
|
+
var _a;
|
|
55
|
+
const { signal, cleanup } = createDragStream(element, { threshold: 0 });
|
|
56
|
+
signal.subscribe((event) => {
|
|
57
|
+
if (event)
|
|
58
|
+
events.push(event);
|
|
59
|
+
});
|
|
60
|
+
// Simulate drag
|
|
61
|
+
const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0 });
|
|
62
|
+
element.dispatchEvent(pointerDown);
|
|
63
|
+
const pointerMove = new PointerEvent('pointermove', { clientX: 20, clientY: 20 });
|
|
64
|
+
document.dispatchEvent(pointerMove);
|
|
65
|
+
expect(events.length).toBeGreaterThan(0);
|
|
66
|
+
expect((_a = events[0]) === null || _a === void 0 ? void 0 : _a.type).toBe('start');
|
|
67
|
+
cleanup();
|
|
68
|
+
});
|
|
69
|
+
it('should emit move events with deltas', () => {
|
|
70
|
+
const { signal, cleanup } = createDragStream(element, { threshold: 0 });
|
|
71
|
+
signal.subscribe((event) => {
|
|
72
|
+
if (event)
|
|
73
|
+
events.push(event);
|
|
74
|
+
});
|
|
75
|
+
// Simulate drag
|
|
76
|
+
const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0 });
|
|
77
|
+
element.dispatchEvent(pointerDown);
|
|
78
|
+
const pointerMove = new PointerEvent('pointermove', { clientX: 20, clientY: 30 });
|
|
79
|
+
document.dispatchEvent(pointerMove);
|
|
80
|
+
const moveEvent = events.find((e) => e.type === 'move');
|
|
81
|
+
expect(moveEvent).toBeDefined();
|
|
82
|
+
expect(moveEvent === null || moveEvent === void 0 ? void 0 : moveEvent.deltaX).toBe(10);
|
|
83
|
+
expect(moveEvent === null || moveEvent === void 0 ? void 0 : moveEvent.deltaY).toBe(20);
|
|
84
|
+
cleanup();
|
|
85
|
+
});
|
|
86
|
+
it('should emit end event on pointer up', () => {
|
|
87
|
+
const { signal, cleanup } = createDragStream(element, { threshold: 0 });
|
|
88
|
+
signal.subscribe((event) => {
|
|
89
|
+
if (event)
|
|
90
|
+
events.push(event);
|
|
91
|
+
});
|
|
92
|
+
// Simulate drag
|
|
93
|
+
const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0 });
|
|
94
|
+
element.dispatchEvent(pointerDown);
|
|
95
|
+
const pointerMove = new PointerEvent('pointermove', { clientX: 20, clientY: 20 });
|
|
96
|
+
document.dispatchEvent(pointerMove);
|
|
97
|
+
const pointerUp = new PointerEvent('pointerup', { clientX: 20, clientY: 20 });
|
|
98
|
+
document.dispatchEvent(pointerUp);
|
|
99
|
+
expect(events.some((e) => e.type === 'end')).toBe(true);
|
|
100
|
+
cleanup();
|
|
101
|
+
});
|
|
102
|
+
it('should respect threshold', () => {
|
|
103
|
+
const { signal, cleanup } = createDragStream(element, { threshold: 10 });
|
|
104
|
+
signal.subscribe((event) => {
|
|
105
|
+
if (event)
|
|
106
|
+
events.push(event);
|
|
107
|
+
});
|
|
108
|
+
// Simulate small drag (below threshold)
|
|
109
|
+
const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0 });
|
|
110
|
+
element.dispatchEvent(pointerDown);
|
|
111
|
+
const pointerMove = new PointerEvent('pointermove', { clientX: 15, clientY: 15 });
|
|
112
|
+
document.dispatchEvent(pointerMove);
|
|
113
|
+
// Should not emit events yet
|
|
114
|
+
expect(events.length).toBe(0);
|
|
115
|
+
// Simulate larger drag (above threshold)
|
|
116
|
+
const pointerMove2 = new PointerEvent('pointermove', { clientX: 25, clientY: 25 });
|
|
117
|
+
document.dispatchEvent(pointerMove2);
|
|
118
|
+
// Should now emit events
|
|
119
|
+
expect(events.length).toBeGreaterThan(0);
|
|
120
|
+
cleanup();
|
|
121
|
+
});
|
|
122
|
+
it('should cleanup event listeners', () => {
|
|
123
|
+
const { cleanup } = createDragStream(element);
|
|
124
|
+
const addEventListenerSpy = jest.spyOn(document, 'addEventListener');
|
|
125
|
+
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener');
|
|
126
|
+
// Trigger drag to attach document listeners
|
|
127
|
+
const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0 });
|
|
128
|
+
element.dispatchEvent(pointerDown);
|
|
129
|
+
expect(addEventListenerSpy).toHaveBeenCalled();
|
|
130
|
+
cleanup();
|
|
131
|
+
expect(removeEventListenerSpy).toHaveBeenCalled();
|
|
132
|
+
addEventListenerSpy.mockRestore();
|
|
133
|
+
removeEventListenerSpy.mockRestore();
|
|
134
|
+
});
|
|
135
|
+
it('should ignore non-primary mouse buttons', () => {
|
|
136
|
+
const { signal, cleanup } = createDragStream(element);
|
|
137
|
+
signal.subscribe((event) => {
|
|
138
|
+
if (event)
|
|
139
|
+
events.push(event);
|
|
140
|
+
});
|
|
141
|
+
// Simulate right-click drag
|
|
142
|
+
const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 2 });
|
|
143
|
+
element.dispatchEvent(pointerDown);
|
|
144
|
+
const pointerMove = new PointerEvent('pointermove', { clientX: 20, clientY: 20 });
|
|
145
|
+
document.dispatchEvent(pointerMove);
|
|
146
|
+
// Should not emit any events
|
|
147
|
+
expect(events.length).toBe(0);
|
|
148
|
+
cleanup();
|
|
149
|
+
});
|
|
150
|
+
it('should stop propagation during drag', () => {
|
|
151
|
+
const { signal, cleanup } = createDragStream(element, { threshold: 0 });
|
|
152
|
+
signal.subscribe((event) => {
|
|
153
|
+
if (event)
|
|
154
|
+
events.push(event);
|
|
155
|
+
});
|
|
156
|
+
// Start drag
|
|
157
|
+
const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0 });
|
|
158
|
+
element.dispatchEvent(pointerDown);
|
|
159
|
+
// Move - should prevent click
|
|
160
|
+
const pointerMove = new PointerEvent('pointermove', { clientX: 20, clientY: 20 });
|
|
161
|
+
document.dispatchEvent(pointerMove);
|
|
162
|
+
// End drag
|
|
163
|
+
const pointerUp = new PointerEvent('pointerup', { clientX: 20, clientY: 20 });
|
|
164
|
+
document.dispatchEvent(pointerUp);
|
|
165
|
+
// Simulate click after drag
|
|
166
|
+
const clickHandler = jest.fn();
|
|
167
|
+
document.addEventListener('click', clickHandler, { capture: true });
|
|
168
|
+
const click = new MouseEvent('click', { bubbles: true });
|
|
169
|
+
Object.defineProperty(click, 'stopPropagation', { value: jest.fn() });
|
|
170
|
+
Object.defineProperty(click, 'preventDefault', { value: jest.fn() });
|
|
171
|
+
document.dispatchEvent(click);
|
|
172
|
+
document.removeEventListener('click', clickHandler, { capture: true });
|
|
173
|
+
cleanup();
|
|
174
|
+
});
|
|
175
|
+
it('should handle pointer leave events', () => {
|
|
176
|
+
const { signal, cleanup } = createDragStream(element, { threshold: 0 });
|
|
177
|
+
signal.subscribe((event) => {
|
|
178
|
+
if (event)
|
|
179
|
+
events.push(event);
|
|
180
|
+
});
|
|
181
|
+
// Start drag
|
|
182
|
+
const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0, pointerId: 1 });
|
|
183
|
+
element.dispatchEvent(pointerDown);
|
|
184
|
+
// Move to start dragging
|
|
185
|
+
const pointerMove = new PointerEvent('pointermove', { clientX: 20, clientY: 20, pointerId: 1 });
|
|
186
|
+
document.dispatchEvent(pointerMove);
|
|
187
|
+
// Pointer leaves document
|
|
188
|
+
const pointerOut = new PointerEvent('pointerout', {
|
|
189
|
+
clientX: 20,
|
|
190
|
+
clientY: 20,
|
|
191
|
+
pointerId: 1,
|
|
192
|
+
relatedTarget: document.documentElement,
|
|
193
|
+
});
|
|
194
|
+
document.dispatchEvent(pointerOut);
|
|
195
|
+
// Should emit end event
|
|
196
|
+
expect(events.some((e) => e.type === 'end')).toBe(true);
|
|
197
|
+
cleanup();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { toStream, toStreams, mergeStreams, mapStream, filterStream } from '../event-stream-emitter';
|
|
2
|
+
import EventEmitter from '../../event-emitter';
|
|
3
|
+
// Test class that exposes emit for testing
|
|
4
|
+
class TestEmitter extends EventEmitter {
|
|
5
|
+
emit(event, ...args) {
|
|
6
|
+
super.emit(event, ...args);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
describe('event-stream-emitter', () => {
|
|
10
|
+
let emitter;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
emitter = new TestEmitter();
|
|
13
|
+
});
|
|
14
|
+
describe('toStream', () => {
|
|
15
|
+
it('should create a stream from event', () => {
|
|
16
|
+
const { stream, cleanup } = toStream(emitter, 'play');
|
|
17
|
+
expect(stream.value).toBeNull();
|
|
18
|
+
const values = [];
|
|
19
|
+
stream.subscribe((value) => values.push(value));
|
|
20
|
+
emitter.emit('play');
|
|
21
|
+
expect(stream.value).toEqual([]);
|
|
22
|
+
// Values includes initial null and then the event
|
|
23
|
+
expect(values.length).toBeGreaterThanOrEqual(1);
|
|
24
|
+
expect(values[values.length - 1]).toEqual([]);
|
|
25
|
+
cleanup();
|
|
26
|
+
});
|
|
27
|
+
it('should update stream with event arguments', () => {
|
|
28
|
+
const { stream, cleanup } = toStream(emitter, 'timeupdate');
|
|
29
|
+
emitter.emit('timeupdate', 42);
|
|
30
|
+
expect(stream.value).toEqual([42]);
|
|
31
|
+
emitter.emit('timeupdate', 100);
|
|
32
|
+
expect(stream.value).toEqual([100]);
|
|
33
|
+
cleanup();
|
|
34
|
+
});
|
|
35
|
+
it('should handle multiple arguments', () => {
|
|
36
|
+
const { stream, cleanup } = toStream(emitter, 'customEvent');
|
|
37
|
+
emitter.emit('customEvent', 'hello', 5);
|
|
38
|
+
expect(stream.value).toEqual(['hello', 5]);
|
|
39
|
+
cleanup();
|
|
40
|
+
});
|
|
41
|
+
it('should cleanup event listener', () => {
|
|
42
|
+
const { stream, cleanup } = toStream(emitter, 'play');
|
|
43
|
+
emitter.emit('play');
|
|
44
|
+
expect(stream.value).toEqual([]);
|
|
45
|
+
cleanup();
|
|
46
|
+
// After cleanup, event should not update stream
|
|
47
|
+
const oldValue = stream.value;
|
|
48
|
+
emitter.emit('play');
|
|
49
|
+
expect(stream.value).toBe(oldValue); // Value should not change after cleanup
|
|
50
|
+
});
|
|
51
|
+
it('should allow multiple subscriptions to stream', () => {
|
|
52
|
+
const { stream, cleanup } = toStream(emitter, 'timeupdate');
|
|
53
|
+
const values1 = [];
|
|
54
|
+
const values2 = [];
|
|
55
|
+
stream.subscribe((value) => values1.push(value));
|
|
56
|
+
stream.subscribe((value) => values2.push(value));
|
|
57
|
+
emitter.emit('timeupdate', 10);
|
|
58
|
+
emitter.emit('timeupdate', 20);
|
|
59
|
+
expect(values1).toContainEqual([10]);
|
|
60
|
+
expect(values1).toContainEqual([20]);
|
|
61
|
+
expect(values2).toContainEqual([10]);
|
|
62
|
+
expect(values2).toContainEqual([20]);
|
|
63
|
+
cleanup();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('toStreams', () => {
|
|
67
|
+
it('should create multiple streams', () => {
|
|
68
|
+
const streams = toStreams(emitter, ['play', 'pause', 'timeupdate']);
|
|
69
|
+
expect(streams.play).toBeDefined();
|
|
70
|
+
expect(streams.pause).toBeDefined();
|
|
71
|
+
expect(streams.timeupdate).toBeDefined();
|
|
72
|
+
emitter.emit('play');
|
|
73
|
+
expect(streams.play.value).toEqual([]);
|
|
74
|
+
emitter.emit('pause');
|
|
75
|
+
expect(streams.pause.value).toEqual([]);
|
|
76
|
+
emitter.emit('timeupdate', 50);
|
|
77
|
+
expect(streams.timeupdate.value).toEqual([50]);
|
|
78
|
+
streams.cleanup();
|
|
79
|
+
});
|
|
80
|
+
it('should cleanup all streams', () => {
|
|
81
|
+
const streams = toStreams(emitter, ['play', 'pause']);
|
|
82
|
+
streams.cleanup();
|
|
83
|
+
emitter.emit('play');
|
|
84
|
+
emitter.emit('pause');
|
|
85
|
+
// Streams should not update after cleanup
|
|
86
|
+
expect(streams.play.value).toBeNull();
|
|
87
|
+
expect(streams.pause.value).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
it('should handle empty event list', () => {
|
|
90
|
+
const streams = toStreams(emitter, []);
|
|
91
|
+
expect(streams.cleanup).toBeDefined();
|
|
92
|
+
expect(typeof streams.cleanup).toBe('function');
|
|
93
|
+
streams.cleanup(); // Should not throw
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('mergeStreams', () => {
|
|
97
|
+
it('should merge multiple events into one stream', () => {
|
|
98
|
+
const { stream, cleanup } = mergeStreams(emitter, ['play', 'pause']);
|
|
99
|
+
expect(stream.value).toBeNull();
|
|
100
|
+
const values = [];
|
|
101
|
+
stream.subscribe((value) => values.push(value));
|
|
102
|
+
emitter.emit('play');
|
|
103
|
+
expect(stream.value).toEqual({ event: 'play', args: [] });
|
|
104
|
+
emitter.emit('pause');
|
|
105
|
+
expect(stream.value).toEqual({ event: 'pause', args: [] });
|
|
106
|
+
expect(values).toContainEqual({ event: 'play', args: [] });
|
|
107
|
+
expect(values).toContainEqual({ event: 'pause', args: [] });
|
|
108
|
+
cleanup();
|
|
109
|
+
});
|
|
110
|
+
it('should include event arguments in merged stream', () => {
|
|
111
|
+
const { stream, cleanup } = mergeStreams(emitter, ['timeupdate', 'error']);
|
|
112
|
+
emitter.emit('timeupdate', 42);
|
|
113
|
+
expect(stream.value).toEqual({ event: 'timeupdate', args: [42] });
|
|
114
|
+
const error = new Error('Test error');
|
|
115
|
+
emitter.emit('error', error);
|
|
116
|
+
expect(stream.value).toEqual({ event: 'error', args: [error] });
|
|
117
|
+
cleanup();
|
|
118
|
+
});
|
|
119
|
+
it('should cleanup all merged event listeners', () => {
|
|
120
|
+
const { stream, cleanup } = mergeStreams(emitter, ['play', 'pause']);
|
|
121
|
+
cleanup();
|
|
122
|
+
emitter.emit('play');
|
|
123
|
+
emitter.emit('pause');
|
|
124
|
+
// Stream should not update after cleanup
|
|
125
|
+
expect(stream.value).toBeNull();
|
|
126
|
+
});
|
|
127
|
+
it('should handle single event', () => {
|
|
128
|
+
const { stream, cleanup } = mergeStreams(emitter, ['play']);
|
|
129
|
+
emitter.emit('play');
|
|
130
|
+
expect(stream.value).toEqual({ event: 'play', args: [] });
|
|
131
|
+
cleanup();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe('mapStream', () => {
|
|
135
|
+
it('should transform stream values', () => {
|
|
136
|
+
const { stream: timeStream, cleanup } = toStream(emitter, 'timeupdate');
|
|
137
|
+
const seconds = mapStream(timeStream, (value) => (value ? Math.floor(value[0]) : 0));
|
|
138
|
+
expect(seconds.value).toBe(0);
|
|
139
|
+
emitter.emit('timeupdate', 42.7);
|
|
140
|
+
expect(seconds.value).toBe(42);
|
|
141
|
+
emitter.emit('timeupdate', 99.9);
|
|
142
|
+
expect(seconds.value).toBe(99);
|
|
143
|
+
cleanup();
|
|
144
|
+
});
|
|
145
|
+
it('should work with multiple subscriptions', () => {
|
|
146
|
+
const { stream: timeStream, cleanup } = toStream(emitter, 'timeupdate');
|
|
147
|
+
const doubled = mapStream(timeStream, (value) => (value ? value[0] * 2 : 0));
|
|
148
|
+
const values = [];
|
|
149
|
+
doubled.subscribe((value) => values.push(value));
|
|
150
|
+
emitter.emit('timeupdate', 5);
|
|
151
|
+
emitter.emit('timeupdate', 10);
|
|
152
|
+
expect(values).toContain(10);
|
|
153
|
+
expect(values).toContain(20);
|
|
154
|
+
cleanup();
|
|
155
|
+
});
|
|
156
|
+
it('should handle null values', () => {
|
|
157
|
+
const { stream, cleanup } = toStream(emitter, 'play');
|
|
158
|
+
const mapped = mapStream(stream, (value) => (value ? 'playing' : 'not playing'));
|
|
159
|
+
expect(mapped.value).toBe('not playing');
|
|
160
|
+
emitter.emit('play');
|
|
161
|
+
expect(mapped.value).toBe('playing');
|
|
162
|
+
cleanup();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe('filterStream', () => {
|
|
166
|
+
it('should filter stream values', () => {
|
|
167
|
+
const { stream: timeStream, cleanup } = toStream(emitter, 'timeupdate');
|
|
168
|
+
const afterTen = filterStream(timeStream, (value) => (value ? value[0] > 10 : false));
|
|
169
|
+
expect(afterTen.value).toBeNull();
|
|
170
|
+
emitter.emit('timeupdate', 5);
|
|
171
|
+
expect(afterTen.value).toBeNull();
|
|
172
|
+
emitter.emit('timeupdate', 15);
|
|
173
|
+
expect(afterTen.value).toEqual([15]);
|
|
174
|
+
emitter.emit('timeupdate', 7);
|
|
175
|
+
expect(afterTen.value).toBeNull();
|
|
176
|
+
cleanup();
|
|
177
|
+
});
|
|
178
|
+
it('should work with subscriptions', () => {
|
|
179
|
+
const { stream: timeStream, cleanup } = toStream(emitter, 'timeupdate');
|
|
180
|
+
const evenSeconds = filterStream(timeStream, (value) => value !== null && Math.floor(value[0]) % 2 === 0);
|
|
181
|
+
const values = [];
|
|
182
|
+
evenSeconds.subscribe((value) => values.push(value));
|
|
183
|
+
emitter.emit('timeupdate', 1);
|
|
184
|
+
emitter.emit('timeupdate', 2);
|
|
185
|
+
emitter.emit('timeupdate', 3);
|
|
186
|
+
emitter.emit('timeupdate', 4);
|
|
187
|
+
expect(values.filter((v) => v !== null)).toEqual([[2], [4]]);
|
|
188
|
+
cleanup();
|
|
189
|
+
});
|
|
190
|
+
it('should pass through values that match predicate', () => {
|
|
191
|
+
const { stream, cleanup } = toStream(emitter, 'customEvent');
|
|
192
|
+
const onlyHello = filterStream(stream, (value) => value !== null && value[0] === 'hello');
|
|
193
|
+
emitter.emit('customEvent', 'hello', 1);
|
|
194
|
+
expect(onlyHello.value).toEqual(['hello', 1]);
|
|
195
|
+
emitter.emit('customEvent', 'world', 2);
|
|
196
|
+
expect(onlyHello.value).toBeNull();
|
|
197
|
+
cleanup();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
describe('integration', () => {
|
|
201
|
+
it('should allow chaining stream operations', () => {
|
|
202
|
+
const { stream: timeStream, cleanup } = toStream(emitter, 'timeupdate');
|
|
203
|
+
// Filter for times > 10, then map to seconds
|
|
204
|
+
const filtered = filterStream(timeStream, (value) => (value ? value[0] > 10 : false));
|
|
205
|
+
const seconds = mapStream(filtered, (value) => (value ? Math.floor(value[0]) : 0));
|
|
206
|
+
const values = [];
|
|
207
|
+
seconds.subscribe((value) => values.push(value));
|
|
208
|
+
emitter.emit('timeupdate', 5);
|
|
209
|
+
emitter.emit('timeupdate', 15.7);
|
|
210
|
+
emitter.emit('timeupdate', 20.3);
|
|
211
|
+
expect(values).toContain(15);
|
|
212
|
+
expect(values).toContain(20);
|
|
213
|
+
expect(values).not.toContain(5);
|
|
214
|
+
cleanup();
|
|
215
|
+
});
|
|
216
|
+
it('should work with merged streams', () => {
|
|
217
|
+
const { stream: mergedStream, cleanup } = mergeStreams(emitter, ['play', 'pause']);
|
|
218
|
+
const eventNames = mapStream(mergedStream, (value) => (value ? value.event : null));
|
|
219
|
+
const names = [];
|
|
220
|
+
eventNames.subscribe((name) => names.push(name));
|
|
221
|
+
emitter.emit('play');
|
|
222
|
+
emitter.emit('pause');
|
|
223
|
+
emitter.emit('play');
|
|
224
|
+
expect(names).toContain('play');
|
|
225
|
+
expect(names).toContain('pause');
|
|
226
|
+
expect(names.filter((n) => n === 'play')).toHaveLength(2);
|
|
227
|
+
cleanup();
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|