@buley/dash 0.0.30

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 (97) hide show
  1. package/.coveralls.yml +1 -0
  2. package/.github/workflows/opencommit.yml +33 -0
  3. package/.github/workflows/testing.yml +20 -0
  4. package/.gitmodules +0 -0
  5. package/README.md +98 -0
  6. package/behaviors/cache.dev.js +282 -0
  7. package/behaviors/changes.dev.js +337 -0
  8. package/behaviors/collect.dev.js +40 -0
  9. package/behaviors/examples/async.dev.js +17 -0
  10. package/behaviors/firebase.dev.js +283 -0
  11. package/behaviors/live.dev.js +67 -0
  12. package/behaviors/map.dev.js +54 -0
  13. package/behaviors/mapreduce.dev.js +68 -0
  14. package/behaviors/match.dev.js +66 -0
  15. package/behaviors/patch.dev.js +69 -0
  16. package/behaviors/rest.dev.js +340 -0
  17. package/behaviors/shorthand.dev.js +59 -0
  18. package/behaviors/stats.dev.js +672 -0
  19. package/dist/behaviors/index.js +142 -0
  20. package/dist/database/index.js +76 -0
  21. package/dist/databases/index.js +121 -0
  22. package/dist/entry/index.js +166 -0
  23. package/dist/index.js +93 -0
  24. package/dist/indexes/index.js +153 -0
  25. package/dist/store/index.js +97 -0
  26. package/dist/stores/index.js +90 -0
  27. package/dist/utilities/index.js +174 -0
  28. package/documentation/database/closing.md +3 -0
  29. package/documentation/database/getting.md +1 -0
  30. package/documentation/database/opening.md +5 -0
  31. package/documentation/database/removing.md +3 -0
  32. package/documentation/databases.md +21 -0
  33. package/documentation/entries.md +13 -0
  34. package/documentation/entry/adding.md +1 -0
  35. package/documentation/entry/getting.md +4 -0
  36. package/documentation/entry/putting.md +0 -0
  37. package/documentation/entry/removing.md +3 -0
  38. package/documentation/entry/updating.md +0 -0
  39. package/documentation/general/security.md +10 -0
  40. package/documentation/general/transaction/requests.md +3 -0
  41. package/documentation/general/transactions.md +5 -0
  42. package/documentation/index/creating.md +1 -0
  43. package/documentation/index/getting.md +1 -0
  44. package/documentation/index/iterating.md +1 -0
  45. package/documentation/index/removing.md +1 -0
  46. package/documentation/indexes.md +3 -0
  47. package/documentation/key/cursors.md +5 -0
  48. package/documentation/key/range/bounds.md +11 -0
  49. package/documentation/key/range/direction.md +1 -0
  50. package/documentation/key/ranges.md +3 -0
  51. package/documentation/keys.md +12 -0
  52. package/documentation/objectstore/clearing.md +1 -0
  53. package/documentation/objectstore/creating.md +1 -0
  54. package/documentation/objectstore/getting.md +1 -0
  55. package/documentation/objectstore/iteration.md +1 -0
  56. package/documentation/objectstore/removing.md +1 -0
  57. package/documentation/overview.md +5 -0
  58. package/documentation/stores.md +13 -0
  59. package/jest.config.js +12 -0
  60. package/package.json +40 -0
  61. package/src/behaviors/index.ts +140 -0
  62. package/src/database/index.ts +81 -0
  63. package/src/databases/index.ts +127 -0
  64. package/src/entry/index.ts +183 -0
  65. package/src/index/index.ts +61 -0
  66. package/src/index.ts +96 -0
  67. package/src/indexes/index.ts +151 -0
  68. package/src/store/index.ts +102 -0
  69. package/src/stores/index.ts +90 -0
  70. package/src/utilities/index.ts +349 -0
  71. package/tests/behaviors/behaviors.spec.ts +123 -0
  72. package/tests/database/database.spec.ts +177 -0
  73. package/tests/databases/databases.spec.ts +199 -0
  74. package/tests/entry/entry.spec.ts +252 -0
  75. package/tests/index/index.spec.ts +94 -0
  76. package/tests/indexes/indexes.spec.ts +203 -0
  77. package/tests/store/store.spec.ts +164 -0
  78. package/tests/stores/stores.spec.ts +148 -0
  79. package/tests/utilities/clone.spec.ts +48 -0
  80. package/tests/utilities/cloneError.spec.ts +33 -0
  81. package/tests/utilities/contains.spec.ts +28 -0
  82. package/tests/utilities/exists.spec.ts +21 -0
  83. package/tests/utilities/is.spec.ts +37 -0
  84. package/tests/utilities/isArray.spec.ts +21 -0
  85. package/tests/utilities/isBoolean.spec.ts +23 -0
  86. package/tests/utilities/isEmpty.spec.ts +45 -0
  87. package/tests/utilities/isFunction.spec.ts +30 -0
  88. package/tests/utilities/isNumber.spec.ts +29 -0
  89. package/tests/utilities/isObject.spec.ts +42 -0
  90. package/tests/utilities/isRegEx.spec.ts +33 -0
  91. package/tests/utilities/isString.spec.ts +25 -0
  92. package/tests/utilities/isnt.spec.ts +50 -0
  93. package/tests/utilities/randomId.spec.ts +39 -0
  94. package/tests/utilities/safeApply.spec.ts +49 -0
  95. package/tests/utilities/safeEach.spec.ts +38 -0
  96. package/tests/utilities/safeIterate.spec.ts +47 -0
  97. package/tsconfig.json +16 -0
