@testing-library/react-native 12.5.1 → 12.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -18
- package/build/matchers/to-be-visible.js +2 -6
- package/build/matchers/to-be-visible.js.map +1 -1
- package/build/test-utils/events.d.ts +2 -1
- package/build/test-utils/events.js +6 -2
- package/build/test-utils/events.js.map +1 -1
- package/build/user-event/clear.js +7 -2
- package/build/user-event/clear.js.map +1 -1
- package/build/user-event/type/type.d.ts +9 -1
- package/build/user-event/type/type.js +34 -9
- package/build/user-event/type/type.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
height="80"
|
|
5
5
|
width="80"
|
|
6
6
|
alt="owl"
|
|
7
|
-
src="https://raw.githubusercontent.com/callstack/react-native-testing-library/main/website/
|
|
7
|
+
src="https://raw.githubusercontent.com/callstack/react-native-testing-library/main/website/docs/public/img/owl.png"
|
|
8
8
|
/>
|
|
9
9
|
<p>Developer-friendly and complete React Native testing utilities that encourage good testing practices.</P>
|
|
10
10
|
</div>
|
|
@@ -89,32 +89,34 @@ You can find the source of `QuestionsBoard` component and this example [here](ht
|
|
|
89
89
|
|
|
90
90
|
React Native Testing Library consists of following APIs:
|
|
91
91
|
|
|
92
|
-
- [`render` function](https://callstack.github.io/react-native-testing-library/docs/render) - render your UI components for testing purposes
|
|
93
|
-
- [`screen` object](https://callstack.github.io/react-native-testing-library/docs/screen) - access rendered UI:
|
|
94
|
-
- [Queries](https://callstack.github.io/react-native-testing-library/docs/queries) - find rendered components by various predicates: role, text, test ids, etc
|
|
95
|
-
- Lifecycle methods: [`rerender`](https://callstack.github.io/react-native-testing-library/docs/screen#rerender), [`unmount`](https://callstack.github.io/react-native-testing-library/docs/screen#unmount)
|
|
96
|
-
- Helpers: [`debug`](https://callstack.github.io/react-native-testing-library/docs/screen#debug), [`toJSON`](https://callstack.github.io/react-native-testing-library/docs/screen#tojson), [`root`](https://callstack.github.io/react-native-testing-library/docs/screen#root)
|
|
97
|
-
- [Jest matchers](https://callstack.github.io/react-native-testing-library/docs/jest-matchers) - validate assumptions about your UI
|
|
98
|
-
- [User Event](https://callstack.github.io/react-native-testing-library/docs/user-event) - simulate common user interactions like [`press`](https://callstack.github.io/react-native-testing-library/docs/user-event#press) or [`type`](https://callstack.github.io/react-native-testing-library/docs/user-event#type) in a realistic way
|
|
99
|
-
- [Fire Event](https://callstack.github.io/react-native-testing-library/docs/fire-event) - simulate any component event in a simplified way
|
|
100
|
-
- [`renderHook` function](https://callstack.github.io/react-native-testing-library/docs/render-hook) - render hooks for testing purposes
|
|
101
|
-
-
|
|
102
|
-
- [Async utils](https://callstack.github.io/react-native-testing-library/docs/
|
|
103
|
-
- [Configuration](https://callstack.github.io/react-native-testing-library/docs/
|
|
104
|
-
- [Accessibility](https://callstack.github.io/react-native-testing-library/docs/
|
|
105
|
-
- [Other](https://callstack.github.io/react-native-testing-library/docs/other
|
|
92
|
+
- [`render` function](https://callstack.github.io/react-native-testing-library/docs/api/render) - render your UI components for testing purposes
|
|
93
|
+
- [`screen` object](https://callstack.github.io/react-native-testing-library/docs/api/screen) - access rendered UI:
|
|
94
|
+
- [Queries](https://callstack.github.io/react-native-testing-library/docs/api/queries) - find rendered components by various predicates: role, text, test ids, etc
|
|
95
|
+
- Lifecycle methods: [`rerender`](https://callstack.github.io/react-native-testing-library/docs/api/screen#rerender), [`unmount`](https://callstack.github.io/react-native-testing-library/docs/api/screen#unmount)
|
|
96
|
+
- Helpers: [`debug`](https://callstack.github.io/react-native-testing-library/docs/api/screen#debug), [`toJSON`](https://callstack.github.io/react-native-testing-library/docs/api/screen#tojson), [`root`](https://callstack.github.io/react-native-testing-library/docs/api/screen#root)
|
|
97
|
+
- [Jest matchers](https://callstack.github.io/react-native-testing-library/docs/api/jest-matchers) - validate assumptions about your UI
|
|
98
|
+
- [User Event](https://callstack.github.io/react-native-testing-library/docs/api/events/user-event) - simulate common user interactions like [`press`](https://callstack.github.io/react-native-testing-library/docs/api/events/user-event#press) or [`type`](https://callstack.github.io/react-native-testing-library/docs/user-event#type) in a realistic way
|
|
99
|
+
- [Fire Event](https://callstack.github.io/react-native-testing-library/docs/api/events/fire-event) - simulate any component event in a simplified way
|
|
100
|
+
- [`renderHook` function](https://callstack.github.io/react-native-testing-library/docs/api/misc/render-hook) - render hooks for testing purposes
|
|
101
|
+
- Miscellaneous APIs:
|
|
102
|
+
- [Async utils](https://callstack.github.io/react-native-testing-library/docs/api/misc/async): `findBy*` queries, `waitFor`, `waitForElementToBeRemoved`
|
|
103
|
+
- [Configuration](https://callstack.github.io/react-native-testing-library/docs/api/misc/config): `configure`, `resetToDefaults`
|
|
104
|
+
- [Accessibility](https://callstack.github.io/react-native-testing-library/docs/api/misc/accessibility): `isHiddenFromAccessibility`
|
|
105
|
+
- [Other](https://callstack.github.io/react-native-testing-library/docs/api/misc/other): `within`, `act`, `cleanup`
|
|
106
106
|
|
|
107
107
|
## Migration Guides
|
|
108
108
|
|
|
109
|
-
- [Migration to 12.0](https://callstack.github.io/react-native-testing-library/docs/migration
|
|
109
|
+
- [Migration to 12.0](https://callstack.github.io/react-native-testing-library/docs/migration/v12)
|
|
110
|
+
- [Migration to built-in Jest Matchers](https://callstack.github.io/react-native-testing-library/docs/migration/jest-matchers)
|
|
111
|
+
|
|
110
112
|
|
|
111
113
|
## Troubleshooting
|
|
112
114
|
|
|
113
|
-
- [Troubleshooting guide](https://callstack.github.io/react-native-testing-library/docs/troubleshooting)
|
|
115
|
+
- [Troubleshooting guide](https://callstack.github.io/react-native-testing-library/docs/guides/troubleshooting)
|
|
114
116
|
|
|
115
117
|
## Community Resources
|
|
116
118
|
|
|
117
|
-
Check out our list of [Community Resources about RNTL](https://callstack.github.io/react-native-testing-library/docs/community-resources).
|
|
119
|
+
Check out our list of [Community Resources about RNTL](https://callstack.github.io/react-native-testing-library/docs/guides/community-resources).
|
|
118
120
|
|
|
119
121
|
## Made with ❤️ at Callstack
|
|
120
122
|
|
|
@@ -46,11 +46,7 @@ function isElementVisible(element, accessibilityCache) {
|
|
|
46
46
|
return isElementVisible(hostParent, cache);
|
|
47
47
|
}
|
|
48
48
|
function isHiddenForStyles(element) {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
display,
|
|
52
|
-
opacity
|
|
53
|
-
} = _reactNative.StyleSheet.flatten(style);
|
|
54
|
-
return display === 'none' || opacity === 0;
|
|
49
|
+
const flatStyle = _reactNative.StyleSheet.flatten(element.props.style);
|
|
50
|
+
return flatStyle?.display === 'none' || flatStyle?.opacity === 0;
|
|
55
51
|
}
|
|
56
52
|
//# sourceMappingURL=to-be-visible.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"to-be-visible.js","names":["_jestMatcherUtils","require","_reactNative","_accessibility","_componentTree","_hostComponentNames","_utils","toBeVisible","element","isNot","checkHostElement","pass","isElementVisible","message","is","matcherHint","formatElement","join","accessibilityCache","cache","WeakMap","isHiddenFromAccessibility","isHiddenForStyles","isHostModal","props","visible","hostParent","getHostParent","
|
|
1
|
+
{"version":3,"file":"to-be-visible.js","names":["_jestMatcherUtils","require","_reactNative","_accessibility","_componentTree","_hostComponentNames","_utils","toBeVisible","element","isNot","checkHostElement","pass","isElementVisible","message","is","matcherHint","formatElement","join","accessibilityCache","cache","WeakMap","isHiddenFromAccessibility","isHiddenForStyles","isHostModal","props","visible","hostParent","getHostParent","flatStyle","StyleSheet","flatten","style","display","opacity"],"sources":["../../src/matchers/to-be-visible.tsx"],"sourcesContent":["import type { ReactTestInstance } from 'react-test-renderer';\nimport { matcherHint } from 'jest-matcher-utils';\nimport { StyleSheet } from 'react-native';\nimport { isHiddenFromAccessibility } from '../helpers/accessibility';\nimport { getHostParent } from '../helpers/component-tree';\nimport { isHostModal } from '../helpers/host-component-names';\nimport { checkHostElement, formatElement } from './utils';\n\nexport function toBeVisible(this: jest.MatcherContext, element: ReactTestInstance) {\n if (element !== null || !this.isNot) {\n checkHostElement(element, toBeVisible, this);\n }\n\n return {\n pass: isElementVisible(element),\n message: () => {\n const is = this.isNot ? 'is' : 'is not';\n return [\n matcherHint(`${this.isNot ? '.not' : ''}.toBeVisible`, 'element', ''),\n '',\n `Received element ${is} visible:`,\n formatElement(element),\n ].join('\\n');\n },\n };\n}\n\nfunction isElementVisible(\n element: ReactTestInstance,\n accessibilityCache?: WeakMap<ReactTestInstance, boolean>,\n): boolean {\n // Use cache to speed up repeated searches by `isHiddenFromAccessibility`.\n const cache = accessibilityCache ?? new WeakMap<ReactTestInstance, boolean>();\n if (isHiddenFromAccessibility(element, { cache })) {\n return false;\n }\n\n if (isHiddenForStyles(element)) {\n return false;\n }\n\n // Note: this seems to be a bug in React Native.\n // PR with fix: https://github.com/facebook/react-native/pull/39157\n if (isHostModal(element) && element.props.visible === false) {\n return false;\n }\n\n const hostParent = getHostParent(element);\n if (hostParent === null) {\n return true;\n }\n\n return isElementVisible(hostParent, cache);\n}\n\nfunction isHiddenForStyles(element: ReactTestInstance) {\n const flatStyle = StyleSheet.flatten(element.props.style);\n return flatStyle?.display === 'none' || flatStyle?.opacity === 0;\n}\n"],"mappings":";;;;;;AACA,IAAAA,iBAAA,GAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AACA,IAAAE,cAAA,GAAAF,OAAA;AACA,IAAAG,cAAA,GAAAH,OAAA;AACA,IAAAI,mBAAA,GAAAJ,OAAA;AACA,IAAAK,MAAA,GAAAL,OAAA;AAEO,SAASM,WAAWA,CAA4BC,OAA0B,EAAE;EACjF,IAAIA,OAAO,KAAK,IAAI,IAAI,CAAC,IAAI,CAACC,KAAK,EAAE;IACnC,IAAAC,uBAAgB,EAACF,OAAO,EAAED,WAAW,EAAE,IAAI,CAAC;EAC9C;EAEA,OAAO;IACLI,IAAI,EAAEC,gBAAgB,CAACJ,OAAO,CAAC;IAC/BK,OAAO,EAAEA,CAAA,KAAM;MACb,MAAMC,EAAE,GAAG,IAAI,CAACL,KAAK,GAAG,IAAI,GAAG,QAAQ;MACvC,OAAO,CACL,IAAAM,6BAAW,EAAE,GAAE,IAAI,CAACN,KAAK,GAAG,MAAM,GAAG,EAAG,cAAa,EAAE,SAAS,EAAE,EAAE,CAAC,EACrE,EAAE,EACD,oBAAmBK,EAAG,WAAU,EACjC,IAAAE,oBAAa,EAACR,OAAO,CAAC,CACvB,CAACS,IAAI,CAAC,IAAI,CAAC;IACd;EACF,CAAC;AACH;AAEA,SAASL,gBAAgBA,CACvBJ,OAA0B,EAC1BU,kBAAwD,EAC/C;EACT;EACA,MAAMC,KAAK,GAAGD,kBAAkB,IAAI,IAAIE,OAAO,CAA6B,CAAC;EAC7E,IAAI,IAAAC,wCAAyB,EAACb,OAAO,EAAE;IAAEW;EAAM,CAAC,CAAC,EAAE;IACjD,OAAO,KAAK;EACd;EAEA,IAAIG,iBAAiB,CAACd,OAAO,CAAC,EAAE;IAC9B,OAAO,KAAK;EACd;;EAEA;EACA;EACA,IAAI,IAAAe,+BAAW,EAACf,OAAO,CAAC,IAAIA,OAAO,CAACgB,KAAK,CAACC,OAAO,KAAK,KAAK,EAAE;IAC3D,OAAO,KAAK;EACd;EAEA,MAAMC,UAAU,GAAG,IAAAC,4BAAa,EAACnB,OAAO,CAAC;EACzC,IAAIkB,UAAU,KAAK,IAAI,EAAE;IACvB,OAAO,IAAI;EACb;EAEA,OAAOd,gBAAgB,CAACc,UAAU,EAAEP,KAAK,CAAC;AAC5C;AAEA,SAASG,iBAAiBA,CAACd,OAA0B,EAAE;EACrD,MAAMoB,SAAS,GAAGC,uBAAU,CAACC,OAAO,CAACtB,OAAO,CAACgB,KAAK,CAACO,KAAK,CAAC;EACzD,OAAOH,SAAS,EAAEI,OAAO,KAAK,MAAM,IAAIJ,SAAS,EAAEK,OAAO,KAAK,CAAC;AAClE","ignoreList":[]}
|
|
@@ -6,4 +6,5 @@ export declare function createEventLogger(): {
|
|
|
6
6
|
events: EventEntry[];
|
|
7
7
|
logEvent: (name: string) => (event: unknown) => void;
|
|
8
8
|
};
|
|
9
|
-
export declare function
|
|
9
|
+
export declare function getEventsNames(events: EventEntry[]): string[];
|
|
10
|
+
export declare function lastEventPayload(events: EventEntry[], name: string): any;
|
|
@@ -4,7 +4,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.createEventLogger = createEventLogger;
|
|
7
|
-
exports.
|
|
7
|
+
exports.getEventsNames = getEventsNames;
|
|
8
|
+
exports.lastEventPayload = lastEventPayload;
|
|
8
9
|
function createEventLogger() {
|
|
9
10
|
const events = [];
|
|
10
11
|
const logEvent = name => {
|
|
@@ -21,7 +22,10 @@ function createEventLogger() {
|
|
|
21
22
|
logEvent
|
|
22
23
|
};
|
|
23
24
|
}
|
|
24
|
-
function
|
|
25
|
+
function getEventsNames(events) {
|
|
25
26
|
return events.map(event => event.name);
|
|
26
27
|
}
|
|
28
|
+
function lastEventPayload(events, name) {
|
|
29
|
+
return events.filter(e => e.name === name).pop()?.payload;
|
|
30
|
+
}
|
|
27
31
|
//# sourceMappingURL=events.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","names":["createEventLogger","events","logEvent","name","event","eventEntry","payload","push","
|
|
1
|
+
{"version":3,"file":"events.js","names":["createEventLogger","events","logEvent","name","event","eventEntry","payload","push","getEventsNames","map","lastEventPayload","filter","e","pop"],"sources":["../../src/test-utils/events.ts"],"sourcesContent":["export interface EventEntry {\n name: string;\n payload: any;\n}\n\nexport function createEventLogger() {\n const events: EventEntry[] = [];\n const logEvent = (name: string) => {\n return (event: unknown) => {\n const eventEntry: EventEntry = {\n name,\n payload: event,\n };\n\n events.push(eventEntry);\n };\n };\n\n return { events, logEvent };\n}\n\nexport function getEventsNames(events: EventEntry[]) {\n return events.map((event) => event.name);\n}\n\nexport function lastEventPayload(events: EventEntry[], name: string) {\n return events.filter((e) => e.name === name).pop()?.payload;\n}\n"],"mappings":";;;;;;;;AAKO,SAASA,iBAAiBA,CAAA,EAAG;EAClC,MAAMC,MAAoB,GAAG,EAAE;EAC/B,MAAMC,QAAQ,GAAIC,IAAY,IAAK;IACjC,OAAQC,KAAc,IAAK;MACzB,MAAMC,UAAsB,GAAG;QAC7BF,IAAI;QACJG,OAAO,EAAEF;MACX,CAAC;MAEDH,MAAM,CAACM,IAAI,CAACF,UAAU,CAAC;IACzB,CAAC;EACH,CAAC;EAED,OAAO;IAAEJ,MAAM;IAAEC;EAAS,CAAC;AAC7B;AAEO,SAASM,cAAcA,CAACP,MAAoB,EAAE;EACnD,OAAOA,MAAM,CAACQ,GAAG,CAAEL,KAAK,IAAKA,KAAK,CAACD,IAAI,CAAC;AAC1C;AAEO,SAASO,gBAAgBA,CAACT,MAAoB,EAAEE,IAAY,EAAE;EACnE,OAAOF,MAAM,CAACU,MAAM,CAAEC,CAAC,IAAKA,CAAC,CAACT,IAAI,KAAKA,IAAI,CAAC,CAACU,GAAG,CAAC,CAAC,EAAEP,OAAO;AAC7D","ignoreList":[]}
|
|
@@ -30,9 +30,14 @@ async function clear(element) {
|
|
|
30
30
|
};
|
|
31
31
|
(0, _utils.dispatchEvent)(element, 'selectionChange', _eventBuilder.EventBuilder.TextInput.selectionChange(selectionRange));
|
|
32
32
|
|
|
33
|
-
// 3. Press backspace
|
|
33
|
+
// 3. Press backspace with selected text
|
|
34
34
|
const finalText = '';
|
|
35
|
-
await (0, _type.emitTypingEvents)(
|
|
35
|
+
await (0, _type.emitTypingEvents)(element, {
|
|
36
|
+
config: this.config,
|
|
37
|
+
key: 'Backspace',
|
|
38
|
+
text: finalText,
|
|
39
|
+
previousText
|
|
40
|
+
});
|
|
36
41
|
|
|
37
42
|
// 4. Exit element
|
|
38
43
|
await (0, _utils.wait)(this.config);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clear.js","names":["_errors","require","_hostComponentNames","_textInput","_pointerEvents","_eventBuilder","_utils","_type","clear","element","isHostTextInput","ErrorWithStack","type","isTextInputEditable","isPointerEventEnabled","dispatchEvent","EventBuilder","Common","focus","previousText","props","value","defaultValue","selectionRange","start","end","length","TextInput","selectionChange","finalText","emitTypingEvents","config","wait","endEditing","blur"],"sources":["../../src/user-event/clear.ts"],"sourcesContent":["import { ReactTestInstance } from 'react-test-renderer';\nimport { ErrorWithStack } from '../helpers/errors';\nimport { isHostTextInput } from '../helpers/host-component-names';\nimport { isTextInputEditable } from '../helpers/text-input';\nimport { isPointerEventEnabled } from '../helpers/pointer-events';\nimport { EventBuilder } from './event-builder';\nimport { UserEventInstance } from './setup';\nimport { dispatchEvent, wait } from './utils';\nimport { emitTypingEvents } from './type/type';\n\nexport async function clear(this: UserEventInstance, element: ReactTestInstance): Promise<void> {\n if (!isHostTextInput(element)) {\n throw new ErrorWithStack(\n `clear() only supports host \"TextInput\" elements. Passed element has type: \"${element.type}\".`,\n clear,\n );\n }\n\n if (!isTextInputEditable(element) || !isPointerEventEnabled(element)) {\n return;\n }\n\n // 1. Enter element\n dispatchEvent(element, 'focus', EventBuilder.Common.focus());\n\n // 2. Select all\n const previousText = element.props.value ?? element.props.defaultValue ?? '';\n const selectionRange = {\n start: 0,\n end: previousText.length,\n };\n dispatchEvent(element, 'selectionChange', EventBuilder.TextInput.selectionChange(selectionRange));\n\n // 3. Press backspace\n const finalText = '';\n await emitTypingEvents(this.config
|
|
1
|
+
{"version":3,"file":"clear.js","names":["_errors","require","_hostComponentNames","_textInput","_pointerEvents","_eventBuilder","_utils","_type","clear","element","isHostTextInput","ErrorWithStack","type","isTextInputEditable","isPointerEventEnabled","dispatchEvent","EventBuilder","Common","focus","previousText","props","value","defaultValue","selectionRange","start","end","length","TextInput","selectionChange","finalText","emitTypingEvents","config","key","text","wait","endEditing","blur"],"sources":["../../src/user-event/clear.ts"],"sourcesContent":["import { ReactTestInstance } from 'react-test-renderer';\nimport { ErrorWithStack } from '../helpers/errors';\nimport { isHostTextInput } from '../helpers/host-component-names';\nimport { isTextInputEditable } from '../helpers/text-input';\nimport { isPointerEventEnabled } from '../helpers/pointer-events';\nimport { EventBuilder } from './event-builder';\nimport { UserEventInstance } from './setup';\nimport { dispatchEvent, wait } from './utils';\nimport { emitTypingEvents } from './type/type';\n\nexport async function clear(this: UserEventInstance, element: ReactTestInstance): Promise<void> {\n if (!isHostTextInput(element)) {\n throw new ErrorWithStack(\n `clear() only supports host \"TextInput\" elements. Passed element has type: \"${element.type}\".`,\n clear,\n );\n }\n\n if (!isTextInputEditable(element) || !isPointerEventEnabled(element)) {\n return;\n }\n\n // 1. Enter element\n dispatchEvent(element, 'focus', EventBuilder.Common.focus());\n\n // 2. Select all\n const previousText = element.props.value ?? element.props.defaultValue ?? '';\n const selectionRange = {\n start: 0,\n end: previousText.length,\n };\n dispatchEvent(element, 'selectionChange', EventBuilder.TextInput.selectionChange(selectionRange));\n\n // 3. Press backspace with selected text\n const finalText = '';\n await emitTypingEvents(element, {\n config: this.config,\n key: 'Backspace',\n text: finalText,\n previousText,\n });\n\n // 4. Exit element\n await wait(this.config);\n dispatchEvent(element, 'endEditing', EventBuilder.TextInput.endEditing(finalText));\n\n dispatchEvent(element, 'blur', EventBuilder.Common.blur());\n}\n"],"mappings":";;;;;;AACA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,mBAAA,GAAAD,OAAA;AACA,IAAAE,UAAA,GAAAF,OAAA;AACA,IAAAG,cAAA,GAAAH,OAAA;AACA,IAAAI,aAAA,GAAAJ,OAAA;AAEA,IAAAK,MAAA,GAAAL,OAAA;AACA,IAAAM,KAAA,GAAAN,OAAA;AAEO,eAAeO,KAAKA,CAA0BC,OAA0B,EAAiB;EAC9F,IAAI,CAAC,IAAAC,mCAAe,EAACD,OAAO,CAAC,EAAE;IAC7B,MAAM,IAAIE,sBAAc,CACrB,8EAA6EF,OAAO,CAACG,IAAK,IAAG,EAC9FJ,KACF,CAAC;EACH;EAEA,IAAI,CAAC,IAAAK,8BAAmB,EAACJ,OAAO,CAAC,IAAI,CAAC,IAAAK,oCAAqB,EAACL,OAAO,CAAC,EAAE;IACpE;EACF;;EAEA;EACA,IAAAM,oBAAa,EAACN,OAAO,EAAE,OAAO,EAAEO,0BAAY,CAACC,MAAM,CAACC,KAAK,CAAC,CAAC,CAAC;;EAE5D;EACA,MAAMC,YAAY,GAAGV,OAAO,CAACW,KAAK,CAACC,KAAK,IAAIZ,OAAO,CAACW,KAAK,CAACE,YAAY,IAAI,EAAE;EAC5E,MAAMC,cAAc,GAAG;IACrBC,KAAK,EAAE,CAAC;IACRC,GAAG,EAAEN,YAAY,CAACO;EACpB,CAAC;EACD,IAAAX,oBAAa,EAACN,OAAO,EAAE,iBAAiB,EAAEO,0BAAY,CAACW,SAAS,CAACC,eAAe,CAACL,cAAc,CAAC,CAAC;;EAEjG;EACA,MAAMM,SAAS,GAAG,EAAE;EACpB,MAAM,IAAAC,sBAAgB,EAACrB,OAAO,EAAE;IAC9BsB,MAAM,EAAE,IAAI,CAACA,MAAM;IACnBC,GAAG,EAAE,WAAW;IAChBC,IAAI,EAAEJ,SAAS;IACfV;EACF,CAAC,CAAC;;EAEF;EACA,MAAM,IAAAe,WAAI,EAAC,IAAI,CAACH,MAAM,CAAC;EACvB,IAAAhB,oBAAa,EAACN,OAAO,EAAE,YAAY,EAAEO,0BAAY,CAACW,SAAS,CAACQ,UAAU,CAACN,SAAS,CAAC,CAAC;EAElF,IAAAd,oBAAa,EAACN,OAAO,EAAE,MAAM,EAAEO,0BAAY,CAACC,MAAM,CAACmB,IAAI,CAAC,CAAC,CAAC;AAC5D","ignoreList":[]}
|
|
@@ -5,4 +5,12 @@ export interface TypeOptions {
|
|
|
5
5
|
submitEditing?: boolean;
|
|
6
6
|
}
|
|
7
7
|
export declare function type(this: UserEventInstance, element: ReactTestInstance, text: string, options?: TypeOptions): Promise<void>;
|
|
8
|
-
|
|
8
|
+
type EmitTypingEventsContext = {
|
|
9
|
+
config: UserEventConfig;
|
|
10
|
+
key: string;
|
|
11
|
+
text: string;
|
|
12
|
+
previousText: string;
|
|
13
|
+
isAccepted?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export declare function emitTypingEvents(element: ReactTestInstance, { config, key, text, previousText, isAccepted }: EmitTypingEventsContext): Promise<void>;
|
|
16
|
+
export {};
|
|
@@ -33,8 +33,16 @@ async function type(element, text, options) {
|
|
|
33
33
|
let currentText = element.props.value ?? element.props.defaultValue ?? '';
|
|
34
34
|
for (const key of keys) {
|
|
35
35
|
const previousText = element.props.value ?? currentText;
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
const proposedText = applyKey(previousText, key);
|
|
37
|
+
const isAccepted = isTextChangeAccepted(element, proposedText);
|
|
38
|
+
currentText = isAccepted ? proposedText : previousText;
|
|
39
|
+
await emitTypingEvents(element, {
|
|
40
|
+
config: this.config,
|
|
41
|
+
key,
|
|
42
|
+
text: currentText,
|
|
43
|
+
previousText,
|
|
44
|
+
isAccepted
|
|
45
|
+
});
|
|
38
46
|
}
|
|
39
47
|
const finalText = element.props.value ?? currentText;
|
|
40
48
|
await (0, _utils.wait)(this.config);
|
|
@@ -44,28 +52,41 @@ async function type(element, text, options) {
|
|
|
44
52
|
(0, _utils.dispatchEvent)(element, 'endEditing', _eventBuilder.EventBuilder.TextInput.endEditing(finalText));
|
|
45
53
|
(0, _utils.dispatchEvent)(element, 'blur', _eventBuilder.EventBuilder.Common.blur());
|
|
46
54
|
}
|
|
47
|
-
async function emitTypingEvents(
|
|
55
|
+
async function emitTypingEvents(element, {
|
|
56
|
+
config,
|
|
57
|
+
key,
|
|
58
|
+
text,
|
|
59
|
+
previousText,
|
|
60
|
+
isAccepted
|
|
61
|
+
}) {
|
|
48
62
|
const isMultiline = element.props.multiline === true;
|
|
49
63
|
await (0, _utils.wait)(config);
|
|
50
64
|
(0, _utils.dispatchEvent)(element, 'keyPress', _eventBuilder.EventBuilder.TextInput.keyPress(key));
|
|
51
65
|
|
|
66
|
+
// Platform difference (based on experiments):
|
|
67
|
+
// - iOS and RN Web: TextInput emits only `keyPress` event when max length has been reached
|
|
68
|
+
// - Android: TextInputs does not emit any events
|
|
69
|
+
if (isAccepted === false) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
52
73
|
// According to the docs only multiline TextInput emits textInput event
|
|
53
74
|
// @see: https://github.com/facebook/react-native/blob/42a2898617da1d7a98ef574a5b9e500681c8f738/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts#L754
|
|
54
75
|
if (isMultiline) {
|
|
55
|
-
(0, _utils.dispatchEvent)(element, 'textInput', _eventBuilder.EventBuilder.TextInput.textInput(
|
|
76
|
+
(0, _utils.dispatchEvent)(element, 'textInput', _eventBuilder.EventBuilder.TextInput.textInput(text, previousText));
|
|
56
77
|
}
|
|
57
|
-
(0, _utils.dispatchEvent)(element, 'change', _eventBuilder.EventBuilder.TextInput.change(
|
|
58
|
-
(0, _utils.dispatchEvent)(element, 'changeText',
|
|
78
|
+
(0, _utils.dispatchEvent)(element, 'change', _eventBuilder.EventBuilder.TextInput.change(text));
|
|
79
|
+
(0, _utils.dispatchEvent)(element, 'changeText', text);
|
|
59
80
|
const selectionRange = {
|
|
60
|
-
start:
|
|
61
|
-
end:
|
|
81
|
+
start: text.length,
|
|
82
|
+
end: text.length
|
|
62
83
|
};
|
|
63
84
|
(0, _utils.dispatchEvent)(element, 'selectionChange', _eventBuilder.EventBuilder.TextInput.selectionChange(selectionRange));
|
|
64
85
|
|
|
65
86
|
// According to the docs only multiline TextInput emits contentSizeChange event
|
|
66
87
|
// @see: https://reactnative.dev/docs/textinput#oncontentsizechange
|
|
67
88
|
if (isMultiline) {
|
|
68
|
-
const contentSize = (0, _utils.getTextContentSize)(
|
|
89
|
+
const contentSize = (0, _utils.getTextContentSize)(text);
|
|
69
90
|
(0, _utils.dispatchEvent)(element, 'contentSizeChange', _eventBuilder.EventBuilder.TextInput.contentSizeChange(contentSize));
|
|
70
91
|
}
|
|
71
92
|
}
|
|
@@ -78,4 +99,8 @@ function applyKey(text, key) {
|
|
|
78
99
|
}
|
|
79
100
|
return text + key;
|
|
80
101
|
}
|
|
102
|
+
function isTextChangeAccepted(element, text) {
|
|
103
|
+
const maxLength = element.props.maxLength;
|
|
104
|
+
return maxLength === undefined || text.length <= maxLength;
|
|
105
|
+
}
|
|
81
106
|
//# sourceMappingURL=type.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type.js","names":["_hostComponentNames","require","_eventBuilder","_errors","_textInput","_pointerEvents","_utils","_parseKeys","type","element","text","options","isHostTextInput","ErrorWithStack","isTextInputEditable","isPointerEventEnabled","keys","parseKeys","skipPress","dispatchEvent","EventBuilder","Common","touch","focus","wait","config","currentText","props","value","defaultValue","key","previousText","applyKey","emitTypingEvents","finalText","submitEditing","TextInput","endEditing","blur","isMultiline","multiline","keyPress","textInput","change","selectionRange","start","length","end","selectionChange","contentSize","getTextContentSize","contentSizeChange","slice"],"sources":["../../../src/user-event/type/type.ts"],"sourcesContent":["import { ReactTestInstance } from 'react-test-renderer';\nimport { isHostTextInput } from '../../helpers/host-component-names';\nimport { EventBuilder } from '../event-builder';\nimport { ErrorWithStack } from '../../helpers/errors';\nimport { isTextInputEditable } from '../../helpers/text-input';\nimport { isPointerEventEnabled } from '../../helpers/pointer-events';\nimport { UserEventConfig, UserEventInstance } from '../setup';\nimport { dispatchEvent, wait, getTextContentSize } from '../utils';\nimport { parseKeys } from './parse-keys';\n\nexport interface TypeOptions {\n skipPress?: boolean;\n submitEditing?: boolean;\n}\n\nexport async function type(\n this: UserEventInstance,\n element: ReactTestInstance,\n text: string,\n options?: TypeOptions,\n): Promise<void> {\n if (!isHostTextInput(element)) {\n throw new ErrorWithStack(\n `type() works only with host \"TextInput\" elements. Passed element has type \"${element.type}\".`,\n type,\n );\n }\n\n // Skip events if the element is disabled\n if (!isTextInputEditable(element) || !isPointerEventEnabled(element)) {\n return;\n }\n\n const keys = parseKeys(text);\n\n if (!options?.skipPress) {\n dispatchEvent(element, 'pressIn', EventBuilder.Common.touch());\n }\n\n dispatchEvent(element, 'focus', EventBuilder.Common.focus());\n\n if (!options?.skipPress) {\n await wait(this.config);\n dispatchEvent(element, 'pressOut', EventBuilder.Common.touch());\n }\n\n let currentText = element.props.value ?? element.props.defaultValue ?? '';\n for (const key of keys) {\n const previousText = element.props.value ?? currentText;\n
|
|
1
|
+
{"version":3,"file":"type.js","names":["_hostComponentNames","require","_eventBuilder","_errors","_textInput","_pointerEvents","_utils","_parseKeys","type","element","text","options","isHostTextInput","ErrorWithStack","isTextInputEditable","isPointerEventEnabled","keys","parseKeys","skipPress","dispatchEvent","EventBuilder","Common","touch","focus","wait","config","currentText","props","value","defaultValue","key","previousText","proposedText","applyKey","isAccepted","isTextChangeAccepted","emitTypingEvents","finalText","submitEditing","TextInput","endEditing","blur","isMultiline","multiline","keyPress","textInput","change","selectionRange","start","length","end","selectionChange","contentSize","getTextContentSize","contentSizeChange","slice","maxLength","undefined"],"sources":["../../../src/user-event/type/type.ts"],"sourcesContent":["import { ReactTestInstance } from 'react-test-renderer';\nimport { isHostTextInput } from '../../helpers/host-component-names';\nimport { EventBuilder } from '../event-builder';\nimport { ErrorWithStack } from '../../helpers/errors';\nimport { isTextInputEditable } from '../../helpers/text-input';\nimport { isPointerEventEnabled } from '../../helpers/pointer-events';\nimport { UserEventConfig, UserEventInstance } from '../setup';\nimport { dispatchEvent, wait, getTextContentSize } from '../utils';\nimport { parseKeys } from './parse-keys';\n\nexport interface TypeOptions {\n skipPress?: boolean;\n submitEditing?: boolean;\n}\n\nexport async function type(\n this: UserEventInstance,\n element: ReactTestInstance,\n text: string,\n options?: TypeOptions,\n): Promise<void> {\n if (!isHostTextInput(element)) {\n throw new ErrorWithStack(\n `type() works only with host \"TextInput\" elements. Passed element has type \"${element.type}\".`,\n type,\n );\n }\n\n // Skip events if the element is disabled\n if (!isTextInputEditable(element) || !isPointerEventEnabled(element)) {\n return;\n }\n\n const keys = parseKeys(text);\n\n if (!options?.skipPress) {\n dispatchEvent(element, 'pressIn', EventBuilder.Common.touch());\n }\n\n dispatchEvent(element, 'focus', EventBuilder.Common.focus());\n\n if (!options?.skipPress) {\n await wait(this.config);\n dispatchEvent(element, 'pressOut', EventBuilder.Common.touch());\n }\n\n let currentText = element.props.value ?? element.props.defaultValue ?? '';\n for (const key of keys) {\n const previousText = element.props.value ?? currentText;\n const proposedText = applyKey(previousText, key);\n const isAccepted = isTextChangeAccepted(element, proposedText);\n currentText = isAccepted ? proposedText : previousText;\n\n await emitTypingEvents(element, {\n config: this.config,\n key,\n text: currentText,\n previousText,\n isAccepted,\n });\n }\n\n const finalText = element.props.value ?? currentText;\n await wait(this.config);\n\n if (options?.submitEditing) {\n dispatchEvent(element, 'submitEditing', EventBuilder.TextInput.submitEditing(finalText));\n }\n\n dispatchEvent(element, 'endEditing', EventBuilder.TextInput.endEditing(finalText));\n\n dispatchEvent(element, 'blur', EventBuilder.Common.blur());\n}\n\ntype EmitTypingEventsContext = {\n config: UserEventConfig;\n key: string;\n text: string;\n previousText: string;\n isAccepted?: boolean;\n};\n\nexport async function emitTypingEvents(\n element: ReactTestInstance,\n { config, key, text, previousText, isAccepted }: EmitTypingEventsContext,\n) {\n const isMultiline = element.props.multiline === true;\n\n await wait(config);\n dispatchEvent(element, 'keyPress', EventBuilder.TextInput.keyPress(key));\n\n // Platform difference (based on experiments):\n // - iOS and RN Web: TextInput emits only `keyPress` event when max length has been reached\n // - Android: TextInputs does not emit any events\n if (isAccepted === false) {\n return;\n }\n\n // According to the docs only multiline TextInput emits textInput event\n // @see: https://github.com/facebook/react-native/blob/42a2898617da1d7a98ef574a5b9e500681c8f738/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts#L754\n if (isMultiline) {\n dispatchEvent(element, 'textInput', EventBuilder.TextInput.textInput(text, previousText));\n }\n\n dispatchEvent(element, 'change', EventBuilder.TextInput.change(text));\n dispatchEvent(element, 'changeText', text);\n\n const selectionRange = {\n start: text.length,\n end: text.length,\n };\n dispatchEvent(element, 'selectionChange', EventBuilder.TextInput.selectionChange(selectionRange));\n\n // According to the docs only multiline TextInput emits contentSizeChange event\n // @see: https://reactnative.dev/docs/textinput#oncontentsizechange\n if (isMultiline) {\n const contentSize = getTextContentSize(text);\n dispatchEvent(\n element,\n 'contentSizeChange',\n EventBuilder.TextInput.contentSizeChange(contentSize),\n );\n }\n}\n\nfunction applyKey(text: string, key: string) {\n if (key === 'Enter') {\n return `${text}\\n`;\n }\n\n if (key === 'Backspace') {\n return text.slice(0, -1);\n }\n\n return text + key;\n}\n\nfunction isTextChangeAccepted(element: ReactTestInstance, text: string) {\n const maxLength = element.props.maxLength;\n return maxLength === undefined || text.length <= maxLength;\n}\n"],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED