如何使用 RTL 测试未定义的文档?
How to test for document being undefined with RTL?
我有以下 React 挂钩,它将焦点带到给定的 ref 并卸载 returns 焦点到先前聚焦的元素。
export default function useFocusOnElement(elementRef: React.RefObject<HTMLHeadingElement>) {
const documentExists = typeof document !== 'undefined';
const [previouslyFocusedEl] = useState(documentExists && (document.activeElement as HTMLElement));
useEffect(() => {
if (documentExists) {
elementRef.current?.focus();
}
return () => {
if (previouslyFocusedEl) {
previouslyFocusedEl?.focus();
}
};
}, []);
}
这是我为它写的测试。
/**
* @jest-environment jsdom
*/
describe('useFocusOnElement', () => {
let ref: React.RefObject<HTMLDivElement>;
let focusMock: jest.SpyInstance;
beforeEach(() => {
ref = { current: document.createElement('div') } as React.RefObject<HTMLDivElement>;
focusMock = jest.spyOn(ref.current as HTMLDivElement, 'focus');
});
it('will call focus on passed ref after mount ', () => {
expect(focusMock).not.toHaveBeenCalled();
renderHook(() => useFocusOnElement(ref));
expect(focusMock).toHaveBeenCalled();
});
});
我还想测试 document
未定义的情况,因为我们也做 SSR。在挂钩中,我正在检查 document
是否存在,我想测试这两种情况。
JSDOM 包含文档,所以我觉得我需要删除它以及一些如何在我的测试中捕获错误?
首先,要将 document
模拟为 undefined
,您应该像这样模拟它:
jest
.spyOn(global as any, 'document', 'get')
.mockImplementationOnce(() => undefined);
但是对于你测试中的这项工作,你需要在 renderHook
中设置 spyOn
因为看起来它也在内部使用文档,如果你之前设置 spyOn
它,你会得到一个错误。
工作测试示例:
it('will NOT call focus on passed ref after mount', () => {
expect(focusMock).not.toHaveBeenCalled();
renderHook(() => {
jest
.spyOn(global as any, 'document', 'get')
.mockImplementationOnce(() => undefined);
useFocusOnElement(ref);
});
expect(focusMock).not.toHaveBeenCalled();
});
您应该可以通过使用节点环境创建第二个测试文件来做到这一点:
/**
* @jest-environment node
*/
describe('useFocusOnElement server-side', () => {
...
});
我最终使用了 https://github.com/airbnb/jest-wrap 中的 wrapWithGlobal
和 wrapWithOverride
。
describe('useFocusOnElement', () => {
let ref: React.RefObject<HTMLDivElement>;
let focusMock: jest.SpyInstance;
let activeElMock: unknown;
let activeEl: HTMLDivElement;
beforeEach(() => {
const { window } = new JSDOM();
global.document = window.document;
activeEl = document.createElement('div');
ref = { current: document.createElement('div') };
focusMock = jest.spyOn(ref.current as HTMLDivElement, 'focus');
activeElMock = jest.spyOn(activeEl, 'focus');
});
wrapWithOverride(
() => document,
'activeElement',
() => activeEl,
);
describe('when document present', () => {
it('will focus on passed ref after mount and will focus on previously active element on unmount', () => {
const hook = renderHook(() => useFocusOnElement(ref));
expect(focusMock).toHaveBeenCalled();
hook.unmount();
expect(activeElMock).toHaveBeenCalled();
});
});
describe('when no document present', () => {
wrapWithGlobal('document', () => undefined);
it('will not call focus on passed ref after mount nor on previously active element on unmount', () => {
const hook = renderHook(() => useFocusOnElement(ref));
expect(focusMock).not.toHaveBeenCalled();
hook.unmount();
expect(activeElMock).not.toHaveBeenCalled();
});
});
});
我有以下 React 挂钩,它将焦点带到给定的 ref 并卸载 returns 焦点到先前聚焦的元素。
export default function useFocusOnElement(elementRef: React.RefObject<HTMLHeadingElement>) {
const documentExists = typeof document !== 'undefined';
const [previouslyFocusedEl] = useState(documentExists && (document.activeElement as HTMLElement));
useEffect(() => {
if (documentExists) {
elementRef.current?.focus();
}
return () => {
if (previouslyFocusedEl) {
previouslyFocusedEl?.focus();
}
};
}, []);
}
这是我为它写的测试。
/**
* @jest-environment jsdom
*/
describe('useFocusOnElement', () => {
let ref: React.RefObject<HTMLDivElement>;
let focusMock: jest.SpyInstance;
beforeEach(() => {
ref = { current: document.createElement('div') } as React.RefObject<HTMLDivElement>;
focusMock = jest.spyOn(ref.current as HTMLDivElement, 'focus');
});
it('will call focus on passed ref after mount ', () => {
expect(focusMock).not.toHaveBeenCalled();
renderHook(() => useFocusOnElement(ref));
expect(focusMock).toHaveBeenCalled();
});
});
我还想测试 document
未定义的情况,因为我们也做 SSR。在挂钩中,我正在检查 document
是否存在,我想测试这两种情况。
JSDOM 包含文档,所以我觉得我需要删除它以及一些如何在我的测试中捕获错误?
首先,要将 document
模拟为 undefined
,您应该像这样模拟它:
jest
.spyOn(global as any, 'document', 'get')
.mockImplementationOnce(() => undefined);
但是对于你测试中的这项工作,你需要在 renderHook
中设置 spyOn
因为看起来它也在内部使用文档,如果你之前设置 spyOn
它,你会得到一个错误。
工作测试示例:
it('will NOT call focus on passed ref after mount', () => {
expect(focusMock).not.toHaveBeenCalled();
renderHook(() => {
jest
.spyOn(global as any, 'document', 'get')
.mockImplementationOnce(() => undefined);
useFocusOnElement(ref);
});
expect(focusMock).not.toHaveBeenCalled();
});
您应该可以通过使用节点环境创建第二个测试文件来做到这一点:
/**
* @jest-environment node
*/
describe('useFocusOnElement server-side', () => {
...
});
我最终使用了 https://github.com/airbnb/jest-wrap 中的 wrapWithGlobal
和 wrapWithOverride
。
describe('useFocusOnElement', () => {
let ref: React.RefObject<HTMLDivElement>;
let focusMock: jest.SpyInstance;
let activeElMock: unknown;
let activeEl: HTMLDivElement;
beforeEach(() => {
const { window } = new JSDOM();
global.document = window.document;
activeEl = document.createElement('div');
ref = { current: document.createElement('div') };
focusMock = jest.spyOn(ref.current as HTMLDivElement, 'focus');
activeElMock = jest.spyOn(activeEl, 'focus');
});
wrapWithOverride(
() => document,
'activeElement',
() => activeEl,
);
describe('when document present', () => {
it('will focus on passed ref after mount and will focus on previously active element on unmount', () => {
const hook = renderHook(() => useFocusOnElement(ref));
expect(focusMock).toHaveBeenCalled();
hook.unmount();
expect(activeElMock).toHaveBeenCalled();
});
});
describe('when no document present', () => {
wrapWithGlobal('document', () => undefined);
it('will not call focus on passed ref after mount nor on previously active element on unmount', () => {
const hook = renderHook(() => useFocusOnElement(ref));
expect(focusMock).not.toHaveBeenCalled();
hook.unmount();
expect(activeElMock).not.toHaveBeenCalled();
});
});
});