如何在 Jest 中模拟 navigator.clipboard.writeText()?
How to mock navigator.clipboard.writeText() in Jest?
在查看 Jest 问题和 后,我尝试了以下 4 个选项,但我遇到了 TypeScript 错误或运行时错误。我真的很想让选项 1 (spyOn) 工作。
// ------ 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;
Jest 测试 运行 在 JSdom 环境中,并不是所有的属性都被定义,但是你应该在监视它之前定义函数。
这是一个例子:
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");
});
});
});
编辑:你也可以使用Object.defineProperty
,但它接受描述符对象作为第三个参数
Object.defineProperty(navigator, "clipboard", {
value: {
writeText: () => {},
},
});
我 运行 遇到了类似的情况,并使用以下方法在导航器对象中模拟剪贴板:
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)
);
});
我扩展了早期的解决方案,还为 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);
});
在我的环境中,testing-library svelte and 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');
});
});
在查看 Jest 问题和
// ------ 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;
Jest 测试 运行 在 JSdom 环境中,并不是所有的属性都被定义,但是你应该在监视它之前定义函数。
这是一个例子:
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");
});
});
});
编辑:你也可以使用Object.defineProperty
,但它接受描述符对象作为第三个参数
Object.defineProperty(navigator, "clipboard", {
value: {
writeText: () => {},
},
});
我 运行 遇到了类似的情况,并使用以下方法在导航器对象中模拟剪贴板:
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)
);
});
我扩展了早期的解决方案,还为 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);
});
在我的环境中,testing-library svelte and 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');
});
});