Skip to content

Commit a337ce1

Browse files
committed
Add waitProperty helper.
1 parent dc57a40 commit a337ce1

7 files changed

Lines changed: 114 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
v1.0.7
5+
------
6+
7+
* Add helper `waitProperty`.
8+
49
v1.0.6
510
------
611

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@fivelab/web-utils",
3-
"version": "1.0.5",
3+
"version": "1.0.7",
44
"private": false,
55
"type": "module",
66
"description": "The helpers for easy manipulate with dom.",

src/core/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './config';
22
export * from './observable';
33
export * from './path';
4-
export * from './time';
4+
export * from './time';
5+
export * from './wait';

src/core/wait.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
export type WaitPropertyOptions = {
2+
timeout?: number; // ms, default 5000
3+
interval?: number; // ms, default 50
4+
};
5+
6+
export function waitProperty<T = unknown>(obj: object, key: PropertyKey, options: WaitPropertyOptions = {}): Promise<T> {
7+
const {
8+
timeout = 5000,
9+
interval = 50,
10+
} = options;
11+
12+
return new Promise((resolve, reject) => {
13+
const start = Date.now();
14+
15+
const check = () => {
16+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
17+
resolve((obj as any)[key] as T); // eslint-disable-line @typescript-eslint/no-explicit-any
18+
19+
return;
20+
}
21+
22+
if (Date.now() - start >= timeout) {
23+
reject(new Error(`Property "${String(key)}" was not defined within ${timeout}ms.`));
24+
25+
return;
26+
}
27+
28+
setTimeout(check, interval);
29+
};
30+
31+
check();
32+
});
33+
}

tests/core/wait.test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest';
2+
import { waitProperty } from '../../src/core';
3+
4+
describe('wait property', () => {
5+
beforeEach(() => {
6+
vi.useFakeTimers();
7+
});
8+
9+
it('resolves immediately if property already exists', async () => {
10+
const obj = {foo: 123};
11+
12+
const promise = waitProperty<number>(obj, 'foo');
13+
14+
await expect(promise).resolves.toBe(123);
15+
});
16+
17+
it('resolves when property appears later', async () => {
18+
const obj: Record<string, unknown> = {};
19+
20+
const promise = waitProperty<string>(obj, 'bar', {
21+
interval: 10,
22+
timeout: 100,
23+
});
24+
25+
setTimeout(() => {
26+
obj.bar = 'hello';
27+
}, 30);
28+
29+
vi.advanceTimersByTime(30);
30+
await Promise.resolve();
31+
32+
await expect(promise).resolves.toBe('hello');
33+
});
34+
35+
it('rejects on timeout', async () => {
36+
const obj = {};
37+
38+
const promise = waitProperty(obj, 'missing', {
39+
interval: 10,
40+
timeout: 50,
41+
});
42+
43+
vi.advanceTimersByTime(60);
44+
await Promise.resolve();
45+
46+
await expect(promise).rejects.toThrow(
47+
'Property "missing" was not defined within 50ms'
48+
);
49+
});
50+
51+
it('resolves multiple waiters when property appears', async () => {
52+
const obj: Record<string, unknown> = {};
53+
54+
const p1 = waitProperty<string>(obj, 'shared', {interval: 10});
55+
const p2 = waitProperty<string>(obj, 'shared', {interval: 10});
56+
57+
setTimeout(() => {
58+
obj.shared = 'ok';
59+
}, 20);
60+
61+
vi.advanceTimersByTime(20);
62+
await Promise.resolve();
63+
64+
const [v1, v2] = await Promise.all([p1, p2]);
65+
66+
expect(v1).toBe('ok');
67+
expect(v2).toBe('ok');
68+
});
69+
});

tests/setup.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { afterEach, beforeEach, Mock, vi } from 'vitest';
1+
import { afterEach, vi } from 'vitest';
22

33
afterEach(() => {
44
vi.clearAllMocks();
55
vi.resetAllMocks();
6+
vi.useRealTimers();
67
});

0 commit comments

Comments
 (0)