@granito/vitest-marbles 1.0.0-dev.1
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/LICENSE +22 -0
- package/README.md +204 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/src/marbles-glossary.d.ts +9 -0
- package/dist/src/marbles-glossary.js +11 -0
- package/dist/src/marbles-glossary.js.map +1 -0
- package/dist/src/marblizer.d.ts +9 -0
- package/dist/src/marblizer.js +47 -0
- package/dist/src/marblizer.js.map +1 -0
- package/dist/src/notification-event.d.ts +6 -0
- package/dist/src/notification-event.js +11 -0
- package/dist/src/notification-event.js.map +1 -0
- package/dist/src/notification-kind.d.ts +7 -0
- package/dist/src/notification-kind.js +8 -0
- package/dist/src/notification-kind.js.map +1 -0
- package/dist/src/rxjs/assert-deep-equal.d.ts +4 -0
- package/dist/src/rxjs/assert-deep-equal.js +19 -0
- package/dist/src/rxjs/assert-deep-equal.js.map +1 -0
- package/dist/src/rxjs/cold-observable.d.ts +11 -0
- package/dist/src/rxjs/cold-observable.js +19 -0
- package/dist/src/rxjs/cold-observable.js.map +1 -0
- package/dist/src/rxjs/hot-observable.d.ts +11 -0
- package/dist/src/rxjs/hot-observable.js +19 -0
- package/dist/src/rxjs/hot-observable.js.map +1 -0
- package/dist/src/rxjs/scheduler.d.ts +10 -0
- package/dist/src/rxjs/scheduler.js +22 -0
- package/dist/src/rxjs/scheduler.js.map +1 -0
- package/dist/src/rxjs/strip-alignment-chars.d.ts +1 -0
- package/dist/src/rxjs/strip-alignment-chars.js +4 -0
- package/dist/src/rxjs/strip-alignment-chars.js.map +1 -0
- package/dist/src/rxjs/types.d.ts +9 -0
- package/dist/src/rxjs/types.js +2 -0
- package/dist/src/rxjs/types.js.map +1 -0
- package/dist/src/vitest/custom-matchers.d.ts +13 -0
- package/dist/src/vitest/custom-matchers.js +103 -0
- package/dist/src/vitest/custom-matchers.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 Synapse Wireless Labs
|
|
4
|
+
Copyright (c) 2025 Alexei Yashkov
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
[](https://github.com/granito-source/vitest-marbles/actions/workflows/build.yaml)
|
|
2
|
+
[](https://npm.im/@granito/vitest-marbles)
|
|
3
|
+
|
|
4
|
+
# Vitest Marbles
|
|
5
|
+
|
|
6
|
+
A set of helper functions and [Vitest](http://vitest.dev/) matchers for
|
|
7
|
+
[RxJs](https://rxjs.dev/)
|
|
8
|
+
[marble testing](https://rxjs.dev/guide/testing/marble-testing).
|
|
9
|
+
This library will help you to test your reactive code in easy and
|
|
10
|
+
clean way.
|
|
11
|
+
|
|
12
|
+
# Features
|
|
13
|
+
|
|
14
|
+
* Typescript;
|
|
15
|
+
* marblized error messages.
|
|
16
|
+
|
|
17
|
+
# Prerequisites
|
|
18
|
+
|
|
19
|
+
- Vitest;
|
|
20
|
+
- RxJS;
|
|
21
|
+
- familiarity with
|
|
22
|
+
[marbles syntax](https://rxjs.dev/guide/testing/marble-testing).
|
|
23
|
+
|
|
24
|
+
# Not supported
|
|
25
|
+
|
|
26
|
+
* time progression syntax.
|
|
27
|
+
|
|
28
|
+
# Usage
|
|
29
|
+
|
|
30
|
+
```shell
|
|
31
|
+
npm install --save-vev @granito/vitest-marbles
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
In the test file:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { cold, hot, time, schedule } from '@granito/vitest-marbles';
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Inside the test:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
expect(stream).toBeObservable(expected);
|
|
44
|
+
expect(stream).toBeMarble(marbleString);
|
|
45
|
+
expect(stream).toHaveSubscriptions(marbleString);
|
|
46
|
+
expect(stream).toHaveSubscriptions(marbleStringsArray);
|
|
47
|
+
expect(stream).toHaveNoSubscriptions();
|
|
48
|
+
expect(stream).toSatisfyOnFlush(() => {
|
|
49
|
+
expect(someMock).toHaveBeenCalled();
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
# Examples
|
|
54
|
+
|
|
55
|
+
## toBeObservable()
|
|
56
|
+
|
|
57
|
+
Verifies that the resulting stream emits certain values at certain time
|
|
58
|
+
frames.
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
it('merges two hot observables and start emitting from the subscription point', () => {
|
|
62
|
+
const e1 = hot('----a--^--b-------c--|', {a: 0});
|
|
63
|
+
const e2 = hot(' ---d-^--e---------f-----|', {a: 0});
|
|
64
|
+
const expected = cold('---(be)----c-f-----|', {a: 0});
|
|
65
|
+
|
|
66
|
+
expect(e1.pipe(merge(e2))).toBeObservable(expected);
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Sample output when the test fails (if change the expected result to
|
|
71
|
+
`'-d--(be)----c-f-----|'`).
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
Expected notifications to be:
|
|
75
|
+
"-d--(be)----c-f-----|"
|
|
76
|
+
But got:
|
|
77
|
+
"---(be)----c-f-----|"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## toBeMarble()
|
|
81
|
+
|
|
82
|
+
Same as `toBeObservable()` but receives marble string instead.
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
it('concatenates two cold observables into single cold observable', () => {
|
|
86
|
+
const a = cold('-a-|');
|
|
87
|
+
const b = cold('-b-|');
|
|
88
|
+
const expected = '-a--b-|';
|
|
89
|
+
|
|
90
|
+
expect(a.pipe(concat(b))).toBeMarble(expected);
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## toHaveSubscriptions()
|
|
95
|
+
|
|
96
|
+
Verifies that the observable was subscribed in the provided time frames.
|
|
97
|
+
Useful, for example, when you want to verify that particular
|
|
98
|
+
`switchMap()` worked as expected.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
it('Should figure out single subscription points', () => {
|
|
102
|
+
const x = cold(' --a---b---c--|');
|
|
103
|
+
const xsubs = ' ------^-------!';
|
|
104
|
+
const y = cold(' ---d--e---f---|');
|
|
105
|
+
const ysubs = ' --------------^-------------!';
|
|
106
|
+
const e1 = hot(' ------x-------y------|', { x, y });
|
|
107
|
+
const expected = cold('--------a---b----d--e---f---|');
|
|
108
|
+
|
|
109
|
+
expect(e1.pipe(switchAll())).toBeObservable(expected);
|
|
110
|
+
expect(x).toHaveSubscriptions(xsubs);
|
|
111
|
+
expect(y).toHaveSubscriptions(ysubs);
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
The matcher can also accept multiple subscription marbles.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
it('figures out multiple subscription points', () => {
|
|
118
|
+
const x = cold(' --a---b---c--|');
|
|
119
|
+
const y = cold(' ----x---x|', {x});
|
|
120
|
+
const ySubscription1 = ' ----^---!';
|
|
121
|
+
// '--a---b---c--|'
|
|
122
|
+
const ySubscription2 = ' --------^------------!';
|
|
123
|
+
const expectedY = cold(' ------a---a---b---c--|');
|
|
124
|
+
const z = cold(' -x|', {x});
|
|
125
|
+
// '--a---b---c--|'
|
|
126
|
+
const zSubscription = ' -^------------!';
|
|
127
|
+
const expectedZ = cold(' ---a---b---c--|');
|
|
128
|
+
|
|
129
|
+
expect(y.pipe(switchAll())).toBeObservable(expectedY);
|
|
130
|
+
expect(z.pipe(switchAll())).toBeObservable(expectedZ);
|
|
131
|
+
expect(x).toHaveSubscriptions([
|
|
132
|
+
ySubscription1,
|
|
133
|
+
ySubscription2,
|
|
134
|
+
zSubscription
|
|
135
|
+
]);
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
Sample output when the test fails (if change `ySubscription1` to
|
|
139
|
+
`'-----------------^---!'`).
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
Expected observable to have the following subscription points:
|
|
143
|
+
["-----------------^---!", "--------^------------!", "-^------------!"]
|
|
144
|
+
But got:
|
|
145
|
+
["-^------------!", "----^---!", "--------^------------!"]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## toHaveNoSubscriptions()
|
|
149
|
+
|
|
150
|
+
Verifies that the observable was not subscribed during the test.
|
|
151
|
+
Especially useful when you want to verify that certain chain was not
|
|
152
|
+
called due to an error:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
it('verifies that switchMap() was not performed due to an error', () => {
|
|
156
|
+
const x = cold('--a---b---c--|');
|
|
157
|
+
const y = cold('---#-x--', {x});
|
|
158
|
+
const result = y.pipe(switchAll());
|
|
159
|
+
|
|
160
|
+
expect(result).toBeMarble('---#');
|
|
161
|
+
expect(x).toHaveNoSubscriptions();
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Sample output when the test fails (if remove error and change the
|
|
166
|
+
expected marble to `'------a---b---c--|'`).
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
Expected observable to have no subscription points
|
|
170
|
+
But got:
|
|
171
|
+
["----^------------!"]
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## toSatisfyOnFlush()
|
|
175
|
+
|
|
176
|
+
Allows you to assert on certain side effects/conditions that should be
|
|
177
|
+
satisfied when the observable has been flushed (finished).
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
it('verifies mock has been called', () => {
|
|
181
|
+
const mock = vi.fn();
|
|
182
|
+
const stream$ = cold('blah|').pipe(tap(mock));
|
|
183
|
+
|
|
184
|
+
expect(stream$).toSatisfyOnFlush(() => {
|
|
185
|
+
expect(mock).toHaveBeenCalledTimes(4);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## schedule()
|
|
191
|
+
|
|
192
|
+
Allows you to schedule task on specified frame.
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
it('verifies subject values', () => {
|
|
196
|
+
const source = new Subject();
|
|
197
|
+
const expected = cold('ab');
|
|
198
|
+
|
|
199
|
+
schedule(() => source.next('a'), 1);
|
|
200
|
+
schedule(() => source.next('b'), 2);
|
|
201
|
+
|
|
202
|
+
expect(source).toBeObservable(expected);
|
|
203
|
+
});
|
|
204
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ColdObservable } from './src/rxjs/cold-observable';
|
|
2
|
+
import { HotObservable } from './src/rxjs/hot-observable';
|
|
3
|
+
import { Subscription } from 'rxjs';
|
|
4
|
+
export type ObservableWithSubscriptions = ColdObservable | HotObservable;
|
|
5
|
+
export { Scheduler } from './src/rxjs/scheduler';
|
|
6
|
+
interface CustomMatchers<R = unknown> {
|
|
7
|
+
toBeObservable(observable: ObservableWithSubscriptions): R;
|
|
8
|
+
toHaveSubscriptions(marbles: string | string[]): R;
|
|
9
|
+
toHaveNoSubscriptions(): R;
|
|
10
|
+
toBeMarble(marble: string): R;
|
|
11
|
+
toSatisfyOnFlush(func: () => void): R;
|
|
12
|
+
}
|
|
13
|
+
declare module 'vitest' {
|
|
14
|
+
interface Matchers<T = any> extends CustomMatchers<T> {
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export declare function hot(marbles: string, values?: object, error?: object): HotObservable;
|
|
18
|
+
export declare function cold(marbles: string, values?: object, error?: object): ColdObservable;
|
|
19
|
+
export declare function time(marbles: string): number;
|
|
20
|
+
export declare function schedule(work: () => void, delay: number): Subscription;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ColdObservable } from './src/rxjs/cold-observable';
|
|
2
|
+
import { HotObservable } from './src/rxjs/hot-observable';
|
|
3
|
+
import { Scheduler } from './src/rxjs/scheduler';
|
|
4
|
+
import { stripAlignmentChars } from './src/rxjs/strip-alignment-chars';
|
|
5
|
+
import { TestScheduler } from 'rxjs/testing';
|
|
6
|
+
export { Scheduler } from './src/rxjs/scheduler';
|
|
7
|
+
export function hot(marbles, values, error) {
|
|
8
|
+
return new HotObservable(stripAlignmentChars(marbles), values, error);
|
|
9
|
+
}
|
|
10
|
+
export function cold(marbles, values, error) {
|
|
11
|
+
return new ColdObservable(stripAlignmentChars(marbles), values, error);
|
|
12
|
+
}
|
|
13
|
+
export function time(marbles) {
|
|
14
|
+
return Scheduler.get().createTime(stripAlignmentChars(marbles));
|
|
15
|
+
}
|
|
16
|
+
export function schedule(work, delay) {
|
|
17
|
+
return Scheduler.get().schedule(work, delay);
|
|
18
|
+
}
|
|
19
|
+
const dummyResult = {
|
|
20
|
+
message: () => '',
|
|
21
|
+
pass: true,
|
|
22
|
+
};
|
|
23
|
+
expect.extend({
|
|
24
|
+
toHaveSubscriptions(actual, marbles) {
|
|
25
|
+
const sanitizedMarbles = Array.isArray(marbles) ? marbles.map(stripAlignmentChars) : stripAlignmentChars(marbles);
|
|
26
|
+
Scheduler.get().expectSubscriptions(actual.getSubscriptions()).toBe(sanitizedMarbles);
|
|
27
|
+
return dummyResult;
|
|
28
|
+
},
|
|
29
|
+
toHaveNoSubscriptions(actual) {
|
|
30
|
+
Scheduler.get().expectSubscriptions(actual.getSubscriptions()).toBe([]);
|
|
31
|
+
return dummyResult;
|
|
32
|
+
},
|
|
33
|
+
toBeObservable(actual, expected) {
|
|
34
|
+
Scheduler.get().expectObservable(actual).toBe(expected.marbles, expected.values, expected.error);
|
|
35
|
+
return dummyResult;
|
|
36
|
+
},
|
|
37
|
+
toBeMarble(actual, marbles) {
|
|
38
|
+
Scheduler.get().expectObservable(actual).toBe(stripAlignmentChars(marbles));
|
|
39
|
+
return dummyResult;
|
|
40
|
+
},
|
|
41
|
+
toSatisfyOnFlush(actual, func) {
|
|
42
|
+
Scheduler.get().expectObservable(actual);
|
|
43
|
+
const flushTests = Scheduler.get()['flushTests'];
|
|
44
|
+
flushTests[flushTests.length - 1].ready = true;
|
|
45
|
+
onFlush.push(func);
|
|
46
|
+
return dummyResult;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
let onFlush = [];
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
Scheduler.init();
|
|
52
|
+
onFlush = [];
|
|
53
|
+
});
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
Scheduler.get().run(() => TestScheduler.frameTimeFactor = 10);
|
|
56
|
+
while (onFlush.length > 0)
|
|
57
|
+
onFlush.shift()?.();
|
|
58
|
+
Scheduler.reset();
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export var MarblesGlossary;
|
|
2
|
+
(function (MarblesGlossary) {
|
|
3
|
+
MarblesGlossary["Completion"] = "|";
|
|
4
|
+
MarblesGlossary["Error"] = "#";
|
|
5
|
+
MarblesGlossary["TimeFrame"] = "-";
|
|
6
|
+
MarblesGlossary["Subscription"] = "^";
|
|
7
|
+
MarblesGlossary["Unsubscription"] = "!";
|
|
8
|
+
MarblesGlossary["GroupStart"] = "(";
|
|
9
|
+
MarblesGlossary["GroupEnd"] = ")";
|
|
10
|
+
})(MarblesGlossary || (MarblesGlossary = {}));
|
|
11
|
+
//# sourceMappingURL=marbles-glossary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marbles-glossary.js","sourceRoot":"","sources":["../../src/marbles-glossary.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,eAQX;AARD,WAAY,eAAe;IACzB,mCAAgB,CAAA;IAChB,8BAAW,CAAA;IACX,kCAAe,CAAA;IACf,qCAAkB,CAAA;IAClB,uCAAoB,CAAA;IACpB,mCAAgB,CAAA;IAChB,iCAAc,CAAA;AAChB,CAAC,EARW,eAAe,KAAf,eAAe,QAQ1B"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SubscriptionLog, TestMessages } from './rxjs/types';
|
|
2
|
+
export declare class Marblizer {
|
|
3
|
+
static marblize(messages: TestMessages): string;
|
|
4
|
+
static marblizeSubscriptions(logs: SubscriptionLog[]): string[];
|
|
5
|
+
private static marblizeLogEntry;
|
|
6
|
+
private static getNotificationEvents;
|
|
7
|
+
private static extractMarble;
|
|
8
|
+
private static encloseGroupEvents;
|
|
9
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { MarblesGlossary } from './marbles-glossary';
|
|
2
|
+
import { NotificationEvent } from './notification-event';
|
|
3
|
+
import { NotificationKindChars, ValueLiteral } from './notification-kind';
|
|
4
|
+
const frameStep = 10;
|
|
5
|
+
export class Marblizer {
|
|
6
|
+
static marblize(messages) {
|
|
7
|
+
const emissions = Marblizer.getNotificationEvents(messages);
|
|
8
|
+
let marbles = '';
|
|
9
|
+
for (let i = 0, prevEndFrame = 0; i < emissions.length; prevEndFrame = emissions[i].end, i++)
|
|
10
|
+
marbles = `${marbles}${MarblesGlossary.TimeFrame.repeat(emissions[i].start - prevEndFrame) + emissions[i].marbles}`;
|
|
11
|
+
return marbles;
|
|
12
|
+
}
|
|
13
|
+
static marblizeSubscriptions(logs) {
|
|
14
|
+
return logs.map(log => this.marblizeLogEntry(log.subscribedFrame / frameStep, MarblesGlossary.Subscription) +
|
|
15
|
+
this.marblizeLogEntry((log.unsubscribedFrame - log.subscribedFrame) / frameStep - 1, MarblesGlossary.Unsubscription));
|
|
16
|
+
}
|
|
17
|
+
static marblizeLogEntry(logPoint, symbol) {
|
|
18
|
+
return logPoint !== Infinity ? MarblesGlossary.TimeFrame.repeat(logPoint) + symbol : '';
|
|
19
|
+
}
|
|
20
|
+
static getNotificationEvents(messages) {
|
|
21
|
+
const framesToEmissions = messages.reduce((result, message) => {
|
|
22
|
+
if (!result[message.frame])
|
|
23
|
+
result[message.frame] = new NotificationEvent(message.frame / frameStep);
|
|
24
|
+
const event = result[message.frame];
|
|
25
|
+
event.marbles += Marblizer.extractMarble(message);
|
|
26
|
+
return result;
|
|
27
|
+
}, {});
|
|
28
|
+
const events = Object
|
|
29
|
+
.keys(framesToEmissions)
|
|
30
|
+
.map(frame => framesToEmissions[Number(frame)]);
|
|
31
|
+
Marblizer.encloseGroupEvents(events);
|
|
32
|
+
return events;
|
|
33
|
+
}
|
|
34
|
+
static extractMarble(message) {
|
|
35
|
+
let marble = NotificationKindChars[message.notification.kind];
|
|
36
|
+
if (marble === ValueLiteral)
|
|
37
|
+
marble = message.notification.value;
|
|
38
|
+
return marble;
|
|
39
|
+
}
|
|
40
|
+
static encloseGroupEvents(events) {
|
|
41
|
+
events.forEach(event => {
|
|
42
|
+
if (event.marbles.length > 1)
|
|
43
|
+
event.marbles = `${MarblesGlossary.GroupStart}${event.marbles}${MarblesGlossary.GroupEnd}`;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=marblizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marblizer.js","sourceRoot":"","sources":["../../src/marblizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-event.js","sourceRoot":"","sources":["../../src/notification-event.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,iBAAiB;IAGT;IAFnB,OAAO,GAAG,EAAE,CAAC;IAEb,YAAmB,KAAa;QAAb,UAAK,GAAL,KAAK,CAAQ;IAChC,CAAC;IAED,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IAC1C,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-kind.js","sourceRoot":"","sources":["../../src/notification-kind.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,CAAC;AAE/B,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,CAAC,EAAE,YAAY;IACf,CAAC,EAAE,eAAe,CAAC,UAAU;IAC7B,CAAC,EAAE,eAAe,CAAC,KAAK;CACzB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { SubscriptionLog, TestMessages } from './types';
|
|
2
|
+
import '../vitest/custom-matchers';
|
|
3
|
+
export type MessageOrSubscription = TestMessages | SubscriptionLog[];
|
|
4
|
+
export declare function assertDeepEqual(actual: MessageOrSubscription, expected: MessageOrSubscription): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import '../vitest/custom-matchers';
|
|
2
|
+
function expectedIsSubscriptionLogArray(actual, expected) {
|
|
3
|
+
return actual.length === 0 && expected.length === 0 ||
|
|
4
|
+
expected.length !== 0 && expected[0].subscribedFrame !== undefined;
|
|
5
|
+
}
|
|
6
|
+
function actualIsSubscriptionsAndExpectedIsEmpty(actual, expected) {
|
|
7
|
+
return expected.length === 0 && actual.length !== 0 && actual[0].subscribedFrame !== undefined;
|
|
8
|
+
}
|
|
9
|
+
export function assertDeepEqual(actual, expected) {
|
|
10
|
+
if (!expected)
|
|
11
|
+
return;
|
|
12
|
+
if (actualIsSubscriptionsAndExpectedIsEmpty(actual, expected))
|
|
13
|
+
expect(actual).toHaveEmptySubscriptions();
|
|
14
|
+
else if (expectedIsSubscriptionLogArray(actual, expected))
|
|
15
|
+
expect(actual).toBeSubscriptions(expected);
|
|
16
|
+
else
|
|
17
|
+
expect(actual).toBeNotifications(expected);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=assert-deep-equal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assert-deep-equal.js","sourceRoot":"","sources":["../../../src/rxjs/assert-deep-equal.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC;AAInC,SAAS,8BAA8B,CAAC,MAA6B,EACnE,QAA+B;IAC/B,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QACjD,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAK,QAAQ,CAAC,CAAC,CAAS,CAAC,eAAe,KAAK,SAAS,CAAC;AAChF,CAAC;AAED,SAAS,uCAAuC,CAAC,MAA6B,EAC5E,QAA+B;IAC/B,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAK,MAAM,CAAC,CAAC,CAAS,CAAC,eAAe,KAAK,SAAS,CAAC;AAC1G,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAA6B,EAAE,QAA+B;IAC5F,IAAI,CAAC,QAAQ;QACX,OAAO;IAET,IAAI,uCAAuC,CAAC,MAAM,EAAE,QAAQ,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,wBAAwB,EAAE,CAAC;SACvC,IAAI,8BAA8B,CAAC,MAAM,EAAE,QAAQ,CAAC;QACvD,MAAM,CAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;;QAE3C,MAAM,CAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { TestScheduler } from 'rxjs/testing';
|
|
3
|
+
import { SubscriptionLog } from './types';
|
|
4
|
+
export declare class ColdObservable extends Observable<any> {
|
|
5
|
+
marbles: string;
|
|
6
|
+
values?: Record<string, any> | undefined;
|
|
7
|
+
error?: any | undefined;
|
|
8
|
+
source: ReturnType<TestScheduler['createColdObservable']>;
|
|
9
|
+
constructor(marbles: string, values?: Record<string, any> | undefined, error?: any | undefined);
|
|
10
|
+
getSubscriptions(): SubscriptionLog[];
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { Scheduler } from './scheduler';
|
|
3
|
+
export class ColdObservable extends Observable {
|
|
4
|
+
marbles;
|
|
5
|
+
values;
|
|
6
|
+
error;
|
|
7
|
+
source;
|
|
8
|
+
constructor(marbles, values, error) {
|
|
9
|
+
super();
|
|
10
|
+
this.marbles = marbles;
|
|
11
|
+
this.values = values;
|
|
12
|
+
this.error = error;
|
|
13
|
+
this.source = Scheduler.get().createColdObservable(marbles, values, error);
|
|
14
|
+
}
|
|
15
|
+
getSubscriptions() {
|
|
16
|
+
return this.source.subscriptions;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=cold-observable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cold-observable.js","sourceRoot":"","sources":["../../../src/rxjs/cold-observable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAGlC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,OAAO,cAAe,SAAQ,UAAe;IAG9B;IAAwB;IAAqC;IAFvE,MAAM,CAAoD;IAEnE,YAAmB,OAAe,EAAS,MAA4B,EAAS,KAAW;QACzF,KAAK,EAAE,CAAC;QADS,YAAO,GAAP,OAAO,CAAQ;QAAS,WAAM,GAAN,MAAM,CAAsB;QAAS,UAAK,GAAL,KAAK,CAAM;QAGzF,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7E,CAAC;IAED,gBAAgB;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC;IACnC,CAAC;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { TestScheduler } from 'rxjs/testing';
|
|
3
|
+
import { SubscriptionLog } from './types';
|
|
4
|
+
export declare class HotObservable extends Observable<any> {
|
|
5
|
+
marbles: string;
|
|
6
|
+
values?: Record<string, any> | undefined;
|
|
7
|
+
error?: any | undefined;
|
|
8
|
+
source: ReturnType<TestScheduler['createHotObservable']>;
|
|
9
|
+
constructor(marbles: string, values?: Record<string, any> | undefined, error?: any | undefined);
|
|
10
|
+
getSubscriptions(): SubscriptionLog[];
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { Scheduler } from './scheduler';
|
|
3
|
+
export class HotObservable extends Observable {
|
|
4
|
+
marbles;
|
|
5
|
+
values;
|
|
6
|
+
error;
|
|
7
|
+
source;
|
|
8
|
+
constructor(marbles, values, error) {
|
|
9
|
+
super();
|
|
10
|
+
this.marbles = marbles;
|
|
11
|
+
this.values = values;
|
|
12
|
+
this.error = error;
|
|
13
|
+
this.source = Scheduler.get().createHotObservable(marbles, values, error);
|
|
14
|
+
}
|
|
15
|
+
getSubscriptions() {
|
|
16
|
+
return this.source.subscriptions;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=hot-observable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hot-observable.js","sourceRoot":"","sources":["../../../src/rxjs/hot-observable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAGlC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,OAAO,aAAc,SAAQ,UAAe;IAG7B;IAAwB;IAAqC;IAFvE,MAAM,CAAmD;IAElE,YAAmB,OAAe,EAAS,MAA4B,EAAS,KAAW;QACzF,KAAK,EAAE,CAAC;QADS,YAAO,GAAP,OAAO,CAAQ;QAAS,WAAM,GAAN,MAAM,CAAsB;QAAS,UAAK,GAAL,KAAK,CAAM;QAGzF,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC5E,CAAC;IAED,gBAAgB;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC;IACnC,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { TestMessages } from './types';
|
|
3
|
+
import { TestScheduler } from 'rxjs/testing';
|
|
4
|
+
export declare class Scheduler {
|
|
5
|
+
static instance: TestScheduler | null;
|
|
6
|
+
static init(): void;
|
|
7
|
+
static get(): TestScheduler;
|
|
8
|
+
static reset(): void;
|
|
9
|
+
static materializeInnerObservable(observable: Observable<any>, outerFrame: number): TestMessages;
|
|
10
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { TestScheduler } from 'rxjs/testing';
|
|
2
|
+
import { assertDeepEqual } from './assert-deep-equal';
|
|
3
|
+
export class Scheduler {
|
|
4
|
+
static instance;
|
|
5
|
+
static init() {
|
|
6
|
+
Scheduler.instance = new TestScheduler(assertDeepEqual);
|
|
7
|
+
}
|
|
8
|
+
static get() {
|
|
9
|
+
if (Scheduler.instance)
|
|
10
|
+
return Scheduler.instance;
|
|
11
|
+
throw new Error('Scheduler is not initialized');
|
|
12
|
+
}
|
|
13
|
+
static reset() {
|
|
14
|
+
Scheduler.instance = null;
|
|
15
|
+
}
|
|
16
|
+
static materializeInnerObservable(observable, outerFrame) {
|
|
17
|
+
const scheduler = Scheduler.get();
|
|
18
|
+
// @ts-expect-error to avoid code duplication
|
|
19
|
+
return scheduler.materializeInnerObservable(observable, outerFrame);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../../src/rxjs/scheduler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,OAAO,SAAS;IACb,MAAM,CAAC,QAAQ,CAAuB;IAEtC,MAAM,CAAC,IAAI;QAChB,SAAS,CAAC,QAAQ,GAAG,IAAI,aAAa,CAAC,eAAe,CAAC,CAAC;IAC1D,CAAC;IAEM,MAAM,CAAC,GAAG;QACf,IAAI,SAAS,CAAC,QAAQ;YACpB,OAAO,SAAS,CAAC,QAAQ,CAAC;QAE5B,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAEM,MAAM,CAAC,KAAK;QACjB,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC;IAC5B,CAAC;IAEM,MAAM,CAAC,0BAA0B,CAAC,UAA2B,EAAE,UAAkB;QACtF,MAAM,SAAS,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC;QAElC,6CAA6C;QAC7C,OAAO,SAAS,CAAC,0BAA0B,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACtE,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function stripAlignmentChars(marbles: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip-alignment-chars.js","sourceRoot":"","sources":["../../../src/rxjs/strip-alignment-chars.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TestScheduler } from 'rxjs/testing';
|
|
2
|
+
/**
|
|
3
|
+
* Exported return type of TestMessage[] to avoid importing internal APIs.
|
|
4
|
+
*/
|
|
5
|
+
export type TestMessages = ReturnType<typeof TestScheduler.parseMarbles>;
|
|
6
|
+
/**
|
|
7
|
+
* Exported return type of SubscriptionLog to avoid importing internal APIs.
|
|
8
|
+
*/
|
|
9
|
+
export type SubscriptionLog = ReturnType<typeof TestScheduler.parseMarblesAsSubscriptions>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/rxjs/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SubscriptionLog, TestMessages } from '../rxjs/types';
|
|
2
|
+
import { MatchersObject } from '@vitest/expect';
|
|
3
|
+
interface CustomMatchers<R = unknown> {
|
|
4
|
+
toBeNotifications: (messages: TestMessages) => R;
|
|
5
|
+
toBeSubscriptions: (subscriptions: SubscriptionLog[]) => R;
|
|
6
|
+
toHaveEmptySubscriptions: () => R;
|
|
7
|
+
}
|
|
8
|
+
export declare const customTestMatchers: MatchersObject;
|
|
9
|
+
declare module 'vitest' {
|
|
10
|
+
interface Matchers<T = any> extends CustomMatchers<T> {
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Marblizer } from '../marblizer';
|
|
2
|
+
export const customTestMatchers = {
|
|
3
|
+
toBeNotifications(actual, expected) {
|
|
4
|
+
let actualMarble = actual;
|
|
5
|
+
let expectedMarble = expected;
|
|
6
|
+
if (canMarblize(actual, expected)) {
|
|
7
|
+
actualMarble = Marblizer.marblize(actual);
|
|
8
|
+
expectedMarble = Marblizer.marblize(expected);
|
|
9
|
+
}
|
|
10
|
+
const pass = this.equals(actualMarble, expectedMarble);
|
|
11
|
+
const message = pass ? () => this.utils.matcherHint('.not.toBeNotifications') +
|
|
12
|
+
'\n\n' +
|
|
13
|
+
`Expected notifications to not be:\n` +
|
|
14
|
+
` ${this.utils.printExpected(expectedMarble)}\n` +
|
|
15
|
+
`But got:\n` +
|
|
16
|
+
` ${this.utils.printReceived(actualMarble)}` : () => {
|
|
17
|
+
const diffString = this.utils.diff(expectedMarble, actualMarble, {
|
|
18
|
+
expand: true,
|
|
19
|
+
});
|
|
20
|
+
return (this.utils.matcherHint('.toBeNotifications') +
|
|
21
|
+
'\n\n' +
|
|
22
|
+
`Expected notifications to be:\n` +
|
|
23
|
+
` ${this.utils.printExpected(expectedMarble)}\n` +
|
|
24
|
+
`But got:\n` +
|
|
25
|
+
` ${this.utils.printReceived(actualMarble)}` +
|
|
26
|
+
(diffString ? `\n\nDifference:\n\n${diffString}` : ''));
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
pass,
|
|
30
|
+
message
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
toBeSubscriptions(actual, expected) {
|
|
34
|
+
const actualMarbleArray = Marblizer.marblizeSubscriptions(actual);
|
|
35
|
+
const expectedMarbleArray = Marblizer.marblizeSubscriptions(expected);
|
|
36
|
+
const pass = subscriptionsPass(actualMarbleArray, expectedMarbleArray);
|
|
37
|
+
const message = pass ? () => this.utils.matcherHint('.not.toHaveSubscriptions') +
|
|
38
|
+
'\n\n' +
|
|
39
|
+
`Expected observable to not have the following subscription points:\n` +
|
|
40
|
+
` ${this.utils.printExpected(expectedMarbleArray)}\n` +
|
|
41
|
+
`But got:\n` +
|
|
42
|
+
` ${this.utils.printReceived(actualMarbleArray)}` : () => {
|
|
43
|
+
const diffString = this.utils.diff(expectedMarbleArray, actualMarbleArray, {
|
|
44
|
+
expand: true,
|
|
45
|
+
});
|
|
46
|
+
return (this.utils.matcherHint('.toHaveSubscriptions') +
|
|
47
|
+
'\n\n' +
|
|
48
|
+
`Expected observable to have the following subscription points:\n` +
|
|
49
|
+
` ${this.utils.printExpected(expectedMarbleArray)}\n` +
|
|
50
|
+
`But got:\n` +
|
|
51
|
+
` ${this.utils.printReceived(actualMarbleArray)}` +
|
|
52
|
+
(diffString ? `\n\nDifference:\n\n${diffString}` : ''));
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
pass,
|
|
56
|
+
message
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
toHaveEmptySubscriptions(actual) {
|
|
60
|
+
const pass = !(actual && actual.length > 0);
|
|
61
|
+
let marbles;
|
|
62
|
+
if (actual && actual.length > 0)
|
|
63
|
+
marbles = Marblizer.marblizeSubscriptions(actual);
|
|
64
|
+
const message = pass ? () => this.utils.matcherHint('.not.toHaveNoSubscriptions') +
|
|
65
|
+
'\n\n' +
|
|
66
|
+
`Expected observable to have at least one subscription point, but got nothing` +
|
|
67
|
+
this.utils.printReceived('') : () => this.utils.matcherHint('.toHaveNoSubscriptions') +
|
|
68
|
+
'\n\n' +
|
|
69
|
+
`Expected observable to have no subscription points\n` +
|
|
70
|
+
`But got:\n` +
|
|
71
|
+
` ${this.utils.printReceived(marbles)}\n\n`;
|
|
72
|
+
return {
|
|
73
|
+
pass,
|
|
74
|
+
message
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
expect.extend(customTestMatchers);
|
|
79
|
+
function canMarblize(...messages) {
|
|
80
|
+
return messages.every(isMessagesMarblizable);
|
|
81
|
+
}
|
|
82
|
+
function isMessagesMarblizable(messages) {
|
|
83
|
+
return messages.every(({ notification }) => notification.kind === 'C' ||
|
|
84
|
+
(notification.kind === 'E' && notification.error === 'error') ||
|
|
85
|
+
(notification.kind === 'N' && isCharacter(notification.value)));
|
|
86
|
+
}
|
|
87
|
+
function isCharacter(value) {
|
|
88
|
+
return typeof value === 'string' && value.length === 1 || value !== undefined && JSON.stringify(value).length === 1;
|
|
89
|
+
}
|
|
90
|
+
function subscriptionsPass(actualMarbleArray, expectedMarbleArray) {
|
|
91
|
+
if (actualMarbleArray.length !== expectedMarbleArray.length) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
let pass = true;
|
|
95
|
+
for (const actualMarble of actualMarbleArray) {
|
|
96
|
+
if (!expectedMarbleArray.includes(actualMarble)) {
|
|
97
|
+
pass = false;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return pass;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=custom-matchers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-matchers.js","sourceRoot":"","sources":["../../../src/vitest/custom-matchers.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@granito/vitest-marbles",
|
|
3
|
+
"version": "1.0.0-dev.1",
|
|
4
|
+
"description": "Marble testing helpers library for RxJs and Vitest",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/granito-source/vitest-marbles.git"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"vitest",
|
|
12
|
+
"marbles",
|
|
13
|
+
"marble",
|
|
14
|
+
"testing",
|
|
15
|
+
"test",
|
|
16
|
+
"rxjs",
|
|
17
|
+
"observable"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22",
|
|
21
|
+
"npm": ">=10"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"test": "vitest --no-watch"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public",
|
|
29
|
+
"tag": "latest"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"type": "module",
|
|
35
|
+
"exports": {
|
|
36
|
+
"./package.json": {
|
|
37
|
+
"default": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
".": {
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"default": "./dist/index.js"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"module": "./dist/index.js",
|
|
45
|
+
"typings": "./dist/index.d.ts",
|
|
46
|
+
"sideEffects": false,
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"jsdom": "^27.2.0",
|
|
49
|
+
"rxjs": "^7.8.2",
|
|
50
|
+
"typescript": "^5.9.3",
|
|
51
|
+
"vitest": "^4.0.13"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"rxjs": "^7.0.0",
|
|
55
|
+
"vitest": "^4.0.0"
|
|
56
|
+
}
|
|
57
|
+
}
|