@terreno/ui 0.10.0 → 0.11.0
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/dist/Banner.js +2 -2
- package/dist/Banner.js.map +1 -1
- package/dist/TextFieldNumberActionSheet.d.ts +1 -1
- package/dist/Toast.d.ts +1 -1
- package/dist/Toast.js +2 -2
- package/dist/Toast.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/package.json +2 -1
- package/src/ActionSheet.test.tsx +262 -3
- package/src/AddressField.test.tsx +50 -0
- package/src/Banner.test.tsx +22 -0
- package/src/Banner.tsx +2 -2
- package/src/Box.test.tsx +218 -0
- package/src/Button.test.tsx +71 -0
- package/src/ConsentFormScreen.test.tsx +167 -0
- package/src/ConsentNavigator.test.tsx +206 -0
- package/src/DecimalRangeActionSheet.test.tsx +53 -2
- package/src/EmailField.test.tsx +81 -0
- package/src/EmojiSelector.test.tsx +262 -1
- package/src/HeightActionSheet.test.tsx +57 -2
- package/src/InfoModalIcon.test.tsx +16 -0
- package/src/InfoTooltipButton.test.tsx +53 -1
- package/src/MobileAddressAutoComplete.test.tsx +137 -7
- package/src/Modal.test.tsx +188 -0
- package/src/NumberPickerActionSheet.test.tsx +59 -2
- package/src/Page.test.tsx +162 -1
- package/src/Pagination.test.tsx +16 -0
- package/src/PhoneNumberField.test.tsx +46 -9
- package/src/PickerSelect.test.tsx +230 -0
- package/src/SegmentedControl.test.tsx +38 -0
- package/src/SelectBadge.test.tsx +52 -1
- package/src/SideDrawer.test.tsx +69 -0
- package/src/Signature.test.tsx +42 -5
- package/src/SignatureField.test.tsx +35 -0
- package/src/Slider.test.tsx +59 -0
- package/src/Spinner.test.tsx +6 -0
- package/src/SplitPage.test.tsx +228 -2
- package/src/TapToEdit.test.tsx +171 -1
- package/src/TerrenoProvider.test.tsx +42 -2
- package/src/TextFieldNumberActionSheet.tsx +1 -1
- package/src/Theme.test.tsx +118 -28
- package/src/Toast.test.tsx +95 -2
- package/src/Toast.tsx +3 -3
- package/src/Tooltip.test.tsx +204 -1
- package/src/UnifiedAddressAutoComplete.test.tsx +38 -19
- package/src/UserInactivity.test.tsx +73 -1
- package/src/Utilities.test.tsx +190 -2
- package/src/WebAddressAutocomplete.test.tsx +148 -1
- package/src/__snapshots__/ActionSheet.test.tsx.snap +1736 -0
- package/src/__snapshots__/Button.test.tsx.snap +68 -0
- package/src/__snapshots__/EmojiSelector.test.tsx.snap +1363 -0
- package/src/__snapshots__/InfoTooltipButton.test.tsx.snap +72 -3
- package/src/__snapshots__/MobileAddressAutoComplete.test.tsx.snap +60 -9
- package/src/__snapshots__/Modal.test.tsx.snap +181 -0
- package/src/__snapshots__/Page.test.tsx.snap +48 -2
- package/src/__snapshots__/PhoneNumberField.test.tsx.snap +0 -93
- package/src/__snapshots__/PickerSelect.test.tsx.snap +706 -0
- package/src/__snapshots__/SideDrawer.test.tsx.snap +533 -1399
- package/src/__snapshots__/Signature.test.tsx.snap +0 -3
- package/src/__snapshots__/SplitPage.test.tsx.snap +970 -0
- package/src/__snapshots__/UnifiedAddressAutoComplete.test.tsx.snap +220 -4
- package/src/__snapshots__/WebAddressAutocomplete.test.tsx.snap +93 -0
- package/src/bunSetup.ts +204 -121
- package/src/index.tsx +2 -2
- package/src/table/TableHeaderCell.test.tsx +142 -0
- package/src/table/TableRow.test.tsx +33 -0
- package/src/table/__snapshots__/TableRow.test.tsx.snap +403 -0
- package/src/table/tableContext.test.tsx +96 -0
- package/src/test-utils.tsx +1 -1
- package/src/useConsentForms.test.ts +130 -0
- package/src/useSubmitConsent.test.ts +64 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {describe, expect, it} from "bun:test";
|
|
2
|
-
import {render} from "@testing-library/react-native";
|
|
1
|
+
import {describe, expect, it, mock} from "bun:test";
|
|
2
|
+
import {act, render} from "@testing-library/react-native";
|
|
3
3
|
import {createRef} from "react";
|
|
4
4
|
|
|
5
5
|
import type {ActionSheet} from "./ActionSheet";
|
|
@@ -43,4 +43,55 @@ describe("DecimalRangeActionSheet", () => {
|
|
|
43
43
|
);
|
|
44
44
|
expect(toJSON()).toMatchSnapshot();
|
|
45
45
|
});
|
|
46
|
+
|
|
47
|
+
it("invokes onChange when whole picker changes", () => {
|
|
48
|
+
const actionSheetRef = createRef<ActionSheet>();
|
|
49
|
+
const handleChange = mock((_val: string) => {});
|
|
50
|
+
const {UNSAFE_getAllByProps} = render(
|
|
51
|
+
<ThemeProvider>
|
|
52
|
+
<DecimalRangeActionSheet
|
|
53
|
+
actionSheetRef={actionSheetRef}
|
|
54
|
+
max={10}
|
|
55
|
+
min={0}
|
|
56
|
+
onChange={handleChange}
|
|
57
|
+
value="5.5"
|
|
58
|
+
/>
|
|
59
|
+
</ThemeProvider>
|
|
60
|
+
);
|
|
61
|
+
// The first Picker is the whole number one (selectedValue: "5")
|
|
62
|
+
const wholePickers = UNSAFE_getAllByProps({selectedValue: "5"});
|
|
63
|
+
const whole = wholePickers.find((p) => typeof p.props.onValueChange === "function");
|
|
64
|
+
act(() => {
|
|
65
|
+
if (whole) {
|
|
66
|
+
whole.props.onValueChange(7);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
expect(handleChange).toHaveBeenCalled();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("invokes onChange when decimal picker changes", () => {
|
|
73
|
+
const actionSheetRef = createRef<ActionSheet>();
|
|
74
|
+
const handleChange = mock((_val: string) => {});
|
|
75
|
+
const {UNSAFE_getAllByProps} = render(
|
|
76
|
+
<ThemeProvider>
|
|
77
|
+
<DecimalRangeActionSheet
|
|
78
|
+
actionSheetRef={actionSheetRef}
|
|
79
|
+
max={10}
|
|
80
|
+
min={0}
|
|
81
|
+
onChange={handleChange}
|
|
82
|
+
value="5.5"
|
|
83
|
+
/>
|
|
84
|
+
</ThemeProvider>
|
|
85
|
+
);
|
|
86
|
+
// The second Picker is the decimal one (selectedValue: "5")
|
|
87
|
+
const decimalPickers = UNSAFE_getAllByProps({selectedValue: "5"});
|
|
88
|
+
// Use the last one (second picker)
|
|
89
|
+
const decimal = decimalPickers[decimalPickers.length - 1];
|
|
90
|
+
act(() => {
|
|
91
|
+
if (decimal && typeof decimal.props.onValueChange === "function") {
|
|
92
|
+
decimal.props.onValueChange(3);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
expect(handleChange).toHaveBeenCalled();
|
|
96
|
+
});
|
|
46
97
|
});
|
package/src/EmailField.test.tsx
CHANGED
|
@@ -103,4 +103,85 @@ describe("EmailField", () => {
|
|
|
103
103
|
);
|
|
104
104
|
expect(toJSON()).toMatchSnapshot();
|
|
105
105
|
});
|
|
106
|
+
|
|
107
|
+
it("calls onBlur with valid email", async () => {
|
|
108
|
+
const handleBlur = mock((_value: string) => {});
|
|
109
|
+
const {getByDisplayValue} = renderWithTheme(
|
|
110
|
+
<EmailField label="Email" onBlur={handleBlur} onChange={() => {}} value="valid@email.com" />
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const input = getByDisplayValue("valid@email.com");
|
|
114
|
+
await act(async () => {
|
|
115
|
+
fireEvent(input, "blur", {nativeEvent: {text: "valid@email.com"}});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await waitFor(() => {
|
|
119
|
+
expect(handleBlur).toHaveBeenCalledWith("valid@email.com");
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("does not call onBlur with invalid email", async () => {
|
|
124
|
+
const handleBlur = mock((_value: string) => {});
|
|
125
|
+
const {getByDisplayValue} = renderWithTheme(
|
|
126
|
+
<EmailField label="Email" onBlur={handleBlur} onChange={() => {}} value="" />
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const input = getByDisplayValue("");
|
|
130
|
+
await act(async () => {
|
|
131
|
+
fireEvent.changeText(input, "invalid-email");
|
|
132
|
+
});
|
|
133
|
+
await act(async () => {
|
|
134
|
+
fireEvent(input, "blur", {nativeEvent: {text: "invalid-email"}});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(handleBlur).not.toHaveBeenCalled();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("clears local error when typing a valid email after invalid", async () => {
|
|
141
|
+
const handleChange = mock((_value: string) => {});
|
|
142
|
+
const {getByDisplayValue, queryByText} = renderWithTheme(
|
|
143
|
+
<EmailField label="Email" onChange={handleChange} value="" />
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const input = getByDisplayValue("");
|
|
147
|
+
|
|
148
|
+
// Type invalid email first
|
|
149
|
+
await act(async () => {
|
|
150
|
+
fireEvent.changeText(input, "bad");
|
|
151
|
+
});
|
|
152
|
+
// Trigger blur to set the error
|
|
153
|
+
await act(async () => {
|
|
154
|
+
fireEvent(input, "blur", {nativeEvent: {text: "bad"}});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
await waitFor(() => {
|
|
158
|
+
expect(queryByText("Invalid email address format")).toBeTruthy();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Now type a valid email to clear the error
|
|
162
|
+
await act(async () => {
|
|
163
|
+
fireEvent.changeText(input, "good@email.com");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
await waitFor(() => {
|
|
167
|
+
expect(queryByText("Invalid email address format")).toBeFalsy();
|
|
168
|
+
expect(handleChange).toHaveBeenCalledWith("good@email.com");
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("validates empty string as valid on blur", async () => {
|
|
173
|
+
const handleBlur = mock((_value: string) => {});
|
|
174
|
+
const {getByDisplayValue} = renderWithTheme(
|
|
175
|
+
<EmailField label="Email" onBlur={handleBlur} onChange={() => {}} value="" />
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const input = getByDisplayValue("");
|
|
179
|
+
await act(async () => {
|
|
180
|
+
fireEvent(input, "blur", {nativeEvent: {text: ""}});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
await waitFor(() => {
|
|
184
|
+
expect(handleBlur).toHaveBeenCalledWith("");
|
|
185
|
+
});
|
|
186
|
+
});
|
|
106
187
|
});
|
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import {describe, expect, it, mock} from "bun:test";
|
|
2
|
+
import {act, fireEvent} from "@testing-library/react-native";
|
|
2
3
|
|
|
3
|
-
import EmojiSelector, {Categories} from "./EmojiSelector";
|
|
4
|
+
import EmojiSelector, {Categories, charFromEmojiObject} from "./EmojiSelector";
|
|
4
5
|
import {renderWithTheme} from "./test-utils";
|
|
5
6
|
|
|
7
|
+
interface LayoutEvent {
|
|
8
|
+
nativeEvent: {layout: {height: number; width: number; x: number; y: number}};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface LayoutRoot {
|
|
12
|
+
props: {onLayout?: (event: LayoutEvent) => void};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface RenderItemCell {
|
|
16
|
+
props: {onPress?: () => void};
|
|
17
|
+
}
|
|
18
|
+
|
|
6
19
|
describe("EmojiSelector", () => {
|
|
7
20
|
it("renders search bar when showSearchBar is true", () => {
|
|
8
21
|
const {getByPlaceholderText} = renderWithTheme(
|
|
@@ -58,4 +71,252 @@ describe("EmojiSelector", () => {
|
|
|
58
71
|
|
|
59
72
|
expect(tree.toJSON()).toMatchSnapshot();
|
|
60
73
|
});
|
|
74
|
+
|
|
75
|
+
it("renders without tabs", () => {
|
|
76
|
+
const {toJSON} = renderWithTheme(
|
|
77
|
+
<EmojiSelector
|
|
78
|
+
category={Categories.people}
|
|
79
|
+
columns={6}
|
|
80
|
+
onEmojiSelected={mock(() => {})}
|
|
81
|
+
placeholder="Search emojis"
|
|
82
|
+
showHistory={false}
|
|
83
|
+
showSearchBar
|
|
84
|
+
showSectionTitles={false}
|
|
85
|
+
showTabs={false}
|
|
86
|
+
theme="#007AFF"
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
expect(toJSON()).toMatchSnapshot();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("renders without search bar", () => {
|
|
93
|
+
const {queryByPlaceholderText} = renderWithTheme(
|
|
94
|
+
<EmojiSelector
|
|
95
|
+
category={Categories.people}
|
|
96
|
+
columns={6}
|
|
97
|
+
onEmojiSelected={mock(() => {})}
|
|
98
|
+
placeholder="Search emojis"
|
|
99
|
+
showHistory={false}
|
|
100
|
+
showSearchBar={false}
|
|
101
|
+
showSectionTitles
|
|
102
|
+
showTabs
|
|
103
|
+
theme="#007AFF"
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
expect(queryByPlaceholderText("Search emojis")).toBeNull();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("renders with history enabled", () => {
|
|
110
|
+
const {toJSON} = renderWithTheme(
|
|
111
|
+
<EmojiSelector
|
|
112
|
+
category={Categories.people}
|
|
113
|
+
columns={6}
|
|
114
|
+
onEmojiSelected={mock(() => {})}
|
|
115
|
+
placeholder="Search emojis"
|
|
116
|
+
showHistory
|
|
117
|
+
showSearchBar
|
|
118
|
+
showSectionTitles
|
|
119
|
+
showTabs
|
|
120
|
+
theme="#007AFF"
|
|
121
|
+
/>
|
|
122
|
+
);
|
|
123
|
+
expect(toJSON()).toMatchSnapshot();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("renders with all category and shouldInclude filter", () => {
|
|
127
|
+
const {toJSON} = renderWithTheme(
|
|
128
|
+
<EmojiSelector
|
|
129
|
+
category={Categories.all}
|
|
130
|
+
columns={8}
|
|
131
|
+
onEmojiSelected={mock(() => {})}
|
|
132
|
+
placeholder="Search"
|
|
133
|
+
shouldInclude={(emoji) => emoji.category === "Smileys & Emotion"}
|
|
134
|
+
showHistory={false}
|
|
135
|
+
showSearchBar
|
|
136
|
+
showSectionTitles
|
|
137
|
+
showTabs
|
|
138
|
+
theme="#007AFF"
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
expect(toJSON()).toMatchSnapshot();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("renders with history category", () => {
|
|
145
|
+
const {toJSON} = renderWithTheme(
|
|
146
|
+
<EmojiSelector
|
|
147
|
+
category={Categories.history}
|
|
148
|
+
columns={6}
|
|
149
|
+
onEmojiSelected={mock(() => {})}
|
|
150
|
+
placeholder="Search"
|
|
151
|
+
showHistory
|
|
152
|
+
showSearchBar
|
|
153
|
+
showSectionTitles
|
|
154
|
+
showTabs
|
|
155
|
+
theme="#007AFF"
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
158
|
+
expect(toJSON()).toMatchSnapshot();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("updates search query when user types in search bar", async () => {
|
|
162
|
+
const {getByPlaceholderText} = renderWithTheme(
|
|
163
|
+
<EmojiSelector
|
|
164
|
+
category={Categories.all}
|
|
165
|
+
columns={6}
|
|
166
|
+
onEmojiSelected={mock(() => {})}
|
|
167
|
+
placeholder="Search emojis"
|
|
168
|
+
showHistory={false}
|
|
169
|
+
showSearchBar
|
|
170
|
+
showSectionTitles
|
|
171
|
+
showTabs
|
|
172
|
+
theme="#007AFF"
|
|
173
|
+
/>
|
|
174
|
+
);
|
|
175
|
+
const input = getByPlaceholderText("Search emojis");
|
|
176
|
+
await act(async () => {
|
|
177
|
+
fireEvent.changeText(input, "smile");
|
|
178
|
+
});
|
|
179
|
+
expect(input.props.value).toBe("smile");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("charFromEmojiObject returns a string for a valid emoji", () => {
|
|
183
|
+
const smiley = {
|
|
184
|
+
category: "Smileys & Emotion",
|
|
185
|
+
short_names: ["smiley"],
|
|
186
|
+
sort_order: 1,
|
|
187
|
+
unified: "1F603",
|
|
188
|
+
};
|
|
189
|
+
expect(charFromEmojiObject(smiley)).toBe("😃");
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("exports Categories object with all expected keys", () => {
|
|
193
|
+
expect(Categories.all).toBeDefined();
|
|
194
|
+
expect(Categories.emotion).toBeDefined();
|
|
195
|
+
expect(Categories.people).toBeDefined();
|
|
196
|
+
expect(Categories.history).toBeDefined();
|
|
197
|
+
expect(Categories.nature).toBeDefined();
|
|
198
|
+
expect(Categories.food).toBeDefined();
|
|
199
|
+
expect(Categories.activities).toBeDefined();
|
|
200
|
+
expect(Categories.places).toBeDefined();
|
|
201
|
+
expect(Categories.objects).toBeDefined();
|
|
202
|
+
expect(Categories.symbols).toBeDefined();
|
|
203
|
+
expect(Categories.flags).toBeDefined();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("handles layout event to compute emoji list and set ready state", async () => {
|
|
207
|
+
const {toJSON, root} = renderWithTheme(
|
|
208
|
+
<EmojiSelector
|
|
209
|
+
category={Categories.people}
|
|
210
|
+
columns={6}
|
|
211
|
+
onEmojiSelected={mock(() => {})}
|
|
212
|
+
placeholder="Search"
|
|
213
|
+
showHistory={false}
|
|
214
|
+
showSearchBar
|
|
215
|
+
showSectionTitles
|
|
216
|
+
showTabs
|
|
217
|
+
theme="#007AFF"
|
|
218
|
+
/>
|
|
219
|
+
);
|
|
220
|
+
// Trigger the onLayout callback so state moves to ready.
|
|
221
|
+
await act(async () => {
|
|
222
|
+
(root as LayoutRoot).props.onLayout?.({
|
|
223
|
+
nativeEvent: {layout: {height: 600, width: 360, x: 0, y: 0}},
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
expect(toJSON()).toBeTruthy();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("switches categories when a tab is pressed after layout", async () => {
|
|
230
|
+
const {root, UNSAFE_getAllByType} = renderWithTheme(
|
|
231
|
+
<EmojiSelector
|
|
232
|
+
category={Categories.people}
|
|
233
|
+
columns={6}
|
|
234
|
+
onEmojiSelected={mock(() => {})}
|
|
235
|
+
placeholder="Search"
|
|
236
|
+
showHistory={false}
|
|
237
|
+
showSearchBar
|
|
238
|
+
showSectionTitles
|
|
239
|
+
showTabs
|
|
240
|
+
theme="#007AFF"
|
|
241
|
+
/>
|
|
242
|
+
);
|
|
243
|
+
await act(async () => {
|
|
244
|
+
(root as LayoutRoot).props.onLayout?.({
|
|
245
|
+
nativeEvent: {layout: {height: 600, width: 360, x: 0, y: 0}},
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
const {TouchableOpacity} = require("react-native");
|
|
249
|
+
const tabs = UNSAFE_getAllByType(TouchableOpacity);
|
|
250
|
+
expect(tabs.length).toBeGreaterThan(0);
|
|
251
|
+
// Press the first tab (e.g., "Smileys & Emotion") to exercise handleTabSelect.
|
|
252
|
+
await act(async () => {
|
|
253
|
+
tabs[0].props.onPress?.();
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("invokes onEmojiSelected when an emoji cell is pressed", async () => {
|
|
258
|
+
const onEmojiSelected = mock(() => {});
|
|
259
|
+
const {root, UNSAFE_getAllByType} = renderWithTheme(
|
|
260
|
+
<EmojiSelector
|
|
261
|
+
category={Categories.emotion}
|
|
262
|
+
columns={6}
|
|
263
|
+
onEmojiSelected={onEmojiSelected}
|
|
264
|
+
placeholder="Search"
|
|
265
|
+
showHistory
|
|
266
|
+
showSearchBar={false}
|
|
267
|
+
showSectionTitles
|
|
268
|
+
showTabs={false}
|
|
269
|
+
theme="#007AFF"
|
|
270
|
+
/>
|
|
271
|
+
);
|
|
272
|
+
await act(async () => {
|
|
273
|
+
(root as LayoutRoot).props.onLayout?.({
|
|
274
|
+
nativeEvent: {layout: {height: 600, width: 360, x: 0, y: 0}},
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const {FlatList} = require("react-native");
|
|
279
|
+
const [list] = UNSAFE_getAllByType(FlatList);
|
|
280
|
+
expect(list).toBeTruthy();
|
|
281
|
+
const data = list.props.data ?? [];
|
|
282
|
+
expect(data.length).toBeGreaterThan(0);
|
|
283
|
+
const first = data[0];
|
|
284
|
+
const cell = list.props.renderItem({index: 0, item: first});
|
|
285
|
+
// Invoke the emoji cell onPress to exercise handleEmojiSelect + addToHistoryAsync.
|
|
286
|
+
(cell.props as RenderItemCell["props"]).onPress?.();
|
|
287
|
+
await act(async () => {
|
|
288
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
289
|
+
});
|
|
290
|
+
expect(onEmojiSelected).toHaveBeenCalled();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("filters the emoji list when a search query is entered", async () => {
|
|
294
|
+
const {getByPlaceholderText, UNSAFE_getAllByType, root} = renderWithTheme(
|
|
295
|
+
<EmojiSelector
|
|
296
|
+
category={Categories.all}
|
|
297
|
+
columns={6}
|
|
298
|
+
onEmojiSelected={mock(() => {})}
|
|
299
|
+
placeholder="Search emojis"
|
|
300
|
+
showHistory={false}
|
|
301
|
+
showSearchBar
|
|
302
|
+
showSectionTitles
|
|
303
|
+
showTabs
|
|
304
|
+
theme="#007AFF"
|
|
305
|
+
/>
|
|
306
|
+
);
|
|
307
|
+
await act(async () => {
|
|
308
|
+
(root as LayoutRoot).props.onLayout?.({
|
|
309
|
+
nativeEvent: {layout: {height: 600, width: 360, x: 0, y: 0}},
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const input = getByPlaceholderText("Search emojis");
|
|
314
|
+
await act(async () => {
|
|
315
|
+
fireEvent.changeText(input, "smile");
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
const {FlatList} = require("react-native");
|
|
319
|
+
const [list] = UNSAFE_getAllByType(FlatList);
|
|
320
|
+
expect(list.props.data.length).toBeGreaterThan(0);
|
|
321
|
+
});
|
|
61
322
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {describe, expect, it} from "bun:test";
|
|
2
|
-
import {render} from "@testing-library/react-native";
|
|
1
|
+
import {describe, expect, it, mock} from "bun:test";
|
|
2
|
+
import {act, render} from "@testing-library/react-native";
|
|
3
3
|
import {createRef} from "react";
|
|
4
4
|
|
|
5
5
|
import type {ActionSheet} from "./ActionSheet";
|
|
@@ -31,4 +31,59 @@ describe("HeightActionSheet", () => {
|
|
|
31
31
|
);
|
|
32
32
|
expect(toJSON()).toMatchSnapshot();
|
|
33
33
|
});
|
|
34
|
+
|
|
35
|
+
it("renders with title and min/max values", () => {
|
|
36
|
+
const actionSheetRef = createRef<ActionSheet>();
|
|
37
|
+
const {getByText} = render(
|
|
38
|
+
<ThemeProvider>
|
|
39
|
+
<HeightActionSheet
|
|
40
|
+
actionSheetRef={actionSheetRef}
|
|
41
|
+
max={84}
|
|
42
|
+
min={36}
|
|
43
|
+
onChange={() => {}}
|
|
44
|
+
title="Select Height"
|
|
45
|
+
value="60"
|
|
46
|
+
/>
|
|
47
|
+
</ThemeProvider>
|
|
48
|
+
);
|
|
49
|
+
expect(getByText("Select Height")).toBeTruthy();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("invokes onChange when feet picker changes", () => {
|
|
53
|
+
const actionSheetRef = createRef<ActionSheet>();
|
|
54
|
+
const handleChange = mock((_val: string) => {});
|
|
55
|
+
const {UNSAFE_getAllByProps} = render(
|
|
56
|
+
<ThemeProvider>
|
|
57
|
+
<HeightActionSheet actionSheetRef={actionSheetRef} onChange={handleChange} value="72" />
|
|
58
|
+
</ThemeProvider>
|
|
59
|
+
);
|
|
60
|
+
// feet picker has selectedValue "6" (72 / 12)
|
|
61
|
+
const feetPickers = UNSAFE_getAllByProps({selectedValue: "6"});
|
|
62
|
+
const feetPicker = feetPickers.find((p) => typeof p.props.onValueChange === "function");
|
|
63
|
+
act(() => {
|
|
64
|
+
if (feetPicker) {
|
|
65
|
+
feetPicker.props.onValueChange(5);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
expect(handleChange).toHaveBeenCalled();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("invokes onChange when inches picker changes", () => {
|
|
72
|
+
const actionSheetRef = createRef<ActionSheet>();
|
|
73
|
+
const handleChange = mock((_val: string) => {});
|
|
74
|
+
const {UNSAFE_getAllByProps} = render(
|
|
75
|
+
<ThemeProvider>
|
|
76
|
+
<HeightActionSheet actionSheetRef={actionSheetRef} onChange={handleChange} value="73" />
|
|
77
|
+
</ThemeProvider>
|
|
78
|
+
);
|
|
79
|
+
// inches picker has selectedValue "1" (73 % 12)
|
|
80
|
+
const inchPickers = UNSAFE_getAllByProps({selectedValue: "1"});
|
|
81
|
+
const inchPicker = inchPickers.find((p) => typeof p.props.onValueChange === "function");
|
|
82
|
+
act(() => {
|
|
83
|
+
if (inchPicker) {
|
|
84
|
+
inchPicker.props.onValueChange(5);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
expect(handleChange).toHaveBeenCalled();
|
|
88
|
+
});
|
|
34
89
|
});
|
|
@@ -51,4 +51,20 @@ describe("InfoModalIcon", () => {
|
|
|
51
51
|
fireEvent.press(infoIcon);
|
|
52
52
|
expect(toJSON()).toMatchSnapshot();
|
|
53
53
|
});
|
|
54
|
+
|
|
55
|
+
it("invokes primary Close button handler without throwing", () => {
|
|
56
|
+
const {getByTestId, getByText} = renderWithTheme(
|
|
57
|
+
<InfoModalIcon infoModalText="Dismissable text" infoModalTitle="Dismissable Title" />
|
|
58
|
+
);
|
|
59
|
+
fireEvent.press(getByTestId("info-icon"));
|
|
60
|
+
expect(() => fireEvent.press(getByText("Close"))).not.toThrow();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("invokes onDismiss when the close (x) icon is pressed", () => {
|
|
64
|
+
const {getByTestId, getByLabelText} = renderWithTheme(
|
|
65
|
+
<InfoModalIcon infoModalText="Body" infoModalTitle="Close Me" />
|
|
66
|
+
);
|
|
67
|
+
fireEvent.press(getByTestId("info-icon"));
|
|
68
|
+
expect(() => fireEvent.press(getByLabelText("Close modal"))).not.toThrow();
|
|
69
|
+
});
|
|
54
70
|
});
|
|
@@ -1,4 +1,38 @@
|
|
|
1
|
-
import {describe, expect, it} from "bun:test";
|
|
1
|
+
import {afterAll, describe, expect, it, mock} from "bun:test";
|
|
2
|
+
import {fireEvent} from "@testing-library/react-native";
|
|
3
|
+
import {Pressable, Text as RNText} from "react-native";
|
|
4
|
+
|
|
5
|
+
// Override the IconButton mock so the inline onClick arrow fires when pressed.
|
|
6
|
+
mock.module("./IconButton", () => ({
|
|
7
|
+
IconButton: ({
|
|
8
|
+
accessibilityLabel,
|
|
9
|
+
accessibilityHint,
|
|
10
|
+
iconName,
|
|
11
|
+
onClick,
|
|
12
|
+
tooltipText,
|
|
13
|
+
}: {
|
|
14
|
+
accessibilityLabel?: string;
|
|
15
|
+
accessibilityHint?: string;
|
|
16
|
+
iconName: string;
|
|
17
|
+
onClick?: () => void;
|
|
18
|
+
tooltipText?: string;
|
|
19
|
+
}) => (
|
|
20
|
+
<Pressable
|
|
21
|
+
accessibilityHint={accessibilityHint}
|
|
22
|
+
accessibilityLabel={accessibilityLabel}
|
|
23
|
+
onPress={onClick}
|
|
24
|
+
testID={`icon-button-${iconName}`}
|
|
25
|
+
>
|
|
26
|
+
<RNText>{tooltipText}</RNText>
|
|
27
|
+
</Pressable>
|
|
28
|
+
),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
afterAll(() => {
|
|
32
|
+
mock.module("./IconButton", () => ({
|
|
33
|
+
IconButton: mock(() => null),
|
|
34
|
+
}));
|
|
35
|
+
});
|
|
2
36
|
|
|
3
37
|
import {InfoTooltipButton} from "./InfoTooltipButton";
|
|
4
38
|
import {renderWithTheme} from "./test-utils";
|
|
@@ -19,4 +53,22 @@ describe("InfoTooltipButton", () => {
|
|
|
19
53
|
// The component renders an IconButton with exclamation icon
|
|
20
54
|
expect(toJSON()).toMatchSnapshot();
|
|
21
55
|
});
|
|
56
|
+
|
|
57
|
+
it("is defined and is a function component", () => {
|
|
58
|
+
expect(InfoTooltipButton).toBeDefined();
|
|
59
|
+
expect(typeof InfoTooltipButton).toBe("function");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("accepts a text prop without throwing", () => {
|
|
63
|
+
expect(() =>
|
|
64
|
+
renderWithTheme(<InfoTooltipButton text="Some details that explain the field" />)
|
|
65
|
+
).not.toThrow();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("fires the inline onClick handler when the IconButton is pressed", () => {
|
|
69
|
+
const {getByTestId} = renderWithTheme(
|
|
70
|
+
<InfoTooltipButton text="Some details that explain the field" />
|
|
71
|
+
);
|
|
72
|
+
expect(() => fireEvent.press(getByTestId("icon-button-exclamation"))).not.toThrow();
|
|
73
|
+
});
|
|
22
74
|
});
|