Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock navigator.clipboard.writeText() in Jest?

I have tried the following 4 options after looking at Jest issues and SO answers, but I am either getting TypeScript errors or runtime errors. I would really like to get option 1 (spyOn) working.

// ------ option 1 -----
// Gives this runtime error: "Cannot spyOn on a primitive value; undefined given"
const writeText = jest.spyOn(navigator.clipboard, 'writeText');

// ------ option 2 -----
Object.defineProperty(navigator, 'clipboard', {
    writeText: jest.fn(),
});

// ------ option 3 -----
// This is from SO answer but gives a TypeScript error
window.__defineGetter__('navigator', function() {
    return {
        clipboard: {
            writeText: jest.fn(x => x)
        }
    }
})

// ------ option 4 -----
const mockClipboard = {
    writeText: jest.fn()
};
global.navigator.clipboard = mockClipboard;
like image 442
Naresh Avatar asked Jun 12 '20 20:06

Naresh


People also ask

What is navigator clipboard?

clipboard. The Clipboard API adds to the Navigator interface the read-only clipboard property, which returns the Clipboard object used to read and write the clipboard's contents. The Clipboard API can be used to implement cut, copy, and paste features within a web application.

How do you open mock windows in jest?

To mock the JavaScript window object using Jest, we can use the jest. spyOn method. let windowSpy; beforeEach(() => { windowSpy = jest. spyOn(window, "window", "get"); }); afterEach(() => { windowSpy.

Is it possible to mock the clipboard content for readtext?

I expanded on the earlier solutions and also gave the mock clipboard functionality for readText so the content of the clipboard can be tested. Show activity on this post. In my environment, testing-library svelte and jest jsdom, I did not manage to mock the global.navigator.

How do you inject test values in jest mock?

Mock Return Values Mock functions can also be used to inject test values into your code during a test: const myMock = jest.fn();

Why doesn't the Navigator work in jest?

Changing to global can't help because navigator is browser feature. It should exists if Jest was configured to use JSDOM. Make sure that you did that. But I'm not sure how you previous attempt could work because there would be no window.navigator without JSDOM. Make sure your dependencies are up to date.

How do I mock the Axios module in jest?

Now, in order to test this method without actually hitting the API (and thus creating slow and fragile tests), we can use the jest.mock (...) function to automatically mock the axios module. Once we mock the module we can provide a mockResolvedValue for .get that returns the data we want our test to assert against.


2 Answers

Jest tests are running in JSdom environment and not all of the properties are defined, but so you should define the function before spying on it.

Here is an example:

Object.assign(navigator, {
  clipboard: {
    writeText: () => {},
  },
});

describe("Clipboard", () => {
  describe("writeText", () => {
    jest.spyOn(navigator.clipboard, "writeText");
    beforeAll(() => {
      yourImplementationThatWouldInvokeClipboardWriteText();
    });
    it("should call clipboard.writeText", () => {
      expect(navigator.clipboard.writeText).toHaveBeenCalledWith("zxc");
    });
  });
});

Edit: you can also use Object.defineProperty, but it accepts descriptors object as third parameter

Object.defineProperty(navigator, "clipboard", {
  value: {
    writeText: () => {},
  },
});
like image 147
Teneff Avatar answered Sep 20 '22 16:09

Teneff


I ran into a similar situation and used the following method to mock the clipboard in the navigator object:

  const originalClipboard = { ...global.navigator.clipboard };
  const mockData = {
     "name": "Test Name",
     "otherKey": "otherValue"
  }

  beforeEach(() => {
    const mockClipboard = {
      writeText: jest.fn(),
    };
    global.navigator.clipboard = mockClipboard;

  });

  afterEach(() => {
    jest.resetAllMocks();
    global.navigator.clipboard = originalClipboard;
  });

  test("copies data to the clipboard", () => {
    copyData(); //my method in the source code which uses the clipboard
    expect(navigator.clipboard.writeText).toBeCalledTimes(1);
    expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
      JSON.stringify(mockData)
    );
  });
like image 34
Parthipan Natkunam Avatar answered Sep 19 '22 16:09

Parthipan Natkunam