mirror of
https://github.com/localForage/localForage.git
synced 2025-12-08 18:26:09 +00:00
2146 lines
78 KiB
JavaScript
2146 lines
78 KiB
JavaScript
/* global after:true, afterEach:true, before:true, beforeEach:true, describe:true, expect:true, it:true, Promise:true */
|
|
var DRIVERS = [
|
|
localforage.INDEXEDDB,
|
|
localforage.WEBSQL,
|
|
localforage.LOCALSTORAGE,
|
|
localforage.MEMORY
|
|
];
|
|
|
|
var SUPPORTED_DRIVERS = DRIVERS.filter(function(driverName) {
|
|
return localforage.supports(driverName);
|
|
});
|
|
|
|
var driverApiMethods = [
|
|
'getItem',
|
|
'setItem',
|
|
'clear',
|
|
'length',
|
|
'removeItem',
|
|
'key',
|
|
'keys'
|
|
];
|
|
|
|
var indexedDB =
|
|
// eslint-disable-next-line no-use-before-define
|
|
indexedDB ||
|
|
window.indexedDB ||
|
|
window.webkitIndexedDB ||
|
|
window.mozIndexedDB ||
|
|
window.OIndexedDB ||
|
|
window.msIndexedDB;
|
|
|
|
describe('localForage API', function() {
|
|
// https://github.com/mozilla/localForage#working-on-localforage
|
|
it('has Promises available', function() {
|
|
expect(Promise).to.be.a('function');
|
|
});
|
|
});
|
|
|
|
describe('localForage', function() {
|
|
var appropriateDriver = DRIVERS.filter(function(driverName) {
|
|
return localforage.supports(driverName);
|
|
})[0];
|
|
|
|
it(
|
|
'automatically selects the most appropriate driver (' +
|
|
appropriateDriver +
|
|
')',
|
|
function() {
|
|
this.timeout(10000);
|
|
return localforage.ready().then(
|
|
function() {
|
|
expect(localforage.driver()).to.be(appropriateDriver);
|
|
},
|
|
function(error) {
|
|
expect(error).to.be.an(Error);
|
|
expect(error.message).to.be(
|
|
'No available storage method found.'
|
|
);
|
|
expect(localforage.driver()).to.be(null);
|
|
}
|
|
);
|
|
}
|
|
);
|
|
|
|
it('errors when a requested driver is not found [callback]', function(done) {
|
|
localforage.getDriver('UnknownDriver', null, function(error) {
|
|
expect(error).to.be.an(Error);
|
|
expect(error.message).to.be('Driver not found.');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('errors when a requested driver is not found [promise]', function(done) {
|
|
localforage.getDriver('UnknownDriver').then(null, function(error) {
|
|
expect(error).to.be.an(Error);
|
|
expect(error.message).to.be('Driver not found.');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('retrieves the serializer [callback]', function(done) {
|
|
localforage.getSerializer(function(serializer) {
|
|
expect(serializer).to.be.an('object');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('retrieves the serializer [promise]', function(done) {
|
|
var serializerPromise = localforage.getSerializer();
|
|
expect(serializerPromise).to.be.an('object');
|
|
expect(serializerPromise.then).to.be.a('function');
|
|
|
|
serializerPromise.then(function(serializer) {
|
|
expect(serializer).to.be.an('object');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('does not support object parameter to setDriver', function(done) {
|
|
var driverPreferedOrder = {
|
|
'0': localforage.INDEXEDDB,
|
|
'1': localforage.WEBSQL,
|
|
'2': localforage.LOCALSTORAGE,
|
|
'3': localforage.MEMORY,
|
|
length: 3
|
|
};
|
|
|
|
localforage.setDriver(driverPreferedOrder).then(null, function(error) {
|
|
expect(error).to.be.an(Error);
|
|
expect(error.message).to.be('No available storage method found.');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('skips drivers that fail to initilize', function(done) {
|
|
var failingStorageDriver = (function() {
|
|
function driverDummyMethod() {
|
|
return Promise.reject(new Error('Driver Method Failed.'));
|
|
}
|
|
|
|
return {
|
|
_driver: 'failingStorageDriver',
|
|
_initStorage: function _initStorage() {
|
|
return Promise.reject(
|
|
new Error('Driver Failed to Initialize.')
|
|
);
|
|
},
|
|
iterate: driverDummyMethod,
|
|
getItem: driverDummyMethod,
|
|
setItem: driverDummyMethod,
|
|
removeItem: driverDummyMethod,
|
|
clear: driverDummyMethod,
|
|
length: driverDummyMethod,
|
|
key: driverDummyMethod,
|
|
keys: driverDummyMethod
|
|
};
|
|
})();
|
|
|
|
var driverPreferedOrder = [
|
|
failingStorageDriver._driver,
|
|
localforage.INDEXEDDB,
|
|
localforage.WEBSQL,
|
|
localforage.LOCALSTORAGE,
|
|
localforage.MEMORY
|
|
];
|
|
|
|
localforage
|
|
.defineDriver(failingStorageDriver)
|
|
.then(function() {
|
|
return localforage.setDriver(driverPreferedOrder);
|
|
})
|
|
.then(function() {
|
|
return localforage.ready();
|
|
})
|
|
.then(function() {
|
|
expect(localforage.driver()).to.be(appropriateDriver);
|
|
done();
|
|
});
|
|
});
|
|
|
|
describe('createInstance()', function() {
|
|
var oldConsoleInfo;
|
|
|
|
before(function() {
|
|
oldConsoleInfo = console.info;
|
|
var logs = [];
|
|
console.info = function() {
|
|
console.info.logs.push({
|
|
args: arguments
|
|
});
|
|
oldConsoleInfo.apply(this, arguments);
|
|
};
|
|
console.info.logs = logs;
|
|
});
|
|
|
|
after(function() {
|
|
console.info = oldConsoleInfo;
|
|
});
|
|
|
|
it('does not log unnecessary messages', function() {
|
|
var oldLogCount = console.info.logs.length;
|
|
var localforage2 = localforage.createInstance();
|
|
var localforage3 = localforage.createInstance();
|
|
|
|
return Promise.all([
|
|
localforage.ready(),
|
|
localforage2.ready(),
|
|
localforage3.ready()
|
|
]).then(function() {
|
|
expect(console.info.logs.length).to.be(oldLogCount);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
SUPPORTED_DRIVERS.forEach(function(driverName) {
|
|
describe(driverName + ' driver', function() {
|
|
'use strict';
|
|
|
|
this.timeout(30000);
|
|
|
|
before(function(done) {
|
|
localforage.setDriver(driverName).then(done);
|
|
});
|
|
|
|
beforeEach(function(done) {
|
|
localStorage.clear();
|
|
localforage.ready().then(function() {
|
|
localforage.clear(done);
|
|
});
|
|
});
|
|
|
|
it('has a localStorage API', function() {
|
|
expect(localforage.getItem).to.be.a('function');
|
|
expect(localforage.setItem).to.be.a('function');
|
|
expect(localforage.clear).to.be.a('function');
|
|
expect(localforage.length).to.be.a('function');
|
|
expect(localforage.removeItem).to.be.a('function');
|
|
expect(localforage.key).to.be.a('function');
|
|
});
|
|
|
|
it('has the localForage API', function() {
|
|
expect(localforage._initStorage).to.be.a('function');
|
|
expect(localforage.config).to.be.a('function');
|
|
expect(localforage.defineDriver).to.be.a('function');
|
|
expect(localforage.driver).to.be.a('function');
|
|
expect(localforage.supports).to.be.a('function');
|
|
expect(localforage.iterate).to.be.a('function');
|
|
expect(localforage.getItem).to.be.a('function');
|
|
expect(localforage.setItem).to.be.a('function');
|
|
expect(localforage.clear).to.be.a('function');
|
|
expect(localforage.length).to.be.a('function');
|
|
expect(localforage.removeItem).to.be.a('function');
|
|
expect(localforage.key).to.be.a('function');
|
|
expect(localforage.getDriver).to.be.a('function');
|
|
expect(localforage.setDriver).to.be.a('function');
|
|
expect(localforage.ready).to.be.a('function');
|
|
expect(localforage.createInstance).to.be.a('function');
|
|
expect(localforage.getSerializer).to.be.a('function');
|
|
expect(localforage.dropInstance).to.be.a('function');
|
|
});
|
|
|
|
// Make sure we don't support bogus drivers.
|
|
it('supports ' + driverName + ' database driver', function() {
|
|
expect(localforage.supports(driverName) === true);
|
|
expect(localforage.supports('I am not a driver') === false);
|
|
});
|
|
|
|
it('sets the right database driver', function() {
|
|
expect(localforage.driver() === driverName);
|
|
});
|
|
|
|
it('has an empty length by default', function(done) {
|
|
localforage.length(function(err, length) {
|
|
expect(length).to.be(0);
|
|
done();
|
|
});
|
|
});
|
|
|
|
if (driverName === localforage.INDEXEDDB) {
|
|
describe('Blob support', function() {
|
|
var transaction;
|
|
var called;
|
|
var db;
|
|
var blob = new Blob([''], { type: 'image/png' });
|
|
|
|
before(function() {
|
|
db = localforage._dbInfo.db;
|
|
transaction = db.transaction;
|
|
db.transaction = function() {
|
|
called += 1;
|
|
return transaction.apply(db, arguments);
|
|
};
|
|
});
|
|
|
|
beforeEach(function() {
|
|
called = 0;
|
|
});
|
|
|
|
it('not check for non Blob', function(done) {
|
|
localforage.setItem('key', {}).then(
|
|
function() {
|
|
expect(called).to.be(1);
|
|
done();
|
|
},
|
|
function(error) {
|
|
done(error || 'error');
|
|
}
|
|
);
|
|
});
|
|
|
|
it('check for Blob', function(done) {
|
|
localforage.setItem('key', blob).then(
|
|
function() {
|
|
expect(called).to.be.above(1);
|
|
done();
|
|
},
|
|
function(error) {
|
|
done(error || 'error');
|
|
}
|
|
);
|
|
});
|
|
|
|
it('check for Blob once', function(done) {
|
|
localforage.setItem('key', blob).then(
|
|
function() {
|
|
expect(called).to.be(1);
|
|
done();
|
|
},
|
|
function(error) {
|
|
done(error || 'error');
|
|
}
|
|
);
|
|
});
|
|
|
|
after(function() {
|
|
localforage._dbInfo.db.transaction = transaction;
|
|
});
|
|
});
|
|
|
|
describe('recover (reconnect) from IDBDatabase InvalidStateError', function() {
|
|
beforeEach(function(done) {
|
|
Promise.all([
|
|
localforage.setItem('key', 'value1'),
|
|
localforage.setItem('key1', 'value1'),
|
|
localforage.setItem('key2', 'value2'),
|
|
localforage.setItem('key3', 'value3')
|
|
]).then(
|
|
function() {
|
|
localforage._dbInfo.db.close();
|
|
done();
|
|
},
|
|
function(error) {
|
|
done(error || 'error');
|
|
}
|
|
);
|
|
});
|
|
|
|
it('retrieves an item from the storage', function(done) {
|
|
localforage.getItem('key').then(
|
|
function(value) {
|
|
expect(value).to.be('value1');
|
|
done();
|
|
},
|
|
function(error) {
|
|
done(error || 'error');
|
|
}
|
|
);
|
|
});
|
|
|
|
it('retrieves more than one items from the storage', function(done) {
|
|
Promise.all([
|
|
localforage.getItem('key1'),
|
|
localforage.getItem('key2'),
|
|
localforage.getItem('key3')
|
|
]).then(
|
|
function(values) {
|
|
expect(values).to.eql([
|
|
'value1',
|
|
'value2',
|
|
'value3'
|
|
]);
|
|
done();
|
|
},
|
|
function(error) {
|
|
done(error || 'error');
|
|
}
|
|
);
|
|
});
|
|
|
|
it('stores and retrieves an item from the storage', function(done) {
|
|
localforage
|
|
.setItem('key', 'value1b')
|
|
.then(function() {
|
|
return localforage.getItem('key');
|
|
})
|
|
.then(
|
|
function(value) {
|
|
expect(value).to.be('value1b');
|
|
done();
|
|
},
|
|
function(error) {
|
|
done(error || 'error');
|
|
}
|
|
);
|
|
});
|
|
|
|
it('stores and retrieves more than one items from the storage', function(done) {
|
|
Promise.all([
|
|
localforage.setItem('key1', 'value1b'),
|
|
localforage.setItem('key2', 'value2b'),
|
|
localforage.setItem('key3', 'value3b')
|
|
])
|
|
.then(function() {
|
|
return Promise.all([
|
|
localforage.getItem('key1'),
|
|
localforage.getItem('key2'),
|
|
localforage.getItem('key3')
|
|
]);
|
|
})
|
|
.then(
|
|
function(values) {
|
|
expect(values).to.eql([
|
|
'value1b',
|
|
'value2b',
|
|
'value3b'
|
|
]);
|
|
done();
|
|
},
|
|
function(error) {
|
|
done(error || 'error');
|
|
}
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
if (driverName === localforage.WEBSQL) {
|
|
describe('on QUOTA ERROR', function() {
|
|
var transaction;
|
|
var called;
|
|
var db;
|
|
|
|
function getQuotaErrorCode(transaction) {
|
|
return new Promise(function(resolve) {
|
|
transaction(
|
|
function(t) {
|
|
t.executeSql('');
|
|
},
|
|
function(err) {
|
|
resolve(err.QUOTA_ERR);
|
|
}
|
|
);
|
|
}).catch(function(err) {
|
|
return err.QUOTA_ERR;
|
|
});
|
|
}
|
|
|
|
beforeEach(function() {
|
|
called = 0;
|
|
db = localforage._dbInfo.db;
|
|
transaction = db.transaction;
|
|
|
|
db.transaction = function(fn, errFn) {
|
|
called += 1;
|
|
// restore the normal transaction,
|
|
// so that subsequent operations work
|
|
db.transaction = transaction;
|
|
|
|
getQuotaErrorCode(transaction).then(function(
|
|
QUOTA_ERR
|
|
) {
|
|
var error = new Error();
|
|
error.code = QUOTA_ERR;
|
|
errFn(error);
|
|
});
|
|
};
|
|
});
|
|
|
|
it('should retry setItem', function(done) {
|
|
localforage.setItem('key', {}).then(
|
|
function() {
|
|
expect(called).to.be(1);
|
|
done();
|
|
},
|
|
function(error) {
|
|
done(error || 'error');
|
|
}
|
|
);
|
|
});
|
|
|
|
after(function() {
|
|
db.transaction = transaction || db.transaction;
|
|
});
|
|
});
|
|
}
|
|
|
|
it('should iterate [callback]', function(done) {
|
|
localforage.setItem('officeX', 'InitechX', function(err, setValue) {
|
|
expect(setValue).to.be('InitechX');
|
|
|
|
localforage.getItem('officeX', function(err, value) {
|
|
expect(value).to.be(setValue);
|
|
|
|
localforage.setItem('officeY', 'InitechY', function(
|
|
err,
|
|
setValue
|
|
) {
|
|
expect(setValue).to.be('InitechY');
|
|
|
|
localforage.getItem('officeY', function(err, value) {
|
|
expect(value).to.be(setValue);
|
|
|
|
var accumulator = {};
|
|
var iterationNumbers = [];
|
|
|
|
localforage.iterate(
|
|
function(value, key, iterationNumber) {
|
|
accumulator[key] = value;
|
|
iterationNumbers.push(iterationNumber);
|
|
},
|
|
function() {
|
|
try {
|
|
expect(accumulator.officeX).to.be(
|
|
'InitechX'
|
|
);
|
|
expect(accumulator.officeY).to.be(
|
|
'InitechY'
|
|
);
|
|
expect(iterationNumbers).to.eql([1, 2]);
|
|
done();
|
|
} catch (e) {
|
|
done(e);
|
|
}
|
|
}
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should iterate [promise]', function(done) {
|
|
var accumulator = {};
|
|
var iterationNumbers = [];
|
|
|
|
return localforage
|
|
.setItem('officeX', 'InitechX')
|
|
.then(function(setValue) {
|
|
expect(setValue).to.be('InitechX');
|
|
return localforage.getItem('officeX');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('InitechX');
|
|
return localforage.setItem('officeY', 'InitechY');
|
|
})
|
|
.then(function(setValue) {
|
|
expect(setValue).to.be('InitechY');
|
|
return localforage.getItem('officeY');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('InitechY');
|
|
|
|
return localforage.iterate(function(
|
|
value,
|
|
key,
|
|
iterationNumber
|
|
) {
|
|
accumulator[key] = value;
|
|
iterationNumbers.push(iterationNumber);
|
|
});
|
|
})
|
|
.then(function() {
|
|
expect(accumulator.officeX).to.be('InitechX');
|
|
expect(accumulator.officeY).to.be('InitechY');
|
|
expect(iterationNumbers).to.eql([1, 2]);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should break iteration with defined return value [callback]', function(done) {
|
|
var breakCondition = 'Some value!';
|
|
|
|
localforage.setItem('officeX', 'InitechX', function(err, setValue) {
|
|
expect(setValue).to.be('InitechX');
|
|
|
|
localforage.getItem('officeX', function(err, value) {
|
|
expect(value).to.be(setValue);
|
|
|
|
localforage.setItem('officeY', 'InitechY', function(
|
|
err,
|
|
setValue
|
|
) {
|
|
expect(setValue).to.be('InitechY');
|
|
|
|
localforage.getItem('officeY', function(err, value) {
|
|
expect(value).to.be(setValue);
|
|
|
|
// Loop is broken within first iteration.
|
|
localforage.iterate(
|
|
function() {
|
|
// Returning defined value will break the cycle.
|
|
return breakCondition;
|
|
},
|
|
function(err, loopResult) {
|
|
// The value that broken the cycle is returned
|
|
// as a result.
|
|
expect(loopResult).to.be(breakCondition);
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should break iteration with defined return value [promise]', function(done) {
|
|
var breakCondition = 'Some value!';
|
|
|
|
localforage
|
|
.setItem('officeX', 'InitechX')
|
|
.then(function(setValue) {
|
|
expect(setValue).to.be('InitechX');
|
|
return localforage.getItem('officeX');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('InitechX');
|
|
return localforage.setItem('officeY', 'InitechY');
|
|
})
|
|
.then(function(setValue) {
|
|
expect(setValue).to.be('InitechY');
|
|
return localforage.getItem('officeY');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('InitechY');
|
|
return localforage.iterate(function() {
|
|
return breakCondition;
|
|
});
|
|
})
|
|
.then(function(result) {
|
|
expect(result).to.be(breakCondition);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should iterate() through only its own keys/values', function(done) {
|
|
localStorage.setItem('local', 'forage');
|
|
localforage
|
|
.setItem('office', 'Initech')
|
|
.then(function() {
|
|
return localforage.setItem('name', 'Bob');
|
|
})
|
|
.then(function() {
|
|
// Loop through all key/value pairs; {local: 'forage'} set
|
|
// manually should not be returned.
|
|
var numberOfItems = 0;
|
|
var iterationNumberConcat = '';
|
|
|
|
localStorage.setItem('locals', 'forages');
|
|
|
|
localforage.iterate(
|
|
function(value, key, iterationNumber) {
|
|
expect(key).to.not.be('local');
|
|
expect(value).to.not.be('forage');
|
|
numberOfItems++;
|
|
iterationNumberConcat += iterationNumber;
|
|
},
|
|
function(err) {
|
|
if (!err) {
|
|
// While there are 4 items in localStorage,
|
|
// only 2 items were set using localForage.
|
|
expect(numberOfItems).to.be(2);
|
|
|
|
// Only 2 items were set using localForage,
|
|
// so we should get '12' and not '1234'
|
|
expect(iterationNumberConcat).to.be('12');
|
|
|
|
done();
|
|
}
|
|
}
|
|
);
|
|
});
|
|
});
|
|
|
|
// Test for https://github.com/mozilla/localForage/issues/175
|
|
it('nested getItem inside clear works [callback]', function() {
|
|
return localforage.setItem('hello', 'Hello World !', function() {
|
|
localforage.clear(function() {
|
|
localforage.getItem('hello', function(secondValue) {
|
|
expect(secondValue).to.be(null);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('nested getItem inside clear works [promise]', function() {
|
|
return localforage
|
|
.setItem('hello', 'Hello World !')
|
|
.then(function() {
|
|
return localforage.clear();
|
|
})
|
|
.then(function() {
|
|
return localforage.getItem('hello');
|
|
})
|
|
.then(function(secondValue) {
|
|
expect(secondValue).to.be(null);
|
|
});
|
|
});
|
|
|
|
// Because localStorage doesn't support saving the `undefined` type, we
|
|
// always return `null` so that localForage is consistent across
|
|
// browsers.
|
|
// https://github.com/mozilla/localForage/pull/42
|
|
it('returns null for undefined key [callback]', function(done) {
|
|
localforage.getItem('key', function(err, value) {
|
|
expect(value).to.be(null);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('returns null for undefined key [promise]', function(done) {
|
|
localforage.getItem('key').then(function(value) {
|
|
expect(value).to.be(null);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('saves an item [callback]', function(done) {
|
|
localforage.setItem('office', 'Initech', function(err, setValue) {
|
|
expect(setValue).to.be('Initech');
|
|
|
|
localforage.getItem('office', function(err, value) {
|
|
expect(value).to.be(setValue);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('saves an item [promise]', function(done) {
|
|
localforage
|
|
.setItem('office', 'Initech')
|
|
.then(function(setValue) {
|
|
expect(setValue).to.be('Initech');
|
|
|
|
return localforage.getItem('office');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('Initech');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('saves an item over an existing key [callback]', function(done) {
|
|
localforage.setItem('4th floor', 'Mozilla', function(
|
|
err,
|
|
setValue
|
|
) {
|
|
expect(setValue).to.be('Mozilla');
|
|
|
|
localforage.setItem('4th floor', 'Quora', function(
|
|
err,
|
|
newValue
|
|
) {
|
|
expect(newValue).to.not.be(setValue);
|
|
expect(newValue).to.be('Quora');
|
|
|
|
localforage.getItem('4th floor', function(err, value) {
|
|
expect(value).to.not.be(setValue);
|
|
expect(value).to.be(newValue);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
it('saves an item over an existing key [promise]', function(done) {
|
|
localforage
|
|
.setItem('4e', 'Mozilla')
|
|
.then(function(setValue) {
|
|
expect(setValue).to.be('Mozilla');
|
|
|
|
return localforage.setItem('4e', 'Quora');
|
|
})
|
|
.then(function(newValue) {
|
|
expect(newValue).to.not.be('Mozilla');
|
|
expect(newValue).to.be('Quora');
|
|
|
|
return localforage.getItem('4e');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.not.be('Mozilla');
|
|
expect(value).to.be('Quora');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('returns null when saving undefined [callback]', function(done) {
|
|
localforage.setItem('undef', undefined, function(err, setValue) {
|
|
expect(setValue).to.be(null);
|
|
|
|
done();
|
|
});
|
|
});
|
|
it('returns null when saving undefined [promise]', function(done) {
|
|
localforage.setItem('undef', undefined).then(function(setValue) {
|
|
expect(setValue).to.be(null);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('returns null when saving null [callback]', function(done) {
|
|
localforage.setItem('null', null, function(err, setValue) {
|
|
expect(setValue).to.be(null);
|
|
|
|
done();
|
|
});
|
|
});
|
|
it('returns null when saving null [promise]', function(done) {
|
|
localforage.setItem('null', null).then(function(setValue) {
|
|
expect(setValue).to.be(null);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('returns null for a non-existant key [callback]', function(done) {
|
|
localforage.getItem('undef', function(err, value) {
|
|
expect(value).to.be(null);
|
|
|
|
done();
|
|
});
|
|
});
|
|
it('returns null for a non-existant key [promise]', function(done) {
|
|
localforage.getItem('undef').then(function(value) {
|
|
expect(value).to.be(null);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
// github.com/mozilla/localforage/pull/24#discussion-diff-9389662R158
|
|
// localStorage's method API (`localStorage.getItem('foo')`) returns
|
|
// `null` for undefined keys, even though its getter/setter API
|
|
// (`localStorage.foo`) returns `undefined` for the same key. Gaia's
|
|
// asyncStorage API, which is based on localStorage and upon which
|
|
// localforage is based, ALSO returns `null`. BLARG! So for now, we
|
|
// just return null, because there's no way to know from localStorage
|
|
// if the key is ACTUALLY `null` or undefined but returning `null`.
|
|
// And returning `undefined` here would break compatibility with
|
|
// localStorage fallback. Maybe in the future we won't care...
|
|
it('returns null from an undefined key [callback]', function(done) {
|
|
localforage.key(0, function(err, key) {
|
|
expect(key).to.be(null);
|
|
|
|
done();
|
|
});
|
|
});
|
|
it('returns null from an undefined key [promise]', function(done) {
|
|
localforage.key(0).then(function(key) {
|
|
expect(key).to.be(null);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('returns key name [callback]', function(done) {
|
|
localforage.setItem('office', 'Initech').then(function() {
|
|
localforage.key(0, function(err, key) {
|
|
expect(key).to.be('office');
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
it('returns key name [promise]', function(done) {
|
|
localforage
|
|
.setItem('office', 'Initech')
|
|
.then(function() {
|
|
return localforage.key(0);
|
|
})
|
|
.then(function(key) {
|
|
expect(key).to.be('office');
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('removes an item [callback]', function(done) {
|
|
localforage.setItem('office', 'Initech', function() {
|
|
localforage.setItem('otherOffice', 'Initrode', function() {
|
|
localforage.removeItem('office', function() {
|
|
localforage.getItem('office', function(
|
|
err,
|
|
emptyValue
|
|
) {
|
|
expect(emptyValue).to.be(null);
|
|
|
|
localforage.getItem('otherOffice', function(
|
|
err,
|
|
value
|
|
) {
|
|
expect(value).to.be('Initrode');
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
it('removes an item [promise]', function(done) {
|
|
localforage
|
|
.setItem('office', 'Initech')
|
|
.then(function() {
|
|
return localforage.setItem('otherOffice', 'Initrode');
|
|
})
|
|
.then(function() {
|
|
return localforage.removeItem('office');
|
|
})
|
|
.then(function() {
|
|
return localforage.getItem('office');
|
|
})
|
|
.then(function(emptyValue) {
|
|
expect(emptyValue).to.be(null);
|
|
|
|
return localforage.getItem('otherOffice');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('Initrode');
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('removes all items [callback]', function(done) {
|
|
localforage.setItem('office', 'Initech', function() {
|
|
localforage.setItem('otherOffice', 'Initrode', function() {
|
|
localforage.length(function(err, length) {
|
|
expect(length).to.be(2);
|
|
|
|
localforage.clear(function() {
|
|
localforage.getItem('office', function(err, value) {
|
|
expect(value).to.be(null);
|
|
|
|
localforage.length(function(err, length) {
|
|
expect(length).to.be(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
it('removes all items [promise]', function(done) {
|
|
localforage
|
|
.setItem('office', 'Initech')
|
|
.then(function() {
|
|
return localforage.setItem('otherOffice', 'Initrode');
|
|
})
|
|
.then(function() {
|
|
return localforage.length();
|
|
})
|
|
.then(function(length) {
|
|
expect(length).to.be(2);
|
|
|
|
return localforage.clear();
|
|
})
|
|
.then(function() {
|
|
return localforage.getItem('office');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be(null);
|
|
|
|
return localforage.length();
|
|
})
|
|
.then(function(length) {
|
|
expect(length).to.be(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
if (driverName === localforage.LOCALSTORAGE) {
|
|
it('removes only own items upon clear', function(done) {
|
|
localStorage.setItem('local', 'forage');
|
|
|
|
localforage
|
|
.setItem('office', 'Initech')
|
|
.then(function() {
|
|
return localforage.clear();
|
|
})
|
|
.then(function() {
|
|
expect(localStorage.getItem('local')).to.be('forage');
|
|
|
|
localStorage.clear();
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('returns only its own keys from keys()', function(done) {
|
|
localStorage.setItem('local', 'forage');
|
|
|
|
localforage
|
|
.setItem('office', 'Initech')
|
|
.then(function() {
|
|
return localforage.keys();
|
|
})
|
|
.then(function(keys) {
|
|
expect(keys).to.eql(['office']);
|
|
|
|
localStorage.clear();
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('counts only its own items with length()', function(done) {
|
|
localStorage.setItem('local', 'forage');
|
|
localStorage.setItem('another', 'value');
|
|
|
|
localforage
|
|
.setItem('office', 'Initech')
|
|
.then(function() {
|
|
return localforage.length();
|
|
})
|
|
.then(function(length) {
|
|
expect(length).to.be(1);
|
|
|
|
localStorage.clear();
|
|
|
|
done();
|
|
});
|
|
});
|
|
}
|
|
|
|
it('has a length after saving an item [callback]', function(done) {
|
|
localforage.length(function(err, length) {
|
|
expect(length).to.be(0);
|
|
localforage.setItem('rapper', 'Black Thought', function() {
|
|
localforage.length(function(err, length) {
|
|
expect(length).to.be(1);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
it('has a length after saving an item [promise]', function(done) {
|
|
localforage
|
|
.length()
|
|
.then(function(length) {
|
|
expect(length).to.be(0);
|
|
|
|
return localforage.setItem('lame rapper', 'Vanilla Ice');
|
|
})
|
|
.then(function() {
|
|
return localforage.length();
|
|
})
|
|
.then(function(length) {
|
|
expect(length).to.be(1);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
// Deal with non-string keys, see issue #250
|
|
// https://github.com/mozilla/localForage/issues/250
|
|
it('casts an undefined key to a String', function(done) {
|
|
localforage
|
|
.setItem(undefined, 'goodness!')
|
|
.then(function(value) {
|
|
expect(value).to.be('goodness!');
|
|
|
|
return localforage.getItem(undefined);
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('goodness!');
|
|
|
|
return localforage.removeItem(undefined);
|
|
})
|
|
.then(function() {
|
|
return localforage.length();
|
|
})
|
|
.then(function(length) {
|
|
expect(length).to.be(0);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('casts a null key to a String', function(done) {
|
|
localforage
|
|
.setItem(null, 'goodness!')
|
|
.then(function(value) {
|
|
expect(value).to.be('goodness!');
|
|
|
|
return localforage.getItem(null);
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('goodness!');
|
|
|
|
return localforage.removeItem(null);
|
|
})
|
|
.then(function() {
|
|
return localforage.length();
|
|
})
|
|
.then(function(length) {
|
|
expect(length).to.be(0);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('casts a float key to a String', function(done) {
|
|
localforage
|
|
.setItem(537.35737, 'goodness!')
|
|
.then(function(value) {
|
|
expect(value).to.be('goodness!');
|
|
|
|
return localforage.getItem(537.35737);
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('goodness!');
|
|
|
|
return localforage.removeItem(537.35737);
|
|
})
|
|
.then(function() {
|
|
return localforage.length();
|
|
})
|
|
.then(function(length) {
|
|
expect(length).to.be(0);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('is retrieved by getDriver [callback]', function(done) {
|
|
localforage.getDriver(driverName, function(driver) {
|
|
expect(typeof driver).to.be('object');
|
|
driverApiMethods
|
|
.concat('_initStorage')
|
|
.forEach(function(methodName) {
|
|
expect(typeof driver[methodName]).to.be('function');
|
|
});
|
|
expect(driver._driver).to.be(driverName);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('is retrieved by getDriver [promise]', function(done) {
|
|
localforage.getDriver(driverName).then(function(driver) {
|
|
expect(typeof driver).to.be('object');
|
|
driverApiMethods
|
|
.concat('_initStorage')
|
|
.forEach(function(methodName) {
|
|
expect(typeof driver[methodName]).to.be('function');
|
|
});
|
|
expect(driver._driver).to.be(driverName);
|
|
done();
|
|
});
|
|
});
|
|
|
|
if (
|
|
driverName === localforage.WEBSQL ||
|
|
driverName === localforage.LOCALSTORAGE
|
|
) {
|
|
it('exposes the serializer on the dbInfo object', function(done) {
|
|
localforage.ready().then(function() {
|
|
expect(localforage._dbInfo.serializer).to.be.an('object');
|
|
done();
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
function prepareStorage(storageName) {
|
|
// Delete IndexedDB storages (start from scratch)
|
|
// Refers to issue #492 - https://github.com/mozilla/localForage/issues/492
|
|
if (driverName === localforage.INDEXEDDB) {
|
|
return new Promise(function(resolve) {
|
|
indexedDB.deleteDatabase(storageName).onsuccess = resolve;
|
|
});
|
|
}
|
|
|
|
// Otherwise, do nothing
|
|
return Promise.resolve();
|
|
}
|
|
|
|
describe(driverName + ' driver multiple instances', function() {
|
|
'use strict';
|
|
|
|
this.timeout(30000);
|
|
|
|
var localforage2 = null;
|
|
var localforage3 = null;
|
|
|
|
before(function(done) {
|
|
prepareStorage('storage2').then(function() {
|
|
localforage2 = localforage.createInstance({
|
|
name: 'storage2',
|
|
// We need a small value here
|
|
// otherwise local PhantomJS test
|
|
// will fail with SECURITY_ERR.
|
|
// TravisCI seem to work fine though.
|
|
size: 1024,
|
|
storeName: 'storagename2'
|
|
});
|
|
|
|
// Same name, but different storeName since this has been
|
|
// malfunctioning before w/ IndexedDB.
|
|
localforage3 = localforage.createInstance({
|
|
name: 'storage2',
|
|
// We need a small value here
|
|
// otherwise local PhantomJS test
|
|
// will fail with SECURITY_ERR.
|
|
// TravisCI seem to work fine though.
|
|
size: 1024,
|
|
storeName: 'storagename3'
|
|
});
|
|
|
|
Promise.all([
|
|
localforage.setDriver(driverName),
|
|
localforage2.setDriver(driverName),
|
|
localforage3.setDriver(driverName)
|
|
]).then(function() {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
beforeEach(function(done) {
|
|
Promise.all([
|
|
localforage.clear(),
|
|
localforage2.clear(),
|
|
localforage3.clear()
|
|
]).then(function() {
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('is not be able to access values of other instances', function(done) {
|
|
Promise.all([
|
|
localforage.setItem('key1', 'value1a'),
|
|
localforage2.setItem('key2', 'value2a'),
|
|
localforage3.setItem('key3', 'value3a')
|
|
])
|
|
.then(function() {
|
|
return Promise.all([
|
|
localforage.getItem('key2').then(function(value) {
|
|
expect(value).to.be(null);
|
|
}),
|
|
localforage2.getItem('key1').then(function(value) {
|
|
expect(value).to.be(null);
|
|
}),
|
|
localforage2.getItem('key3').then(function(value) {
|
|
expect(value).to.be(null);
|
|
}),
|
|
localforage3.getItem('key2').then(function(value) {
|
|
expect(value).to.be(null);
|
|
})
|
|
]);
|
|
})
|
|
.then(
|
|
function() {
|
|
done();
|
|
},
|
|
function(errors) {
|
|
done(new Error(errors));
|
|
}
|
|
);
|
|
});
|
|
|
|
it('retrieves the proper value when using the same key with other instances', function(done) {
|
|
Promise.all([
|
|
localforage.setItem('key', 'value1'),
|
|
localforage2.setItem('key', 'value2'),
|
|
localforage3.setItem('key', 'value3')
|
|
])
|
|
.then(function() {
|
|
return Promise.all([
|
|
localforage.getItem('key').then(function(value) {
|
|
expect(value).to.be('value1');
|
|
}),
|
|
localforage2.getItem('key').then(function(value) {
|
|
expect(value).to.be('value2');
|
|
}),
|
|
localforage3.getItem('key').then(function(value) {
|
|
expect(value).to.be('value3');
|
|
})
|
|
]);
|
|
})
|
|
.then(
|
|
function() {
|
|
done();
|
|
},
|
|
function(errors) {
|
|
done(new Error(errors));
|
|
}
|
|
);
|
|
});
|
|
});
|
|
|
|
// Refers to issue #492 - https://github.com/mozilla/localForage/issues/492
|
|
describe(
|
|
driverName + ' driver multiple instances (concurrent on same database)',
|
|
function() {
|
|
'use strict';
|
|
|
|
this.timeout(30000);
|
|
|
|
before(function() {
|
|
return Promise.all([
|
|
prepareStorage('storage3'),
|
|
prepareStorage('commonStorage'),
|
|
prepareStorage('commonStorage2'),
|
|
prepareStorage('commonStorage3')
|
|
]);
|
|
});
|
|
|
|
it('chains operation on multiple stores', function() {
|
|
var localforage1 = localforage.createInstance({
|
|
name: 'storage3',
|
|
storeName: 'store1',
|
|
size: 1024
|
|
});
|
|
|
|
var localforage2 = localforage.createInstance({
|
|
name: 'storage3',
|
|
storeName: 'store2',
|
|
size: 1024
|
|
});
|
|
|
|
var localforage3 = localforage.createInstance({
|
|
name: 'storage3',
|
|
storeName: 'store3',
|
|
size: 1024
|
|
});
|
|
|
|
var promise1 = localforage1
|
|
.setItem('key', 'value1')
|
|
.then(function() {
|
|
return localforage1.getItem('key');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value1');
|
|
});
|
|
|
|
var promise2 = localforage2
|
|
.setItem('key', 'value2')
|
|
.then(function() {
|
|
return localforage2.getItem('key');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value2');
|
|
});
|
|
|
|
var promise3 = localforage3
|
|
.setItem('key', 'value3')
|
|
.then(function() {
|
|
return localforage3.getItem('key');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value3');
|
|
});
|
|
|
|
return Promise.all([promise1, promise2, promise3]);
|
|
});
|
|
|
|
it('can create multiple instances of the same store', function() {
|
|
var localforage1;
|
|
var localforage2;
|
|
var localforage3;
|
|
|
|
Promise.resolve()
|
|
.then(function() {
|
|
localforage1 = localforage.createInstance({
|
|
name: 'commonStorage',
|
|
storeName: 'commonStore',
|
|
size: 1024
|
|
});
|
|
return localforage1.ready();
|
|
})
|
|
.then(function() {
|
|
localforage2 = localforage.createInstance({
|
|
name: 'commonStorage',
|
|
storeName: 'commonStore',
|
|
size: 1024
|
|
});
|
|
return localforage2.ready();
|
|
})
|
|
.then(function() {
|
|
localforage3 = localforage.createInstance({
|
|
name: 'commonStorage',
|
|
storeName: 'commonStore',
|
|
size: 1024
|
|
});
|
|
return localforage3.ready();
|
|
})
|
|
.then(function() {
|
|
return Promise.resolve()
|
|
.then(function() {
|
|
return localforage1
|
|
.setItem('key1', 'value1')
|
|
.then(function() {
|
|
return localforage1.getItem('key1');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value1');
|
|
});
|
|
})
|
|
.then(function() {
|
|
return localforage2
|
|
.setItem('key2', 'value2')
|
|
.then(function() {
|
|
return localforage2.getItem('key2');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value2');
|
|
});
|
|
})
|
|
.then(function() {
|
|
return localforage3
|
|
.setItem('key3', 'value3')
|
|
.then(function() {
|
|
return localforage3.getItem('key3');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value3');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can create multiple instances of the same store and do concurrent operations', function() {
|
|
var localforage1;
|
|
var localforage2;
|
|
var localforage3;
|
|
var localforage3b;
|
|
|
|
Promise.resolve()
|
|
.then(function() {
|
|
localforage1 = localforage.createInstance({
|
|
name: 'commonStorage2',
|
|
storeName: 'commonStore',
|
|
size: 1024
|
|
});
|
|
return localforage1.ready();
|
|
})
|
|
.then(function() {
|
|
localforage2 = localforage.createInstance({
|
|
name: 'commonStorage2',
|
|
storeName: 'commonStore',
|
|
size: 1024
|
|
});
|
|
return localforage2.ready();
|
|
})
|
|
.then(function() {
|
|
localforage3 = localforage.createInstance({
|
|
name: 'commonStorage2',
|
|
storeName: 'commonStore',
|
|
size: 1024
|
|
});
|
|
return localforage3.ready();
|
|
})
|
|
.then(function() {
|
|
localforage3b = localforage.createInstance({
|
|
name: 'commonStorage2',
|
|
storeName: 'commonStore',
|
|
size: 1024
|
|
});
|
|
return localforage3b.ready();
|
|
})
|
|
.then(function() {
|
|
var promise1 = localforage1
|
|
.setItem('key1', 'value1')
|
|
.then(function() {
|
|
return localforage1.getItem('key1');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value1');
|
|
});
|
|
|
|
var promise2 = localforage2
|
|
.setItem('key2', 'value2')
|
|
.then(function() {
|
|
return localforage2.getItem('key2');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value2');
|
|
});
|
|
|
|
var promise3 = localforage3
|
|
.setItem('key3', 'value3')
|
|
.then(function() {
|
|
return localforage3.getItem('key3');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value3');
|
|
});
|
|
|
|
var promise4 = localforage3b
|
|
.setItem('key3', 'value3')
|
|
.then(function() {
|
|
return localforage3.getItem('key3');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value3');
|
|
});
|
|
|
|
return Promise.all([
|
|
promise1,
|
|
promise2,
|
|
promise3,
|
|
promise4
|
|
]);
|
|
});
|
|
});
|
|
|
|
it('can create multiple instances of the same store concurrently', function() {
|
|
var localforage1 = localforage.createInstance({
|
|
name: 'commonStorage3',
|
|
storeName: 'commonStore',
|
|
size: 1024
|
|
});
|
|
|
|
var localforage2 = localforage.createInstance({
|
|
name: 'commonStorage3',
|
|
storeName: 'commonStore',
|
|
size: 1024
|
|
});
|
|
|
|
var localforage3 = localforage.createInstance({
|
|
name: 'commonStorage3',
|
|
storeName: 'commonStore',
|
|
size: 1024
|
|
});
|
|
|
|
var localforage3b = localforage.createInstance({
|
|
name: 'commonStorage3',
|
|
storeName: 'commonStore',
|
|
size: 1024
|
|
});
|
|
|
|
var promise1 = localforage1
|
|
.setItem('key1', 'value1')
|
|
.then(function() {
|
|
return localforage1.getItem('key1');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value1');
|
|
});
|
|
|
|
var promise2 = localforage2
|
|
.setItem('key2', 'value2')
|
|
.then(function() {
|
|
return localforage2.getItem('key2');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value2');
|
|
});
|
|
|
|
var promise3 = localforage3
|
|
.setItem('key3', 'value3')
|
|
.then(function() {
|
|
return localforage3.getItem('key3');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value3');
|
|
});
|
|
|
|
var promise4 = localforage3b
|
|
.setItem('key3', 'value3')
|
|
.then(function() {
|
|
return localforage3.getItem('key3');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value3');
|
|
});
|
|
|
|
return Promise.all([promise1, promise2, promise3, promise4]);
|
|
});
|
|
}
|
|
);
|
|
|
|
describe(driverName + ' driver', function() {
|
|
'use strict';
|
|
|
|
var driverPreferedOrder;
|
|
|
|
before(function() {
|
|
// add some unsupported drivers before
|
|
// and after the target driver
|
|
driverPreferedOrder = ['I am a not supported driver'];
|
|
|
|
if (!localforage.supports(localforage.WEBSQL)) {
|
|
driverPreferedOrder.push(localforage.WEBSQL);
|
|
}
|
|
if (!localforage.supports(localforage.INDEXEDDB)) {
|
|
driverPreferedOrder.push(localforage.INDEXEDDB);
|
|
}
|
|
if (!localforage.supports(localforage.LOCALSTORAGE)) {
|
|
driverPreferedOrder.push(localforage.localStorage);
|
|
}
|
|
|
|
driverPreferedOrder.push(driverName);
|
|
|
|
driverPreferedOrder.push('I am another not supported driver');
|
|
});
|
|
|
|
it('is used according to setDriver preference order', function(done) {
|
|
localforage.setDriver(driverPreferedOrder).then(function() {
|
|
expect(localforage.driver()).to.be(driverName);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe(
|
|
driverName + ' driver when the callback throws an Error',
|
|
function() {
|
|
'use strict';
|
|
|
|
var testObj = {
|
|
throwFunc: function() {
|
|
testObj.throwFuncCalls++;
|
|
throw new Error('Thrown test error');
|
|
},
|
|
throwFuncCalls: 0
|
|
};
|
|
|
|
beforeEach(function(done) {
|
|
testObj.throwFuncCalls = 0;
|
|
done();
|
|
});
|
|
|
|
it('resolves the promise of getItem()', function(done) {
|
|
localforage.getItem('key', testObj.throwFunc).then(function() {
|
|
expect(testObj.throwFuncCalls).to.be(1);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('resolves the promise of setItem()', function(done) {
|
|
localforage
|
|
.setItem('key', 'test', testObj.throwFunc)
|
|
.then(function() {
|
|
expect(testObj.throwFuncCalls).to.be(1);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('resolves the promise of clear()', function(done) {
|
|
localforage.clear(testObj.throwFunc).then(function() {
|
|
expect(testObj.throwFuncCalls).to.be(1);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('resolves the promise of length()', function(done) {
|
|
localforage.length(testObj.throwFunc).then(function() {
|
|
expect(testObj.throwFuncCalls).to.be(1);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('resolves the promise of removeItem()', function(done) {
|
|
localforage
|
|
.removeItem('key', testObj.throwFunc)
|
|
.then(function() {
|
|
expect(testObj.throwFuncCalls).to.be(1);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('resolves the promise of key()', function(done) {
|
|
localforage.key('key', testObj.throwFunc).then(function() {
|
|
expect(testObj.throwFuncCalls).to.be(1);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('resolves the promise of keys()', function(done) {
|
|
localforage.keys(testObj.throwFunc).then(function() {
|
|
expect(testObj.throwFuncCalls).to.be(1);
|
|
done();
|
|
});
|
|
});
|
|
}
|
|
);
|
|
|
|
describe(driverName + ' driver when ready() gets rejected', function() {
|
|
'use strict';
|
|
|
|
this.timeout(30000);
|
|
|
|
var _oldReady;
|
|
|
|
beforeEach(function(done) {
|
|
_oldReady = localforage.ready;
|
|
localforage.ready = function() {
|
|
return Promise.reject();
|
|
};
|
|
done();
|
|
});
|
|
|
|
afterEach(function(done) {
|
|
localforage.ready = _oldReady;
|
|
_oldReady = null;
|
|
done();
|
|
});
|
|
|
|
driverApiMethods.forEach(function(methodName) {
|
|
it('rejects ' + methodName + '() promise', function(done) {
|
|
localforage[methodName]().then(null, function(/*err*/) {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
DRIVERS.forEach(function(driverName) {
|
|
describe(driverName + ' driver instance', function() {
|
|
it('creates a new instance and sets the driver', function(done) {
|
|
var localforage2 = localforage.createInstance({
|
|
name: 'storage2',
|
|
driver: driverName,
|
|
// We need a small value here
|
|
// otherwise local PhantomJS test
|
|
// will fail with SECURITY_ERR.
|
|
// TravisCI seem to work fine though.
|
|
size: 1024,
|
|
storeName: 'storagename2'
|
|
});
|
|
|
|
// since config actually uses setDriver which is async,
|
|
// and since driver() and supports() are not defered (are sync),
|
|
// we have to wait till an async method returns
|
|
localforage2.length().then(
|
|
function() {
|
|
expect(localforage2.driver()).to.be(driverName);
|
|
done();
|
|
},
|
|
function() {
|
|
expect(localforage2.driver()).to.be(null);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
SUPPORTED_DRIVERS.forEach(function(driverName) {
|
|
describe(driverName + ' driver dropInstance', function() {
|
|
this.timeout(30000);
|
|
|
|
function setCommonOpts(opts) {
|
|
opts.driver = driverName;
|
|
opts.size = 1024;
|
|
return opts;
|
|
}
|
|
|
|
var dropStoreDbName = 'dropStoreDb';
|
|
|
|
var nodropInstance;
|
|
var nodropInstanceOptions = setCommonOpts({
|
|
name: dropStoreDbName,
|
|
storeName: 'nodropStore'
|
|
});
|
|
|
|
var dropStoreInstance1;
|
|
var dropStoreInstance1Options = setCommonOpts({
|
|
name: dropStoreDbName,
|
|
storeName: 'dropStore'
|
|
});
|
|
|
|
var dropStoreInstance2;
|
|
var dropStoreInstance2Options = setCommonOpts({
|
|
name: dropStoreDbName,
|
|
storeName: 'dropStore2'
|
|
});
|
|
|
|
var dropStoreInstance3;
|
|
var dropStoreInstance3Options = setCommonOpts({
|
|
name: dropStoreDbName,
|
|
storeName: 'dropStore3'
|
|
});
|
|
|
|
var dropDbInstance;
|
|
var dropDbInstanceOptions = setCommonOpts({
|
|
name: 'dropDb',
|
|
storeName: 'dropStore'
|
|
});
|
|
|
|
var dropDb2Instance;
|
|
var dropDb2InstanceOptions = setCommonOpts({
|
|
name: 'dropDb2',
|
|
storeName: 'dropStore'
|
|
});
|
|
|
|
var dropDb3name = 'dropDb3';
|
|
|
|
var dropDb3Instance1;
|
|
var dropDb3Instance1Options = setCommonOpts({
|
|
name: dropDb3name,
|
|
storeName: 'dropStore1'
|
|
});
|
|
|
|
var dropDb3Instance2;
|
|
var dropDb3Instance2Options = setCommonOpts({
|
|
name: dropDb3name,
|
|
storeName: 'dropStore2'
|
|
});
|
|
|
|
var dropDb3Instance3;
|
|
var dropDb3Instance3Options = setCommonOpts({
|
|
name: dropDb3name,
|
|
storeName: 'dropStore3'
|
|
});
|
|
|
|
before(function() {
|
|
nodropInstance = localforage.createInstance(nodropInstanceOptions);
|
|
dropStoreInstance1 = localforage.createInstance(
|
|
dropStoreInstance1Options
|
|
);
|
|
dropStoreInstance2 = localforage.createInstance(
|
|
dropStoreInstance2Options
|
|
);
|
|
dropStoreInstance3 = localforage.createInstance(
|
|
dropStoreInstance3Options
|
|
);
|
|
dropDbInstance = localforage.createInstance(dropDbInstanceOptions);
|
|
dropDb2Instance = localforage.createInstance(
|
|
dropDb2InstanceOptions
|
|
);
|
|
dropDb3Instance1 = localforage.createInstance(
|
|
dropDb3Instance1Options
|
|
);
|
|
dropDb3Instance2 = localforage.createInstance(
|
|
dropDb3Instance2Options
|
|
);
|
|
dropDb3Instance3 = localforage.createInstance(
|
|
dropDb3Instance3Options
|
|
);
|
|
return Promise.resolve()
|
|
.then(function() {
|
|
return nodropInstance.setItem('key1', 'value0');
|
|
})
|
|
.then(function() {
|
|
return dropStoreInstance1.setItem('key1', 'value1');
|
|
})
|
|
.then(function() {
|
|
return dropStoreInstance2.setItem('key1', 'value2');
|
|
})
|
|
.then(function() {
|
|
return dropStoreInstance3.setItem('key1', 'value3');
|
|
})
|
|
.then(function() {
|
|
return dropDbInstance.setItem('key1', 'value3');
|
|
})
|
|
.then(function() {
|
|
return dropDb2Instance.setItem('key1', 'value3');
|
|
})
|
|
.then(function() {
|
|
return dropDb3Instance1.setItem('key1', 'value1');
|
|
})
|
|
.then(function() {
|
|
return dropDb3Instance2.setItem('key1', 'value2');
|
|
})
|
|
.then(function() {
|
|
return dropDb3Instance3.setItem('key1', 'value3');
|
|
});
|
|
});
|
|
|
|
function expectStoreToNotExistAsync(options) {
|
|
return new Promise(function(resolve, reject) {
|
|
if (driverName === localforage.INDEXEDDB) {
|
|
var req = indexedDB.open(options.name);
|
|
req.onsuccess = function() {
|
|
var db = req.result;
|
|
if (!db) {
|
|
reject();
|
|
return;
|
|
}
|
|
expect(
|
|
db.objectStoreNames.contains(options.storeName)
|
|
).to.be(false);
|
|
db.close();
|
|
resolve();
|
|
};
|
|
req.onerror = req.onblocked = reject;
|
|
} else if (driverName === localforage.WEBSQL) {
|
|
var db = openDatabase(options.name, '', '', 0);
|
|
db.transaction(function(t) {
|
|
t.executeSql(
|
|
"SELECT name FROM sqlite_master WHERE type='table' AND name = ?",
|
|
[options.storeName],
|
|
function(t, results) {
|
|
expect(results.rows.length).to.be(0);
|
|
resolve();
|
|
},
|
|
reject
|
|
);
|
|
}, reject);
|
|
} else if (driverName === localforage.LOCALSTORAGE) {
|
|
var keyPrefix = (function _getKeyPrefix(
|
|
options,
|
|
defaultConfig
|
|
) {
|
|
var keyPrefix = options.name + '/';
|
|
|
|
if (options.storeName !== defaultConfig.storeName) {
|
|
keyPrefix += options.storeName + '/';
|
|
}
|
|
return keyPrefix;
|
|
})(options, {
|
|
name: 'localforage',
|
|
storeName: 'keyvaluepairs'
|
|
});
|
|
|
|
var foundLocalStorageKey = false;
|
|
for (
|
|
var i = 0, length = localStorage.length;
|
|
i < length;
|
|
i++
|
|
) {
|
|
if (localStorage.key(i).indexOf(keyPrefix) === 0) {
|
|
foundLocalStorageKey = true;
|
|
break;
|
|
}
|
|
}
|
|
expect(foundLocalStorageKey).to.be(false);
|
|
resolve();
|
|
} else if (driverName === localforage.MEMORY) {
|
|
// can't think of a good way to test this
|
|
resolve();
|
|
} else {
|
|
throw new Error('Not Implemented Exception');
|
|
}
|
|
});
|
|
}
|
|
|
|
it('drops the current instance without affecting the rest', function() {
|
|
return dropStoreInstance1
|
|
.dropInstance()
|
|
.then(function() {
|
|
return nodropInstance.getItem('key1');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value0');
|
|
});
|
|
});
|
|
|
|
it('can recreate and set values to previously dropped instances', function() {
|
|
return dropStoreInstance1
|
|
.dropInstance()
|
|
.then(function() {
|
|
return dropStoreInstance1.getItem('key1');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be(null);
|
|
return dropStoreInstance1.length();
|
|
})
|
|
.then(function(length) {
|
|
expect(length).to.be(0);
|
|
})
|
|
.then(function() {
|
|
return dropStoreInstance1.setItem('key1', 'newvalue2');
|
|
})
|
|
.then(function() {
|
|
return dropStoreInstance1.getItem('key1');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('newvalue2');
|
|
});
|
|
});
|
|
|
|
it('drops an other instance without affecting the rest', function() {
|
|
var opts = {
|
|
name: dropStoreInstance2Options.name,
|
|
storeName: dropStoreInstance2Options.storeName
|
|
};
|
|
return nodropInstance
|
|
.dropInstance(opts)
|
|
.then(function() {
|
|
return nodropInstance.getItem('key1');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be('value0');
|
|
});
|
|
});
|
|
|
|
it('the dropped instance is completely removed', function() {
|
|
var opts = {
|
|
name: dropStoreInstance3Options.name,
|
|
storeName: dropStoreInstance3Options.storeName
|
|
};
|
|
return dropStoreInstance3.dropInstance().then(function() {
|
|
return expectStoreToNotExistAsync(opts);
|
|
});
|
|
});
|
|
|
|
it('resolves when trying to drop a store that does not exit', function() {
|
|
var opts = {
|
|
name: dropStoreInstance3Options.name,
|
|
storeName: 'NotExistingStore' + Date.now()
|
|
};
|
|
return dropStoreInstance3.dropInstance(opts);
|
|
});
|
|
|
|
function expectDBToNotExistAsync(options) {
|
|
return new Promise(function(resolve, reject) {
|
|
if (driverName === localforage.INDEXEDDB) {
|
|
var req = indexedDB.open(options.name);
|
|
req.onsuccess = function() {
|
|
var db = req.result;
|
|
if (!db) {
|
|
reject();
|
|
return;
|
|
}
|
|
expect(db.objectStoreNames.length).to.be(0);
|
|
db.close();
|
|
resolve();
|
|
};
|
|
req.onerror = req.onblocked = reject;
|
|
} else if (driverName === localforage.WEBSQL) {
|
|
var db = openDatabase(options.name, '', '', 0);
|
|
db.transaction(function(t) {
|
|
t.executeSql(
|
|
"SELECT name FROM sqlite_master WHERE type='table'",
|
|
[],
|
|
function(t, results) {
|
|
var stores = Array.prototype.filter.call(
|
|
results.rows,
|
|
function(obj) {
|
|
return (
|
|
obj &&
|
|
obj.name &&
|
|
obj.name.indexOf('__') !== 0
|
|
);
|
|
}
|
|
);
|
|
expect(stores.length).to.be(0);
|
|
resolve();
|
|
},
|
|
reject
|
|
);
|
|
}, reject);
|
|
} else if (driverName === localforage.LOCALSTORAGE) {
|
|
var keyPrefix = (function _getKeyPrefix(options) {
|
|
return options.name + '/';
|
|
})(options);
|
|
|
|
var foundLocalStorageKey = false;
|
|
for (
|
|
var i = 0, length = localStorage.length;
|
|
i < length;
|
|
i++
|
|
) {
|
|
if (localStorage.key(i).indexOf(keyPrefix) === 0) {
|
|
foundLocalStorageKey = true;
|
|
break;
|
|
}
|
|
}
|
|
expect(foundLocalStorageKey).to.be(false);
|
|
resolve();
|
|
} else if (driverName === localforage.MEMORY) {
|
|
// can't think of a good way to test this
|
|
resolve();
|
|
} else {
|
|
throw new Error('Not Implemented Exception');
|
|
}
|
|
});
|
|
}
|
|
|
|
it('the dropped "DB" can be recreated', function() {
|
|
var opts = {
|
|
name: dropDbInstanceOptions.name
|
|
};
|
|
return dropDbInstance
|
|
.dropInstance(opts)
|
|
.then(function() {
|
|
return dropDbInstance.getItem('key1');
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be(null);
|
|
})
|
|
.then(function() {
|
|
return dropDbInstance.length();
|
|
})
|
|
.then(function(value) {
|
|
expect(value).to.be(0);
|
|
});
|
|
});
|
|
|
|
it('the dropped "DB" is completely removed', function() {
|
|
var opts = {
|
|
name: dropDb2InstanceOptions.name
|
|
};
|
|
return dropDb2Instance.dropInstance(opts).then(function() {
|
|
return expectDBToNotExistAsync(opts);
|
|
});
|
|
});
|
|
|
|
it('resolves when trying to drop a store of a "DB" that does not exit', function() {
|
|
var opts = {
|
|
name: 'NotExistingDB' + Date.now(),
|
|
storeName: 'NotExistingStore' + Date.now()
|
|
};
|
|
return dropStoreInstance3.dropInstance(opts);
|
|
});
|
|
|
|
it('resolves when trying to drop a "DB" that does not exist', function() {
|
|
var opts = {
|
|
name: 'NotExistingDB' + Date.now()
|
|
};
|
|
return dropStoreInstance3.dropInstance(opts);
|
|
});
|
|
|
|
it('drops a "DB" that we previously dropped a store', function() {
|
|
var opts = {
|
|
name: dropStoreInstance3Options.name
|
|
};
|
|
return dropStoreInstance3.dropInstance(opts).then(function() {
|
|
return expectDBToNotExistAsync(opts);
|
|
});
|
|
});
|
|
|
|
it('drops a "DB" after dropping all its stores', function() {
|
|
var opts = {
|
|
name: dropDb3name
|
|
};
|
|
// Before trying to drop a different store/DB
|
|
// make sure that the instance that you will use
|
|
// is configured to use the same driver as well.
|
|
return Promise.resolve()
|
|
.then(function() {
|
|
return dropDb3Instance1.dropInstance({
|
|
name: dropDb3name,
|
|
storeName: dropDb3Instance1Options.storeName
|
|
});
|
|
})
|
|
.then(function() {
|
|
return dropDb3Instance1.dropInstance({
|
|
name: dropDb3name,
|
|
storeName: dropDb3Instance2Options.storeName
|
|
});
|
|
})
|
|
.then(function() {
|
|
return dropDb3Instance1.dropInstance({
|
|
name: dropDb3name,
|
|
storeName: dropDb3Instance3Options.storeName
|
|
});
|
|
})
|
|
.then(function() {
|
|
return dropDb3Instance1.dropInstance(opts);
|
|
})
|
|
.then(function() {
|
|
return expectDBToNotExistAsync(opts);
|
|
});
|
|
});
|
|
});
|
|
});
|