@powerhousedao/vetra 4.1.0-dev.81 → 4.1.0-dev.83

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.
Files changed (60) hide show
  1. package/dist/document-models/app-module/src/reducers/base-operations.d.ts.map +1 -1
  2. package/dist/document-models/app-module/src/reducers/base-operations.js +5 -1
  3. package/dist/document-models/app-module/src/tests/base-operations.test.js +157 -30
  4. package/dist/document-models/app-module/src/tests/dnd-operations.test.js +38 -7
  5. package/dist/document-models/app-module/src/tests/document-model.test.js +80 -8
  6. package/dist/document-models/document-editor/src/reducers/base-operations.d.ts.map +1 -1
  7. package/dist/document-models/document-editor/src/reducers/base-operations.js +10 -1
  8. package/dist/document-models/document-editor/src/tests/base-operations.test.js +137 -23
  9. package/dist/document-models/document-editor/src/tests/document-model.test.js +91 -8
  10. package/dist/document-models/processor-module/src/reducers/base-operations.d.ts.map +1 -1
  11. package/dist/document-models/processor-module/src/reducers/base-operations.js +15 -2
  12. package/dist/document-models/processor-module/src/tests/base-operations.test.js +153 -33
  13. package/dist/document-models/processor-module/src/tests/document-model.test.js +96 -8
  14. package/dist/document-models/subgraph-module/src/reducers/base-operations.d.ts.map +1 -1
  15. package/dist/document-models/subgraph-module/src/reducers/base-operations.js +5 -1
  16. package/dist/document-models/subgraph-module/src/tests/base-operations.test.js +33 -12
  17. package/dist/document-models/subgraph-module/src/tests/document-model.test.js +25 -8
  18. package/dist/document-models/vetra-package/src/reducers/base-operations.d.ts.map +1 -1
  19. package/dist/document-models/vetra-package/src/reducers/base-operations.js +5 -0
  20. package/dist/document-models/vetra-package/src/tests/base-operations.test.js +171 -75
  21. package/dist/document-models/vetra-package/src/tests/document-model.test.js +101 -8
  22. package/dist/editors/app-editor/components/AppEditorForm.d.ts.map +1 -1
  23. package/dist/editors/app-editor/components/AppEditorForm.js +2 -2
  24. package/dist/editors/app-editor/editor.test.d.ts +2 -0
  25. package/dist/editors/app-editor/editor.test.d.ts.map +1 -0
  26. package/dist/editors/app-editor/editor.test.js +422 -0
  27. package/dist/editors/document-editor/components/DocumentEditorForm.d.ts.map +1 -1
  28. package/dist/editors/document-editor/components/DocumentEditorForm.js +1 -1
  29. package/dist/editors/document-editor/editor.test.d.ts +2 -0
  30. package/dist/editors/document-editor/editor.test.d.ts.map +1 -0
  31. package/dist/editors/document-editor/editor.test.js +374 -0
  32. package/dist/editors/processor-editor/components/ProcessorEditorForm.d.ts.map +1 -1
  33. package/dist/editors/processor-editor/components/ProcessorEditorForm.js +1 -1
  34. package/dist/editors/processor-editor/editor.test.d.ts +2 -0
  35. package/dist/editors/processor-editor/editor.test.d.ts.map +1 -0
  36. package/dist/editors/processor-editor/editor.test.js +459 -0
  37. package/dist/editors/subgraph-editor/components/SubgraphEditorForm.d.ts.map +1 -1
  38. package/dist/editors/subgraph-editor/components/SubgraphEditorForm.js +3 -3
  39. package/dist/editors/subgraph-editor/editor.test.d.ts +2 -0
  40. package/dist/editors/subgraph-editor/editor.test.d.ts.map +1 -0
  41. package/dist/editors/subgraph-editor/editor.test.js +201 -0
  42. package/dist/editors/vetra-package/components/MetaForm.d.ts.map +1 -1
  43. package/dist/editors/vetra-package/components/MetaForm.js +3 -3
  44. package/dist/editors/vetra-package/editor.test.d.ts +2 -0
  45. package/dist/editors/vetra-package/editor.test.d.ts.map +1 -0
  46. package/dist/editors/vetra-package/editor.test.js +330 -0
  47. package/dist/processors/codegen/__tests__/codegen-processor-e2e.test.d.ts +2 -0
  48. package/dist/processors/codegen/__tests__/codegen-processor-e2e.test.d.ts.map +1 -0
  49. package/dist/processors/codegen/__tests__/codegen-processor-e2e.test.js +615 -0
  50. package/dist/processors/codegen/__tests__/factory.test.d.ts +2 -0
  51. package/dist/processors/codegen/__tests__/factory.test.d.ts.map +1 -0
  52. package/dist/processors/codegen/__tests__/factory.test.js +190 -0
  53. package/dist/setupTests.d.ts +2 -0
  54. package/dist/setupTests.d.ts.map +1 -0
  55. package/dist/setupTests.js +1 -0
  56. package/dist/style.css +9 -0
  57. package/dist/tsconfig.tsbuildinfo +1 -1
  58. package/dist/vitest.config.d.ts.map +1 -1
  59. package/dist/vitest.config.js +9 -0
  60. package/package.json +19 -14
