Skip to content

Commit aabbbd8

Browse files
fix(operators): cleanup and standardize tests
1 parent 2b5f78b commit aabbbd8

21 files changed

+390
-520
lines changed

.vscode/extensions.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"formulahendry.auto-close-tag",
1414
"formulahendry.auto-rename-tag",
1515
"naumovs.color-highlight",
16-
"humao.rest-client"
16+
"humao.rest-client",
17+
"techer.open-in-browser"
1718
]
1819
}

packages/mock/async.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { of } from 'rxjs';
2+
3+
export const mockAsync = v => of(v);
File renamed without changes.

packages/mock/response.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { vi } from 'vitest';
2+
3+
import { mockAsync } from './async';
4+
5+
export const mockResponse = () => {
6+
return vi.fn((e, url) => ({
7+
url: url,
8+
clone: () => new Response(e),
9+
json: () => mockAsync(e),
10+
text: () => mockAsync(e),
11+
blob: () => mockAsync(e),
12+
arrayBuffer: () => mockAsync(e),
13+
ok: true
14+
}));
15+
};

packages/observables/src/dom/window.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { tap } from 'rxjs';
22
import { TestScheduler } from 'rxjs/testing';
33
import { beforeEach, test, expect, describe, afterEach } from 'vitest';
44

5-
import { mockOffline, mockOnline, mockReset } from '../../../test-utils/network.js';
5+
import { mockOffline, mockOnline, mockReset } from '../../../mock/network.js';
66
import { connectionObservable } from './window.js';
77

88
// HINT: https://betterprogramming.pub/rxjs-testing-write-unit-tests-for-observables-603af959e251

