@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.
- package/.coveralls.yml +1 -0
- package/.github/workflows/opencommit.yml +33 -0
- package/.github/workflows/testing.yml +20 -0
- package/.gitmodules +0 -0
- package/README.md +98 -0
- package/behaviors/cache.dev.js +282 -0
- package/behaviors/changes.dev.js +337 -0
- package/behaviors/collect.dev.js +40 -0
- package/behaviors/examples/async.dev.js +17 -0
- package/behaviors/firebase.dev.js +283 -0
- package/behaviors/live.dev.js +67 -0
- package/behaviors/map.dev.js +54 -0
- package/behaviors/mapreduce.dev.js +68 -0
- package/behaviors/match.dev.js +66 -0
- package/behaviors/patch.dev.js +69 -0
- package/behaviors/rest.dev.js +340 -0
- package/behaviors/shorthand.dev.js +59 -0
- package/behaviors/stats.dev.js +672 -0
- package/dist/behaviors/index.js +142 -0
- package/dist/database/index.js +76 -0
- package/dist/databases/index.js +121 -0
- package/dist/entry/index.js +166 -0
- package/dist/index.js +93 -0
- package/dist/indexes/index.js +153 -0
- package/dist/store/index.js +97 -0
- package/dist/stores/index.js +90 -0
- package/dist/utilities/index.js +174 -0
- package/documentation/database/closing.md +3 -0
- package/documentation/database/getting.md +1 -0
- package/documentation/database/opening.md +5 -0
- package/documentation/database/removing.md +3 -0
- package/documentation/databases.md +21 -0
- package/documentation/entries.md +13 -0
- package/documentation/entry/adding.md +1 -0
- package/documentation/entry/getting.md +4 -0
- package/documentation/entry/putting.md +0 -0
- package/documentation/entry/removing.md +3 -0
- package/documentation/entry/updating.md +0 -0
- package/documentation/general/security.md +10 -0
- package/documentation/general/transaction/requests.md +3 -0
- package/documentation/general/transactions.md +5 -0
- package/documentation/index/creating.md +1 -0
- package/documentation/index/getting.md +1 -0
- package/documentation/index/iterating.md +1 -0
- package/documentation/index/removing.md +1 -0
- package/documentation/indexes.md +3 -0
- package/documentation/key/cursors.md +5 -0
- package/documentation/key/range/bounds.md +11 -0
- package/documentation/key/range/direction.md +1 -0
- package/documentation/key/ranges.md +3 -0
- package/documentation/keys.md +12 -0
- package/documentation/objectstore/clearing.md +1 -0
- package/documentation/objectstore/creating.md +1 -0
- package/documentation/objectstore/getting.md +1 -0
- package/documentation/objectstore/iteration.md +1 -0
- package/documentation/objectstore/removing.md +1 -0
- package/documentation/overview.md +5 -0
- package/documentation/stores.md +13 -0
- package/jest.config.js +12 -0
- package/package.json +40 -0
- package/src/behaviors/index.ts +140 -0
- package/src/database/index.ts +81 -0
- package/src/databases/index.ts +127 -0
- package/src/entry/index.ts +183 -0
- package/src/index/index.ts +61 -0
- package/src/index.ts +96 -0
- package/src/indexes/index.ts +151 -0
- package/src/store/index.ts +102 -0
- package/src/stores/index.ts +90 -0
- package/src/utilities/index.ts +349 -0
- package/tests/behaviors/behaviors.spec.ts +123 -0
- package/tests/database/database.spec.ts +177 -0
- package/tests/databases/databases.spec.ts +199 -0
- package/tests/entry/entry.spec.ts +252 -0
- package/tests/index/index.spec.ts +94 -0
- package/tests/indexes/indexes.spec.ts +203 -0
- package/tests/store/store.spec.ts +164 -0
- package/tests/stores/stores.spec.ts +148 -0
- package/tests/utilities/clone.spec.ts +48 -0
- package/tests/utilities/cloneError.spec.ts +33 -0
- package/tests/utilities/contains.spec.ts +28 -0
- package/tests/utilities/exists.spec.ts +21 -0
- package/tests/utilities/is.spec.ts +37 -0
- package/tests/utilities/isArray.spec.ts +21 -0
- package/tests/utilities/isBoolean.spec.ts +23 -0
- package/tests/utilities/isEmpty.spec.ts +45 -0
- package/tests/utilities/isFunction.spec.ts +30 -0
- package/tests/utilities/isNumber.spec.ts +29 -0
- package/tests/utilities/isObject.spec.ts +42 -0
- package/tests/utilities/isRegEx.spec.ts +33 -0
- package/tests/utilities/isString.spec.ts +25 -0
- package/tests/utilities/isnt.spec.ts +50 -0
- package/tests/utilities/randomId.spec.ts +39 -0
- package/tests/utilities/safeApply.spec.ts +49 -0
- package/tests/utilities/safeEach.spec.ts +38 -0
- package/tests/utilities/safeIterate.spec.ts +47 -0
- 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
|
+
});
|