Skip to content

Commit

Permalink
v1.4.0 (#144)
Browse files Browse the repository at this point in the history
* fix(callFirestore): support timestamps objects other than serverTimestamp - #132
* chore(docs): add note to README about how to use timestamps - #132
  • Loading branch information
prescottprue authored Jul 12, 2020
1 parent 36d115b commit d043c74
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 19 deletions.
58 changes: 48 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ If you are interested in what drove the need for this checkout [the why section]
const cypressFirebasePlugin = require("cypress-firebase").plugin;

module.exports = (on, config) => {
const extendedConfig = cypressFirebasePlugin(on, config, admin)
const extendedConfig = cypressFirebasePlugin(on, config, admin);

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

return extendedConfig
return extendedConfig;
};
```

Expand All @@ -85,7 +85,8 @@ If you are interested in what drove the need for this checkout [the why section]
});
});
```
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.

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.
1. Look in your Firestore instance and see the `test_hello_world` collection to confirm that a document was added.
1. Pat yourself on the back, you are all setup to access Firebase/Firestore from within your tests!

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

_Set Data With Timestamps_

```javascript
import firebase from "firebase/app";
import "firebase/database";

const fakeProject = {
some: "data",
createdAt: firebase.database.ServerValue.TIMESTAMP,
};
cy.callRtdb("set", "projects/ABC123", fakeProject);
```

_Get/Verify Data_

```javascript
Expand Down Expand Up @@ -236,6 +250,22 @@ _Basic_
cy.callFirestore("set", "project/test-project", "fakeProject.json");
```

_Set Data With Server Timestamps_

```javascript
import firebase from "firebase/app";
import "firebase/firestore";

const fakeProject = {
some: "data",
// Use new firebase.firestore.Timestamp.now in place of serverTimestamp()
createdAt: firebase.firestore.Timestamp.now(),
// Or use fromDate if you would like to specify a date
// createdAt: firebase.firestore.Timestamp.fromDate(new Date())
};
cy.callFirestore("set", "projects/ABC123", fakeProject);
```

_Recursive Delete_

```javascript
Expand Down Expand Up @@ -444,13 +474,13 @@ Pass `commandNames` in the `options` object to `attachCustomCommands`:
const options = {
// Key is current command name, value is new command name
commandNames: {
login: 'newNameForLogin',
logout: 'newNameForLogout',
callRtdb: 'newNameForCallRtdb',
callFirestore: 'newNameForCallFirestore',
getAuthUser: 'newNameForGetAuthUser',
}
}
login: "newNameForLogin",
logout: "newNameForLogout",
callRtdb: "newNameForCallRtdb",
callFirestore: "newNameForCallFirestore",
getAuthUser: "newNameForGetAuthUser",
},
};
attachCustomCommands({ Cypress, cy, firebase }, options);
```

Expand Down Expand Up @@ -548,6 +578,14 @@ When testing, tests should have admin read/write access to the database for seed
- [fireadmin.io][fireadmin-url] - A Firebase project management tool ([here is the source][fireadmin-source])
- [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))

## Troubleshooting

1. An error is coming from cypress mentioning "Error converting circular structure to JSON"

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'))`.

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`.

[1]: #cylogin
[2]: #examples
[3]: #currentuser
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cypress-firebase",
"version": "1.3.0",
"version": "1.4.0",
"description": "Utilities to help testing Firebase projects with Cypress.",
"main": "lib/index.js",
"module": "lib/index.js",
Expand Down
2 changes: 1 addition & 1 deletion src/attachCustomCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ export default function attachCustomCommands(
dataToWrite.createdBy = Cypress.env('TEST_UID');
}
if (!dataToWrite.createdAt) {
dataToWrite.createdAt = firebase.firestore.FieldValue.serverTimestamp();
dataToWrite.createdAt = firebase.firestore.Timestamp.now();
}
}
taskSettings.data = dataToWrite;
Expand Down
40 changes: 33 additions & 7 deletions src/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,28 @@ function optionsToRtdbRef(baseRef: any, options?: CallRtdbOptions): any {
return newRef;
}

/**
* @param dataVal - Value of data
* @param firestoreStatics - Statics from firestore instance
* @returns Value converted into timestamp object if possible
*/
function convertValueToTimestampIfPossible(
dataVal: any,
firestoreStatics: typeof admin.firestore,
): admin.firestore.FieldValue {
if (dataVal?._methodName === 'FieldValue.serverTimestamp') {
return firestoreStatics.FieldValue.serverTimestamp();
}
if (
typeof dataVal?.seconds === 'number' &&
typeof dataVal?.nanoseconds === 'number'
) {
return new firestoreStatics.Timestamp(dataVal.seconds, dataVal.nanoseconds);
}

return dataVal;
}

/**
* @param data - Data to be set in firestore
* @param firestoreStatics - Statics from Firestore object
Expand All @@ -52,19 +74,22 @@ function getDataWithTimestamps(
return data;
}
return Object.keys(data).reduce<object>((acc, currKey) => {
/* eslint-disable-next-line no-underscore-dangle */
if (typeof data[currKey] === 'object' && !data[currKey]._methodName) {
if (
typeof data[currKey] === 'object' &&
/* eslint-disable-next-line no-underscore-dangle */
!data[currKey]._methodName &&
!data[currKey].seconds
) {
return {
...acc,
[currKey]: getDataWithTimestamps(data[currKey], firestoreStatics),
};
}

const isTimestamp =
data[currKey]?._methodName === 'FieldValue.serverTimestamp';
const value = isTimestamp
? firestoreStatics.FieldValue.serverTimestamp()
: data[currKey];
const value = convertValueToTimestampIfPossible(
data[currKey],
firestoreStatics,
);

return {
...acc,
Expand Down Expand Up @@ -218,6 +243,7 @@ export function callFirestore(
// Tests do not have statics since they are using @firebase/testing
options?.statics || (adminInstance.firestore as typeof admin.firestore),
);

if (action === 'set') {
return adminInstance
.firestore()
Expand Down

0 comments on commit d043c74

Please sign in to comment.