@manojkmfsi/monodog 1.1.35 → 1.1.37

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 (197) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/monodog-dashboard/THEME_SYSTEM.md +309 -0
  3. package/monodog-dashboard/__tests__/AuthCallbackPage.test.ts +138 -0
  4. package/monodog-dashboard/__tests__/ConfigInspector.test.ts +33 -0
  5. package/monodog-dashboard/__tests__/DependencyGraph.test.ts +72 -0
  6. package/monodog-dashboard/__tests__/HealthStatus.test.ts +69 -0
  7. package/monodog-dashboard/__tests__/LoginPage.test.ts +79 -0
  8. package/monodog-dashboard/__tests__/PackageDetail.test.ts +22 -0
  9. package/monodog-dashboard/__tests__/ReleaseManager.test.tsx +510 -0
  10. package/monodog-dashboard/__tests__/api-client.test.ts +475 -0
  11. package/monodog-dashboard/__tests__/components.test.ts +117 -0
  12. package/monodog-dashboard/__tests__/dashboard.utils.test.ts +144 -0
  13. package/monodog-dashboard/__tests__/dependency.utils.test.ts +125 -0
  14. package/monodog-dashboard/__tests__/monorepoService.unit.test.ts +103 -0
  15. package/monodog-dashboard/__tests__/packages.utils.test.ts +37 -0
  16. package/monodog-dashboard/__tests__/pages.test.ts +103 -0
  17. package/monodog-dashboard/index.html +13 -0
  18. package/monodog-dashboard/package-lock.json +4587 -0
  19. package/monodog-dashboard/package.json +3 -0
  20. package/monodog-dashboard/postcss.config.js +6 -0
  21. package/monodog-dashboard/src/components/App.tsx +36 -0
  22. package/monodog-dashboard/src/components/LoadingState.tsx +18 -0
  23. package/monodog-dashboard/src/components/PermissionGuard.tsx +92 -0
  24. package/monodog-dashboard/src/components/ProtectedRoute.tsx +24 -0
  25. package/monodog-dashboard/src/components/configuration/Configuration.tsx +91 -0
  26. package/monodog-dashboard/src/components/configuration/components/BrandingSettings.tsx +128 -0
  27. package/monodog-dashboard/src/components/configuration/components/ConfigurationHeader.tsx +34 -0
  28. package/monodog-dashboard/src/components/configuration/components/ConfigurationModal.tsx +20 -0
  29. package/monodog-dashboard/src/components/configuration/components/ConfigurationTabs.tsx +30 -0
  30. package/monodog-dashboard/src/components/configuration/components/FeatureToggles.tsx +66 -0
  31. package/monodog-dashboard/src/components/configuration/components/GeneralSettings.tsx +77 -0
  32. package/monodog-dashboard/src/components/configuration/components/MonorepoSettings.tsx +179 -0
  33. package/monodog-dashboard/src/components/configuration/components/index.ts +8 -0
  34. package/monodog-dashboard/src/components/configuration/types/config.types.ts +52 -0
  35. package/monodog-dashboard/src/components/configuration/utils/config.utils.tsx +115 -0
  36. package/monodog-dashboard/src/components/main-dashboard/Dashboard.tsx +242 -0
  37. package/monodog-dashboard/src/components/main-dashboard/Layout.tsx +112 -0
  38. package/monodog-dashboard/src/components/main-dashboard/components/Header.tsx +47 -0
  39. package/monodog-dashboard/src/components/main-dashboard/components/PackageDistribution.tsx +35 -0
  40. package/monodog-dashboard/src/components/main-dashboard/components/PackageSearchFilter.tsx +47 -0
  41. package/monodog-dashboard/src/components/main-dashboard/components/PackageTable.tsx +87 -0
  42. package/monodog-dashboard/src/components/main-dashboard/components/QuickActions.tsx +60 -0
  43. package/monodog-dashboard/src/components/main-dashboard/components/StatsCards.tsx +73 -0
  44. package/monodog-dashboard/src/components/main-dashboard/components/index.ts +7 -0
  45. package/monodog-dashboard/src/components/main-dashboard/types/dashboard.types.ts +33 -0
  46. package/monodog-dashboard/src/components/main-dashboard/utils/dashboard.utils.tsx +70 -0
  47. package/monodog-dashboard/src/components/modules/ci-integration/CIIntegration.tsx +277 -0
  48. package/monodog-dashboard/src/components/modules/ci-integration/components/BuildDetails.tsx +228 -0
  49. package/monodog-dashboard/src/components/modules/ci-integration/components/BuildList.tsx +212 -0
  50. package/monodog-dashboard/src/components/modules/ci-integration/components/BuildOverview.tsx +158 -0
  51. package/monodog-dashboard/src/components/modules/ci-integration/components/CIIntegrationHeader.tsx +38 -0
  52. package/monodog-dashboard/src/components/modules/ci-integration/components/ErrorState.tsx +25 -0
  53. package/monodog-dashboard/src/components/modules/ci-integration/components/LoadingState.tsx +16 -0
  54. package/monodog-dashboard/src/components/modules/ci-integration/components/PipelineStatus.tsx +223 -0
  55. package/monodog-dashboard/src/components/modules/ci-integration/components/index.ts +8 -0
  56. package/monodog-dashboard/src/components/modules/ci-integration/types/ci.types.ts +97 -0
  57. package/monodog-dashboard/src/components/modules/ci-integration/utils/ci.utils.tsx +264 -0
  58. package/monodog-dashboard/src/components/modules/config-inspector/ConfigInspector.tsx +324 -0
  59. package/monodog-dashboard/src/components/modules/config-inspector/components/ConfigEditor.tsx +93 -0
  60. package/monodog-dashboard/src/components/modules/config-inspector/components/ConfigInspectorHeader.tsx +36 -0
  61. package/monodog-dashboard/src/components/modules/config-inspector/components/ConfigPreview.tsx +89 -0
  62. package/monodog-dashboard/src/components/modules/config-inspector/components/ConfigSidebar.tsx +141 -0
  63. package/monodog-dashboard/src/components/modules/config-inspector/components/ConfigToolbar.tsx +184 -0
  64. package/monodog-dashboard/src/components/modules/config-inspector/components/ErrorState.tsx +25 -0
  65. package/monodog-dashboard/src/components/modules/config-inspector/components/LoadingState.tsx +16 -0
  66. package/monodog-dashboard/src/components/modules/config-inspector/components/ValidationPanel.tsx +155 -0
  67. package/monodog-dashboard/src/components/modules/config-inspector/components/index.ts +9 -0
  68. package/monodog-dashboard/src/components/modules/config-inspector/types/config.types.ts +100 -0
  69. package/monodog-dashboard/src/components/modules/config-inspector/utils/config.utils.tsx +704 -0
  70. package/monodog-dashboard/src/components/modules/dependency-graph/DependencyGraph.tsx +224 -0
  71. package/monodog-dashboard/src/components/modules/dependency-graph/components/CircularDependencies.tsx +177 -0
  72. package/monodog-dashboard/src/components/modules/dependency-graph/components/DependencyDetails.tsx +192 -0
  73. package/monodog-dashboard/src/components/modules/dependency-graph/components/DependencyGraphHeader.tsx +30 -0
  74. package/monodog-dashboard/src/components/modules/dependency-graph/components/DependencyList.tsx +177 -0
  75. package/monodog-dashboard/src/components/modules/dependency-graph/components/ErrorState.tsx +25 -0
  76. package/monodog-dashboard/src/components/modules/dependency-graph/components/GraphLegend.tsx +89 -0
  77. package/monodog-dashboard/src/components/modules/dependency-graph/components/GraphStats.tsx +141 -0
  78. package/monodog-dashboard/src/components/modules/dependency-graph/components/GraphToolbar.tsx +107 -0
  79. package/monodog-dashboard/src/components/modules/dependency-graph/components/GraphVisualization.tsx +179 -0
  80. package/monodog-dashboard/src/components/modules/dependency-graph/components/LoadingState.tsx +16 -0
  81. package/monodog-dashboard/src/components/modules/dependency-graph/components/index.ts +11 -0
  82. package/monodog-dashboard/src/components/modules/dependency-graph/types/dependency.types.ts +105 -0
  83. package/monodog-dashboard/src/components/modules/dependency-graph/utils/dependency.utils.tsx +433 -0
  84. package/monodog-dashboard/src/components/modules/health-status/HealthStatus.tsx +739 -0
  85. package/monodog-dashboard/src/components/modules/health-status/components/ErrorState.tsx +25 -0
  86. package/monodog-dashboard/src/components/modules/health-status/components/HealthActions.tsx +122 -0
  87. package/monodog-dashboard/src/components/modules/health-status/components/HealthAlerts.tsx +151 -0
  88. package/monodog-dashboard/src/components/modules/health-status/components/HealthMetrics.tsx +132 -0
  89. package/monodog-dashboard/src/components/modules/health-status/components/HealthStatusHeader.tsx +30 -0
  90. package/monodog-dashboard/src/components/modules/health-status/components/LoadingState.tsx +16 -0
  91. package/monodog-dashboard/src/components/modules/health-status/components/OverallHealthScore.tsx +122 -0
  92. package/monodog-dashboard/src/components/modules/health-status/components/PackageHealthList.tsx +195 -0
  93. package/monodog-dashboard/src/components/modules/health-status/types/health.types.ts +80 -0
  94. package/monodog-dashboard/src/components/modules/health-status/utils/health.utils.tsx +220 -0
  95. package/monodog-dashboard/src/components/modules/packages/PackageDetail.tsx +255 -0
  96. package/monodog-dashboard/src/components/modules/packages/PackagesOverview.tsx +166 -0
  97. package/monodog-dashboard/src/components/modules/packages/components/ConfigurationTab.tsx +311 -0
  98. package/monodog-dashboard/src/components/modules/packages/components/DependenciesTab.tsx +154 -0
  99. package/monodog-dashboard/src/components/modules/packages/components/ErrorState.tsx +21 -0
  100. package/monodog-dashboard/src/components/modules/packages/components/HealthMetricsTab.tsx +275 -0
  101. package/monodog-dashboard/src/components/modules/packages/components/LoadingState.tsx +14 -0
  102. package/monodog-dashboard/src/components/modules/packages/components/PackageDetailHeader.tsx +167 -0
  103. package/monodog-dashboard/src/components/modules/packages/components/PackageDetailTabs.tsx +49 -0
  104. package/monodog-dashboard/src/components/modules/packages/components/PackageStats.tsx +70 -0
  105. package/monodog-dashboard/src/components/modules/packages/components/PackagesTable.tsx +163 -0
  106. package/monodog-dashboard/src/components/modules/packages/components/RecentCommitsTab.tsx +90 -0
  107. package/monodog-dashboard/src/components/modules/packages/components/SearchAndFilter.tsx +66 -0
  108. package/monodog-dashboard/src/components/modules/packages/components/index.ts +12 -0
  109. package/monodog-dashboard/src/components/modules/packages/types/packages.types.ts +101 -0
  110. package/monodog-dashboard/src/components/modules/packages/utils/packages.utils.tsx +178 -0
  111. package/monodog-dashboard/src/components/pipeline/JobsList.tsx +83 -0
  112. package/monodog-dashboard/src/components/pipeline/LogViewer.tsx +392 -0
  113. package/monodog-dashboard/src/components/pipeline/PipelineManager.tsx +562 -0
  114. package/monodog-dashboard/src/components/pipeline/WorkflowRunsList.tsx +272 -0
  115. package/monodog-dashboard/src/components/pipeline/WorkflowTrigger.tsx +182 -0
  116. package/monodog-dashboard/src/components/pipeline/utils/pipeline.utils.tsx +11 -0
  117. package/monodog-dashboard/src/components/publish-control/PublishControl.tsx +229 -0
  118. package/monodog-dashboard/src/components/publish-control/components/ChangelogViewer.tsx +103 -0
  119. package/monodog-dashboard/src/components/publish-control/components/ErrorState.tsx +23 -0
  120. package/monodog-dashboard/src/components/publish-control/components/LoadingState.tsx +10 -0
  121. package/monodog-dashboard/src/components/publish-control/components/PackageReleaseTable.tsx +140 -0
  122. package/monodog-dashboard/src/components/publish-control/components/PublishHeader.tsx +30 -0
  123. package/monodog-dashboard/src/components/publish-control/components/QuickActionCards.tsx +56 -0
  124. package/monodog-dashboard/src/components/publish-control/components/ReleaseSchedule.tsx +104 -0
  125. package/monodog-dashboard/src/components/publish-control/components/index.ts +8 -0
  126. package/monodog-dashboard/src/components/publish-control/types/publish.types.ts +39 -0
  127. package/monodog-dashboard/src/components/publish-control/utils/publish.utils.ts +59 -0
  128. package/monodog-dashboard/src/components/release-manager/ReleaseManager.tsx +342 -0
  129. package/monodog-dashboard/src/components/release-manager/components/ChangesetPreview.tsx +123 -0
  130. package/monodog-dashboard/src/components/release-manager/components/ErrorState.tsx +38 -0
  131. package/monodog-dashboard/src/components/release-manager/components/LoadingState.tsx +18 -0
  132. package/monodog-dashboard/src/components/release-manager/components/PackageSelector.tsx +137 -0
  133. package/monodog-dashboard/src/components/release-manager/components/PublishConfirmation.tsx +150 -0
  134. package/monodog-dashboard/src/components/release-manager/components/ReleaseValidation.tsx +138 -0
  135. package/monodog-dashboard/src/components/release-manager/components/VersionBumpSelector.tsx +142 -0
  136. package/monodog-dashboard/src/components/release-manager/types/index.ts +9 -0
  137. package/monodog-dashboard/src/components/release-manager/types/release-manager.types.ts +29 -0
  138. package/monodog-dashboard/src/components/setup-guide/SetupGuide.tsx +96 -0
  139. package/monodog-dashboard/src/components/setup-guide/components/SetupHeader.tsx +24 -0
  140. package/monodog-dashboard/src/components/setup-guide/components/SetupModal.tsx +24 -0
  141. package/monodog-dashboard/src/components/setup-guide/components/SetupProgress.tsx +29 -0
  142. package/monodog-dashboard/src/components/setup-guide/components/StepContent.tsx +272 -0
  143. package/monodog-dashboard/src/components/setup-guide/components/StepNavigation.tsx +62 -0
  144. package/monodog-dashboard/src/components/setup-guide/components/index.ts +6 -0
  145. package/monodog-dashboard/src/components/setup-guide/types/setup.types.ts +38 -0
  146. package/monodog-dashboard/src/components/setup-guide/utils/setup.utils.ts +79 -0
  147. package/monodog-dashboard/src/constants/api-config.ts +81 -0
  148. package/monodog-dashboard/src/constants/index.ts +9 -0
  149. package/monodog-dashboard/src/constants/messages.ts +40 -0
  150. package/monodog-dashboard/src/icons/heroicons.ts +65 -0
  151. package/monodog-dashboard/src/icons/index.tsx +1788 -0
  152. package/monodog-dashboard/src/index.css +175 -0
  153. package/monodog-dashboard/src/main.tsx +9 -0
  154. package/monodog-dashboard/src/pages/AuthCallbackPage.tsx +103 -0
  155. package/monodog-dashboard/src/pages/CIPage.tsx +6 -0
  156. package/monodog-dashboard/src/pages/ConfigPage.tsx +6 -0
  157. package/monodog-dashboard/src/pages/DashboardPage.tsx +6 -0
  158. package/monodog-dashboard/src/pages/DependenciesPage.tsx +6 -0
  159. package/monodog-dashboard/src/pages/HealthPage.tsx +6 -0
  160. package/monodog-dashboard/src/pages/LoginPage.tsx +77 -0
  161. package/monodog-dashboard/src/pages/PackageDetailPage.tsx +6 -0
  162. package/monodog-dashboard/src/pages/PackagesPage.tsx +6 -0
  163. package/monodog-dashboard/src/pages/PipelinePage.tsx +22 -0
  164. package/monodog-dashboard/src/pages/PublishPage.tsx +6 -0
  165. package/monodog-dashboard/src/pages/ReleaseManagerPage.tsx +9 -0
  166. package/monodog-dashboard/src/pages/index.ts +28 -0
  167. package/monodog-dashboard/src/routes/AppRouter.tsx +89 -0
  168. package/monodog-dashboard/src/routes/AppRouterPages.tsx +56 -0
  169. package/monodog-dashboard/src/routes/index.ts +10 -0
  170. package/monodog-dashboard/src/routes/routes.config.ts +94 -0
  171. package/monodog-dashboard/src/services/api/api-client.ts +189 -0
  172. package/monodog-dashboard/src/services/api/index.ts +30 -0
  173. package/monodog-dashboard/src/services/api/types/api.types.ts +63 -0
  174. package/monodog-dashboard/src/services/auth-context.tsx +344 -0
  175. package/monodog-dashboard/src/services/monorepoService.ts +733 -0
  176. package/monodog-dashboard/src/services/permission-context.tsx +193 -0
  177. package/monodog-dashboard/src/theme/COMPONENT_UPDATES.md +273 -0
  178. package/monodog-dashboard/src/theme/INTEGRATION.md +381 -0
  179. package/monodog-dashboard/src/theme/README.md +239 -0
  180. package/monodog-dashboard/src/theme/examples.tsx +386 -0
  181. package/monodog-dashboard/src/theme/index.ts +402 -0
  182. package/monodog-dashboard/src/theme/migration-guide.md +335 -0
  183. package/monodog-dashboard/src/types/auth-context.types.ts +51 -0
  184. package/monodog-dashboard/src/types/component.types.ts +96 -0
  185. package/monodog-dashboard/src/types/icons.types.ts +13 -0
  186. package/monodog-dashboard/src/types/index.ts +68 -0
  187. package/monodog-dashboard/src/types/monorepo-service.types.ts +53 -0
  188. package/monodog-dashboard/src/types/permission-context.types.ts +49 -0
  189. package/monodog-dashboard/src/types/pipeline.types.ts +89 -0
  190. package/monodog-dashboard/src/types/routes.types.ts +12 -0
  191. package/monodog-dashboard/src/utils/cookies.ts +47 -0
  192. package/monodog-dashboard/tailwind.config.js +68 -0
  193. package/monodog-dashboard/tsconfig.app.json +14 -0
  194. package/monodog-dashboard/tsconfig.json +17 -0
  195. package/monodog-dashboard/tsconfig.node.json +10 -0
  196. package/monodog-dashboard/vite.config.ts +9 -0
  197. package/package.json +2 -2
