英文:
How to test pasted content when using `useClipboard` hook in ChakraUI
问题
I have a component that has a copy button that copies some data to the clipboard.
I would like to test that the correct data has been copied to the clipboard using jest
& @testing-library/react
This is the component's implementation:
import React from "react";
import { Button, useClipboard } from "@chakra-ui/react";
import { formatData } from "@/lib/formatters";
export const MyComponent = ({ data }: Data) => {
const { hasCopied, onCopy } = useClipboard(formatData(data));
return <Button onClick={onCopy}>{hasCopied ? "Copied!" : "Copy Data"}</Button>;
};
And here is the unit test:
it("copies data to clipboard", () => {
render(<MyComponent data={data} />);
screen.getByRole("button").click();
fireEvent.click(screen.getByRole("button", { name: "Copy Data" }));
// Expect that the clipboard data is set to the formatted data
expect(navigator.clipboard.readText()).toEqual(formatData(data)); // Doesn't work!
});
However, when I run the unit test, I get an error:
TypeError: Cannot read properties of undefined (reading 'readText')
Is there a way to elegantly test the pasted content?
PS: useClipboard
is using copy-to-clipboard
package under the hood, which could be mocked, but that solution wouldn't be so elegant.
英文:
I have a component that has a copy button that copies some data to the clipboard.
I would like to test that the correct data has been copied to the clipboard using jest
& @testing-library/react
This is the component's implementation:
import React from "react";
import { Button, useClipboard } from "@chakra-ui/react";
import { formatData } from "@/lib/formatters";
export const MyComponent = ({ data }: Data) => {
const { hasCopied, onCopy } = useClipboard(formatData(data));
return <Button onClick={onCopy}>{hasCopied ? "Copied!" : "Copy Data"}</Button>;
};
And here is the unit test
it("copies data to clipboard", () => {
render(<MyComponent data={data} />);
screen.getByRole("button").click();
fireEvent.click(screen.getByRole("button", { name: "Copy Data" }));
// Expect that the clipboard data is set to the formatted data
expect(navigator.clipboard.readText()).toEqual(formatData(data)); // Doesn't work!
});
However, when I run the unit test, I get an error
TypeError: Cannot read properties of undefined (reading 'readText')
Is there a way to elegantly test the pasted content?
PS: useClipboard
is using copy-to-clipboard
package under the hood, which could be mocked, but that solution wouldn't be so elegant.
答案1
得分: 2
以下是您要翻译的内容:
Jest is running the tests with jsdom and jsdom doesn't support navigator.clipborad
, this is why clipboard
is undefined
and cannot read property writeText
of undefined. However react testing library replaces window.navigator.clipboard
with a stub when userEvent.setup()
is used.
如果您的实现是使用 navigator.clipboard
而不是 copy-to-clipboard
const MyComponent = ({ data }: Data) => {
const [hasCopied, setHasCopied] = React.useState(false)
const val = formatData(data)
return (
<Button onClick={() => {
navigator.clipboard.writeText(val).then(() => {
setHasCopied(true)
});
}}>{hasCopied ? "Copied!" : "Copy Data"}</Button>
);
};
you would have been able to expect:
test("should return text, reading the clipboard text", async () => {
const user = userEvent.setup()
render(<App />)
const linkElement = screen.getByText(/copy data/i)
user.click(linkElement)
await waitFor(() => {
expect(linkElement).toHaveTextContent('Copied!')
})
await expect(navigator.clipboard.readText()).resolves.toEqual("{\"hello\":\"world\"}")
})
but since execCommand
is used instead of the navigator.clipboard
API it would have to be mocked as well, as it's not supported by jsdom
to mock it I would use the already prepared stub by react testing library like this
let user: ReturnType<typeof userEvent.setup>;
beforeAll(() => {
user = userEvent.setup();
Object.assign(document, {
execCommand: (cmd: string) => {
switch (cmd) {
case "copy":
user.copy();
return true;
case "paste":
user.paste();
return;
}
},
});
});
and the above test should work once again
英文:
Jest is running the tests with jsdom and jsdom doesn't support navigator.clipborad
, this is why clipboard
is undefined
and cannot read property writeText
of undefined. However react testing library replaces window.navigator.clipboard
with a stub when userEvent.setup()
is used.
If your implementation was using navigator.clipboard
instead of copy-to-clipboard
const MyComponent = ({ data }: Data) => {
const [hasCopied, setHasCopied] = React.useState(false)
const val = formatData(data)
return (
<Button onClick={() => {
navigator.clipboard.writeText(val).then(() => {
setHasCopied(true)
});
}}>{hasCopied ? "Copied!" : "Copy Data"}</Button>
);
};
you would have been able to expect:
test("should return text, reading the clipboard text", async () => {
const user = userEvent.setup()
render(<App />)
const linkElement = screen.getByText(/copy data/i)
user.click(linkElement)
await waitFor(() => {
expect(linkElement).toHaveTextContent('Copied!')
})
await expect(navigator.clipboard.readText()).resolves.toEqual("{\"hello\":\"world\"}")
})
but since execCommand
is used instead of the navigator.clipboard
API it would have to be mocked as well, as it's not supported by jsdom
to mock it I would use the already prepared stub by react testing library like this
let user: ReturnType<typeof userEvent.setup>;
beforeAll(() => {
user = userEvent.setup();
Object.assign(document, {
execCommand: (cmd: string) => {
switch (cmd) {
case "copy":
user.copy();
return true;
case "paste":
user.paste();
return;
}
},
});
});
and the above test should work once again
答案2
得分: 0
navigator.clipboard.readText()
返回一个promise,而不是一个string。MDN文档
这就是为什么 expect(navigator.clipboard.readText()).toEqual(formatData(data))
不起作用。
英文:
navigator.clipboard.readText(
) returns a promise, not a string. MDN-Docs
That's why expect(navigator.clipboard.readText()).toEqual(formatData(data))
isn't working.
// MOCK CLIPBOARD
const mockClipboard = {
writeText: jest.fn(),
readText: jest.fn(),
};
global.navigator.clipboard = mockClipboard;
it('copies data to clipboard', async () => {
render(<MyComponent data={data} />);
// Reset the mock functions before each test
mockClipboard.writeText.mockReset();
mockClipboard.readText.mockReset().mockResolvedValue(formattedData);
const copyButton = screen.getByRole('button', { name: 'Copy Data' });
fireEvent.click(copyButton);
await waitFor(() => {
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(formattedData);
});
const clipboardContent = await navigator.clipboard.readText();
expect(clipboardContent).toEqual(formattedData);
expect(copyButton).toHaveTextContent('Copied!');
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论