I'm looking into testing an indexedDB setup using Jasmine (with Testacular in particular). Inside of an app I'm writing the database to be opened, created, removed, etc with no issue whatsoever. Now when I attempt to write a unit test to ensure data is being saved from a service correctly, I keep receiving errors, such as timeouts, and the Chrome resource panel (currently testing on Chrome 25) doesn't show the database created with the correct tables. What is a simple implementation involving testing indexedDB?
There are a few things to keep in mind when testing indexedDB.
The following is something I made to demonstrate a simple test for adding one item to the database. This should work on Chrome 24+, FF 16+, and IE10 (granted you have a version of jquery with Deferred's) without vendor prefixes. However I highly suggest using one of the aforementioned IDB wrappers / plugins. Adding more error output is likely desired here, but hopefully this helps in getting started.
describe("Database interaction", function () {
var settings = {
name: "TEST",
version: 1
};
var stores = [
{name: "store1", keyPath: "id"},
{name: "store2", keyPath: "id"},
{name: "store3", keyPath: "id"}
];
var db;
function setupDB(){
var dbRequest = window.indexedDB.open( settings.name, settings.version),
dbDfd = $.Deferred();
dbRequest.onsuccess = function( event ) {
console.log("Opened DB");
db = dbRequest.result;
dbDfd.resolve( db );
};
dbRequest.onblocked = function( event ){
console.error("DB connection blocked");
db.close();
setupDB();
};
dbRequest.onerror = function( event ){
console.error("DB connection issues");
dbDfd.reject();
};
dbRequest.onupgradeneeded = function(){
var i, cur;
db = dbRequest.result;
//Create non-existant tables
for(i=0; i < stores.length; i+=1){
cur = stores[i];
db.createObjectStore( cur.name, {keyPath: cur.keyPath, autoIncrement: true});
}
};
return dbDfd.promise();
}
beforeEach(function(){
var done = false;
runs( function(){
var delRequest = indexedDB.deleteDatabase("TEST");
delRequest.onsuccess = function( event ){
console.log("DB Deleted");
setupDB()
.then(function(db){
console.log("DB Setup with stores: ", db.objectStoreNames );
done = true;
})
};
delRequest.onerror = function(event){
console.log("DB Err: ", event );
done = true;
};
});
waitsFor( function(){ return done; }, "Database never created..", 10000 );
});
it('should add an item to store1', function(){
var done = false;
//Open a transaction with a scope of data stores and a read-write mode.
var trans = db.transaction( stores.map(function(s){return s.name;}), 'readwrite');
//"objectStore()" is an IDBTransaction method that returns an object store
//that has already been added to the scope of the transaction.
var store = trans.objectStore('store1');
var req = store.add({"id": 2, "foo":"bar"});
req.onsuccess = function(){
//Remember to not access store or trans (from above), or this.source.prototype functions as we can only access those in a new transaction
//Added an object to the store, expect result to be ID of item added
expect( this.result ).toBe( 2 );
//Some other expectations
expect( this.source.name ).toBe("store1");
expect( this.source.keyPath ).toBe("id");
expect( this.source.autoIncrement ).toBe( true );
expect( this.source.indexNames.length ).toBe( 0 );
expect( this.transaction.mode ).toBe("readwrite");
expect( this.transaction.db.name ).toBe("TEST");
done = true;
};
req.onerror = function(){
console.log("Error adding object to store");
done = true;
};
waitsFor(function(){ return done; }, "Didn't add store item", 10000 );
});
});
I've got quite a large test suite in db.js which is a IDB wrapper and it mostly relies on using the waitsFor
to do the async operations. Combining this with the beforeEach
and afterEach
are useful as they can be used to control your setup/tare down of the DB connection, check out the specs folder if you want to see some in action.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With