8

I would expect this test to pass but it fails:

it('should consume files on drop', () => {
  const mock = jest.fn();
  const file = new File(['file'], 'file.txt', { type: 'text/plain' });
  const fileList = [file];

  render(<DropZone onDocumentDrop={mock} />);

  const dropZone = screen.getByTestId('dropZone');
  const dropEvent = createEvent.drop(dropZone);

  Object.defineProperty(dropEvent, 'dataTransfer', {
    value: {
      files: {
        item: (index: number) => fileList[index],
        length: fileList.length,
      },
    },
  });

  fireEvent(dropZone, dropEvent);

  expect(dropZone).toBeInTheDocument();
  expect(mock).toHaveBeenCalled();
  expect(mock).toHaveBeenCalledWith({
    item: (index: number) => fileList[index],
    length: 1,
  });
});

Jest reports that:

expect(jest.fn()).toHaveBeenCalledWith(...expected)

- Expected
+ Received

- {"item": [Function item], "length": 1},
+ {"item": [Function item], "length": 1},

I am not sure how to make it pass or get anymore insight into what is wrong?

NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
RyanP13
  • 7,413
  • 27
  • 96
  • 166

2 Answers2

4

When checking the arguments of the last function call, you can use expect.objectContaining() to match the received object.

You can also use lastCalledWith() instead of toHaveBeenCalledWith(). They are both the same but I personally prefer the former because it's shorter and easier to read.

const item = (index: number) => []
const args = {
  length: 1,
  item,
}

const mock = jest.fn()

mock(args)

expect(mock).lastCalledWith(
  expect.objectContaining({
    length: expect.any(Number), // or 1 if you know the exact value
    item: expect.any(Function),
  })
)
NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
  • As a side note, you can actually nest expect.objectContaining. Comes in handy when testing network responses. E.g `expect.objectContaining(status:"fullfilled", body:expect.objectContaining({id:"1"}))` – Ali Mert Çakar Nov 25 '22 at 08:01
1

Two different objects in javascript are not the same value, even if they have the same key/value pairs - An object is stored in memory as a reference and so two different objects have two different references, therefore they are not equal.

Try defining the object once instead and passing it's reference around:

const files = {
    item: (index: number) => fileList[index],
    length: fileList.length,
};

Object.defineProperty(dropEvent, 'dataTransfer', { value: { files, }, });
// -- snip --- //
expect(mock).toHaveBeenCalledWith(files);
Dylan Kerler
  • 2,007
  • 1
  • 8
  • 23