packages/operators/src/log.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,20 @@ export const log = tag => {
1414
if (debug.enabled(tag)) {
1515
return source => {
1616
return new Observable(observer => {
17-
return source.subscribe(
18-
val => {
17+
return source.subscribe({
18+
next: val => {
1919
logger(val);
2020
observer.next(val);
2121
},
22-
err => {
22+
error: err => {
2323
error(err);
2424
observer.error(err);
2525
},
26-
() => {
26+
complete: () => {
2727
logger(bgGreen.bold('Complete!'));
2828
observer.complete();
2929
}
30-
);
30+
});
3131
});
3232
};
3333
} else {

packages/operators/src/request/autoPagination.test.js

Lines changed: 47 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,60 @@
1-
import { concatAll, concatMap, delay, from, map, of, toArray } from 'rxjs';
1+
import { concatAll, delay, from, map, of } from 'rxjs';
22
import { TestScheduler } from 'rxjs/testing';
3-
import { afterAll, afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
3+
import { afterAll, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest';
44

5-
import { log, logOutput, logResult } from '../log';
5+
import { mockAsync } from '../../../mock/async';
6+
import { mockResponse } from '../../../mock/response';
7+
import { log, logResult } from '../log';
68
import { resolveJSON } from './response';
79

8-
describe('auto pagination - mocked', () => {
9-
const testScheduler = new TestScheduler((actual, expected) => {
10-
expect(actual).to.eql(expected);
11-
});
10+
describe('auto pagination', () => {
11+
let testScheduler;
1212

13-
beforeEach(() => {
14-
vi.doMock('./request', importOriginal => ({
15-
request: () => source => source.pipe(concatMap(({ v, t }) => of(v).pipe(delay(t))))
16-
}));
13+
beforeAll(() => {
14+
vi.spyOn(global, 'fetch').mockImplementation(({ v, t }) => mockAsync(v).pipe(delay(t)));
1715

18-
Object.prototype.clone = vi.fn();
19-
vi.spyOn(Object.prototype, 'clone').mockImplementation(function (e) {
20-
return { ...JSON.parse(JSON.stringify(this)) };
21-
});
16+
global.Response = mockResponse();
2217
});
2318

24-
afterEach(() => {
25-
vi.doUnmock('./request');
19+
beforeEach(() => {
20+
testScheduler = new TestScheduler((actual, expected) => expect(actual).to.eql(expected));
2621
});
2722

2823
afterAll(() => {
29-
vi.resetModules();
24+
vi.restoreAllMocks();
3025
});
3126

32-
test('classic testing', async () => {
27+
test('default', async () => {
3328
const { autoPagination } = await import('./autoPagination');
3429

35-
const triggerVal = [
36-
{ t: 2, v: { value: 'a', next: 1 } },
37-
{ t: 5, v: { value: 'b', next: 2 } },
38-
{ t: 3, v: { value: 'c', next: 3 } },
39-
{ t: 1, v: { value: 'd', next: 4 } },
40-
{ t: 4, v: { value: 'e', next: null } }
41-
];
42-
43-
const expectedVal = triggerVal.map(({ v }) => v);
44-
45-
await new Promise((done, error) => {
46-
of(triggerVal[0])
47-
.pipe(
48-
autoPagination({
49-
resolveRoute: (conf, resp) =>
50-
((!resp || resp.next) && [triggerVal[resp?.next || 0]]) || []
51-
}),
52-
toArray()
53-
)
54-
.subscribe({
55-
next: e => expect(e).toStrictEqual(expectedVal),
56-
complete: () => done(),
57-
error: () => error()
58-
});
59-
});
60-
});
61-
62-
test('marble testing', async () => {
63-
const { autoPagination } = await import('./autoPagination');
30+
const expectedVal = {
31+
a: { value: 'a', next: 'b' },
32+
b: { value: 'b', next: 'c' },
33+
c: { value: 'c', next: 'd' },
34+
d: { value: 'd', next: 'e' },
35+
e: { value: 'e', next: null }
36+
};
6437

6538
const triggerVal = {
66-
a: { t: 2, v: { value: 'a', next: 'b' } },
67-
b: { t: 5, v: { value: 'b', next: 'c' } },
68-
c: { t: 3, v: { value: 'c', next: 'd' } },
69-
d: { t: 1, v: { value: 'd', next: 'e' } },
70-
e: { t: 4, v: { value: 'e', next: null } }
39+
a: { t: 2, v: new Response(expectedVal.a) },
40+
b: { t: 5, v: new Response(expectedVal.b) },
41+
c: { t: 3, v: new Response(expectedVal.c) },
42+
d: { t: 1, v: new Response(expectedVal.d) },
43+
e: { t: 4, v: new Response(expectedVal.e) }
7144
};
7245

73-
const expectedVal = Object.fromEntries(
74-
Array.from(Object.entries(triggerVal)).map(([k, { v }]) => [k, v])
75-
);
76-
7746
testScheduler.run(({ cold, expectObservable }) => {
7847
expectObservable(
79-
cold('-a-------------------', triggerVal).pipe(
48+
cold('-a-------------------', { a: 'a' }).pipe(
8049
autoPagination({
81-
resolveRoute: (conf, resp) =>
82-
((!resp || resp.next) && [triggerVal[resp?.next || 'a']]) || []
50+
resolveRoute: (url, resp) => {
51+
if (resp) {
52+
return from(resp.json()).pipe(map(({ next }) => triggerVal[String(next)]));
53+
}
54+
return of(triggerVal[String(url)]);
55+
}
8356
}),
57+
resolveJSON(),
8458
log('marble:result')
8559
)
8660
).toBe('---a----b--cd---e----', expectedVal);
@@ -89,23 +63,25 @@ describe('auto pagination - mocked', () => {
8963
});
9064

9165
describe('auto pagination - demo', () => {
92-
test('sample testing', async () => {
66+
test('sample', async () => {
9367
const { autoPagination } = await import('./autoPagination');
9468

9569
await logResult(
9670
'demo',
9771
of(new URL('https://dummyjson.com/products')).pipe(
9872
autoPagination({
99-
resolveRoute: async (url, resp) => {
100-
const data = (await resp?.json()) || { skip: -10, limit: 10 };
101-
102-
if (!data.total || data.total > data.skip + data.limit) {
103-
const newUrl = new URL(`${url}`);
104-
newUrl.searchParams.set('skip', data.skip + data.limit);
105-
newUrl.searchParams.set('limit', data.limit);
106-
newUrl.searchParams.set('select', 'title,price');
107-
return newUrl;
108-
}
73+
resolveRoute: (url, resp) => {
74+
return from(resp?.json() || of({ skip: -10, limit: 10 })).pipe(
75+
map(data => {
76+
if (!data.total || data.total > data.skip + data.limit) {
77+
const newUrl = new URL(`${url}`);
78+
newUrl.searchParams.set('skip', data.skip + data.limit);
79+
newUrl.searchParams.set('limit', data.limit);
80+
newUrl.searchParams.set('select', 'title,price');
81+
return newUrl;
82+
}
83+
})
84+
);
10985
}
11086
}),
11187
log('demo:response'),

packages/operators/src/request/cache.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ReplaySubject, share, tap, timer } from 'rxjs';
1+
import { ReplaySubject, share, timer } from 'rxjs';
22

33
export const cache = ttl => {
44
return source =>
Lines changed: 7 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,23 @@
1-
import fetchMock from 'fetch-mock';
2-
import { defer, delay, map, of, tap } from 'rxjs';
1+
import { map } from 'rxjs';
32
import { TestScheduler } from 'rxjs/testing';
4-
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
3+
import { beforeEach, describe, expect, test } from 'vitest';
54

65
import { cache } from './cache';
7-
import { requestText } from './request';
86

9-
describe('cache - mocked', () => {
10-
const testScheduler = new TestScheduler((actual, expected) => {
11-
expect(actual).deep.equal(expected);
12-
});
7+
describe('cache', () => {
8+
let testScheduler;
139

1410
beforeEach(() => {
15-
//
16-
});
17-
18-
afterEach(() => {
19-
//
11+
testScheduler = new TestScheduler((actual, expected) => expect(actual).deep.equal(expected));
2012
});
2113

22-
test('marble testing', () => {
14+
test('default', () => {
2315
const initial = new Response('initial', { status: 200 });
2416
const updated = new Response('updated', { status: 200 });
2517
const orderedResponses = [initial, updated];
2618

2719
testScheduler.run(({ cold, expectObservable }) => {
28-
const stream = cold('a-----------', {
20+
const stream = cold('a', {
2921
a: () => orderedResponses.shift()
3022
}).pipe(
3123
map(fn => fn()),
@@ -42,76 +34,4 @@ describe('cache - mocked', () => {
4234
expectObservable(stream, unsubC).toBe('---------a', { a: updated }, new Error());
4335
});
4436
});
45-
46-
test('cache resetted after 100ms', async () => {
47-
let counter = 0;
48-
const a = of(counter).pipe(
49-
tap(e => console.log('U', e)),
50-
cache(5)
51-
);
52-
53-
defer(() => a)
54-
.pipe(delay(2))
55-
.subscribe(e => console.log(e));
56-
defer(() => a)
57-
.pipe(delay(2))
58-
.subscribe(e => console.log(e));
59-
60-
await new Promise(done => setTimeout(done), 500);
61-
62-
defer(() => a)
63-
.pipe(delay(100))
64-
.subscribe(e => console.log(e));
65-
66-
await new Promise(done => setTimeout(done), 1000);
67-
});
68-
});
69-
70-
describe('cache', () => {
71-
beforeEach(() => {
72-
let counter = 0;
73-
fetchMock.mockGlobal().get(
74-
'https://httpbin.org/my-url-fast',
75-
() =>
76-
new Response(++counter, {
77-
status: 200,
78-
headers: { 'Content-type': 'plain/text' }
79-
}),
80-
{ delay: 0, repeat: 2 }
81-
);
82-
});
83-
84-
afterEach(() => {
85-
fetchMock.unmockGlobal();
86-
});
87-
88-
test('cache resetted after 100ms', async () => {
89-
const a = of('https://httpbin.org/my-url-fast').pipe(
90-
requestText(),
91-
tap(() => console.log('CHECK')),
92-
cache(1000)
93-
);
94-
await new Promise(done => {
95-
a.subscribe({
96-
next: e => expect(e).toBe('1'),
97-
complete: () => done()
98-
});
99-
});
100-
101-
await new Promise(done => {
102-
a.subscribe({
103-
next: e => expect(e).toBe('1'),
104-
complete: () => done()
105-
});
106-
});
107-
108-
await new Promise(resolve => setTimeout(resolve, 1000));
109-
110-
await new Promise(done => {
111-
a.subscribe({
112-
next: e => expect(e).toBe('2'),
113-
complete: () => done()
114-
});
115-
});
116-
});
11737
});

0 commit comments

Comments
 (0)