@@ -0,0 +1,201 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen, waitFor } from "@testing-library/react";
3
+ import { userEvent } from "@testing-library/user-event";
4
+ import { beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { useSelectedSubgraphModuleDocument } from "../hooks/useVetraDocument.js";
6
+ import { Editor } from "./editor.js";
7
+ vi.mock("../hooks/useVetraDocument.js", () => ({
8
+ useSelectedSubgraphModuleDocument: vi.fn(),
9
+ }));
10
+ describe("SubgraphModule Editor", () => {
11
+ let mockDispatch;
12
+ beforeEach(() => {
13
+ mockDispatch = vi.fn();
14
+ vi.mocked(useSelectedSubgraphModuleDocument).mockReturnValue([
15
+ {
16
+ state: {
17
+ global: {
18
+ name: "",
19
+ status: "DRAFT",
20
+ },
21
+ },
22
+ },
23
+ mockDispatch,
24
+ ]);
25
+ });
26
+ describe("Core Rendering", () => {
27
+ it("should render all main form sections and labels", () => {
28
+ render(_jsx(Editor, {}));
29
+ expect(screen.getByText("Subgraph Configuration")).toBeInTheDocument();
30
+ expect(screen.getByText("Subgraph Name")).toBeInTheDocument();
31
+ expect(screen.getByText("Confirm")).toBeInTheDocument();
32
+ });
33
+ it("should display existing subgraph data when document has values", () => {
34
+ vi.mocked(useSelectedSubgraphModuleDocument).mockReturnValue([
35
+ {
36
+ state: {
37
+ global: {
38
+ name: "test-subgraph",
39
+ status: "DRAFT",
40
+ },
41
+ },
42
+ },
43
+ mockDispatch,
44
+ ]);
45
+ render(_jsx(Editor, {}));
46
+ expect(screen.getByDisplayValue("test-subgraph")).toBeInTheDocument();
47
+ });
48
+ });
49
+ describe("Subgraph Name Input", () => {
50
+ it("should dispatch setSubgraphName when name changes (debounced)", async () => {
51
+ const user = userEvent.setup();
52
+ render(_jsx(Editor, {}));
53
+ const nameInput = screen.getByLabelText("Subgraph Name");
54
+ await user.type(nameInput, "new-subgraph");
55
+ await waitFor(() => {
56
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
57
+ type: "SET_SUBGRAPH_NAME",
58
+ input: { name: "new-subgraph" },
59
+ }));
60
+ }, { timeout: 500 });
61
+ });
62
+ it("should dispatch when clearing a non-empty field", async () => {
63
+ vi.mocked(useSelectedSubgraphModuleDocument).mockReturnValue([
64
+ {
65
+ state: {
66
+ global: {
67
+ name: "existing-subgraph",
68
+ status: "DRAFT",
69
+ },
70
+ },
71
+ },
72
+ mockDispatch,
73
+ ]);
74
+ const user = userEvent.setup();
75
+ render(_jsx(Editor, {}));
76
+ const nameInput = screen.getByLabelText("Subgraph Name");
77
+ await user.clear(nameInput);
78
+ await waitFor(() => {
79
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
80
+ type: "SET_SUBGRAPH_NAME",
81
+ input: { name: "" },
82
+ }));
83
+ }, { timeout: 500 });
84
+ });
85
+ it("should NOT dispatch when typing identical value without clearing", async () => {
86
+ vi.mocked(useSelectedSubgraphModuleDocument).mockReturnValue([
87
+ {
88
+ state: {
89
+ global: {
90
+ name: "test",
91
+ status: "DRAFT",
92
+ },
93
+ },
94
+ },
95
+ mockDispatch,
96
+ ]);
97
+ const user = userEvent.setup();
98
+ render(_jsx(Editor, {}));
99
+ const nameInput = screen.getByLabelText("Subgraph Name");
100
+ await user.tripleClick(nameInput);
101
+ await user.type(nameInput, "test");
102
+ await waitFor(() => {
103
+ expect(mockDispatch).not.toHaveBeenCalled();
104
+ });
105
+ });
106
+ it("should NOT dispatch when both old and new values are empty", async () => {
107
+ const user = userEvent.setup();
108
+ render(_jsx(Editor, {}));
109
+ const nameInput = screen.getByLabelText("Subgraph Name");
110
+ await user.click(nameInput);
111
+ await user.tab();
112
+ await waitFor(() => {
113
+ expect(mockDispatch).not.toHaveBeenCalled();
114
+ });
115
+ });
116
+ });
117
+ describe("Confirm Button", () => {
118
+ it("should dispatch setSubgraphStatus when confirm clicked", async () => {
119
+ vi.mocked(useSelectedSubgraphModuleDocument).mockReturnValue([
120
+ {
121
+ state: {
122
+ global: {
123
+ name: "test-subgraph",
124
+ status: "DRAFT",
125
+ },
126
+ },
127
+ },
128
+ mockDispatch,
129
+ ]);
130
+ const user = userEvent.setup();
131
+ render(_jsx(Editor, {}));
132
+ const confirmButton = screen.getByText("Confirm");
133
+ await user.click(confirmButton);
134
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
135
+ type: "SET_SUBGRAPH_STATUS",
136
+ input: { status: "CONFIRMED" },
137
+ }));
138
+ });
139
+ it("should be disabled when subgraph name is empty", () => {
140
+ render(_jsx(Editor, {}));
141
+ const confirmButton = screen.getByText("Confirm");
142
+ expect(confirmButton).toBeDisabled();
143
+ });
144
+ it("should be hidden when status is CONFIRMED", () => {
145
+ vi.mocked(useSelectedSubgraphModuleDocument).mockReturnValue([
146
+ {
147
+ state: {
148
+ global: {
149
+ name: "test-subgraph",
150
+ status: "CONFIRMED",
151
+ },
152
+ },
153
+ },
154
+ mockDispatch,
155
+ ]);
156
+ render(_jsx(Editor, {}));
157
+ const confirmButton = screen.queryByText("Confirm");
158
+ expect(confirmButton).not.toBeInTheDocument();
159
+ });
160
+ });
161
+ describe("Read-only Mode", () => {
162
+ it("should disable form field when status is CONFIRMED", () => {
163
+ vi.mocked(useSelectedSubgraphModuleDocument).mockReturnValue([
164
+ {
165
+ state: {
166
+ global: {
167
+ name: "test-subgraph",
168
+ status: "CONFIRMED",
169
+ },
170
+ },
171
+ },
172
+ mockDispatch,
173
+ ]);
174
+ render(_jsx(Editor, {}));
175
+ const nameInput = screen.getByLabelText("Subgraph Name");
176
+ expect(nameInput).toBeDisabled();
177
+ // Confirm button should not be visible
178
+ const confirmButton = screen.queryByText("Confirm");
179
+ expect(confirmButton).not.toBeInTheDocument();
180
+ });
181
+ it("should enable form field when status is DRAFT", () => {
182
+ vi.mocked(useSelectedSubgraphModuleDocument).mockReturnValue([
183
+ {
184
+ state: {
185
+ global: {
186
+ name: "test-subgraph",
187
+ status: "DRAFT",
188
+ },
189
+ },
190
+ },
191
+ mockDispatch,
192
+ ]);
193
+ render(_jsx(Editor, {}));
194
+ const nameInput = screen.getByLabelText("Subgraph Name");
195
+ expect(nameInput).not.toBeDisabled();
196
+ // Confirm button should be visible
197
+ const confirmButton = screen.getByText("Confirm");
198
+ expect(confirmButton).toBeInTheDocument();
199
+ });
200
+ });
201
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"MetaForm.d.ts","sourceRoot":"","sources":["../../../../editors/vetra-package/components/MetaForm.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iDAAiD,CAAC;AAE/E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,oBAAoB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,wBAAwB,CAAC,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9D,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAChE,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAoP5C,CAAC"}
1
+ {"version":3,"file":"MetaForm.d.ts","sourceRoot":"","sources":["../../../../editors/vetra-package/components/MetaForm.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iDAAiD,CAAC;AAE/E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,oBAAoB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,wBAAwB,CAAC,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9D,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAChE,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAgS5C,CAAC"}
@@ -20,11 +20,11 @@ export const MetaForm = (props) => {
20
20
  useDebounce(publisherUrl, onPublisherUrlChange, 300);
21
21
  useDebounce(githubRepository, onGithubRepositoryChange, 300);
22
22
  useDebounce(npmPackage, onNpmPackageChange, 300);
23
- return (_jsxs("div", { className: "grid grid-cols-1 gap-6 bg-white p-6 lg:grid-cols-3", children: [_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Name" }), _jsx("input", { type: "text", value: name, onChange: (e) => setName(e.target.value), className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Description" }), _jsx("textarea", { rows: 6, value: description, onChange: (e) => setDescription(e.target.value), className: "w-full resize-none rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" })] })] }), _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Category" }), _jsxs("select", { value: category, onChange: (e) => {
23
+ return (_jsxs("div", { className: "grid grid-cols-1 gap-6 bg-white p-6 lg:grid-cols-3", children: [_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: "package-name", className: "mb-2 block text-sm font-medium text-gray-700", children: "Name" }), _jsx("input", { id: "package-name", type: "text", value: name, onChange: (e) => setName(e.target.value), className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "package-description", className: "mb-2 block text-sm font-medium text-gray-700", children: "Description" }), _jsx("textarea", { id: "package-description", rows: 6, value: description, onChange: (e) => setDescription(e.target.value), className: "w-full resize-none rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" })] })] }), _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: "package-category", className: "mb-2 block text-sm font-medium text-gray-700", children: "Category" }), _jsxs("select", { id: "package-category", value: category, onChange: (e) => {
24
24
  const newValue = e.target.value;
25
25
  setCategory(newValue);
26
26
  onCategoryChange?.(newValue);
27
- }, className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500", children: [_jsx("option", { value: "", children: "Not selected" }), _jsx("option", { value: "Productivity", children: "Productivity" }), _jsx("option", { value: "Governance", children: "Governance" }), _jsx("option", { value: "Project Management", children: "Project Management" }), _jsx("option", { value: "Finance", children: "Finance" }), _jsx("option", { value: "Legal", children: "Legal" }), _jsx("option", { value: "People & Culture", children: "People & Culture" }), _jsx("option", { value: "Engineering", children: "Engineering" })] })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Publisher" }), _jsx("input", { type: "text", value: publisher, onChange: (e) => setPublisher(e.target.value), className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Publisher URL" }), _jsx("input", { type: "text", value: publisherUrl, onChange: (e) => setPublisherUrl(e.target.value), className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Keywords" }), _jsxs("div", { className: "space-y-2", children: [_jsx("input", { type: "text", value: keywordInput, onChange: (e) => setKeywordInput(e.target.value), onKeyDown: (e) => {
27
+ }, className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500", children: [_jsx("option", { value: "", children: "Not selected" }), _jsx("option", { value: "Productivity", children: "Productivity" }), _jsx("option", { value: "Governance", children: "Governance" }), _jsx("option", { value: "Project Management", children: "Project Management" }), _jsx("option", { value: "Finance", children: "Finance" }), _jsx("option", { value: "Legal", children: "Legal" }), _jsx("option", { value: "People & Culture", children: "People & Culture" }), _jsx("option", { value: "Engineering", children: "Engineering" })] })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "package-publisher", className: "mb-2 block text-sm font-medium text-gray-700", children: "Publisher" }), _jsx("input", { id: "package-publisher", type: "text", value: publisher, onChange: (e) => setPublisher(e.target.value), className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "package-publisher-url", className: "mb-2 block text-sm font-medium text-gray-700", children: "Publisher URL" }), _jsx("input", { id: "package-publisher-url", type: "text", value: publisherUrl, onChange: (e) => setPublisherUrl(e.target.value), className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "package-keywords", className: "mb-2 block text-sm font-medium text-gray-700", children: "Keywords" }), _jsxs("div", { className: "space-y-2", children: [_jsx("input", { id: "package-keywords", type: "text", value: keywordInput, onChange: (e) => setKeywordInput(e.target.value), onKeyDown: (e) => {
28
28
  if (e.key === "Enter" && keywordInput.trim()) {
29
29
  e.preventDefault();
30
30
  const newKeyword = {
@@ -38,5 +38,5 @@ export const MetaForm = (props) => {
38
38
  }, placeholder: "Type a keyword and press Enter", className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" }), _jsx("div", { className: "flex min-h-[80px] flex-wrap gap-2 rounded-md border border-gray-300 p-3", children: keywords.map((keyword) => (_jsxs("span", { className: "inline-flex items-center rounded border border-blue-300 bg-blue-100 px-2 py-0.5 text-xs text-blue-800", children: [keyword.label, _jsx("button", { onClick: () => {
39
39
  setKeywords(keywords.filter((k) => k.id !== keyword.id));
40
40
  onRemoveKeyword?.(keyword.id);
41
- }, className: "ml-1 text-blue-600 hover:text-blue-800 focus:outline-none", children: "\u00D7" })] }, keyword.id))) })] })] })] }), _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Github Repository" }), _jsx("input", { type: "text", value: githubRepository, onChange: (e) => setGithubRepository(e.target.value), className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "NPM-package" }), _jsx("input", { type: "text", value: npmPackage, onChange: (e) => setNpmPackage(e.target.value), className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Version" }), _jsx("input", { type: "text", placeholder: "1.0.0-dev", readOnly: true, className: "w-full cursor-not-allowed rounded-md border border-gray-300 bg-gray-50 px-3 py-2 text-gray-500" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "License" }), _jsx("input", { type: "text", placeholder: "AGPL-3.0-only", readOnly: true, className: "w-full cursor-not-allowed rounded-md border border-gray-300 bg-gray-50 px-3 py-2 text-gray-500" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Install with:" }), _jsx("input", { type: "text", placeholder: "@powerhousedao/todo-demo-package", readOnly: true, className: "w-full cursor-not-allowed rounded-md border border-gray-300 bg-gray-50 px-3 py-2 text-gray-500" })] })] })] }));
41
+ }, className: "ml-1 text-blue-600 hover:text-blue-800 focus:outline-none", children: "\u00D7" })] }, keyword.id))) })] })] })] }), _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: "package-github", className: "mb-2 block text-sm font-medium text-gray-700", children: "Github Repository" }), _jsx("input", { id: "package-github", type: "text", value: githubRepository, onChange: (e) => setGithubRepository(e.target.value), className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "package-npm", className: "mb-2 block text-sm font-medium text-gray-700", children: "NPM-package" }), _jsx("input", { id: "package-npm", type: "text", value: npmPackage, onChange: (e) => setNpmPackage(e.target.value), className: "w-full rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "package-version", className: "mb-2 block text-sm font-medium text-gray-700", children: "Version" }), _jsx("input", { id: "package-version", type: "text", placeholder: "1.0.0-dev", readOnly: true, className: "w-full cursor-not-allowed rounded-md border border-gray-300 bg-gray-50 px-3 py-2 text-gray-500" })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "package-license", className: "mb-2 block text-sm font-medium text-gray-700", children: "License" }), _jsx("input", { id: "package-license", type: "text", placeholder: "AGPL-3.0-only", readOnly: true, className: "w-full cursor-not-allowed rounded-md border border-gray-300 bg-gray-50 px-3 py-2 text-gray-500" })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "package-install", className: "mb-2 block text-sm font-medium text-gray-700", children: "Install with:" }), _jsx("input", { id: "package-install", type: "text", placeholder: "@powerhousedao/todo-demo-package", readOnly: true, className: "w-full cursor-not-allowed rounded-md border border-gray-300 bg-gray-50 px-3 py-2 text-gray-500" })] })] })] }));
42
42
  };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=editor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor.test.d.ts","sourceRoot":"","sources":["../../../editors/vetra-package/editor.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,330 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen, waitFor } from "@testing-library/react";
