Search code examples
reactjsunit-testingjestjsnavigator

How to mock navigator mediaDevices for Jest


I have a test that needs to use navigator.mediaDevices but I cannot get any mocks to work correctly.

I am using create-react-app.

Here is my test:

import getConnectedDevices from "./getConnectedDevices";

describe("getConnectedDevices", () => {
  it("Should work", () => {
    console.log(navigator);              // Navigator {}
    console.log(navigator.mediaDevices); // undefined
  });
});

I have tried adding a mock as it states here in the jest documentation

// tests/navigator.mock
Object.defineProperty(window, "navigator", {
  writable: true,
  value: {
    mediaDevices: {
      enumerateDevices: jest.fn(),
    },
  },
});

import "../../tests/navigator.mock"; // <- Mock added

import getConnectedDevices from "./getConnectedDevices";

describe("getConnectedDevices", () => {
  it("Should work", () => {
    console.log(navigator);              // Navigator {}
    console.log(navigator.mediaDevices); // undefined
  });
});

I have also tried Initializing the test environment according to the create-react-app docs.

// src/setupTests.ts

// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import "@testing-library/jest-dom";

const navigatorMock = {
  mediaDevices: {
    enumerateDevices: jest.fn(),
  },
};

global.navigator = navigatorMock;

What am I doing wrong?


Solution

  • It appears that because there already is a navigator object, it's not possible to re-assign it.

    Thanks to this stackoverflow answer, you mock the object within the navigator and assign it to the existing navigator object.

    // src/setupTests.ts
    
    const mediaDevicesMock = {
      enumerateDevices: jest.fn(),
    };
    
    global.navigator.mediaDevices = mediaDevicesMock; // here
    

    Note: This will throw a TSC error Cannot assign to 'mediaDevices' because it is a read-only property.ts(2540). Still trying to figure that one out.