blob: 29252d29749a490c5d8a591c896ce9f4a8958066 [file] [log] [blame]
/* global PouchDB */
/* jshint -W079 */
'use strict';
var path = require('path');
var testUtils = {};
function uniq(list) {
var map = {};
list.forEach(function (item) {
map[item] = true;
});
return Object.keys(map);
}
testUtils.isCouchMaster = function () {
return 'SERVER' in testUtils.params() &&
testUtils.params().SERVER === 'couchdb-master';
};
testUtils.isIE = function () {
var ua = (typeof navigator !== 'undefined' && navigator.userAgent) ?
navigator.userAgent.toLowerCase() : '';
var isIE = ua.indexOf('msie') !== -1;
var isTrident = ua.indexOf('trident') !== -1;
var isEdge = ua.indexOf('edge') !== -1;
return (isIE || isTrident || isEdge);
};
testUtils.params = function () {
if (typeof process !== 'undefined' && !process.browser) {
return process.env;
}
var paramStr = document.location.search.slice(1);
return paramStr.split('&').reduce(function (acc, val) {
if (!val) {
return acc;
}
var tmp = val.split('=');
acc[tmp[0]] = decodeURIComponent(tmp[1]) || true;
return acc;
}, {});
};
testUtils.couchHost = function () {
if (typeof window !== 'undefined' && window.cordova) {
// magic route to localhost on android emulator
return 'http://10.0.2.2:5984';
}
if (typeof window !== 'undefined' && window.COUCH_HOST) {
return window.COUCH_HOST;
}
if (typeof process !== 'undefined' && process.env.COUCH_HOST) {
return process.env.COUCH_HOST;
}
if ('couchHost' in testUtils.params()) {
// Remove trailing slash from url if the user defines one
return testUtils.params().couchHost.replace(/\/$/, '');
}
return 'http://localhost:5984';
};
testUtils.readBlob = function (blob, callback) {
if (typeof process !== 'undefined' && !process.browser) {
callback(blob.toString('binary'));
} else {
var reader = new FileReader();
reader.onloadend = function () {
var binary = "";
var bytes = new Uint8Array(this.result || '');
var length = bytes.byteLength;
for (var i = 0; i < length; i++) {
binary += String.fromCharCode(bytes[i]);
}
callback(binary);
};
reader.readAsArrayBuffer(blob);
}
};
testUtils.readBlobPromise = function (blob) {
return new testUtils.Promise(function (resolve) {
testUtils.readBlob(blob, resolve);
});
};
testUtils.base64Blob = function (blob, callback) {
if (typeof process !== 'undefined' && !process.browser) {
callback(blob.toString('base64'));
} else {
testUtils.readBlob(blob, function (binary) {
callback(testUtils.btoa(binary));
});
}
};
// Prefix http adapter database names with their host and
// node adapter ones with a db location
testUtils.adapterUrl = function (adapter, name) {
// CouchDB master has problems with cycling databases rapidly
// so give tests seperate names
name += '_' + Date.now();
if (adapter === 'http') {
return testUtils.couchHost() + '/' + name;
}
return name;
};
// Delete specified databases
testUtils.cleanup = function (dbs, done) {
dbs = uniq(dbs);
var num = dbs.length;
var finished = function () {
if (--num === 0) {
done();
}
};
dbs.forEach(function (db) {
new PouchDB(db).destroy(finished, finished);
});
};
// Put doc after prevRev (so that doc is a child of prevDoc
// in rev_tree). Doc must have _rev. If prevRev is not specified
// just insert doc with correct _rev (new_edits=false!)
testUtils.putAfter = function (db, doc, prevRev, callback) {
var newDoc = testUtils.assign({}, doc);
if (!prevRev) {
db.put(newDoc, { new_edits: false }, callback);
return;
}
newDoc._revisions = {
start: +newDoc._rev.split('-')[0],
ids: [
newDoc._rev.split('-')[1],
prevRev.split('-')[1]
]
};
db.put(newDoc, { new_edits: false }, callback);
};
// docs will be inserted one after another
// starting from root
testUtils.putBranch = function (db, docs, callback) {
function insert(i) {
var doc = docs[i];
var prev = i > 0 ? docs[i - 1]._rev : null;
function next() {
if (i < docs.length - 1) {
insert(i + 1);
} else {
callback();
}
}
db.get(doc._id, { rev: doc._rev }, function (err) {
if (err) {
testUtils.putAfter(db, docs[i], prev, function () {
next();
});
} else {
next();
}
});
}
insert(0);
};
testUtils.putTree = function (db, tree, callback) {
function insert(i) {
var branch = tree[i];
testUtils.putBranch(db, branch, function () {
if (i < tree.length - 1) {
insert(i + 1);
} else {
callback();
}
});
}
insert(0);
};
testUtils.isCouchDB = function (cb) {
PouchDB.fetch(testUtils.couchHost(), {}).then(function (response) {
return response.json();
}).then(function (res) {
cb('couchdb' in res || 'express-pouchdb' in res);
});
};
testUtils.writeDocs = function (db, docs, callback, res) {
if (!res) {
res = [];
}
if (!docs.length) {
return callback(null, res);
}
var doc = docs.shift();
db.put(doc, function (err, info) {
res.push(info);
testUtils.writeDocs(db, docs, callback, res);
});
};
// Borrowed from: http://stackoverflow.com/a/840849
testUtils.eliminateDuplicates = function (arr) {
var i, element, len = arr.length, out = [], obj = {};
for (i = 0; i < len; i++) {
obj[arr[i]] = 0;
}
for (element in obj) {
if (obj.hasOwnProperty(element)) {
out.push(element);
}
}
return out;
};
// Promise finally util similar to Q.finally
testUtils.fin = function (promise, cb) {
return promise.then(function (res) {
var promise2 = cb();
if (typeof promise2.then === 'function') {
return promise2.then(function () {
return res;
});
}
return res;
}, function (reason) {
var promise2 = cb();
if (typeof promise2.then === 'function') {
return promise2.then(function () {
throw reason;
});
}
throw reason;
});
};
testUtils.promisify = function (fun, context) {
return function () {
var args = [];
for (var i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
return new testUtils.Promise(function (resolve, reject) {
args.push(function (err, res) {
if (err) {
return reject(err);
}
return resolve(res);
});
fun.apply(context, args);
});
};
};
// We need to use pouchdb-for-coverage here to ensure that e.g pouchdb-utils
// and pouchdb-ajax don't get pulled in, because then our coverage tests
// would complain that we're not using the "whole" thing.
var PouchForCoverage = require('../../packages/node_modules/pouchdb-for-coverage');
var pouchUtils = PouchForCoverage.utils;
testUtils.binaryStringToBlob = pouchUtils.binaryStringToBlobOrBuffer;
testUtils.btoa = pouchUtils.btoa;
testUtils.atob = pouchUtils.atob;
testUtils.Promise = pouchUtils.Promise;
testUtils.ajax = PouchForCoverage.ajax;
testUtils.uuid = pouchUtils.uuid;
testUtils.rev = pouchUtils.rev;
testUtils.parseUri = pouchUtils.parseUri;
testUtils.errors = PouchForCoverage.Errors;
testUtils.assign = pouchUtils.assign;
testUtils.generateReplicationId = pouchUtils.generateReplicationId;
testUtils.makeBlob = function (data, type) {
if (typeof process !== 'undefined' && !process.browser) {
// "global.Buffer" is to avoid Browserify pulling this in
return new global.Buffer(data, 'binary');
} else {
return pouchUtils.blob([data], {
type: (type || 'text/plain')
});
}
};
testUtils.getUnHandledRejectionEventName = function () {
return typeof window !== 'undefined' ? 'unhandledrejection' :
'unhandledRejection';
};
testUtils.addGlobalEventListener = function (eventName, listener) {
// The window test has to go first because the process test will pass
// in the browser's test environment
if (typeof window !== 'undefined' && window.addEventListener) {
return window.addEventListener(eventName, listener);
}
if (typeof process !== 'undefined') {
return process.on(eventName, listener);
}
return null;
};
testUtils.addUnhandledRejectionListener = function (listener) {
return testUtils.addGlobalEventListener(
testUtils.getUnHandledRejectionEventName(), listener);
};
testUtils.removeGlobalEventListener = function (eventName, listener) {
if (typeof process !== 'undefined') {
return process.removeListener(eventName, listener);
}
if (typeof window !== 'undefined' && window.removeEventListener) {
return window.removeEventListener(eventName, listener);
}
return null;
};
testUtils.removeUnhandledRejectionListener = function (listener) {
return testUtils.removeGlobalEventListener(
testUtils.getUnHandledRejectionEventName(), listener);
};
testUtils.sortById = function (a, b) {
return a._id < b._id ? -1 : 1;
};
if (typeof process !== 'undefined' && !process.browser) {
if (process.env.COVERAGE) {
global.PouchDB = require('../../packages/node_modules/pouchdb-for-coverage');
} else { // no need to check for coverage
// string addition is to avoid browserify pulling in whole thing
global.PouchDB = require('../../packages/' + 'node_modules/pouchdb');
}
if (process.env.AUTO_COMPACTION) {
// test autocompaction
global.PouchDB = global.PouchDB.defaults({
auto_compaction: true,
prefix: './tmp/_pouch_'
});
} else if (process.env.ADAPTER === 'websql') {
// test WebSQL in Node
// (the two strings are just to fool Browserify because sqlite3 fails
// in Node 0.11-0.12)
global.PouchDB.plugin(require('../../packages/node_modules/' +
'pouchdb-adapter-node-websql'));
global.PouchDB.preferredAdapters = ['websql', 'leveldb'];
global.PouchDB = global.PouchDB.defaults({
prefix: path.resolve('./tmp/_pouch_')
});
} else if (process.env.ADAPTER === 'memory') {
global.PouchDB.plugin(require('../../packages/node_modules/' +
'pouchdb-adapter-memory'));
global.PouchDB.preferredAdapters = ['memory', 'leveldb'];
} else {
// test regular leveldown in node
global.PouchDB = global.PouchDB.defaults({
prefix: path.resolve('./tmp/_pouch_')
});
}
require('mkdirp').sync('./tmp');
module.exports = testUtils;
} else {
window.testUtils = testUtils;
}