@algolia/client-common 4.14.2 → 5.0.0-alpha.11

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.
Files changed (89) hide show
  1. package/dist/client-common.cjs.js +804 -92
  2. package/dist/client-common.esm.node.js +776 -0
  3. package/dist/index.d.ts +10 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/src/cache/createBrowserLocalStorageCache.d.ts +3 -0
  6. package/dist/src/cache/createBrowserLocalStorageCache.d.ts.map +1 -0
  7. package/dist/src/cache/createFallbackableCache.d.ts +3 -0
  8. package/dist/src/cache/createFallbackableCache.d.ts.map +1 -0
  9. package/dist/src/cache/createMemoryCache.d.ts +3 -0
  10. package/dist/src/cache/createMemoryCache.d.ts.map +1 -0
  11. package/dist/src/cache/createNullCache.d.ts +3 -0
  12. package/dist/src/cache/createNullCache.d.ts.map +1 -0
  13. package/dist/src/cache/index.d.ts +5 -0
  14. package/dist/src/cache/index.d.ts.map +1 -0
  15. package/dist/src/constants.d.ts +7 -0
  16. package/dist/src/constants.d.ts.map +1 -0
  17. package/dist/src/createAlgoliaAgent.d.ts +3 -0
  18. package/dist/src/createAlgoliaAgent.d.ts.map +1 -0
  19. package/dist/src/createAuth.d.ts +6 -0
  20. package/dist/src/createAuth.d.ts.map +1 -0
  21. package/dist/src/createEchoRequester.d.ts +7 -0
  22. package/dist/src/createEchoRequester.d.ts.map +1 -0
  23. package/dist/src/createIterablePromise.d.ts +13 -0
  24. package/dist/src/createIterablePromise.d.ts.map +1 -0
  25. package/dist/src/getAlgoliaAgent.d.ts +8 -0
  26. package/dist/src/getAlgoliaAgent.d.ts.map +1 -0
  27. package/dist/src/transporter/createStatefulHost.d.ts +3 -0
  28. package/dist/src/transporter/createStatefulHost.d.ts.map +1 -0
  29. package/dist/src/transporter/createTransporter.d.ts +3 -0
  30. package/dist/src/transporter/createTransporter.d.ts.map +1 -0
  31. package/dist/src/transporter/errors.d.ts +21 -0
  32. package/dist/src/transporter/errors.d.ts.map +1 -0
  33. package/dist/src/transporter/helpers.d.ts +9 -0
  34. package/dist/src/transporter/helpers.d.ts.map +1 -0
  35. package/dist/src/transporter/index.d.ts +7 -0
  36. package/dist/src/transporter/index.d.ts.map +1 -0
  37. package/dist/src/transporter/responses.d.ts +5 -0
  38. package/dist/src/transporter/responses.d.ts.map +1 -0
  39. package/dist/src/transporter/stackTrace.d.ts +4 -0
  40. package/dist/src/transporter/stackTrace.d.ts.map +1 -0
  41. package/dist/src/types/cache.d.ts +47 -0
  42. package/dist/src/types/cache.d.ts.map +1 -0
  43. package/dist/src/types/createClient.d.ts +12 -0
  44. package/dist/src/types/createClient.d.ts.map +1 -0
  45. package/dist/src/types/createIterablePromise.d.ts +36 -0
  46. package/dist/src/types/createIterablePromise.d.ts.map +1 -0
  47. package/dist/src/types/host.d.ts +33 -0
  48. package/dist/src/types/host.d.ts.map +1 -0
  49. package/dist/src/types/index.d.ts +7 -0
  50. package/dist/src/types/index.d.ts.map +1 -0
  51. package/dist/src/types/requester.d.ts +66 -0
  52. package/dist/src/types/requester.d.ts.map +1 -0
  53. package/dist/src/types/transporter.d.ts +128 -0
  54. package/dist/src/types/transporter.d.ts.map +1 -0
  55. package/index.ts +9 -0
  56. package/package.json +25 -15
  57. package/src/__tests__/cache/browser-local-storage-cache.test.ts +134 -0
  58. package/src/__tests__/cache/fallbackable-cache.test.ts +126 -0
  59. package/src/__tests__/cache/memory-cache.test.ts +90 -0
  60. package/src/__tests__/cache/null-cache.test.ts +49 -0
  61. package/src/__tests__/create-iterable-promise.test.ts +238 -0
  62. package/src/cache/createBrowserLocalStorageCache.ts +74 -0
  63. package/src/cache/createFallbackableCache.ts +53 -0
  64. package/src/cache/createMemoryCache.ts +56 -0
  65. package/src/cache/createNullCache.ts +34 -0
  66. package/src/cache/index.ts +4 -0
  67. package/src/constants.ts +7 -0
  68. package/src/createAlgoliaAgent.ts +20 -0
  69. package/src/createAuth.ts +25 -0
  70. package/src/createEchoRequester.ts +59 -0
  71. package/src/createIterablePromise.ts +47 -0
  72. package/src/getAlgoliaAgent.ts +25 -0
  73. package/src/transporter/createStatefulHost.ts +24 -0
  74. package/src/transporter/createTransporter.ts +350 -0
  75. package/src/transporter/errors.ts +51 -0
  76. package/src/transporter/helpers.ts +119 -0
  77. package/src/transporter/index.ts +6 -0
  78. package/src/transporter/responses.ts +23 -0
  79. package/src/transporter/stackTrace.ts +30 -0
  80. package/src/types/cache.ts +61 -0
  81. package/src/types/createClient.ts +23 -0
  82. package/src/types/createIterablePromise.ts +40 -0
  83. package/src/types/host.ts +38 -0
  84. package/src/types/index.ts +6 -0
  85. package/src/types/requester.ts +72 -0
  86. package/src/types/transporter.ts +153 -0
  87. package/dist/client-common.d.ts +0 -102
  88. package/dist/client-common.esm.js +0 -89
  89. package/index.js +0 -2
