airdcpp-apisocket 3.0.0-beta.1 → 3.0.0-beta.10

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 (41) hide show
  1. package/dist-es/PublicHelpers.js +21 -4
  2. package/dist-es/PublicHelpers.js.map +1 -1
  3. package/dist-es/SocketBase.js +13 -11
  4. package/dist-es/SocketBase.js.map +1 -1
  5. package/dist-es/SocketRequestHandler.js +3 -5
  6. package/dist-es/SocketRequestHandler.js.map +1 -1
  7. package/dist-es/tests/mocks/index.d.ts +3 -0
  8. package/dist-es/tests/mocks/index.js +4 -0
  9. package/dist-es/tests/mocks/index.js.map +1 -0
  10. package/dist-es/tests/mocks/mock-data.d.ts +54 -0
  11. package/dist-es/tests/mocks/mock-data.js +34 -0
  12. package/dist-es/tests/mocks/mock-data.js.map +1 -0
  13. package/dist-es/tests/mocks/mock-server.d.ts +43 -0
  14. package/dist-es/tests/mocks/mock-server.js +154 -0
  15. package/dist-es/tests/mocks/mock-server.js.map +1 -0
  16. package/dist-es/tests/mocks/mock-socket.d.ts +23 -0
  17. package/dist-es/tests/mocks/mock-socket.js +31 -0
  18. package/dist-es/tests/mocks/mock-socket.js.map +1 -0
  19. package/dist-es/tests/test-utils.d.ts +8 -0
  20. package/dist-es/tests/test-utils.js +28 -0
  21. package/dist-es/tests/test-utils.js.map +1 -0
  22. package/dist-es/types/public_helpers.d.ts +12 -3
  23. package/dist-es/types/public_helpers_internal.d.ts +4 -4
  24. package/dist-es/types/socket.d.ts +1 -0
  25. package/package.json +10 -7
  26. package/src/PublicHelpers.ts +22 -4
  27. package/src/SocketBase.ts +15 -12
  28. package/src/SocketRequestHandler.ts +3 -5
  29. package/src/tests/Socket.test.ts +107 -109
  30. package/src/tests/mocks/index.ts +3 -0
  31. package/src/tests/mocks/mock-data.ts +36 -0
  32. package/src/tests/mocks/mock-server.ts +269 -0
  33. package/src/tests/mocks/mock-socket.ts +68 -0
  34. package/src/tests/public_helpers.test.ts +63 -62
  35. package/src/tests/test-utils.ts +31 -0
  36. package/src/types/api_internal.ts +0 -1
  37. package/src/types/public_helpers.ts +11 -11
  38. package/src/types/public_helpers_internal.ts +4 -4
  39. package/src/types/socket.ts +1 -0
  40. package/tsconfig.json +1 -1
  41. package/src/tests/helpers.ts +0 -195
