@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
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {describe, expect, it, mock} from "bun:test";
|
|
2
|
+
import {render} from "@testing-library/react-native";
|
|
3
|
+
import {useEffect} from "react";
|
|
4
|
+
import {Text} from "react-native";
|
|
5
|
+
|
|
6
|
+
import type {TableContextType} from "../Common";
|
|
7
|
+
import {renderWithTheme} from "../test-utils";
|
|
8
|
+
import {TableContextProvider, useTableContext} from "./tableContext";
|
|
9
|
+
|
|
10
|
+
const captureContext = (): {
|
|
11
|
+
getContext: () => TableContextType;
|
|
12
|
+
Consumer: () => React.ReactElement;
|
|
13
|
+
} => {
|
|
14
|
+
let captured: TableContextType | undefined;
|
|
15
|
+
const Consumer = () => {
|
|
16
|
+
captured = useTableContext();
|
|
17
|
+
return <Text>ok</Text>;
|
|
18
|
+
};
|
|
19
|
+
const getContext = (): TableContextType => {
|
|
20
|
+
if (!captured) {
|
|
21
|
+
throw new Error("Context was not captured");
|
|
22
|
+
}
|
|
23
|
+
return captured;
|
|
24
|
+
};
|
|
25
|
+
return {Consumer, getContext};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
describe("tableContext", () => {
|
|
29
|
+
it("exposes default values from useTableContext when no provider is present", () => {
|
|
30
|
+
const {getContext, Consumer} = captureContext();
|
|
31
|
+
renderWithTheme(<Consumer />);
|
|
32
|
+
const ctx = getContext();
|
|
33
|
+
expect(ctx.columns).toEqual([]);
|
|
34
|
+
expect(ctx.stickyHeader).toBe(true);
|
|
35
|
+
expect(ctx.alternateRowBackground).toBe(true);
|
|
36
|
+
expect(ctx.borderStyle).toBe("sm");
|
|
37
|
+
expect(ctx.hasDrawerContents).toBe(false);
|
|
38
|
+
expect(typeof ctx.setSortColumn).toBe("function");
|
|
39
|
+
// Invoke default setSortColumn to cover the no-op default
|
|
40
|
+
expect(() => ctx.setSortColumn({column: 0, direction: "asc"})).not.toThrow();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("passes provided values through the provider", () => {
|
|
44
|
+
const setSortColumn = mock(() => {});
|
|
45
|
+
const {getContext, Consumer} = captureContext();
|
|
46
|
+
render(
|
|
47
|
+
<TableContextProvider
|
|
48
|
+
alternateRowBackground={false}
|
|
49
|
+
borderStyle="lg"
|
|
50
|
+
columns={[100, 200]}
|
|
51
|
+
hasDrawerContents
|
|
52
|
+
page={3}
|
|
53
|
+
setSortColumn={setSortColumn}
|
|
54
|
+
sortColumn={{column: 1, direction: "desc"}}
|
|
55
|
+
stickyHeader={false}
|
|
56
|
+
>
|
|
57
|
+
<Consumer />
|
|
58
|
+
</TableContextProvider>
|
|
59
|
+
);
|
|
60
|
+
const ctx = getContext();
|
|
61
|
+
expect(ctx.columns).toEqual([100, 200]);
|
|
62
|
+
expect(ctx.hasDrawerContents).toBe(true);
|
|
63
|
+
expect(ctx.stickyHeader).toBe(false);
|
|
64
|
+
expect(ctx.alternateRowBackground).toBe(false);
|
|
65
|
+
expect(ctx.borderStyle).toBe("lg");
|
|
66
|
+
expect(ctx.sortColumn).toEqual({column: 1, direction: "desc"});
|
|
67
|
+
ctx.setSortColumn({column: 0, direction: "asc"});
|
|
68
|
+
expect(setSortColumn).toHaveBeenCalled();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("useEffect-based consumer can call setSortColumn without errors", () => {
|
|
72
|
+
const setSortColumn = mock(() => {});
|
|
73
|
+
const Consumer = () => {
|
|
74
|
+
const {setSortColumn: setter} = useTableContext();
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
setter({column: 2, direction: "desc"});
|
|
77
|
+
}, [setter]);
|
|
78
|
+
return <Text>ok</Text>;
|
|
79
|
+
};
|
|
80
|
+
render(
|
|
81
|
+
<TableContextProvider
|
|
82
|
+
alternateRowBackground
|
|
83
|
+
borderStyle="sm"
|
|
84
|
+
columns={[100]}
|
|
85
|
+
hasDrawerContents={false}
|
|
86
|
+
page={1}
|
|
87
|
+
setSortColumn={setSortColumn}
|
|
88
|
+
sortColumn={undefined}
|
|
89
|
+
stickyHeader
|
|
90
|
+
>
|
|
91
|
+
<Consumer />
|
|
92
|
+
</TableContextProvider>
|
|
93
|
+
);
|
|
94
|
+
expect(setSortColumn).toHaveBeenCalledWith({column: 2, direction: "desc"});
|
|
95
|
+
});
|
|
96
|
+
});
|
package/src/test-utils.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import type React from "react";
|
|
|
5
5
|
import {ThemeProvider} from "./Theme";
|
|
6
6
|
|
|
7
7
|
export const renderWithTheme = (ui: React.ReactElement) => {
|
|
8
|
-
return render(
|
|
8
|
+
return render(ui, {wrapper: ThemeProvider});
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export const createCommonMocks = () => ({
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {describe, expect, it, mock} from "bun:test";
|
|
2
|
+
import {renderHook} from "@testing-library/react-native";
|
|
3
|
+
|
|
4
|
+
import {detectLocale, useConsentForms} from "./useConsentForms";
|
|
5
|
+
|
|
6
|
+
mock.module("expo-localization", () => ({
|
|
7
|
+
getLocales: () => [{languageTag: "es-ES"}],
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe("detectLocale", () => {
|
|
11
|
+
it("returns locale from expo-localization in native test env", () => {
|
|
12
|
+
const originalNavigator = (globalThis as any).navigator;
|
|
13
|
+
(globalThis as any).navigator = undefined;
|
|
14
|
+
const locale = detectLocale();
|
|
15
|
+
(globalThis as any).navigator = originalNavigator;
|
|
16
|
+
expect(typeof locale).toBe("string");
|
|
17
|
+
expect(locale.length).toBeGreaterThan(0);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("falls back to en when expo-localization throws and no navigator", () => {
|
|
21
|
+
const originalNavigator = (globalThis as any).navigator;
|
|
22
|
+
(globalThis as any).navigator = undefined;
|
|
23
|
+
mock.module("expo-localization", () => ({
|
|
24
|
+
getLocales: () => {
|
|
25
|
+
throw new Error("not available");
|
|
26
|
+
},
|
|
27
|
+
}));
|
|
28
|
+
const locale = detectLocale();
|
|
29
|
+
(globalThis as any).navigator = originalNavigator;
|
|
30
|
+
// Reset mock
|
|
31
|
+
mock.module("expo-localization", () => ({
|
|
32
|
+
getLocales: () => [{languageTag: "es-ES"}],
|
|
33
|
+
}));
|
|
34
|
+
expect(locale).toBe("en");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("returns navigator.language when available", () => {
|
|
38
|
+
const originalNavigator = (globalThis as any).navigator;
|
|
39
|
+
(globalThis as any).navigator = {language: "fr-FR"};
|
|
40
|
+
const locale = detectLocale();
|
|
41
|
+
(globalThis as any).navigator = originalNavigator;
|
|
42
|
+
expect(locale).toBe("fr-FR");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("useConsentForms", () => {
|
|
47
|
+
const buildApi = (queryResult: {data?: unknown; error?: unknown; isLoading?: boolean}) => {
|
|
48
|
+
const refetch = mock(() => {});
|
|
49
|
+
const useGetPendingConsentsQuery = mock(() => ({
|
|
50
|
+
data: queryResult.data,
|
|
51
|
+
error: queryResult.error,
|
|
52
|
+
isLoading: queryResult.isLoading ?? false,
|
|
53
|
+
refetch,
|
|
54
|
+
}));
|
|
55
|
+
const api = {
|
|
56
|
+
enhanceEndpoints: mock(() => ({
|
|
57
|
+
injectEndpoints: mock((opts: any) => {
|
|
58
|
+
// Call endpoint builder to exercise provides/onQueryStarted
|
|
59
|
+
const build = {
|
|
60
|
+
query: mock((def: any) => {
|
|
61
|
+
// Call onQueryStarted with a successful result
|
|
62
|
+
if (def.onQueryStarted) {
|
|
63
|
+
void def.onQueryStarted(undefined, {
|
|
64
|
+
queryFulfilled: Promise.resolve({data: [{id: "1"}]}),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// Also exercise the URL builder
|
|
68
|
+
def.query();
|
|
69
|
+
return "pending-query";
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
opts.endpoints(build);
|
|
73
|
+
return {useGetPendingConsentsQuery};
|
|
74
|
+
}),
|
|
75
|
+
})),
|
|
76
|
+
};
|
|
77
|
+
return {api, refetch};
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
it("returns an array of forms when response is an array", () => {
|
|
81
|
+
const {api} = buildApi({data: [{id: "1", title: "Form 1"} as any]});
|
|
82
|
+
const {result} = renderHook(() => useConsentForms(api as any));
|
|
83
|
+
expect(result.current.forms).toBeDefined();
|
|
84
|
+
expect(Array.isArray(result.current.forms)).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("unwraps .data property when response is object shape", () => {
|
|
88
|
+
const {api} = buildApi({data: {data: [{id: "2"} as any]}});
|
|
89
|
+
const {result} = renderHook(() => useConsentForms(api as any, "/api"));
|
|
90
|
+
expect(Array.isArray(result.current.forms)).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("returns empty array when no data is present", () => {
|
|
94
|
+
const {api} = buildApi({data: undefined, isLoading: true});
|
|
95
|
+
const {result} = renderHook(() => useConsentForms(api as any));
|
|
96
|
+
expect(result.current.forms).toEqual([]);
|
|
97
|
+
expect(result.current.isLoading).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("calls onQueryStarted with failing query", () => {
|
|
101
|
+
const refetch = mock(() => {});
|
|
102
|
+
const api = {
|
|
103
|
+
enhanceEndpoints: mock(() => ({
|
|
104
|
+
injectEndpoints: mock((opts: any) => {
|
|
105
|
+
const build = {
|
|
106
|
+
query: mock((def: any) => {
|
|
107
|
+
if (def.onQueryStarted) {
|
|
108
|
+
void def.onQueryStarted(undefined, {
|
|
109
|
+
queryFulfilled: Promise.reject(new Error("failed")),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return "q";
|
|
113
|
+
}),
|
|
114
|
+
};
|
|
115
|
+
opts.endpoints(build);
|
|
116
|
+
return {
|
|
117
|
+
useGetPendingConsentsQuery: () => ({
|
|
118
|
+
data: undefined,
|
|
119
|
+
error: "error",
|
|
120
|
+
isLoading: false,
|
|
121
|
+
refetch,
|
|
122
|
+
}),
|
|
123
|
+
};
|
|
124
|
+
}),
|
|
125
|
+
})),
|
|
126
|
+
};
|
|
127
|
+
const {result} = renderHook(() => useConsentForms(api as any));
|
|
128
|
+
expect(result.current.error).toBe("error");
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {describe, expect, it, mock} from "bun:test";
|
|
2
|
+
import {renderHook} from "@testing-library/react-native";
|
|
3
|
+
|
|
4
|
+
import {useSubmitConsent} from "./useSubmitConsent";
|
|
5
|
+
|
|
6
|
+
describe("useSubmitConsent", () => {
|
|
7
|
+
const buildApi = () => {
|
|
8
|
+
const unwrap = mock(async () => ({success: true}));
|
|
9
|
+
const submitMutation = mock(() => ({unwrap}));
|
|
10
|
+
const useSubmitConsentResponseMutation = mock(() => [
|
|
11
|
+
submitMutation,
|
|
12
|
+
{error: undefined, isLoading: false},
|
|
13
|
+
]);
|
|
14
|
+
const api = {
|
|
15
|
+
enhanceEndpoints: mock(() => ({
|
|
16
|
+
injectEndpoints: mock((opts: any) => {
|
|
17
|
+
const build = {
|
|
18
|
+
mutation: mock((def: any) => {
|
|
19
|
+
// Exercise the query builder
|
|
20
|
+
const result = def.query({
|
|
21
|
+
agreed: true,
|
|
22
|
+
consentFormId: "f1",
|
|
23
|
+
locale: "en",
|
|
24
|
+
});
|
|
25
|
+
expect(result.method).toBe("POST");
|
|
26
|
+
expect(result.url).toContain("/consents/respond");
|
|
27
|
+
return "submit-mutation";
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
opts.endpoints(build);
|
|
31
|
+
return {useSubmitConsentResponseMutation};
|
|
32
|
+
}),
|
|
33
|
+
})),
|
|
34
|
+
};
|
|
35
|
+
return {api, submitMutation, unwrap};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
it("returns submit function and state", () => {
|
|
39
|
+
const {api} = buildApi();
|
|
40
|
+
const {result} = renderHook(() => useSubmitConsent(api as any));
|
|
41
|
+
expect(typeof result.current.submit).toBe("function");
|
|
42
|
+
expect(result.current.isSubmitting).toBe(false);
|
|
43
|
+
expect(result.current.error).toBeUndefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("calls submit mutation when submit is invoked", async () => {
|
|
47
|
+
const {api, submitMutation, unwrap} = buildApi();
|
|
48
|
+
const {result} = renderHook(() => useSubmitConsent(api as any, "/api"));
|
|
49
|
+
const response = await result.current.submit({
|
|
50
|
+
agreed: true,
|
|
51
|
+
consentFormId: "f1",
|
|
52
|
+
locale: "en",
|
|
53
|
+
});
|
|
54
|
+
expect(submitMutation).toHaveBeenCalled();
|
|
55
|
+
expect(unwrap).toHaveBeenCalled();
|
|
56
|
+
expect(response).toEqual({success: true});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("uses empty baseUrl when none provided", () => {
|
|
60
|
+
const {api} = buildApi();
|
|
61
|
+
const {result} = renderHook(() => useSubmitConsent(api as any));
|
|
62
|
+
expect(result.current.submit).toBeDefined();
|
|
63
|
+
});
|
|
64
|
+
});
|