diff --git a/src/document/patchFocus.ts b/src/document/patchFocus.ts index f6e5d83b..fc32a6e2 100644 --- a/src/document/patchFocus.ts +++ b/src/document/patchFocus.ts @@ -5,7 +5,7 @@ const patched = Symbol('patched focus/blur methods') declare global { interface HTMLElement { - readonly [patched]?: Pick + [patched]?: Pick } } @@ -93,11 +93,17 @@ export function restoreFocus(HTMLElement: typeof globalThis['HTMLElement']) { Object.defineProperties(HTMLElement.prototype, { focus: { configurable: true, - get: () => focus, + value: focus, + writable: true, }, blur: { configurable: true, - get: () => blur, + value: blur, + writable: true, + }, + [patched]: { + configurable: true, + get: () => undefined, }, }) } diff --git a/src/index.ts b/src/index.ts index c5e3cdd1..e04ad50f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,32 @@ // the default export is kept for backward compatibility only... export {userEvent as default} from './setup' -export {userEvent} from './setup' +export {userEvent, prepare, reset, detach} from './setup' export type {UserEvent} from './setup/setup' export type {keyboardKey} from './system/keyboard' export type {pointerKey} from './system/pointer' export {PointerEventsCheckLevel, type Options} from './options' + +import {reset, detach} from './setup' + +const g = globalThis as { + afterEach?: (cb?: () => void) => void + afterAll?: (cb?: () => void) => void + window?: Window & typeof globalThis +} + +if (typeof g.afterEach === 'function') { + g.afterEach(() => { + if (g.window) { + reset(g.window) + } + }) +} + +if (typeof g.afterAll === 'function') { + g.afterAll(() => { + if (g.window) { + detach(g.window) + } + }) +} diff --git a/src/setup/index.ts b/src/setup/index.ts index b0308f38..f2328c72 100644 --- a/src/setup/index.ts +++ b/src/setup/index.ts @@ -7,3 +7,9 @@ export const userEvent = { ...directApi, setup: setupMain, } as const + +export { + prepare, + reset, + detach, +} from './setup' diff --git a/src/setup/setup.ts b/src/setup/setup.ts index 9475a4c2..c9747efa 100644 --- a/src/setup/setup.ts +++ b/src/setup/setup.ts @@ -1,4 +1,4 @@ -import {patchFocus} from '../document/patchFocus' +import {patchFocus, restoreFocus} from '../document/patchFocus' import {prepareDocument} from '../document/prepareDocument' import {dispatchEvent, dispatchUIEvent} from '../event' import {defaultKeyMap as defaultKeyboardMap} from '../keyboard/keyMap' @@ -7,8 +7,10 @@ import {Options, PointerEventsCheckLevel} from '../options' import { ApiLevel, attachClipboardStubToView, + detachClipboardStubFromView, getDocumentFromNode, getWindow, + resetClipboardStubOnView, setLevelRef, wait, } from '../utils' @@ -83,12 +85,9 @@ export function createConfig( */ export function setupMain(options: Options = {}) { const config = createConfig(options) - prepareDocument(config.document) - patchFocus(getWindow(config.document).HTMLElement) - const view = - config.document.defaultView ?? /* istanbul ignore next */ globalThis.window - attachClipboardStubToView(view) + const view = getWindow(config.document) + _prepare(view) return createInstance(config).api } @@ -105,8 +104,9 @@ export function setupDirect( node?: Node, ) { const config = createConfig(options, defaultOptionsDirect, node) - prepareDocument(config.document) - patchFocus(getWindow(config.document).HTMLElement) + + const view = getWindow(config.document) + _prepare(view, false) const system = pointerState ?? keyboardState ?? new System() @@ -182,3 +182,44 @@ function getDocument( options.document ?? (node && getDocumentFromNode(node)) ?? defaults.document ) } + +function _prepare( + view: Window & typeof globalThis, + attachClipboardStub = true, +) { + prepareDocument(view.document) + patchFocus(view.HTMLElement) + + if (attachClipboardStub) { + attachClipboardStubToView(view) + } +} + +/** + * @experimental + */ +export function prepare( + view: Window & typeof globalThis, +) { + _prepare(view) +} + +/** + * @experimental + */ +export function reset( + view: Window & typeof globalThis, +) { + restoreFocus(view.HTMLElement) + resetClipboardStubOnView(view) +} + +/** + * @experimental + */ +export function detach( + view: Window & typeof globalThis, +) { + restoreFocus(view.HTMLElement) + detachClipboardStubFromView(view) +} diff --git a/src/utils/dataTransfer/Clipboard.ts b/src/utils/dataTransfer/Clipboard.ts index 7b933695..e8e77580 100644 --- a/src/utils/dataTransfer/Clipboard.ts +++ b/src/utils/dataTransfer/Clipboard.ts @@ -209,17 +209,3 @@ export async function writeDataTransferToClipboard( throw new Error('The Clipboard API is unavailable.') } } - -const g = globalThis as { - afterEach?: (cb?: () => void) => void - afterAll?: (cb?: () => void) => void -} -/* istanbul ignore else */ -if (typeof g.afterEach === 'function') { - g.afterEach(() => resetClipboardStubOnView(globalThis.window)) -} - -/* istanbul ignore else */ -if (typeof g.afterAll === 'function') { - g.afterAll(() => detachClipboardStubFromView(globalThis.window)) -}