Source: fakes.js

/**
 * A set of functions which help you to unit test code that uses firebase.database
 * @module firebase-dbfakes
 */
(function(){
	'use strict'
	const	_ = require('lodash')

	const defaults = {
		key: 'someKey',
		root: 'https://sample-app.firebaseio.com',
	}

	function reference(options){
		const	context = _.assign({}, defaults, options)
		function* defaultPushGenerator(){let n = 1; yield `${context.key}${n}`}

		let	pushGenerator = (context.pushGenerator || defaultPushGenerator)(),
			parentRef
		return {
			//properties
			key: context.key,
			get parent () {
				if (!parentRef)
					parentRef = context.parent || reference()
				return parentRef
			},
			root: context.root,

			//functions
			child: function(key){
						return reference({
											key: key,
											parent: this
										})
					},
			endAt: function(){ return this },
			equalTo: function(){ return this },
			limitToFirst: function(){ return this },
			limitToLast: function(){ return this },
			off: () => {},
			on: () => {},
			once: () => resolveSnapshot(undefined),
			orderByChild: function(){ return this },
			orderByKey: function(){ return this },
			orderByPriority: function(){ return this },
			orderByValue: function(){ return this },
			push: function() { return reference({
				key: pushGenerator.next().value,
				parent: this
			}) },
			remove: () => Promise.resolve(),
			set: () => Promise.resolve(),
			setPriority: () => Promise.resolve(),
			setWithPriority: () => Promise.resolve(),
			startAt: function(){ return this },
			toString: function(){ return `path/${this.key}`},
			transaction: () => Promise.resolve({
													committed: true,
													snapshot: snapshot(undefined)
												}),
			update: () => Promise.resolve(),
		}
	}

	function snapshot(val, key){
		const	k = key || defaults.key,
				r = reference({key: k})
		return {
			exists: () => val !== undefined,
			key: k,
			ref: r,
			val: () => val || null,
		}
	}

	function resolveSnapshot(val, key){
		return Promise.resolve(snapshot(val, key))
	}

	function memoizeSnapshot(val, key){
		return () => resolveSnapshot(val, key)
	}

	module.exports = {
		/**
		 * A helper function when setting up default fake behavior for a suite of tests. It takes boiler plate out of your test code.
		 * @param {object} val - The data contained by the snapshot. You will get this back when calling val on the result
		 * @param {string} [key] - The key you would like for the object. Most of the time you will not need to provide a key
		 * @returns {function} - A function that will call resolveSnapshot() with the supplied values
		 */
		memoizeSnapshot: memoizeSnapshot,
		/**
		 * A factory function that creates a fake DataSnapshot object.
		 * @param {object} val - The data contained by the snapshot. You will get this back when calling val on the result
		 * @param {string} [key] - The key you would like for the object. Most of the time you will not need to provide a key
		 * @returns {object} snapshot - The fake snapshot
		 * @returns {function} snapshot.exists - A predicate that reports if the snapshot holds data
		 * @returns {string} snapshot.key - The child identifier for the snapshot
		 * @returns {object} snapshot.ref - A fake firebase reference
		 * @returns {function} snapshot.val - Returns you the data that the snapshot represents
		 */
		snapshot: snapshot,
		/**
		 * A factory function that creates a fake database.reference object.
		 * @param {object} [options] - The setup and value options you would like to give the reference
		 * @param {string} [options.key] - The key you would like for the object.
		 * @param {object} [options.parent] - The parent you would like for the object.
		 * @returns {object} reference - database.reference fake
		 * @returns {string} reference.key - The location that the reference represents
		 * @returns {object} reference.parent - Another reference, ostensibly the parent of this one, but it is just another fake.
		 * @returns {string} reference.root - The base URL for the database
		 * @returns {function} reference.child - A factory function for another reference that would be below this one in a real firebase document.
		 * @returns {function} reference.endAt - Simulates a query factory function. NOTE: If you provide another implementation, you should do so with a function, not an arrow function so that this is correct. Be sure to end with returning this.
		 * @returns {function} reference.equalTo - Simulates a query factory function. NOTE: If you provide another implementation, you should do so with a function, not an arrow function so that this is correct. Be sure to end with returning this.
		 * @returns {function} reference.limitToFirst - Simulates a query factory function. NOTE: If you provide another implementation, you should do so with a function, not an arrow function so that this is correct. Be sure to end with returning this.
		 * @returns {function} reference.limitToLast - Simulates a query factory function. NOTE: If you provide another implementation, you should do so with a function, not an arrow function so that this is correct. Be sure to end with returning this.
		 * @returns {function} reference.off - The would be disconnect for an 'on' callback. For the fake, it's a no-op.
		 * @returns {function} reference.on - The would be way to attach an event listener. The fake does not wire any data to a callback, but you can and should supply behavior that does pass data. See examples for how.
		 * @returns {function} reference.once - Returns a promise to a snapshot.
		 * @returns {function} reference.orderByChild - Simulates a query factory function. NOTE: If you provide another implementation, you should do so with a function, not an arrow function so that this is correct. Be sure to end with returning this.
		 * @returns {function} reference.orderByKey - Simulates a query factory function. NOTE: If you provide another implementation, you should do so with a function, not an arrow function so that this is correct. Be sure to end with returning this.
		 * @returns {function} reference.orderByPriority - Simulates a query factory function. NOTE: If you provide another implementation, you should do so with a function, not an arrow function so that this is correct. Be sure to end with returning this.
		 * @returns {function} reference.orderByValue - Simulates a query factory function. NOTE: If you provide another implementation, you should do so with a function, not an arrow function so that this is correct. Be sure to end with returning this.
		 * @returns {function} reference.remove - simulates removing the data at the reference. Returns a promise resolved to null
		 * @returns {function} reference.set - Simulates setting the data at the reference. Returns a promise resolved
		 * @returns {function} reference.setPriority - Simulates setting the data priority at the reference. Returns a promise resolved
		 * @returns {function} reference.setWithPriority - Simulates setting the data with a priority at the reference. Returns a promise resolved
		 * @returns {function} reference.startAt - Simulates a query factory function. NOTE: If you provide another implementation, you should do so with a function, not an arrow function so that this is correct. Be sure to end with returning this.
		 * @returns {function} reference.toString - Simulates a path sringifyer. NOTE: If you provide another implementation, you should do so with a function, not an arrow function so that this is correct and you can access the key if you need it.
		 * @returns {function} reference.transaction - Simulates a transaction.
		 * @returns {function} reference.update - This would write multiple keys to firebase.
		 */
		reference: reference,
		/**
		 * Return a promise that will resolve a snapshot from the value and key given. This is useful in modeling once()
		 * @param {object} val - The data contained by the snapshot. You will get this back when calling val on the result
		 * @param {string} [key] - The key you would like for the object. Most of the time you will not need to provide a key
		 * @returns {promise} - A snapshot in the future
		 */
		resolveSnapshot: resolveSnapshot,
	}
}())