@nahisaho/yata-ui 1.7.0 → 1.8.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": "@nahisaho/yata-ui",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "YATA Knowledge Graph Web UI - Interactive visualization and management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -38,7 +38,7 @@
38
38
  "@types/express": "^4.17.21",
39
39
  "@types/node": "^20.10.0",
40
40
  "typescript": "^5.3.3",
41
- "vitest": "^1.6.0"
41
+ "vitest": "^4.0.16"
42
42
  },
43
43
  "engines": {
44
44
  "node": ">=20.0.0"
@@ -0,0 +1,245 @@
1
+ /**
2
+ * YATA UI Integration Tests
3
+ *
4
+ * Tests the UI Server and data transformation integration
5
+ *
6
+ * @packageDocumentation
7
+ * @see REQ-YI-WEB-001 - Web-based Visualization
8
+ * @see REQ-YI-WEB-002 - Interactive Graph Editing
9
+ * @see REQ-YI-WEB-003 - Real-time Updates
10
+ */
11
+
12
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
13
+ import { YataUIServer, createYataUIServer, DEFAULT_UI_CONFIG } from '../index.js';
14
+ import type { GraphData, GraphNode, GraphEdge } from '../index.js';
15
+
16
+ /**
17
+ * Mock YataLocal-like data source for testing
18
+ */
19
+ class MockYataLocal {
20
+ private entities: Array<{ id: string; type: string; name: string; namespace: string }> = [];
21
+ private relationships: Array<{ id: string; sourceId: string; targetId: string; type: string }> = [];
22
+
23
+ async addEntity(entity: { type: string; name: string; namespace: string }) {
24
+ const id = `E-${this.entities.length + 1}`;
25
+ this.entities.push({ id, ...entity });
26
+ return { id };
27
+ }
28
+
29
+ async addRelationship(rel: { sourceId: string; targetId: string; type: string }) {
30
+ const id = `R-${this.relationships.length + 1}`;
31
+ this.relationships.push({ id, ...rel });
32
+ return { id };
33
+ }
34
+
35
+ async getAllEntities() {
36
+ return this.entities;
37
+ }
38
+
39
+ async getAllRelationships() {
40
+ return this.relationships;
41
+ }
42
+
43
+ toGraphData(): GraphData {
44
+ return {
45
+ nodes: this.entities.map(e => ({
46
+ id: e.id,
47
+ label: e.name,
48
+ type: e.type,
49
+ namespace: e.namespace,
50
+ })),
51
+ edges: this.relationships.map(r => ({
52
+ id: r.id,
53
+ source: r.sourceId,
54
+ target: r.targetId,
55
+ type: r.type,
56
+ })),
57
+ };
58
+ }
59
+ }
60
+
61
+ describe('YATA UI Integration', () => {
62
+ let server: YataUIServer;
63
+ let mockYata: MockYataLocal;
64
+
65
+ beforeEach(async () => {
66
+ mockYata = new MockYataLocal();
67
+
68
+ // Seed test data
69
+ const user = await mockYata.addEntity({
70
+ type: 'class',
71
+ name: 'User',
72
+ namespace: 'domain.entities',
73
+ });
74
+
75
+ const userRepo = await mockYata.addEntity({
76
+ type: 'interface',
77
+ name: 'UserRepository',
78
+ namespace: 'domain.repositories',
79
+ });
80
+
81
+ const userService = await mockYata.addEntity({
82
+ type: 'class',
83
+ name: 'UserService',
84
+ namespace: 'application.services',
85
+ });
86
+
87
+ await mockYata.addRelationship({
88
+ sourceId: userService.id,
89
+ targetId: userRepo.id,
90
+ type: 'depends_on',
91
+ });
92
+
93
+ await mockYata.addRelationship({
94
+ sourceId: userRepo.id,
95
+ targetId: user.id,
96
+ type: 'manages',
97
+ });
98
+
99
+ // Create server
100
+ server = new YataUIServer({
101
+ port: 0, // Random port
102
+ enableRealtime: true,
103
+ });
104
+
105
+ server.setDataProvider(async () => mockYata.toGraphData());
106
+ });
107
+
108
+ afterEach(async () => {
109
+ if (server.isRunning()) {
110
+ await server.stop();
111
+ }
112
+ });
113
+
114
+ describe('Server Lifecycle', () => {
115
+ it('should start and stop server', async () => {
116
+ expect(server.isRunning()).toBe(false);
117
+
118
+ await server.start();
119
+ expect(server.isRunning()).toBe(true);
120
+
121
+ await server.stop();
122
+ expect(server.isRunning()).toBe(false);
123
+ });
124
+
125
+ it('should return URL', () => {
126
+ const url = server.getUrl();
127
+ expect(url).toMatch(/^http:\/\/localhost:\d+$/);
128
+ });
129
+ });
130
+
131
+ describe('Data Provider Integration', () => {
132
+ it('should accept data provider function', () => {
133
+ const mockProvider = async (): Promise<GraphData> => ({
134
+ nodes: [{ id: '1', label: 'Test', type: 'entity' }],
135
+ edges: [],
136
+ });
137
+
138
+ // Should not throw
139
+ server.setDataProvider(mockProvider);
140
+ });
141
+
142
+ it('should update data provider dynamically', async () => {
143
+ // Add more entities
144
+ await mockYata.addEntity({
145
+ type: 'class',
146
+ name: 'Order',
147
+ namespace: 'domain.entities',
148
+ });
149
+
150
+ const graphData = mockYata.toGraphData();
151
+ expect(graphData.nodes.length).toBe(4);
152
+ });
153
+ });
154
+
155
+ describe('Real-time Updates', () => {
156
+ it('should broadcast updates to connected clients', () => {
157
+ // Broadcast an update - should not throw
158
+ server.broadcastUpdate('entity:created', {
159
+ id: 'E-NEW',
160
+ type: 'class',
161
+ name: 'NewEntity',
162
+ });
163
+
164
+ expect(true).toBe(true);
165
+ });
166
+
167
+ it('should broadcast to namespaced clients', () => {
168
+ server.broadcastUpdate('entity:updated', {
169
+ id: 'E-1',
170
+ changes: { name: 'UpdatedUser' },
171
+ });
172
+
173
+ // Verify no error thrown
174
+ expect(server.isRunning()).toBe(false); // Not started yet
175
+ });
176
+ });
177
+ });
178
+
179
+ describe('YataUIServer Factory', () => {
180
+ it('should create server with factory function', () => {
181
+ const server = createYataUIServer({ port: 0 });
182
+ expect(server).toBeInstanceOf(YataUIServer);
183
+ });
184
+
185
+ it('should apply default configuration', () => {
186
+ expect(DEFAULT_UI_CONFIG.port).toBe(3000);
187
+ expect(DEFAULT_UI_CONFIG.host).toBe('localhost');
188
+ expect(DEFAULT_UI_CONFIG.cors).toBe(true);
189
+ expect(DEFAULT_UI_CONFIG.enableRealtime).toBe(true);
190
+ });
191
+ });
192
+
193
+ describe('GraphData Transformation', () => {
194
+ it('should transform entities to nodes correctly', async () => {
195
+ const mockYata = new MockYataLocal();
196
+ await mockYata.addEntity({ type: 'class', name: 'Test', namespace: 'ns' });
197
+
198
+ const graphData = mockYata.toGraphData();
199
+
200
+ expect(graphData.nodes[0]).toMatchObject({
201
+ label: 'Test',
202
+ type: 'class',
203
+ namespace: 'ns',
204
+ });
205
+ });
206
+
207
+ it('should transform relationships to edges correctly', async () => {
208
+ const mockYata = new MockYataLocal();
209
+ const e1 = await mockYata.addEntity({ type: 'class', name: 'A', namespace: 'ns' });
210
+ const e2 = await mockYata.addEntity({ type: 'class', name: 'B', namespace: 'ns' });
211
+ await mockYata.addRelationship({
212
+ sourceId: e1.id,
213
+ targetId: e2.id,
214
+ type: 'uses',
215
+ });
216
+
217
+ const graphData = mockYata.toGraphData();
218
+
219
+ expect(graphData.edges[0]).toMatchObject({
220
+ source: e1.id,
221
+ target: e2.id,
222
+ type: 'uses',
223
+ });
224
+ });
225
+
226
+ it('should handle empty data', () => {
227
+ const mockYata = new MockYataLocal();
228
+ const graphData = mockYata.toGraphData();
229
+
230
+ expect(graphData.nodes).toEqual([]);
231
+ expect(graphData.edges).toEqual([]);
232
+ });
233
+
234
+ it('should handle multiple nodes and edges', async () => {
235
+ const mockYata = new MockYataLocal();
236
+
237
+ // Add multiple entities
238
+ for (let i = 0; i < 5; i++) {
239
+ await mockYata.addEntity({ type: 'class', name: `Class${i}`, namespace: 'ns' });
240
+ }
241
+
242
+ const graphData = mockYata.toGraphData();
243
+ expect(graphData.nodes.length).toBe(5);
244
+ });
245
+ });