typescript 如何在Jest中模拟navigator.clipboard.writeText()?

t2a7ltrp  于 2023-05-08  发布在  TypeScript
关注(0)|答案(5)|浏览(265)

在查看Jest问题和SO answers之后,我尝试了以下4个选项,但我要么得到TypeScript错误,要么得到运行时错误。我真的很想得到选项1(间谍)工作.

// ------ 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;
oyjwcjzk

oyjwcjzk1#

Jest测试在JSdom环境中运行,并不是所有的属性都被定义,但是你应该在监视它之前定义函数。
下面是一个例子:

const writeText = jest.fn()

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

describe("Clipboard", () => {
  describe("writeText", () => {
    beforeAll(() => {
      navigator.clipboard.writeText.mockResolvedValue(undefined)
      // or if needed
      // navigator.clipboard.writeText.mockRejectedValue(new Error()) 
      yourImplementationThatWouldInvokeClipboardWriteText();
    });
    it("should call clipboard.writeText", () => {
      expect(navigator.clipboard.writeText).toHaveBeenCalledWith("zxc");
    });
  });
});

**编辑:**也可以使用Object.defineProperty,但它接受descriptors对象作为第三个参数

Object.defineProperty(navigator, "clipboard", {
  value: {
    writeText: async () => {},
  },
});
g6ll5ycj

g6ll5ycj2#

我对早期的解决方案进行了扩展,并给出了readText的模拟剪贴板功能,以便可以测试剪贴板的内容。
下面是我的test.js文件的全部内容

import copyStringToClipboard from 'functions/copy-string-to-clipboard.js';

// ------- Mock -------
//Solution for mocking clipboard so it can be tested credit: <link to this post>
const originalClipboard = { ...global.navigator.clipboard };

beforeEach(() => {
    let clipboardData = '' //initalizing clipboard data so it can be used in testing
    const mockClipboard = {
        writeText: jest.fn(
            (data) => {clipboardData = data}
        ),
        readText: jest.fn(
            () => {return clipboardData}  
        ),
    };
    global.navigator.clipboard = mockClipboard;

});

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

it("copies a string to the clipboard", async () => {
    
    //arrange
    const string = 'test 😃'
  
    //act
    copyStringToClipboard(string)

    //assert
    expect(navigator.clipboard.readText()).toBe(string)
    expect(navigator.clipboard.writeText).toBeCalledTimes(1);
    expect(navigator.clipboard.writeText).toHaveBeenCalledWith(string);
});
8xiog9wr

8xiog9wr3#

如果您使用react-testing-library
首先,安装@testing-library/user-event
然后,导入用户事件,如下所示:import userEvent from '@testing-library/user-event';
然后,例如:

test('copies all codes to clipboard when clicked', async () => {
    const user = userEvent.setup()
    render(<Success />);
    const copyButton = screen.getByTestId('test-copy-button');
    await user.click(copyButton);
    const clipboardText = await navigator.clipboard.readText();
    expect(clipboardText).toBe('bla bla bla');
})
f1tvaqid

f1tvaqid4#

我遇到了类似的情况,并使用以下方法在navigator对象中模拟剪贴板:

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)
    );
  });
9q78igpj

9q78igpj5#

在我的环境testing-library svelte和jest jsdom中,我没有设法模拟global.navigator。有效的解决方案是在我的测试中模拟window.navigator

describe('my-test', () => {

  it("should copy to clipboard", () => {
    const { getByRole } = render(MyComponent);

    Object.assign(window.navigator, {
      clipboard: {
        writeText: jest.fn().mockImplementation(() => Promise.resolve()),
      },
    });

    const button = getByRole("button");
    fireEvent.click(button);

    expect(window.navigator.clipboard.writeText)
      .toHaveBeenCalledWith('the text that needs to be copied');
  });

});

相关问题