Skip to content
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"lodash.clone": "^4.5.0",
"lodash.clonedeep": "^4.5.0",
"lodash.clonedeepwith": "^4.5.0",
"lodash.clonewith": "^4.5.0",
"lodash.compact": "^3.0.1",
"lodash.difference": "^4.5.0",
"lodash.every": "^4.6.0",
Expand All @@ -53,6 +54,7 @@
"lodash.isfunction": "^3.0.9",
"lodash.isnumber": "^3.0.3",
"lodash.isobject": "^3.0.2",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.isundefined": "^3.0.1",
"lodash.keys": "^4.2.0",
Expand Down
19 changes: 7 additions & 12 deletions src/firebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,12 @@ MockFirebase.ServerValue = {
}
};

var getServerTime, defaultClock;
getServerTime = defaultClock = function () {
return new Date().getTime();
};

MockFirebase.setClock = function (fn) {
getServerTime = fn;
utils.setServerClock(fn);
};

MockFirebase.restoreClock = function () {
getServerTime = defaultClock;
utils.restoreServerClock();
};

MockFirebase.autoId = function () {
Expand Down Expand Up @@ -449,7 +444,7 @@ MockFirebase.prototype._dataChanged = function (unparsedData) {
var data = utils.cleanData(unparsedData);

if (utils.isServerTimestamp(data)) {
data = getServerTime();
data = utils.getServerTime();
}

if (pri !== this.priority) {
Expand All @@ -475,7 +470,7 @@ MockFirebase.prototype._dataChanged = function (unparsedData) {
keysToChange.forEach(function (key) {
var childData = unparsedData[key];
if (utils.isServerTimestamp(childData)) {
childData = getServerTime();
childData = utils.getServerTime();
}
self._updateOrAdd(key, childData, events);
});
Expand All @@ -493,7 +488,7 @@ MockFirebase.prototype._dataChanged = function (unparsedData) {

MockFirebase.prototype._priChanged = function (newPriority) {
if (utils.isServerTimestamp(newPriority)) {
newPriority = getServerTime();
newPriority = utils.getServerTime();
}
this.priority = newPriority;
if (this.parent) {
Expand Down Expand Up @@ -719,10 +714,10 @@ function render(datum) {
return array;
}
} else {
return _.cloneDeep(datum);
return _.cloneDeepWith(datum, utils.cloneCustomizer);
}
} else {
return _.clone(datum);
return _.cloneWith(datum, utils.cloneCustomizer);
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/firestore-document-snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

var _ = require('./lodash');
var FieldPath = require('./firestore-field-path');
var utils = require('./utils');

function MockFirestoreDocumentSnapshot (id, ref, data) {
this.id = id;
this.ref = ref;
this._snapshotdata = _.cloneDeep(data) || null;
this.data = function() {
return _.cloneDeep(this._snapshotdata);
return _.cloneDeepWith(this._snapshotdata, utils.cloneCustomizer);
};
this.exists = this._snapshotdata !== null;
this.metadata = {
Expand Down
12 changes: 9 additions & 3 deletions src/firestore-document.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ var Promise = require('rsvp').Promise;
var autoId = require('firebase-auto-ids');
var DocumentSnapshot = require('./firestore-document-snapshot');
var Queue = require('./queue').Queue;
var Timestamp = require('./timestamp');
var utils = require('./utils');
var validate = require('./validators');
var WriteResult = require('./write-result');

function MockFirestoreDocument(path, data, parent, name, CollectionReference) {
this.ref = this;
Expand Down Expand Up @@ -108,9 +110,12 @@ MockFirestoreDocument.prototype.create = function (data, callback) {

var base = self._getData();
err = err || self._validateDoesNotExist(base);
if (err === null) {
if (err === null) {
var serverTime = utils.getServerTime();
var result = new WriteResult(Timestamp.fromMillis(serverTime));
data = utils.removeEmptyFirestoreProperties(data, serverTime);
self._dataChanged(data);
resolve();
resolve(result);
} else {
if (callback) {
callback(err);
Expand All @@ -132,6 +137,7 @@ MockFirestoreDocument.prototype.set = function (data, opts, callback) {
return new Promise(function (resolve, reject) {
self._defer('set', _.toArray(arguments), function () {
if (err === null) {
data = utils.removeEmptyFirestoreProperties(data, utils.getServerTime());
self._dataChanged(data);
resolve();
} else {
Expand Down Expand Up @@ -167,7 +173,7 @@ MockFirestoreDocument.prototype._update = function (changes, opts, callback) {
data = _.assign(_.isObject(base) ? base : {}, utils.updateToFirestoreObject(changes));
}
}
data = utils.removeEmptyFirestoreProperties(data, original);
data = utils.removeEmptyFirestoreProperties(data, original, utils.getServerTime());
self._dataChanged(data);
resolve(data);
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/firestore-query.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ MockFirestoreQuery.prototype._results = function () {
if (this.orderedProperties.length === 0) {
_.forEach(this.data, function(data, key) {
if (inRange(data, key) && (self.limited <= 0 || limit < self.limited)) {
results[key] = _.cloneDeep(data);
results[key] = _.cloneDeepWith(data, utils.cloneCustomizer);
limit++;
}
});
Expand All @@ -285,7 +285,7 @@ MockFirestoreQuery.prototype._results = function () {
queryable = _.orderBy(queryable, orderBy, self.orderedDirections);
queryable.forEach(function(q) {
if (inRange(q.data, q.key) && (self.limited <= 0 || limit < self.limited)) {
results[q.key] = _.cloneDeep(q.data);
results[q.key] = _.cloneDeepWith(q.data, utils.cloneCustomizer);
limit++;
}
});
Expand Down
2 changes: 2 additions & 0 deletions src/lodash.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module.exports = {
assign: require('lodash.assign'),
bind: require('lodash.bind'),
clone: require('lodash.clone'),
cloneWith: require('lodash.clonewith'),
cloneDeep: require('lodash.clonedeep'),
cloneDeepWith: require('lodash.clonedeepwith'),
compact: require('lodash.compact'),
Expand All @@ -22,6 +23,7 @@ module.exports = {
isEqual: require('lodash.isequal'),
isFunction: require('lodash.isfunction'),
isNumber: require('lodash.isnumber'),
isPlainObject: require('lodash.isplainobject'),
isObject: require('lodash.isobject'),
isString: require('lodash.isstring'),
isUndefined: require('lodash.isundefined'),
Expand Down
2 changes: 2 additions & 0 deletions src/sdk.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var MockFieldPath = require('./firestore-field-path');
var MockFieldValue = require('./firestore-field-value');
var MockMessaging = require('./messaging');
var MockStorage = require('./storage');
var MockTimestamp = require('./timestamp');

var EmailAuthProvider = function() {
this.providerId = EmailAuthProvider.PROVIDER_ID;
Expand Down Expand Up @@ -79,6 +80,7 @@ function MockFirebaseSdk(createDatabase, createAuth, createFirestore, createStor
}
MockFirebaseFirestore.FieldPath = MockFieldPath;
MockFirebaseFirestore.FieldValue = MockFieldValue;
MockFirebaseFirestore.Timestamp = MockTimestamp;

function MockFirebaseStorage() {
return createStorage ? createStorage() : new MockStorage();
Expand Down
23 changes: 23 additions & 0 deletions src/timestamp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';

function Timestamp(seconds, nanoseconds) {
this.seconds = seconds;
this.nanoseconds = nanoseconds;
}

Timestamp.fromDate = function(date) {
return Timestamp.fromMillis(date.getTime());
};

Timestamp.fromMillis = function(ms) {
var sec = Math.floor(ms / 1000);
var ns = (ms % 1000) * 1000 * 1000;
return new Timestamp(sec, ns);
};

Timestamp.prototype.toDate = function () {
var millis = this.seconds * 1000 + this.nanoseconds / (1000 * 1000);
return new Date(millis);
};

module.exports = Timestamp;
41 changes: 32 additions & 9 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

var Snapshot = require('./snapshot');
var Timestamp = require('./timestamp');
var FieldValue = require('./firestore-field-value');
var _ = require('./lodash');

Expand Down Expand Up @@ -81,6 +82,24 @@ exports.priorityComparator = function priorityComparator(a, b) {
return 0;
};

var serverClock, defaultClock;

serverClock = defaultClock = function () {
return new Date().getTime();
};

exports.getServerTime = function getServerTime() {
return serverClock();
};

exports.setServerClock = function setServerTime(fn) {
serverClock = fn;
};

exports.restoreServerClock = function restoreServerTime() {
serverClock = defaultClock;
};

exports.isServerTimestamp = function isServerTimestamp(data) {
return _.isObject(data) && data['.sv'] === 'timestamp';
};
Expand Down Expand Up @@ -114,12 +133,10 @@ exports.removeEmptyRtdbProperties = function removeEmptyRtdbProperties(obj) {
}
};

exports.removeEmptyFirestoreProperties = function removeEmptyFirestoreProperties(obj, current) {
var t = typeof obj;
if (t === 'boolean' || t === 'string' || t === 'number' || t === 'undefined') {
exports.removeEmptyFirestoreProperties = function removeEmptyFirestoreProperties(obj, current, serverTime) {
if (!_.isPlainObject(obj)) {
return obj;
}
if (obj instanceof Date) return obj;

var keys = getKeys(obj);

Expand All @@ -131,13 +148,13 @@ exports.removeEmptyFirestoreProperties = function removeEmptyFirestoreProperties

if (keys.length > 0) {
for (var s in obj) {

var value = removeEmptyFirestoreProperties(obj[s]);
var value = removeEmptyFirestoreProperties(obj[s], serverTime);
if (FieldValue.delete().isEqual(value)) {
delete obj[s];
}
if (FieldValue.serverTimestamp().isEqual(value)) {
obj[s] = new Date();
} else if (FieldValue.serverTimestamp().isEqual(value)) {
obj[s] = new Date(serverTime);
} else if (value instanceof Timestamp) {
obj[s] = value.toDate();
}
if (FieldValue.arrayRemove().isEqual(value)) {
const replacement = Array.isArray(value.arg) ? value.arg : [value.arg];
Expand Down Expand Up @@ -228,3 +245,9 @@ exports.createThenableReference = function(reference, promise) {
};
return reference;
};

exports.cloneCustomizer = function(value) {
if (value instanceof Date) {
return Timestamp.fromMillis(value.getTime());
}
};
7 changes: 7 additions & 0 deletions src/write-result.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

function WriteResult(writeTime) {
this.writeTime = writeTime;
}

module.exports = WriteResult;
Loading