Skip to content

Commit 25d7856

Browse files
committed
add RAF/CAF
1 parent ed0950e commit 25d7856

18 files changed

+515
-208
lines changed

jest/tests/wrappers.test.ts

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { describe, expect, test, beforeEach, afterEach } from '@jest/globals';
22
import { Wrapper } from '../../src/api/wrappers.ts';
33
import { TAG_EXCEPTION, TAG_UNDEFINED } from '../../src/api/clone.ts';
4+
import {
5+
DEFAULT_SETTINGS,
6+
panelsArrayToVisibilityMap,
7+
} from '../../src/api/settings.ts';
48

59
describe('wrappers', () => {
610
let wrapper: Wrapper;
@@ -12,23 +16,19 @@ describe('wrappers', () => {
1216

1317
afterEach(() => {
1418
wrapper.unwrapApis();
15-
wrapper.cleanHistory();
1619
});
1720

18-
test('onlineTimers emptied after setTimeout expires', () => {
19-
return new Promise<void>((resolve) => {
20-
const DELAY = 5;
21-
const handler = setTimeout(() => {}, DELAY);
21+
test('onlineTimers emptied after setTimeout expires', async () => {
22+
const DELAY = 5;
23+
const handler = setTimeout(() => {}, DELAY);
24+
25+
// typecasting handler to number since here its having NodeJS.Timeout type
26+
expect(wrapper.onlineTimers.size).toBe(1);
27+
expect(wrapper.onlineTimers.has(Number(handler))).toBe(true);
2228

23-
// typecasting handler to number since here its having NodeJS.Timeout type
24-
expect(wrapper.onlineTimers.size).toBe(1);
25-
expect(wrapper.onlineTimers.has(Number(handler))).toBe(true);
29+
await new Promise((resolve) => setTimeout(resolve, 2 * DELAY));
2630

27-
setTimeout(() => {
28-
expect(wrapper.onlineTimers.size).toBe(0);
29-
resolve();
30-
}, 2 * DELAY);
31-
});
31+
expect(wrapper.onlineTimers.size).toBe(0);
3232
});
3333

3434
test('setTimeoutHistory & clearTimeoutHistory - recorded', () => {
@@ -47,7 +47,7 @@ describe('wrappers', () => {
4747
test('setTimeoutHistory - valid delay', () => {
4848
const DELAY = 123;
4949
const handler = setTimeout(() => {}, DELAY);
50-
const rec = [...wrapper.setTimeoutHistory.values()][0];
50+
const rec = Array.from(wrapper.setTimeoutHistory.values())[0];
5151

5252
expect(rec.individualInvocations).toBe(1);
5353
expect(rec.handlerDelay).toBe(DELAY);
@@ -61,7 +61,8 @@ describe('wrappers', () => {
6161

6262
test('setTimeoutHistory - invalid delay', () => {
6363
setTimeout(() => {}, -1);
64-
const rec = [...wrapper.setTimeoutHistory.values()][0];
64+
65+
const rec = Array.from(wrapper.setTimeoutHistory.values())[0];
6566

6667
expect(rec.individualInvocations).toBe(1);
6768
expect(rec.handlerDelay).toBe(TAG_EXCEPTION('-1'));
@@ -72,13 +73,16 @@ describe('wrappers', () => {
7273
test('clearTimeoutHistory - valid handler', () => {
7374
const handler = setTimeout(() => {}, 1e3);
7475
clearTimeout(handler);
75-
const rec = [...wrapper.clearTimeoutHistory.values()][0];
76+
77+
const rec = Array.from(wrapper.clearTimeoutHistory.values())[0];
78+
7679
expect(rec.handlerDelay).toBe(1e3);
7780
});
7881

7982
test('clearTimeoutHistory - non existent handler', () => {
80-
clearTimeout(1000);
81-
const rec = [...wrapper.clearTimeoutHistory.values()][0];
83+
clearTimeout(Number.MAX_SAFE_INTEGER);
84+
85+
const rec = Array.from(wrapper.clearTimeoutHistory.values())[0];
8286

8387
expect(rec.handlerDelay).toBe('N/A');
8488
expect(rec.hasError).toBe(false);
@@ -87,9 +91,10 @@ describe('wrappers', () => {
8791
test('clearTimeoutHistory - invalid handler', () => {
8892
clearTimeout(0);
8993

90-
const rec = [...wrapper.clearTimeoutHistory.values()][0];
94+
const rec = Array.from(wrapper.clearTimeoutHistory.values())[0];
9195

9296
expect(rec.handlerDelay).toBe('N/A');
97+
expect(rec.recentHandler).toBe(TAG_EXCEPTION(0));
9398
expect(rec.hasError).toBe(true);
9499
});
95100

@@ -109,7 +114,7 @@ describe('wrappers', () => {
109114
test('setIntervalHistory - valid delay', () => {
110115
const DELAY = 123;
111116
const handler = setInterval(() => {}, DELAY);
112-
const rec = [...wrapper.setIntervalHistory.values()][0];
117+
const rec = Array.from(wrapper.setIntervalHistory.values())[0];
113118

114119
expect(rec.individualInvocations).toBe(1);
115120
expect(rec.handlerDelay).toBe(DELAY);
@@ -123,7 +128,7 @@ describe('wrappers', () => {
123128

124129
test('setIntervalHistory - invalid delay', () => {
125130
const handler = setInterval(() => {}, -1);
126-
const rec = [...wrapper.setIntervalHistory.values()][0];
131+
const rec = Array.from(wrapper.setIntervalHistory.values())[0];
127132

128133
expect(rec.individualInvocations).toBe(1);
129134
expect(rec.handlerDelay).toBe(TAG_EXCEPTION('-1'));
@@ -136,23 +141,26 @@ describe('wrappers', () => {
136141
test('clearIntervalHistory - valid handler', () => {
137142
const handler = setInterval(() => {}, 1e3);
138143
clearInterval(handler);
139-
const rec = [...wrapper.clearIntervalHistory.values()][0];
144+
145+
const rec = Array.from(wrapper.clearIntervalHistory.values())[0];
140146

141147
expect(rec.handlerDelay).toBe(1e3);
142148
});
143149

144150
test('clearIntervalHistory - non existent handler', () => {
145151
clearInterval(1000);
146152

147-
const rec = [...wrapper.clearIntervalHistory.values()][0];
153+
const rec = Array.from(wrapper.clearIntervalHistory.values())[0];
154+
148155
expect(rec.handlerDelay).toBe('N/A');
149156
expect(rec.hasError).toBe(false);
150157
});
151158

152159
test('clearIntervalHistory - invalid handler', () => {
153160
clearInterval(0);
154161

155-
const rec = [...wrapper.clearIntervalHistory.values()][0];
162+
const rec = Array.from(wrapper.clearIntervalHistory.values())[0];
163+
156164
expect(rec.handlerDelay).toBe('N/A');
157165
expect(rec.hasError).toBe(true);
158166
});
@@ -167,7 +175,8 @@ describe('wrappers', () => {
167175
}
168176
expect(wrapper.evalHistory.size).toBe(1);
169177

170-
const rec = [...wrapper.evalHistory.values()][0];
178+
const rec = Array.from(wrapper.evalHistory.values())[0];
179+
171180
expect(rec.individualInvocations).toBe(NUMBER_OF_INVOCATIONS);
172181
expect(rec.usesLocalScope).toBe(false);
173182
expect(rec.code).toBe(CODE);
@@ -180,10 +189,59 @@ describe('wrappers', () => {
180189
const local_variable = 0;
181190
window.eval('(local_variable++)');
182191

183-
const rec = [...wrapper.evalHistory.values()][0];
192+
const rec = Array.from(wrapper.evalHistory.values())[0];
193+
184194
expect(rec.individualInvocations).toBe(1);
185195
expect(local_variable).toBe(0);
186196
expect(rec.usesLocalScope).toBe(true);
187197
expect(rec.returnedValue).toBe(TAG_UNDEFINED);
188198
});
199+
200+
test('rafHistory - recorded', async () => {
201+
let typeOfArgument = '';
202+
const handler = await new Promise((resolve) => {
203+
const handler = requestAnimationFrame((time) => {
204+
typeOfArgument = typeof time;
205+
resolve(handler);
206+
});
207+
});
208+
const rec = Array.from(wrapper.rafHistory.values())[0];
209+
210+
expect(typeOfArgument).toBe('number');
211+
expect(wrapper.rafHistory.size).toBe(1);
212+
expect(rec.recentHandler).toBe(handler);
213+
expect(rec.individualInvocations).toBe(1);
214+
expect(rec.trace.length).toBeGreaterThan(1);
215+
expect(rec.traceId.length).toBeGreaterThan(1);
216+
expect(wrapper.callCounter.requestAnimationFrame).toBe(1);
217+
});
218+
219+
test('cafHistory - recorded', async () => {
220+
const unchanged = 0,
221+
changed = 1;
222+
let changeable = unchanged;
223+
const handler = requestAnimationFrame(() => {
224+
changeable = changed;
225+
});
226+
cancelAnimationFrame(handler);
227+
228+
const rec = Array.from(wrapper.cafHistory.values())[0];
229+
230+
expect(changeable).toBe(unchanged);
231+
expect(wrapper.rafHistory.size).toBe(1);
232+
expect(wrapper.cafHistory.size).toBe(1);
233+
expect(rec.recentHandler).toBe(handler);
234+
expect(rec.individualInvocations).toBe(1);
235+
expect(rec.trace.length).toBeGreaterThan(1);
236+
expect(rec.traceId.length).toBeGreaterThan(1);
237+
expect(wrapper.callCounter.cancelAnimationFrame).toBe(1);
238+
});
239+
240+
test('cafHistory - invalid handler', () => {
241+
cancelAnimationFrame(0);
242+
243+
const rec = Array.from(wrapper.cafHistory?.values())[0];
244+
245+
expect(rec.recentHandler).toBe(TAG_EXCEPTION(0));
246+
});
189247
});

src/api-monitor-cs-main.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
doMediaCommand,
1212
type TMediaTelemetry,
1313
} from '@/api/mediaMonitor.ts';
14-
import { Wrapper, ETimeType, type TWrapperMetrics } from '@/api/wrappers.ts';
14+
import { Wrapper, ETimerType, type TWrapperMetrics } from '@/api/wrappers.ts';
1515
import {
1616
DEFAULT_SETTINGS,
1717
panelsArrayToVisibilityMap,
@@ -26,6 +26,8 @@ export interface TMetrics {
2626
setInterval: number;
2727
clearInterval: number;
2828
eval: number;
29+
requestAnimationFrame: number;
30+
cancelAnimationFrame: number;
2931
};
3032
tickTook: string;
3133
}
@@ -99,7 +101,7 @@ windowListen((o) => {
99101
wrapper.cleanHistory();
100102
tick.trigger();
101103
} else if (o.msg === 'clear-timer-handler') {
102-
if (o.type === ETimeType.TIMEOUT) {
104+
if (o.type === ETimerType.TIMEOUT) {
103105
window.clearTimeout(o.handler);
104106
} else {
105107
window.clearInterval(o.handler);

src/api/clone.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const TAG_EXCEPTION_FALLBACK = '⁉️ ⟪exception⟫';
2-
export const TAG_EXCEPTION = (str: string) => `⁉️ ⟪${str}⟫`;
2+
export const TAG_EXCEPTION = (x: unknown) => `⁉️ ⟪${x}⟫`;
33
const TAG_FUNCTION = (name: string) => ${name ? ` ${name}` : ''}⟪function⟫`;
44
const TAG_NUMERIC = (value: bigint | number) =>
55
typeof value === 'bigint' ? `BigInt⟪${value}⟫` : `Number⟪${value}⟫`;

src/api/communication.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import { APPLICATION_NAME } from '@/api/env.ts';
1414
import { ERRORS_IGNORED } from '@/api/const.ts';
1515
import type { TMetrics } from '@/api-monitor-cs-main.ts';
16-
import type { ETimeType } from '@/api/wrappers.ts';
16+
import type { ETimerType } from '@/api/wrappers.ts';
1717
import type { TSettings } from '@/api/settings.ts';
1818

1919
export function portPost(payload: TMsgOptions) {
@@ -104,7 +104,7 @@ export interface TMsgResetHistory {
104104
}
105105
export interface TMsgClearHandler {
106106
msg: 'clear-timer-handler';
107-
type: ETimeType;
107+
type: ETimerType;
108108
handler: number;
109109
}
110110
export interface TMsgLoaded {

src/api/comparator.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type { TOnlineTimerMetrics } from '@/api/wrappers.ts';
2+
import { ESortOrder } from '@/api/settings.ts';
3+
4+
export function compareByFieldOrder<T, Key extends keyof T>(
5+
field: Key,
6+
order: ESortOrder
7+
) {
8+
return function (first: T, second: T) {
9+
const a = first[field];
10+
const b = second[field];
11+
12+
if (a === undefined) {
13+
return ESortOrder.DESCENDING ? -1 : 1;
14+
} else if (b === undefined) {
15+
return ESortOrder.DESCENDING ? 1 : -1;
16+
}
17+
18+
if (
19+
(typeof a === 'number' && typeof b === 'number') ||
20+
(typeof a === 'string' && typeof b === 'string')
21+
) {
22+
return order === ESortOrder.DESCENDING
23+
? b > a
24+
? 1
25+
: b < a
26+
? -1
27+
: 0
28+
: a > b
29+
? 1
30+
: a < b
31+
? -1
32+
: 0;
33+
} else {
34+
return typeof (ESortOrder.DESCENDING ? b : a) === 'number' ? -1 : 1;
35+
}
36+
};
37+
}
38+
39+
export function compareByDelayHandlerDescending<T extends TOnlineTimerMetrics>(
40+
a: T,
41+
b: T
42+
) {
43+
const aDelay = a.delay ?? 0;
44+
const bDelay = b.delay ?? 0;
45+
return bDelay > aDelay
46+
? 1
47+
: bDelay < aDelay
48+
? -1
49+
: b.handler > a.handler
50+
? 1
51+
: -1;
52+
}

src/api/const.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export const setInterval = window.setInterval.bind(window);
1111
export const clearInterval = window.clearInterval.bind(window);
1212
// https://rollupjs.org/troubleshooting/#avoiding-eval
1313
export const lessEval = window.eval.bind(window);
14+
export const requestAnimationFrame = window.requestAnimationFrame.bind(window);
15+
export const cancelAnimationFrame = window.cancelAnimationFrame.bind(window);
1416
export const TAG_INVALID_CALLSTACK_LINK = '⟪N/A⟫';
1517

1618
export const MEDIA_ELEMENT_EVENTS = [

src/api/settings.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ type TPanelKey =
77
| 'setTimeoutHistory'
88
| 'clearTimeoutHistory'
99
| 'setIntervalHistory'
10-
| 'clearIntervalHistory';
10+
| 'clearIntervalHistory'
11+
| 'requestAnimationFrame'
12+
| 'cancelAnimationFrame';
1113

1214
export enum ETimerHistoryField {
1315
individualInvocations = 'individualInvocations',
@@ -46,6 +48,8 @@ const DEFAULT_PANELS: TSettingsPanel[] = [
4648
label: 'clearInterval History',
4749
visible: true,
4850
},
51+
{ key: 'requestAnimationFrame', label: 'RAF', visible: true },
52+
{ key: 'cancelAnimationFrame', label: 'CAF', visible: true },
4953
];
5054

5155
export const DEFAULT_SORT = {

src/api/time.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { setTimeout, clearTimeout } from '@/api/const.ts';
1+
import {
2+
setTimeout,
3+
clearTimeout,
4+
requestAnimationFrame,
5+
cancelAnimationFrame,
6+
} from '@/api/const.ts';
27

38
/**
49
* Measure time between start/stop calls

0 commit comments

Comments
 (0)