package/package.json CHANGED
@@ -1,22 +1,32 @@
1
1
  {
2
2
  "name": "@algolia/client-common",
3
- "version": "4.14.2",
4
- "private": false,
5
- "repository": {
6
- "type": "git",
7
- "url": "git://github.com/algolia/algoliasearch-client-javascript.git"
8
- },
3
+ "version": "5.0.0-alpha.11",
4
+ "description": "Common package for the Algolia JavaScript API client.",
5
+ "repository": "algolia/algoliasearch-client-javascript",
9
6
  "license": "MIT",
10
- "sideEffects": false,
11
- "main": "index.js",
12
- "module": "dist/client-common.esm.js",
13
- "types": "dist/client-common.d.ts",
7
+ "author": "Algolia",
8
+ "main": "dist/client-common.cjs.js",
9
+ "module": "dist/client-common.esm.node.js",
10
+ "types": "dist/index.d.ts",
14
11
  "files": [
15
- "index.js",
16
- "dist"
12
+ "dist",
13
+ "src",
14
+ "index.ts"
17
15
  ],
18
- "dependencies": {
19
- "@algolia/requester-common": "4.14.2",
20
- "@algolia/transporter": "4.14.2"
16
+ "scripts": {
17
+ "build": "yarn clean && rollup --config",
18
+ "clean": "rm -rf ./dist || true",
19
+ "test": "jest"
20
+ },
21
+ "devDependencies": {
22
+ "@types/jest": "28.1.7",
23
+ "@types/node": "16.11.47",
24
+ "jest": "28.1.3",
25
+ "jest-environment-jsdom": "28.1.3",
26
+ "ts-jest": "28.0.8",
27
+ "typescript": "4.7.4"
28
+ },
29
+ "engines": {
30
+ "node": ">= 14.0.0"
21
31
  }
22
32
  }
@@ -0,0 +1,134 @@
1
+ import { createBrowserLocalStorageCache } from '../../cache';
2
+
3
+ const version = 'foobar';
4
+ const notAvailableStorage = new Proxy(window.localStorage, {
5
+ get() {
6
+ return (): void => {
7
+ throw new Error('Component is not available');
8
+ };
9
+ },
10
+ });
11
+
12
+ type DefaultValue = Promise<{ bar: number }>;
13
+
14
+ describe('browser local storage cache', () => {
15
+ const missMock = jest.fn();
16
+ const events = {
17
+ miss: (): Promise<any> => Promise.resolve(missMock()),
18
+ };
19
+
20
+ beforeEach(() => {
21
+ window.localStorage.clear();
22
+ jest.clearAllMocks();
23
+ });
24
+
25
+ it('sets/gets values', async () => {
26
+ const cache = createBrowserLocalStorageCache({ key: version });
27
+ const defaultValue = (): DefaultValue => Promise.resolve({ bar: 1 });
28
+
29
+ expect(await cache.get({ key: 'foo' }, defaultValue, events)).toMatchObject(
30
+ { bar: 1 }
31
+ );
32
+ expect(missMock.mock.calls.length).toBe(1);
33
+
34
+ await cache.set({ key: 'foo' }, { foo: 2 });
35
+
36
+ expect(await cache.get({ key: 'foo' }, defaultValue, events)).toMatchObject(
37
+ { foo: 2 }
38
+ );
39
+ expect(missMock.mock.calls.length).toBe(1);
40
+ });
41
+
42
+ it('deletes keys', async () => {
43
+ const cache = createBrowserLocalStorageCache({ key: version });
44
+
45
+ await cache.set({ key: 'foo' }, { bar: 1 });
46
+ await cache.delete({ key: 'foo' });
47
+
48
+ const defaultValue = (): DefaultValue => Promise.resolve({ bar: 2 });
49
+
50
+ expect(await cache.get({ key: 'foo' }, defaultValue, events)).toMatchObject(
51
+ { bar: 2 }
52
+ );
53
+ expect(missMock.mock.calls.length).toBe(1);
54
+ });
55
+
56
+ it('can be cleared', async () => {
57
+ const cache = createBrowserLocalStorageCache({ key: version });
58
+
59
+ await cache.set({ key: 'foo' }, { bar: 1 });
60
+ await cache.clear();
61
+
62
+ const defaultValue = (): DefaultValue => Promise.resolve({ bar: 2 });
63
+
64
+ expect(await cache.get({ key: 'foo' }, defaultValue, events)).toMatchObject(
65
+ { bar: 2 }
66
+ );
67
+ expect(missMock.mock.calls.length).toBe(1);
68
+ expect(localStorage.length).toBe(0);
69
+ });
70
+
71
+ it('do throws localstorage exceptions on access', async () => {
72
+ const message =
73
+ "Failed to read the 'localStorage' property from 'Window': Access is denied for this document.";
74
+ const cache = createBrowserLocalStorageCache(
75
+ new Proxy(
76
+ { key: 'foo' },
77
+ {
78
+ get(_, key): DOMException | string {
79
+ if (key === 'key') {
80
+ return 'foo';
81
+ }
82
+
83
+ // Simulates a window.localStorage access.
84
+ throw new DOMException(message);
85
+ },
86
+ }
87
+ )
88
+ );
89
+ const key = { foo: 'bar' };
90
+ const value = 'foo';
91
+ const fallback = 'bar';
92
+
93
+ await expect(cache.delete(key)).rejects.toEqual(new DOMException(message));
94
+ await expect(cache.set(key, value)).rejects.toEqual(
95
+ new DOMException(message)
96
+ );
97
+ await expect(
98
+ cache.get(key, () => Promise.resolve(fallback))
99
+ ).rejects.toEqual(new DOMException(message));
100
+ });
101
+
102
+ it('do throws localstorage exceptions after access', async () => {
103
+ const cache = createBrowserLocalStorageCache({
104
+ key: version,
105
+ localStorage: notAvailableStorage,
106
+ });
107
+ const key = { foo: 'bar' };
108
+ const value = 'foo';
109
+ const fallback = 'bar';
110
+ const message = 'Component is not available';
111
+
112
+ await expect(cache.delete(key)).rejects.toEqual(new Error(message));
113
+ await expect(cache.set(key, value)).rejects.toEqual(new Error(message));
114
+ await expect(
115
+ cache.get(key, () => Promise.resolve(fallback))
116
+ ).rejects.toEqual(new Error(message));
117
+ });
118
+
119
+ it('creates a namespace within local storage', async () => {
120
+ const cache = createBrowserLocalStorageCache({
121
+ key: version,
122
+ });
123
+ const key = { foo: 'bar' };
124
+ const value = 'foo';
125
+
126
+ expect(localStorage.getItem(`algolia-client-js-${version}`)).toBeNull();
127
+
128
+ await cache.set(key, value);
129
+
130
+ expect(localStorage.getItem(`algolia-client-js-${version}`)).toBe(
131
+ '{"{\\"foo\\":\\"bar\\"}":"foo"}'
132
+ );
133
+ });
134
+ });
@@ -0,0 +1,126 @@
1
+ import {
2
+ createBrowserLocalStorageCache,
3
+ createFallbackableCache,
4
+ createMemoryCache,
5
+ createNullCache,
6
+ } from '../../cache';
7
+
8
+ const version = 'foobar';
9
+ const notAvailableStorage = new Proxy(window.localStorage, {
10
+ get() {
11
+ return (): void => {
12
+ throw new Error('Component is not available');
13
+ };
14
+ },
15
+ });
16
+
17
+ type DefaultValue = Promise<Record<number, number>>;
18
+
19
+ describe('fallbackable cache', () => {
20
+ const key = { 1: 2 };
21
+ const value = { 3: 4 };
22
+ const defaultValue = (): DefaultValue => Promise.resolve({ 5: 6 });
23
+
24
+ it('always fallback in null cache', async () => {
25
+ const cache = createFallbackableCache({ caches: [] });
26
+
27
+ await cache.set(key, value);
28
+ expect(await cache.get(key, defaultValue)).toEqual({
29
+ 5: 6,
30
+ });
31
+ });
32
+
33
+ describe('order', () => {
34
+ it('use memory cache', async () => {
35
+ const cache = createFallbackableCache({
36
+ caches: [createMemoryCache()],
37
+ });
38
+
39
+ await cache.set(key, value);
40
+
41
+ expect(await cache.get(key, defaultValue)).toEqual({
42
+ 3: 4,
43
+ });
44
+ });
45
+
46
+ it('use null cache first', async () => {
47
+ const cache = createFallbackableCache({
48
+ caches: [createNullCache(), createMemoryCache()],
49
+ });
50
+
51
+ await cache.set(key, value);
52
+
53
+ expect(await cache.get(key, defaultValue)).toEqual({
54
+ 5: 6,
55
+ });
56
+ });
57
+ });
58
+
59
+ describe('fallbacks', () => {
60
+ it('to memory cache', async () => {
61
+ const cache = createFallbackableCache({
62
+ caches: [
63
+ createBrowserLocalStorageCache({
64
+ key: version,
65
+ // @ts-expect-error this will make the cache fail, and normally we fallback on memory cache
66
+ localStorage: {},
67
+ }),
68
+ createMemoryCache(),
69
+ ],
70
+ });
71
+
72
+ await cache.set(key, value);
73
+
74
+ expect(await cache.get(key, defaultValue)).toEqual({
75
+ 3: 4,
76
+ });
77
+ });
78
+
79
+ it('to null cache', async () => {
80
+ const cache = createFallbackableCache({
81
+ caches: [
82
+ createBrowserLocalStorageCache({
83
+ key: version,
84
+ // @ts-expect-error this will make the cache fail, and normally we fallback on memory cache
85
+ localStorage: {},
86
+ }),
87
+ ],
88
+ });
89
+
90
+ await cache.set(key, value);
91
+
92
+ expect(await cache.get(key, defaultValue)).toEqual({
93
+ 5: 6,
94
+ });
95
+ });
96
+
97
+ it('to memory cache', async () => {
98
+ const cache = createFallbackableCache({
99
+ caches: [
100
+ createBrowserLocalStorageCache({
101
+ key: version,
102
+ // @ts-expect-error this will make the cache fail
103
+ localStorage: {},
104
+ }),
105
+ createBrowserLocalStorageCache({
106
+ key: version,
107
+ localStorage: notAvailableStorage, // this will make the cache fail due localStorage not available
108
+ }),
109
+ createMemoryCache(),
110
+ ],
111
+ });
112
+
113
+ await cache.set(key, value);
114
+
115
+ expect(await cache.get(key, defaultValue)).toEqual({
116
+ 3: 4,
117
+ });
118
+
119
+ await cache.clear();
120
+
121
+ expect(await cache.get(key, defaultValue)).toEqual({
122
+ 5: 6,
123
+ });
124
+ });
125
+ });
126
+ });
@@ -0,0 +1,90 @@
1
+ import { createMemoryCache } from '../../cache';
2
+
3
+ type DefaultValue = Promise<{ bar: number }>;
4
+
5
+ describe('memory cache', () => {
6
+ const missMock = jest.fn();
7
+ const events = {
8
+ miss: (): Promise<any> => Promise.resolve(missMock()),
9
+ };
10
+
11
+ beforeEach(() => {
12
+ jest.clearAllMocks();
13
+ });
14
+
15
+ it('sets/gets values', async () => {
16
+ const cache = createMemoryCache();
17
+ const defaultValue = (): DefaultValue => Promise.resolve({ bar: 1 });
18
+
19
+ expect(await cache.get({ key: 'foo' }, defaultValue, events)).toMatchObject(
20
+ {
21
+ bar: 1,
22
+ }
23
+ );
24
+
25
+ await cache.set({ key: 'foo' }, { foo: 2 });
26
+
27
+ expect(missMock.mock.calls.length).toBe(1);
28
+ expect(await cache.get({ key: 'foo' }, defaultValue, events)).toMatchObject(
29
+ { foo: 2 }
30
+ );
31
+ expect(missMock.mock.calls.length).toBe(1);
32
+ });
33
+
34
+ it('getted values do not have references to the value on cache', async () => {
35
+ const cache = createMemoryCache();
36
+ const key = { foo: 'bar' };
37
+ const obj = { 1: { 2: 'bar' } };
38
+ const defaultObj = { 1: { 2: 'too' } };
39
+
40
+ await cache.set(key, obj);
41
+ const gettedValue = await cache.get(key, () => Promise.resolve(defaultObj));
42
+ gettedValue[1][2] = 'foo';
43
+
44
+ expect(await cache.get(key, () => Promise.resolve(defaultObj))).toEqual({
45
+ 1: { 2: 'bar' },
46
+ });
47
+ });
48
+
49
+ it('deletes keys', async () => {
50
+ const cache = createMemoryCache();
51
+
52
+ await cache.set({ key: 'foo' }, { bar: 1 });
53
+ await cache.delete({ key: 'foo' });
54
+
55
+ const defaultValue = (): DefaultValue => Promise.resolve({ bar: 2 });
56
+
57
+ expect(await cache.get({ key: 'foo' }, defaultValue, events)).toMatchObject(
58
+ { bar: 2 }
59
+ );
60
+ expect(missMock.mock.calls.length).toBe(1);
61
+ });
62
+
63
+ it('can be cleared', async () => {
64
+ const cache = createMemoryCache();
65
+
66
+ await cache.set({ key: 'foo' }, { bar: 1 });
67
+ await cache.clear();
68
+
69
+ const defaultValue = (): DefaultValue => Promise.resolve({ bar: 2 });
70
+
71
+ expect(await cache.get({ key: 'foo' }, defaultValue, events)).toMatchObject(
72
+ { bar: 2 }
73
+ );
74
+ expect(missMock.mock.calls.length).toBe(1);
75
+ });
76
+
77
+ it('do not force promise based api for clearing cache', async () => {
78
+ const cache = createMemoryCache();
79
+
80
+ cache.set({ key: 'foo' }, { bar: 1 });
81
+ cache.clear();
82
+
83
+ const defaultValue = (): DefaultValue => Promise.resolve({ bar: 2 });
84
+
85
+ expect(await cache.get({ key: 'foo' }, defaultValue, events)).toMatchObject(
86
+ { bar: 2 }
87
+ );
88
+ expect(missMock.mock.calls.length).toBe(1);
89
+ });
90
+ });
@@ -0,0 +1,49 @@
1
+ import { createNullCache } from '../../cache';
2
+
3
+ type DefaultValue = Promise<{ bar: number }>;
4
+
5
+ describe('null cache', () => {
6
+ const cache = createNullCache();
7
+ const missMock = jest.fn();
8
+ const events = {
9
+ miss: (): Promise<any> => Promise.resolve(missMock()),
10
+ };
11
+
12
+ beforeEach(() => {
13
+ jest.clearAllMocks();
14
+ });
15
+
16
+ it('does not set value', async () => {
17
+ const defaultValue = (): DefaultValue => Promise.resolve({ bar: 12 });
18
+
19
+ await cache.set({ key: 'key' }, { foo: 10 });
20
+
21
+ expect(await cache.get({ key: 'key' }, defaultValue, events)).toMatchObject(
22
+ {
23
+ bar: 12,
24
+ }
25
+ );
26
+
27
+ expect(missMock.mock.calls.length).toBe(1);
28
+ });
29
+
30
+ it('returns default value', async () => {
31
+ const defaultValue = (): DefaultValue => Promise.resolve({ bar: 12 });
32
+
33
+ expect(await cache.get({ foo: 'foo' }, defaultValue, events)).toMatchObject(
34
+ {
35
+ bar: 12,
36
+ }
37
+ );
38
+
39
+ expect(missMock.mock.calls.length).toBe(1);
40
+ });
41
+
42
+ it('can be deleted', async () => {
43
+ await cache.delete('foo');
44
+ });
45
+
46
+ it('can be cleared', async () => {
47
+ await cache.clear();
48
+ });
49
+ });
@@ -0,0 +1,238 @@
1
+ import { createIterablePromise } from '../createIterablePromise';
2
+
3
+ describe('createIterablePromise', () => {
4
+ describe('func', () => {
5
+ it('provides the `previousResponse` parameter', async () => {
6
+ const responses: Array<string | undefined> = [];
7
+ const promise = createIterablePromise<string | undefined>({
8
+ func: (previousResponse) => {
9
+ return new Promise((resolve) => {
10
+ resolve(previousResponse === undefined ? 'yes' : 'no');
11
+ });
12
+ },
13
+ validate: () => responses.length === 3,
14
+ aggregator: (response) => responses.push(response),
15
+ });
16
+
17
+ await expect(promise).resolves.toEqual('no');
18
+ expect(responses).toEqual(['yes', 'no', 'no']);
19
+ });
20
+ });
21
+
22
+ describe('validate', () => {
23
+ it('iterates on a `func` until `validate` is met', async () => {
24
+ let calls = 0;
25
+ const promise = createIterablePromise({
26
+ func: () => {
27
+ return new Promise((resolve) => {
28
+ calls += 1;
29
+ resolve(`success #${calls}`);
30
+ });
31
+ },
32
+ validate: () => calls >= 3,
33
+ });
34
+
35
+ await expect(promise).resolves.toEqual('success #3');
36
+ expect(calls).toBe(3);
37
+ });
38
+
39
+ it('forward the response of the `func`', async () => {
40
+ let calls = 0;
41
+ const promise = createIterablePromise<number>({
42
+ func: () => {
43
+ return new Promise((resolve) => {
44
+ calls += 1;
45
+ resolve(calls);
46
+ });
47
+ },
48
+ validate: (response) => response >= 3,
49
+ });
50
+
51
+ await expect(promise).resolves.toEqual(3);
52
+ expect(calls).toBe(3);
53
+ });
54
+ });
55
+
56
+ describe('aggregator', () => {
57
+ it('is called before iterating', async () => {
58
+ let calls = 0;
59
+ let count = 0;
60
+ const promise = createIterablePromise({
61
+ func: () => {
62
+ return new Promise((resolve) => {
63
+ calls += 1;
64
+ resolve(`success #${calls}`);
65
+ });
66
+ },
67
+ validate: () => calls >= 3,
68
+ aggregator: () => (count += 3),
69
+ });
70
+
71
+ await expect(promise).resolves.toEqual('success #3');
72
+ expect(calls).toBe(3);
73
+ expect(count).toBe(3 * 3);
74
+ });
75
+
76
+ it('forward the response of the `func`', async () => {
77
+ let calls = 0;
78
+ const responses: string[] = [];
79
+ const promise = createIterablePromise<string>({
80
+ func: () => {
81
+ return new Promise((resolve) => {
82
+ calls += 1;
83
+ resolve(`success #${calls}`);
84
+ });
85
+ },
86
+ validate: () => calls >= 3,
87
+ aggregator: (response) => {
88
+ responses.push(response);
89
+ },
90
+ });
91
+
92
+ await expect(promise).resolves.toEqual('success #3');
93
+ expect(calls).toBe(3);
94
+ expect(responses).toEqual(['success #1', 'success #2', 'success #3']);
95
+ });
96
+ });
97
+
98
+ describe('timeout', () => {
99
+ it('defaults to no timeout (0)', async () => {
100
+ let calls = 0;
101
+ const before = Date.now();
102
+ const promise = createIterablePromise({
103
+ func: () => {
104
+ return new Promise((resolve) => {
105
+ calls += 1;
106
+ resolve(`success #${calls}`);
107
+ });
108
+ },
109
+ validate: () => calls >= 2,
110
+ });
111
+
112
+ await expect(promise).resolves.toEqual('success #2');
113
+
114
+ expect(Date.now() - before).toBeGreaterThanOrEqual(0);
115
+ expect(Date.now() - before).toBeLessThanOrEqual(10);
116
+ expect(calls).toBe(2);
117
+ });
118
+
119
+ it('waits before calling the `func` again', async () => {
120
+ let calls = 0;
121
+ const before = Date.now();
122
+ const promise = createIterablePromise({
123
+ func: () => {
124
+ return new Promise((resolve) => {
125
+ calls += 1;
126
+ resolve(`success #${calls}`);
127
+ });
128
+ },
129
+ validate: () => calls >= 2,
130
+ timeout: () => 2000,
131
+ });
132
+
133
+ await expect(promise).resolves.toEqual('success #2');
134
+
135
+ expect(Date.now() - before).toBeGreaterThanOrEqual(2000);
136
+ expect(Date.now() - before).toBeLessThanOrEqual(2010);
137
+ expect(calls).toBe(2);
138
+ });
139
+ });
140
+
141
+ describe('error', () => {
142
+ it('gets the rejection of the given promise via reject', async () => {
143
+ let calls = 0;
144
+
145
+ const promise = createIterablePromise({
146
+ func: () => {
147
+ return new Promise((resolve, reject) => {
148
+ calls += 1;
149
+ if (calls <= 3) {
150
+ resolve('okay');
151
+ } else {
152
+ reject(new Error('nope'));
153
+ }
154
+ });
155
+ },
156
+ validate: () => false,
157
+ });
158
+
159
+ await expect(promise).rejects.toEqual(
160
+ expect.objectContaining({ message: 'nope' })
161
+ );
162
+ });
163
+
164
+ it('gets the rejection of the given promise via throw', async () => {
165
+ let calls = 0;
166
+
167
+ const promise = createIterablePromise({
168
+ func: () => {
169
+ return new Promise((resolve) => {
170
+ calls += 1;
171
+ if (calls <= 3) {
172
+ resolve('okay');
173
+ } else {
174
+ throw new Error('nope');
175
+ }
176
+ });
177
+ },
178
+ validate: () => false,
179
+ });
180
+
181
+ await expect(promise).rejects.toEqual(
182
+ expect.objectContaining({ message: 'nope' })
183
+ );
184
+ });
185
+
186
+ it('rejects with the given `message` when `validate` hits', async () => {
187
+ const MAX_RETRIES = 3;
188
+ let calls = 0;
189
+
190
+ const promise = createIterablePromise({
191
+ func: () => {
192
+ return new Promise((resolve) => {
193
+ calls += 1;
194
+ resolve('okay');
195
+ });
196
+ },
197
+ validate: () => false,
198
+ error: {
199
+ validate: () => calls >= MAX_RETRIES,
200
+ message: () => `Error is thrown: ${calls}/${MAX_RETRIES}`,
201
+ },
202
+ });
203
+
204
+ await expect(promise).rejects.toEqual(
205
+ expect.objectContaining({
206
+ message: 'Error is thrown: 3/3',
207
+ })
208
+ );
209
+ expect(calls).toBe(MAX_RETRIES);
210
+ });
211
+
212
+ it('forward the response of the `func`', async () => {
213
+ const MAX_RETRIES = 3;
214
+ let calls = 0;
215
+
216
+ const promise = createIterablePromise<number>({
217
+ func: () => {
218
+ return new Promise((resolve) => {
219
+ calls += 1;
220
+ resolve(calls);
221
+ });
222
+ },
223
+ validate: () => false,
224
+ error: {
225
+ validate: (response) => response >= MAX_RETRIES,
226
+ message: (response) => `Error is thrown: ${response}/${MAX_RETRIES}`,
227
+ },
228
+ });
229
+
230
+ await expect(promise).rejects.toEqual(
231
+ expect.objectContaining({
232
+ message: 'Error is thrown: 3/3',
233
+ })
234
+ );
235
+ expect(calls).toBe(MAX_RETRIES);
236
+ });
237
+ });
238
+ });