Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(callFirestore): create document with reference to another using cy.callFirestore #232

Open
itsravenous opened this issue Nov 18, 2020 · 5 comments
Labels
enhancement New feature or request

Comments

@itsravenous
Copy link

itsravenous commented Nov 18, 2020

Is your feature request related to a problem? Please describe.

I would like to be able to use callFirestore to create one document, then create another with a field of type reference (see https://firebase.google.com/docs/firestore/manage-data/data-types) that points to that document. For example:

cy.callFirestore('set', 'people/tom', { name: "Tom" })
cy.callFirestore('set', 'cars/car1', { make: 'Honda Civic', owner: /* reference to people/tom here */ })
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->

**Describe the solution you'd like**

I don't think it's possible right now, because I'd need access to the firestore instance. In an application, I would do something like this:

const db = firebase.firestore()
const personRef = db.doc('people/tom')
const carRef = db.doc('cars/car1')
await personRef.set({ name: 'Tom' })
await carRef.set({ make: 'Honda Civic', owner: personRef })

Perhaps the set call could return a reference? Or we could have access to the firebase instance? I'm not sure what the most elegant solution would be.

Describe alternatives you've considered

I tried doing the following:

cy.callFirestore('set', 'people/tom', { name: "Tom" })
cy.callFirestore('get', 'people/tom').then(person => {
  cy.callFirestore('set', 'cars/car1', { make: 'Honda Civic', owner: person })
})

but this just adds the person as an object, rather than a reference (presumably because cy.callFirestore('get') returns the data, not a reference).

Additional context

@prescottprue
Copy link
Owner

Yeah, definitely a valid use case, thanks for reaching out! Returning the snapshot (which contains a reference) is what I was describing in #128, but as noted that would mean either an option or a change to the API.

The firebase instance is initialized in cypress (since it is running in the browser) - so we should be able to fix things so you could just use the browser SDK directly like with timestamps:

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

const personRef = db.doc('people/tom')
const carRef = db.doc('cars/car1')
cy.callFirestore('set', 'people/tom', { name: "Tom" })
cy.callFirestore('set', 'cars/car1',  make: 'Honda Civic', owner: person })

NOTE: I don't believe this will work yet, but I will start testing and looking into the change

@itsravenous
Copy link
Author

Wonderful, thanks!

@sdedieu
Copy link

sdedieu commented Dec 16, 2020

Hello,

I'm kinda stuck at the moment with the same issue.

When I call const db = firebase.firestore() inside a test a got the ERROR that initilizeApp wasn't call.
I do have called it inside my command.js file and cy.callFirestore() works well.

Is it legit ?

Thanks for the help,

Regards

-- EDIT ----

I workedaround the problem by exporting the db object of the command.js file and importing it inside my test, but when i run
cy.callFirestore("set", "some_collection/some_id"), { some_field: db.doc("some_other_collection/some_other_id")}), I got the following error :
``
CypressError: cy.task('callFirestore') failed with the following error:

TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'firebase_' -> object with constructor 'Object'
| property 'apps' -> object with constructor 'Array'
--- index 0 closes the circle
at JSON.stringify (:null:null)
at ChildProcess.target._send (internal/child_process.js:778:25)
at ChildProcess.target.send (internal/child_process.js:676:19)
at Object.send (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/util.js:34:27)
at /Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/index.js:82:28
at /Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/util.js:77:16
at Promise.cancellationExecute [as _execute] (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/node_modules/bluebird/js/release/debuggability.js:449:9)
at Promise._resolveFromExecutor (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/node_modules/bluebird/js/release/promise.js:518:18)
at new Promise (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/node_modules/bluebird/js/release/promise.js:103:10)
at Object.wrapParentPromise (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/util.js:61:14)
at Object.task (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/index.js:75:27)
at Object.execute (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/index.js:128:38)
at /Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/lib/task.js:36:24
at tryCatcher (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/node_modules/bluebird/js/release/util.js:16:23)
at Function.Promise.attempt.Promise.try (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/node_modules/bluebird/js/release/method.js:39:29)
at Object.run (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/lib/task.js:31:28)
at backendRequest (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/lib/socket.js:378:27)
at tryCatcher (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/node_modules/bluebird/js/release/util.js:16:23)
at Function.Promise.attempt.Promise.try (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/node_modules/bluebird/js/release/method.js:39:29)
at Socket. (/Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/server/lib/socket.js:386:27)
at Socket.emit (events.js:203:13)
at /Users/sylvaindedieu/Library/Caches/Cypress/3.8.3/Cypress.app/Contents/Resources/app/packages/socket/node_modules/socket.io/lib/socket.js:528:12
at processTicksAndRejections (internal/process/task_queues.js:75:11)
``

@delete
Copy link

delete commented Jan 19, 2021

Hi, perhaps we could do something like getDataWithTimestampsAndGeoPoints does before set the data?

const dataToSet = getDataWithTimestampsAndGeoPoints(

Something like

function getDataWithReference(adminInstance, data) {
    const isDoc = (value) => value.split('/').length === 3 // should be a better way to do this
    const isString = (value) => typeof value === 'string'

    return Object.entries(data).reduce((acc, [currKey, currData]) => {
        if(isString(currData) && isDoc(currData)) {
            return {
                ...acc,
                [currKey]: adminInstance.firestore().doc(currData)
            }
        }

        if(Array.isArray(currData)) {
            return {
                ...acc,
                [currKey]: currData.map(dataSet => isString(dataSet) && isDoc(dataSet) 
                    ? adminInstance.firestore().doc(dataSet)
                    : dataSet)
            }
        }

        return {
                ...acc,
                [currKey]: currData
            }
    }, {})
}

Then use

cy.callFirestore('set', 'cars/car1',  make: 'Honda Civic', owner: '/people/tom' })

@prescottprue
Copy link
Owner

@delete I think this will potentially cause issues if someone is trying to just write a slash path to a parameter instead of having that point to a document - We may need to pass along some additional information about which params should be converted into references.

It would be good to have this happen automatically within the task/plugin, but it is going from the browser through to the node environment, so there is that parsing to deal with (why a reference isn't currently being passed straight through)

@prescottprue prescottprue changed the title How to create document with reference to another using cy.callFirestore? feat(callFirestore): create document with reference to another using cy.callFirestore May 2, 2021
@prescottprue prescottprue added the enhancement New feature or request label May 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants