@cosmicdrift/kumiko-renderer-web 0.149.2 → 0.151.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-renderer-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.151.0",
|
|
4
4
|
"description": "Web-platform bindings for @cosmicdrift/kumiko-renderer. HTML default-primitives, browser history-based navigation, EventSource-backed live events, and a one-call createKumikoApp that mounts the whole stack via react-dom.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"./styles.css": "./src/styles.css"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@cosmicdrift/kumiko-dispatcher-live": "0.
|
|
20
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
21
|
-
"@cosmicdrift/kumiko-renderer": "0.
|
|
19
|
+
"@cosmicdrift/kumiko-dispatcher-live": "0.151.0",
|
|
20
|
+
"@cosmicdrift/kumiko-headless": "0.151.0",
|
|
21
|
+
"@cosmicdrift/kumiko-renderer": "0.151.0",
|
|
22
22
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
23
23
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
24
24
|
"@radix-ui/react-label": "^2.1.8",
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Unit-Tests für den projectionDetail-Screen-Type (read-only single-row
|
|
2
|
+
// inspector, kumiko-framework#255). Deckt den Pfad ab, der Integration/E2E
|
|
3
|
+
// nicht prüft (e2e-generator skippt projectionDetail explizit — kein CRUD):
|
|
4
|
+
// - Row wird über idParam gefetcht, Felder zeigen die Query-Response-Werte
|
|
5
|
+
// - jedes Feld ist readOnly, kein Submit-Button (hasEditableSection=false)
|
|
6
|
+
// - fehlende entityId → Error-Banner statt Crash
|
|
7
|
+
|
|
8
|
+
import { describe, expect, test } from "bun:test";
|
|
9
|
+
import type { ProjectionDetailScreenDefinition } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
10
|
+
import type { Dispatcher } from "@cosmicdrift/kumiko-headless";
|
|
11
|
+
import type { FeatureSchema } from "@cosmicdrift/kumiko-renderer";
|
|
12
|
+
import { DispatcherProvider, KumikoScreen } from "@cosmicdrift/kumiko-renderer";
|
|
13
|
+
import { act, createMockDispatcher, render, screen, waitFor } from "./test-utils";
|
|
14
|
+
|
|
15
|
+
const detailScreen: ProjectionDetailScreenDefinition = {
|
|
16
|
+
id: "session-detail",
|
|
17
|
+
type: "projectionDetail",
|
|
18
|
+
query: "sessions:query:user-session:detail",
|
|
19
|
+
idParam: "id",
|
|
20
|
+
layout: {
|
|
21
|
+
sections: [{ title: "Session", fields: ["userId", "createdAt"] }],
|
|
22
|
+
},
|
|
23
|
+
fieldLabels: {
|
|
24
|
+
userId: "sessions.detail.field.userId",
|
|
25
|
+
createdAt: "sessions.detail.field.createdAt",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const schema: FeatureSchema = {
|
|
30
|
+
featureName: "sessions",
|
|
31
|
+
entities: {},
|
|
32
|
+
screens: [detailScreen],
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe("KumikoScreen / projectionDetail", () => {
|
|
36
|
+
test("fetches the row via idParam and renders its fields read-only, no submit button", async () => {
|
|
37
|
+
const querySpy = (async (_qn: string, payload: unknown) => {
|
|
38
|
+
expect(payload).toEqual({ id: "sess-1" });
|
|
39
|
+
return {
|
|
40
|
+
isSuccess: true,
|
|
41
|
+
data: { userId: "user-42", createdAt: "2026-07-01T00:00:00Z" },
|
|
42
|
+
};
|
|
43
|
+
}) as unknown as Dispatcher["query"];
|
|
44
|
+
const dispatcher: Dispatcher = createMockDispatcher({ query: querySpy });
|
|
45
|
+
|
|
46
|
+
render(
|
|
47
|
+
<DispatcherProvider dispatcher={dispatcher}>
|
|
48
|
+
<KumikoScreen schema={schema} qn="sessions:screen:session-detail" entityId="sess-1" />
|
|
49
|
+
</DispatcherProvider>,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
await waitFor(() => screen.getByTestId("render-edit-form"));
|
|
53
|
+
const userIdInput = screen.getByTestId("field-userId").querySelector("input");
|
|
54
|
+
expect(userIdInput?.value).toBe("user-42");
|
|
55
|
+
expect(userIdInput?.disabled).toBe(true);
|
|
56
|
+
|
|
57
|
+
// hasEditableSection() reads readOnly on every field — projectionDetail
|
|
58
|
+
// forces it hard in the shim, so RenderEdit must never draw a Save button.
|
|
59
|
+
expect(screen.queryByTestId("render-edit-submit")).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("missing entityId shows an error banner instead of crashing", async () => {
|
|
63
|
+
let resolveQuery: (value: unknown) => void = () => {};
|
|
64
|
+
const dispatcher: Dispatcher = createMockDispatcher({
|
|
65
|
+
query: (() =>
|
|
66
|
+
new Promise((resolve) => {
|
|
67
|
+
resolveQuery = resolve;
|
|
68
|
+
})) as unknown as Dispatcher["query"],
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
render(
|
|
72
|
+
<DispatcherProvider dispatcher={dispatcher}>
|
|
73
|
+
<KumikoScreen schema={schema} qn="sessions:screen:session-detail" />
|
|
74
|
+
</DispatcherProvider>,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
expect(screen.getByTestId("kumiko-screen-projection-detail-missing-id")).toBeTruthy();
|
|
78
|
+
|
|
79
|
+
// Screen skips the record entirely without entityId, but useQuery's
|
|
80
|
+
// effect still fired (unconditional hook call) — settle it so its async
|
|
81
|
+
// setState doesn't land after the test unmounts.
|
|
82
|
+
await act(async () => {
|
|
83
|
+
resolveQuery({ isSuccess: true, data: {} });
|
|
84
|
+
await Promise.resolve();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("record not found shows an error banner", async () => {
|
|
89
|
+
const dispatcher: Dispatcher = createMockDispatcher({
|
|
90
|
+
query: (async () => ({ isSuccess: true, data: null })) as unknown as Dispatcher["query"],
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
render(
|
|
94
|
+
<DispatcherProvider dispatcher={dispatcher}>
|
|
95
|
+
<KumikoScreen schema={schema} qn="sessions:screen:session-detail" entityId="sess-missing" />
|
|
96
|
+
</DispatcherProvider>,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
await waitFor(() => screen.getByTestId("kumiko-screen-record-missing"));
|
|
100
|
+
});
|
|
101
|
+
});
|