forked from dexie/Dexie.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdexie-unittest-utils.js
196 lines (181 loc) · 8.16 KB
/
dexie-unittest-utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import Dexie from 'dexie';
import {ok, start, test, config} from 'QUnit';
// Custom QUnit config options.
config.urlConfig.push(/*{
id: "polyfillIE", // Remarked because has no effect anymore. Find out why.
label: "Include IE Polyfill",
tooltip: "Enabling this will include the idb-iegap polyfill that makes" +
" IE10&IE11 support multiEntry and compound indexes as well as compound" +
" primary keys"
}, {
id: "indexedDBShim", // Remarked because has no effect anymore. Need to find out why. Should invoke the shim if set!
label: "IndexedDBShim (UseWebSQL as backend)",
tooltip: "Enable this in Safari browsers without indexedDB support or" +
" with poor indexedDB support"
},*/ {
id: "dontoptimize",
label: "Dont optimize tests",
tooltip: "Always delete and recreate the DB between each test"
}, {
id: "longstacks",
label: "Long async stacks",
tooltip: "Set Dexie.debug=true, turning on long async stacks on all" +
" errors (Actually we use Dexie.debug='dexie' so that frames from" +
" dexie.js are also included)"
});
Dexie.debug = window.location.search.indexOf('longstacks') !== -1 ? 'dexie' : false;
if (window.location.search.indexOf('longstacks=tests') !== -1) Dexie.debug = true; // Don't include stuff from dexie.js.
var no_optimize = window.no_optimize || window.location.search.indexOf('dontoptimize') !== -1;
const ArrayBuffer = window.ArrayBuffer;
function stringify (idbKey) {
var res = '' + (idbKey && idbKey.constructor && idbKey.constructor === ArrayBuffer ?
new Uint8Array(idbKey) : idbKey);
return res;
}
export function resetDatabase(db) {
/// <param name="db" type="Dexie"></param>
var Promise = Dexie.Promise;
return no_optimize || !db._hasBeenCreated ?
// Full Database recreation. Takes much time!
db.delete().then(function () {
return db.open().then(function() {
if (!no_optimize) {
db._hasBeenCreated = true;
var initialState = (db._initialState = {});
// Now, snapshot the database how it looks like initially (what on.populate did)
return db.transaction('r', db.tables, function() {
var trans = Dexie.currentTransaction;
return Promise.all(trans.storeNames.filter(function(tableName) {
// Don't clear 'meta tables'
return tableName[0] != '_' && tableName[0] != '$';
}).map(function (tableName) {
var items = {};
initialState[tableName] = items;
return db.table(tableName).each(function(item, cursor) {
items[stringify(cursor.primaryKey)] = { key: cursor.primaryKey, value: item };
});
}));
});
}
});
})
:
// Optimize: Don't delete and recreate database. Instead, just clear all object stores,
// and manually run db.on.populate
db.transaction('rw!', db.tables, function() {
// Got to do an operation in order for backend transaction to be created.
var trans = Dexie.currentTransaction;
var initialState = db._initialState;
return Promise.all(trans.storeNames.filter(function(tableName) {
// Don't clear 'meta tables'
return tableName[0] != '_' && tableName[0] != '$';
}).map(function(tableName) {
// Read current state
var items = {};
return db.table(tableName).each(function(item, cursor) {
items[stringify(cursor.primaryKey)] = { key: cursor.primaryKey, value: item };
}).then(function() {
// Diff from initialState
// Go through initialState and diff with current state
var initialItems = initialState[tableName];
return Promise.all(Object.keys(initialItems).map(key => {
var item = items[key];
var initialItem = initialItems[key];
if (!item || JSON.stringify(item.value) != JSON.stringify(initialItem.value))
return (db.table(tableName).schema.primKey.keyPath ? db.table(tableName).put(initialItem.value) :
db.table(tableName).put(initialItem.value, initialItem.key));
return Promise.resolve();
}));
}).then(function() {
// Go through current state and diff with initialState
var initialItems = initialState[tableName];
var keysToDelete = Object.keys(items)
.filter(key => !initialItems[key])
.map(key => items[key].key);
if (keysToDelete.length > 0) {
return db.table(tableName).bulkDelete(keysToDelete);
}
});
}));
});
}
export function deleteDatabase(db) {
var Promise = Dexie.Promise;
return no_optimize ? db.delete() : db.transaction('rw!', db.tables, function() {
// Got to do an operation in order for backend transaction to be created.
var trans = Dexie.currentTransaction;
return Promise.all(trans.storeNames.filter(function(tableName) {
// Don't clear 'meta tables'
return tableName[0] != '_' && tableName[0] != '$';
}).map(function(tableName) {
// Clear all tables
return db.table(tableName).clear();
}));
});
}
export const isIE = !(window.ActiveXObject) && "ActiveXObject" in window;
export const isEdge = /Edge\/\d+/.test(navigator.userAgent);
export const isChrome = !!window.chrome;
var hasPolyfillIE = [].slice.call(document.getElementsByTagName("script")).some(
s => s.src.indexOf("idb-iegap") !== -1);
export function supports (features) {
return features.split('+').reduce((result,feature)=>{
switch (feature.toLowerCase()) {
case "compound":
return result && Array.isArray(Dexie.maxKey);
case "multientry":
return result && (hasPolyfillIE || (!isIE && !isEdge)); // Should add Safari to
case "deleteobjectstoreafterread":
return result && (!isIE && !isEdge);
case "versionchange":
return result;
//return result && (!isIE && !isEdge); // Should add Safari to
case "binarykeys":
try {
return result && Array.isArray(Dexie.maxKey) && indexedDB.cmp(new Uint8Array([1]), new Uint8Array([1])) === 0;
} catch (e) {
return false;
}
case "domevents":
return typeof window === 'object' && window.addEventListener;
default:
throw new Error ("Unknown feature: " + feature);
}
}, true);
}
export function spawnedTest (name, num, promiseGenerator) {
if (!promiseGenerator) {
promiseGenerator = num;
test(name, function(assert) {
let done = assert.async();
Dexie.spawn(promiseGenerator)
.catch(e => ok(false, e.stack || e))
.then(done);
});
} else {
test(name, num, function(assert) {
let done = assert.async();
Dexie.spawn(promiseGenerator)
.catch(e => ok(false, e.stack || e))
.then(done);
});
}
}
export function promisedTest (name, num, asyncFunction) {
if (!asyncFunction) {
asyncFunction = num;
test(name, (assert) => {
let done = assert.async();
Promise.resolve().then(asyncFunction)
.catch(e => ok(false, e.stack || e))
.then(done);
});
} else {
test(name, num, (assert) => {
let done = assert.async();
Promise.resolve().then(asyncFunction)
.catch(e => ok(false, e.stack || e))
.then(done);
});
}
}