2024-01-11 15:11:55 +08:00
|
|
|
import React from 'react';
|
2022-06-09 18:09:43 +08:00
|
|
|
import { LikeOutlined, SmileOutlined } from '@ant-design/icons';
|
2022-06-22 14:57:09 +08:00
|
|
|
import * as copyObj from 'copy-to-clipboard';
|
2021-06-06 13:41:26 +08:00
|
|
|
|
2024-03-29 13:20:02 +08:00
|
|
|
import { fireEvent, render, renderHook, waitFakeTimer, waitFor } from '../../../tests/utils';
|
2021-06-06 13:41:26 +08:00
|
|
|
import Base from '../Base';
|
2024-03-29 13:20:02 +08:00
|
|
|
import useCopyClick from '../hooks/useCopyClick';
|
2021-06-06 13:41:26 +08:00
|
|
|
|
|
|
|
describe('Typography copy', () => {
|
2023-06-07 21:59:21 +08:00
|
|
|
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
2021-06-06 13:41:26 +08:00
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
errorSpy.mockReset();
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Base', () => {
|
|
|
|
describe('copyable', () => {
|
|
|
|
function copyTest({
|
|
|
|
name,
|
|
|
|
icon,
|
|
|
|
tooltips,
|
|
|
|
iconClassNames = [],
|
|
|
|
iconTexts = [],
|
|
|
|
tooltipTexts = [],
|
|
|
|
tooltipLength,
|
|
|
|
}: {
|
|
|
|
name: string;
|
2024-01-11 15:11:55 +08:00
|
|
|
icon?: React.ReactNode;
|
|
|
|
tooltips?: React.ReactNode;
|
2021-06-06 13:41:26 +08:00
|
|
|
iconClassNames?: string[];
|
|
|
|
iconTexts?: string[];
|
|
|
|
tooltipTexts?: string[];
|
|
|
|
tooltipLength?: number;
|
|
|
|
}) {
|
|
|
|
it(name, async () => {
|
2023-06-07 21:59:21 +08:00
|
|
|
jest.useFakeTimers();
|
2023-07-28 16:17:43 +08:00
|
|
|
const { container, unmount } = render(
|
2021-06-06 13:41:26 +08:00
|
|
|
<Base component="p" copyable={{ icon, tooltips }}>
|
|
|
|
test copy
|
|
|
|
</Base>,
|
|
|
|
);
|
2023-07-28 16:17:43 +08:00
|
|
|
|
2021-06-06 13:41:26 +08:00
|
|
|
if (iconClassNames[0] !== undefined) {
|
2023-07-28 16:17:43 +08:00
|
|
|
expect(container.querySelector(iconClassNames[0])).not.toBeNull();
|
2021-06-06 13:41:26 +08:00
|
|
|
}
|
|
|
|
if (iconTexts[0] !== undefined) {
|
2023-07-28 16:17:43 +08:00
|
|
|
expect(container.querySelectorAll('.ant-typography-copy')[0].textContent).toBe(
|
2022-06-09 18:09:43 +08:00
|
|
|
iconTexts[0],
|
|
|
|
);
|
2021-06-06 13:41:26 +08:00
|
|
|
}
|
|
|
|
|
2023-07-28 16:17:43 +08:00
|
|
|
fireEvent.mouseEnter(container.querySelectorAll('.ant-typography-copy')[0]);
|
|
|
|
await waitFakeTimer();
|
2021-06-06 13:41:26 +08:00
|
|
|
|
|
|
|
if (tooltipTexts[0] !== undefined) {
|
2023-06-07 21:59:21 +08:00
|
|
|
await waitFor(() => {
|
2023-07-28 16:17:43 +08:00
|
|
|
expect(container.querySelector('.ant-tooltip-inner')?.textContent).toBe(
|
2023-06-07 21:59:21 +08:00
|
|
|
tooltipTexts[0],
|
|
|
|
);
|
|
|
|
});
|
2021-06-06 13:41:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tooltipLength !== undefined) {
|
2023-06-07 21:59:21 +08:00
|
|
|
await waitFor(() => {
|
2023-07-28 16:17:43 +08:00
|
|
|
expect(container.querySelectorAll('.ant-tooltip-inner').length).toBe(tooltipLength);
|
2023-06-07 21:59:21 +08:00
|
|
|
});
|
2021-06-06 13:41:26 +08:00
|
|
|
}
|
|
|
|
|
2023-07-28 16:17:43 +08:00
|
|
|
fireEvent.click(container.querySelectorAll('.ant-typography-copy')[0]);
|
2023-06-07 21:59:21 +08:00
|
|
|
jest.useRealTimers();
|
2021-06-06 13:41:26 +08:00
|
|
|
if (iconClassNames[1] !== undefined) {
|
2023-07-28 16:17:43 +08:00
|
|
|
expect(container.querySelector(iconClassNames[1])).not.toBeNull();
|
2021-06-06 13:41:26 +08:00
|
|
|
}
|
2023-07-28 16:17:43 +08:00
|
|
|
fireEvent.mouseEnter(container.querySelectorAll('.ant-typography-copy')[0]);
|
2021-06-06 13:41:26 +08:00
|
|
|
|
2023-07-28 16:17:43 +08:00
|
|
|
fireEvent.mouseEnter(container.querySelectorAll('.ant-typography-copy')[0]);
|
2021-06-06 13:41:26 +08:00
|
|
|
|
|
|
|
if (tooltipTexts[1] !== undefined) {
|
2023-04-18 11:35:07 +08:00
|
|
|
const expectedInner = tooltipTexts[1] === '' ? tooltipTexts[0] : tooltipTexts[1];
|
2023-06-07 21:59:21 +08:00
|
|
|
await waitFor(() => {
|
2023-07-28 16:17:43 +08:00
|
|
|
expect(container.querySelector('.ant-tooltip-inner')?.textContent).toBe(
|
|
|
|
expectedInner,
|
|
|
|
);
|
2023-06-07 21:59:21 +08:00
|
|
|
});
|
2021-06-06 13:41:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (iconTexts[1] !== undefined) {
|
2023-07-28 16:17:43 +08:00
|
|
|
expect(container.querySelectorAll('.ant-typography-copy')[0].textContent).toBe(
|
2022-06-09 18:09:43 +08:00
|
|
|
iconTexts[1],
|
|
|
|
);
|
2021-06-06 13:41:26 +08:00
|
|
|
}
|
|
|
|
|
2023-06-07 21:59:21 +08:00
|
|
|
jest.useFakeTimers();
|
2023-07-28 16:17:43 +08:00
|
|
|
fireEvent.click(container.querySelectorAll('.ant-typography-copy')[0]);
|
|
|
|
await waitFakeTimer();
|
2021-06-06 13:41:26 +08:00
|
|
|
|
2022-06-09 18:09:43 +08:00
|
|
|
unmount();
|
2023-06-07 21:59:21 +08:00
|
|
|
jest.useRealTimers();
|
2021-06-06 13:41:26 +08:00
|
|
|
});
|
|
|
|
}
|
2022-06-09 18:09:43 +08:00
|
|
|
|
2021-06-06 13:41:26 +08:00
|
|
|
const dom = (
|
|
|
|
<>
|
|
|
|
<span>1</span>2
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
const dom2 = (
|
|
|
|
<>
|
|
|
|
<span>3</span>4
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
const copy = '.anticon-copy';
|
|
|
|
const check = '.anticon-check';
|
|
|
|
|
|
|
|
copyTest({
|
|
|
|
name: 'icon basic copy',
|
|
|
|
iconClassNames: [copy, check],
|
|
|
|
tooltipTexts: ['Copy', 'Copied'],
|
|
|
|
});
|
|
|
|
copyTest({ name: 'icon true', icon: true, iconClassNames: [copy, check] });
|
|
|
|
copyTest({ name: 'icon two true', icon: [true, true], iconClassNames: [copy, check] });
|
|
|
|
copyTest({ name: 'icon false', icon: false, iconClassNames: [copy, check] });
|
|
|
|
copyTest({ name: 'icon custom text', icon: ['a', 'b'], iconTexts: ['a', 'b'] });
|
|
|
|
copyTest({ name: 'icon custom element', icon: [dom, dom2], iconTexts: ['12', '34'] });
|
|
|
|
copyTest({
|
|
|
|
name: 'icon custom icon',
|
|
|
|
icon: <SmileOutlined />,
|
|
|
|
iconClassNames: ['.anticon-smile', check],
|
|
|
|
});
|
|
|
|
copyTest({
|
|
|
|
name: 'icon custom icon2',
|
|
|
|
icon: [<SmileOutlined key="a" />, <LikeOutlined key="b" />],
|
|
|
|
iconClassNames: ['.anticon-smile', '.anticon-like'],
|
|
|
|
});
|
|
|
|
copyTest({
|
|
|
|
name: 'icon custom icon3',
|
|
|
|
icon: [
|
|
|
|
<>
|
|
|
|
<SmileOutlined />
|
|
|
|
<SmileOutlined />
|
|
|
|
</>,
|
|
|
|
<LikeOutlined key="b" />,
|
|
|
|
],
|
|
|
|
iconClassNames: ['.anticon-smile', '.anticon-like'],
|
|
|
|
});
|
|
|
|
copyTest({
|
|
|
|
name: 'icon custom icon4',
|
|
|
|
icon: (
|
|
|
|
<>
|
|
|
|
<SmileOutlined />
|
|
|
|
<LikeOutlined />
|
|
|
|
</>
|
|
|
|
),
|
|
|
|
iconClassNames: ['.anticon-smile', check],
|
|
|
|
});
|
|
|
|
copyTest({
|
|
|
|
name: 'icon custom icon5',
|
|
|
|
icon: (
|
|
|
|
<>
|
|
|
|
<SmileOutlined />
|
|
|
|
<LikeOutlined />
|
|
|
|
</>
|
|
|
|
),
|
|
|
|
iconClassNames: ['.anticon-like', check],
|
|
|
|
});
|
|
|
|
copyTest({
|
|
|
|
name: 'tooltips true',
|
|
|
|
tooltips: true,
|
|
|
|
tooltipLength: 1,
|
|
|
|
tooltipTexts: ['Copy', 'Copied'],
|
|
|
|
});
|
|
|
|
copyTest({ name: 'tooltips false', tooltips: false, tooltipLength: 0 });
|
|
|
|
copyTest({
|
|
|
|
name: 'tooltips custom text',
|
|
|
|
tooltips: ['a', 'b'],
|
|
|
|
tooltipLength: 1,
|
|
|
|
tooltipTexts: ['a', 'b'],
|
|
|
|
});
|
|
|
|
copyTest({
|
|
|
|
name: 'tooltips custom element ',
|
|
|
|
tooltips: [dom, dom2],
|
|
|
|
tooltipTexts: ['12', '34'],
|
|
|
|
});
|
|
|
|
copyTest({
|
|
|
|
name: 'tooltips first empty',
|
|
|
|
tooltips: ['', 'xxx'],
|
|
|
|
tooltipLength: 0,
|
|
|
|
});
|
|
|
|
copyTest({
|
|
|
|
name: 'tooltips first empty 2',
|
|
|
|
tooltips: [''],
|
|
|
|
tooltipLength: 0,
|
|
|
|
});
|
|
|
|
|
|
|
|
copyTest({
|
|
|
|
name: 'tooltips true true',
|
|
|
|
tooltips: [true, true],
|
|
|
|
tooltipTexts: ['Copy', 'Copied'],
|
|
|
|
});
|
|
|
|
copyTest({
|
|
|
|
name: 'tooltips true false',
|
|
|
|
tooltips: [true, false],
|
|
|
|
tooltipTexts: ['Copy', ''],
|
|
|
|
});
|
|
|
|
|
|
|
|
copyTest({
|
|
|
|
name: 'tooltips false true',
|
|
|
|
tooltips: [false, true],
|
|
|
|
tooltipLength: 0,
|
|
|
|
});
|
|
|
|
});
|
2022-02-10 16:22:31 +08:00
|
|
|
|
|
|
|
it('copy click event stopPropagation', () => {
|
2023-06-07 21:59:21 +08:00
|
|
|
const onDivClick = jest.fn();
|
2022-06-09 18:09:43 +08:00
|
|
|
const { container: wrapper } = render(
|
2022-02-10 16:22:31 +08:00
|
|
|
<div onClick={onDivClick}>
|
|
|
|
<Base component="p" copyable>
|
|
|
|
test copy
|
|
|
|
</Base>
|
|
|
|
</div>,
|
|
|
|
);
|
2022-06-09 18:09:43 +08:00
|
|
|
fireEvent.click(wrapper.querySelectorAll('.ant-typography-copy')[0]);
|
2022-08-30 10:57:13 +08:00
|
|
|
expect(onDivClick).not.toHaveBeenCalled();
|
2022-02-10 16:22:31 +08:00
|
|
|
});
|
2022-02-16 19:41:06 +08:00
|
|
|
|
2022-03-22 16:52:44 +08:00
|
|
|
it('the first parameter of onCopy is the click event', () => {
|
2024-04-01 15:49:45 +08:00
|
|
|
function onCopy(e?: React.MouseEvent<HTMLDivElement>) {
|
2022-03-22 16:52:44 +08:00
|
|
|
expect(e).not.toBeUndefined();
|
|
|
|
}
|
2022-06-09 18:09:43 +08:00
|
|
|
|
|
|
|
const { container: wrapper } = render(
|
2022-03-22 16:52:44 +08:00
|
|
|
<Base component="p" copyable={{ onCopy }}>
|
|
|
|
test copy
|
|
|
|
</Base>,
|
|
|
|
);
|
2022-06-09 18:09:43 +08:00
|
|
|
fireEvent.click(wrapper.querySelectorAll('.ant-typography-copy')[0]);
|
2022-03-22 16:52:44 +08:00
|
|
|
});
|
|
|
|
|
2023-07-28 16:17:43 +08:00
|
|
|
it('copy to clipboard', async () => {
|
2023-06-07 21:59:21 +08:00
|
|
|
jest.useFakeTimers();
|
|
|
|
const spy = jest.spyOn(copyObj, 'default');
|
2022-02-16 19:41:06 +08:00
|
|
|
const originText = 'origin text.';
|
|
|
|
const nextText = 'next text.';
|
|
|
|
const Test = () => {
|
|
|
|
const [dynamicText, setDynamicText] = React.useState(originText);
|
|
|
|
React.useEffect(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
setDynamicText(nextText);
|
|
|
|
}, 500);
|
2022-06-17 15:07:32 +08:00
|
|
|
}, []);
|
2022-02-16 19:41:06 +08:00
|
|
|
return (
|
|
|
|
<Base component="p" copyable>
|
|
|
|
{dynamicText}
|
|
|
|
</Base>
|
|
|
|
);
|
|
|
|
};
|
2022-06-09 18:09:43 +08:00
|
|
|
const { container: wrapper } = render(<Test />);
|
|
|
|
const copyBtn = wrapper.querySelectorAll('.ant-typography-copy')[0];
|
|
|
|
fireEvent.click(copyBtn);
|
2022-02-16 19:41:06 +08:00
|
|
|
expect(spy.mock.calls[0][0]).toEqual(originText);
|
2023-07-28 16:17:43 +08:00
|
|
|
await waitFakeTimer();
|
2022-06-17 15:07:32 +08:00
|
|
|
spy.mockReset();
|
|
|
|
fireEvent.click(copyBtn);
|
|
|
|
expect(spy.mock.calls[0][0]).toEqual(nextText);
|
2023-06-07 21:59:21 +08:00
|
|
|
jest.useRealTimers();
|
2024-03-29 13:20:02 +08:00
|
|
|
spy.mockReset();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('copy by async', async () => {
|
|
|
|
const spy = jest.spyOn(copyObj, 'default');
|
|
|
|
const { container: wrapper } = render(
|
|
|
|
<Base
|
|
|
|
component="p"
|
|
|
|
copyable={{
|
|
|
|
text: jest.fn().mockResolvedValueOnce('Request text'),
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
test copy
|
|
|
|
</Base>,
|
|
|
|
);
|
|
|
|
fireEvent.click(wrapper.querySelectorAll('.ant-typography-copy')[0]);
|
|
|
|
expect(wrapper.querySelectorAll('.anticon-loading')[0]).toBeTruthy();
|
|
|
|
await waitFakeTimer();
|
|
|
|
expect(spy.mock.calls[0][0]).toEqual('Request text');
|
|
|
|
spy.mockReset();
|
|
|
|
expect(wrapper.querySelectorAll('.anticon-loading')[0]).toBeFalsy();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('useCopyClick error', async () => {
|
|
|
|
const { result } = renderHook(() =>
|
|
|
|
useCopyClick({
|
|
|
|
copyConfig: {
|
|
|
|
text: jest.fn().mockRejectedValueOnce('Oops'),
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
await expect(() => result.current?.onClick?.()).rejects.toMatch('Oops');
|
|
|
|
expect(result.current?.copyLoading).toBe(false);
|
2022-02-16 19:41:06 +08:00
|
|
|
});
|
2021-06-06 13:41:26 +08:00
|
|
|
});
|
2024-04-09 11:00:52 +08:00
|
|
|
|
|
|
|
it('not block copy text change', () => {
|
|
|
|
const spy = jest.spyOn(copyObj, 'default');
|
|
|
|
|
|
|
|
const renderDemo = (text: string) => (
|
|
|
|
<Base copyable={{ text }} component="p">
|
|
|
|
Text
|
|
|
|
</Base>
|
|
|
|
);
|
|
|
|
|
|
|
|
const { container, rerender } = render(renderDemo('Bamboo'));
|
|
|
|
rerender(renderDemo('Light'));
|
|
|
|
|
|
|
|
fireEvent.click(container.querySelector('.ant-typography-copy')!);
|
|
|
|
expect(spy.mock.calls[0][0]).toBe('Light');
|
|
|
|
|
|
|
|
spy.mockRestore();
|
|
|
|
});
|
2024-04-09 12:01:46 +08:00
|
|
|
|
|
|
|
it('dynamic set editable', () => {
|
|
|
|
const { container, rerender } = render(<Base component="p">test</Base>);
|
|
|
|
expect(container.querySelector('.ant-typography-copy')).toBeFalsy();
|
|
|
|
|
|
|
|
rerender(
|
|
|
|
<Base component="p" copyable>
|
|
|
|
test
|
|
|
|
</Base>,
|
|
|
|
);
|
|
|
|
expect(container.querySelector('.ant-typography-copy')).toBeTruthy();
|
|
|
|
});
|
2024-04-24 18:06:38 +08:00
|
|
|
|
|
|
|
it('tabIndex of copy button', () => {
|
|
|
|
const { container } = render(
|
|
|
|
<Base component="p" copyable={{ tabIndex: -1 }}>
|
|
|
|
test
|
|
|
|
</Base>,
|
|
|
|
);
|
|
|
|
expect(container.querySelector('.ant-typography-copy')?.getAttribute('tabIndex')).toBe('-1');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('locale text for button tooltip', async () => {
|
|
|
|
const { container } = render(
|
|
|
|
<Base component="p" copyable>
|
|
|
|
test
|
|
|
|
</Base>,
|
|
|
|
);
|
|
|
|
fireEvent.mouseEnter(container.querySelectorAll('.ant-typography-copy')[0]);
|
|
|
|
await waitFakeTimer();
|
|
|
|
await waitFor(() => {
|
|
|
|
expect(container.querySelector('.ant-tooltip-inner')?.textContent).toBe('Copy');
|
|
|
|
});
|
|
|
|
fireEvent.click(container.querySelectorAll('.ant-typography-copy')[0]);
|
|
|
|
expect(container.querySelector('.ant-tooltip-inner')?.textContent).toBe('Copied');
|
|
|
|
});
|
2021-06-06 13:41:26 +08:00
|
|
|
});
|