From 58392efd1089b69e132a98ebe452fe01d52e4a2c Mon Sep 17 00:00:00 2001 From: "David Humphrey (:humph) david.humphrey@senecacollege.ca" Date: Wed, 27 Nov 2013 16:52:52 -0500 Subject: [PATCH 1/4] WebSQL Storage Provider with tests (4 tests failing still) --- src/constants.js | 4 + src/providers/providers.js | 30 ++- src/providers/websql.js | 130 +++++++++++ tests/spec/providers/providers.spec.js | 8 + tests/spec/providers/providers.websql.spec.js | 208 ++++++++++++++++++ tests/test-manifest.js | 1 + 6 files changed, 378 insertions(+), 3 deletions(-) create mode 100644 src/providers/websql.js create mode 100644 tests/spec/providers/providers.websql.spec.js diff --git a/src/constants.js b/src/constants.js index 847067c..c53387a 100644 --- a/src/constants.js +++ b/src/constants.js @@ -15,6 +15,10 @@ define(function(require) { IDB_RO: 'readonly', IDB_RW: 'readwrite', + WSQL_VERSION: "1", + WSQL_SIZE: 5 * 1024 * 1024, + WSQL_DESC: "FileSystem Storage", + MODE_FILE: 'FILE', MODE_DIRECTORY: 'DIRECTORY', MODE_SYMBOLIC_LINK: 'SYMLINK', diff --git a/src/providers/providers.js b/src/providers/providers.js index ba8b58f..960679f 100644 --- a/src/providers/providers.js +++ b/src/providers/providers.js @@ -1,7 +1,31 @@ define(function(require) { + + var IndexedDB = require('src/providers/indexeddb'); + var WebSQL = require('src/providers/websql'); + var Memory = require('src/providers/memory'); + return { - IndexedDB: require('src/providers/indexeddb'), - Memory: require('src/providers/memory'), - Default: require('src/providers/indexeddb') + IndexedDB: IndexedDB, + WebSQL: WebSQL, + Memory: Memory, + Default: IndexedDB, + // The Legacy provider does automatic fallback checks + Legacy: (function() { + if(IndexedDB.isSupported()) { + return IndexedDB; + } + + if(WebSQL.isSupported()) { + return WebSQL; + } + + function NotSupported() { + throw "[IDBFS Error] Your browser doesn't support IndexedDB or WebSQL."; + } + NotSupported.isSupported = function() { + return false; + }; + return NotSupported; + }()) }; }); diff --git a/src/providers/websql.js b/src/providers/websql.js new file mode 100644 index 0000000..7dc5f98 --- /dev/null +++ b/src/providers/websql.js @@ -0,0 +1,130 @@ +define(function(require) { + var FILE_SYSTEM_NAME = require('src/constants').FILE_SYSTEM_NAME; + var FILE_STORE_NAME = require('src/constants').FILE_STORE_NAME; + var WSQL_VERSION = require('src/constants').WSQL_VERSION; + var WSQL_SIZE = require('src/constants').WSQL_SIZE; + var WSQL_DESC = require('src/constants').WSQL_DESC; + + function WebSQLContext(db, isReadOnly) { + var that = this; + this.getTransaction = function(callback) { + if(that.transaction) { + callback(that.transaction); + return; + } + // Either do readTransaction() (read-only) or transaction() (read/write) + db[isReadOnly ? 'transaction' : 'readTransaction'](function(transaction) { + that.transaction = transaction; + callback(transaction); + }); + }; + } + WebSQLContext.prototype.clear = function(callback) { + function onSuccess(transaction, result) { + callback(null); + } + function onError(transaction, error) { + callback(error); + } + this.getTransaction(function(transaction) { + transaction.executeSql("DELETE FROM " + FILE_STORE_NAME, + [], onSuccess, onError); + }); + }; + WebSQLContext.prototype.get = function(key, callback) { + function onSuccess(transaction, result) { + if(result.rows.length !== 1) { + callback("[WebSQLContext] Error: expected 1 row for get operation."); + return; + } + callback(null, result.rows.item(0).data); + } + function onError(transaction, error) { + callback(error); + } + this.getTransaction(function(transaction) { + transaction.executeSql("SELECT * FROM " + FILE_STORE_NAME + " WHERE id = ?", + [key], onSuccess, onError); + }); + }; + WebSQLContext.prototype.put = function(key, value, callback) { + function onSuccess(transaction, result) { + callback(null); + } + function onError(transaction, error) { + callback(error); + } + this.getTransaction(function(transaction) { + transaction.executeSql("INSERT OR REPLACE INTO " + FILE_STORE_NAME + " (id, data)", + [key, value], onSuccess, onError); + }); + }; + WebSQLContext.prototype.delete = function(key, callback) { + function onSuccess(transaction, result) { + callback(null); + } + function onError(transaction, error) { + callback(error); + } + this.getTransaction(function(transaction) { + transaction.executeSql("DELETE FROM " + FILE_STORE_NAME + " WHERE id = ?", + [key], onSuccess, onError); + }); + }; + + + function WebSQL(name) { + this.name = name || FILE_SYSTEM_NAME; + this.db = null; + } + WebSQL.isSupported = function() { + return !!window.openDatabase; + }; + + WebSQL.prototype.open = function(callback) { + var that = this; + + // Bail if we already have a db open + if(that.db) { + callback(null, false); + return; + } + + var db = window.openDatabase(that.name, WSQL_VERSION, WSQL_DESC, WSQL_SIZE); + if(!db) { + callback("[WebSQL] Unable to open database."); + return; + } + that.db = db; + + function onError(transaction, error) { + callback(error); + } + function onSuccess(transaction, result) { + function gotCount(transaction, result) { + var firstAccess = result.rows.item(0).count > 0; + callback(null, firstAccess); + } + function onError(transaction, error) { + callback(error); + } + // Keep track of whether we're accessing this db for the first time + // and therefore needs to get formatted. + transaction.executeSql("SELECT COUNT(id) AS count FROM " + FILE_STORE_NAME, + [], gotCount, onError); + } + + db.transaction(function(transaction) { + transaction.executeSql("CREATE TABLE IF NOT EXISTS " + FILE_STORE_NAME + " (id unique, data)", + [], onSuccess, onError); + }); + }; + WebSQL.prototype.getReadOnlyContext = function() { + return new WebSQLContext(this.db, true); + }; + WebSQL.prototype.getReadWriteContext = function() { + return new WebSQLContext(this.db, false); + }; + + return WebSQL; +}); diff --git a/tests/spec/providers/providers.spec.js b/tests/spec/providers/providers.spec.js index d2dcc50..fc007a7 100644 --- a/tests/spec/providers/providers.spec.js +++ b/tests/spec/providers/providers.spec.js @@ -8,6 +8,10 @@ define(["IDBFS"], function(IDBFS) { expect(typeof IDBFS.FileSystem.providers.IndexedDB).toEqual('function'); }); + it("has WebSQL constructor", function() { + expect(typeof IDBFS.FileSystem.providers.WebSQL).toEqual('function'); + }); + it("has Memory constructor", function() { expect(typeof IDBFS.FileSystem.providers.Memory).toEqual('function'); }); @@ -15,5 +19,9 @@ define(["IDBFS"], function(IDBFS) { it("has a Default constructor", function() { expect(typeof IDBFS.FileSystem.providers.Default).toEqual('function'); }); + + it("has Legacy constructor", function() { + expect(typeof IDBFS.FileSystem.providers.Legacy).toEqual('function'); + }); }); }); diff --git a/tests/spec/providers/providers.websql.spec.js b/tests/spec/providers/providers.websql.spec.js new file mode 100644 index 0000000..ce5fbbc --- /dev/null +++ b/tests/spec/providers/providers.websql.spec.js @@ -0,0 +1,208 @@ +define(["IDBFS"], function(IDBFS) { + + var WEBSQL_NAME = "websql-test-db"; + + function wipeDB() { + var provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); + provider.open(function(err, firstAccess) { + if(err) { + console.error("Problem clearing WebSQL db: " + err); + return; + } + var context = provider.getReadWriteContext(); + context.clear(function(err) { + console.error("Problem clearing WebSQL db: " + err); + }); + }); + } + + describe("IDBFS.FileSystem.providers.WebSQL", function() { + it("is supported -- if it isn't, none of these tests can run.", function() { + expect(IDBFS.FileSystem.providers.WebSQL.isSupported()).toEqual(true); + }); + + it("has open, getReadOnlyContext, and getReadWriteContext instance methods", function() { + var webSQLProvider = new IDBFS.FileSystem.providers.WebSQL(); + expect(typeof webSQLProvider.open).toEqual('function'); + expect(typeof webSQLProvider.getReadOnlyContext).toEqual('function'); + expect(typeof webSQLProvider.getReadWriteContext).toEqual('function'); + }); + + describe("open an WebSQL provider", function() { + beforeEach(function() { + wipeDB(); + }); + + afterEach(function() { + wipeDB(); + }); + + it("should open a new WebSQL database", function() { + var complete = false; + var _error, _result; + + var provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); + provider.open(function(err, firstAccess) { + _error = err; + _result = firstAccess; + complete = true; + }); + + waitsFor(function() { + return complete; + }, 'test to complete', DEFAULT_TIMEOUT); + + runs(function() { + expect(_error).toEqual(null); + expect(_result).toEqual(true); + }); + }); + }); + + describe("Read/Write operations on an WebSQL provider", function() { + beforeEach(function() { + wipeDB(); + }); + + afterEach(function() { + wipeDB(); + }); + + it("should allow put() and get()", function() { + var complete = false; + var _error, _result; + + var provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); + provider.open(function(err, firstAccess) { + _error = err; + + var context = provider.getReadWriteContext(); + context.put("key", "value", function(err, result) { + _error = _error || err; + context.get("key", function(err, result) { + _error = _error || err; + _result = result; + + complete = true; + }); + }); + }); + + waitsFor(function() { + return complete; + }, 'test to complete', DEFAULT_TIMEOUT); + + runs(function() { + expect(_error).toEqual(null); + expect(_result).toEqual("value"); + }); + }); + + it("should allow delete()", function() { + var complete = false; + var _error, _result; + + var provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); + provider.open(function(err, firstAccess) { + _error = err; + + var context = provider.getReadWriteContext(); + context.put("key", "value", function(err, result) { + _error = _error || err; + context.delete("key", function(err, result) { + _error = _error || err; + context.get("key", function(err, result) { + _error = _error || err; + _result = result; + + complete = true; + }); + }); + }); + }); + + waitsFor(function() { + return complete; + }, 'test to complete', DEFAULT_TIMEOUT); + + runs(function() { + expect(_error).toEqual(null); + expect(_result).toEqual(null); + }); + }); + + it("should allow clear()", function() { + var complete = false; + var _error, _result1, _result2; + + var provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); + provider.open(function(err, firstAccess) { + _error = err; + + var context = provider.getReadWriteContext(); + context.put("key1", "value1", function(err, result) { + _error = _error || err; + context.put("key2", "value2", function(err, result) { + _error = _error || err; + + context.clear(function(err) { + _error = _error || err; + + context.get("key1", function(err, result) { + _error = _error || err; + _result1 = result; + + context.get("key2", function(err, result) { + _error = _error || err; + _result2 = result; + + complete = true; + }); + }); + }); + }); + }); + }); + + waitsFor(function() { + return complete; + }, 'test to complete', DEFAULT_TIMEOUT); + + runs(function() { + expect(_error).toEqual(null); + expect(_result1).toEqual(null); + expect(_result2).toEqual(null); + }); + }); + + it("should fail when trying to write on ReadOnlyContext", function() { + var complete = false; + var _error, _result; + + var provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); + provider.open(function(err, firstAccess) { + _error = err; + + var context = provider.getReadOnlyContext(); + context.put("key1", "value1", function(err, result) { + _error = _error || err; + _result = result; + + complete = true; + }); + }); + + waitsFor(function() { + return complete; + }, 'test to complete', DEFAULT_TIMEOUT); + + runs(function() { + expect(_error).toBeDefined(); + expect(_result).toEqual(null); + }); + }); + }); + + }); + +}); diff --git a/tests/test-manifest.js b/tests/test-manifest.js index 880bf9a..47e9d84 100644 --- a/tests/test-manifest.js +++ b/tests/test-manifest.js @@ -32,6 +32,7 @@ define([ "spec/providers/providers.spec", "spec/providers/providers.memory.spec", "spec/providers/providers.indexeddb.spec", + "spec/providers/providers.websql.spec", // Ported node.js tests (filenames match names in https://github.com/joyent/node/tree/master/test) "spec/node-js/simple/test-fs-mkdir", From c92d3a6c5f1bb5cf62e6c99a8c134ed88de43600 Mon Sep 17 00:00:00 2001 From: "David Humphrey (:humph) david.humphrey@senecacollege.ca" Date: Wed, 27 Nov 2013 17:12:46 -0500 Subject: [PATCH 2/4] Passing 1/4 failing tests now --- src/providers/websql.js | 2 +- tests/spec/providers/providers.websql.spec.js | 42 +++++++------------ 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/providers/websql.js b/src/providers/websql.js index 7dc5f98..4b2e406 100644 --- a/src/providers/websql.js +++ b/src/providers/websql.js @@ -55,7 +55,7 @@ define(function(require) { callback(error); } this.getTransaction(function(transaction) { - transaction.executeSql("INSERT OR REPLACE INTO " + FILE_STORE_NAME + " (id, data)", + transaction.executeSql("INSERT OR REPLACE INTO " + FILE_STORE_NAME + " (id, data) VALUES (?, ?)", [key, value], onSuccess, onError); }); }; diff --git a/tests/spec/providers/providers.websql.spec.js b/tests/spec/providers/providers.websql.spec.js index ce5fbbc..82e8bb6 100644 --- a/tests/spec/providers/providers.websql.spec.js +++ b/tests/spec/providers/providers.websql.spec.js @@ -2,20 +2,18 @@ define(["IDBFS"], function(IDBFS) { var WEBSQL_NAME = "websql-test-db"; - function wipeDB() { - var provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); - provider.open(function(err, firstAccess) { - if(err) { - console.error("Problem clearing WebSQL db: " + err); - return; - } - var context = provider.getReadWriteContext(); - context.clear(function(err) { - console.error("Problem clearing WebSQL db: " + err); - }); + function wipeDB(provider) { + var context = provider.getReadWriteContext(); + context.clear(function(err) { + console.error("Problem clearing WebSQL db: " + err); }); } + if(!IDBFS.FileSystem.providers.WebSQL.isSupported()) { + console.log("Skipping IDBFS.FileSystem.providers.WebSQL tests, since WebSQL isn't supported."); + return; + } + describe("IDBFS.FileSystem.providers.WebSQL", function() { it("is supported -- if it isn't, none of these tests can run.", function() { expect(IDBFS.FileSystem.providers.WebSQL.isSupported()).toEqual(true); @@ -29,19 +27,15 @@ define(["IDBFS"], function(IDBFS) { }); describe("open an WebSQL provider", function() { - beforeEach(function() { - wipeDB(); - }); - afterEach(function() { - wipeDB(); + wipeDB(this.provider); }); it("should open a new WebSQL database", function() { var complete = false; var _error, _result; - var provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); + var provider = this.provider = this.provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); provider.open(function(err, firstAccess) { _error = err; _result = firstAccess; @@ -60,19 +54,15 @@ define(["IDBFS"], function(IDBFS) { }); describe("Read/Write operations on an WebSQL provider", function() { - beforeEach(function() { - wipeDB(); - }); - afterEach(function() { - wipeDB(); + wipeDB(this.provider); }); it("should allow put() and get()", function() { var complete = false; var _error, _result; - var provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); + var provider = this.provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); provider.open(function(err, firstAccess) { _error = err; @@ -102,7 +92,7 @@ define(["IDBFS"], function(IDBFS) { var complete = false; var _error, _result; - var provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); + var provider = this.provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); provider.open(function(err, firstAccess) { _error = err; @@ -135,7 +125,7 @@ define(["IDBFS"], function(IDBFS) { var complete = false; var _error, _result1, _result2; - var provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); + var provider = this.provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); provider.open(function(err, firstAccess) { _error = err; @@ -179,7 +169,7 @@ define(["IDBFS"], function(IDBFS) { var complete = false; var _error, _result; - var provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); + var provider = this.provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); provider.open(function(err, firstAccess) { _error = err; From d5db4c51b95dea3d8ed22dfc2f44640ecf5c920d Mon Sep 17 00:00:00 2001 From: "David Humphrey (:humph) david.humphrey@senecacollege.ca" Date: Wed, 27 Nov 2013 19:35:52 -0500 Subject: [PATCH 3/4] Down to 1 test failure --- src/providers/websql.js | 11 ++++++----- tests/spec/providers/providers.websql.spec.js | 6 ++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/providers/websql.js b/src/providers/websql.js index 4b2e406..d81d2b1 100644 --- a/src/providers/websql.js +++ b/src/providers/websql.js @@ -13,7 +13,7 @@ define(function(require) { return; } // Either do readTransaction() (read-only) or transaction() (read/write) - db[isReadOnly ? 'transaction' : 'readTransaction'](function(transaction) { + db[isReadOnly ? 'readTransaction' : 'transaction'](function(transaction) { that.transaction = transaction; callback(transaction); }); @@ -33,8 +33,9 @@ define(function(require) { }; WebSQLContext.prototype.get = function(key, callback) { function onSuccess(transaction, result) { - if(result.rows.length !== 1) { - callback("[WebSQLContext] Error: expected 1 row for get operation."); + if(result.rows.length === 0) { + // Key not found, return null + callback(null, null); return; } callback(null, result.rows.item(0).data); @@ -43,7 +44,7 @@ define(function(require) { callback(error); } this.getTransaction(function(transaction) { - transaction.executeSql("SELECT * FROM " + FILE_STORE_NAME + " WHERE id = ?", + transaction.executeSql("SELECT data FROM " + FILE_STORE_NAME + " WHERE id = ? LIMIT 1", [key], onSuccess, onError); }); }; @@ -110,7 +111,7 @@ define(function(require) { } // Keep track of whether we're accessing this db for the first time // and therefore needs to get formatted. - transaction.executeSql("SELECT COUNT(id) AS count FROM " + FILE_STORE_NAME, + transaction.executeSql("SELECT COUNT(id) AS count FROM " + FILE_STORE_NAME + ";", [], gotCount, onError); } diff --git a/tests/spec/providers/providers.websql.spec.js b/tests/spec/providers/providers.websql.spec.js index 82e8bb6..d3dcd80 100644 --- a/tests/spec/providers/providers.websql.spec.js +++ b/tests/spec/providers/providers.websql.spec.js @@ -5,7 +5,9 @@ define(["IDBFS"], function(IDBFS) { function wipeDB(provider) { var context = provider.getReadWriteContext(); context.clear(function(err) { - console.error("Problem clearing WebSQL db: " + err); + if(err) { + console.error("Problem clearing WebSQL db: [" + err.code + "] - " + err.message); + } }); } @@ -35,7 +37,7 @@ define(["IDBFS"], function(IDBFS) { var complete = false; var _error, _result; - var provider = this.provider = this.provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); + var provider = this.provider = new IDBFS.FileSystem.providers.WebSQL(WEBSQL_NAME); provider.open(function(err, firstAccess) { _error = err; _result = firstAccess; From 6ebbdc5915237c5b6a8d2960aedfdb612f8b1a38 Mon Sep 17 00:00:00 2001 From: "David Humphrey (:humph) david.humphrey@senecacollege.ca" Date: Fri, 29 Nov 2013 10:29:04 -0500 Subject: [PATCH 4/4] Finish WebSQL implementation, all tests passing. Fixes #21. --- gruntfile.js | 3 +-- src/providers/indexeddb.js | 1 - src/providers/providers.js | 4 ++-- src/providers/websql.js | 22 ++++++++++------------ tests/spec/providers/providers.spec.js | 4 ++-- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/gruntfile.js b/gruntfile.js index d2fa564..e08ab82 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -23,8 +23,7 @@ module.exports = function(grunt) { 'src/error.js', 'src/fs.js', 'src/shared.js', - 'src/providers/**/*.js', - 'src/filesystems-manager.js' + 'src/providers/**/*.js' ] }, diff --git a/src/providers/indexeddb.js b/src/providers/indexeddb.js index 540ec9c..9de9e9a 100644 --- a/src/providers/indexeddb.js +++ b/src/providers/indexeddb.js @@ -7,7 +7,6 @@ define(function(require) { window.webkitIndexedDB || window.msIndexedDB; - var IDB_RW = require('src/constants').IDB_RW; var IDB_RO = require('src/constants').IDB_RO; diff --git a/src/providers/providers.js b/src/providers/providers.js index 960679f..edc6c53 100644 --- a/src/providers/providers.js +++ b/src/providers/providers.js @@ -9,8 +9,8 @@ define(function(require) { WebSQL: WebSQL, Memory: Memory, Default: IndexedDB, - // The Legacy provider does automatic fallback checks - Legacy: (function() { + // The Fallback provider does automatic fallback checks + Fallback: (function() { if(IndexedDB.isSupported()) { return IndexedDB; } diff --git a/src/providers/websql.js b/src/providers/websql.js index d81d2b1..721b775 100644 --- a/src/providers/websql.js +++ b/src/providers/websql.js @@ -20,12 +20,12 @@ define(function(require) { }; } WebSQLContext.prototype.clear = function(callback) { - function onSuccess(transaction, result) { - callback(null); - } function onError(transaction, error) { callback(error); } + function onSuccess(transaction, result) { + callback(null); + } this.getTransaction(function(transaction) { transaction.executeSql("DELETE FROM " + FILE_STORE_NAME, [], onSuccess, onError); @@ -33,18 +33,15 @@ define(function(require) { }; WebSQLContext.prototype.get = function(key, callback) { function onSuccess(transaction, result) { - if(result.rows.length === 0) { - // Key not found, return null - callback(null, null); - return; - } - callback(null, result.rows.item(0).data); + // If the key isn't found, return null + var value = result.rows.length === 0 ? null : result.rows.item(0).data; + callback(null, value); } function onError(transaction, error) { callback(error); } this.getTransaction(function(transaction) { - transaction.executeSql("SELECT data FROM " + FILE_STORE_NAME + " WHERE id = ? LIMIT 1", + transaction.executeSql("SELECT data FROM " + FILE_STORE_NAME + " WHERE id = ?", [key], onSuccess, onError); }); }; @@ -96,14 +93,15 @@ define(function(require) { callback("[WebSQL] Unable to open database."); return; } - that.db = db; function onError(transaction, error) { callback(error); } function onSuccess(transaction, result) { + that.db = db; + function gotCount(transaction, result) { - var firstAccess = result.rows.item(0).count > 0; + var firstAccess = result.rows.item(0).count === 0; callback(null, firstAccess); } function onError(transaction, error) { diff --git a/tests/spec/providers/providers.spec.js b/tests/spec/providers/providers.spec.js index fc007a7..fbdde86 100644 --- a/tests/spec/providers/providers.spec.js +++ b/tests/spec/providers/providers.spec.js @@ -20,8 +20,8 @@ define(["IDBFS"], function(IDBFS) { expect(typeof IDBFS.FileSystem.providers.Default).toEqual('function'); }); - it("has Legacy constructor", function() { - expect(typeof IDBFS.FileSystem.providers.Legacy).toEqual('function'); + it("has Fallback constructor", function() { + expect(typeof IDBFS.FileSystem.providers.Fallback).toEqual('function'); }); }); });