dumbmatter / fakeindexeddb Goto Github PK
View Code? Open in Web Editor NEWA pure JS in-memory implementation of the IndexedDB API
License: Apache License 2.0
A pure JS in-memory implementation of the IndexedDB API
License: Apache License 2.0
Thank you for making this library, we've been using it since we started using indexeddb.
This codesandbox succeeds with in-browser IndexedDB but fails with fake-indexeddb. I found it while looking into an issue where not all results are returned from a table with a compound primary index.
https://codesandbox.io/s/dexie-bug-tr8ml
I don't know how to reduce this to a more minimal case for fake-indexeddb, but I have also opened a ticket on dexie. dexie/Dexie.js#954
First off, awesome project! 👍
When running npm install fake-indexeddb
the dev dependencies of the project are also installed like browserify and phantomjs. Running ls node_modules/fake-indexeddb/node_modules
confirms this.
Looking at the package.json this shouldn't be happening so I'm somewhat confused. This may be due to the use of shrinkwrapping which appears to include the dev dependencies alongside the production ones.
Using v2.0.1 of io.js, I cloned and did npm install
and then npm test
and got:
W3C IDBCursor.advance Tests
asyncness
1) "before all" hook
0 passing (15ms)
1 failing
1) W3C IDBCursor.advance Tests asyncness "before all" hook:
Uncaught TypeError: database._rawDatabase.transactions.find is not a function
at confirmActiveVersionchangeTransaction (lib/FDBDatabase.js:19:58)
at FDBDatabase.createObjectStore (lib/FDBDatabase.js:65:27)
at FDBOpenDBRequest.open.onupgradeneeded (test/w3c/IDBCursor.advance.js:19:35)
at invokeEventListeners (lib/EventTarget.js:32:27)
at FDBOpenDBRequest.dispatchEvent (lib/EventTarget.js:82:13)
at waitForOthersClosed (lib/FDBFactory.js:132:17)
at runVersionchangeTransaction (lib/FDBFactory.js:162:5)
at openDatabase (lib/FDBFactory.js:186:9)
at null.<anonymous> (lib/FDBFactory.js:251:13)
npm ERR! Darwin 14.1.0
npm ERR! argv "/usr/local/bin/iojs" "/usr/local/bin/npm" "run" "mocha"
npm ERR! node v2.0.1
npm ERR! npm v2.9.0
npm ERR! code ELIFECYCLE
npm ERR! [email protected] mocha: `mocha --harmony --timeout 5000 test/w3c test/fakeIndexedDB`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] mocha script 'mocha --harmony --timeout 5000 test/w3c test/fakeIndexedDB'.
npm ERR! This is most likely a problem with the fake-indexeddb package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! mocha --harmony --timeout 5000 test/w3c test/fakeIndexedDB
npm ERR! You can get their info via:
npm ERR! npm owner ls fake-indexeddb
npm ERR! There is likely additional logging output above.
npm ERR! Please include the following file with any support request:
npm ERR! /Users/danielearwicker/github/fakeIndexedDB/npm-debug.log
npm ERR! Test failed. See above for more details.
Similar to #50, it would be awesome for fakeindexeddb to have a way to simulate the storage device running out of space and throwing a QuotaExceededError.
Currently this is really hard to repro, even in a browser (it's recommended to create a separate volume and start Chrome with that volume). Having it in fakeindexeddb would be extremely convenient.
Something like:
indexedDB.__simulateQuotaExceeded = true // false by default
The 3.1.0 version introduced a regress bug. The previous 3.0.2 works OK.
I have the following deletion using Dexie.js (3.0.1 version):
db.table('some-table')
.where('key')
.startsWith('some-prefix)
.delete();
It produces the next error:
Error: Could not delete some values. Errors: TypeError: Cannot read property 'key' of undefined
at /testproject/node_modules/dexie/src/classes/collection/collection.ts:586:36
at /testproject/node_modules/dexie/src/helpers/promise.js:843:23
at callListener (/testproject/node_modules/dexie/src/helpers/promise.js:497:19)
at endMicroTickScope (/testproject/node_modules/dexie/src/helpers/promise.js:583:25)
at /testproject/node_modules/dexie/src/helpers/promise.js:652:30
at done (/testproject/node_modules/dexie/src/dbcore/dbcore-indexeddb.ts:194:11)
at FDBRequest.req.onerror (/testproject/node_modules/dexie/src/dbcore/dbcore-indexeddb.ts:204:11)
at invokeEventListeners (/testproject/node_modules/fake-indexeddb/build/lib/FakeEventTarget.js:67:31)
at FDBRequest.Object.<anonymous>.FakeEventTarget.dispatchEvent (/testproject/node_modules/fake-indexeddb/build/lib/FakeEventTarget.js:120:13)
at FDBTransaction.Object.<anonymous>.FDBTransaction._start (/testproject/node_modules/fake-indexeddb/build/FDBTransaction.js:214:29)
at processImmediate (internal/timers.js:456:21)
From previous:
at DexiePromise.then (/testproject/node_modules/dexie/src/helpers/promise.js:182:22)
at /testproject/node_modules/dexie/src/classes/collection/collection.ts:585:12
at /testproject/node_modules/dexie/src/helpers/promise.js:843:23
at callListener (/testproject/node_modules/dexie/src/helpers/promise.js:497:19)
at endMicroTickScope (/testproject/node_modules/dexie/src/helpers/promise.js:583:25)
at FDBRequest.onsuccess (/testproject/node_modules/dexie/src/helpers/promise.js:652:30)
at invokeEventListeners (/testproject/node_modules/fake-indexeddb/build/lib/FakeEventTarget.js:67:31)
at FDBRequest.Object.<anonymous>.FakeEventTarget.dispatchEvent (/testproject/node_modules/fake-indexeddb/build/lib/FakeEventTarget.js:120:13)
at FDBTransaction.Object.<anonymous>.FDBTransaction._start (/testproject/node_modules/fake-indexeddb/build/FDBTransaction.js:214:29)
at Immediate.<anonymous> (/testproject/node_modules/fake-indexeddb/build/lib/Database.js:26:26)
at processImmediate (internal/timers.js:456:21)
From previous:
at Object.mutate (/testproject/node_modules/dexie/src/dbcore/dbcore-indexeddb.ts:120:14)
at Object.mutate (/testproject/node_modules/dexie/src/hooks/hooks-middleware.ts:52:28)
at /testproject/node_modules/dexie/src/classes/collection/collection.ts:584:33
at /testproject/node_modules/dexie/src/helpers/promise.js:843:23
at callListener (/testproject/node_modules/dexie/src/helpers/promise.js:497:19)
at endMicroTickScope (/testproject/node_modules/dexie/src/helpers/promise.js:583:25)
at FDBRequest.onsuccess (/testproject/node_modules/dexie/src/helpers/promise.js:652:30)
at invokeEventListeners (/testproject/node_modules/fake-indexeddb/build/lib/FakeEventTarget.js:67:31)
at FDBRequest.Object.<anonymous>.FakeEventTarget.dispatchEvent (/testproject/node_modules/fake-indexeddb/build/lib/FakeEventTarget.js:120:13)
at FDBTransaction.Object.<anonymous>.FDBTransaction._start (/testproject/node_modules/fake-indexeddb/build/FDBTransaction.js:214:29)
at Immediate.<anonymous> (/testproject/node_modules/fake-indexeddb/build/lib/Database.js:26:26)
at processImmediate (internal/timers.js:456:21)
Blobs are corrupted when saved to a store. I believe it is caused by Typeson not supporting blobs. FakeIndexedDB version 2.0.3 kind of supports it, it works but the console is filled with uncaught errors.
Are there any plans on adding blob support?
Running the dist files requires setImmediate
, so it should not be marked as a dev-only dependency.
Looks like doesn't support db.transaction(["customers"], "readwrite")
? Only support db.transaction("customers", "readwrite")
?
This code is from MDN
version: [email protected]
Hi there! First, the library is awesome, great work :)
And now my question, is it possible to reset the db after each test in a way that auto-increment ids begin from 1?
The problem that I have is that I can't write independent tests for stores with auto-incrementing ids (I'm testing those autogenerated values as well, I can explain why I do this if you're interested). For example, in one test I push several items to a store and then ids are created like 1,2,3 and then I continue with another test which pushes to that same store (previously cleared of course) but ids begin from 4 (unless this test is run in isolation).
Can you suggest on how to completely reset the db so it affects this global counter or is there any kind of workaround for this issue?
Thanks!
When build in release-optimised mode, the FDB Cursor no longer works, due to erroneous runtime type-checking.
The outcome is that a null or undefined value is returned from any Cursor Read operation. No exception is thrown, so it's difficult to debug. Everything works just fine in a normal development environment. To replicate the issue something like the Webpack Terser plugin must be used to optimise the javascript output.
Summary: code similar to the following occurs 3 times in FDBCursor.cs. In an optimised build, the name of the constructor will be different, causing these conditions to never be fulfilled.
if ( !this._keyOnly && this.constructor.name === "FDBCursorWithValue" ) {
Resolution: Since both FDBCursor and the derived class FDBCursorWithValue already implement a toString() function, a quick fix is as follows:
if ( !this._keyOnly && this.toString() === "[object IDBCursorWithValue]" ) {
The blanket include of core-js is causing some issues in our project. Specifically there is a package in core-js that introduces numbers as iterators (e.g. you can write for (i of 20) {
). React tends to blow up with this, and if you return a number from a render function you get long strings of zeros for some reason. So some of our tests now look like Expected 10, got "000000000000000000000000000000000000000000000000000000000000000"
Suggest that the project be updated to include specific polyfills that are needed as opposed to a blanket include, or at least only enabling stage 2 features
Is there any options for permanent storages? If not, does FIDB have any plans for supporting this feature? Something like sync data with the real IndexedDB?
Some of the shrinkwrapped dependencies are quite old and are causing deprecation warnings when installing. In particular, graceful-fs
and minimatch
, which are dependencies of mocha
.
I've verified that all tests pass if mocha is upgraded to the latest release ([email protected]).
With jsdom
environment in jest@27
acting on a transaction after allowing other code to run (per await
) throws errors.
This differs from executing the same calls in a node
environment.
Maybe this is related to #64
Hi! Thanks for fakeIndexedDB, we're using it in kinto.js for testing and verifying that everything works the same way as it would in a browser.
I'm investigating Kinto/kinto.js#690, which demonstrates our code failing with fakeIndexedDB 2.0.2. Previously we were using fakeIndexedDB 1.0.12 and everything seemed to work OK.
I boiled down this minimal example that demonstrates the problem:
"use strict";
import fakeIndexedDB from "fake-indexeddb";
import { expect } from "chai";
describe("FakeIndexedDB", () => {
let db;
beforeEach((done) => {
const request = fakeIndexedDB.open("test" + Math.random());
request.onupgradeneeded = (e) => {
const db2 = e.target.result;
const collStore = db2.createObjectStore("store", {keyPath: "id"});
// Primary key (generated by IdSchema, UUID by default)
collStore.createIndex("id", "id", { unique: true });
// Local record status ("synced", "created", "updated", "deleted")
collStore.createIndex("_status", "_status", {unique: false});
collStore.add({id: "5", _status: "created"});
collStore.add({id: "0", _status: "created"});
};
request.onsuccess = (e) => {
db = e.target.result;
done();
};
request.onerror = (e) => {
done(e.target.error);
};
});
it("should be awesome", (done) => {
const results = [];
const finish = () => {
expect(results).lengthOf(2);
done();
};
const txn = db.transaction(["store"]);
const store = txn.objectStore("store");
const request = store.index("_status").openCursor();
request.onsuccess = (event) => {
const cursor = event.target.result;
if (!cursor) {
finish();
return;
}
const { key, value } = cursor;
if (key > "created") {
finish();
return;
}
if (key === "created") {
results.push(value);
cursor.continue();
}
if (key < "created") {
cursor.continue("created");
}
};
request.onerror = (e) => {
done(e.target.error);
};
});
});
I can construct a complete git repo with this code (and the necessary package.json stuff) if that would make it easier to try out. The behavior is that only the first record ({id: "5", _status: "created"}
) is produced by the cursor. The second ({id: "0", _status: "created"}
) never gets produced. Changing the second record's ID to "6"
or something greater than the first one causes the example to succeed.
I'm not really an expert with IndexedDB so I wasn't sure whether the problem is in my example or in the library. I tried tracing through the fakeIndexedDB code and it seems like the second record gets added later in the index than the first one, but it seems like _iterate
method skips the second record because it compares as less than the first one. Does that seem right to you?
Thanks for any help!
Hello!
I've got this error during testing a plugin for Dexie.js with FakeIndexedDB, more details about a context here: dexie/Dexie.js#1053
Error output:
RangeError: Maximum call stack size exceeded
at FDBTransaction.Object.<anonymous>.FDBTransaction._start
If I comment out the following lines in FDBTransaction.js
, it works (or seems like working):
// On to the next one
// if (this._requests.length > 0) {
// this._start();
// }
// else {
// Give it another chance for new handlers to be set before finishing
setImmediate(this._start.bind(this));
// }
I'm trying to refresh the DB before each test to have my tests completely isolated. I imported fake-indexeddb/auto
in the top of test file and use beforeEach
to call FDBFactory
inside (like in README example). Is it a correct way to refresh db ?
import 'fake-indexeddb/auto';
import FDBFactory from 'fake-indexeddb/lib/FDBFactory';
beforeEach(() => {
new FDBFactory();
});
I know, we all love supporting PhantomJS and it's terrible old buggy APIs, but our main reason for using fakeIndexedDB is to work around Phantom's horrifically buggy indexedDB implementation 😞
TypeError: undefined is not a constructor (evaluating 'Array.from(rawDatabase.rawObjectStores.keys())')
Looks like it's caused by
fakeIndexedDB/src/FDBDatabase.ts
Line 77 in 8547e57
Array.from
not existingThe "close"
event on an IDBDatabase only fires when the database closes for an abnormal reason, as described in the spec. (An abnormal reason would be something like the user clearing their browser data while the IDB connection is open.)
For unit testing, it would be neat if fakeIndexedDB had an API for simulating this abnormal close event. Presumably it would just be a matter of running db.close()
plus firing an event. (I would do this in userland, but it seems that fakeIndexedDB throws an error if you try to call db.dispatchEvent(new CustomEvent('close'))
.
I'm having some problems since the upgrade to 3.1.4; Jest tests that previously worked fine are now timing out. I did some digging, and if I understand correctly:
setTimeout
instead of setImmediate
.I experimented with using Jest's fake timers to speed up my tests, but they don't appear to be a great solution: because IndexedDB operations result in fake-indexeddb timers which resolve promises which may then trigger additional IndexedDB operations (etc.), there's an awkward dance of trying to figure out which timers and promises are outstanding after each operation so that they can all be properly resolved. (See the linked test case for an example of what this looks like - this gets even harder when using third-party code that may do multiple IndexedDB operations in a row. Maybe there's an easier way to do this in Jest? If so please let me know.)
We ran into this using the idb-keyval package, which, in order to keep a simple API, relies on lots of small operations.
Test case: https://github.com/joshkel/fake-indexeddb-settimeout-test-case
Timings for the "create and read a bunch of values" from that project in fake-indexeddb 3.1.4:
In fake-indexeddb 3.1.3:
I just installed and wonder why all the devDependencies got installed.
npm --version
3.10.2
npm i --save-dev fake-indexeddb ⏎ master ✱ ◼
npm WARN deprecated [email protected]: graceful-fs v3.0.0 and before will fail on node releases >= v7.0. Please update to graceful-fs@^4.0.0 as soon as possible. Use 'npm ls graceful-fs' to find it in the tree.
npm WARN deprecated [email protected]: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
> [email protected] install /Users/bsr/test/node_modules/fake-indexeddb/node_modules/node-qunit-phantomjs/node_modules/phantomjs-prebuilt
> node install.js
PhantomJS not found on PATH
Downloading https://github.com/Medium/phantomjs/releases/download/v2.1.1//phantomjs-2.1.1-macosx.zip
Saving to /var/folders/fc/k8b9ds214nv4lqv2x4395khc0000gn/T/phantomjs/phantomjs-2.1.1-macosx.zip
Receiving...
[===================================-----] 88%
Received 16746K total.
Extracting zip contents
Removing /Users/bsr/test/node_modules/fake-indexeddb/node_modules/node-qunit-phantomjs/node_modules/phantomjs-prebuilt/lib/phantom
Copying extracted folder /var/folders/fc/k8b9ds214nv4lqv2x4395khc0000gn/T/phantomjs/phantomjs-2.1.1-macosx.zip-extract-1468612535803/phantomjs-2.1.1-macosx -> /Users/bsr/test/node_modules/fake-indexeddb/node_modules/node-qunit-phantomjs/node_modules/phantomjs-prebuilt/lib/phantom
Writing location.js file
Done. Phantomjs binary available at /Users/bsr/test/node_modules/fake-indexeddb/node_modules/node-qunit-phantomjs/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
[email protected] /Users/bsr/test
ls node_modules/fake-indexeddb/node_modules master ✱ ◼
array.prototype.find browserify mocha qunitjs setimmediate
array.prototype.findindex eslint node-qunit-phantomjs realistic-structured-clone
Started running into failing tests when I upgraded fakeIDB this morning:
beforeEach(function(done) {
let dbReq = fakeIndexedDB.open('storage', 2);
dbReq.onerror = (e) => {
console.error(e);
done(dbReq.error);
};
dbReq.onupgradeneeded = (e) => {
dbReq.result.createObjectStore('test', { keyPath: 'key' });
};
dbReq.onsuccess = (e) => {
let txn = dbReq.result.transaction(['test'], 'readwrite');
let os = txn.objectStore('test');
let p = os.put({key: 'name', value: 'test'}); // <-- FAILING
p.onsuccess = () => {
dbReq.result.close();
done();
};
};
});
The put
call is now throwing DataCloneError: The data being stored could not be cloned by the internal structured cloning algorithm.
First of all, fantastic work on this! It's been a great help to me recently, letting me develop in the node environment without having to use something overpowered like puppeteer.
That being said, I think I've found a bug.
We first crease a simple database; it has one object store with an indexed attribute indexed_attr
. We then, in two separate transactions, place an object { indexed_attr: 'xxx' }
into the database. As we'd expect, since indexed_attr
is unique, the first transaction succeeds and the second fails. However, they both seem to commit: we find that the resultant database contains two copies of the object, not just one.
I do not know if this is spec-compliant behaviour or not, but it differs from Chrome, which you note has a 99% pass rate on the W3C IndexedDB test suite. In Chrome, the resultant db has only one copy of the object.
require('fake-indexeddb/auto');
function setup() {
/* Create database, object store, and unique index */
return new Promise(resolve => {
indexedDB.deleteDatabase('mydb').onsuccess = event => {
const openreq = indexedDB.open('mydb');
openreq.onupgradeneeded = event => {
const db = event.target.result;
const store = db.createObjectStore('mystore', { autoIncrement: true });
store.createIndex('myindex', 'indexed_attr', { unique: true });
};
openreq.onsuccess = _event => resolve();
};
});
}
const my_object = { indexed_attr: 'xxx' };
function put() {
/* Put `my_object` into the db. */
return new Promise(resolve => {
indexedDB.open('mydb').onsuccess = event => {
const db = event.target.result;
const tx = db.transaction(['mystore'], 'readwrite');
const store = tx.objectStore('mystore');
const addreq = store.add(my_object);
addreq.onsuccess = _event => resolve('succ');
addreq.onerror = _event => resolve('fail');
};
});
}
function read() {
/* Return list of all objects in the db */
return new Promise(resolve => {
indexedDB.open('mydb').onsuccess = event => {
const db = event.target.result;
const tx = db.transaction(['mystore'], 'readonly');
const store = tx.objectStore('mystore');
store.getAll().onsuccess = event => resolve(event.target.result);
};
});
}
await setup();
console.log(await put()); // returns 'succ', as expected
console.log(await put()); // returns 'fail', as expected
console.log(await read()); // returns [my_object, my_object] instead of just [my_object]
I'm using fakeIndexedDB for testing. I am unable to close db connections after calling db.close()
. I set an onClose
event listener to indicate when the db closes but it never fires. I need to upgrade the db in one of my tests, I can't do this with other connections open. So, I set the db to close onversionchange
but alas, the db doesn't close and upgrade request is blocked.
For our indexedDB usage in app, we have written graceful error handling for indexedDB failure, ex - transaction.onerror, .onerror handler for indexedDB.open() function, .error for read write db. These error handlers are not getting covered while using fake-indexedDB. Can you suggest how to cover them, we are using React testing library with Jest.
Just so you know, installing 1.0.4 currently fails because of a foreign dependency: phantomjs-prebuilt
.
$ npm install fake-indexeddb --save-dev
npm WARN package.json [email protected] No description
npm WARN package.json [email protected] No repository field.
npm WARN package.json [email protected] No README data
npm WARN deprecated [email protected]: graceful-fs version 3 and before will fail on newer node releases. Please update to graceful-fs@^4.0.0 as soon as possible.
> [email protected] install /home/niko/tmp/node_modules/fake-indexeddb/node_modules/phantomjs-prebuilt
> node install.js
module.js:339
throw err;
^
Error: Cannot find module './util/assign'
at Function.Module._resolveFilename (module.js:337:15)
at Function.Module._load (module.js:287:25)
at Module.require (module.js:366:17)
at require (module.js:385:17)
at Object.<anonymous> (/home/niko/tmp/node_modules/fake-indexeddb/node_modules/fs-extra/lib/index.js:1:76)
at Module._compile (module.js:435:26)
at Object.Module._extensions..js (module.js:442:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:311:12)
at Module.require (module.js:366:17)
npm ERR! Linux 4.2.0-34-generic
npm ERR! argv "/home/niko/.nvm/versions/node/v4.2.1/bin/node" "/home/niko/.nvm/versions/node/v4.2.1/bin/npm" "install" "fake-indexeddb" "--save-dev"
npm ERR! node v4.2.1
npm ERR! npm v2.14.7
npm ERR! code ELIFECYCLE
npm ERR! [email protected] install: `node install.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] install script 'node install.js'.
npm ERR! This is most likely a problem with the phantomjs-prebuilt package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! node install.js
npm ERR! You can get their info via:
npm ERR! npm owner ls phantomjs-prebuilt
npm ERR! There is likely additional logging output above.
npm ERR! Please include the following file with any support request:
npm ERR! /home/niko/tmp/npm-debug.log
I'm filing this here just so you know about the issue and can track it until it's fixed.
(As seen in #46)
Create a database and in the onsuccess
handler, .close()
and re-open the db. The re-opening will succeed, suggesting that the db properly closed. Despite this, the onclose
handler will not have run.
require('fake-indexeddb/auto');
indexedDB.deleteDatabase('mydb').onsuccess = event => {
const openreq = indexedDB.open('mydb');
openreq.onsuccess = event => {
const db_connection = event.target.result;
db_connection.onclose = () => console.log('closed');
db_connection.close();
const reopenreq = indexedDB.open('mydb');
reopenreq.onsuccess = () => console.log('db re-opened');
}
};
// 'closed' does not get logged but 'db re-opened' does
FakeDOMStringList seems to be extending array, but a number of array methods aren't available. Such that tests can pass and then result in failure in production.
I had a migration that executed transaction.objectStoreNames.includes('...')
and didn't cause any issues, but then failed in production as should have been transaction.objectStoreNames.contains('...')
. Didn't pick up on it with types due to a bug in idb
I'm using fakeIndexedDB in my unit tests. It would be great to make one method call and delete all object stores. This would be used after each test.
Reproduction:
import indexedDB from "fake-indexeddb";
export const STORE = "store";
export const checkForGarbage = () => {
const dbReq = indexedDB.open("db", 1);
dbReq.addEventListener("upgradeneeded", (ev) => {
ev.target.result.createObjectStore(STORE, { autoIncrement: true });
});
dbReq.addEventListener("success", (ev) => {
const db = ev.target.result
const tx = db.transaction([STORE], "readwrite");
const complete = () => {
tx.removeEventListener('complete', complete);
};
tx.addEventListener('complete', complete);
const store = tx.objectStore(STORE);
store.get(0).addEventListener("success", () => console.log("Done!"));
});
};
for (let i = 0; i < 5; i++) {
checkForGarbage();
}
The expected result is to print "Done!" five times. However, only two are produced. Commenting out the body of complete
(i.e., removing the removeEventListener
call) produces the desired result.
demo repo: https://github.com/ZYinMD/jest-indexeddb-undefined-demo
I encountered an "indexeddb is not defined" issue in jest with a create-react-app project. I tried using this package to fix it, but had no luck. Can someone shed some light?
// a.js
import { openDB } from "idb";
export const dbConnection = openDB("myDB", 1);
// b.js
import { dbConnection } from "./a";
export const foo = 1;
export function doSomethingWithDB() {
dbConnection.then((db) => {
//...
});
}
// c.js
import { foo } from "./b";
export const bar = foo + 1;
// d.js
import { bar } from "./c";
export function notEvenCalled() {
return sum(bar, 100);
}
export function sum(a, b) {
return a + b;
}
// d.test.js
import { sum } from "./d";
test("why", () => {
expect(sum(1, 1)).toBe(2); // fail!! indexeddb is not defined.
});
I think it's because jest traverses all files that are connected to the test file via import/export, and also all files connected to those files, and so on.
Is there an easy fix for things like this?
https://github.com/dumbmatter/fakeIndexedDB/blob/master/src/lib/FakeEvent.ts#L28
https://developer.mozilla.org/en-US/docs/Web/API/Event/timeStamp
// Wrong
public timestamp = Date.now();
// Right
public timeStamp = Date.now();
This mock is awesome! Thanks
fake-indexeddb currently throws exceptions that are Error
objects, but IndexedDB throws DOMException
s.
Of course, this is a little sticky 'cause fake-indexeddb is on Node, but there seems to be a well-known DOMException
polyfill that could be used.
Seems like an easy change: just modify src/lib/errors.ts
, right?. I can try to make a PR with your go-ahead.
I see an InvalidStateError from nodejs. When I run the same thing in chrome I get a TransactionInactiveError.
WEB Reports: DOMException: Failed to execute 'add' on 'IDBObjectStore': The transaction is not active.
Node and fake index db (matching InvalidStateError from the spec): An operation was called on an object on which it is not allowed or at a time when it is not allowed. Also occurs if a request is made on a source object that has been deleted or removed. Use TransactionInactiveError or ReadOnlyError when possible, as they are more specific variations of InvalidStateError.'
I think FIDB should use self
as the global object instead of window
. Otherwise, it won't work within web worker contexts.
We are using fake-indexedDB for our app. We are unable to cover the openCursor().onSuccess scenario, though we are writing test case the same way it is displayed in your npmjs.com page. Can you please suggest how to cover these lines. We are using React testing library and Jest.
Hi @dumbmatter.
Thanks for such an awesome module! I'm currently using this for some tests in a library of mine that I'm transitioning from JavaScript to Typescript. I was wondering if it would be possible to push a new release out with some Typescript declaration files. It shouldn't be too hard to do since you're using Typescript yourself. Just setting declaration: true
and removing allowJs: true
in tsconfig.json
should do the trick.
I'm using a fairly typical mocha/chai testing strategy which uses Typescript code instead of plain JS). I would like to test my Dexie implementation but when I try to open a connection to the database I get the following error:
indexedDB API not found. If using IE10+, make sure to run your code on a server URL (not locally). If using old Safari versions, make sure to include indexedDB polyfill.
To give context, in the unit test file I am adding the following to the top of the file:
import 'fake-indexeddb/auto';
this import reports no issues but the error above persists. I started to wonder if maybe I needed to adjust my tsconfig
file but typically the following params do the trick:
{
// ...
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
and those are both in my configuration.
Now while I'm quite comfortable with a lot of Typescript/Javascript things along with named portability between the browser and node, I must say I don't always catch everything when it comes to "global scope" and I'm not yet entirely sure what the /auto
script is doing (will be looking soon I guess).
Anyway thought I'd try:
import indexedDB from "fake-indexeddb";
const window: IDictionary = { indexedDB };
This does of course complain about fake-indexeddb
not being typed but I got around that by adding a simple declaration file:
declare module "fake-indexeddb";
This removes the Typescript error but as I was suspecting the underlying error is still thrown. As I finish writing this Issue, I am starting to suspect that I need to dig into Dexie a bit more to diagnose the problem but if you've had an exposure to using your library with Dexie I would sure love some help and for that matter I think a small part of the README might talk to integration strategies for the various IDB wrapper libraries (of which Dexie is just one).
Hi, thank you so much for making fakeIndexedDB
! I love using it in my tests; it's a marvelous piece of software. 🙂
I'm running into an issue where fakeIndexedDB
is taking a very long time to insert data. Here is a test where I am inserting 1000 objects, each with 100 multiEntry
keys.
On my Dell XPS 13 (i7 processor) running Ubuntu 20.04 and Node v12.16.3, this takes 35.5 seconds to complete. Unfortunately this means that my tests time out, and it's not feasible for me to create/destroy the database between each test.
In contrast, this same code takes 595ms in Chrome, 774ms in Firefox, and 1773ms in GNOME Web (WebKit, like Safari).
Looking at a Node cpuprofile, it appears that the main issue is that FDBTransaction._start queues up many recursive calls (if you insert 10,000 objects it will throw a stack overflow error), and each of those are spending time in Index.storeRecord
and RecordStore.add
. (Maybe switching to a non-recursive pattern would help?)
Attached is the trace file, which you can load into Chrome Dev Tools in the "JavaScript Profiler" section. If you'd like to profile this yourself, you can run node --inspect-brk index.js
, then in chrome:inspect
open the Node debugger, then in Profiler click "start".
The code below fails when using fake-indexeddb, because at least one of the Dexie bulkGet operations never resolves:
import "fake-indexeddb/auto";
import Dexie from "dexie";
const db = new Dexie("test1");
db.version(1).stores({ table1: "++id" });
const table1 = db.table("table1");
const result = await Promise.all([
table1.bulkGet([1]),
table1.bulkGet([]),
table1.bulkGet([3])
]);
I'm guessing this is a fake-indexeddb issue—or an interaction between it and Dexie—because the same Dexie code seems to work fine with Chrome and Firefox indexedDB.
Sandbox demonstrating problem: https://codesandbox.io/s/fakeidb-simultaneous-bulkget-w2j55?file=/src/index.test.ts. You should see tests failing with "Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout."
Same code running successfully with browser indexedDB: https://codesandbox.io/s/dexie-simultaneous-bulkget-test-qemz8?file=/src/index.test.ts
The bulkGet([])
with an empty key list is necessary to reproduce, and it must be preceded and followed by non-empty bulkGets. (Weird, right?) It doesn't seem to matter whether the bulkGets refer to records actually in the store or not.
Versions:
fake-indexeddb v3.1.1 (also 3.0.2 and 3.0.0; haven't tried others)
Dexie v3.0.1 (current latest; haven't tried others)
And in case they're relevant, I've reproed on:
node v10 or v12
typescript 3.9.7 targeting es2018, es2019, or es2020
jest v25 or v26
(I've included core-js in the sandbox, but it doesn't seem to matter; I believe TS is providing any missing core functions.)
According to the spec, db.objectStorenames
should have the DOMStringList
type that resembles Array
but have the contains
method. Currently your implementation lacks of this method.
Hi, I met with below error while using getAll
. Below is part of the code.
async all() {
const db = await connect();
return db.sess.getAll();
}
in my test
const values = await sess.all();
assert(values.length === 3);
1) Session all, find:
TypeError: Cannot read property 'apply' of undefined
at node_modules/backboard/dist/lib/wrap-request.js:24:96
at new Promise (node_modules/babel-polyfill/node_modules/core-js/modules/es6.promise.js:193:7)
at exports.default (node_modules/backboard/dist/lib/wrap-request.js:18:12)
at ObjectStore.(anonymous function) [as getAll] (node_modules/backboard/dist/lib/ObjectStore.js:112:38)
at Session._callee3$ (src/frontend/bundles/layout/services/Session.js:379:59)
at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
at GeneratorFunctionPrototype.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:336:22)
at GeneratorFunctionPrototype.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:95:21)
at step (Session.js:6:37)
at Session.js:6:37
at run (node_modules/babel-polyfill/node_modules/core-js/modules/es6.promise.js:89:22)
at node_modules/babel-polyfill/node_modules/core-js/modules/es6.promise.js:102:28
at flush (node_modules/babel-polyfill/node_modules/core-js/modules/_microtask.js:18:9)
at _combinedTickCallback (internal/process/next_tick.js:67:7)
at process._tickCallback (internal/process/next_tick.js:98:9)
I tried with bluebird
, still the same error
1) Session all, find:
TypeError: Cannot read property 'apply' of undefined
at node_modules/backboard/dist/lib/wrap-request.js:24:96
at Promise._execute (node_modules/bluebird/js/release/debuggability.js:272:9)
at Promise._resolveFromExecutor (node_modules/bluebird/js/release/promise.js:475:18)
at new Promise (node_modules/bluebird/js/release/promise.js:77:14)
at exports.default (node_modules/backboard/dist/lib/wrap-request.js:18:12)
at ObjectStore.(anonymous function) [as getAll] (node_modules/backboard/dist/lib/ObjectStore.js:112:38)
at Session._callee3$ (Session.js:21:15)
at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
at GeneratorFunctionPrototype.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:336:22)
at GeneratorFunctionPrototype.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:95:21)
at step (Session.js:1:1)
at Session.js:1:1
at run (node_modules/babel-polyfill/node_modules/core-js/modules/es6.promise.js:89:22)
at node_modules/babel-polyfill/node_modules/core-js/modules/es6.promise.js:102:28
at flush (node_modules/babel-polyfill/node_modules/core-js/modules/_microtask.js:18:9)
at _combinedTickCallback (internal/process/next_tick.js:67:7)
at process._tickCallback (internal/process/next_tick.js:98:9)
It works fine in Chrome Canary
_recordStoreFactory = recordStoreFactory(mode, resolve, reject)[method]
getAll() { [native code] }
I tried using with latest dexie inside a CRA, but it doesn't seem to work:
import Dexie, { DexieOptions } from "dexie";
import indexedDB from "fake-indexeddb";
import IDBKeyRange from "fake-indexeddb/lib/FDBKeyRange";
interface IMessage {
id?: number;
message: string;
}
class TestDatabase extends Dexie {
messages: Dexie.Table<IMessage, number>;
constructor(options?: DexieOptions) {
super("Database", options);
this.version(1).stores({
messages: "++id",
});
this.messages = this.table("messages");
}
}
test("should pass", async () => {
const db = new TestDatabase({
indexedDB: indexedDB,
IDBKeyRange: IDBKeyRange,
});
const result = await db.messages.put({ message: "a message" });
console.warn(result);
expect(result).not.toBeUndefined();
});
Fails with:
FAIL src/db.test.ts
✕ should pass (22 ms)
● should pass
MissingAPIError: IndexedDB API missing. Please visit https://tinyurl.com/y2uuvskb
at cmp (node_modules/dexie/src/functions/cmp.ts:9:25)
at Array.forEach (<anonymous>)
From previous:
From previous:
Here is a STR:
var indexedDB = require("fake-indexeddb");
function openDb() {
return new Promise((resolve, reject) => {
var request = indexedDB.open("test", 3);
request.onupgradeneeded = function () {
var db = request.result;
var store = db.createObjectStore("books", {keyPath: "isbn"});
}
request.onsuccess = function (event) {
var db = event.target.result;
resolve(db);
};
});
}
openDb();
openDb().then(db => {
db.transaction("books");
console.log('All good!');
});
You can clone the repository here: https://github.com/julienw/fake-indexeddb-bug
You can also see it live (open the Devtools' console to see it, as the page itself is blank):
In case you weren't aware, IndexedDBShim can also allow Node usage in a manner that creates databases in memory online (or to temporary file): See memoryDatabase
under https://github.com/axemclion/IndexedDBShim/#user-content-configuration-options
We also have a high test passing rate, with about 94% of W3C test files completely passing (96% if excluding exception order tests which have issues due to transaction timing limits from our avoiding synchronous operations).
It'd be great to have extra hands working together on what seems to be similar work or at least hear about any disadvantages you may have found with our project relative to yours...
Best wishes!
Hi! First, thanks for this lib, I have built a web-app that relies heavily on IndexedDB for functionality and it doesn't seem like Google would index the site properly when using the real IndexedDB, but I've had better success when using the fake lib.
My problem was with compound keys, specifically when the object I try to add did not have one or both of the key values set. To get around the problem I modified extractKey.js to handle undefined values also.
Example indata:
keyPath: ['parent', 'id']
value: { some_other_values: 1 }
In this case validateKey throws an exception since the keys have undefined value.
Here is my workaround:
function extractKey(keyPath, value) {
if (Array.isArray(keyPath)) {
var result = [];
keyPath.forEach(function (item) {
// This doesn't make sense to me based on the spec, but it is needed to pass the W3C KeyPath tests (see same comment in validateKey)
if (item !== undefined && item !== null && typeof item !== 'string' && item.toString) {
item = item.toString();
}
var key = extractKey(item, value);
if(key === undefined || key === null) {
result.push(undefined);
} else {
result.push(structuredClone(validateKey(key)));
}
});
Now, this seems to work for my code at least, but I don't know if this will break specs or introduce other problems.
I would love to have an option to be able to delete all databases and totally reset the state of the indexedDB mock.
My use case:
Because of this situation, I'm note able to make the solution found by @jcalfee here.
fakeIndexedDB.deleteDatabase("unknown name") // can't because I don't know the name
And there doesn't seem to be a standard way to do a wipe of all dbs in indexedDB.
So for the sake of testing (especially if testing stuff using 3rd party libraries that interact with indexedDB
), it would be great if a special method could be added to totally wipe and reset the state of indexedDB
.
Hey,
Recently, the jest folks removed setImmediate and clearImmediate from the jsdom environment (they had it before incorrectly). This is a good thing.
fakeIndexedDB uses the setimmediate polyfill in case setImmediate
isn't present. This is all good.
But... there's a problem when we start using the fake timers. Indeed, the polyfill seems to rely on setTimeout (although I'm not 100% sure why, from their code it should be process.nextTick
... which isn't a macrotask either in recent node versions actually), and setTimeout
is using the fake timeouts from jest so never ends.
The testing-library folks try to detect if we're in jest with fake timers, and in that case they run some code using the real timers:
https://github.com/testing-library/dom-testing-library/blob/c273ed518cf783591692be8c0c5e5a34feafd4bb/src/helpers.js#L7-L12
Could that be a solution for fakeIndexedDB? Should you move to process.nextTick
anyway?
Thanks for this wonderful tool!
FDBCursor.update breaks if fed a compound key in certain situations. This particular line is causing the issue:
https://github.com/dumbmatter/fakeIndexedDB/blob/master/lib/FDBCursor.js#L227
I'm guessing you want Array.equals() when given a compound key.
Hey! Thanks for this lib!
I'm trying to use fakeIndexedDB in node environment and have the following code:
import FDBCursor = require("fake-indexeddb/lib/FDBCursor");
Just trying to import fdbcursor but it throws error:
TypeError: Object prototype may only be an Object or null: undefined
at setPrototypeOf (<anonymous>)
at extendStatics (node_modules/fake-indexeddb/build/FDBCursorWithValue.js:7:16)
at __extends (node_modules/fake-indexeddb/build/FDBCursorWithValue.js:10:9)
at node_modules/fake-indexeddb/build/FDBCursorWithValue.js:18:5
at Object.<anonymous> (node_modules/fake-indexeddb/build/FDBCursorWithValue.js:28:2)
I found that it is because of circular import.
What should i do?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.