@@ -0,0 +1,269 @@
1
+ import { Client, Server, WebSocket } from 'mock-socket';
2
+
3
+ import { OutgoingRequest, RequestSuccessResponse, RequestErrorResponse } from '../../types/api_internal.js';
4
+ import { EventEmitter } from 'events';
5
+ import { DEFAULT_CONNECT_PARAMS } from './mock-data.js';
6
+
7
+ interface MockFunctionCreator {
8
+ fn: (...args: any[]) => any;
9
+ };
10
+
11
+ type RequestCallback = (requestData: object) => void;
12
+
13
+ const toEmitId = (path: string, method: string) => {
14
+ return `${path}_${method}`;
15
+ };
16
+
17
+ const getDefaultMockCreatorF = () => ({
18
+ fn: () => {},
19
+ });
20
+
21
+ interface MockServerOptions {
22
+ url: string;
23
+ reportMissingListeners?: boolean;
24
+ mockF: MockFunctionCreator;
25
+ }
26
+
27
+ const DEFAULT_MOCK_SERVER_OPTIONS: MockServerOptions = {
28
+ url: DEFAULT_CONNECT_PARAMS.url,
29
+ reportMissingListeners: true,
30
+ mockF: getDefaultMockCreatorF(),
31
+ }
32
+
33
+ type MockRequestResponseDataObject<DataT extends object | undefined> = Omit<RequestSuccessResponse<DataT>, 'callback_id'> | Omit<RequestErrorResponse, 'callback_id'>;
34
+ type MockRequestResponseDataHandler<DataT extends object | undefined> = (request: OutgoingRequest, s: WebSocket) => MockRequestResponseDataObject<DataT>;
35
+ type MockRequestResponseData<DataT extends object | undefined> = MockRequestResponseDataObject<DataT> | MockRequestResponseDataHandler<DataT>;
36
+
37
+ const getMockServer = (initialOptions: Partial<MockServerOptions> = {}) => {
38
+ const { url, reportMissingListeners, mockF }: MockServerOptions = {
39
+ ...DEFAULT_MOCK_SERVER_OPTIONS,
40
+ ...initialOptions,
41
+ };
42
+
43
+ const mockServer = new Server(url);
44
+ let socket: Client;
45
+ const emitter = new EventEmitter();
46
+
47
+ const send = (data: object) => {
48
+ socket.send(JSON.stringify(data));
49
+ };
50
+
51
+ const addServerHandler = <DataT extends object | undefined>(
52
+ method: string,
53
+ path: string,
54
+ responseData: MockRequestResponseData<DataT>,
55
+ subscriptionCallback?: RequestCallback
56
+ ) => {
57
+ const requestHandler = (request: OutgoingRequest, s: WebSocket) => {
58
+ if (subscriptionCallback) {
59
+ subscriptionCallback(request);
60
+ }
61
+
62
+ const data = typeof responseData === 'function' ? responseData(request, s) : responseData;
63
+ if (!data ||!data.code) {
64
+ throw new Error(`Mock server: response handler for path ${path} must return a status code`);
65
+ }
66
+
67
+ const response: RequestSuccessResponse | RequestErrorResponse = {
68
+ callback_id: request.callback_id,
69
+ ...data,
70
+ };
71
+
72
+ s.send(JSON.stringify(response));
73
+ };
74
+
75
+ const emitId = toEmitId(path, method);
76
+ emitter.addListener(
77
+ emitId,
78
+ requestHandler,
79
+ );
80
+ emitter.setMaxListeners(1);
81
+
82
+ return () => emitter.removeListener(emitId, requestHandler);
83
+ };
84
+
85
+ const addDummyDataHandler = (method: string, path: string) => {
86
+ const handler = (request: OutgoingRequest, s: WebSocket) => {
87
+ // Do nothing
88
+ };
89
+
90
+ const emitId = toEmitId(path, method);
91
+ emitter.addListener(
92
+ emitId,
93
+ handler
94
+ );
95
+ emitter.setMaxListeners(1);
96
+
97
+ return () => emitter.removeListener(emitId, handler);
98
+ }
99
+
100
+ const addRequestHandler = <DataT extends object | undefined>(
101
+ method: string,
102
+ path: string,
103
+ data?: DataT | MockRequestResponseDataHandler<DataT>,
104
+ subscriptionCallback?: RequestCallback
105
+ ) => {
106
+ const handlerData = typeof data === 'function' ? data : {
107
+ data,
108
+ code: data ? 200 : 204,
109
+ }
110
+
111
+ return addServerHandler<DataT>(
112
+ method,
113
+ path,
114
+ handlerData,
115
+ subscriptionCallback
116
+ );
117
+ }
118
+
119
+ const addErrorHandler = (
120
+ method: string,
121
+ path: string,
122
+ errorStr: string | null,
123
+ errorCode: number,
124
+ subscriptionCallback?: RequestCallback
125
+ ) => {
126
+ return addServerHandler(
127
+ method,
128
+ path,
129
+ {
130
+ error: !errorStr ? null as any : {
131
+ message: errorStr,
132
+ },
133
+ code: errorCode,
134
+ },
135
+ subscriptionCallback
136
+ );
137
+ }
138
+
139
+ const addSubscriptionHandlerImpl = (
140
+ moduleName: string,
141
+ type: string,
142
+ listenerName: string,
143
+ entityId?: string | number,
144
+ ) => {
145
+ const subscribeFn = mockF.fn();
146
+ const unsubscribeFn = mockF.fn();
147
+
148
+ const path = entityId ? `${moduleName}/${entityId}/${type}/${listenerName}` : `${moduleName}/${type}/${listenerName}`;
149
+
150
+ const subscribeRemove = addRequestHandler('POST', path, undefined, subscribeFn);
151
+ const unsubscribeRemove = addRequestHandler('DELETE', path, undefined, unsubscribeFn);
152
+
153
+ const fire = (data: object, entityId?: string | number) => {
154
+ send({
155
+ event: listenerName,
156
+ data,
157
+ id: entityId,
158
+ });
159
+ }
160
+
161
+ const remove = () => {
162
+ subscribeRemove();
163
+ unsubscribeRemove();
164
+ };
165
+
166
+ return {
167
+ fire,
168
+ remove,
169
+
170
+ subscribeFn,
171
+ unsubscribeFn,
172
+
173
+ path,
174
+ }
175
+ }
176
+
177
+
178
+ const addSubscriptionHandler = (
179
+ moduleName: string,
180
+ listenerName: string,
181
+ entityId?: string | number,
182
+ ) => {
183
+ return addSubscriptionHandlerImpl(moduleName, 'listeners', listenerName, entityId);
184
+ }
185
+
186
+ const addHookHandler = (
187
+ moduleName: string,
188
+ listenerName: string,
189
+ ) => {
190
+ const subscriber = addSubscriptionHandlerImpl(moduleName, 'hooks', listenerName);
191
+
192
+ const addResolver = (completionId: number) => {
193
+ const resolveFn = mockF.fn();
194
+ const rejectFn = mockF.fn();
195
+
196
+ const resolveRemove = addRequestHandler(
197
+ 'POST',
198
+ `${subscriber.path}/${completionId}/resolve`,
199
+ undefined,
200
+ resolveFn
201
+ );
202
+
203
+ const rejectRemove = addRequestHandler(
204
+ 'POST',
205
+ `${subscriber.path}/${completionId}/reject`,
206
+ undefined,
207
+ rejectFn
208
+ );
209
+
210
+ const remove = () => {
211
+ resolveRemove();
212
+ rejectRemove();
213
+ }
214
+
215
+ const fire = (data: object) => {
216
+ send({
217
+ event: listenerName,
218
+ data,
219
+ completion_id: completionId,
220
+ });
221
+ }
222
+
223
+ return { fire, remove, resolveFn, rejectFn };
224
+ };
225
+
226
+ return {
227
+ addResolver,
228
+
229
+ ...subscriber,
230
+ }
231
+ }
232
+
233
+
234
+ mockServer.on('connection', s => {
235
+ socket = s;
236
+
237
+ socket.on('message', (messageObj) => {
238
+ const request: OutgoingRequest = JSON.parse(messageObj as string);
239
+ const emitId = toEmitId(request.path, request.method);
240
+ const processed = emitter.emit(emitId, request, s);
241
+ if (reportMissingListeners && !processed) {
242
+ console.warn(`Mock server: no listeners for event ${request.method} ${request.path}`);
243
+ }
244
+ });
245
+ });
246
+
247
+ mockServer.on('close', () => {
248
+ emitter.removeAllListeners();
249
+ });
250
+
251
+ return {
252
+ addRequestHandler,
253
+ addErrorHandler,
254
+
255
+ addSubscriptionHandler,
256
+ addHookHandler,
257
+
258
+ ignoreMissingHandler: addDummyDataHandler,
259
+ stop: () => {
260
+ mockServer.stop(() => {
261
+ // ...
262
+ });
263
+ },
264
+ send,
265
+ url,
266
+ };
267
+ };
268
+
269
+ export { getMockServer };
@@ -0,0 +1,68 @@
1
+ import { Socket } from '../../NodeSocket.js';
2
+ import { WebSocket } from 'mock-socket';
3
+
4
+ import * as Options from '../../types/options.js';
5
+ import ApiConstants from '../../ApiConstants.js';
6
+
7
+ import { getMockServer } from './mock-server.js';
8
+ import { DEFAULT_AUTH_RESPONSE, DEFAULT_CONNECT_PARAMS } from './mock-data.js';
9
+
10
+ const getDefaultSocketOptions = (): Options.APISocketOptions => ({
11
+ ...DEFAULT_CONNECT_PARAMS,
12
+ logOutput: console,
13
+ logLevel: 'warn',
14
+ });
15
+
16
+ export type MockSocketConnectOptions = Omit<Options.APISocketOptions, 'username' | 'password' | 'url'> & {
17
+ username?: string;
18
+ password?: string;
19
+ url?: string;
20
+ };
21
+
22
+
23
+ type RequestCallback = (requestData: object) => void;
24
+
25
+ interface MockSocketOptions {
26
+ console: Options.LogOutput;
27
+ socketOptions?: MockSocketConnectOptions;
28
+ }
29
+
30
+ interface MockConnectedSocketOptions extends MockSocketOptions {
31
+ authCallback?: RequestCallback;
32
+ authResponse: object;
33
+ }
34
+
35
+
36
+ export const getSocket = (socketOptions: MockSocketConnectOptions = {}) => {
37
+ const socket = Socket(
38
+ {
39
+ ...getDefaultSocketOptions(),
40
+ ...socketOptions,
41
+ },
42
+ WebSocket as any
43
+ );
44
+
45
+ return { socket };
46
+ };
47
+
48
+ const getDefaultConnectOptions = () => ({
49
+ console,
50
+ authResponse: DEFAULT_AUTH_RESPONSE,
51
+ });
52
+
53
+ export const getConnectedSocket = async (
54
+ server: ReturnType<typeof getMockServer>,
55
+ userOptions?: Partial<MockConnectedSocketOptions>,
56
+ ) => {
57
+ const options: MockConnectedSocketOptions = {
58
+ ...getDefaultConnectOptions(),
59
+ ...userOptions,
60
+ };
61
+
62
+ server.addRequestHandler('POST', ApiConstants.LOGIN_URL, options.authResponse, options.authCallback);
63
+
64
+ const { socket } = getSocket(options.socketOptions);
65
+ await socket.connect();
66
+
67
+ return { socket };
68
+ };
@@ -1,15 +1,14 @@
1
1
  import {
2
2
  getMockServer,
3
3
  getConnectedSocket,
4
- waitForExpect,
5
- } from './helpers.js';
4
+ } from './mocks';
5
+ import { waitForExpect } from './test-utils.js';
6
6
 
