mirror of
https://github.com/streamich/react-use.git
synced 2026-01-25 14:17:16 +00:00
Merge pull request #899 from cleverjam/useCopyToClipboard-tests
refactor: use copy to clipboard
This commit is contained in:
commit
5d4785750d
@ -18,31 +18,46 @@ const useCopyToClipboard = (): [CopyToClipboardState, (value: string) => void] =
|
||||
});
|
||||
|
||||
const copyToClipboard = useCallback(value => {
|
||||
if (!isMounted()) {
|
||||
return;
|
||||
}
|
||||
let noUserInteraction;
|
||||
let normalizedValue;
|
||||
try {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
if (typeof value !== 'string') {
|
||||
console.error(`Cannot copy typeof ${typeof value} to clipboard, must be a string`);
|
||||
}
|
||||
}
|
||||
|
||||
const noUserInteraction = writeText(value);
|
||||
|
||||
if (!isMounted()) {
|
||||
// only strings and numbers casted to strings can be copied to clipboard
|
||||
if (typeof value !== 'string' && typeof value !== 'number') {
|
||||
const error = new Error(`Cannot copy typeof ${typeof value} to clipboard, must be a string`);
|
||||
if (process.env.NODE_ENV === 'development') console.error(error);
|
||||
setState({
|
||||
value,
|
||||
error,
|
||||
noUserInteraction: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
// empty strings are also considered invalid
|
||||
else if (value === '') {
|
||||
const error = new Error(`Cannot copy empty string to clipboard.`);
|
||||
if (process.env.NODE_ENV === 'development') console.error(error);
|
||||
setState({
|
||||
value,
|
||||
error,
|
||||
noUserInteraction: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
normalizedValue = value.toString();
|
||||
noUserInteraction = writeText(normalizedValue);
|
||||
setState({
|
||||
value,
|
||||
value: normalizedValue,
|
||||
error: undefined,
|
||||
noUserInteraction,
|
||||
});
|
||||
} catch (error) {
|
||||
if (!isMounted()) {
|
||||
return;
|
||||
}
|
||||
setState({
|
||||
value: undefined,
|
||||
value: normalizedValue,
|
||||
error,
|
||||
noUserInteraction: true,
|
||||
noUserInteraction,
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
103
tests/useCopyToClipboard.test.ts
Normal file
103
tests/useCopyToClipboard.test.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import writeText from 'copy-to-clipboard';
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import { useCopyToClipboard } from '../src';
|
||||
|
||||
const valueToRaiseMockException = 'fake input causing exception in copy to clipboard';
|
||||
|
||||
jest.mock('copy-to-clipboard', () =>
|
||||
jest.fn().mockImplementation(input => {
|
||||
if (input === valueToRaiseMockException) {
|
||||
throw new Error(input);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
);
|
||||
jest.spyOn(global.console, 'error').mockImplementation(() => {});
|
||||
|
||||
describe('useCopyToClipboard', () => {
|
||||
let hook;
|
||||
|
||||
beforeEach(() => {
|
||||
hook = renderHook(() => useCopyToClipboard());
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should be defined ', () => {
|
||||
expect(useCopyToClipboard).toBeDefined();
|
||||
});
|
||||
|
||||
it('should pass a given value to copy to clipboard and set state', () => {
|
||||
const testValue = 'test';
|
||||
let [state, copyToClipboard] = hook.result.current;
|
||||
act(() => copyToClipboard(testValue));
|
||||
[state, copyToClipboard] = hook.result.current;
|
||||
|
||||
expect(writeText).toBeCalled();
|
||||
expect(state.value).toBe(testValue);
|
||||
expect(state.noUserInteraction).toBe(true);
|
||||
expect(state.error).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should not call writeText if passed an invalid input and set state', () => {
|
||||
let testValue = {}; // invalid value
|
||||
let [state, copyToClipboard] = hook.result.current;
|
||||
act(() => copyToClipboard(testValue));
|
||||
[state, copyToClipboard] = hook.result.current;
|
||||
|
||||
expect(writeText).not.toBeCalled();
|
||||
expect(state.value).toBe(testValue);
|
||||
expect(state.noUserInteraction).toBe(true);
|
||||
expect(state.error).toBeDefined();
|
||||
|
||||
testValue = ''; // emtpy string is also invalid
|
||||
act(() => copyToClipboard(testValue));
|
||||
[state, copyToClipboard] = hook.result.current;
|
||||
|
||||
expect(writeText).not.toBeCalled();
|
||||
expect(state.value).toBe(testValue);
|
||||
expect(state.noUserInteraction).toBe(true);
|
||||
expect(state.error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should catch exception thrown by copy-to-clipboard and set state', () => {
|
||||
let [state, copyToClipboard] = hook.result.current;
|
||||
act(() => copyToClipboard(valueToRaiseMockException));
|
||||
[state, copyToClipboard] = hook.result.current;
|
||||
|
||||
expect(writeText).toBeCalledWith(valueToRaiseMockException);
|
||||
expect(state.value).toBe(valueToRaiseMockException);
|
||||
expect(state.noUserInteraction).not.toBeDefined();
|
||||
expect(state.error).toStrictEqual(new Error(valueToRaiseMockException));
|
||||
});
|
||||
|
||||
it('should return initial state while unmounted', () => {
|
||||
hook.unmount();
|
||||
const [state, copyToClipboard] = hook.result.current;
|
||||
|
||||
act(() => copyToClipboard('value'));
|
||||
expect(state.value).not.toBeDefined();
|
||||
expect(state.error).not.toBeDefined();
|
||||
expect(state.noUserInteraction).toBe(true);
|
||||
});
|
||||
|
||||
it('should console error if in dev environment', () => {
|
||||
const ORIGINAL_NODE_ENV = process.env.NODE_ENV;
|
||||
const testValue = {}; // invalid value
|
||||
|
||||
process.env.NODE_ENV = 'development';
|
||||
let [state, copyToClipboard] = hook.result.current;
|
||||
act(() => copyToClipboard(testValue));
|
||||
process.env.NODE_ENV = ORIGINAL_NODE_ENV;
|
||||
|
||||
[state, copyToClipboard] = hook.result.current;
|
||||
|
||||
expect(writeText).not.toBeCalled();
|
||||
expect(console.error).toBeCalled();
|
||||
expect(state.value).toBe(testValue);
|
||||
expect(state.noUserInteraction).toBe(true);
|
||||
expect(state.error).toBeDefined();
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user