Giter VIP home page Giter VIP logo

Comments (7)

AverageHelper avatar AverageHelper commented on July 18, 2024 1

@Shaggy13spe The issue is that that field is indistinguishable from a document field that contains an array of maps, each of which might have an id field. Our project had a few at the time that I opened this issue, and while there are certainly better ways to layout Firestore documents, sometimes you need to live with poor planning for a while πŸ˜…. Besides, since Firestore (kindof) supports that case, we'd best be able to test that. My proposal is to make the distinction more explicit to better handle that case.

i.e. Since this ugly thing works, we should support it:
Screen Shot 2020-09-12 at 9 34 56 AM

As a bonus, my fork restructures some things internally to cover even more canon Firestore API that we just don't have right now.

from firestore-jest-mock.

AverageHelper avatar AverageHelper commented on July 18, 2024

Part of the bother appears to be this line here in FakeFirestore.doc:

const records = this.database[this.collectionName] || [];

We shouldn't always check in this.database. What if our present spot is a document? How do we access its subcollections instead? And what if we have references exploring multiple database locations simultaneously?

One potential solution would be to document a special key in the mock database structure called _subcollections. The leading underscore precludes it from conflicting with any actual data property called "subcollections", and this way we're not just checking the contents of every document's array property for objects with id properties. This special document property would map to an object with collection IDs and arrays of document children. Like so:

const db = new FakeFirestore({
  characters: [
    { id: 'homer', name: 'Homer', occupation: 'technician' },
    { id: 'krusty', name: 'Krusty', occupation: 'clown' },
    {
      id: 'bob',
      name: 'Bob',
      occupation: 'repairman',
      _subcollections: {
        family: [ // *this* is a subcollection! 😊
          { id: 'thing1', name: 'Thing 1', relation: 'pet' },
          { id: 'thing2', name: 'Thing 2', relation: 'pet' },
          { id: 'deborah', name: 'Deborah', relation: 'wife' },
        ],
      },
    },
  ],
});

Now for the fun part: how do we keep track of where we are in the object graph? Presently, everything is handled by an instance of FakeFirestore. Each call to collection sets a collectionName property and resets its recordToFetch property to null. Then when doc is called, the function looks in the mock database's root (this.database[this.collectionName]) for the document to pull. It remembers that object only until collection is called again.

Now, this works great when we have one or more references to documents at the root of the Firestore database, but this limits our view; we cannot dig any deeper!

In an effort to prevent many more persistent properties on FakeFirestore, and to permit multilevel retrieval in more places than one at a time, I recommend we refactor the doc and collection mocks to instead return reference objects. These class instances would keep track of where they are in the tree by hanging onto a reference document, whose subcollections may be accessed via the _subcollections property defined on the mock database.

from firestore-jest-mock.

AverageHelper avatar AverageHelper commented on July 18, 2024

Another thought: If I'm reading the code right (it is getting late for me), it looks like a new instance of FakeFirestore is created each time we call firestore().

This would prevent us from overwriting our spot in the document tree, assuming first that FakeFirestore remains the main player in working with the document tree; and second, that Firebase clients don't cache this object (say, as db or something) to access disparate parts of the tree between calls to get. Unfortunately, we cannot prevent this, and it's actually a fairly common pattern to get several document references and call get on each of them in quick succession, such as in a batch or a transaction.

Unfortunately, I don't see a reliable way to juggle multiple paths down the document tree with just one referencing object. We'll likely need to do what Firestore proper does and return something like DocumentReference and CollectionReference instances with each call to doc and collection, respectively.

Thoughts?

from firestore-jest-mock.

MattGoldwater avatar MattGoldwater commented on July 18, 2024

Are there any plans to incorporate sub-collection mocking? It would be really useful for me.

from firestore-jest-mock.

AverageHelper avatar AverageHelper commented on July 18, 2024

@MattGoldwater I've actually implemented something for that in my own fork. I plan to take some time this weekend to bring it up to date with the master branch here and submit a PR.

from firestore-jest-mock.

Shaggy13spe avatar Shaggy13spe commented on July 18, 2024

Maybe I'm missing something?? I have the following mocked db:

mockFirebase({
  database: {
    interviews: [
      {
        id: '1',
        recruit: 'John Doe',
        screener: 'Michael Morrison',
        email: '[email protected]',
        currentWhiteboard: {},
        interviewTimestamp: mockTimestamp,
        whiteboards: [{ ///this is a subcollection in actual Firestore
          id: '1',
          title: 'whiteboard1',
          scenario: 'scenario1',
          code: '',
        },
        {
          id: '2',
          title: 'whiteboard2',
          scenario: 'scenario2',
          code: '',
        }],
      },
]

this is the code for accessing the interview doc (and it's subcollection) and updating it:

  async addWhiteboard (interviewId, whiteboard) {
    const ref = this.firestore.db.collection('interviews').doc(interviewId).collection('whiteboards')

    await ref.doc(whiteboard.id).set(whiteboard)
  }

And my unit test:

  test('addWhiteboard', async () => {
    expect.assertions(5)
    const whiteboard = {
      id: '1',
      title: 'whiteboard1',
      scenario: 'scenario1',
      code: '',
    }
    const interviewService = new InterviewService()
    await interviewService.addWhiteboard('2', whiteboard)

    expect(mockCollection).toHaveBeenCalledWith('interviews')
    expect(mockDoc).toHaveBeenCalledWith('2')
    expect(mockCollection).toHaveBeenCalledWith('whiteboards')
    expect(mockDoc).toHaveBeenCalledWith('1')
    expect(mockSet).toHaveBeenCalledWith(whiteboard)
  })

This all works. Is there some other functionality that you are needing?

from firestore-jest-mock.

Shaggy13spe avatar Shaggy13spe commented on July 18, 2024

from firestore-jest-mock.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.