7
7
  import { jest } from '@jest/globals';
8
8
 
9
9
  import { addContextMenuItems } from '../PublicHelpers.js';
10
10
  import { SelectedMenuItemListenerData, MenuItemListHookData, MenuItemListHookAcceptData } from '../types/public_helpers_internal.js';
11
- import { HookSubscriberInfo } from '../types/index.js';
12
- import { IncomingSubscriptionEvent } from '../types/api_internal.js';
11
+ import { HookSubscriberInfo, MenuCallbackProperties, MenuClickHandlerProperties } from '../types/index.js';
13
12
 
14
13
 
15
14
  let server: ReturnType<typeof getMockServer>;
@@ -18,7 +17,9 @@ let server: ReturnType<typeof getMockServer>;
18
17
  describe('public helpers', () => {
19
18
 
20
19
  beforeEach(() => {
21
- server = getMockServer();
20
+ server = getMockServer({
21
+ mockF: jest,
22
+ });
22
23
  });
23
24
 
24
25
  afterEach(() => {
@@ -97,19 +98,10 @@ describe('public helpers', () => {
97
98
  // Socket handlers
98
99
  const { socket } = await getConnectedSocket(server);
99
100
 
100
- const listenerAddCallback = jest.fn();
101
- server.addDataHandler('POST', `menus/listeners/${MENU_ID}_menuitem_selected`, undefined, listenerAddCallback);
102
-
103
- const hookAddCallback = jest.fn();
104
- const hookResolveCallback = jest.fn();
105
- server.addDataHandler('POST', `menus/hooks/${MENU_ID}_list_menuitems`, undefined, hookAddCallback);
106
- server.addDataHandler(
107
- 'POST',
108
- `menus/hooks/${MENU_ID}_list_menuitems/${HOOK_COMPLETION_ID}/resolve`,
109
- menuItemListData,
110
- hookResolveCallback
111
- );
101
+ const itemSelectedListener = server.addSubscriptionHandler('menus', `${MENU_ID}_menuitem_selected`);
112
102
 
103
+ const listItemsHook = server.addHookHandler('menus', `${MENU_ID}_list_menuitems`);
104
+ const listItemsResolver = listItemsHook.addResolver(HOOK_COMPLETION_ID);
113
105
 
114
106
  // Add menu items
115
107
  const onClickItem1Mock = jest.fn();
@@ -123,42 +115,42 @@ describe('public helpers', () => {
123
115
  [
124
116
  {
125
117
  ...MENU_ITEM1,
126
- filter: async (ids, entityId) => {
118
+ filter: async () => {
127
119
  return true;
128
120
  },
129
121
  access: VALID_ACCESS,
130
- onClick: (ids, entityId, permissions, supports, formValues) => {
131
- onClickItem1Mock(ids, entityId, permissions, supports, formValues);
122
+ onClick: (props) => {
123
+ onClickItem1Mock(props);
132
124
  },
133
- formDefinitions: (ids, entityId, permissions, supports) => {
125
+ formDefinitions: () => {
134
126
  return FORM_DEFINITIONS;
135
127
  },
136
128
  }, {
137
129
  ...MENU_ITEM2,
138
- onClick: (ids, entityId) => {
139
- onClickItem2Mock(ids, entityId);
130
+ onClick: (props) => {
131
+ onClickItem2Mock(props);
140
132
  }
141
133
  }, {
142
134
  ...MENU_ITEM3,
143
- urls: (ids, entityId, permissions, supports) => {
144
- onGetUrlsItem3Mock(ids, entityId, permissions, supports);
135
+ urls: (props) => {
136
+ onGetUrlsItem3Mock(props);
145
137
  return URLS;
146
138
  }
147
139
  }, {
148
140
  id: 'ignored_filter_id',
149
141
  title: 'Mock item ignored by filter',
150
- filter: (ids, entityId, permissions, supports) => {
142
+ filter: () => {
151
143
  return false;
152
144
  },
153
- onClick: (ids, entityId, permissions, supports, formValues) => {
154
- onClickItemIgnoredMock(ids, entityId, permissions, supports, formValues);
145
+ onClick: (props) => {
146
+ onClickItemIgnoredMock(props);
155
147
  }
156
148
  }, {
157
149
  id: 'ignored_access_id',
158
150
  title: 'Mock item ignored by access',
159
151
  access: 'invalid_access',
160
- onClick: (ids, entityId, permissions, supports, formValues) => {
161
- onClickItemIgnoredMock(ids, entityId, permissions, supports, formValues);
152
+ onClick: (props) => {
153
+ onClickItemIgnoredMock(props);
162
154
  }
163
155
  }
164
156
  ],
@@ -166,33 +158,29 @@ describe('public helpers', () => {
166
158
  SUBSCRIBER_INFO,
167
159
  );
168
160
 
169
- expect(listenerAddCallback).toBeCalledTimes(1);
170
- expect(hookAddCallback).toBeCalledTimes(1);
161
+ expect(itemSelectedListener.subscribeFn).toHaveBeenCalledTimes(1);
162
+ expect(listItemsHook.subscribeFn).toHaveBeenCalledTimes(1);
171
163
 
172
164
 
173
165
  // List items hook
174
166
  {
175
- const hookEventData: IncomingSubscriptionEvent<MenuItemListHookData<string, null>> = {
176
- event: `${MENU_ID}_list_menuitems`,
177
- data: {
178
- selected_ids: selectedMenuIds,
179
- entity_id: null,
180
- permissions: PERMISSIONS,
181
- supports: SUPPORTS,
182
- },
183
- completion_id: 1,
167
+ const menuItemListData: MenuItemListHookData<string, null> = {
168
+ selected_ids: selectedMenuIds,
169
+ entity_id: null,
170
+ permissions: PERMISSIONS,
171
+ supports: SUPPORTS,
184
172
  };
185
173
 
186
- server.send(hookEventData);
174
+ listItemsResolver.fire(menuItemListData);
187
175
  }
188
176
 
189
177
  // Validate list items results
190
178
  {
191
179
  await waitForExpect(() => {
192
- expect(hookResolveCallback).toHaveBeenCalledTimes(1);
180
+ expect(listItemsResolver.resolveFn).toHaveBeenCalledTimes(1);
193
181
  });
194
182
 
195
- expect(hookResolveCallback).toBeCalledWith(
183
+ expect(listItemsResolver.resolveFn).toHaveBeenCalledWith(
196
184
  expect.objectContaining({
197
185
  data: menuItemListData
198
186
  }),
@@ -201,27 +189,31 @@ describe('public helpers', () => {
201
189
  await waitForExpect(() => {
202
190
  expect(onGetUrlsItem3Mock).toHaveBeenCalledTimes(1);
203
191
  });
204
- expect(onGetUrlsItem3Mock).toHaveBeenCalledWith(selectedMenuIds, null, PERMISSIONS, SUPPORTS);
192
+
193
+ const urlCallbackProps: MenuCallbackProperties<string, null> = {
194
+ selectedIds: selectedMenuIds,
195
+ entityId: null,
196
+ permissions: PERMISSIONS,
197
+ supports: SUPPORTS
198
+ }
199
+
200
+ expect(onGetUrlsItem3Mock).toHaveBeenCalledWith(urlCallbackProps);
205
201
  }
206
202
 
207
203
  // Select event listener
208
204
  {
209
- const selectEventData: IncomingSubscriptionEvent<SelectedMenuItemListenerData<string, null>> = {
210
- event: `${MENU_ID}_menuitem_selected`,
211
- data: {
212
- menuitem_id: MENU_ITEM1_ID,
213
- hook_id: SUBSCRIBER_INFO.id,
214
- menu_id: MENU_ID,
215
- entity_id: null,
216
- selected_ids: selectedMenuIds,
217
- permissions: PERMISSIONS,
218
- supports: SUPPORTS,
219
- form_values: FORM_VALUES
220
- },
221
- completion_id: HOOK_COMPLETION_ID,
205
+ const selectData: SelectedMenuItemListenerData<string, null> = {
206
+ menuitem_id: MENU_ITEM1_ID,
207
+ hook_id: SUBSCRIBER_INFO.id,
208
+ menu_id: MENU_ID,
209
+ entity_id: null,
210
+ selected_ids: selectedMenuIds,
211
+ permissions: PERMISSIONS,
212
+ supports: SUPPORTS,
213
+ form_values: FORM_VALUES
222
214
  };
223
215
 
224
- server.send(selectEventData);
216
+ itemSelectedListener.fire(selectData);
225
217
  }
226
218
 
227
219
  // Validate select event results
@@ -229,9 +221,18 @@ describe('public helpers', () => {
229
221
  await waitForExpect(() => {
230
222
  expect(onClickItem1Mock).toHaveBeenCalledTimes(1);
231
223
  });
232
- expect(onClickItem1Mock).toHaveBeenCalledWith(selectedMenuIds, null, PERMISSIONS, SUPPORTS, FORM_VALUES);
233
- expect(onClickItem2Mock).not.toBeCalled();
234
- expect(onClickItemIgnoredMock).not.toBeCalled();
224
+
225
+ const clickHandlerProps: MenuClickHandlerProperties<string, null> = {
226
+ selectedIds: selectedMenuIds,
227
+ entityId: null,
228
+ permissions: PERMISSIONS,
229
+ supports: SUPPORTS,
230
+ formValues: FORM_VALUES
231
+ }
232
+
233
+ expect(onClickItem1Mock).toHaveBeenCalledWith(clickHandlerProps);
234
+ expect(onClickItem2Mock).not.toHaveBeenCalled();
235
+ expect(onClickItemIgnoredMock).not.toHaveBeenCalled();
235
236
  }
236
237
 
237
238
  // Remove items
@@ -0,0 +1,31 @@
1
+ import waitForExpectOriginal from 'wait-for-expect';
2
+
3
+ const EXCEPT_TIMEOUT = 1000;
4
+ //@ts-ignore
5
+ export const waitForExpect = (func: () => void | Promise<void>) => waitForExpectOriginal.default(func, EXCEPT_TIMEOUT);
6
+
7
+
8
+ // This is a helper function that will suppress the error of a promise.
9
+ export const defusedPromise = (promise: Promise<any>) => {
10
+ promise.catch(() => {});
11
+ return promise;
12
+ }
13
+
14
+ export const getMockConsole = (verbose = false) => ({
15
+ log: jest.fn((a1: any, a2: any, a3: any, a4: any) => {
16
+ if (verbose) {
17
+ console.log(a1, a2, a3, a4);
18
+ }
19
+ }),
20
+ info: jest.fn((a1: any, a2: any, a3: any, a4: any) => {
21
+ if (verbose) {
22
+ console.info(a1, a2, a3, a4);
23
+ }
24
+ }),
25
+ warn: jest.fn((a1: any, a2: any, a3: any, a4: any) => {
26
+ console.warn(a1, a2, a3, a4);
27
+ }),
28
+ error: jest.fn((a1: any, a2: any, a3: any, a4: any) => {
29
+ console.error(a1, a2, a3, a4);
30
+ }),
31
+ });
@@ -1,6 +1,5 @@
1
1
  // HELPERS
2
2
  export type CompletionIdType = number;
3
- //export type SubscriptionIdType = string;
4
3
 
5
4
 
6
5
  // REQUESTS
@@ -1,29 +1,29 @@
1
1
  import { HookSubscriberInfo } from './subscriptions.js';
2
2
 
3
- type AsyncCallbackProperty<IdT, EntityIdT, ReturnT> = (
3
+ export interface MenuCallbackProperties<IdT, EntityIdT> {
4
4
  selectedIds: IdT[],
5
- entityId: EntityIdT | null,
5
+ entityId: EntityIdT,
6
6
  permissions: string[],
7
7
  supports: string[]
8
- ) => ReturnT | Promise<ReturnT>;
8
+ }
9
+
10
+ export interface MenuClickHandlerProperties<IdT, EntityIdT, FormValueT extends object = object> extends MenuCallbackProperties<IdT, EntityIdT> {
11
+ formValues: FormValueT
12
+ }
13
+
14
+ type AsyncCallbackProperty<IdT, EntityIdT, ReturnT> = (props: MenuCallbackProperties<IdT, EntityIdT>) => ReturnT | Promise<ReturnT>;
9
15
 
10
16
  export type ContextMenuIcon = { [key in string]: string };
11
17
 
12
18
  export interface ContextMenu extends HookSubscriberInfo {
13
19
  icon?: ContextMenuIcon;
14
20
  }
15
- export interface ContextMenuItem<IdT, EntityIdT> {
21
+ export interface ContextMenuItem<IdT, EntityIdT, FormValueT extends object = object> {
16
22
  id: string;
17
23
  title: string;
18
24
  icon?: ContextMenuIcon;
19
25
  urls?: string[] | AsyncCallbackProperty<IdT, EntityIdT, string[] | undefined>;
20
- onClick?: (
21
- selectedIds: IdT[],
22
- entityId: EntityIdT | null,
23
- permissions: string[],
24
- supports: string[],
25
- formValues: object
26
- ) => void;
26
+ onClick?: (props: MenuClickHandlerProperties<IdT, EntityIdT, FormValueT>) => void;
27
27
  filter?: AsyncCallbackProperty<IdT, EntityIdT, boolean>;
28
28
  access?: string;
29
29
  formDefinitions?: object[] | AsyncCallbackProperty<IdT, EntityIdT, object[]>;
@@ -6,7 +6,7 @@ export interface SelectedMenuItemListenerData<IdT, EntityIdT> {
6
6
  menu_id: string;
7
7
  menuitem_id: string;
8
8
  selected_ids: IdT[];
9
- entity_id: EntityIdT | null;
9
+ entity_id: EntityIdT;
10
10
  permissions: string[];
11
11
  supports: string[];
12
12
  form_values: object;
@@ -14,14 +14,14 @@ export interface SelectedMenuItemListenerData<IdT, EntityIdT> {
14
14
 
15
15
  export interface MenuItemListHookData<IdT, EntityIdT> {
16
16
  selected_ids: IdT[];
17
- entity_id: EntityIdT | null;
17
+ entity_id: EntityIdT;
18
18
  permissions: string[];
19
19
  supports: string[];
20
20
  }
21
21
 
22
22
  export interface ResponseMenuItemCallbackFields {
23
- urls?: string[] | undefined;
24
- form_definitions?: object[] | undefined;
23
+ urls?: string[];
24
+ form_definitions?: object[];
25
25
  }
26
26
 
27
27
  export type ResponseMenuItem<IdT, EntityIdT> = Omit<ContextMenuItem<IdT, EntityIdT>, 'onClick' | 'filter' | 'urls' | 'form_definitions'> & ResponseMenuItemCallbackFields;
@@ -26,4 +26,5 @@ export interface APISocket extends SocketRequestMethods, SocketSubscriptions {
26
26
  onSessionReset: SessionResetCallback | null;
27
27
  onDisconnected: DisconnectedCallback | null;
28
28
  readonly nativeSocket: WebSocket | null;
29
+ readonly url: string;
29
30
  }
package/tsconfig.json CHANGED
@@ -19,7 +19,7 @@
19
19
  "esModuleInterop": true
20
20
  },
21
21
  "exclude": [
22
- "src/tests",
22
+ "src/tests/*.test.ts",
23
23
  "jest.config.js",
24
24
  ]
25
25
  }