diff --git a/src/js/database/BackupDatabase.js b/src/js/database/BackupDatabase.js index 8d09658..1bd7a38 100644 --- a/src/js/database/BackupDatabase.js +++ b/src/js/database/BackupDatabase.js @@ -21,9 +21,7 @@ this.db = null; }; - ns.BackupDatabase.drop = function () { - return _requestPromise(window.indexedDB.deleteDatabase(DB_NAME)); - }; + ns.BackupDatabase.DB_NAME = DB_NAME; /** * Open and initialize the database. diff --git a/src/js/database/PiskelDatabase.js b/src/js/database/PiskelDatabase.js index 8aef33f..3832b4b 100644 --- a/src/js/database/PiskelDatabase.js +++ b/src/js/database/PiskelDatabase.js @@ -20,27 +20,18 @@ this.db = null; }; + ns.PiskelDatabase.DB_NAME = DB_NAME; + ns.PiskelDatabase.prototype.init = function () { - this.initDeferred_ = Q.defer(); - var request = window.indexedDB.open(DB_NAME, DB_VERSION); - - request.onerror = this.onRequestError_.bind(this); - request.onsuccess = this.onRequestSuccess_.bind(this); request.onupgradeneeded = this.onUpgradeNeeded_.bind(this); - return this.initDeferred_.promise; - }; - - ns.PiskelDatabase.prototype.onRequestError_ = function (event) { - console.log('Failed to initialize IndexedDB, local browser saves will be unavailable.'); - this.initDeferred_.reject(); - }; - - ns.PiskelDatabase.prototype.onRequestSuccess_ = function (event) { - console.log('Successfully initialized IndexedDB, local browser saves are available.'); - this.db = event.target.result; - this.initDeferred_.resolve(this.db); + return _requestPromise(request).then(function (event) { + this.db = event.target.result; + return this.db; + }.bind(this)).catch(function (e) { + console.log('Failed to initialize IndexedDB, local browser saves will be unavailable.'); + }); }; ns.PiskelDatabase.prototype.onUpgradeNeeded_ = function (event) { @@ -50,7 +41,7 @@ // Create an object store "piskels" with the autoIncrement flag set as true. var objectStore = this.db.createObjectStore('piskels', { keyPath : 'name' }); objectStore.transaction.oncomplete = function(event) { - pskl.service.storage.migrate.MigrateLocalStorageToIndexed.migrate(this.db); + pskl.database.migrate.MigrateLocalStorageToIndexedDb.migrate(this.db); }.bind(this); }; @@ -64,7 +55,9 @@ */ ns.PiskelDatabase.prototype.get = function (name) { var objectStore = this.openObjectStore_(); - return _requestPromise(objectStore.get(name)); + return _requestPromise(objectStore.get(name)).then(function (event) { + return event.target.result; + }); }; /** diff --git a/src/js/service/storage/migrate/MigrateLocalStorageToIndexedDb.js b/src/js/database/migrate/MigrateLocalStorageToIndexedDb.js similarity index 93% rename from src/js/service/storage/migrate/MigrateLocalStorageToIndexedDb.js rename to src/js/database/migrate/MigrateLocalStorageToIndexedDb.js index c0c6f31..aa6ac9b 100644 --- a/src/js/service/storage/migrate/MigrateLocalStorageToIndexedDb.js +++ b/src/js/database/migrate/MigrateLocalStorageToIndexedDb.js @@ -1,5 +1,5 @@ (function () { - var ns = $.namespace('pskl.service.storage.migrate'); + var ns = $.namespace('pskl.database.migrate'); // Simple migration helper to move local storage saves to indexed db. ns.MigrateLocalStorageToIndexedDb = {}; @@ -52,8 +52,7 @@ }; ns.MigrateLocalStorageToIndexedDb.save_ = function (piskelDatabase, piskelData) { - return piskelDatabase.get(piskelData.name).then(function (event) { - var data = event.target.result; + return piskelDatabase.get(piskelData.name).then(function (data) { if (typeof data !== 'undefined') { return piskelDatabase.update(piskelData.name, piskelData.description, piskelData.date, piskelData.serialized); } else { diff --git a/src/js/service/storage/IndexedDbStorageService.js b/src/js/service/storage/IndexedDbStorageService.js index 6878641..b4d5122 100644 --- a/src/js/service/storage/IndexedDbStorageService.js +++ b/src/js/service/storage/IndexedDbStorageService.js @@ -20,9 +20,8 @@ }; ns.IndexedDbStorageService.prototype.save_ = function (name, description, date, serialized) { - return this.piskelDatabase.get(name).then(function (event) { - var data = event.target.result; - if (typeof data !== 'undefined') { + return this.piskelDatabase.get(name).then(function (piskelData) { + if (typeof piskelData !== 'undefined') { return this.piskelDatabase.update(name, description, date, serialized); } else { return this.piskelDatabase.create(name, description, date, serialized); @@ -31,10 +30,9 @@ }; ns.IndexedDbStorageService.prototype.load = function (name) { - this.piskelDatabase.get(name).then(function (event) { - var data = event.target.result; - if (typeof data !== 'undefined') { - var serialized = data.serialized; + this.piskelDatabase.get(name).then(function (piskelData) { + if (typeof piskelData !== 'undefined') { + var serialized = piskelData.serialized; pskl.utils.serialization.Deserializer.deserialize( JSON.parse(serialized), function (piskel) { diff --git a/src/piskel-script-list.js b/src/piskel-script-list.js index c096390..1a6b226 100644 --- a/src/piskel-script-list.js +++ b/src/piskel-script-list.js @@ -82,6 +82,7 @@ // Database (IndexedDB) "js/database/BackupDatabase.js", "js/database/PiskelDatabase.js", + "js/database/migrate/MigrateLocalStorageToIndexedDb.js", // Selection "js/selection/SelectionManager.js", @@ -172,7 +173,6 @@ "js/widgets/Wizard.js", // Services - "js/service/storage/migrate/MigrateLocalStorageToIndexedDb.js", "js/service/storage/StorageService.js", "js/service/storage/FileDownloadStorageService.js", "js/service/storage/IndexedDbStorageService.js", diff --git a/test/js/database/BackupDatabaseTest.js b/test/js/database/BackupDatabaseTest.js index 6b3ad31..139e0d1 100644 --- a/test/js/database/BackupDatabaseTest.js +++ b/test/js/database/BackupDatabaseTest.js @@ -14,11 +14,11 @@ describe('BackupDatabase test', function () { }; var _checkSnapshot = function (actual, expected) { - expect(actual.session_id).toBe(actual.session_id); - expect(actual.name).toBe(actual.name); - expect(actual.description).toBe(actual.description); - expect(actual.date).toBe(actual.date); - expect(actual.serialized).toBe(actual.serialized); + expect(actual.session_id).toBe(expected.session_id); + expect(actual.name).toBe(expected.name); + expect(actual.description).toBe(expected.description); + expect(actual.date).toBe(expected.date); + expect(actual.serialized).toBe(expected.serialized); }; var _addSnapshots = function (snapshots) { @@ -37,7 +37,9 @@ describe('BackupDatabase test', function () { beforeEach(function (done) { // Drop the database before each test. - pskl.database.BackupDatabase.drop().then(done); + var dbName = pskl.database.BackupDatabase.DB_NAME; + var req = window.indexedDB.deleteDatabase(dbName); + req.onsuccess = done; }); afterEach(function () { diff --git a/test/js/database/PiskelDatabaseTest.js b/test/js/database/PiskelDatabaseTest.js new file mode 100644 index 0000000..4e45cd9 --- /dev/null +++ b/test/js/database/PiskelDatabaseTest.js @@ -0,0 +1,158 @@ +describe('PiskelDatabase test', function () { + + // Test object. + var piskelDatabase; + + var _toSnapshot = function (session_id, name, description, date, serialized) { + return { + session_id: session_id, + name: name, + description: description, + date: date, + serialized: serialized + }; + }; + + var _checkPiskel = function (actual, expected) { + expect(actual.name).toBe(expected[0]); + expect(actual.description).toBe(expected[1]); + expect(actual.date).toBe(expected[2]); + expect(actual.serialized).toBe(expected[3]); + }; + + var _addPiskels = function (piskels) { + var _add = function (index) { + var piskelData = piskels[index]; + return piskelDatabase.create.apply(piskelDatabase, piskelData) + .then(function () { + if (piskels[index + 1]) { + return _add(index + 1); + } else { + return Promise.resolve(); + } + }); + }; + + return _add(0); + }; + + beforeEach(function (done) { + // Mock the migration script. + spyOn(pskl.database.migrate.MigrateLocalStorageToIndexedDb, "migrate"); + + // Drop the database before each test. + var dbName = pskl.database.PiskelDatabase.DB_NAME; + var req = window.indexedDB.deleteDatabase(dbName); + req.onsuccess = done; + }); + + afterEach(function () { + // Close the database if it was still open. + if (piskelDatabase && piskelDatabase.db) { + piskelDatabase.db.close(); + } + }); + + it('initializes the DB and returns a promise', function (done) { + piskelDatabase = new pskl.database.PiskelDatabase(); + piskelDatabase.init().then(done); + }); + + it('can add a piskel and retrieve it', function (done) { + piskelDatabase = new pskl.database.PiskelDatabase(); + piskelDatabase.init() + .then(function (db) { + return piskelDatabase.create('name', 'desc', 0, 'serialized'); + }).then(function () { + return piskelDatabase.get('name'); + }).then(function (piskel) { + expect(piskel.name).toBe('name'); + expect(piskel.description).toBe('desc'); + expect(piskel.date).toBe(0); + expect(piskel.serialized).toBe('serialized'); + done(); + }); + }); + + it('can delete piskel by name', function (done) { + var piskels = [ + ['n1', 'd1', 10, 's1'], + ['n2', 'd2', 20, 's2'], + ['n3', 'd3', 30, 's3'], + ]; + + piskelDatabase = new pskl.database.PiskelDatabase(); + piskelDatabase.init() + .then(function (db) { + return _addPiskels(piskels); + }).then(function () { + return piskelDatabase.delete('n2'); + }).then(function () { + return piskelDatabase.get('n1'); + }).then(function (piskelData) { + _checkPiskel(piskelData, piskels[0]); + return piskelDatabase.get('n3'); + }).then(function (piskelData) { + _checkPiskel(piskelData, piskels[2]); + return piskelDatabase.get('n2'); + }).then(function (piskelData) { + expect(piskelData).toBe(undefined); + done(); + }); + }); + + it('can list piskels', function (done) { + var piskels = [ + ['n1', 'd1', 10, 's1'], + ['n2', 'd2', 20, 's2'], + ['n3', 'd3', 30, 's3'], + ]; + + piskelDatabase = new pskl.database.PiskelDatabase(); + piskelDatabase.init() + .then(function (db) { + return _addPiskels(piskels); + }).then(function () { + return piskelDatabase.list(); + }).then(function (piskels) { + expect(piskels.length).toBe(3); + piskels.forEach(function (piskelData) { + expect(piskelData.name).toMatch(/n[1-3]/); + expect(piskelData.description).toMatch(/d[1-3]/); + expect(piskelData.date).toBeDefined(); + expect(piskelData.serialized).not.toBeDefined(); + }) + done(); + }); + }); + + it('can update piskel with same name', function (done) { + var piskels = [ + ['n1', 'd1', 10, 's1'], + ['n2', 'd2', 20, 's2'], + ['n3', 'd3', 30, 's3'], + ]; + + piskelDatabase = new pskl.database.PiskelDatabase(); + piskelDatabase.init() + .then(function (db) { + return _addPiskels(piskels); + }).then(function () { + return piskelDatabase.update('n2', 'd2_updated', 40, 's2_updated'); + }).then(function (piskels) { + return piskelDatabase.list(); + }).then(function (piskels) { + expect(piskels.length).toBe(3); + var p2 = piskels.filter(function (p) { return p.name === 'n2'})[0]; + expect(p2.name).toBe('n2'); + expect(p2.description).toBe('d2_updated'); + expect(p2.date).toBe(40); + + return piskelDatabase.get('n2'); + }).then(function (piskel) { + _checkPiskel(piskel, ['n2', 'd2_updated', 40, 's2_updated']); + done(); + }); + }); + +});