diff --git a/contributing/event-dispatch.md b/contributing/event-dispatch.md index 05eee8511..f5d09203d 100644 --- a/contributing/event-dispatch.md +++ b/contributing/event-dispatch.md @@ -2,7 +2,7 @@ RNTL has two ways to trigger events. Neither goes through React Native's native event system. Both find `on*` props in the rendered tree and call them inside `act()`. -Both are built on the shared event subsystem in `src/events/legacy/`, which also holds `fireEvent` itself. This is the `'legacy'` event system, the default for the `unstable_eventSystem` config option. +Both are built on the shared event subsystem in `src/events/legacy/`, which also holds `fireEvent` itself. This is the `'legacy'` event system, the default for the `eventSystem` config option. The `'modern'` event system (`src/events/modern/`), still in progress, follows the W3C [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) dispatch that React Native enables by default from 0.88 (the `enableNativeEventTargetEventDispatching` feature flag; `src/private/renderer/events/dispatchNativeEvent.js` and `src/private/webapis/dom/events/EventTarget.js` in `node_modules/react-native`). The path holds only host elements. Bubbling events run a capture phase from the root to the target (`on*Capture` props), then a bubble phase back up (`on*` props), and direct events reach only the target. Event objects have the W3C `Event` fields (`eventPhase`, `currentTarget`, `composedPath()`, `stopPropagation()`, `preventDefault()`) next to the legacy synthetic event API (`nativeEvent`, `persist()`, `isDefaultPrevented()`). Check changes to modern dispatch against these upstream files, not React's legacy plugin system. @@ -48,7 +48,7 @@ Modern `fireEvent` doesn't log `eventDiagnostics` warnings yet. Only legacy `fir Each step dispatches a native event, created for the configured event system by `createEvent()` (`src/events/create-event.ts`: a legacy event object or a `SyntheticEvent`) and dispatched by `dispatchEvent()` (`src/events/dispatch-event.ts`), or calls a JavaScript callback (`changeText`, `pressIn`, ...) with `invokeEventHandler()`, which only calls the target's own handler. Modern `fireEvent.changeText()` instead calls `onChangeText` from the `change` dispatch, through `dispatchEvent()`'s `afterTargetHandler` option, right after the input's own `onChange`, where `TextInput` calls it. Neither checks whether the element is enabled. Each action does those checks itself, so the rules for an interaction live in one place. -Where the two event systems need different rules, the action picks them by `getConfig().unstable_eventSystem` from `src/user-event/legacy/` or `src/user-event/modern/`, e.g. for `press()` and `longPress()`. Legacy `findPressTarget()` finds the element, then the action calls `onResponderGrant` and `onResponderRelease` of a touch responder, so Pressability runs with its timers, or the `onPress*` props of a host, with stub touch events. Modern `emitPressEvents()` reuses modern `fireEvent`'s hit testing and responder negotiation (`getTouchPath()` and `findResponder()` in `src/events/modern/pressability.ts`), so both press the same element. It keeps the press on a `TextInput`, which claims the touch on a device even without `onPress*` props; the legacy one passes it on to the nearest ancestor that handles it. It then calls the responder's Pressability callbacks itself, in Pressability's order, with `SyntheticEvent`s (`dispatchPressabilityEvent()`): `onPressIn`, `onLongPress` for `longPress()`, `onPressOut`, then `onPress` unless `onLongPress` was called. The action, not the touch duration, decides whether it is a long press, so neither action waits by default: `duration` only holds the touch, for timers in the component. Pressability's timing props (`delayLongPress`, `minPressDuration`, `delayPressIn`, `delayPressOut`) are ignored. +Where the two event systems need different rules, the action picks them by `getConfig().eventSystem` from `src/user-event/legacy/` or `src/user-event/modern/`, e.g. for `press()` and `longPress()`. Legacy `findPressTarget()` finds the element, then the action calls `onResponderGrant` and `onResponderRelease` of a touch responder, so Pressability runs with its timers, or the `onPress*` props of a host, with stub touch events. Modern `emitPressEvents()` reuses modern `fireEvent`'s hit testing and responder negotiation (`getTouchPath()` and `findResponder()` in `src/events/modern/pressability.ts`), so both press the same element. It keeps the press on a `TextInput`, which claims the touch on a device even without `onPress*` props; the legacy one passes it on to the nearest ancestor that handles it. It then calls the responder's Pressability callbacks itself, in Pressability's order, with `SyntheticEvent`s (`dispatchPressabilityEvent()`): `onPressIn`, `onLongPress` for `longPress()`, `onPressOut`, then `onPress` unless `onLongPress` was called. The action, not the touch duration, decides whether it is a long press, so neither action waits by default: `duration` only holds the touch, for timers in the component. Pressability's timing props (`delayLongPress`, `minPressDuration`, `delayPressIn`, `delayPressOut`) are ignored. For the `eventDiagnostics` warning, each action tracks itself with an `Interaction` from `src/user-event/utils/interaction.ts`: diff --git a/contributing/testing.md b/contributing/testing.md index 1992db051..44ef48dea 100644 --- a/contributing/testing.md +++ b/contributing/testing.md @@ -16,14 +16,14 @@ Every change in `src/` should come with tests. Tests use Jest and live next to t ## Event systems -`fireEvent` and `userEvent` work with both event systems (`configure({ unstable_eventSystem })`), so `jest.config.js` has two projects: +`fireEvent` and `userEvent` work with both event systems (`configure({ eventSystem })`), so `jest.config.js` has two projects: - `legacy` runs all tests with the default `'legacy'` event system. - `modern` runs all tests again with `'modern'` (`jest-setup-modern.ts`). Both projects share the same snapshots. `createEventLogger()` entries print only the `nativeEvent` of event payloads (`src/test-utils/event-serializer.ts`), so a snapshot is the same for a legacy event object and a modern `SyntheticEvent`, and a difference between the two systems fails the snapshot. Use `--selectProjects legacy` or `--selectProjects modern` to run one of them. -When a test expects different behavior in the two systems, e.g. events bubbling to a parent, branch on `getConfig().unstable_eventSystem` inside the test instead of skipping it, with a short comment saying which system matches React Native. Name the test after the modern behavior, which aims to match React Native, with the legacy difference in a `(legacy: ...)` suffix, e.g. `does not bubble $name (legacy: bubbles with a warning)`. Prefer ternaries in the expectations (`toHaveBeenCalledTimes(isModern ? 0 : 1)`). For assertions only one system can make, like a legacy warning message, end the test with `if (isModern) return;` before them (`jest/no-conditional-expect` forbids `expect()` inside `if` blocks). Generic `fireEvent(element, eventType, event)` calls that run in both systems pass an event object, e.g. `{ nativeEvent: {} }`, as modern `fireEvent` requires one. `src/events/__tests__/fire-event.test.tsx` runs the public `fireEvent` this way. +When a test expects different behavior in the two systems, e.g. events bubbling to a parent, branch on `getConfig().eventSystem` inside the test instead of skipping it, with a short comment saying which system matches React Native. Name the test after the modern behavior, which aims to match React Native, with the legacy difference in a `(legacy: ...)` suffix, e.g. `does not bubble $name (legacy: bubbles with a warning)`. Prefer ternaries in the expectations (`toHaveBeenCalledTimes(isModern ? 0 : 1)`). For assertions only one system can make, like a legacy warning message, end the test with `if (isModern) return;` before them (`jest/no-conditional-expect` forbids `expect()` inside `if` blocks). Generic `fireEvent(element, eventType, event)` calls that run in both systems pass an event object, e.g. `{ nativeEvent: {} }`, as modern `fireEvent` requires one. `src/events/__tests__/fire-event.test.tsx` runs the public `fireEvent` this way. Tests that can't run in the modern event system at all can call `runInLegacyEventSystem()` from `src/test-utils/event-system.ts` at the top of the file (or in a `describe()`). They then run in the legacy event system in both projects. diff --git a/docs/api/configuration.md b/docs/api/configuration.md index 322d52550..3c33a9069 100644 --- a/docs/api/configuration.md +++ b/docs/api/configuration.md @@ -13,6 +13,9 @@ type Config = { /** Warn when `fireEvent` or a `userEvent` interaction calls no handler. Off by default. */ eventDiagnostics: boolean; + /** Event system used by `fireEvent` and `userEvent`. Defaults to `'legacy'`. */ + eventSystem: 'legacy' | 'modern'; + /** Default options for `debug` helper. */ defaultDebugOptions?: Partial; }; @@ -56,6 +59,19 @@ No warning is logged when the event updates native state, e.g. `fireEvent.change configure({ eventDiagnostics: true }); ``` +### `eventSystem` option + +Event system used by [`fireEvent`](./fire-event.md), [`userEvent`](./user-event.md) and [`UNSAFE_invokeHandler`](./fire-event.md#unsafe-invoke-handler). Defaults to `'legacy'`. + +- `'legacy'`: the simplified event system RNTL has used from the beginning. +- `'modern'` (recommended): simulates React Native's event dispatch, with direct and bubbling events and `SyntheticEvent` objects. + +```ts +configure({ eventSystem: 'modern' }); +``` + +See [Modern Event System](/docs/api/events/modern-event-system) for the differences and how to enable it. + ### `defaultDebugOptions` option Default [debug options](#debug) to be used when calling `debug()`. These default options will be overridden by the ones you specify directly when calling `debug()`. diff --git a/jest-setup-modern.ts b/jest-setup-modern.ts index 6274d014d..5526da7f2 100644 --- a/jest-setup-modern.ts +++ b/jest-setup-modern.ts @@ -2,5 +2,5 @@ import { configure } from './src/pure'; // Runs after `resetToDefaults()` in `jest-setup.ts`. beforeEach(() => { - configure({ unstable_eventSystem: 'modern' }); + configure({ eventSystem: 'modern' }); }); diff --git a/jest.config.js b/jest.config.js index 8fe6f054f..b5a21f24c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -22,7 +22,7 @@ module.exports = { ], projects: [ { ...baseProject, displayName: 'legacy' }, - // All tests again with `configure({ unstable_eventSystem: 'modern' })`. They share snapshots with the + // All tests again with `configure({ eventSystem: 'modern' })`. They share snapshots with the // legacy run, so both event systems must call the same handlers with the same native events. { ...baseProject, diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts index 5b88a76f8..ff4517054 100644 --- a/src/__tests__/config.test.ts +++ b/src/__tests__/config.test.ts @@ -13,7 +13,7 @@ afterEach(() => { test('getConfig() returns existing configuration', () => { expect(getConfig().asyncUtilTimeout).toEqual(1000); expect(getConfig().defaultIncludeHiddenElements).toEqual(false); - expect(getConfig().unstable_eventSystem).toEqual('legacy'); + expect(getConfig().eventSystem).toEqual('legacy'); }); test('configure() overrides existing config values', () => { @@ -24,19 +24,19 @@ test('configure() overrides existing config values', () => { defaultDebugOptions: { message: 'debug message' }, defaultIncludeHiddenElements: false, eventDiagnostics: false, - unstable_eventSystem: 'legacy', + eventSystem: 'legacy', }); }); -test('configure() sets unstable_eventSystem', () => { - configure({ unstable_eventSystem: 'modern' }); - expect(getConfig().unstable_eventSystem).toEqual('modern'); +test('configure() sets eventSystem', () => { + configure({ eventSystem: 'modern' }); + expect(getConfig().eventSystem).toEqual('modern'); configure({ asyncUtilTimeout: 2000 }); - expect(getConfig().unstable_eventSystem).toEqual('modern'); + expect(getConfig().eventSystem).toEqual('modern'); resetToDefaults(); - expect(getConfig().unstable_eventSystem).toEqual('legacy'); + expect(getConfig().eventSystem).toEqual('legacy'); }); test('resetToDefaults() resets config to defaults', () => { @@ -79,7 +79,7 @@ test('does not warn when only valid options are passed', () => { defaultIncludeHiddenElements: true, defaultDebugOptions: { message: 'test' }, defaultHidden: false, - unstable_eventSystem: 'modern', + eventSystem: 'modern', }); expect(_console.warn).not.toHaveBeenCalled(); diff --git a/src/__tests__/modern-types/fire-event.test.tsx b/src/__tests__/modern-types/fire-event.test.tsx index bf2c405a6..8ff338f78 100644 --- a/src/__tests__/modern-types/fire-event.test.tsx +++ b/src/__tests__/modern-types/fire-event.test.tsx @@ -11,7 +11,7 @@ import { _console } from '../../helpers/logger'; // modern signatures. Calls the types reject also throw at runtime. beforeEach(() => { - configure({ unstable_eventSystem: 'modern' }); + configure({ eventSystem: 'modern' }); }); const emptyEvent = { nativeEvent: {} }; diff --git a/src/config.ts b/src/config.ts index 895b06829..5b099c0cd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -21,11 +21,10 @@ export type Config = { eventDiagnostics: boolean; /** - * Experimental, may change or be removed in any release. Event system used by `fireEvent` and - * `userEvent`. `'legacy'` is the current simplified implementation. `'modern'` follows React - * Native's event dispatch. Defaults to `'legacy'`. + * Event system used by `fireEvent` and `userEvent`. `'legacy'` is the current simplified + * implementation. `'modern'` follows React Native's event dispatch. Defaults to `'legacy'`. */ - unstable_eventSystem: EventSystem; + eventSystem: EventSystem; /** Default options for `debug` helper. */ defaultDebugOptions?: Partial; @@ -40,7 +39,7 @@ const defaultConfig: Config = { asyncUtilTimeout: 1000, defaultIncludeHiddenElements: false, eventDiagnostics: false, - unstable_eventSystem: 'legacy', + eventSystem: 'legacy', }; let config = { ...defaultConfig }; @@ -55,7 +54,7 @@ export function configure(options: Partial) { defaultHidden, defaultIncludeHiddenElements, eventDiagnostics, - unstable_eventSystem, + eventSystem, ...rest } = options; @@ -70,7 +69,7 @@ export function configure(options: Partial) { defaultDebugOptions, defaultIncludeHiddenElements: resolvedDefaultIncludeHiddenElements, eventDiagnostics: eventDiagnostics ?? config.eventDiagnostics, - unstable_eventSystem: unstable_eventSystem ?? config.unstable_eventSystem, + eventSystem: eventSystem ?? config.eventSystem, }; } diff --git a/src/events/__tests__/create-event.test.ts b/src/events/__tests__/create-event.test.ts index d37ba1a14..de4867822 100644 --- a/src/events/__tests__/create-event.test.ts +++ b/src/events/__tests__/create-event.test.ts @@ -19,7 +19,7 @@ afterEach(() => { }); test('builds a legacy event object in the legacy event system', () => { - configure({ unstable_eventSystem: 'legacy' }); + configure({ eventSystem: 'legacy' }); const nativeEvent = { target: 0 }; const event = createEvent('focus', nativeEvent); @@ -29,7 +29,7 @@ test('builds a legacy event object in the legacy event system', () => { }); test('builds a SyntheticEvent in the modern event system', () => { - configure({ unstable_eventSystem: 'modern' }); + configure({ eventSystem: 'modern' }); const nativeEvent = { target: 0 }; const event = createEvent('focus', nativeEvent); @@ -42,7 +42,7 @@ test('builds a SyntheticEvent in the modern event system', () => { test.each(['legacy', 'modern'] as const)( 'throws for event types that are not native in the %s event system', (eventSystem) => { - configure({ unstable_eventSystem: eventSystem }); + configure({ eventSystem: eventSystem }); expect(() => createEvent('changeText', {})).toThrow( '"changeText" is not a native event type. Use invokeEventHandler() instead.', @@ -64,9 +64,9 @@ test.each([ ])('builds %s event for the configured event system', (type, build) => { // The touch payload has a `Date.now()` timestamp. jest.spyOn(Date, 'now').mockReturnValue(1); - configure({ unstable_eventSystem: 'legacy' }); + configure({ eventSystem: 'legacy' }); const legacyEvent = build(); - configure({ unstable_eventSystem: 'modern' }); + configure({ eventSystem: 'modern' }); const modernEvent = build(); expect(legacyEvent).not.toBeInstanceOf(SyntheticEvent); diff --git a/src/events/__tests__/fire-event.test.tsx b/src/events/__tests__/fire-event.test.tsx index 667d5c0a4..99a6a6c5e 100644 --- a/src/events/__tests__/fire-event.test.tsx +++ b/src/events/__tests__/fire-event.test.tsx @@ -3956,9 +3956,9 @@ test('fireEvent reads the event system on each call', async () => { const onLayout = jest.fn(); await render(); - configure({ unstable_eventSystem: 'legacy' }); + configure({ eventSystem: 'legacy' }); await fireEvent.layout(screen.getByTestId('view')); - configure({ unstable_eventSystem: 'modern' }); + configure({ eventSystem: 'modern' }); await fireEvent.layout(screen.getByTestId('view')); expect(onLayout.mock.calls[0][0]).not.toBeInstanceOf(SyntheticEvent); expect(onLayout.mock.calls[1][0]).toBeInstanceOf(SyntheticEvent); diff --git a/src/events/create-event.ts b/src/events/create-event.ts index 58aff016b..15aad1905 100644 --- a/src/events/create-event.ts +++ b/src/events/create-event.ts @@ -22,7 +22,7 @@ export type LegacyEvent = ReturnType> /** * Creates the event object for a native event (`focus`, `change`, `scroll`, ...) in the event - * system selected by `configure({ unstable_eventSystem })`: a legacy event object with stubs, or a + * system selected by `configure({ eventSystem })`: a legacy event object with stubs, or a * `SyntheticEvent`. Used by `userEvent`, with `dispatchEvent()`. Unlike modern `createEvent()`, * takes the `nativeEvent` itself, not an init object. * @@ -39,7 +39,7 @@ export function createEvent( throw new Error(`"${eventType}" is not a native event type. Use invokeEventHandler() instead.`); } - if (getConfig().unstable_eventSystem === 'legacy') { + if (getConfig().eventSystem === 'legacy') { return wrapNativeEvent(nativeEvent); } diff --git a/src/events/fire-event.ts b/src/events/fire-event.ts index 55b9f4ed6..fb2ef25fb 100644 --- a/src/events/fire-event.ts +++ b/src/events/fire-event.ts @@ -61,7 +61,7 @@ export type FireEvent = RegisteredEventSystem extends 'modern' ? ModernFireEvent /** * Public `fireEvent`. Calls the implementation of the event system selected by - * `configure({ unstable_eventSystem })`, read on each call, so `configure()` inside a test applies. + * `configure({ eventSystem })`, read on each call, so `configure()` inside a test applies. * * Types follow the legacy signatures, or the modern ones when `Register` sets `eventSystem`. The * modern implementation also checks its arguments at runtime. @@ -117,7 +117,7 @@ fireEventImpl.layout = async ( }; function isModern() { - return getConfig().unstable_eventSystem === 'modern'; + return getConfig().eventSystem === 'modern'; } const fireEvent = fireEventImpl as FireEvent; diff --git a/src/events/modern/dispatch.ts b/src/events/modern/dispatch.ts index 898a802a4..8d0a40bb8 100644 --- a/src/events/modern/dispatch.ts +++ b/src/events/modern/dispatch.ts @@ -138,7 +138,7 @@ function dispatchBubblingEvent( } /** Target first, then host ancestors. The root container is not an element, so it's excluded. */ -function getAncestorPath(target: TestInstance): TestInstance[] { +export function getAncestorPath(target: TestInstance): TestInstance[] { const path: TestInstance[] = []; let current: TestInstance | null = target; while (current?.parent != null) { diff --git a/src/events/modern/pressability.ts b/src/events/modern/pressability.ts index c9fd3062d..d2c80d9e5 100644 --- a/src/events/modern/pressability.ts +++ b/src/events/modern/pressability.ts @@ -12,6 +12,7 @@ import { } from '../shared/handler'; import { getPointerEventsBlocker } from '../shared/pointer-events'; import type { EventHandler } from '../shared/types'; +import { getAncestorPath } from './dispatch'; import type { SyntheticEvent } from './event'; import { getEventInternals } from './event'; import type { PressabilityEventType } from './event-types'; @@ -111,19 +112,12 @@ export function getTouchPath(target: TestInstance): TestInstance[] { return []; } - let hitTarget: TestInstance | null = target; - while (hitTarget?.parent != null && getPointerEventsBlocker(hitTarget) != null) { + let hitTarget = target; + while (hitTarget.parent != null && getPointerEventsBlocker(hitTarget) != null) { hitTarget = hitTarget.parent; } - const path: TestInstance[] = []; - let current = hitTarget; - while (current?.parent != null) { - path.push(current); - current = current.parent; - } - - return path; + return getAncestorPath(hitTarget); } /** diff --git a/src/modern-types.ts b/src/modern-types.ts index 586f056bb..6fd3638fd 100644 --- a/src/modern-types.ts +++ b/src/modern-types.ts @@ -1,6 +1,6 @@ /** * Switches `fireEvent` types to the modern event system. Users import it as - * `@testing-library/react-native/modern-types`, next to `configure({ unstable_eventSystem: 'modern' })`. + * `@testing-library/react-native/modern-types`, next to `configure({ eventSystem: 'modern' })`. * * Not part of the main tsconfig program, as the augmentation would apply to all tests. */ diff --git a/src/test-utils/event-system.ts b/src/test-utils/event-system.ts index 64fc86046..91ad871e6 100644 --- a/src/test-utils/event-system.ts +++ b/src/test-utils/event-system.ts @@ -1,7 +1,7 @@ import { configure, getConfig } from '../config'; export function isModernEventSystem() { - return getConfig().unstable_eventSystem === 'modern'; + return getConfig().eventSystem === 'modern'; } /** @@ -10,6 +10,6 @@ export function isModernEventSystem() { */ export function runInLegacyEventSystem() { beforeEach(() => { - configure({ unstable_eventSystem: 'legacy' }); + configure({ eventSystem: 'legacy' }); }); } diff --git a/src/user-event/press/press.ts b/src/user-event/press/press.ts index 1fe25f3d3..2c7a06e31 100644 --- a/src/user-event/press/press.ts +++ b/src/user-event/press/press.ts @@ -46,7 +46,7 @@ async function emitPressEvents( instance: TestInstance, options: EmitPressOptions, ): Promise { - if (getConfig().unstable_eventSystem === 'modern') { + if (getConfig().eventSystem === 'modern') { await emitModernPressEvents(config, interaction, instance, options); } else { await emitLegacyPressEvents(config, interaction, instance, options); diff --git a/website/docs/14.x/docs/api/events/modern-event-system.mdx b/website/docs/14.x/docs/api/events/modern-event-system.mdx index deb52e502..bd58ba55c 100644 --- a/website/docs/14.x/docs/api/events/modern-event-system.mdx +++ b/website/docs/14.x/docs/api/events/modern-event-system.mdx @@ -17,7 +17,7 @@ import { configure } from '@testing-library/react-native'; import '@testing-library/react-native/modern-types'; // Switches `fireEvent` and `userEvent` to the modern event system. -configure({ unstable_eventSystem: 'modern' }); +configure({ eventSystem: 'modern' }); ``` Make sure your `tsconfig.json` includes the setup file. If it doesn't, TypeScript won't see the import, and you won't get the stricter checks. diff --git a/website/docs/14.x/docs/api/misc/config.mdx b/website/docs/14.x/docs/api/misc/config.mdx index f8abebbb7..b2ee57aca 100644 --- a/website/docs/14.x/docs/api/misc/config.mdx +++ b/website/docs/14.x/docs/api/misc/config.mdx @@ -13,6 +13,9 @@ type Config = { /** Warn when `fireEvent` or a `userEvent` interaction calls no handler. Off by default. */ eventDiagnostics: boolean; + /** Event system used by `fireEvent` and `userEvent`. Defaults to `'legacy'`. */ + eventSystem: 'legacy' | 'modern'; + /** Default options for `debug` helper. */ defaultDebugOptions?: Partial; }; @@ -56,6 +59,19 @@ No warning is logged when the event updates native state, e.g. `fireEvent.change configure({ eventDiagnostics: true }); ``` +### `eventSystem` option + +Event system used by [`fireEvent`](/docs/api/events/fire-event), [`userEvent`](/docs/api/events/user-event) and [`UNSAFE_invokeHandler`](/docs/api/events/fire-event#unsafe-invoke-handler). Defaults to `'legacy'`. + +- `'legacy'`: the simplified event system RNTL has used from the beginning. +- `'modern'` (recommended): simulates React Native's event dispatch, with direct and bubbling events and `SyntheticEvent` objects. + +```ts +configure({ eventSystem: 'modern' }); +``` + +See [Modern Event System](/docs/api/events/modern-event-system) for the differences and how to enable it. + ### `defaultDebugOptions` option Default [debug options](#debug) to be used when calling `debug()`. These default options will be overridden by the ones you specify directly when calling `debug()`.