@@ -0,0 +1,349 @@
1
+ /**
2
+ * Common interfaces and utilities.
3
+ * @module utilities
4
+ */
5
+
6
+ /**
7
+ * @typedef {Object} DashContext
8
+ * @property {Object} [db] - The IDBDatabase object.
9
+ * @property {Object} [transaction] - The IDBTransaction object.
10
+ * @property {DashObjectStore} [objectstore] - The IDBObjectStore object.
11
+ * @property {IDBKeyRange} [range] - The IDBKeyRange object.
12
+ * @property {IDBIndex} [idx] - The IDBIndex object.
13
+ * @property {any} [entry] - The entry.
14
+ * @property {IDBRequest} [request] - The IDBRequest object.
15
+ * @property {any} [error] - The error.
16
+ * @property {any} [key] - The key.
17
+ * @property {Event} [event] - The event.
18
+ * @property {any} [data] - The data.
19
+ * @property {number} [amount] - The amount.
20
+ * @property {string} [type] - The type.
21
+ * @property {Function} [on_success] - The on_success function.
22
+ * @property {Function} [on_error] - The on_error function.
23
+ */
24
+ export interface DashContext {
25
+ [key: string]: any;
26
+ db?: IDBDatabase;
27
+ transaction?: IDBTransaction;
28
+ objectstore?: IDBObjectStore;
29
+ range?: IDBKeyRange;
30
+ idx?: IDBIndex;
31
+ entry?: any;
32
+ request?: IDBRequest;
33
+ error?: any;
34
+ key?: any;
35
+ event?: Event;
36
+ data?: any;
37
+ amount?: number;
38
+ type?: string;
39
+ on_success?: (ctx: DashContext) => void;
40
+ on_error?: (ctx: DashContext) => void;
41
+ }
42
+
43
+ /**
44
+ * @typedef {Object} DashEnvironment
45
+ * @property {Document} [document] - The document object.
46
+ * @property {IDBFactory} [indexedDB] - The indexedDB object.
47
+ * @property {typeof IDBKeyRange} [IDBKeyRange] - The IDBKeyRange object.
48
+ * @property {typeof DOMStringList} [DOMStringList] - The DOMStringList object.
49
+ * @property {typeof Worker} [Worker] - The Worker object.
50
+ * @property {Function} constructor - The constructor function.
51
+ * @property {typeof addEventListener} [addEventListener] - The addEventListener function.
52
+ */
53
+ export interface DashEnvironment {
54
+ document?: Document;
55
+ indexedDB?: IDBFactory;
56
+ IDBKeyRange?: typeof IDBKeyRange;
57
+ DOMStringList?: typeof DOMStringList;
58
+ Worker?: typeof Worker;
59
+ constructor: Function;
60
+ addEventListener?: typeof addEventListener;
61
+ }
62
+
63
+ /**
64
+ * @typedef {Object} PublicAPI
65
+ * @property {Function} [add] - Add functions.
66
+ * @property {Function} [clear] - Clear functions.
67
+ * @property {Function} [count] - The count functions.
68
+ * @property {Function} [get] - The get functions.
69
+ * @property {Function} [remove] - The remove functions.
70
+ * @property {Function} [update] - The update functions.
71
+ */
72
+ export interface PublicAPI {
73
+ add: (ctx: DashContext) => Promise<DashContext>;
74
+ clear: (ctx: DashContext) => Promise<DashContext>;
75
+ count: (ctx: DashContext) => Promise<DashContext>;
76
+ get: (ctx: DashContext) => Promise<DashContext>;
77
+ remove: (ctx: DashContext) => Promise<DashContext>;
78
+ update: (ctx: DashContext) => Promise<DashContext>;
79
+ }
80
+
81
+ /**
82
+ * @typedef {Object} DashBehaviorContext
83
+ * @property {Function} clone - The clone function.
84
+ * @property {Function} contains - The contains function.
85
+ * @property {Function} exists - The exists function.
86
+ * @property {Function} is - The is function.
87
+ * @property {Function} isEmpty - The isEmpty function.
88
+ * @property {Function} isnt - The isnt function.
89
+ * @property {Function} isArray - The isArray function.
90
+ * @property {Function} isBoolean - The isBoolean function.
91
+ * @property {Function} isRegEx - The isRegEx function.
92
+ * @property {Function} isFunction - The isFunction function.
93
+ * @property {Function} isObject - The isObject function.
94
+ * @property {Function} isNumber - The isNumber function.
95
+ * @property {Function} isString - The isString function.
96
+ * @property {Function} apply - The apply function.
97
+ * @property {Function} each - The each function.
98
+ * @property {Function} iterate - The iterate function.
99
+ * @property {Function} random - The random function.
100
+ * @property {PublicAPI} api - The public API object.
101
+ */
102
+
103
+ export interface DashBehaviorContext {
104
+ clone: typeof clone;
105
+ contains: typeof contains;
106
+ exists: typeof exists;
107
+ is: typeof is;
108
+ isEmpty: typeof isEmpty;
109
+ isnt: typeof isnt;
110
+ isArray: typeof isArray;
111
+ isBoolean: typeof isBoolean;
112
+ isRegEx: typeof isRegEx;
113
+ isFunction: typeof isFunction;
114
+ isObject: typeof isObject;
115
+ isNumber: typeof isNumber;
116
+ isString: typeof isString;
117
+ apply: typeof safeApply;
118
+ each: typeof safeEach;
119
+ iterate: typeof safeIterate;
120
+ random: typeof randomId;
121
+ api: PublicAPI;
122
+ }
123
+ /**
124
+ * @typedef {Object} DashObjectStore
125
+ * @property {string | null} [keyPath] - The key path.
126
+ * @property {boolean} [autoIncrement] - Whether to auto-increment.
127
+ * @property {Function} [clear] - The clear function.
128
+ * @property {Function} [deleteObjectStore] - The deleteObjectStore function.
129
+ * @property {Function} [put] - The put function.
130
+ * @property {Function} [index] - The index function.
131
+ */
132
+ export interface DashObjectStore {
133
+ keyPath?: string | null;
134
+ autoIncrement?: boolean;
135
+ clear?: () => void;
136
+ deleteObjectStore?: (store: string) => void;
137
+ put?: (data: any, key?: any) => IDBRequest;
138
+ index?: (name: string) => IDBIndex;
139
+ }
140
+
141
+ /**
142
+ * @typedef {Object} DashIndex
143
+ * @property {string} name - The name.
144
+ * @property {boolean} multiEntry - Whether it's a multi-entry index.
145
+ * @property {boolean} unique - Whether it's a unique index.
146
+ * @property {string} keyPath - The key path.
147
+ */
148
+ export interface DashIndex {
149
+ name: string;
150
+ multiEntry: boolean;
151
+ unique: boolean;
152
+ keyPath: string;
153
+ }
154
+
155
+ /**
156
+ * @typedef {Object} DashProviderCacheObj
157
+ * @property {Object} [database] - The database object.
158
+ * @property {Object} [stores] - The stores object.
159
+ * @property {Object} [indexes] - The indexes object.
160
+ * @property {DashIndex} [data] - The data object.
161
+ */
162
+ export interface DashProviderCacheObj {
163
+ [database: string]: {
164
+ stores: {
165
+ [store: string]: {
166
+ indexes: {
167
+ [index: string]: {
168
+ data: DashIndex;
169
+ };
170
+ };
171
+ data: any;
172
+ };
173
+ };
174
+ data: any;
175
+ };
176
+ }
177
+
178
+
179
+ /**
180
+ * @param {DashContext} ctx - The context.
181
+ * @returns {Promise<DashContext>} The promise object.
182
+ * @private
183
+ */
184
+ export const cloneError = (error: Error): Error => {
185
+ const clonedError = new Error(error.message);
186
+
187
+ // Optionally, clone the stack trace if it's available
188
+ if (error.stack) {
189
+ clonedError.stack = error.stack;
190
+ }
191
+
192
+ // Clone custom properties
193
+ Object.keys(error).forEach((key) => {
194
+ (clonedError as any)[key] = (error as any)[key];
195
+ });
196
+
197
+ return clonedError;
198
+ };
199
+
200
+ // Clone function that performs a deep copy of an object, array, or function
201
+ export const clone = (source: any): any => {
202
+ if (source === null || typeof source !== 'object') {
203
+ return source;
204
+ }
205
+
206
+ // Handle arrays
207
+ if (Array.isArray(source)) {
208
+ return source.map(item => clone(item));
209
+ }
210
+
211
+ // Handle functions (return the same function reference)
212
+ if (typeof source === 'function') {
213
+ return source;
214
+ }
215
+
216
+ // Handle objects
217
+ const copy: { [key: string]: any } = {};
218
+ for (const key in source) {
219
+ if (source.hasOwnProperty(key)) {
220
+ copy[key] = clone(source[key]); // Recursively clone properties
221
+ }
222
+ }
223
+ return copy;
224
+ };
225
+
226
+ // Checks if an item exists in an array or object
227
+ export const contains = (str: string, substr: string): boolean => {
228
+ if (typeof str !== 'string' || typeof substr !== 'string') {
229
+ return false; // Ensure both inputs are strings
230
+ }
231
+
232
+ // An empty substring should always return true
233
+ if (substr === '') {
234
+ return true;
235
+ }
236
+
237
+ return str.includes(substr); // Use built-in includes method for substring checking
238
+ };
239
+
240
+ // Checks whether a value exists
241
+ export const exists = (value: any): boolean => {
242
+ return value !== null && value !== undefined;
243
+ };
244
+
245
+ // Checks if a variable is empty
246
+ export const isEmpty = (mixed_var: any): boolean => {
247
+ // Handle null and undefined cases
248
+ if (mixed_var === null || mixed_var === undefined) {
249
+ return true;
250
+ }
251
+
252
+ // Handle objects (including arrays)
253
+ if (typeof mixed_var === 'object') {
254
+ return Object.keys(mixed_var).length === 0;
255
+ }
256
+
257
+ // Handle strings (empty string should return true)
258
+ if (typeof mixed_var === 'string') {
259
+ return mixed_var.length === 0;
260
+ }
261
+
262
+ // For numbers and booleans, they are considered "not collections" and hence should return true
263
+ if (typeof mixed_var === 'number' || typeof mixed_var === 'boolean') {
264
+ return true;
265
+ }
266
+
267
+ return false;
268
+ };
269
+
270
+ export const is = (a: any, b: any): boolean => {
271
+ return Object.is(a, b);
272
+ };
273
+
274
+ export const isnt = (a: any, b: any): boolean => {
275
+ return a !== b;
276
+ };
277
+ export const isArray = Array.isArray;
278
+ export const isBoolean = (mixed_var: any): boolean => typeof mixed_var === 'boolean';
279
+ export const isRegEx = (mixed_var: any): boolean => mixed_var instanceof RegExp;
280
+ export const isFunction = (value: any): boolean => {
281
+ if (typeof value !== 'function') {
282
+ return false;
283
+ }
284
+
285
+ // Check if it's a class constructor by examining the function's string representation
286
+ const isClass = /^class\s/.test(Function.prototype.toString.call(value));
287
+ return !isClass;
288
+ };
289
+
290
+ export const isObject = (value: any): boolean => {
291
+ return Object.prototype.toString.call(value) === '[object Object]';
292
+ };
293
+ export const isNumber = (mixed_var: any): boolean => typeof mixed_var === 'number';
294
+ export const isString = (value: any): boolean => {
295
+ return typeof value === 'string' || value instanceof String;
296
+ };
297
+
298
+ // Applies a function if it's defined
299
+ export const safeApply = (fn: Function | undefined, args: any[], context?: any, err?: Function): any => {
300
+ if (typeof fn === 'function') return fn.apply(context || {}, args || []);
301
+ if (typeof err === 'function') return safeApply(err, []);
302
+ };
303
+
304
+ // Iterates through an array and applies a callback function
305
+ export const safeEach = (items: any[], callback: (item: any, index: number) => void, inc = 1): void => {
306
+ for (let x = 0; x < items.length; x += inc) {
307
+ safeApply(callback, [items[x], x]);
308
+ }
309
+ };
310
+
311
+ // Iterates through an object's properties and applies a callback function
312
+ export const safeIterate = (item: any, callback: (key: string, value: any) => void): void => {
313
+ for (const attr in item) {
314
+ if (item.hasOwnProperty(attr)) {
315
+ callback(attr, item[attr]);
316
+ }
317
+ }
318
+ };
319
+
320
+ // Generates a random string ID
321
+ export const randomId = (len = 16, charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"): string => {
322
+ let str = '';
323
+ for (let i = 0; i < len; i++) {
324
+ str += charset.charAt(Math.floor(Math.random() * charset.length));
325
+ }
326
+ return str;
327
+ };
328
+
329
+
330
+ export default {
331
+ cloneError,
332
+ clone,
333
+ contains,
334
+ exists,
335
+ isEmpty,
336
+ is,
337
+ isnt,
338
+ isArray,
339
+ isBoolean,
340
+ isRegEx,
341
+ isFunction,
342
+ isObject,
343
+ isNumber,
344
+ isString,
345
+ safeApply,
346
+ safeEach,
347
+ safeIterate,
348
+ randomId
349
+ };
@@ -0,0 +1,123 @@
1
+ import behaviorMethods from './../../src/behaviors';
2
+ import { DashContext } from './../../src/utilities';
3
+
4
+ // Mock the behavior context functions
5
+ jest.mock('./../../src/utilities', () => ({
6
+ cloneError: jest.fn(),
7
+ safeApply: jest.fn(),
8
+ safeEach: jest.fn((array, fn) => array.forEach(fn)),
9
+ }));
10
+
11
+ describe('behaviorMethods', () => {
12
+ let mockRequest: any;
13
+
14
+ beforeEach(() => {
15
+ // Clear behavior actions and filters to avoid shared state between tests
16
+ behaviorMethods.behaviorFilters.length = 0;
17
+ behaviorMethods.behaviorActions.length = 0;
18
+
19
+ // Clear all mocks before each test
20
+ jest.clearAllMocks();
21
+ });
22
+
23
+ beforeEach(() => {
24
+ mockRequest = {
25
+ addEventListener: jest.fn(),
26
+ removeEventListener: jest.fn(),
27
+ result: 'testResult',
28
+ error: new Error('Test Error'),
29
+ };
30
+ });
31
+
32
+ describe('get', () => {
33
+ it('should retrieve all behaviors', async () => {
34
+ const get_ctx: DashContext = {
35
+ objectstore: {
36
+ indexNames: ['behavior1', 'behavior2'],
37
+ } as unknown as IDBObjectStore,
38
+ on_success: jest.fn(),
39
+ };
40
+
41
+ // Mock safeApply to call the on_success without using the spread operator
42
+ const safeApply = require('./../../src/utilities').safeApply;
43
+ safeApply.mockImplementation((callback: (arg0: any) => any, args: any[]) => callback(args[0]));
44
+
45
+ const result = await behaviorMethods.get(get_ctx);
46
+
47
+ expect(get_ctx.behaviors).toEqual(['behavior1', 'behavior2']);
48
+ expect(get_ctx.on_success).toHaveBeenCalledWith(get_ctx); // Check if on_success is called correctly
49
+ });
50
+
51
+
52
+ it('should handle errors during retrieval', async () => {
53
+ const get_ctx: DashContext = {
54
+ objectstore: {
55
+ indexNames: null, // Simulate an error
56
+ } as unknown as IDBObjectStore,
57
+ on_error: jest.fn(),
58
+ };
59
+
60
+ // Mock cloneError to return a proper error object
61
+ const cloneError = require('./../../src/utilities').cloneError;
62
+ cloneError.mockImplementation((error: Error) => ({ message: 'Test Error' }));
63
+
64
+ // Mock safeApply to call the on_error callback
65
+ const safeApply = require('./../../src/utilities').safeApply;
66
+ safeApply.mockImplementation((callback: (arg0: any) => any, args: any[]) => callback(args[0]));
67
+
68
+ await expect(behaviorMethods.get(get_ctx)).rejects.toEqual(get_ctx);
69
+ expect(get_ctx.error).toBeDefined(); // Check if error is set
70
+ expect(get_ctx.error.message).toBe('Test Error'); // Check error message
71
+ expect(get_ctx.on_error).toHaveBeenCalledWith(get_ctx); // Ensure on_error is called
72
+ });
73
+
74
+
75
+ });
76
+
77
+ describe('add', () => {
78
+ it('should add a behavior action', () => {
79
+ const mockFilter = jest.fn();
80
+ const mockAction = jest.fn();
81
+
82
+ behaviorMethods.add([mockFilter, mockAction]);
83
+
84
+ // Ensure the filter and action are added to the internal lists
85
+ expect(behaviorMethods.behaviorFilters).toContain(mockFilter);
86
+ expect(behaviorMethods.behaviorActions).toContain(mockAction);
87
+ });
88
+ });
89
+
90
+ describe('applyBehaviors', () => {
91
+ it('should apply all behavior actions', async () => {
92
+ const mockAction = jest.fn(() => ({ context: { objectstore: {} }, type: 'testSlug' })); // Return the correct structure
93
+ const ctx: DashContext = { objectstore: {} } as DashContext;
94
+
95
+ const safeApply = require('./../../src/utilities').safeApply;
96
+ safeApply.mockImplementation((action: (arg0: any) => any, args: any) => action(args[0])); // Pass the context directly
97
+
98
+ behaviorMethods.add(mockAction);
99
+
100
+ const finalContext = await behaviorMethods.applyBehaviors(ctx, 'testSlug', 'testMethod', Promise.resolve(ctx));
101
+
102
+ expect(mockAction).toHaveBeenCalled();
103
+ expect(finalContext).toStrictEqual(ctx); // Use toStrictEqual for deep equality check
104
+ });
105
+
106
+ it('should handle errors during action application', async () => {
107
+ const mockAction = jest.fn(() => {
108
+ throw new Error('Test Error');
109
+ });
110
+ const ctx: DashContext = { objectstore: {} } as DashContext;
111
+
112
+ const safeApply = require('./../../src/utilities').safeApply;
113
+ safeApply.mockImplementation(() => {
114
+ throw new Error('Test Error');
115
+ });
116
+
117
+ behaviorMethods.add(mockAction);
118
+
119
+ await expect(behaviorMethods.applyBehaviors(ctx, 'testSlug', 'testMethod', Promise.resolve(ctx))).rejects.toThrow('Test Error');
120
+ });
121
+
122
+ });
123
+ });
@@ -0,0 +1,177 @@
1
+ import { databaseMethods } from '../../src/database';
2
+ import { DashContext } from '../../src/utilities';
3
+
4
+ describe('databaseMethods', () => {
5
+ let mockObjectStore: {
6
+ get: jest.Mock;
7
+ put: jest.Mock;
8
+ delete: jest.Mock;
9
+ };
10
+
11
+ beforeEach(() => {
12
+ mockObjectStore = {
13
+ get: jest.fn(),
14
+ put: jest.fn(),
15
+ delete: jest.fn(),
16
+ };
17
+ });
18
+
19
+ describe('get', () => {
20
+ it('should retrieve an entry from the database', async () => {
21
+ const get_ctx: DashContext = {
22
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
23
+ key: 'testKey',
24
+ };
25
+
26
+ const mockRequest = {
27
+ onsuccess: null as any,
28
+ onerror: null as any,
29
+ result: 'testData',
30
+ };
31
+
32
+ mockObjectStore.get.mockReturnValue(mockRequest);
33
+
34
+ const resultPromise = databaseMethods.get(get_ctx);
35
+ mockRequest.onsuccess({ target: { result: 'testData' } } as any);
36
+
37
+ const result = await resultPromise;
38
+ expect(result.entry).toBe('testData');
39
+ });
40
+
41
+ it('should handle errors when retrieving an entry', async () => {
42
+ const get_ctx: DashContext = {
43
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
44
+ key: 'testKey',
45
+ };
46
+
47
+ const mockRequest = {
48
+ onsuccess: null as any,
49
+ onerror: null as any,
50
+ error: new Error('Get Error'),
51
+ };
52
+
53
+ mockObjectStore.get.mockReturnValue(mockRequest);
54
+
55
+ const resultPromise = databaseMethods.get(get_ctx);
56
+ mockRequest.onerror({ target: { error: new Error('Get Error') } } as any);
57
+
58
+ await expect(resultPromise).rejects.toMatchObject({
59
+ error: { message: 'Get Error' },
60
+ });
61
+ });
62
+
63
+ it('should reject if the entry is missing', async () => {
64
+ const get_ctx: DashContext = {
65
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
66
+ key: 'testKey',
67
+ };
68
+
69
+ const mockRequest = {
70
+ onsuccess: null as any,
71
+ onerror: null as any,
72
+ result: undefined,
73
+ };
74
+
75
+ mockObjectStore.get.mockReturnValue(mockRequest);
76
+
77
+ const resultPromise = databaseMethods.get(get_ctx);
78
+ mockRequest.onsuccess({ target: { result: undefined } } as any);
79
+
80
+ await expect(resultPromise).rejects.toMatchObject({
81
+ error: { message: 'missing', name: 'DashNoEntry' },
82
+ });
83
+ });
84
+ });
85
+
86
+ describe('put', () => {
87
+ it('should put an entry in the database', async () => {
88
+ const put_ctx: DashContext = {
89
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
90
+ data: 'testData',
91
+ key: 'testKey',
92
+ };
93
+
94
+ const mockRequest = {
95
+ onsuccess: null as any,
96
+ onerror: null as any,
97
+ result: 'testKey',
98
+ };
99
+
100
+ mockObjectStore.put.mockReturnValue(mockRequest);
101
+
102
+ const resultPromise = databaseMethods.put(put_ctx);
103
+ mockRequest.onsuccess({ target: { result: 'testKey' } } as any);
104
+
105
+ const result = await resultPromise;
106
+ expect(result.key).toBe('testKey');
107
+ expect(result.entry).toBe('testData');
108
+ });
109
+
110
+ it('should handle errors during put', async () => {
111
+ const put_ctx: DashContext = {
112
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
113
+ data: 'testData',
114
+ key: 'testKey',
115
+ };
116
+
117
+ const mockRequest = {
118
+ onsuccess: null as any,
119
+ onerror: null as any,
120
+ error: new Error('Put Error'),
121
+ };
122
+
123
+ mockObjectStore.put.mockReturnValue(mockRequest);
124
+
125
+ const resultPromise = databaseMethods.put(put_ctx);
126
+ mockRequest.onerror({ target: { error: new Error('Put Error') } } as any);
127
+
128
+ await expect(resultPromise).rejects.toMatchObject({
129
+ error: { message: 'Put Error' },
130
+ });
131
+ });
132
+ });
133
+
134
+ describe('remove', () => {
135
+ it('should remove an entry from the database', async () => {
136
+ const remove_ctx: DashContext = {
137
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
138
+ key: 'testKey',
139
+ };
140
+
141
+ const mockRequest = {
142
+ onsuccess: null as any,
143
+ onerror: null as any,
144
+ };
145
+
146
+ mockObjectStore.delete.mockReturnValue(mockRequest);
147
+
148
+ const resultPromise = databaseMethods.remove(remove_ctx);
149
+ mockRequest.onsuccess({} as any);
150
+
151
+ const result = await resultPromise;
152
+ expect(result).toEqual(remove_ctx);
153
+ });
154
+
155
+ it('should handle errors during remove', async () => {
156
+ const remove_ctx: DashContext = {
157
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
158
+ key: 'testKey',
159
+ };
160
+
161
+ const mockRequest = {
162
+ onsuccess: null as any,
163
+ onerror: null as any,
164
+ error: new Error('Remove Error'),
165
+ };
166
+
167
+ mockObjectStore.delete.mockReturnValue(mockRequest);
168
+
169
+ const resultPromise = databaseMethods.remove(remove_ctx);
170
+ mockRequest.onerror({ target: { error: new Error('Remove Error') } } as any);
171
+
172
+ await expect(resultPromise).rejects.toMatchObject({
173
+ error: { message: 'Remove Error' },
174
+ });
175
+ });
176
+ });
177
+ });