Skip to content

Commit eedec1f

Browse files
CopilotCopilot
andcommitted
Add test files for all 9 Phase 23 SDUI hooks
Create comprehensive test suites for: - useRecordDetails (7 tests) - useRecordHighlights (7 tests) - useRecordActivity (7 tests) - useRecordChatter (7 tests) - useRecordPath (8 tests) - useRecordRelatedList (6 tests) - useRecordReview (8 tests) - useInterfacePageConfig (7 tests) - useBlankPageLayout (8 tests) Each test file covers initial state, setting data, key operations, computed values, and edge cases following existing test conventions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 6106960 commit eedec1f

9 files changed

Lines changed: 1061 additions & 0 deletions
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/**
2+
* Tests for useBlankPageLayout – validates blank page layout management,
3+
* item addition, removal, reordering, updates, and template selection.
4+
*/
5+
import { renderHook, act } from "@testing-library/react-native";
6+
import { useBlankPageLayout, LayoutItem } from "~/hooks/useBlankPageLayout";
7+
8+
describe("useBlankPageLayout", () => {
9+
it("returns default state initially", () => {
10+
const { result } = renderHook(() => useBlankPageLayout());
11+
12+
expect(result.current.items).toEqual([]);
13+
expect(result.current.template).toBe("blank");
14+
});
15+
16+
it("sets layout items", () => {
17+
const { result } = renderHook(() => useBlankPageLayout());
18+
19+
const items: LayoutItem[] = [
20+
{ id: "i1", type: "text", config: { content: "Hello" }, order: 0 },
21+
{ id: "i2", type: "image", config: { src: "img.png" }, order: 1 },
22+
];
23+
24+
act(() => {
25+
result.current.setItems(items);
26+
});
27+
28+
expect(result.current.items).toHaveLength(2);
29+
});
30+
31+
it("adds an item sorted by order", () => {
32+
const { result } = renderHook(() => useBlankPageLayout());
33+
34+
act(() => {
35+
result.current.addItem({ id: "i2", type: "image", config: {}, order: 2 });
36+
});
37+
38+
act(() => {
39+
result.current.addItem({ id: "i1", type: "text", config: {}, order: 1 });
40+
});
41+
42+
expect(result.current.items).toHaveLength(2);
43+
expect(result.current.items[0].id).toBe("i1");
44+
expect(result.current.items[1].id).toBe("i2");
45+
});
46+
47+
it("removes an item by id", () => {
48+
const { result } = renderHook(() => useBlankPageLayout());
49+
50+
act(() => {
51+
result.current.setItems([
52+
{ id: "i1", type: "text", config: {}, order: 0 },
53+
{ id: "i2", type: "image", config: {}, order: 1 },
54+
]);
55+
});
56+
57+
act(() => {
58+
result.current.removeItem("i1");
59+
});
60+
61+
expect(result.current.items).toHaveLength(1);
62+
expect(result.current.items[0].id).toBe("i2");
63+
});
64+
65+
it("moves an item to a new order", () => {
66+
const { result } = renderHook(() => useBlankPageLayout());
67+
68+
act(() => {
69+
result.current.setItems([
70+
{ id: "i1", type: "text", config: {}, order: 0 },
71+
{ id: "i2", type: "image", config: {}, order: 1 },
72+
{ id: "i3", type: "video", config: {}, order: 2 },
73+
]);
74+
});
75+
76+
act(() => {
77+
result.current.moveItem("i1", 5);
78+
});
79+
80+
expect(result.current.items[0].id).toBe("i2");
81+
expect(result.current.items[2].id).toBe("i1");
82+
expect(result.current.items[2].order).toBe(5);
83+
});
84+
85+
it("updates an item's configuration", () => {
86+
const { result } = renderHook(() => useBlankPageLayout());
87+
88+
act(() => {
89+
result.current.setItems([
90+
{ id: "i1", type: "text", config: { content: "Hello" }, order: 0 },
91+
]);
92+
});
93+
94+
act(() => {
95+
result.current.updateItem("i1", { config: { content: "Updated" } });
96+
});
97+
98+
expect(result.current.items[0].config).toEqual({ content: "Updated" });
99+
});
100+
101+
it("sets the template type", () => {
102+
const { result } = renderHook(() => useBlankPageLayout());
103+
104+
act(() => {
105+
result.current.setTemplate("dashboard");
106+
});
107+
108+
expect(result.current.template).toBe("dashboard");
109+
});
110+
111+
it("changes template without affecting items", () => {
112+
const { result } = renderHook(() => useBlankPageLayout());
113+
114+
act(() => {
115+
result.current.addItem({ id: "i1", type: "text", config: {}, order: 0 });
116+
});
117+
118+
act(() => {
119+
result.current.setTemplate("form");
120+
});
121+
122+
expect(result.current.template).toBe("form");
123+
expect(result.current.items).toHaveLength(1);
124+
});
125+
});
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* Tests for useInterfacePageConfig – validates page configuration management,
3+
* block addition, removal, reordering, and updates.
4+
*/
5+
import { renderHook, act } from "@testing-library/react-native";
6+
import { useInterfacePageConfig, PageBlock } from "~/hooks/useInterfacePageConfig";
7+
8+
describe("useInterfacePageConfig", () => {
9+
it("returns default empty state initially", () => {
10+
const { result } = renderHook(() => useInterfacePageConfig());
11+
12+
expect(result.current.config).toEqual({ id: "", title: "", blocks: [] });
13+
expect(result.current.blocks).toEqual([]);
14+
});
15+
16+
it("sets full page configuration", () => {
17+
const { result } = renderHook(() => useInterfacePageConfig());
18+
19+
act(() => {
20+
result.current.setConfig({
21+
id: "p1",
22+
title: "Dashboard",
23+
description: "Main dashboard",
24+
blocks: [{ id: "b1", type: "chart", config: {}, order: 0 }],
25+
});
26+
});
27+
28+
expect(result.current.config.title).toBe("Dashboard");
29+
expect(result.current.blocks).toHaveLength(1);
30+
});
31+
32+
it("adds a block sorted by order", () => {
33+
const { result } = renderHook(() => useInterfacePageConfig());
34+
35+
act(() => {
36+
result.current.setConfig({ id: "p1", title: "Page", blocks: [] });
37+
});
38+
39+
act(() => {
40+
result.current.addBlock({ id: "b2", type: "table", config: {}, order: 2 });
41+
});
42+
43+
act(() => {
44+
result.current.addBlock({ id: "b1", type: "chart", config: {}, order: 1 });
45+
});
46+
47+
expect(result.current.blocks).toHaveLength(2);
48+
expect(result.current.blocks[0].id).toBe("b1");
49+
expect(result.current.blocks[1].id).toBe("b2");
50+
});
51+
52+
it("removes a block by id", () => {
53+
const { result } = renderHook(() => useInterfacePageConfig());
54+
55+
act(() => {
56+
result.current.setConfig({
57+
id: "p1",
58+
title: "Page",
59+
blocks: [
60+
{ id: "b1", type: "chart", config: {}, order: 0 },
61+
{ id: "b2", type: "table", config: {}, order: 1 },
62+
],
63+
});
64+
});
65+
66+
act(() => {
67+
result.current.removeBlock("b1");
68+
});
69+
70+
expect(result.current.blocks).toHaveLength(1);
71+
expect(result.current.blocks[0].id).toBe("b2");
72+
});
73+
74+
it("moves a block to a new order", () => {
75+
const { result } = renderHook(() => useInterfacePageConfig());
76+
77+
act(() => {
78+
result.current.setConfig({
79+
id: "p1",
80+
title: "Page",
81+
blocks: [
82+
{ id: "b1", type: "chart", config: {}, order: 0 },
83+
{ id: "b2", type: "table", config: {}, order: 1 },
84+
{ id: "b3", type: "text", config: {}, order: 2 },
85+
],
86+
});
87+
});
88+
89+
act(() => {
90+
result.current.moveBlock("b1", 5);
91+
});
92+
93+
expect(result.current.blocks[0].id).toBe("b2");
94+
expect(result.current.blocks[2].id).toBe("b1");
95+
expect(result.current.blocks[2].order).toBe(5);
96+
});
97+
98+
it("updates a block's configuration", () => {
99+
const { result } = renderHook(() => useInterfacePageConfig());
100+
101+
act(() => {
102+
result.current.setConfig({
103+
id: "p1",
104+
title: "Page",
105+
blocks: [{ id: "b1", type: "chart", config: { color: "blue" }, order: 0 }],
106+
});
107+
});
108+
109+
act(() => {
110+
result.current.updateBlock("b1", { config: { color: "red" } });
111+
});
112+
113+
expect(result.current.blocks[0].config).toEqual({ color: "red" });
114+
});
115+
116+
it("updates a block's type without affecting other fields", () => {
117+
const { result } = renderHook(() => useInterfacePageConfig());
118+
119+
act(() => {
120+
result.current.setConfig({
121+
id: "p1",
122+
title: "Page",
123+
blocks: [{ id: "b1", type: "chart", config: { color: "blue" }, order: 0 }],
124+
});
125+
});
126+
127+
act(() => {
128+
result.current.updateBlock("b1", { type: "pie-chart" });
129+
});
130+
131+
expect(result.current.blocks[0].type).toBe("pie-chart");
132+
expect(result.current.blocks[0].config).toEqual({ color: "blue" });
133+
});
134+
});
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* Tests for useRecordActivity – validates activity timeline management,
3+
* filtering by type/user/date, and sorting.
4+
*/
5+
import { renderHook, act } from "@testing-library/react-native";
6+
import { useRecordActivity, ActivityEntry } from "~/hooks/useRecordActivity";
7+
8+
const sampleActivities: ActivityEntry[] = [
9+
{ id: "a1", type: "update", user: "u1", summary: "Edited name", timestamp: "2025-01-01T10:00:00Z" },
10+
{ id: "a2", type: "create", user: "u2", summary: "Created record", timestamp: "2025-01-02T10:00:00Z" },
11+
{ id: "a3", type: "update", user: "u1", summary: "Changed status", timestamp: "2025-01-03T10:00:00Z" },
12+
{ id: "a4", type: "delete", user: "u3", summary: "Removed item", timestamp: "2025-01-04T10:00:00Z" },
13+
];
14+
15+
describe("useRecordActivity", () => {
16+
it("returns empty state initially", () => {
17+
const { result } = renderHook(() => useRecordActivity());
18+
19+
expect(result.current.activities).toEqual([]);
20+
expect(result.current.filter).toEqual({});
21+
expect(result.current.sortOrder).toBe("desc");
22+
expect(result.current.filteredActivities).toEqual([]);
23+
});
24+
25+
it("sets activities and sorts descending by default", () => {
26+
const { result } = renderHook(() => useRecordActivity());
27+
28+
act(() => {
29+
result.current.setActivities(sampleActivities);
30+
});
31+
32+
expect(result.current.activities).toHaveLength(4);
33+
expect(result.current.filteredActivities[0].id).toBe("a4");
34+
expect(result.current.filteredActivities[3].id).toBe("a1");
35+
});
36+
37+
it("adds a single activity entry", () => {
38+
const { result } = renderHook(() => useRecordActivity());
39+
40+
act(() => {
41+
result.current.addActivity({ id: "a1", type: "update", user: "u1", summary: "Edited name", timestamp: "2025-01-01T10:00:00Z" });
42+
});
43+
44+
expect(result.current.activities).toHaveLength(1);
45+
});
46+
47+
it("filters activities by type", () => {
48+
const { result } = renderHook(() => useRecordActivity());
49+
50+
act(() => {
51+
result.current.setActivities(sampleActivities);
52+
});
53+
54+
act(() => {
55+
result.current.setFilter({ type: "update" });
56+
});
57+
58+
expect(result.current.filteredActivities).toHaveLength(2);
59+
expect(result.current.filteredActivities.every((a) => a.type === "update")).toBe(true);
60+
});
61+
62+
it("filters activities by user", () => {
63+
const { result } = renderHook(() => useRecordActivity());
64+
65+
act(() => {
66+
result.current.setActivities(sampleActivities);
67+
});
68+
69+
act(() => {
70+
result.current.setFilter({ user: "u1" });
71+
});
72+
73+
expect(result.current.filteredActivities).toHaveLength(2);
74+
expect(result.current.filteredActivities.every((a) => a.user === "u1")).toBe(true);
75+
});
76+
77+
it("filters activities by date range", () => {
78+
const { result } = renderHook(() => useRecordActivity());
79+
80+
act(() => {
81+
result.current.setActivities(sampleActivities);
82+
});
83+
84+
act(() => {
85+
result.current.setFilter({ dateRange: { start: "2025-01-02T00:00:00Z", end: "2025-01-03T23:59:59Z" } });
86+
});
87+
88+
expect(result.current.filteredActivities).toHaveLength(2);
89+
});
90+
91+
it("sorts activities ascending", () => {
92+
const { result } = renderHook(() => useRecordActivity());
93+
94+
act(() => {
95+
result.current.setActivities(sampleActivities);
96+
});
97+
98+
act(() => {
99+
result.current.setSortOrder("asc");
100+
});
101+
102+
expect(result.current.filteredActivities[0].id).toBe("a1");
103+
expect(result.current.filteredActivities[3].id).toBe("a4");
104+
});
105+
});

0 commit comments

Comments
 (0)