@@ -0,0 +1,475 @@
1
+ /**
2
+ * API Client Tests
3
+ * Tests for ApiClient class: request handling, error handling, retries, interceptors
4
+ */
5
+
6
+ import axios from 'axios';
7
+ import ApiClient from '../src/services/api/api-client';
8
+ import { cookieUtils } from '../src/utils/cookies';
9
+
10
+ // Mock axios
11
+ jest.mock('axios');
12
+
13
+ describe('ApiClient', () => {
14
+ let mockAxiosInstance: Record<string, jest.Mock|jest.MockedFunction<any>>;
15
+ let apiClient: ApiClient;
16
+
17
+ beforeEach(() => {
18
+ jest.clearAllMocks();
19
+
20
+ // Mock axios.create
21
+ mockAxiosInstance = {
22
+ request: jest.fn(),
23
+ interceptors: {
24
+ request: { use: jest.fn() },
25
+ response: { use: jest.fn() },
26
+ },
27
+ };
28
+
29
+ (axios.create as jest.Mock).mockReturnValue(mockAxiosInstance);
30
+ // cast to unknown first to satisfy TS
31
+ ((axios.isAxiosError as unknown) as jest.Mock).mockImplementation(error => error && error.response);
32
+
33
+ apiClient = new ApiClient({ baseUrl: 'http://localhost:8999' });
34
+ });
35
+
36
+ describe('Constructor and Configuration', () => {
37
+ it('should create instance with default config', () => {
38
+ expect(axios.create).toHaveBeenCalledWith(
39
+ expect.objectContaining({
40
+ baseURL: 'http://localhost:8999',
41
+ timeout: 30000,
42
+ headers: expect.objectContaining({
43
+ 'Content-Type': 'application/json',
44
+ }),
45
+ withCredentials: true,
46
+ })
47
+ );
48
+ });
49
+
50
+ it('should create instance with custom config', () => {
51
+ jest.clearAllMocks();
52
+ mockAxiosInstance = {
53
+ request: jest.fn(),
54
+ interceptors: {
55
+ request: { use: jest.fn() },
56
+ response: { use: jest.fn() },
57
+ },
58
+ };
59
+ (axios.create as jest.Mock).mockReturnValue(mockAxiosInstance);
60
+
61
+ const customClient = new ApiClient({
62
+ baseUrl: 'http://api.example.com',
63
+ timeout: 60000,
64
+ headers: { Authorization: 'Bearer token' },
65
+ });
66
+
67
+ expect(axios.create).toHaveBeenCalledWith(
68
+ expect.objectContaining({
69
+ baseURL: 'http://api.example.com',
70
+ timeout: 60000,
71
+ headers: expect.objectContaining({
72
+ 'Content-Type': 'application/json',
73
+ Authorization: 'Bearer token',
74
+ }),
75
+ })
76
+ );
77
+ });
78
+ });
79
+
80
+ describe('Authentication handling', () => {
81
+ it('registers request interceptor that adds Authorization header', () => {
82
+ const token = 'abc123';
83
+ jest.spyOn(cookieUtils, 'get').mockReturnValue(token);
84
+
85
+ // ensure interceptor was attached
86
+ expect(mockAxiosInstance.interceptors.request.use).toHaveBeenCalled();
87
+ const interceptorFn = mockAxiosInstance.interceptors.request.use.mock.calls[0][0];
88
+ const cfg = interceptorFn({ headers: {} });
89
+ expect(cfg.headers).toEqual(expect.objectContaining({ Authorization: `Bearer ${token}` }));
90
+ });
91
+
92
+ it('registers response interceptor that clears cookies on 401 errors', async () => {
93
+ jest.spyOn(cookieUtils, 'get').mockReturnValue('token');
94
+ const removeSpy = jest.spyOn(cookieUtils, 'remove');
95
+
96
+ // confirm interceptor attached
97
+ expect(mockAxiosInstance.interceptors.response.use).toHaveBeenCalled();
98
+ const errorHandler = mockAxiosInstance.interceptors.response.use.mock.calls[0][1];
99
+
100
+ const fakeError = { response: { status: 401 } };
101
+ await errorHandler(fakeError).catch(() => {});
102
+
103
+ expect(removeSpy).toHaveBeenCalledWith('monodog_session_token');
104
+ expect(removeSpy).toHaveBeenCalledWith('monodog_session_data');
105
+ });
106
+ });
107
+
108
+ describe('GET Request', () => {
109
+ it('should make successful GET request', async () => {
110
+ const mockResponse = {
111
+ data: { id: 1, name: 'Test' },
112
+ status: 200,
113
+ statusText: 'OK',
114
+ headers: {},
115
+ };
116
+
117
+ mockAxiosInstance.request.mockResolvedValue(mockResponse);
118
+
119
+ const result = await apiClient.get('/test');
120
+
121
+ expect(result.success).toBe(true);
122
+ expect((result).data).toEqual({ id: 1, name: 'Test' });
123
+ expect(mockAxiosInstance.request).toHaveBeenCalledWith(
124
+ expect.objectContaining({
125
+ method: 'get',
126
+ url: '/api/test',
127
+ })
128
+ );
129
+ });
130
+
131
+ it('should include request config in GET request', async () => {
132
+ mockAxiosInstance.request.mockResolvedValue({
133
+ data: {},
134
+ status: 200,
135
+ statusText: 'OK',
136
+ headers: {},
137
+ });
138
+
139
+ await apiClient.get('/test', {
140
+ headers: { 'X-Custom': 'value' },
141
+ timeout: 5000,
142
+ });
143
+
144
+ expect(mockAxiosInstance.request).toHaveBeenCalledWith(
145
+ expect.objectContaining({
146
+ method: 'get',
147
+ headers: { 'X-Custom': 'value' },
148
+ timeout: 5000,
149
+ })
150
+ );
151
+ });
152
+ });
153
+
154
+ describe('POST Request', () => {
155
+ it('should make successful POST request with body', async () => {
156
+ const mockResponse = {
157
+ data: { id: 1, name: 'Created' },
158
+ status: 201,
159
+ statusText: 'Created',
160
+ headers: {},
161
+ };
162
+
163
+ mockAxiosInstance.request.mockResolvedValue(mockResponse);
164
+
165
+ const result = await apiClient.post('/test', { name: 'Test' });
166
+
167
+ expect(result.success).toBe(true);
168
+ expect((result).data).toEqual({ id: 1, name: 'Created' });
169
+ expect(result.meta?.status).toBe(201);
170
+ expect(mockAxiosInstance.request).toHaveBeenCalledWith(
171
+ expect.objectContaining({
172
+ method: 'post',
173
+ url: '/api/test',
174
+ data: { name: 'Test' },
175
+ })
176
+ );
177
+ });
178
+
179
+ it('should post without body', async () => {
180
+ mockAxiosInstance.request.mockResolvedValue({
181
+ data: {},
182
+ status: 200,
183
+ statusText: 'OK',
184
+ headers: {},
185
+ });
186
+
187
+ await apiClient.post('/test');
188
+
189
+ expect(mockAxiosInstance.request).toHaveBeenCalledWith(
190
+ expect.objectContaining({
191
+ method: 'post',
192
+ url: '/api/test',
193
+ data: undefined,
194
+ })
195
+ );
196
+ });
197
+ });
198
+
199
+ describe('PUT Request', () => {
200
+ it('should make successful PUT request', async () => {
201
+ mockAxiosInstance.request.mockResolvedValue({
202
+ data: { id: 1, name: 'Updated' },
203
+ status: 200,
204
+ statusText: 'OK',
205
+ headers: {},
206
+ });
207
+
208
+ const result = await apiClient.put('/test/1', { name: 'Updated' });
209
+
210
+ expect(result.success).toBe(true);
211
+ expect(mockAxiosInstance.request).toHaveBeenCalledWith(
212
+ expect.objectContaining({
213
+ method: 'put',
214
+ url: '/api/test/1',
215
+ data: { name: 'Updated' },
216
+ })
217
+ );
218
+ });
219
+ });
220
+
221
+ describe('DELETE Request', () => {
222
+ it('should make successful DELETE request', async () => {
223
+ mockAxiosInstance.request.mockResolvedValue({
224
+ data: { success: true },
225
+ status: 200,
226
+ statusText: 'OK',
227
+ headers: {},
228
+ });
229
+
230
+ const result = await apiClient.delete('/test/1');
231
+
232
+ expect(result.success).toBe(true);
233
+ expect(mockAxiosInstance.request).toHaveBeenCalledWith(
234
+ expect.objectContaining({
235
+ method: 'delete',
236
+ url: '/api/test/1',
237
+ })
238
+ );
239
+ });
240
+ });
241
+
242
+ describe('PATCH Request', () => {
243
+ it('should make successful PATCH request', async () => {
244
+ mockAxiosInstance.request.mockResolvedValue({
245
+ data: { id: 1, status: 'active' },
246
+ status: 200,
247
+ statusText: 'OK',
248
+ headers: {},
249
+ });
250
+
251
+ const result = await apiClient.patch('/test/1', { status: 'active' });
252
+
253
+ expect(result.success).toBe(true);
254
+ expect(mockAxiosInstance.request).toHaveBeenCalledWith(
255
+ expect.objectContaining({
256
+ method: 'patch',
257
+ url: '/api/test/1',
258
+ data: { status: 'active' },
259
+ })
260
+ );
261
+ });
262
+ });
263
+
264
+ describe('Error Handling', () => {
265
+ it('should handle 400 validation error', async () => {
266
+ const error = {
267
+ response: {
268
+ status: 400,
269
+ statusText: 'Bad Request',
270
+ data: { error: 'Invalid input' },
271
+ headers: {},
272
+ },
273
+ config: {},
274
+ };
275
+
276
+ mockAxiosInstance.request.mockRejectedValue(error);
277
+
278
+ const result = await apiClient.get('/test');
279
+
280
+ expect(result.success).toBe(false);
281
+ expect((result).error?.code).toMatch(/VALIDATION/i);
282
+ expect((result).error?.status).toBe(400);
283
+ });
284
+
285
+ it('should handle 401 unauthorized error', async () => {
286
+ const error = {
287
+ response: {
288
+ status: 401,
289
+ statusText: 'Unauthorized',
290
+ headers: {},
291
+ },
292
+ config: {},
293
+ };
294
+
295
+ mockAxiosInstance.request.mockRejectedValue(error);
296
+
297
+ const result = await apiClient.get('/test');
298
+
299
+ expect(result.success).toBe(false);
300
+ expect((result).error?.code).toMatch(/UNAUTHORIZED/i);
301
+ expect((result).error?.status).toBe(401);
302
+ });
303
+
304
+ it('should handle 403 forbidden error', async () => {
305
+ const error = {
306
+ response: {
307
+ status: 403,
308
+ statusText: 'Forbidden',
309
+ headers: {},
310
+ },
311
+ config: {},
312
+ };
313
+
314
+ mockAxiosInstance.request.mockRejectedValue(error);
315
+
316
+ const result = await apiClient.get('/test');
317
+
318
+ expect(result.success).toBe(false);
319
+ expect((result).error?.code).toMatch(/FORBIDDEN/i);
320
+ expect((result).error?.status).toBe(403);
321
+ });
322
+
323
+ it('should handle 404 not found error', async () => {
324
+ const error = {
325
+ response: {
326
+ status: 404,
327
+ statusText: 'Not Found',
328
+ headers: {},
329
+ },
330
+ config: {},
331
+ };
332
+
333
+ mockAxiosInstance.request.mockRejectedValue(error);
334
+
335
+ const result = await apiClient.get('/test');
336
+
337
+ expect(result.success).toBe(false);
338
+ expect((result).error?.code).toMatch(/NOT_FOUND/i);
339
+ expect((result).error?.status).toBe(404);
340
+ });
341
+
342
+ it('should handle 408 timeout error', async () => {
343
+ const error = {
344
+ response: {
345
+ status: 408,
346
+ statusText: 'Request Timeout',
347
+ headers: {},
348
+ },
349
+ config: {},
350
+ };
351
+
352
+ mockAxiosInstance.request.mockRejectedValue(error);
353
+
354
+ const result = await apiClient.get('/test');
355
+
356
+ expect(result.success).toBe(false);
357
+ expect((result).error?.code).toMatch(/TIMEOUT/i);
358
+ expect((result).error?.status).toBe(408);
359
+ });
360
+
361
+ it('should handle 429 rate limit error', async () => {
362
+ const error = {
363
+ response: {
364
+ status: 429,
365
+ statusText: 'Too Many Requests',
366
+ headers: {},
367
+ },
368
+ config: {},
369
+ };
370
+
371
+ mockAxiosInstance.request.mockRejectedValue(error);
372
+
373
+ const result = await apiClient.get('/test');
374
+
375
+ expect(result.success).toBe(false);
376
+ expect((result).error?.code).toMatch(/RATE_LIMIT/i);
377
+ expect((result).error?.status).toBe(429);
378
+ });
379
+
380
+ it('should handle 500 server error', async () => {
381
+ const error = {
382
+ response: {
383
+ status: 500,
384
+ statusText: 'Internal Server Error',
385
+ headers: {},
386
+ },
387
+ config: {},
388
+ };
389
+
390
+ mockAxiosInstance.request.mockRejectedValue(error);
391
+
392
+ const result = await apiClient.get('/test');
393
+
394
+ expect(result.success).toBe(false);
395
+ expect((result).error?.code).toMatch(/SERVER/i);
396
+ expect((result).error?.status).toBe(500);
397
+ });
398
+
399
+ it('should handle unknown non-axios errors', async () => {
400
+ const error = new Error('Network error');
401
+ ((axios.isAxiosError as unknown) as jest.Mock).mockReturnValue(false);
402
+
403
+ mockAxiosInstance.request.mockRejectedValue(error);
404
+
405
+ const result = await apiClient.get('/test');
406
+
407
+ expect(result.success).toBe(false);
408
+ expect((result).error?.code).toMatch(/UNKNOWN/i);
409
+ expect((result).error?.message).toBe('Network error');
410
+ });
411
+ });
412
+
413
+ describe('Response Metadata', () => {
414
+ it('should include timestamp and duration in response', async () => {
415
+ mockAxiosInstance.request.mockResolvedValue({
416
+ data: { test: true },
417
+ status: 200,
418
+ statusText: 'OK',
419
+ headers: { 'content-type': 'application/json' },
420
+ });
421
+
422
+ const result = await apiClient.get('/test');
423
+
424
+ expect(result.meta).toBeDefined();
425
+ expect(result.meta?.status).toBe(200);
426
+ expect(result.meta?.statusText).toBe('OK');
427
+ expect(result.meta?.timestamp).toBeDefined();
428
+ expect(result.meta?.duration).toBeDefined();
429
+ expect(typeof result.meta?.duration).toBe('number');
430
+ });
431
+
432
+ it('should include headers in response metadata', async () => {
433
+ mockAxiosInstance.request.mockResolvedValue({
434
+ data: {},
435
+ status: 200,
436
+ statusText: 'OK',
437
+ headers: { 'x-custom': 'value' },
438
+ });
439
+
440
+ const result = await apiClient.get('/test');
441
+
442
+ expect(result.meta?.headers).toEqual(expect.objectContaining({ 'x-custom': 'value' }));
443
+ });
444
+ });
445
+
446
+
447
+ describe('Endpoint Path Construction', () => {
448
+ it('should prepend /api to all endpoint paths', async () => {
449
+ mockAxiosInstance.request.mockResolvedValue({
450
+ data: {},
451
+ status: 200,
452
+ statusText: 'OK',
453
+ headers: {},
454
+ });
455
+
456
+ await apiClient.get('/packages');
457
+ expect(mockAxiosInstance.request).toHaveBeenCalledWith(
458
+ expect.objectContaining({ url: '/api/packages' })
459
+ );
460
+
461
+ jest.clearAllMocks();
462
+ mockAxiosInstance.request.mockResolvedValue({
463
+ data: {},
464
+ status: 200,
465
+ statusText: 'OK',
466
+ headers: {},
467
+ });
468
+
469
+ await apiClient.get('/auth/login');
470
+ expect(mockAxiosInstance.request).toHaveBeenCalledWith(
471
+ expect.objectContaining({ url: '/api/auth/login' })
472
+ );
473
+ });
474
+ });
475
+ });
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Tests for dashboard component utilities and structure.
3
+ * These are basic unit tests that verify components can be imported and have expected structure.
4
+ */
5
+
6
+ // Mock all components before using them
7
+ jest.mock('../src/components/modules/packages/components/ConfigurationTab', () => ({
8
+ __esModule: true,
9
+ default: jest.fn(() => null),
10
+ }));
11
+
12
+ jest.mock('../src/components/modules/health-status/HealthStatus', () => ({
13
+ __esModule: true,
14
+ default: jest.fn(() => null),
15
+ }));
16
+
17
+ jest.mock('../src/components/modules/packages/PackagesOverview', () => ({
18
+ __esModule: true,
19
+ default: jest.fn(() => null),
20
+ }));
21
+
22
+ jest.mock('../src/components/modules/packages/PackageDetail', () => ({
23
+ __esModule: true,
24
+ default: jest.fn(() => null),
25
+ }));
26
+
27
+ jest.mock('../src/components/modules/dependency-graph/DependencyGraph', () => ({
28
+ __esModule: true,
29
+ default: jest.fn(() => null),
30
+ }));
31
+
32
+ jest.mock('../src/components/modules/ci-integration/CIIntegration', () => ({
33
+ __esModule: true,
34
+ default: jest.fn(() => null),
35
+ }));
36
+
37
+ jest.mock('../src/components/modules/config-inspector/ConfigInspector', () => ({
38
+ __esModule: true,
39
+ default: jest.fn(() => null),
40
+ }));
41
+
42
+ jest.mock('../src/components/publish-control/PublishControl', () => ({
43
+ __esModule: true,
44
+ default: jest.fn(() => null),
45
+ }));
46
+
47
+ import ConfigurationTab from '../src/components/modules/packages/components/ConfigurationTab';
48
+ import HealthStatus from '../src/components/modules/health-status/HealthStatus';
49
+ import PackagesOverview from '../src/components/modules/packages/PackagesOverview';
50
+ import PackageDetail from '../src/components/modules/packages/PackageDetail';
51
+ import DependencyGraph from '../src/components/modules/dependency-graph/DependencyGraph';
52
+ import CIIntegration from '../src/components/modules/ci-integration/CIIntegration';
53
+ import ConfigInspector from '../src/components/modules/config-inspector/ConfigInspector';
54
+ import PublishControl from '../src/components/publish-control/PublishControl';
55
+
56
+ describe('Dashboard components exist', () => {
57
+ test('ConfigurationTab can be imported', () => {
58
+ // This verifies the file exists and can be imported
59
+ expect(ConfigurationTab).toBeDefined();
60
+ expect(typeof ConfigurationTab).toBe('function');
61
+ });
62
+
63
+ test('HealthStatus can be imported', () => {
64
+ expect(HealthStatus).toBeDefined();
65
+ expect(typeof HealthStatus).toBe('function');
66
+ });
67
+
68
+ test('PackagesOverview can be imported', () => {
69
+ expect(PackagesOverview).toBeDefined();
70
+ expect(typeof PackagesOverview).toBe('function');
71
+ });
72
+
73
+ test('PackageDetail can be imported', () => {
74
+ expect(PackageDetail).toBeDefined();
75
+ expect(typeof PackageDetail).toBe('function');
76
+ });
77
+
78
+ test('DependencyGraph can be imported', () => {
79
+ expect(DependencyGraph).toBeDefined();
80
+ expect(typeof DependencyGraph).toBe('function');
81
+ });
82
+
83
+ test('CIIntegration can be imported', () => {
84
+ expect(CIIntegration).toBeDefined();
85
+ expect(typeof CIIntegration).toBe('function');
86
+ });
87
+
88
+ test('ConfigInspector can be imported', () => {
89
+ expect(ConfigInspector).toBeDefined();
90
+ expect(typeof ConfigInspector).toBe('function');
91
+ });
92
+
93
+ test('PublishControl can be imported', () => {
94
+ expect(PublishControl).toBeDefined();
95
+ expect(typeof PublishControl).toBe('function');
96
+ });
97
+ });
98
+
99
+ describe('Dashboard components are React components', () => {
100
+ test('components are functions that can be called', () => {
101
+ const components = [
102
+ ConfigurationTab,
103
+ HealthStatus,
104
+ PackagesOverview,
105
+ PackageDetail,
106
+ DependencyGraph,
107
+ CIIntegration,
108
+ ConfigInspector,
109
+ PublishControl,
110
+ ];
111
+
112
+ components.forEach((component: any) => {
113
+ expect(typeof component).toBe('function');
114
+ expect(component).toBeDefined();
115
+ });
116
+ });
117
+ });