3
+ import { userEvent } from "@testing-library/user-event";
4
+ import { beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { useSelectedDriveVetraPackage } from "../hooks/useVetraDocument.js";
6
+ import { Editor } from "./editor.js";
7
+ vi.mock("../hooks/useVetraDocument.js", () => ({
8
+ useSelectedDriveVetraPackage: vi.fn(),
9
+ }));
10
+ describe("VetraPackage Editor", () => {
11
+ let mockDispatch;
12
+ beforeEach(() => {
13
+ mockDispatch = vi.fn();
14
+ vi.mocked(useSelectedDriveVetraPackage).mockReturnValue([
15
+ {
16
+ state: {
17
+ global: {
18
+ name: null,
19
+ description: null,
20
+ category: null,
21
+ author: {
22
+ name: null,
23
+ website: null,
24
+ },
25
+ keywords: [],
26
+ githubUrl: null,
27
+ npmUrl: null,
28
+ },
29
+ },
30
+ },
31
+ mockDispatch,
32
+ ]);
33
+ });
34
+ describe("Core Rendering", () => {
35
+ it("should render all main form sections and labels", () => {
36
+ render(_jsx(Editor, {}));
37
+ expect(screen.getByText("Name")).toBeInTheDocument();
38
+ expect(screen.getByText("Description")).toBeInTheDocument();
39
+ expect(screen.getByText("Category")).toBeInTheDocument();
40
+ expect(screen.getByText("Publisher")).toBeInTheDocument();
41
+ expect(screen.getByText("Publisher URL")).toBeInTheDocument();
42
+ expect(screen.getByText("Keywords")).toBeInTheDocument();
43
+ expect(screen.getByText("Github Repository")).toBeInTheDocument();
44
+ expect(screen.getByText("NPM-package")).toBeInTheDocument();
45
+ });
46
+ });
47
+ describe("Initial Data Display", () => {
48
+ it("should display existing package data when document has values", () => {
49
+ vi.mocked(useSelectedDriveVetraPackage).mockReturnValue([
50
+ {
51
+ state: {
52
+ global: {
53
+ name: "test-package",
54
+ description: "A test package",
55
+ category: "Productivity",
56
+ author: {
57
+ name: "John Doe",
58
+ website: "https://johndoe.com",
59
+ },
60
+ keywords: [
61
+ { id: "kw-1", label: "react" },
62
+ { id: "kw-2", label: "typescript" },
63
+ ],
64
+ githubUrl: "https://github.com/test/package",
65
+ npmUrl: "https://npmjs.com/package/test",
66
+ },
67
+ },
68
+ },
69
+ mockDispatch,
70
+ ]);
71
+ render(_jsx(Editor, {}));
72
+ expect(screen.getByDisplayValue("test-package")).toBeInTheDocument();
73
+ expect(screen.getByDisplayValue("A test package")).toBeInTheDocument();
74
+ expect(screen.getByDisplayValue("Productivity")).toBeInTheDocument();
75
+ expect(screen.getByDisplayValue("John Doe")).toBeInTheDocument();
76
+ expect(screen.getByDisplayValue("https://johndoe.com")).toBeInTheDocument();
77
+ expect(screen.getByText("react")).toBeInTheDocument();
78
+ expect(screen.getByText("typescript")).toBeInTheDocument();
79
+ expect(screen.getByDisplayValue("https://github.com/test/package")).toBeInTheDocument();
80
+ expect(screen.getByDisplayValue("https://npmjs.com/package/test")).toBeInTheDocument();
81
+ });
82
+ });
83
+ describe("Text Input Fields - Dispatch Verification", () => {
84
+ it("should dispatch setPackageName when name changes", async () => {
85
+ const user = userEvent.setup();
86
+ render(_jsx(Editor, {}));
87
+ const nameInput = screen.getByLabelText("Name");
88
+ await user.type(nameInput, "new-package");
89
+ await waitFor(() => {
90
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
91
+ type: "SET_PACKAGE_NAME",
92
+ input: { name: "new-package" },
93
+ }));
94
+ }, { timeout: 500 });
95
+ });
96
+ it("should dispatch setPackageDescription when description changes", async () => {
97
+ const user = userEvent.setup();
98
+ render(_jsx(Editor, {}));
99
+ const descriptionInput = screen.getByLabelText("Description");
100
+ await user.type(descriptionInput, "New description");
101
+ await waitFor(() => {
102
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
103
+ type: "SET_PACKAGE_DESCRIPTION",
104
+ input: { description: "New description" },
105
+ }));
106
+ }, { timeout: 500 });
107
+ });
108
+ it("should dispatch setPackageAuthorName when publisher changes", async () => {
109
+ const user = userEvent.setup();
110
+ render(_jsx(Editor, {}));
111
+ const publisherInput = screen.getByLabelText("Publisher");
112
+ await user.type(publisherInput, "Acme Corp");
113
+ await waitFor(() => {
114
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
115
+ type: "SET_PACKAGE_AUTHOR_NAME",
116
+ input: { name: "Acme Corp" },
117
+ }));
118
+ }, { timeout: 500 });
119
+ });
120
+ it("should dispatch setPackageAuthorWebsite when publisher URL changes", async () => {
121
+ const user = userEvent.setup();
122
+ render(_jsx(Editor, {}));
123
+ const publisherUrlInput = screen.getByLabelText("Publisher URL");
124
+ await user.type(publisherUrlInput, "https://acme.com");
125
+ await waitFor(() => {
126
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
127
+ type: "SET_PACKAGE_AUTHOR_WEBSITE",
128
+ input: { website: "https://acme.com" },
129
+ }));
130
+ }, { timeout: 500 });
131
+ });
132
+ it("should dispatch setPackageGithubUrl when Github repository changes", async () => {
133
+ const user = userEvent.setup();
134
+ render(_jsx(Editor, {}));
135
+ const githubInput = screen.getByLabelText("Github Repository");
136
+ await user.type(githubInput, "https://github.com/acme/test");
137
+ await waitFor(() => {
138
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
139
+ type: "SET_PACKAGE_GITHUB_URL",
140
+ input: { url: "https://github.com/acme/test" },
141
+ }));
142
+ }, { timeout: 500 });
143
+ });
144
+ it("should dispatch setPackageNpmUrl when NPM package changes", async () => {
145
+ const user = userEvent.setup();
146
+ render(_jsx(Editor, {}));
147
+ const npmInput = screen.getByLabelText("NPM-package");
148
+ await user.type(npmInput, "https://npmjs.com/package/acme");
149
+ await waitFor(() => {
150
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
151
+ type: "SET_PACKAGE_NPM_URL",
152
+ input: { url: "https://npmjs.com/package/acme" },
153
+ }));
154
+ }, { timeout: 500 });
155
+ });
156
+ });
157
+ describe("Conditional Dispatch Logic", () => {
158
+ it("should dispatch when clearing a non-empty field", async () => {
159
+ vi.mocked(useSelectedDriveVetraPackage).mockReturnValue([
160
+ {
161
+ state: {
162
+ global: {
163
+ name: "existing-package",
164
+ description: null,
165
+ category: null,
166
+ author: { name: null, website: null },
167
+ keywords: [],
168
+ githubUrl: null,
169
+ npmUrl: null,
170
+ },
171
+ },
172
+ },
173
+ mockDispatch,
174
+ ]);
175
+ const user = userEvent.setup();
176
+ render(_jsx(Editor, {}));
177
+ const nameInput = screen.getByLabelText("Name");
178
+ await user.clear(nameInput);
179
+ await waitFor(() => {
180
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
181
+ type: "SET_PACKAGE_NAME",
182
+ input: { name: "" },
183
+ }));
184
+ }, { timeout: 500 });
185
+ });
186
+ it("should NOT dispatch when typing identical value without clearing", async () => {
187
+ vi.mocked(useSelectedDriveVetraPackage).mockReturnValue([
188
+ {
189
+ state: {
190
+ global: {
191
+ name: "test",
192
+ description: null,
193
+ category: null,
194
+ author: { name: null, website: null },
195
+ keywords: [],
196
+ githubUrl: null,
197
+ npmUrl: null,
198
+ },
199
+ },
200
+ },
201
+ mockDispatch,
202
+ ]);
203
+ const user = userEvent.setup();
204
+ render(_jsx(Editor, {}));
205
+ const nameInput = screen.getByLabelText("Name");
206
+ // Simulate user selecting all text and typing the same value
207
+ await user.tripleClick(nameInput); // Select all
208
+ await user.type(nameInput, "test");
209
+ await waitFor(() => {
210
+ expect(mockDispatch).not.toHaveBeenCalled();
211
+ });
212
+ });
213
+ it("should NOT dispatch when both old and new values are empty", async () => {
214
+ const user = userEvent.setup();
215
+ render(_jsx(Editor, {}));
216
+ const nameInput = screen.getByLabelText("Name");
217
+ await user.click(nameInput);
218
+ await user.tab(); // Focus and blur without typing
219
+ await waitFor(() => {
220
+ expect(mockDispatch).not.toHaveBeenCalled();
221
+ });
222
+ });
223
+ });
224
+ describe("Category Selection", () => {
225
+ it("should dispatch category change immediately (no debounce)", async () => {
226
+ const user = userEvent.setup();
227
+ render(_jsx(Editor, {}));
228
+ const categorySelect = screen.getByLabelText("Category");
229
+ await user.selectOptions(categorySelect, "Productivity");
230
+ // Should dispatch immediately without waiting for debounce
231
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
232
+ type: "SET_PACKAGE_CATEGORY",
233
+ input: { category: "Productivity" },
234
+ }));
235
+ });
236
+ });
237
+ describe("Debounce Behavior", () => {
238
+ it("should debounce text input changes (300ms delay)", async () => {
239
+ const user = userEvent.setup();
240
+ render(_jsx(Editor, {}));
241
+ const nameInput = screen.getByLabelText("Name");
242
+ // Type multiple characters quickly
243
+ await user.type(nameInput, "test");
244
+ // Should not dispatch immediately
245
+ expect(mockDispatch).not.toHaveBeenCalled();
246
+ // Should dispatch after debounce delay
247
+ await waitFor(() => {
248
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
249
+ type: "SET_PACKAGE_NAME",
250
+ input: { name: "test" },
251
+ }));
252
+ }, { timeout: 500 });
253
+ });
254
+ });
255
+ describe("Keywords Management", () => {
256
+ it("should add keyword on Enter with valid input", async () => {
257
+ const user = userEvent.setup();
258
+ render(_jsx(Editor, {}));
259
+ const keywordInput = screen.getByPlaceholderText("Type a keyword and press Enter");
260
+ await user.type(keywordInput, "react{Enter}");
261
+ expect(screen.getByText("react")).toBeInTheDocument();
262
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
263
+ type: "ADD_PACKAGE_KEYWORD",
264
+ input: expect.objectContaining({
265
+ label: "react",
266
+ }),
267
+ }));
268
+ });
269
+ it("should NOT add empty or whitespace-only keyword", async () => {
270
+ const user = userEvent.setup();
271
+ render(_jsx(Editor, {}));
272
+ const keywordInput = screen.getByPlaceholderText("Type a keyword and press Enter");
273
+ // Try to add empty keyword
274
+ await user.type(keywordInput, "{Enter}");
275
+ expect(mockDispatch).not.toHaveBeenCalled();
276
+ // Try to add whitespace-only keyword
277
+ await user.type(keywordInput, " {Enter}");
278
+ expect(mockDispatch).not.toHaveBeenCalled();
279
+ });
280
+ it("should display keywords and allow removal", async () => {
281
+ vi.mocked(useSelectedDriveVetraPackage).mockReturnValue([
282
+ {
283
+ state: {
284
+ global: {
285
+ name: null,
286
+ description: null,
287
+ category: null,
288
+ author: { name: null, website: null },
289
+ keywords: [
290
+ { id: "kw-1", label: "react" },
291
+ { id: "kw-2", label: "typescript" },
292
+ ],
293
+ githubUrl: null,
294
+ npmUrl: null,
295
+ },
296
+ },
297
+ },
298
+ mockDispatch,
299
+ ]);
300
+ const user = userEvent.setup();
301
+ render(_jsx(Editor, {}));
302
+ expect(screen.getByText("react")).toBeInTheDocument();
303
+ expect(screen.getByText("typescript")).toBeInTheDocument();
304
+ // Find and click the remove button for "react"
305
+ const reactKeyword = screen.getByText("react").closest("span");
306
+ const removeButton = reactKeyword?.querySelector("button");
307
+ if (removeButton) {
308
+ await user.click(removeButton);
309
+ }
310
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
311
+ type: "REMOVE_PACKAGE_KEYWORD",
312
+ input: { id: "kw-1" },
313
+ }));
314
+ });
315
+ it("should dispatch addKeyword action with correct payload", async () => {
316
+ const user = userEvent.setup();
317
+ render(_jsx(Editor, {}));
318
+ // Add keyword
319
+ const keywordInput = screen.getByPlaceholderText("Type a keyword and press Enter");
320
+ await user.type(keywordInput, "testing{Enter}");
321
+ expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({
322
+ type: "ADD_PACKAGE_KEYWORD",
323
+ input: expect.objectContaining({
324
+ label: "testing",
325
+ id: expect.any(String),
326
+ }),
327
+ }));
328
+ });
329
+ });
330
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=codegen-processor-e2e.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen-processor-e2e.test.d.ts","sourceRoot":"","sources":["../../../../processors/codegen/__tests__/codegen-processor-e2e.test.ts"],"names":[],"mappings":""}