Skip to content

Commit d043c74

Browse files
authored
v1.4.0 (#144)
* fix(callFirestore): support timestamps objects other than serverTimestamp - #132 * chore(docs): add note to README about how to use timestamps - #132
1 parent 36d115b commit d043c74

File tree

4 files changed

+83
-19
lines changed

4 files changed

+83
-19
lines changed

README.md

+48-10
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,11 @@ If you are interested in what drove the need for this checkout [the why section]
6868
const cypressFirebasePlugin = require("cypress-firebase").plugin;
6969

7070
module.exports = (on, config) => {
71-
const extendedConfig = cypressFirebasePlugin(on, config, admin)
71+
const extendedConfig = cypressFirebasePlugin(on, config, admin);
7272

7373
// Add other plugins/tasks such as code coverage here
7474

75-
return extendedConfig
75+
return extendedConfig;
7676
};
7777
```
7878

@@ -85,7 +85,8 @@ If you are interested in what drove the need for this checkout [the why section]
8585
});
8686
});
8787
```
88-
1. From the root of your project, start Cypress with the command `$(npm bin)/cypress open`. In the Cypress window, click your new test (`test_hello_world.js`) to run it.
88+
89+
1. From the root of your project, start Cypress with the command `$(npm bin)/cypress open`. In the Cypress window, click your new test (`test_hello_world.js`) to run it.
8990
1. Look in your Firestore instance and see the `test_hello_world` collection to confirm that a document was added.
9091
1. Pat yourself on the back, you are all setup to access Firebase/Firestore from within your tests!
9192

@@ -198,6 +199,19 @@ const fakeProject = { some: "data" };
198199
cy.callRtdb("set", "projects/ABC123", fakeProject, { withMeta: true });
199200
```
200201

202+
_Set Data With Timestamps_
203+
204+
```javascript
205+
import firebase from "firebase/app";
206+
import "firebase/database";
207+
208+
const fakeProject = {
209+
some: "data",
210+
createdAt: firebase.database.ServerValue.TIMESTAMP,
211+
};
212+
cy.callRtdb("set", "projects/ABC123", fakeProject);
213+
```
214+
201215
_Get/Verify Data_
202216

203217
```javascript
@@ -236,6 +250,22 @@ _Basic_
236250
cy.callFirestore("set", "project/test-project", "fakeProject.json");
237251
```
238252

253+
_Set Data With Server Timestamps_
254+
255+
```javascript
256+
import firebase from "firebase/app";
257+
import "firebase/firestore";
258+
259+
const fakeProject = {
260+
some: "data",
261+
// Use new firebase.firestore.Timestamp.now in place of serverTimestamp()
262+
createdAt: firebase.firestore.Timestamp.now(),
263+
// Or use fromDate if you would like to specify a date
264+
// createdAt: firebase.firestore.Timestamp.fromDate(new Date())
265+
};
266+
cy.callFirestore("set", "projects/ABC123", fakeProject);
267+
```
268+
239269
_Recursive Delete_
240270

241271
```javascript
@@ -444,13 +474,13 @@ Pass `commandNames` in the `options` object to `attachCustomCommands`:
444474
const options = {
445475
// Key is current command name, value is new command name
446476
commandNames: {
447-
login: 'newNameForLogin',
448-
logout: 'newNameForLogout',
449-
callRtdb: 'newNameForCallRtdb',
450-
callFirestore: 'newNameForCallFirestore',
451-
getAuthUser: 'newNameForGetAuthUser',
452-
}
453-
}
477+
login: "newNameForLogin",
478+
logout: "newNameForLogout",
479+
callRtdb: "newNameForCallRtdb",
480+
callFirestore: "newNameForCallFirestore",
481+
getAuthUser: "newNameForGetAuthUser",
482+
},
483+
};
454484
attachCustomCommands({ Cypress, cy, firebase }, options);
455485
```
456486

@@ -548,6 +578,14 @@ When testing, tests should have admin read/write access to the database for seed
548578
- [fireadmin.io][fireadmin-url] - A Firebase project management tool ([here is the source][fireadmin-source])
549579
- [cv19assist.com](https://cv19assist.com) - App for connecting volunteers with at-health-risk population during the coronavirus pandemic. ([here is the source](https://github.com/CV19Assist/app))
550580

581+
## Troubleshooting
582+
583+
1. An error is coming from cypress mentioning "Error converting circular structure to JSON"
584+
585+
The issue is most likely due to a circular object, such as a timestamp, being included in data you are attempting to write to Firestore. Instead of using `firebase.firestore.FieldValue.serverTimestamp()` you should instead use `firebase.firestore.Timestamp.now()` or you would like to specify a certain date `firebase.firestore.Timestamp.fromDate(new Date('01/01/18'))`.
586+
587+
This comes from the fact that cypress stringifies values as it is passing them from the browser environment to the node environment through `cy.task`.
588+
551589
[1]: #cylogin
552590
[2]: #examples
553591
[3]: #currentuser

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cypress-firebase",
3-
"version": "1.3.0",
3+
"version": "1.4.0",
44
"description": "Utilities to help testing Firebase projects with Cypress.",
55
"main": "lib/index.js",
66
"module": "lib/index.js",

src/attachCustomCommands.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ export default function attachCustomCommands(
415415
dataToWrite.createdBy = Cypress.env('TEST_UID');
416416
}
417417
if (!dataToWrite.createdAt) {
418-
dataToWrite.createdAt = firebase.firestore.FieldValue.serverTimestamp();
418+
dataToWrite.createdAt = firebase.firestore.Timestamp.now();
419419
}
420420
}
421421
taskSettings.data = dataToWrite;

src/tasks.ts

+33-7
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,28 @@ function optionsToRtdbRef(baseRef: any, options?: CallRtdbOptions): any {
3838
return newRef;
3939
}
4040

41+
/**
42+
* @param dataVal - Value of data
43+
* @param firestoreStatics - Statics from firestore instance
44+
* @returns Value converted into timestamp object if possible
45+
*/
46+
function convertValueToTimestampIfPossible(
47+
dataVal: any,
48+
firestoreStatics: typeof admin.firestore,
49+
): admin.firestore.FieldValue {
50+
if (dataVal?._methodName === 'FieldValue.serverTimestamp') {
51+
return firestoreStatics.FieldValue.serverTimestamp();
52+
}
53+
if (
54+
typeof dataVal?.seconds === 'number' &&
55+
typeof dataVal?.nanoseconds === 'number'
56+
) {
57+
return new firestoreStatics.Timestamp(dataVal.seconds, dataVal.nanoseconds);
58+
}
59+
60+
return dataVal;
61+
}
62+
4163
/**
4264
* @param data - Data to be set in firestore
4365
* @param firestoreStatics - Statics from Firestore object
@@ -52,19 +74,22 @@ function getDataWithTimestamps(
5274
return data;
5375
}
5476
return Object.keys(data).reduce<object>((acc, currKey) => {
55-
/* eslint-disable-next-line no-underscore-dangle */
56-
if (typeof data[currKey] === 'object' && !data[currKey]._methodName) {
77+
if (
78+
typeof data[currKey] === 'object' &&
79+
/* eslint-disable-next-line no-underscore-dangle */
80+
!data[currKey]._methodName &&
81+
!data[currKey].seconds
82+
) {
5783
return {
5884
...acc,
5985
[currKey]: getDataWithTimestamps(data[currKey], firestoreStatics),
6086
};
6187
}
6288

63-
const isTimestamp =
64-
data[currKey]?._methodName === 'FieldValue.serverTimestamp';
65-
const value = isTimestamp
66-
? firestoreStatics.FieldValue.serverTimestamp()
67-
: data[currKey];
89+
const value = convertValueToTimestampIfPossible(
90+
data[currKey],
91+
firestoreStatics,
92+
);
6893

6994
return {
7095
...acc,
@@ -218,6 +243,7 @@ export function callFirestore(
218243
// Tests do not have statics since they are using @firebase/testing
219244
options?.statics || (adminInstance.firestore as typeof admin.firestore),
220245
);
246+
221247
if (action === 'set') {
222248
return adminInstance
223249
.firestore()

0 commit comments

Comments
 (0)