@jobber/components-native 0.101.3 → 0.101.4
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/package.json +19 -11
- package/dist/src/ActionItem/ActionItem.test.js +81 -0
- package/dist/src/ActionItem/ActionItemGroup.test.js +25 -0
- package/dist/src/ActionItem/components/ActionItemContainer.test.js +24 -0
- package/dist/src/ActionLabel/ActionLabel.test.js +81 -0
- package/dist/src/ActivityIndicator/ActivityIndicator.test.js +23 -0
- package/dist/src/AtlantisContext/AtlantisContext.test.js +35 -0
- package/dist/src/AtlantisThemeContext/AtlantisThemeContext.test.js +65 -0
- package/dist/src/AtlantisThemeContext/buildThemedStyles.test.js +43 -0
- package/dist/src/AutoLink/AutoLink.test.js +133 -0
- package/dist/src/AutoLink/components/Link/Link.test.js +18 -0
- package/dist/src/Banner/Banner.test.js +98 -0
- package/dist/src/BottomSheet/BottomSheet.test.js +105 -0
- package/dist/src/BottomSheet/components/BottomSheetOption/BottomSheetOption.test.js +19 -0
- package/dist/src/BottomSheet/hooks/useBottomSheetBackHandler.test.js +68 -0
- package/dist/src/Button/Button.test.js +228 -0
- package/dist/src/Button/components/InternalButtonLoading/InternalButtonLoading.test.js +25 -0
- package/dist/src/ButtonGroup/ButtonGroup.test.js +153 -0
- package/dist/src/Card/Card.test.js +80 -0
- package/dist/src/Card/components/InternalCardHeader.test.js +18 -0
- package/dist/src/Checkbox/Checkbox.test.js +135 -0
- package/dist/src/Checkbox/CheckboxGroup.test.js +197 -0
- package/dist/src/Checkbox/CheckboxGroupReducer.test.js +25 -0
- package/dist/src/Chip/Chip.test.js +69 -0
- package/dist/src/Content/Content.test.js +250 -0
- package/dist/src/ContentOverlay/ContentOverlay.test.js +297 -0
- package/dist/src/ContentOverlay/computeContentOverlayBehavior.test.js +197 -0
- package/dist/src/ContentOverlay/hooks/useBottomSheetModalBackHandler.test.js +62 -0
- package/dist/src/ContentOverlay/hooks/useKeyboardVisibility.test.js +41 -0
- package/dist/src/ContentOverlay/hooks/useViewLayoutHeight.test.js +62 -0
- package/dist/src/Disclosure/Disclosure.test.js +64 -0
- package/dist/src/Divider/Divider.test.js +65 -0
- package/dist/src/EmptyState/EmptyState.test.js +82 -0
- package/dist/src/ErrorMessageWrapper/ErrorMessageWrapper.test.js +29 -0
- package/dist/src/Flex/Flex.test.js +104 -0
- package/dist/src/Form/Form.test.js +393 -0
- package/dist/src/Form/components/FormErrorBanner/FormErrorBanner.test.js +41 -0
- package/dist/src/Form/components/FormMessage/FormMessage.test.js +73 -0
- package/dist/src/Form/components/FormMessageBanner/FormMessageBanner.test.js +30 -0
- package/dist/src/Form/components/FormSaveButton/FormSaveButton.test.js +82 -0
- package/dist/src/Form/context/AtlantisFormContext.test.js +28 -0
- package/dist/src/Form/hooks/useScrollToError/useScrollToError.test.js +89 -0
- package/dist/src/FormField/FormField.test.js +81 -0
- package/dist/src/FormatFile/FormatFile.test.js +212 -0
- package/dist/src/FormatFile/FormatFileThumbnail.test.js +192 -0
- package/dist/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.test.js +74 -0
- package/dist/src/FormatFile/components/MediaView/MediaView.test.js +202 -0
- package/dist/src/FormatFile/utils/parseFile.test.js +188 -0
- package/dist/src/Glimmer/Glimmer.test.js +61 -0
- package/dist/src/Heading/Heading.test.js +61 -0
- package/dist/src/Icon/Icon.test.js +40 -0
- package/dist/src/IconButton/IconButton.test.js +38 -0
- package/dist/src/InputCurrency/InputCurrency.test.js +106 -0
- package/dist/src/InputDate/InputDate.test.js +184 -0
- package/dist/src/InputEmail/InputEmail.test.js +27 -0
- package/dist/src/InputFieldWrapper/InputFieldWrapper.test.js +279 -0
- package/dist/src/InputFieldWrapper/components/ClearAction/ClearAction.test.js +9 -0
- package/dist/src/InputFieldWrapper/components/Prefix/Prefix.test.js +130 -0
- package/dist/src/InputFieldWrapper/components/Suffix/Suffix.test.js +51 -0
- package/dist/src/InputNumber/InputNumber.test.js +220 -0
- package/dist/src/InputPassword/InputPassword.test.js +63 -0
- package/dist/src/InputPressable/InputPressable.test.js +138 -0
- package/dist/src/InputSearch/InputSearch.test.js +54 -0
- package/dist/src/InputText/InputText.test.js +652 -0
- package/dist/src/InputText/context/InputAccessoriesProvider.test.js +71 -0
- package/dist/src/InputTime/InputTime.test.js +199 -0
- package/dist/src/InputTime/utils/utils.test.js +32 -0
- package/dist/src/ProgressBar/ProgressBar.test.js +89 -0
- package/dist/src/Select/Select.test.js +183 -0
- package/dist/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.test.js +51 -0
- package/dist/src/Select/components/SelectInternalPicker/SelectInternalPicker.test.js +72 -0
- package/dist/src/StatusLabel/StatusLabel.test.js +51 -0
- package/dist/src/Switch/Switch.test.js +60 -0
- package/dist/src/Switch/components/BaseSwitch/BaseSwitch.test.js +61 -0
- package/dist/src/Text/Text.test.js +161 -0
- package/dist/src/TextList/TextList.test.js +16 -0
- package/dist/src/ThumbnailList/ThumbnailList.test.js +72 -0
- package/dist/src/Toast/Toast.test.js +51 -0
- package/dist/src/Typography/Typography.test.js +225 -0
- package/dist/src/hooks/useAtlantisI18n/useAtlantisI18n.test.js +103 -0
- package/dist/src/utils/meta/meta.test.js +83 -0
- package/dist/tsconfig.build.json +5 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/tsconfig.eslint.json +14 -0
- package/dist/tsconfig.json +3 -4
- package/dist/types/src/ActionItem/ActionItem.test.d.ts +1 -0
- package/dist/types/src/ActionItem/ActionItemGroup.test.d.ts +1 -0
- package/dist/types/src/ActionItem/components/ActionItemContainer.test.d.ts +1 -0
- package/dist/types/src/ActionLabel/ActionLabel.test.d.ts +1 -0
- package/dist/types/src/ActivityIndicator/ActivityIndicator.test.d.ts +1 -0
- package/dist/types/src/AtlantisContext/AtlantisContext.test.d.ts +1 -0
- package/dist/types/src/AtlantisThemeContext/AtlantisThemeContext.test.d.ts +1 -0
- package/dist/types/src/AtlantisThemeContext/buildThemedStyles.test.d.ts +1 -0
- package/dist/types/src/AutoLink/AutoLink.test.d.ts +1 -0
- package/dist/types/src/AutoLink/components/Link/Link.test.d.ts +1 -0
- package/dist/types/src/Banner/Banner.test.d.ts +1 -0
- package/dist/types/src/BottomSheet/BottomSheet.test.d.ts +1 -0
- package/dist/types/src/BottomSheet/components/BottomSheetOption/BottomSheetOption.test.d.ts +1 -0
- package/dist/types/src/BottomSheet/hooks/useBottomSheetBackHandler.test.d.ts +1 -0
- package/dist/types/src/Button/Button.test.d.ts +1 -0
- package/dist/types/src/Button/components/InternalButtonLoading/InternalButtonLoading.test.d.ts +1 -0
- package/dist/types/src/ButtonGroup/ButtonGroup.test.d.ts +1 -0
- package/dist/types/src/Card/Card.test.d.ts +1 -0
- package/dist/types/src/Card/components/InternalCardHeader.test.d.ts +1 -0
- package/dist/types/src/Checkbox/Checkbox.test.d.ts +1 -0
- package/dist/types/src/Checkbox/CheckboxGroup.test.d.ts +1 -0
- package/dist/types/src/Checkbox/CheckboxGroupReducer.test.d.ts +1 -0
- package/dist/types/src/Chip/Chip.test.d.ts +1 -0
- package/dist/types/src/Content/Content.test.d.ts +1 -0
- package/dist/types/src/ContentOverlay/BottomSheetKeyboardAwareScrollView.d.ts +2 -1
- package/dist/types/src/ContentOverlay/ContentOverlay.test.d.ts +1 -0
- package/dist/types/src/ContentOverlay/computeContentOverlayBehavior.test.d.ts +1 -0
- package/dist/types/src/ContentOverlay/hooks/useBottomSheetModalBackHandler.test.d.ts +1 -0
- package/dist/types/src/ContentOverlay/hooks/useKeyboardVisibility.test.d.ts +1 -0
- package/dist/types/src/ContentOverlay/hooks/useViewLayoutHeight.test.d.ts +1 -0
- package/dist/types/src/Disclosure/Disclosure.test.d.ts +1 -0
- package/dist/types/src/Divider/Divider.test.d.ts +1 -0
- package/dist/types/src/EmptyState/EmptyState.test.d.ts +1 -0
- package/dist/types/src/ErrorMessageWrapper/ErrorMessageWrapper.test.d.ts +1 -0
- package/dist/types/src/Flex/Flex.test.d.ts +1 -0
- package/dist/types/src/Form/Form.test.d.ts +1 -0
- package/dist/types/src/Form/components/FormErrorBanner/FormErrorBanner.test.d.ts +1 -0
- package/dist/types/src/Form/components/FormMessage/FormMessage.test.d.ts +1 -0
- package/dist/types/src/Form/components/FormMessageBanner/FormMessageBanner.test.d.ts +1 -0
- package/dist/types/src/Form/components/FormSaveButton/FormSaveButton.test.d.ts +1 -0
- package/dist/types/src/Form/context/AtlantisFormContext.test.d.ts +1 -0
- package/dist/types/src/Form/hooks/useScrollToError/useScrollToError.test.d.ts +1 -0
- package/dist/types/src/FormField/FormField.test.d.ts +1 -0
- package/dist/types/src/FormatFile/FormatFile.test.d.ts +1 -0
- package/dist/types/src/FormatFile/FormatFileThumbnail.test.d.ts +1 -0
- package/dist/types/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.test.d.ts +1 -0
- package/dist/types/src/FormatFile/components/MediaView/MediaView.test.d.ts +1 -0
- package/dist/types/src/FormatFile/utils/parseFile.test.d.ts +1 -0
- package/dist/types/src/Glimmer/Glimmer.test.d.ts +1 -0
- package/dist/types/src/Heading/Heading.test.d.ts +1 -0
- package/dist/types/src/Icon/Icon.test.d.ts +1 -0
- package/dist/types/src/IconButton/IconButton.test.d.ts +1 -0
- package/dist/types/src/InputCurrency/InputCurrency.test.d.ts +1 -0
- package/dist/types/src/InputDate/InputDate.test.d.ts +1 -0
- package/dist/types/src/InputEmail/InputEmail.test.d.ts +1 -0
- package/dist/types/src/InputFieldWrapper/InputFieldWrapper.test.d.ts +1 -0
- package/dist/types/src/InputFieldWrapper/components/ClearAction/ClearAction.test.d.ts +1 -0
- package/dist/types/src/InputFieldWrapper/components/Prefix/Prefix.test.d.ts +1 -0
- package/dist/types/src/InputFieldWrapper/components/Suffix/Suffix.test.d.ts +1 -0
- package/dist/types/src/InputNumber/InputNumber.test.d.ts +1 -0
- package/dist/types/src/InputPassword/InputPassword.test.d.ts +1 -0
- package/dist/types/src/InputPressable/InputPressable.test.d.ts +1 -0
- package/dist/types/src/InputSearch/InputSearch.test.d.ts +1 -0
- package/dist/types/src/InputText/InputText.test.d.ts +1 -0
- package/dist/types/src/InputText/context/InputAccessoriesProvider.test.d.ts +1 -0
- package/dist/types/src/InputTime/InputTime.test.d.ts +1 -0
- package/dist/types/src/InputTime/utils/utils.test.d.ts +1 -0
- package/dist/types/src/ProgressBar/ProgressBar.test.d.ts +1 -0
- package/dist/types/src/Select/Select.test.d.ts +1 -0
- package/dist/types/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.test.d.ts +1 -0
- package/dist/types/src/Select/components/SelectInternalPicker/SelectInternalPicker.test.d.ts +1 -0
- package/dist/types/src/StatusLabel/StatusLabel.test.d.ts +1 -0
- package/dist/types/src/Switch/Switch.test.d.ts +1 -0
- package/dist/types/src/Switch/components/BaseSwitch/BaseSwitch.test.d.ts +1 -0
- package/dist/types/src/Text/Text.test.d.ts +1 -0
- package/dist/types/src/TextList/TextList.test.d.ts +1 -0
- package/dist/types/src/ThumbnailList/ThumbnailList.test.d.ts +1 -0
- package/dist/types/src/Toast/Toast.test.d.ts +1 -0
- package/dist/types/src/Typography/Typography.test.d.ts +1 -0
- package/dist/types/src/hooks/useAtlantisI18n/useAtlantisI18n.test.d.ts +1 -0
- package/dist/types/src/utils/meta/meta.test.d.ts +1 -0
- package/package.json +19 -11
- package/src/Button/Button.test.tsx +6 -2
- package/src/ContentOverlay/hooks/useViewLayoutHeight.test.ts +3 -3
- package/src/Divider/Divider.stories.tsx +1 -1
- package/src/Flex/Flex.test.tsx +1 -1
- package/src/Form/Form.test.tsx +3 -1
- package/src/FormField/FormField.test.tsx +5 -1
- package/src/Heading/__snapshots__/Heading.test.tsx.snap +1 -1
- package/src/InputDate/InputDate.test.tsx +7 -1
- package/src/InputText/InputText.test.tsx +2 -1
- package/src/InputTime/InputTime.test.tsx +7 -1
- package/src/Select/Select.test.tsx +1 -1
- package/src/StatusLabel/__snapshots__/StatusLabel.test.tsx.snap +8 -8
- package/src/Text/__snapshots__/Text.test.tsx.snap +2 -2
- package/src/ThumbnailList/__snapshots__/ThumbnailList.test.tsx.snap +1 -1
- package/src/Typography/__snapshots__/Typography.test.tsx.snap +4 -4
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobber/components-native",
|
|
3
|
-
"version": "0.101.
|
|
3
|
+
"version": "0.101.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React Native implementation of Atlantis",
|
|
6
6
|
"repository": {
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
],
|
|
30
30
|
"scripts": {
|
|
31
31
|
"clean": "rm -rf dist/* tsconfig.tsbuildinfo",
|
|
32
|
-
"build": "
|
|
33
|
-
"bootstrap": "
|
|
34
|
-
"prepack": "
|
|
32
|
+
"build": "pnpm run build:clean && pnpm run compile",
|
|
33
|
+
"bootstrap": "pnpm run build",
|
|
34
|
+
"prepack": "pnpm run build",
|
|
35
35
|
"watch": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
|
|
36
36
|
"compile": "tsc -p tsconfig.build.json",
|
|
37
37
|
"build:clean": "rm -rf ./dist",
|
|
@@ -52,20 +52,26 @@
|
|
|
52
52
|
"ts-xor": "^1.1.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
+
"@babel/runtime": "^7.29.2",
|
|
55
56
|
"@gorhom/bottom-sheet": "^5.2.8",
|
|
57
|
+
"@jobber/hooks": "2.19.4",
|
|
56
58
|
"@react-native-community/datetimepicker": "^8.4.5",
|
|
57
59
|
"@react-native/babel-preset": "^0.82.1",
|
|
58
|
-
"@storybook/addon-a11y": "
|
|
59
|
-
"@storybook/
|
|
60
|
-
"@storybook/react-native-web-vite": "
|
|
60
|
+
"@storybook/addon-a11y": "10.3.5",
|
|
61
|
+
"@storybook/addon-mcp": "^0.4.2",
|
|
62
|
+
"@storybook/react-native-web-vite": "10.3.5",
|
|
61
63
|
"@testing-library/react-native": "^13.3.3",
|
|
64
|
+
"@types/jest": "^29.5.14",
|
|
62
65
|
"@types/lodash.chunk": "^4.2.7",
|
|
63
66
|
"@types/lodash.debounce": "^4.0.7",
|
|
64
67
|
"@types/lodash.identity": "^3.0.7",
|
|
68
|
+
"@types/node": "^22.19.17",
|
|
65
69
|
"@types/react-native-uuid": "^1.4.0",
|
|
66
70
|
"@types/react-test-renderer": "19.1.0",
|
|
67
71
|
"date-fns": "^2.30.0",
|
|
68
72
|
"date-fns-tz": "^2.0.0",
|
|
73
|
+
"react": "19.1.1",
|
|
74
|
+
"react-dom": "19.1.1",
|
|
69
75
|
"react-native": "^0.82.1",
|
|
70
76
|
"react-native-gesture-handler": "^2.29.1",
|
|
71
77
|
"react-native-keyboard-controller": "^1.12.0",
|
|
@@ -75,17 +81,19 @@
|
|
|
75
81
|
"react-native-screens": "^4.18.0",
|
|
76
82
|
"react-native-svg": "^15.1.0",
|
|
77
83
|
"react-native-web": "^0.20.0",
|
|
78
|
-
"react-test-renderer": "
|
|
79
|
-
"storybook": "
|
|
84
|
+
"react-test-renderer": "19.1.1",
|
|
85
|
+
"storybook": "10.3.5",
|
|
86
|
+
"vite": "^8.0.9"
|
|
80
87
|
},
|
|
81
88
|
"peerDependencies": {
|
|
82
89
|
"@babel/core": "^7.4.5",
|
|
83
90
|
"@gorhom/bottom-sheet": "^5.2.8",
|
|
84
91
|
"@jobber/design": "*",
|
|
92
|
+
"@jobber/hooks": ">=2",
|
|
85
93
|
"@react-native-community/datetimepicker": ">=6.7.0",
|
|
86
94
|
"date-fns": "^2.30.0",
|
|
87
95
|
"date-fns-tz": "^2.0.0",
|
|
88
|
-
"react": "^19",
|
|
96
|
+
"react": "^19.1.0",
|
|
89
97
|
"react-intl": "^6 || ^7",
|
|
90
98
|
"react-native": ">=0.79.5",
|
|
91
99
|
"react-native-gesture-handler": ">=2.22.0",
|
|
@@ -96,5 +104,5 @@
|
|
|
96
104
|
"react-native-screens": ">=4.18.0",
|
|
97
105
|
"react-native-svg": ">=12.0.0"
|
|
98
106
|
},
|
|
99
|
-
"gitHead": "
|
|
107
|
+
"gitHead": "2306bc5ca1ed0a56871602085cc3c4223b9d08d8"
|
|
100
108
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { fireEvent, render } from "@testing-library/react-native";
|
|
3
|
+
import { ActionItem } from "./ActionItem";
|
|
4
|
+
import { Button } from "../Button";
|
|
5
|
+
import { Text } from "../Text";
|
|
6
|
+
describe("ActionItem", () => {
|
|
7
|
+
const pressHandler = jest.fn();
|
|
8
|
+
const defaultActionIcon = "longArrowRight";
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest.resetAllMocks();
|
|
11
|
+
});
|
|
12
|
+
it("should call onPress when ActionItem is pressed", () => {
|
|
13
|
+
const text = "Get out of my swamp";
|
|
14
|
+
const { getByText } = render(React.createElement(ActionItem, { onPress: pressHandler },
|
|
15
|
+
React.createElement(Text, null, text)));
|
|
16
|
+
fireEvent.press(getByText(text));
|
|
17
|
+
expect(pressHandler).toHaveBeenCalled();
|
|
18
|
+
});
|
|
19
|
+
it("should should display title when provided", () => {
|
|
20
|
+
const title = "Profitttt 💰";
|
|
21
|
+
const { getByText } = render(React.createElement(ActionItem, { title: title }));
|
|
22
|
+
expect(getByText(title)).toBeDefined();
|
|
23
|
+
});
|
|
24
|
+
it("should display icon when provided", () => {
|
|
25
|
+
const iconName = "calendar";
|
|
26
|
+
const { getByTestId } = render(React.createElement(ActionItem, { onPress: pressHandler, icon: iconName }));
|
|
27
|
+
expect(getByTestId(iconName)).toBeDefined();
|
|
28
|
+
});
|
|
29
|
+
describe("Action Icon", () => {
|
|
30
|
+
it("should display default action icon when custom action icon is not provided", () => {
|
|
31
|
+
const { getByTestId } = render(React.createElement(ActionItem, { onPress: pressHandler }));
|
|
32
|
+
expect(getByTestId(defaultActionIcon)).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
it("should display custom action icon when provided", () => {
|
|
35
|
+
const iconName = "calendar";
|
|
36
|
+
const { getByTestId, queryByTestId } = render(React.createElement(ActionItem, { onPress: pressHandler, actionIcon: iconName }));
|
|
37
|
+
expect(getByTestId(iconName)).toBeDefined();
|
|
38
|
+
expect(queryByTestId(defaultActionIcon)).toBeFalsy();
|
|
39
|
+
});
|
|
40
|
+
it("should not display action icon when onPress does not exist", () => {
|
|
41
|
+
const { queryByTestId } = render(React.createElement(ActionItem, { title: "Some title" }));
|
|
42
|
+
expect(queryByTestId(defaultActionIcon)).toBeFalsy();
|
|
43
|
+
});
|
|
44
|
+
it("should display longArrowRight icon when edit name is used", () => {
|
|
45
|
+
const iconName = "edit";
|
|
46
|
+
const { getByTestId } = render(React.createElement(ActionItem, { onPress: pressHandler, actionIcon: iconName }));
|
|
47
|
+
expect(getByTestId(defaultActionIcon)).toBeDefined();
|
|
48
|
+
});
|
|
49
|
+
it("should display actual edit icon when editpencil name is used", () => {
|
|
50
|
+
const iconName = "editpencil";
|
|
51
|
+
const { getByTestId } = render(React.createElement(ActionItem, { onPress: pressHandler, actionIcon: iconName }));
|
|
52
|
+
expect(getByTestId("edit")).toBeDefined();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe("When child has onPress event", () => {
|
|
56
|
+
const childPressHandler = jest.fn();
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
childPressHandler.mockClear();
|
|
59
|
+
});
|
|
60
|
+
it("should only call child onPress when child is pressed", () => {
|
|
61
|
+
const buttonLabel = "I am button label";
|
|
62
|
+
const text = "Some other text chillin";
|
|
63
|
+
const { getByText } = render(React.createElement(ActionItem, { onPress: pressHandler },
|
|
64
|
+
React.createElement(Button, { label: buttonLabel, onPress: childPressHandler }),
|
|
65
|
+
React.createElement(Text, null, text)));
|
|
66
|
+
fireEvent.press(getByText(buttonLabel));
|
|
67
|
+
expect(childPressHandler).toHaveBeenCalledTimes(1);
|
|
68
|
+
expect(pressHandler).toHaveBeenCalledTimes(0);
|
|
69
|
+
});
|
|
70
|
+
it("should only call parent onPress when the rest of Parent is pressed", () => {
|
|
71
|
+
const buttonLabel = "I am button label";
|
|
72
|
+
const text = "Some other text chillin";
|
|
73
|
+
const { getByText } = render(React.createElement(ActionItem, { onPress: pressHandler },
|
|
74
|
+
React.createElement(Button, { label: buttonLabel, onPress: childPressHandler }),
|
|
75
|
+
React.createElement(Text, null, text)));
|
|
76
|
+
fireEvent.press(getByText(text));
|
|
77
|
+
expect(pressHandler).toHaveBeenCalledTimes(1);
|
|
78
|
+
expect(childPressHandler).toHaveBeenCalledTimes(0);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { ActionItemGroup } from "./ActionItemGroup";
|
|
4
|
+
import { Text } from "../Text";
|
|
5
|
+
describe("ActionItemGroup", () => {
|
|
6
|
+
const dividerTestId = "Divider";
|
|
7
|
+
describe("Separate By Dividers", () => {
|
|
8
|
+
it("should display dividers between children", () => {
|
|
9
|
+
const text = "Get out of my swamp";
|
|
10
|
+
const { queryAllByTestId } = render(React.createElement(ActionItemGroup, null,
|
|
11
|
+
React.createElement(Text, null, text),
|
|
12
|
+
React.createElement(Text, null, text),
|
|
13
|
+
React.createElement(Text, null, text)));
|
|
14
|
+
const dividers = queryAllByTestId(dividerTestId);
|
|
15
|
+
expect(dividers).toHaveLength(2);
|
|
16
|
+
});
|
|
17
|
+
it("should not display divider when only 1 child", () => {
|
|
18
|
+
const text = "Get out of my swamp";
|
|
19
|
+
const { queryByTestId } = render(React.createElement(ActionItemGroup, null,
|
|
20
|
+
React.createElement(Text, null, text)));
|
|
21
|
+
const dividers = queryByTestId(dividerTestId);
|
|
22
|
+
expect(dividers).toBeFalsy();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { fireEvent, render } from "@testing-library/react-native";
|
|
3
|
+
import { ActionItemContainer } from "./ActionItemContainer";
|
|
4
|
+
import { Text } from "../../Text";
|
|
5
|
+
describe("ActionItemContainer", () => {
|
|
6
|
+
const testID = "actionItemContainer";
|
|
7
|
+
const text = "some text";
|
|
8
|
+
it("should render a pressable header", () => {
|
|
9
|
+
const handlePress = jest.fn();
|
|
10
|
+
const { getByTestId } = render(React.createElement(ActionItemContainer, { onPress: handlePress, testID: testID },
|
|
11
|
+
React.createElement(Text, null, text)));
|
|
12
|
+
const container = getByTestId(testID);
|
|
13
|
+
expect(container.props.accessibilityRole).toBe("button");
|
|
14
|
+
fireEvent.press(container);
|
|
15
|
+
expect(handlePress).toHaveBeenCalled();
|
|
16
|
+
});
|
|
17
|
+
it("should render an un-pressable header", () => {
|
|
18
|
+
const { getByTestId } = render(React.createElement(ActionItemContainer, { testID: testID },
|
|
19
|
+
React.createElement(Text, null, text)));
|
|
20
|
+
const container = getByTestId(testID);
|
|
21
|
+
expect(container.props.accessibilityRole).not.toBe("button");
|
|
22
|
+
expect(container.props.onPress).toBeUndefined();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { ActionLabel } from "./ActionLabel";
|
|
4
|
+
import { tokens } from "../utils/design";
|
|
5
|
+
const defaultStyles = {
|
|
6
|
+
fontFamily: "inter-semibold",
|
|
7
|
+
color: tokens["color-interactive"],
|
|
8
|
+
textAlign: "center",
|
|
9
|
+
fontSize: tokens["typography--fontSize-base"],
|
|
10
|
+
lineHeight: tokens["typography--lineHeight-tight"],
|
|
11
|
+
letterSpacing: tokens["typography--letterSpacing-base"],
|
|
12
|
+
};
|
|
13
|
+
describe("ActionLabel", () => {
|
|
14
|
+
it("renders the default action label", () => {
|
|
15
|
+
const text = "Default Action Label";
|
|
16
|
+
const { getByText } = render(React.createElement(ActionLabel, null, text));
|
|
17
|
+
const el = getByText(text);
|
|
18
|
+
expect(el).toBeDefined();
|
|
19
|
+
expect(getStyleObject(el)).toMatchObject(defaultStyles);
|
|
20
|
+
});
|
|
21
|
+
describe("Variations", () => {
|
|
22
|
+
it("renders a destructive variation", () => {
|
|
23
|
+
const text = "Destructive Action Label";
|
|
24
|
+
const { getByText } = render(React.createElement(ActionLabel, { variation: "destructive" }, text));
|
|
25
|
+
expect(getStyleObject(getByText(text))).toMatchObject(Object.assign(Object.assign({}, defaultStyles), { color: tokens["color-destructive"] }));
|
|
26
|
+
});
|
|
27
|
+
it("renders a learning variation", () => {
|
|
28
|
+
const text = "Learning Action Label";
|
|
29
|
+
const { getByText } = render(React.createElement(ActionLabel, { variation: "learning" }, text));
|
|
30
|
+
expect(getStyleObject(getByText(text))).toMatchObject(Object.assign(Object.assign({}, defaultStyles), { color: tokens["color-informative"] }));
|
|
31
|
+
});
|
|
32
|
+
it("renders an interactiveSubtle variation", () => {
|
|
33
|
+
const text = "Interactive Subtle Action Label";
|
|
34
|
+
const { getByText } = render(React.createElement(ActionLabel, { variation: "interactiveSubtle" }, text));
|
|
35
|
+
expect(getStyleObject(getByText(text))).toMatchObject(Object.assign(Object.assign({}, defaultStyles), { color: tokens["color-interactive--subtle"] }));
|
|
36
|
+
});
|
|
37
|
+
it("still supports the deprecated subtle variation", () => {
|
|
38
|
+
const text = "Subtle Action Label";
|
|
39
|
+
const { getByText } = render(React.createElement(ActionLabel, { variation: "subtle" }, text));
|
|
40
|
+
expect(getStyleObject(getByText(text))).toMatchObject(Object.assign(Object.assign({}, defaultStyles), { color: tokens["color-interactive--subtle"] }));
|
|
41
|
+
});
|
|
42
|
+
it("renders an onPrimary variation", () => {
|
|
43
|
+
const text = "onPrimary Action Label";
|
|
44
|
+
const { getByText } = render(React.createElement(ActionLabel, { variation: "onPrimary" }, text));
|
|
45
|
+
expect(getStyleObject(getByText(text))).toMatchObject(Object.assign(Object.assign({}, defaultStyles), { color: tokens["color-surface"] }));
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe("when action label is disabled", () => {
|
|
49
|
+
it("renders text with disabled color, overriding variation", () => {
|
|
50
|
+
const text = "Disabled Action Label";
|
|
51
|
+
const { getByText } = render(React.createElement(ActionLabel, { disabled: true, variation: "destructive" }, text));
|
|
52
|
+
const styles = getStyleObject(getByText(text));
|
|
53
|
+
expect(styles).toMatchObject(Object.assign(Object.assign({}, defaultStyles), { color: tokens["color-disabled"] }));
|
|
54
|
+
expect(styles).not.toHaveProperty("color", tokens["color-destructive"]);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe("when action label is aligned", () => {
|
|
58
|
+
it("renders text with left alignment", () => {
|
|
59
|
+
const text = "Left Aligned Action Label";
|
|
60
|
+
const { getByText } = render(React.createElement(ActionLabel, { align: "start" }, text));
|
|
61
|
+
expect(getStyleObject(getByText(text))).toMatchObject(Object.assign(Object.assign({}, defaultStyles), { textAlign: "left" }));
|
|
62
|
+
});
|
|
63
|
+
it("renders text with right alignment", () => {
|
|
64
|
+
const text = "Right Aligned Action Label";
|
|
65
|
+
const { getByText } = render(React.createElement(ActionLabel, { align: "end" }, text));
|
|
66
|
+
expect(getStyleObject(getByText(text))).toMatchObject(Object.assign(Object.assign({}, defaultStyles), { textAlign: "right" }));
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
it("supports nested inline content", () => {
|
|
70
|
+
const { Text } = require("../Text");
|
|
71
|
+
const { getByText, toJSON } = render(React.createElement(ActionLabel, null,
|
|
72
|
+
"Before ",
|
|
73
|
+
React.createElement(Text, { variation: "interactive" }, "Inner"),
|
|
74
|
+
" After"));
|
|
75
|
+
expect(getByText("Inner")).toBeDefined();
|
|
76
|
+
expect(toJSON()).toMatchSnapshot();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
function getStyleObject(el) {
|
|
80
|
+
return el.props.style.reduce((mergedStyles, additionalStyles) => (Object.assign(Object.assign({}, mergedStyles), additionalStyles)), {});
|
|
81
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { ActivityIndicator } from "./index";
|
|
4
|
+
import { tokens } from "../utils/design";
|
|
5
|
+
const testId = "ActivityIndicator";
|
|
6
|
+
describe("ActivityIndicator", () => {
|
|
7
|
+
it("renders with the default color when no props are provided", () => {
|
|
8
|
+
const { getByTestId } = render(React.createElement(ActivityIndicator, null));
|
|
9
|
+
expect(getByTestId(testId).props.color).toBe(tokens["color-greyBlue"]);
|
|
10
|
+
});
|
|
11
|
+
it("renders with a custom color", () => {
|
|
12
|
+
const color = "red";
|
|
13
|
+
const { getByTestId } = render(React.createElement(ActivityIndicator, { color: color }));
|
|
14
|
+
expect(getByTestId(testId).props.color).toBe(color);
|
|
15
|
+
});
|
|
16
|
+
it("renders with large size", () => {
|
|
17
|
+
const size = "large";
|
|
18
|
+
const color = "red";
|
|
19
|
+
const { getByTestId } = render(React.createElement(ActivityIndicator, { color: color, size: size }));
|
|
20
|
+
expect(getByTestId(testId).props.size).toBe(size);
|
|
21
|
+
expect(getByTestId(testId).props.color).toBe(color);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { renderHook } from "@testing-library/react-native";
|
|
3
|
+
import { AtlantisContext, atlantisContextDefaultValues, useAtlantisContext, } from "./AtlantisContext";
|
|
4
|
+
const providerValues = {
|
|
5
|
+
dateFormat: "MM/DD/YYYY",
|
|
6
|
+
timeFormat: "hh:mm a",
|
|
7
|
+
timeZone: "America/Edmonton",
|
|
8
|
+
locale: "en",
|
|
9
|
+
isOnline: false,
|
|
10
|
+
onLogError: _ => {
|
|
11
|
+
return;
|
|
12
|
+
},
|
|
13
|
+
floatSeparators: { decimal: ".", group: "," },
|
|
14
|
+
currencySymbol: "€",
|
|
15
|
+
headerHeight: 50,
|
|
16
|
+
setHeaderHeight: _ => {
|
|
17
|
+
return;
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
describe("AtlantisContext", () => {
|
|
21
|
+
describe("No Provider", () => {
|
|
22
|
+
it("should get the default values", () => {
|
|
23
|
+
const { result } = renderHook(() => useAtlantisContext());
|
|
24
|
+
expect(result.current).toMatchObject(atlantisContextDefaultValues);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe("With Provider", () => {
|
|
28
|
+
it("should get the provider values", () => {
|
|
29
|
+
const { result } = renderHook(() => useAtlantisContext(), {
|
|
30
|
+
wrapper: ({ children }) => (React.createElement(AtlantisContext.Provider, { value: providerValues }, children)),
|
|
31
|
+
});
|
|
32
|
+
expect(result.current).toMatchObject(providerValues);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import React from "react";
|
|
11
|
+
import { act, renderHook } from "@testing-library/react-native";
|
|
12
|
+
import { darkTokens, iosTokens } from "@jobber/design";
|
|
13
|
+
import merge from "lodash/merge";
|
|
14
|
+
import { AtlantisThemeContextProvider, useAtlantisTheme, } from "./AtlantisThemeContext";
|
|
15
|
+
const expectedDarkTokens = merge({}, iosTokens, darkTokens);
|
|
16
|
+
const expectedLightTokens = iosTokens;
|
|
17
|
+
function Wrapper({ children, dangerouslyOverrideTheme, }) {
|
|
18
|
+
return (React.createElement(AtlantisThemeContextProvider, { dangerouslyOverrideTheme: dangerouslyOverrideTheme }, children));
|
|
19
|
+
}
|
|
20
|
+
function WrapperWithOverride({ children, dangerouslyOverrideTheme, }) {
|
|
21
|
+
return (React.createElement(Wrapper, null,
|
|
22
|
+
React.createElement(AtlantisThemeContextProvider, { dangerouslyOverrideTheme: dangerouslyOverrideTheme }, children)));
|
|
23
|
+
}
|
|
24
|
+
describe("ThemeContext", () => {
|
|
25
|
+
it("defaults to the light theme", () => {
|
|
26
|
+
const { result } = renderHook(useAtlantisTheme, {
|
|
27
|
+
wrapper: (props) => (React.createElement(Wrapper, Object.assign({}, props))),
|
|
28
|
+
});
|
|
29
|
+
expect(result.current.theme).toBe("light");
|
|
30
|
+
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
31
|
+
});
|
|
32
|
+
it("updates the theme and tokens", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
|
+
const { result } = renderHook(useAtlantisTheme, {
|
|
34
|
+
wrapper: (props) => (React.createElement(Wrapper, Object.assign({}, props))),
|
|
35
|
+
});
|
|
36
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () { return result.current.setTheme("dark"); }));
|
|
37
|
+
expect(result.current.theme).toBe("dark");
|
|
38
|
+
expect(result.current.tokens).toEqual(expectedDarkTokens);
|
|
39
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () { return result.current.setTheme("light"); }));
|
|
40
|
+
expect(result.current.theme).toBe("light");
|
|
41
|
+
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
42
|
+
}));
|
|
43
|
+
describe("when theme is forced for provider", () => {
|
|
44
|
+
it("ignores updates to the global theme", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
45
|
+
const { result } = renderHook(useAtlantisTheme, {
|
|
46
|
+
wrapper: (props) => (React.createElement(WrapperWithOverride, Object.assign({}, props, { dangerouslyOverrideTheme: "light" }))),
|
|
47
|
+
});
|
|
48
|
+
// Update the global theme
|
|
49
|
+
yield act(() => __awaiter(void 0, void 0, void 0, function* () { return result.current.setTheme("dark"); }));
|
|
50
|
+
// This hook shouldn't be affected by it because it's set to the light theme
|
|
51
|
+
expect(result.current.theme).toBe("light");
|
|
52
|
+
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
53
|
+
}));
|
|
54
|
+
it.each([
|
|
55
|
+
{ defaultTheme: "light", expectedTokens: expectedLightTokens },
|
|
56
|
+
{ defaultTheme: "dark", expectedTokens: expectedDarkTokens },
|
|
57
|
+
])("provides the dangerouslyOverrideTheme $defaultTheme tokens", ({ defaultTheme, expectedTokens }) => {
|
|
58
|
+
const { result } = renderHook(useAtlantisTheme, {
|
|
59
|
+
wrapper: (props) => (React.createElement(WrapperWithOverride, Object.assign({}, props, { dangerouslyOverrideTheme: defaultTheme }))),
|
|
60
|
+
});
|
|
61
|
+
expect(result.current.theme).toBe(defaultTheme);
|
|
62
|
+
expect(result.current.tokens).toEqual(expectedTokens);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { renderHook } from "@testing-library/react-native";
|
|
3
|
+
import { buildThemedStyles } from "./buildThemedStyles";
|
|
4
|
+
import { AtlantisThemeContextProvider } from "./AtlantisThemeContext";
|
|
5
|
+
describe("buildThemedStyles", () => {
|
|
6
|
+
const wrapper = ({ children }) => (React.createElement(AtlantisThemeContextProvider, null, children));
|
|
7
|
+
it("creates styles using theme tokens", () => {
|
|
8
|
+
const useTestStyles = buildThemedStyles(tokens => ({
|
|
9
|
+
container: {
|
|
10
|
+
backgroundColor: tokens["color-surface"],
|
|
11
|
+
padding: tokens["space-base"],
|
|
12
|
+
},
|
|
13
|
+
}));
|
|
14
|
+
const { result } = renderHook(() => useTestStyles(), { wrapper });
|
|
15
|
+
expect(result.current).toEqual({
|
|
16
|
+
container: {
|
|
17
|
+
backgroundColor: expect.any(String),
|
|
18
|
+
padding: expect.any(Number),
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
it("memoizes styles and only updates when tokens change", () => {
|
|
23
|
+
const styleFactory = jest.fn(tokens => ({
|
|
24
|
+
container: {
|
|
25
|
+
backgroundColor: tokens["color-surface"],
|
|
26
|
+
},
|
|
27
|
+
}));
|
|
28
|
+
const useTestStyles = buildThemedStyles(styleFactory);
|
|
29
|
+
const { result, rerender } = renderHook(() => useTestStyles(), { wrapper });
|
|
30
|
+
// Initial render should call styleFactory
|
|
31
|
+
expect(styleFactory).toHaveBeenCalledTimes(1);
|
|
32
|
+
const initialResult = result.current;
|
|
33
|
+
// Rerender without token changes should not call styleFactory again
|
|
34
|
+
rerender({ wrapper });
|
|
35
|
+
expect(styleFactory).toHaveBeenCalledTimes(1);
|
|
36
|
+
expect(result.current).toBe(initialResult);
|
|
37
|
+
});
|
|
38
|
+
it("handles empty style objects", () => {
|
|
39
|
+
const useTestStyles = buildThemedStyles(() => ({}));
|
|
40
|
+
const { result } = renderHook(() => useTestStyles(), { wrapper });
|
|
41
|
+
expect(result.current).toEqual({});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { fireEvent, render } from "@testing-library/react-native";
|
|
3
|
+
import { Linking } from "react-native";
|
|
4
|
+
import { copyTextToClipboard } from "./clipboard";
|
|
5
|
+
import { AutoLink } from "./AutoLink";
|
|
6
|
+
const mockOpenUrl = jest.spyOn(Linking, "openURL").mockImplementation();
|
|
7
|
+
jest.mock("./clipboard", () => {
|
|
8
|
+
return {
|
|
9
|
+
copyTextToClipboard: jest.fn(),
|
|
10
|
+
};
|
|
11
|
+
});
|
|
12
|
+
describe("AutoLink", () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
mockOpenUrl.mockClear();
|
|
15
|
+
});
|
|
16
|
+
describe("Urls", () => {
|
|
17
|
+
it("should find and link url amongst other text", () => {
|
|
18
|
+
const linkText = "getjobber.com";
|
|
19
|
+
const nonLinkText = "The best website is ";
|
|
20
|
+
const { getByText } = render(React.createElement(AutoLink, null, `${nonLinkText}${linkText}`));
|
|
21
|
+
fireEvent.press(getByText(linkText));
|
|
22
|
+
expect(mockOpenUrl).toHaveBeenCalledWith(`http://${linkText}`);
|
|
23
|
+
});
|
|
24
|
+
it("should find and link a url without other text around it", () => {
|
|
25
|
+
const linkText = "getjobber.com";
|
|
26
|
+
const { getByText } = render(React.createElement(AutoLink, null, linkText));
|
|
27
|
+
fireEvent.press(getByText(linkText));
|
|
28
|
+
expect(mockOpenUrl).toHaveBeenCalled();
|
|
29
|
+
});
|
|
30
|
+
it("should copy link onLongPress", () => {
|
|
31
|
+
const linkText = "getjobber.com";
|
|
32
|
+
const nonLinkText = "The best website is ";
|
|
33
|
+
const { getByText } = render(React.createElement(AutoLink, null, `${nonLinkText}${linkText}`));
|
|
34
|
+
fireEvent(getByText(linkText), "onLongPress");
|
|
35
|
+
const expectedToastConfig = {
|
|
36
|
+
message: "URL copied",
|
|
37
|
+
bottomTabsVisible: true,
|
|
38
|
+
};
|
|
39
|
+
expect(copyTextToClipboard).toHaveBeenCalledWith(`http://${linkText}`, expectedToastConfig);
|
|
40
|
+
expect(mockOpenUrl).not.toHaveBeenCalled();
|
|
41
|
+
});
|
|
42
|
+
it("should skip linking url if linkUrls is false", () => {
|
|
43
|
+
const linkText = "getjobber.com";
|
|
44
|
+
const nonLinkText = "The best website is ";
|
|
45
|
+
const { getByText } = render(React.createElement(AutoLink, { urls: false }, `${nonLinkText}${linkText}`));
|
|
46
|
+
fireEvent.press(getByText(RegExp(linkText, "i")));
|
|
47
|
+
expect(mockOpenUrl).not.toHaveBeenCalled();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe("Emails", () => {
|
|
51
|
+
it("should find and link emails in text", () => {
|
|
52
|
+
const nonEmailText = "The best email is ";
|
|
53
|
+
const emailText = "test@example.com";
|
|
54
|
+
const { getByText } = render(React.createElement(AutoLink, null, `${nonEmailText}${emailText}`));
|
|
55
|
+
fireEvent.press(getByText(emailText));
|
|
56
|
+
expect(mockOpenUrl).toHaveBeenCalledWith(`mailto:${encodeURIComponent(emailText)}`);
|
|
57
|
+
});
|
|
58
|
+
it("should find and link an email without other text around it", () => {
|
|
59
|
+
const emailText = "test@example.com";
|
|
60
|
+
const { getByText } = render(React.createElement(AutoLink, null, emailText));
|
|
61
|
+
fireEvent.press(getByText(emailText));
|
|
62
|
+
expect(mockOpenUrl).toHaveBeenCalledWith(`mailto:${encodeURIComponent(emailText)}`);
|
|
63
|
+
});
|
|
64
|
+
it("should copy email onLongPress", () => {
|
|
65
|
+
const nonEmailText = "The best email is ";
|
|
66
|
+
const emailText = "test@example.com";
|
|
67
|
+
const { getByText } = render(React.createElement(AutoLink, null, `${nonEmailText}${emailText}`));
|
|
68
|
+
fireEvent(getByText(emailText), "onLongPress");
|
|
69
|
+
const expectedToastConfig = {
|
|
70
|
+
message: "Email copied",
|
|
71
|
+
bottomTabsVisible: true,
|
|
72
|
+
};
|
|
73
|
+
expect(copyTextToClipboard).toHaveBeenCalledWith(emailText, expectedToastConfig);
|
|
74
|
+
expect(mockOpenUrl).not.toHaveBeenCalled();
|
|
75
|
+
});
|
|
76
|
+
it("should skip linking email if linkEmails is false", () => {
|
|
77
|
+
const nonEmailText = "The best email is ";
|
|
78
|
+
const emailText = "test@example.com";
|
|
79
|
+
const { getByText } = render(React.createElement(AutoLink, { email: false }, `${nonEmailText}${emailText}`));
|
|
80
|
+
fireEvent.press(getByText(RegExp(emailText, "i")));
|
|
81
|
+
expect(mockOpenUrl).not.toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe("Phone Numbers", () => {
|
|
85
|
+
it("should find and link phone numbers in text", () => {
|
|
86
|
+
const nonPhoneText = "The best phone number is ";
|
|
87
|
+
const phoneText = "902-555-5555";
|
|
88
|
+
const { getByText } = render(React.createElement(AutoLink, null, `${nonPhoneText}${phoneText}`));
|
|
89
|
+
const expectedPhone = phoneText.replace(/-/g, "");
|
|
90
|
+
fireEvent.press(getByText(phoneText));
|
|
91
|
+
expect(mockOpenUrl).toHaveBeenCalledWith(`tel:${expectedPhone}`);
|
|
92
|
+
});
|
|
93
|
+
it("should find and link a phone number without other text around it", () => {
|
|
94
|
+
const phoneText = "902-555-5555";
|
|
95
|
+
const { getByText } = render(React.createElement(AutoLink, null, phoneText));
|
|
96
|
+
const expectedPhone = phoneText.replace(/-/g, "");
|
|
97
|
+
fireEvent.press(getByText(phoneText));
|
|
98
|
+
expect(mockOpenUrl).toHaveBeenCalledWith(`tel:${expectedPhone}`);
|
|
99
|
+
});
|
|
100
|
+
it("should copy phone number onLongPress", () => {
|
|
101
|
+
const nonPhoneText = "The best phone number is ";
|
|
102
|
+
const phoneText = "902-555-5555";
|
|
103
|
+
const { getByText } = render(React.createElement(AutoLink, null, `${nonPhoneText}${phoneText}`));
|
|
104
|
+
fireEvent(getByText(phoneText), "onLongPress");
|
|
105
|
+
const expectedToastConfig = {
|
|
106
|
+
message: "Phone number copied",
|
|
107
|
+
bottomTabsVisible: true,
|
|
108
|
+
};
|
|
109
|
+
expect(copyTextToClipboard).toHaveBeenCalledWith(phoneText.replace(/-/g, ""), expectedToastConfig);
|
|
110
|
+
expect(mockOpenUrl).not.toHaveBeenCalled();
|
|
111
|
+
});
|
|
112
|
+
it("should skip linking phone number if linkPhones is false", () => {
|
|
113
|
+
const nonPhoneText = "The best phone number is ";
|
|
114
|
+
const phoneText = "902-555-5555";
|
|
115
|
+
const { getByText } = render(React.createElement(AutoLink, { phone: false }, `${nonPhoneText}${phoneText}`));
|
|
116
|
+
fireEvent.press(getByText(RegExp(phoneText, "i")));
|
|
117
|
+
expect(mockOpenUrl).not.toHaveBeenCalled();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
it("should properly link a combination of urls, emails and phone numbers", () => {
|
|
121
|
+
const phoneText = "902-555-5555";
|
|
122
|
+
const emailText = "test@example.com";
|
|
123
|
+
const urlText = "getjobber.com";
|
|
124
|
+
const { getByText } = render(React.createElement(AutoLink, null, `${phoneText} ${emailText} ${urlText}`));
|
|
125
|
+
const expectedPhone = phoneText.replace(/-/g, "");
|
|
126
|
+
fireEvent.press(getByText(phoneText));
|
|
127
|
+
expect(mockOpenUrl).toHaveBeenCalledWith(`tel:${expectedPhone}`);
|
|
128
|
+
fireEvent.press(getByText(emailText));
|
|
129
|
+
expect(mockOpenUrl).toHaveBeenCalledWith(`mailto:${encodeURIComponent(emailText)}`);
|
|
130
|
+
fireEvent.press(getByText(urlText));
|
|
131
|
+
expect(mockOpenUrl).toHaveBeenCalledWith(`http://${urlText}`);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { fireEvent, render } from "@testing-library/react-native";
|
|
3
|
+
import { Link } from "./Link";
|
|
4
|
+
describe("Link", () => {
|
|
5
|
+
const textLink = "getjobber.com";
|
|
6
|
+
const mockPress = jest.fn();
|
|
7
|
+
it("should call the onPress callback", () => {
|
|
8
|
+
const { getAllByText } = render(React.createElement(Link, { onPress: mockPress }, textLink));
|
|
9
|
+
fireEvent.press(getAllByText("getjobber.com")[0]);
|
|
10
|
+
expect(mockPress).toHaveBeenCalled();
|
|
11
|
+
});
|
|
12
|
+
it("should call the longPress callback", () => {
|
|
13
|
+
const mockLongPress = jest.fn();
|
|
14
|
+
const { getAllByText } = render(React.createElement(Link, { onPress: mockPress, onLongPress: mockLongPress }, textLink));
|
|
15
|
+
fireEvent(getAllByText(textLink)[0], "onLongPress");
|
|
16
|
+
expect(mockLongPress).toHaveBeenCalled();
|
|
17
|
+
});
|
|
18
|
+
});
|