-
Notifications
You must be signed in to change notification settings - Fork 74
/
Copy pathasynciterablehelpers.ts
128 lines (116 loc) · 3.65 KB
/
asynciterablehelpers.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import './Ix.js';
import { Observer, PartialObserver } from '../src/observer.js';
import { AsyncIterableX } from 'ix/asynciterable/asynciterablex.js';
export async function hasNext<T>(source: AsyncIterator<T>, expected: T) {
await expect(source.next()).resolves.toEqual({ done: false, value: expected });
}
export async function hasErr(source: AsyncIterator<any>, expected: any) {
await expect(source.next()).rejects.toThrow(expected);
}
export async function noNext<T>(source: AsyncIterator<T>) {
await expect(source.next()).resolves.toEqual({ done: true, value: undefined });
}
export function delayValue<T>(item: T, delay: number): Promise<T> {
return new Promise<T>((res) => {
setTimeout(() => {
res(item);
}, delay);
});
}
export function delayError<T>(item: T, delay: number): Promise<void> {
return new Promise<void>((_, reject) => {
setTimeout(() => {
reject(item);
}, delay);
});
}
const noop = (_?: any) => {
/**/
};
// eslint-disable-next-line complexity
export function toObserver<T>(
next?: PartialObserver<T> | ((value: T) => void) | null,
error?: ((err: any) => void) | null,
complete?: (() => void) | null
): Observer<T> {
if (next && typeof next === 'object') {
const observer = <any>next;
return {
next: (observer.next || noop).bind(observer),
error: (observer.error || noop).bind(observer),
complete: (observer.complete || noop).bind(observer),
};
} else {
return {
next: typeof next === 'function' ? next : noop,
error: typeof error === 'function' ? error : noop,
complete: typeof complete === 'function' ? complete : noop,
};
}
}
declare global {
namespace jest {
interface Matchers<R> {
toEqualStream<T>(
expected: Iterable<T> | AsyncIterable<T>,
comparer?: (first: T, second: T) => boolean | Promise<boolean>
): Promise<CustomMatcherResult>;
}
}
}
const defaultAsyncCompare = <T>(x: T, y: T) => {
// poor man's deep-equals
try {
expect(x).toEqual(y);
} catch (e) {
return false;
}
return true;
};
expect.extend({
// eslint-disable-next-line complexity
async toEqualStream<T>(
this: jest.MatcherUtils,
actual: Iterable<T> | AsyncIterable<T>,
expected: Iterable<T> | AsyncIterable<T>,
comparer: (first: T, second: T) => boolean | Promise<boolean> = defaultAsyncCompare
) {
let actualCount = 0;
let expectedCount = 0;
let next1: IteratorResult<T>;
let next2: IteratorResult<T>;
const results: string[] = [];
const it1 = AsyncIterableX.as(expected)[Symbol.asyncIterator]();
const it2 =
typeof (<any>actual)[Symbol.asyncIterator] === 'function'
? (<any>actual)[Symbol.asyncIterator]()
: (<any>actual)[Symbol.iterator]();
while (!(next1 = await it1.next()).done) {
expectedCount++;
if (!(next2 = await it2.next(getValueByteLength(next1.value))).done) {
actualCount++;
if (!(await comparer(next1.value, next2.value))) {
results.push(
`expected ${this.utils.printExpected(next2.value)} to equal ${this.utils.printReceived(
next1.value
)}`
);
break;
}
}
}
while (!(await it1.next()).done) {
expectedCount++;
}
if ((!(await it2.next()).done && ++actualCount) || expectedCount !== actualCount) {
results.push(`expected length ${expectedCount}, instead received ${++actualCount}`);
}
return { pass: results.length === 0, message: () => results.join('\n') };
},
});
function getValueByteLength(value: any) {
if (value && value.buffer instanceof ArrayBuffer) {
return value.byteLength;
}
return undefined;
}