| Index: qunit/tests/ioIndexedDB.js | 
| diff --git a/qunit/tests/ioIndexedDB.js b/qunit/tests/ioIndexedDB.js | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..58defadc4d6e9624370d50d85f7c80067e5f65fb | 
| --- /dev/null | 
| +++ b/qunit/tests/ioIndexedDB.js | 
| @@ -0,0 +1,227 @@ | 
| +"use strict"; | 
| + | 
| +{ | 
| + const {IO} = require("io"); | 
| + | 
| + const testFileNames = { | 
| + testData: "testData", | 
| + simpleCheck: "simpleCheck", | 
| + write: "writeCheck", | 
| + read: "readCheck", | 
| + rename: "renameCheck" | 
| + }; | 
| + const testData = { | 
| + fileName: "file:" + testFileNames.testData, | 
| + content: [1, 2, 3], | 
| + lastModified: Date.now() | 
| + }; | 
| + | 
| + QUnit.module("IO tests", { | 
| + beforeEach() | 
| + { | 
| + return prePopulateStorage(); | 
| + }, | 
| + afterEach() | 
| + { | 
| + return clearStorage(); | 
| + } | 
| + }); | 
| + | 
| + test("statFile", assert => | 
| + { | 
| + const noFileMsg = "returns correct value if file doesn't exist"; | 
| + const fileExistsMsg = "returns correct value if file exists"; | 
| + | 
| + ok(IO.statFile(testFileNames.simpleCheck) instanceof Promise, | 
| + "returns a promise"); | 
| + | 
| + asyncReadHelper( | 
| + IO.statFile, | 
| + testFileNames.testData, | 
| + {exists: true, lastModified: testData.lastModified}, | 
| + fileExistsMsg, | 
| + assert); | 
| + | 
| + asyncReadHelper( | 
| + IO.statFile, | 
| + testFileNames.simpleCheck, | 
| + {exists: false}, | 
| + noFileMsg, | 
| + assert); | 
| + }); | 
| + | 
| + test("writeToFile", assert => | 
| + { | 
| + ok(IO.writeToFile(testFileNames.simpleCheck, ["test"]) instanceof Promise, | 
| + "returns a promise"); | 
| + | 
| + writesCorrectValue(assert); | 
| + }); | 
| + | 
| + function writesCorrectValue(assert) | 
| + { | 
| + const writeCheck = { | 
| + fileName: "file:writeCheck", | 
| + content: [1, 2, 3], | 
| + lastModified: Date.now() | 
| + }; | 
| + let done = assert.async(); | 
| + | 
| + IO.writeToFile(testFileNames.write, writeCheck.content) | 
| + .then(() => readFromStorage(writeCheck.fileName)) | 
| + .then(result => | 
| + { | 
| + deepEqual( | 
| + Object.keys(writeCheck), | 
| + Object.keys(result), | 
| + "data is written in the correct format"); | 
| + | 
| + deepEqual( | 
| + writeCheck.content, | 
| + result.content, | 
| + "data has the correct content"); | 
| + done(); | 
| + }); | 
| + } | 
| + | 
| + test("readFromFile", assert => | 
| + { | 
| + const noFileMsg = "returns correct value if file doesn't exist"; | 
| + | 
| + ok(IO.readFromFile(testFileNames.simpleCheck) instanceof Promise, | 
| + "returns a promise"); | 
| + | 
| + asyncReadHelper( | 
| + IO.readFromFile, | 
| + testFileNames.read, | 
| + {type: "NoSuchFile"}, | 
| + noFileMsg, | 
| + assert | 
| + ); | 
| + | 
| + callsListeners(assert); | 
| + }); | 
| + | 
| + function callsListeners(assert) | 
| + { | 
| + let done = assert.async(); | 
| + let called = []; | 
| + | 
| + IO.readFromFile(testFileNames.testData, (entry) => called.push(entry)) | 
| + .then(() => | 
| + { | 
| + deepEqual( | 
| + called, | 
| + testData.content, | 
| + "calls listeners with the correct values"); | 
| + done(); | 
| + }); | 
| + } | 
| + | 
| + test("renameFile", assert => | 
| + { | 
| + ok(IO.renameFile(testFileNames.simpleCheck) instanceof Promise, | 
| + "returns a promise"); | 
| + | 
| + checkRename(assert); | 
| + }); | 
| + | 
| + function checkRename(assert) | 
| + { | 
| + let done = assert.async(); | 
| + const expected = { | 
| + fileName: "file:" + testFileNames.rename, | 
| + content: testData.content, | 
| + lastModified: testData.lastModified | 
| + }; | 
| + | 
| + IO.renameFile(testFileNames.testData, testFileNames.rename) | 
| + .then(() => readFromStorage("file:" + testFileNames.rename)) | 
| + .then(result => | 
| + { | 
| + deepEqual(result, expected, "overrites file"); | 
| + done(); | 
| + }); | 
| + } | 
| + | 
| + function asyncReadHelper(method, fileName, expectedValue, description, assert) | 
| + { | 
| + let done = assert.async(); | 
| + method(fileName) | 
| + .then(result => | 
| + { | 
| + deepEqual(result, expectedValue, description); | 
| + done(); | 
| + }) | 
| + .catch(error => | 
| + { | 
| + deepEqual(error, expectedValue, description); | 
| + done(); | 
| + }); | 
| + } | 
| + | 
| + function readFromStorage(fileName) | 
| + { | 
| + return new Promise(resolve => | 
| + { | 
| + let db; | 
| + let req = indexedDB.open("adblockplus", 1); | 
| + req.onsuccess = (event) => | 
| + { | 
| + db = event.currentTarget.result; | 
| + let store = db | 
| + .transaction(["file"], "readwrite") | 
| + .objectStore("file"); | 
| + | 
| + store.get(fileName).onsuccess = (evt => | 
| + resolve(evt.currentTarget.result) | 
| + ); | 
| + }; | 
| + }); | 
| + } | 
| + | 
| + function prePopulateStorage() | 
| + { | 
| + return new Promise(resolve => | 
| + { | 
| + let db; | 
| + let req = indexedDB.open("adblockplus", 1); | 
| + | 
| + req.onsuccess = (event) => | 
| + { | 
| + db = event.currentTarget.result; | 
| + let store = db | 
| + .transaction(["file"], "readwrite") | 
| + .objectStore("file"); | 
| + | 
| + store.put(testData).onsuccess = resolve; | 
| + }; | 
| + }); | 
| + } | 
| + | 
| + function clearStorage() | 
| + { | 
| + return new Promise(resolve => | 
| + { | 
| + let db; | 
| + let req = indexedDB.open("adblockplus", 1); | 
| + | 
| + req.onsuccess = (event) => | 
| + { | 
| + db = event.currentTarget.result; | 
| + let files = Object.keys(testFileNames) | 
| + .map(fileName => new Promise((resolveFile, reject) => | 
| + { | 
| + let store = db | 
| + .transaction(["file"], "readwrite") | 
| + .objectStore("file"); | 
| + | 
| + store.delete("file:" + fileName).onsuccess = resolveFile; | 
| + })); | 
| + | 
| + Promise.all(files).then(resolve); | 
| + }; | 
| + }); | 
| + } | 
| +} | 
| + |