diff --git a/TestMasterUI/package.json b/TestMasterUI/package.json
index 70817c3..ed7ff7d 100644
--- a/TestMasterUI/package.json
+++ b/TestMasterUI/package.json
@@ -53,7 +53,6 @@
"globals": "^15.14.0",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.17",
- "vite": "^6.0.5",
- "vite-plugin-monaco-editor": "^1.1.0"
+ "vite": "^6.0.5"
}
}
diff --git a/TestMasterUI/src/App.jsx b/TestMasterUI/src/App.jsx
index 5253cea..12a234f 100644
--- a/TestMasterUI/src/App.jsx
+++ b/TestMasterUI/src/App.jsx
@@ -1,16 +1,15 @@
-import React, {useEffect, useState} from 'react';
-import {APIProvider, useAPI} from './providers/APIProvider';
-import {LoadTestProvider} from './providers/LoadTestProvider';
-import APITester from './components/APITester';
-import LoadTestManager from './components/LoadTestManager';
-import DialogManager from './components/DialogManager';
-import {monacoConfig} from '@/config/monaco.js';
-import LoginDialog from '@/components/LoginDialog.jsx';
-import {Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarTrigger} from '@/components/ui/menubar.jsx';
+import React, { useEffect, useState } from 'react';
+import { APIProvider, useAPI } from './providers/APIProvider';
+import { LoadTestProvider } from './providers/LoadTestProvider';
+import DialogManager from './components/shared/DialogManager';
+import { monacoConfig } from '@/config/monaco.js';
+import LoginDialog from '@/components/account/LoginDialog.jsx';
+import AppMenu from './components/app-menu/AppMenu';
+import { SCREENS } from './config/screenConfig';
window.MonacoEnvironment = monacoConfig;
-const App = ({ options = { type: 'api', contextPath: '/' } }) => {
+const App = ({ options = { contextPath: '/' } }) => {
const [showLogin, setShowLogin] = useState(false);
return (
@@ -20,102 +19,50 @@ const App = ({ options = { type: 'api', contextPath: '/' } }) => {
);
};
-const menuConfig = [
- {
- id: 'response',
- label: '응답관리',
- items: [
- {id: 'response-list', label: '응답목록', action: 'response-list'},
- {id: 'response-add', label: '응답추가', action: 'response-add'}
- ]
- },
- {
- id: 'request',
- label: '요청관리',
- items: [
- {id: 'collection-add', label: '컬렉션 추가', action: 'collection-add'},
- {id: 'request-add', label: '요청 추가', action: 'request-add'},
- {id: 'scenario-add', label: '시나리오 추가', action: 'scenario-add'},
- {id: 'variables-show', label: '변수 보기', action: 'variables-show'},
- ]
- },
- {
- id: 'queue',
- label: '큐관리',
- items: [
- {id: 'queue-status', label: '큐상태', action: 'queue-status'},
- {id: 'queue-settings', label: '큐설정', action: 'queue-settings'}
- ]
- },
- {
- id: 'loadtest',
- label: '부하테스트',
- items: [
- {id: 'load-test-new', label: '새 테스트', action: 'load-test-new'},
- {id: 'load-test-history', label: '테스트 이력', action: 'load-test-history'},
- {id: 'load-test-reports', label: '결과 리포트', action: 'load-test-reports'}
- ]
- },
- {
- id: 'system',
- label: '시스템관리',
- items: [
- {id: 'system-settings', label: '시스템설정', action: 'system-settings'},
- {id: 'user-management', label: '사용자관리', action: 'user-management'},
- {type: 'separator'},
- {id: 'system-logs', label: '시스템로그', action: 'system-logs'}
- ]
- },
- {
- id: 'account',
- label: '계정',
- items: [
- {id: 'change-password', label: '비밀번호변경', action: 'change-password'},
- {type: 'separator'},
- {id: 'logout', label: '로그아웃', action: 'logout'}
- ]
- }
-];
-
-const MenuItem = ({item, onMenuClick}) => {
- if (item.type === 'separator') {
- return ;
- }
-
- return (
- onMenuClick(item.action)}>
- {item.label}
-
- );
-};
-
const AppContent = ({ options, showLogin, setShowLogin }) => {
const { isAuthenticated, setIsAuthenticated, handleLogout } = useAPI();
const [isInitialized, setIsInitialized] = useState(false);
+ const [currentScreen, setCurrentScreen] = useState('api-tester');
const [dialogState, setDialogState] = useState({
showAddApi: false,
showAddCollection: false,
showAddScenario: false,
- showVariableModal: false
+ showVariableModal: false,
+ showMockRouteEdit: false,
+ selectedItem: null,
});
- const handleMenuClick = async (action) => {
+ const handleMenuClick = async (action, screen, item = null) => {
- console.log('Menu action:', action);
+ if (screen) {
+ console.log('Screen:', screen);
+ setCurrentScreen(screen);
+ return;
+ }
+
+ console.log(item);
+ // Handle modal actions
switch (action) {
case 'request-add':
setDialogState(prev => ({ ...prev, showAddApi: true }));
break;
+ case 'response-add':
+ case 'response-edit':
+ case 'response-clone':
+ setDialogState(prev => ({
+ ...prev,
+ showMockRouteEdit: true,
+ selectedItem: item // Pass the selected item
+ }));
+ break;
case 'collection-add':
setDialogState(prev => ({ ...prev, showAddCollection: true }));
break;
case 'scenario-add':
setDialogState(prev => ({ ...prev, showAddScenario: true }));
-
break;
case 'variables-show':
setDialogState(prev => ({ ...prev, showVariableModal: true }));
-
break;
case 'logout':
const success = await handleLogout();
@@ -123,10 +70,6 @@ const AppContent = ({ options, showLogin, setShowLogin }) => {
window.location.href = "/actionLogout.do";
}
break;
- case 'change-password':
- // Handle password change
- break;
- // Add other cases as needed
}
};
@@ -167,53 +110,27 @@ const AppContent = ({ options, showLogin, setShowLogin }) => {
return ;
}
+ const CurrentScreenComponent = SCREENS[currentScreen] || SCREENS['api-tester'];
+
return (
-
-
-
-

-
-
- {menuConfig.map((menu) => (
-
-
- {menu.label}
-
-
- {menu.items.map((item, index) => (
-
- ))}
-
-
- ))}
-
-
-
+
- {options.type === 'api' ? (
-
- ) : (
-
- )}
+
+
+
);
};
-export default App
+export default App;
diff --git a/TestMasterUI/src/components/LoginDialog.jsx b/TestMasterUI/src/components/account/LoginDialog.jsx
similarity index 100%
rename from TestMasterUI/src/components/LoginDialog.jsx
rename to TestMasterUI/src/components/account/LoginDialog.jsx
diff --git a/TestMasterUI/src/components/account/PasswordEditDialog.jsx b/TestMasterUI/src/components/account/PasswordEditDialog.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/TestMasterUI/src/components/APITester.jsx b/TestMasterUI/src/components/api-tester/APITester.jsx
similarity index 83%
rename from TestMasterUI/src/components/APITester.jsx
rename to TestMasterUI/src/components/api-tester/APITester.jsx
index 58beed8..c61ba05 100644
--- a/TestMasterUI/src/components/APITester.jsx
+++ b/TestMasterUI/src/components/api-tester/APITester.jsx
@@ -1,5 +1,5 @@
import React, {useState} from 'react';
-import MainContentArea from '@/components/MainContentArea.jsx';
+import MainContentArea from '@/components/api-tester/MainContentArea.jsx';
const APITester = () => {
// API 모드와 시나리오 모드 상태를 관리
@@ -13,7 +13,7 @@ const APITester = () => {
return (
{/* Main content area with top padding for menubar */}
-
diff --git a/TestMasterUI/src/components/api-tester/AddAPIDialog.jsx b/TestMasterUI/src/components/api-tester/AddAPIDialog.jsx
index 3c7f639..dd8cd41 100644
--- a/TestMasterUI/src/components/api-tester/AddAPIDialog.jsx
+++ b/TestMasterUI/src/components/api-tester/AddAPIDialog.jsx
@@ -7,8 +7,8 @@ import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/c
import ErrorAlertDialog from '@/components/shared/ErrorAlertDialog';
import {useAPI} from '@/providers/APIProvider';
-const AddAPIDialog = ({open, onOpenChange, onSubmit, selectedCollection}) => {
- const {collections, loadCollections} = useAPI();
+const AddAPIDialog = ({open, onOpenChange, selectedCollection}) => {
+ const {collections, loadCollections, createAPIRequest} = useAPI();
const [formData, setFormData] = useState({
name: '',
type: '',
@@ -19,6 +19,15 @@ const AddAPIDialog = ({open, onOpenChange, onSubmit, selectedCollection}) => {
const [showErrorDialog, setShowErrorDialog] = useState(false);
const [isInitialized, setIsInitialized] = useState(false);
+ const handleAddApi = async (collectionId, apiData) => {
+ try {
+ await createAPIRequest(collectionId, apiData);
+ return true;
+ } catch (error) {
+ console.error('Failed to create API:', error);
+ return false;
+ }
+ };
useEffect(() => {
if (selectedCollection?.id) {
@@ -56,7 +65,7 @@ const AddAPIDialog = ({open, onOpenChange, onSubmit, selectedCollection}) => {
setIsSubmitting(true);
try {
- const success = await onSubmit(formData.collectionId, {
+ const success = await handleAddApi(formData.collectionId, {
name: formData.name.trim(),
type: formData.type
});
diff --git a/TestMasterUI/src/components/api-tester/AddCollectionDialog.jsx b/TestMasterUI/src/components/api-tester/AddCollectionDialog.jsx
index fb0f161..cfc4b00 100644
--- a/TestMasterUI/src/components/api-tester/AddCollectionDialog.jsx
+++ b/TestMasterUI/src/components/api-tester/AddCollectionDialog.jsx
@@ -1,23 +1,26 @@
-// /components/AddCollectionDialog.jsx
-import React, { useState } from 'react';
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
-} from '@/components/ui/dialog';
-import { Button } from '@/components/ui/button';
-import { Input } from '@/components/ui/input';
-import { Label } from '@/components/ui/label';
+import React, {useState} from 'react';
+import {Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle} from '@/components/ui/dialog';
+import {Button} from '@/components/ui/button';
+import {Input} from '@/components/ui/input';
+import {Label} from '@/components/ui/label';
import ErrorAlertDialog from '@/components/shared/ErrorAlertDialog.jsx';
+import {useAPI} from '@/providers/APIProvider.jsx';
-const AddCollectionDialog = ({ open, onOpenChange, onSubmit }) => {
+const AddCollectionDialog = ({ open, onOpenChange}) => {
const [collectionName, setCollectionName] = useState('');
const [error, setError] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const [showErrorDialog, setShowErrorDialog] = useState(false);
+ const {createCollection} = useAPI();
+
+ const handleAddCollection = async (name) => {
+ try {
+ await createCollection(name);
+ return true;
+ } catch (error) {
+ throw new Error('컬렉션 생성에 실패했습니다');
+ }
+ };
const handleSubmit = async (e) => {
e.preventDefault();
@@ -29,7 +32,7 @@ const AddCollectionDialog = ({ open, onOpenChange, onSubmit }) => {
setIsSubmitting(true);
try {
- const success = await onSubmit(collectionName.trim());
+ const success = await handleAddCollection(collectionName.trim());
if (success) {
setCollectionName('');
setError('');
diff --git a/TestMasterUI/src/components/MainContentArea.jsx b/TestMasterUI/src/components/api-tester/MainContentArea.jsx
similarity index 100%
rename from TestMasterUI/src/components/MainContentArea.jsx
rename to TestMasterUI/src/components/api-tester/MainContentArea.jsx
diff --git a/TestMasterUI/src/components/app-menu/AppMenu.jsx b/TestMasterUI/src/components/app-menu/AppMenu.jsx
new file mode 100644
index 0000000..2d45883
--- /dev/null
+++ b/TestMasterUI/src/components/app-menu/AppMenu.jsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import { Menubar, MenubarContent, MenubarMenu, MenubarTrigger } from '@/components/ui/menubar';
+import MenuItem from './MenuItem';
+import { menuConfig } from '@/config/menuConfig';
+
+const AppMenu = ({ onMenuClick }) => {
+ return (
+
+
+
+

+
+
+ {menuConfig.map((menu) => (
+
+
+ {menu.label}
+
+
+ {menu.items.map((item, index) => (
+
+ ))}
+
+
+ ))}
+
+
+
+ );
+};
+
+export default AppMenu;
diff --git a/TestMasterUI/src/components/app-menu/MenuItem.jsx b/TestMasterUI/src/components/app-menu/MenuItem.jsx
new file mode 100644
index 0000000..c896557
--- /dev/null
+++ b/TestMasterUI/src/components/app-menu/MenuItem.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { MenubarItem, MenubarSeparator } from '@/components/ui/menubar';
+
+const MenuItem = ({ item, onMenuClick }) => {
+ if (item.type === 'separator') {
+ return
;
+ }
+
+ return (
+
onMenuClick(item.action, item.screen)}>
+ {item.label}
+
+ );
+};
+
+export default MenuItem;
diff --git a/TestMasterUI/src/components/client-views/HTTPClientView.jsx b/TestMasterUI/src/components/client-views/HTTPClientView.jsx
index 79169fa..a3c75f2 100644
--- a/TestMasterUI/src/components/client-views/HTTPClientView.jsx
+++ b/TestMasterUI/src/components/client-views/HTTPClientView.jsx
@@ -5,7 +5,7 @@ import {Input} from '@/components/ui/input';
import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs';
import EditableInput from '../shared/EditableInput';
import PropertyTable from '../shared/PropertyTable';
-import ServerManager from '@/components/ServerManager.jsx';
+import ServerManager from '@/components/shared/ServerManager';
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from '@/components/ui/table.jsx';
import {useAPI} from '@/providers/APIProvider';
import MonacoEditor from '@/components/shared/MonacoEditor';
diff --git a/TestMasterUI/src/components/client-views/TCPClientView.jsx b/TestMasterUI/src/components/client-views/TCPClientView.jsx
index b9d696a..4938bbb 100644
--- a/TestMasterUI/src/components/client-views/TCPClientView.jsx
+++ b/TestMasterUI/src/components/client-views/TCPClientView.jsx
@@ -1,10 +1,10 @@
import React, {useCallback, useEffect, useState} from 'react';
import {Button} from '@/components/ui/button';
import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs';
-import ServerManager from '@/components/ServerManager.jsx';
+import ServerManager from '@/components/shared/ServerManager';
import {useAPI} from '@/providers/APIProvider';
import MonacoEditor from '@/components/shared/MonacoEditor';
-import APIClient from '@/services/APIClient.js';
+import APIClient from '@/services/APIClient';
import {SidebarTrigger} from '@/components/ui/sidebar.jsx';
import {Separator} from '@/components/ui/separator.jsx';
import LayoutGrid from '@/components/layout/LayoutGrid.jsx';
@@ -233,6 +233,7 @@ export const TCPClientView = ({model, index}) => {
+
{/* Response Section */}
@@ -242,13 +243,13 @@ export const TCPClientView = ({model, index}) => {
{responseData.size}
-
+
Body
Console
-
+
{
+ const { createMockRoute, updateMockRoute } = useAPI();
+ const [mockRoute, setMockRoute] = useState({
+ id: null,
+ method: 'POST',
+ pathPattern: '',
+ name: '',
+ description: '',
+ requestScript: '',
+ responses: [
+ {
+ statusCode: 200,
+ delay: 0,
+ encodingCharset: '',
+ defaultResponse: 'true',
+ conditions: [],
+ headers: [],
+ body: ''
+ }
+ ]
+ });
+
+ // Initialize form when item changes
+ useEffect(() => {
+ if (item) {
+ setMockRoute(item);
+ } else {
+ // Reset to default state when creating new
+ setMockRoute({
+ id: null,
+ method: 'POST',
+ pathPattern: '',
+ name: '',
+ description: '',
+ requestScript: '',
+ responses: [
+ {
+ statusCode: 200,
+ delay: 0,
+ encodingCharset: '',
+ defaultResponse: 'true',
+ conditions: [],
+ headers: [],
+ body: ''
+ }
+ ]
+ });
+ }
+ }, [item]);
+
+ const handleChange = (field, value) => {
+ setMockRoute((prev) => ({...prev, [field]: value}));
+ };
+
+ const handleScriptChange = (value) => {
+ setMockRoute((prev) => ({...prev, requestScript: value}));
+ };
+
+ const addResponse = () => {
+ setMockRoute((prev) => ({
+ ...prev,
+ responses: [
+ ...prev.responses,
+ {
+ statusCode: 200,
+ delay: 0,
+ encodingCharset: '',
+ defaultResponse: 'false',
+ conditions: [],
+ headers: [],
+ body: ''
+ }
+ ]
+ }));
+ };
+
+ const handleOpenChange = (open) => {
+ if (!open) {
+ setMockRoute({
+ id: null,
+ method: 'POST',
+ pathPattern: '',
+ name: '',
+ description: '',
+ requestScript: '',
+ responses: [
+ {
+ statusCode: 200,
+ delay: 0,
+ encodingCharset: '',
+ defaultResponse: 'true',
+ conditions: [],
+ headers: [],
+ body: ''
+ }
+ ]
+ });
+ }
+ onOpenChange(open);
+ };
+
+ const removeResponse = (index) => {
+ setMockRoute((prev) => {
+ const newResponses = [...prev.responses];
+ if (newResponses[index].defaultResponse === 'true') {
+ alert('기본 응답은 삭제할 수 없습니다.');
+ return prev;
+ }
+ newResponses.splice(index, 1);
+ return {...prev, responses: newResponses};
+ });
+ };
+
+ const updateResponseField = (index, field, value) => {
+ setMockRoute((prev) => {
+ const newResponses = [...prev.responses];
+ newResponses[index] = {...newResponses[index], [field]: value};
+ return {...prev, responses: newResponses};
+ });
+ };
+
+ const addCondition = (respIndex) => {
+ setMockRoute((prev) => {
+ const newResponses = [...prev.responses];
+ newResponses[respIndex] = {
+ ...newResponses[respIndex],
+ conditions: [
+ ...newResponses[respIndex].conditions,
+ {type: 'query', key: '', value: ''}
+ ]
+ };
+ return {...prev, responses: newResponses};
+ });
+ };
+
+ const removeCondition = (respIndex, condIndex) => {
+ setMockRoute((prev) => {
+ const newResponses = [...prev.responses];
+ const newConds = [...newResponses[respIndex].conditions];
+ newConds.splice(condIndex, 1);
+ newResponses[respIndex] = {
+ ...newResponses[respIndex],
+ conditions: newConds
+ };
+ return {...prev, responses: newResponses};
+ });
+ };
+
+ const updateConditionField = (respIndex, condIndex, field, value) => {
+ setMockRoute((prev) => {
+ const newResponses = [...prev.responses];
+ const newConds = [...newResponses[respIndex].conditions];
+ newConds[condIndex] = {...newConds[condIndex], [field]: value};
+ newResponses[respIndex] = {
+ ...newResponses[respIndex],
+ conditions: newConds
+ };
+ return {...prev, responses: newResponses};
+ });
+ };
+
+ const addHeader = (respIndex) => {
+ setMockRoute((prev) => {
+ const newResponses = [...prev.responses];
+ newResponses[respIndex] = {
+ ...newResponses[respIndex],
+ headers: [...newResponses[respIndex].headers, {key: '', value: ''}]
+ };
+ return {...prev, responses: newResponses};
+ });
+ };
+
+ const removeHeader = (respIndex, hdrIndex) => {
+ setMockRoute((prev) => {
+ const newResponses = [...prev.responses];
+ const newHeaders = [...newResponses[respIndex].headers];
+ newHeaders.splice(hdrIndex, 1);
+ newResponses[respIndex] = {
+ ...newResponses[respIndex],
+ headers: newHeaders
+ };
+ return {...prev, responses: newResponses};
+ });
+ };
+
+ const updateHeaderField = (respIndex, hdrIndex, field, value) => {
+ setMockRoute((prev) => {
+ const newResponses = [...prev.responses];
+ const newHeaders = [...newResponses[respIndex].headers];
+ newHeaders[hdrIndex] = {...newHeaders[hdrIndex], [field]: value};
+ newResponses[respIndex] = {
+ ...newResponses[respIndex],
+ headers: newHeaders
+ };
+ return {...prev, responses: newResponses};
+ });
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ try {
+ if (mockRoute.id) {
+ await updateMockRoute(mockRoute);
+ } else {
+ await createMockRoute(mockRoute);
+ }
+ onOpenChange(false);
+ } catch (error) {
+ console.error('Failed to save mock route:', error);
+ alert('Failed to save mock route');
+ }
+ };
+
+ return (
+
+ );
+};
+
+export default MockRouteEditDialog;
diff --git a/TestMasterUI/src/components/mock-route/MockRouteList.jsx b/TestMasterUI/src/components/mock-route/MockRouteList.jsx
new file mode 100644
index 0000000..942d663
--- /dev/null
+++ b/TestMasterUI/src/components/mock-route/MockRouteList.jsx
@@ -0,0 +1,132 @@
+import React from 'react';
+import {Button} from '@/components/ui/button';
+import DataTable from '@/components/shared/DataTable';
+import {useAPI} from '@/providers/APIProvider';
+import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card.jsx';
+import {Input} from '@/components/ui/input.jsx';
+
+const MockRouteList = ({onMenuClick}) => {
+ const {
+ mockRoutes,
+ loadMockRoutes,
+ deleteMockRoutes,
+ loading
+ } = useAPI();
+
+ const [searchTerm, setSearchTerm] = React.useState('');
+ const [selectedIds, setSelectedIds] = React.useState([]);
+ const [page, setPage] = React.useState(0);
+
+ React.useEffect(() => {
+ loadMockRoutes({page});
+ }, [page, loadMockRoutes]);
+
+ const columns = [
+ {key: 'method', label: 'Method'},
+ {key: 'pathPattern', label: 'Path'},
+ {key: 'name', label: '이름'}
+ ];
+
+ const handleSearch = () => {
+ setPage(0);
+ loadMockRoutes({name: searchTerm, page: 0});
+ };
+
+ const handleDelete = async () => {
+ const success = await deleteMockRoutes(selectedIds);
+ if (success) {
+ setSelectedIds([]);
+ }
+ };
+
+ const handleEdit = (item) => {
+ setSelectedItem(item);
+ setDialogState(prev => ({...prev, showMockRouteEdit: true}));
+ };
+
+ const handleCreate = () => {
+ setSelectedItem(null);
+ setDialogState(prev => ({...prev, showMockRouteEdit: true}));
+ };
+
+ const handleDialogChange = (key, value) => {
+ setDialogState(prev => ({...prev, [key]: value}));
+ if (!value) {
+ setSelectedItem(null);
+ }
+ };
+
+ const renderRowActions = (row) => (
+ <>
+
+
+ >
+ );
+
+ const tableActions = (
+ <>
+
+
+
+
+ >
+ );
+
+ return (
+
+
+
+ 응답 API 목록
+
+
+
+
+ setSearchTerm(e.target.value)}
+ onKeyUp={(e) => {
+ if (e.key === 'Enter') {
+ handleSearch();
+ }
+ }}
+ />
+
+ {tableActions}
+
+
+
+
+
+ );
+};
+
+export default MockRouteList;
diff --git a/TestMasterUI/src/components/mock-route/MockRouteRequest.jsx b/TestMasterUI/src/components/mock-route/MockRouteRequest.jsx
new file mode 100644
index 0000000..b573a5e
--- /dev/null
+++ b/TestMasterUI/src/components/mock-route/MockRouteRequest.jsx
@@ -0,0 +1,86 @@
+import React from 'react'
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
+import { Input } from '@/components/ui/input'
+import { Label } from '@/components/ui/label'
+import MonacoEditor from '@/components/shared/MonacoEditor.jsx'
+
+const MockRouteRequest=({
+ name,
+ method,
+ pathPattern,
+ requestScript,
+ onChange,
+ onScriptChange
+}) => {
+ return (
+
+ {/* Name */}
+
+
+ onChange('name', e.target.value)}
+ />
+
+
+ {/* Method & Path */}
+
+
+
+
+
+
+
+ onChange('pathPattern', e.target.value)}
+ />
+
+
+
+ {/* Request Script */}
+
+
+ 요청 스크립트
+
+
+
+
+
+
+ )
+}
+
+export default MockRouteRequest;
diff --git a/TestMasterUI/src/components/mock-route/MockRouteResponse.jsx b/TestMasterUI/src/components/mock-route/MockRouteResponse.jsx
new file mode 100644
index 0000000..15da259
--- /dev/null
+++ b/TestMasterUI/src/components/mock-route/MockRouteResponse.jsx
@@ -0,0 +1,225 @@
+import React from 'react';
+import {Button} from '@/components/ui/button';
+import {Input} from '@/components/ui/input';
+import {Card, CardContent, CardHeader} from '@/components/ui/card';
+import {Label} from '@/components/ui/label';
+import {Trash2} from 'lucide-react';
+import MonacoEditor from '@/components/shared/MonacoEditor.jsx';
+
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow
+} from '@/components/ui/table';
+
+const MockRouteResponse = ({
+ resp,
+ idx,
+ onRemoveResponse,
+ onAddCondition,
+ onRemoveCondition,
+ onUpdateConditionField,
+ onUpdateResponseField,
+ onAddHeader,
+ onRemoveHeader,
+ onUpdateHeaderField
+}) => {
+ return (
+
+
+ {/* Left side - Condition card */}
+
+
+
+
+
조건 항목
+
+
+
+
+
+
+
+
+ 종류
+ 키
+ 값
+
+
+
+
+
+
+ {resp.conditions.map((cond, cIndex) => (
+
+
+
+
+
+
+ onUpdateConditionField(idx, cIndex, 'key', e.target.value)
+ }
+ />
+
+
+
+ onUpdateConditionField(idx, cIndex, 'value', e.target.value)
+ }
+ />
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+ {/* Right side - Response Content */}
+
+
+
+ 응답 내용
+
+
+
+
+ onUpdateResponseField(idx, 'statusCode', e.target.value)}
+ />
+
+
+
+
+ onUpdateResponseField(idx, 'delay', e.target.value)}
+ />
+
+
+
+
+
+ onUpdateResponseField(idx, 'encodingCharset', e.target.value)
+ }
+ />
+
+
+
+
+
+ onUpdateResponseField(idx, 'body', v || '')}
+ height="100%"
+ />
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default MockRouteResponse
diff --git a/TestMasterUI/src/components/mock-route/MockRouteResponses.jsx b/TestMasterUI/src/components/mock-route/MockRouteResponses.jsx
new file mode 100644
index 0000000..caf51ce
--- /dev/null
+++ b/TestMasterUI/src/components/mock-route/MockRouteResponses.jsx
@@ -0,0 +1,62 @@
+import React from 'react'
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
+import { Plus } from 'lucide-react'
+import { Button } from '@/components/ui/button'
+import MockRouteResponse from './MockRouteResponse.jsx'
+
+const MockRouteResponses = ({
+ responses,
+ onAddResponse,
+ onRemoveResponse,
+ onAddCondition,
+ onRemoveCondition,
+ onUpdateConditionField,
+ onUpdateResponseField,
+ onAddHeader,
+ onRemoveHeader,
+ onUpdateHeaderField
+}) => {
+ return (
+
+
+ {responses.map((resp, idx) => {
+ const tabValue = idx.toString()
+ return (
+
+ {resp.defaultResponse === 'true'
+ ? '기본 응답'
+ : `조건 ${idx + 1}`}
+
+ )
+ })}
+
+
+
+ {responses.map((resp, idx) => {
+ const tabValue = idx.toString()
+ return (
+
+ {/* Render a single response with all its fields and actions */}
+
+
+ )
+ })}
+
+ )
+}
+
+export default MockRouteResponses
diff --git a/TestMasterUI/src/components/queues/QueueEditDialog.jsx b/TestMasterUI/src/components/queues/QueueEditDialog.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/TestMasterUI/src/components/queues/QueueList.jsx b/TestMasterUI/src/components/queues/QueueList.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/TestMasterUI/src/components/servers/ServerEditDialog.jsx b/TestMasterUI/src/components/servers/ServerEditDialog.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/TestMasterUI/src/components/servers/ServerList.jsx b/TestMasterUI/src/components/servers/ServerList.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/TestMasterUI/src/components/shared/DataTable.jsx b/TestMasterUI/src/components/shared/DataTable.jsx
new file mode 100644
index 0000000..36c8f0c
--- /dev/null
+++ b/TestMasterUI/src/components/shared/DataTable.jsx
@@ -0,0 +1,195 @@
+import React from 'react';
+import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from '@/components/ui/table';
+import {Button} from '@/components/ui/button';
+import {Card, CardContent} from '@/components/ui/card';
+import {Checkbox} from '@/components/ui/checkbox';
+import {AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle} from '@/components/ui/alert-dialog';
+
+const DataTable = ({
+ // Data and columns
+ data,
+ columns,
+
+ // Selection
+ selectable = false,
+ selectedIds = [],
+ onSelectIds,
+
+ renderRowActions,
+
+ // Pagination
+ onPageChange,
+
+ // Loading state
+ loading = false,
+
+ // Delete dialog
+ deleteDialogTitle = 'Delete Items',
+ deleteDialogDescription = 'Are you sure you want to delete the selected items? This action cannot be undone.',
+ onDelete
+}) => {
+
+ const [showDeleteDialog, setShowDeleteDialog] = React.useState(false);
+
+ const handleSelectAll = (checked) => {
+ if (!onSelectIds) return;
+ if (checked) {
+ onSelectIds(data.content.map(item => item.id));
+ } else {
+ onSelectIds([]);
+ }
+ };
+
+ const handleSelectItem = (id, checked) => {
+ if (!onSelectIds) return;
+ if (checked) {
+ onSelectIds([...selectedIds, id]);
+ } else {
+ onSelectIds(selectedIds.filter(selectedId => selectedId !== id));
+ }
+ };
+
+ const handlePageChange = (page) => {
+ // Reset selections when changing pages
+ if (onSelectIds) {
+ onSelectIds([]);
+ }
+ onPageChange(page);
+ };
+
+ if (loading) {
+ return (
+
+
+ Loading...
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ {selectable && (
+
+ 0}
+ onCheckedChange={handleSelectAll}
+ />
+
+ )}
+ {columns.map((column) => (
+ {column.label}
+ ))}
+ {renderRowActions && Actions}
+
+
+
+ {data.content.map((row) => (
+
+ {selectable && (
+
+ handleSelectItem(row.id, checked)}
+ />
+
+ )}
+ {columns.map((column) => (
+
+ {column.render ? column.render(row[column.key], row) : row[column.key]}
+
+ ))}
+ {renderRowActions && (
+
+ {renderRowActions(row)}
+
+ )}
+
+ ))}
+
+
+
+
+ {onPageChange && (
+
+
+ Showing {data.numberOfElements} of {data.totalElements} results
+
+
+
+
+
+ {Array.from({ length: data.totalPages }, (_, i) => (
+
+ ))}
+
+
+
+
+
+ )}
+
+ {onDelete && (
+
+
+
+ {deleteDialogTitle}
+
+ {deleteDialogDescription}
+
+
+
+ Cancel
+ {
+ onDelete?.();
+ setShowDeleteDialog(false);
+ }}>Delete
+
+
+
+ )}
+
+ );
+};
+
+export default DataTable;
diff --git a/TestMasterUI/src/components/DialogManager.jsx b/TestMasterUI/src/components/shared/DialogManager.jsx
similarity index 50%
rename from TestMasterUI/src/components/DialogManager.jsx
rename to TestMasterUI/src/components/shared/DialogManager.jsx
index d4dc134..eb93d10 100644
--- a/TestMasterUI/src/components/DialogManager.jsx
+++ b/TestMasterUI/src/components/shared/DialogManager.jsx
@@ -3,69 +3,40 @@ import React from 'react';
import AddCollectionDialog from '@/components/api-tester/AddCollectionDialog.jsx';
import AddScenarioDialog from '@/components/api-tester/AddScenarioDialog.jsx';
import VariableModal from '@/components/api-tester/VariableModal.jsx';
-import {useLoadTest} from '@/providers/LoadTestProvider.jsx';
-import {useAPI} from '@/providers/APIProvider.jsx';
+import MockRouteEditDialog from '@/components/mock-route/MockRouteEditDialog.jsx';
const DialogManager = ({
showAddApi,
showAddCollection,
showAddScenario,
showVariableModal,
+ showMockRouteEdit,
+ selectedItem,
onDialogChange
}) => {
- const {createCollection, createAPIRequest} = useAPI();
- const {createScenario} = useLoadTest();
-
- const handleAddCollection = async (name) => {
- try {
- await createCollection(name);
- return true;
- } catch (error) {
- throw new Error('컬렉션 생성에 실패했습니다');
- }
- };
-
- const handleAddApi = async (collectionId, apiData) => {
- try {
- await createAPIRequest(collectionId, apiData);
- return true;
- } catch (error) {
- console.error('Failed to create API:', error);
- return false;
- }
- };
-
- const handleAddScenario = async (name) => {
- try {
- await createScenario(name);
- return true;
- } catch (error) {
- console.error('시나리오 생성에 실패했습니다.');
- }
- }
return <>
onDialogChange('showAddCollection', isOpen)}
- onSubmit={handleAddCollection}
/>
- {/* Add these at the bottom of your return statement */}
onDialogChange('showAddApi', isOpen)}
- onSubmit={handleAddApi}
/>
onDialogChange('showAddScenario', isOpen)}
- onSubmit={handleAddScenario}
/>
onDialogChange('showVariableModal', isOpen)}
/>
-
+ onDialogChange('showMockRouteEdit', isOpen)}
+ item={selectedItem}
+ />
>;
}
diff --git a/TestMasterUI/src/components/shared/MonacoEditor.jsx b/TestMasterUI/src/components/shared/MonacoEditor.jsx
index a210211..fed0b62 100644
--- a/TestMasterUI/src/components/shared/MonacoEditor.jsx
+++ b/TestMasterUI/src/components/shared/MonacoEditor.jsx
@@ -1,5 +1,17 @@
import React, { useEffect, useRef, memo } from 'react';
import * as monaco from 'monaco-editor';
+import {loader} from '@monaco-editor/react';
+
+loader.config({
+ paths: {
+ vs: '/monaco-editor/min/vs'
+ },
+ 'vs/nls': {
+ availableLanguages: {},
+ },
+ // Specify which languages to load
+ languages: ['javascript', 'json', 'xml','plaintext']
+});
const MonacoEditor = memo(({
id,
@@ -10,10 +22,12 @@ const MonacoEditor = memo(({
onChange = () => {}
}) => {
const editorRef = useRef(null);
- const modelRef = useRef(null);
const containerRef = useRef(null);
const subscriptionRef = useRef(null);
+ // Ensure value is always a string
+ const normalizedValue = value ?? '';
+
const getEditorOptions = (lang) => {
const baseOptions = {
automaticLayout: true,
@@ -57,63 +71,91 @@ const MonacoEditor = memo(({
useEffect(() => {
if (!containerRef.current) return;
- const modelUri = monaco.Uri.parse(`inmemory://model-${id}`);
- modelRef.current = monaco.editor.createModel(value, language, modelUri);
-
const options = getEditorOptions(language);
- editorRef.current = monaco.editor.create(containerRef.current, {
+ const editor = monaco.editor.create(containerRef.current, {
...options,
- model: modelRef.current
+ value: normalizedValue,
+ language: language
});
- subscriptionRef.current = editorRef.current.onDidChangeModelContent(() => {
- onChange(editorRef.current.getValue());
+ editorRef.current = editor;
+
+ const subscription = editor.onDidChangeModelContent(() => {
+ const newValue = editor.getValue();
+ onChange(newValue);
});
- if (language === 'json' && value) {
+ subscriptionRef.current = subscription;
+
+ if (language === 'json' && normalizedValue) {
try {
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
allowComments: false,
schemas: []
});
- setTimeout(() => {
- const action = editorRef.current?.getAction('editor.action.formatDocument');
- if (action) action.run();
- }, 100);
+
+ const action = editor.getAction('editor.action.formatDocument');
+ if (action) {
+ setTimeout(() => {
+ action.run();
+ }, 100);
+ }
} catch (e) {
console.warn('Failed to format JSON:', e);
}
}
const resizeObserver = new ResizeObserver(() => {
- editorRef.current?.layout();
+ editor.layout();
});
resizeObserver.observe(containerRef.current);
return () => {
resizeObserver.disconnect();
- subscriptionRef.current?.dispose();
- editorRef.current?.dispose();
- if (modelRef.current && !modelRef.current.isDisposed()) {
- modelRef.current.dispose();
+ subscription.dispose();
+
+ // Ensure proper cleanup
+ if (editor) {
+ try {
+ const model = editor.getModel();
+ if (model) {
+ model.dispose();
+ }
+ editor.dispose();
+ } catch (e) {
+ console.warn('Editor disposal error:', e);
+ }
}
};
- }, [language]);
+ }, [language]); // Only recreate when language changes
+ // Handle value updates
useEffect(() => {
- if (modelRef.current && !modelRef.current.isDisposed()) {
- modelRef.current.setValue(value);
+ if (editorRef.current) {
+ const currentValue = editorRef.current.getValue();
+ if (currentValue !== normalizedValue) {
+ editorRef.current.setValue(normalizedValue);
+ }
}
- }, [value]);
+ }, [normalizedValue]);
+
+ // Convert height to pixel value if it's a number
+ const containerHeight = typeof height === 'number'
+ ? `${height}px`
+ : height.endsWith('px') ? height : `${height}px`;
return (
);
});
diff --git a/TestMasterUI/src/components/ServerManager.jsx b/TestMasterUI/src/components/shared/ServerManager.jsx
similarity index 100%
rename from TestMasterUI/src/components/ServerManager.jsx
rename to TestMasterUI/src/components/shared/ServerManager.jsx
diff --git a/TestMasterUI/src/components/tcp-servers/TCPServerEditDialog.jsx b/TestMasterUI/src/components/tcp-servers/TCPServerEditDialog.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/TestMasterUI/src/components/tcp-servers/TCPServerList.jsx b/TestMasterUI/src/components/tcp-servers/TCPServerList.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/TestMasterUI/src/components/users/UserEditDialog.jsx b/TestMasterUI/src/components/users/UserEditDialog.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/TestMasterUI/src/components/users/UserList.jsx b/TestMasterUI/src/components/users/UserList.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/TestMasterUI/src/config/menuConfig.js b/TestMasterUI/src/config/menuConfig.js
new file mode 100644
index 0000000..1451cb5
--- /dev/null
+++ b/TestMasterUI/src/config/menuConfig.js
@@ -0,0 +1,64 @@
+// config/menuConfig.js
+export const menuConfig = [
+ {
+ id: 'response',
+ label: '응답관리',
+ items: [
+ {id: 'response-list', label: 'API 응답 목록', action: 'response-list', screen: 'response-list'},
+ {id: 'response-add', label: 'API 응답 추가', action: 'response-add'},
+ {type: 'separator'},
+ {id: 'tcp-list', label: 'TCP 서버 목록', action: 'tcp-list'},
+ {id: 'tcp-add', label: 'TCP 서버 추가', action: 'tcp-add'},
+ ]
+ },
+ {
+ id: 'request',
+ label: '요청 관리',
+ items: [
+ {id: 'api-tester', label: 'API 테스터', action: 'api-tester', screen: 'api-tester'},
+ {type: 'separator'},
+ {id: 'collection-add', label: '컬렉션 추가', action: 'collection-add'},
+ {id: 'request-add', label: '요청 추가', action: 'request-add'},
+ {id: 'scenario-add', label: '시나리오 추가', action: 'scenario-add'},
+ {type: 'separator'},
+ {id: 'variables-show', label: '변수 보기', action: 'variables-show'}
+ ]
+ },
+ {
+ id: 'queue',
+ label: '큐 관리',
+ items: [
+ {id: 'queue-list', label: '큐 목록', action: 'queue-list', screen: 'queue-list'},
+ {id: 'queue-add', label: '큐 추가', action: 'queue-add', screen: 'queue-add'}
+ ]
+ },
+ {
+ id: 'loadtest',
+ label: '부하 테스트',
+ items: [
+ {id: 'load-test', label: '새 테스트', action: 'load-test', screen: 'load-test'},
+ {type: 'separator'},
+ {id: 'load-test-history', label: '테스트 이력', action: 'load-test-history', screen: 'load-test-history'},
+ {id: 'load-test-reports', label: '결과 리포트', action: 'load-test-reports', screen: 'load-test-reports'}
+ ]
+ },
+ {
+ id: 'system',
+ label: '시스템 관리',
+ items: [
+ {id: 'user-management', label: '사용자 관리', action: 'user-management', screen: 'user-management'},
+ {type: 'separator'},
+ {id: 'server-management', label: '서버 관리', action: 'server-management', screen: 'server-management'},
+ {id: 'server-add', label: '서버 추가', action: 'server-add', screen: 'server-add'},
+ ]
+ },
+ {
+ id: 'account',
+ label: '계정',
+ items: [
+ {id: 'change-password', label: '비밀번호 변경', action: 'change-password'},
+ {type: 'separator'},
+ {id: 'logout', label: '로그 아웃', action: 'logout'}
+ ]
+ }
+];
diff --git a/TestMasterUI/src/config/screenConfig.js b/TestMasterUI/src/config/screenConfig.js
new file mode 100644
index 0000000..d3c94f8
--- /dev/null
+++ b/TestMasterUI/src/config/screenConfig.js
@@ -0,0 +1,10 @@
+// config/screenConfig.js
+import APITester from '../components/api-tester/APITester';
+import LoadTestManager from '../components/load-tester/LoadTestManager.jsx';
+import MockRouteList from '../components/mock-route/MockRouteList.jsx';
+
+export const SCREENS = {
+ 'api-tester': APITester,
+ 'load-test': LoadTestManager,
+ 'response-list': MockRouteList,
+};
diff --git a/TestMasterUI/src/index.css b/TestMasterUI/src/index.css
index f124e40..d036df1 100644
--- a/TestMasterUI/src/index.css
+++ b/TestMasterUI/src/index.css
@@ -44,7 +44,7 @@ h1 {
button {
border-radius: 8px;
border: 1px solid transparent;
- padding: 0.6em 1.2em;
+ /*padding: 0.6em 1.2em;*/
font-size: 1em;
font-weight: 500;
font-family: inherit;
@@ -52,6 +52,7 @@ button {
cursor: pointer;
transition: border-color 0.25s;
}
+
button:hover {
border-color: #646cff;
}
diff --git a/TestMasterUI/src/providers/APIProvider.jsx b/TestMasterUI/src/providers/APIProvider.jsx
index 2e2dc1e..9fe4672 100644
--- a/TestMasterUI/src/providers/APIProvider.jsx
+++ b/TestMasterUI/src/providers/APIProvider.jsx
@@ -26,6 +26,13 @@ export const APIProvider = ({
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
+ const [mockRoutes, setMockRoutes] = useState({
+ content: [],
+ totalPages: 0,
+ number: 0,
+ first: true,
+ last: true
+ });
// Initialize services
const collections$ = collectionService(contextPath);
@@ -224,6 +231,67 @@ export const APIProvider = ({
}
}, [apiClient]);
+ const loadMockRoutes = useCallback(async (params = { name: '', page: 0 }) => {
+ setLoading(true);
+ setError(null);
+ try {
+ const data = await apiRequests$.getMockRoutes(params);
+ setMockRoutes(data);
+ return data;
+ } catch (err) {
+ setError(err.message);
+ return null;
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ const deleteMockRoutes = useCallback(async (ids) => {
+ setLoading(true);
+ setError(null);
+ try {
+ await apiRequests$.deleteMockRoutes(ids);
+ await loadMockRoutes(); // Reload the list after deletion
+ return true;
+ } catch (err) {
+ setError(err.message);
+ return false;
+ } finally {
+ setLoading(false);
+ }
+ }, [loadMockRoutes]);
+
+ const createMockRoute = useCallback(async (mockRoute) => {
+ setLoading(true);
+ setError(null);
+ try {
+ const newMockRoute = await apiRequests$.createMockRoute(mockRoute);
+ await loadMockRoutes();
+ return newMockRoute;
+ } catch (err) {
+ setError(err.message);
+ return null;
+ } finally {
+ setLoading(false);
+ }
+ }, [loadMockRoutes]);
+
+ const updateMockRoute = useCallback(async (mockRoute) => {
+ setLoading(true);
+ setError(null);
+ try {
+ const updatedMockRoute = await apiRequests$.updateMockRoute(mockRoute);
+ await loadMockRoutes();
+ return updatedMockRoute;
+ } catch (err) {
+ setError(err.message);
+ return null;
+ } finally {
+ setLoading(false);
+ }
+ }, [loadMockRoutes]);
+
+
const contextValue = {
apiClient,
collections,
@@ -243,7 +311,12 @@ export const APIProvider = ({
sendAPIRequest,
isAuthenticated,
setIsAuthenticated,
- handleLogout
+ handleLogout,
+ mockRoutes,
+ loadMockRoutes,
+ deleteMockRoutes,
+ createMockRoute,
+ updateMockRoute
};
return (
diff --git a/TestMasterUI/src/services/apiRequestService.js b/TestMasterUI/src/services/apiRequestService.js
index 26f2f03..243fd6a 100644
--- a/TestMasterUI/src/services/apiRequestService.js
+++ b/TestMasterUI/src/services/apiRequestService.js
@@ -52,6 +52,53 @@ export const apiRequestService = (contextPath = '/') => {
if (!response.ok) throw new Error('Failed to delete API request');
return true;
+ },
+
+ async getMockRoutes(params = { name: '', page: 0 }) {
+ const searchParams = new URLSearchParams();
+ if (params.name) searchParams.append('name', params.name);
+ if (typeof params.page !== 'undefined') searchParams.append('page', params.page.toString());
+
+ const queryString = searchParams.toString();
+ const url = `${contextPath}api/mock-routes${queryString ? `?${queryString}` : ''}`;
+
+ const response = await fetch(url, { headers });
+ if (!response.ok) throw new Error('Failed to fetch mock routes');
+ return response.json();
+ },
+
+ async deleteMockRoutes(ids) {
+ const response = await fetch(`${contextPath}api/mock-routes/delete`, {
+ method: 'POST',
+ headers,
+ body: JSON.stringify(ids)
+ });
+
+ if (!response.ok) throw new Error('Failed to delete mock routes');
+ return true;
+ },
+
+ async createMockRoute(mockRoute) {
+ const response = await fetch(`${contextPath}api/mock-routes`, {
+ method: 'POST',
+ headers,
+ body: JSON.stringify(mockRoute)
+ });
+
+ if (!response.ok) throw new Error('Failed to create mock route');
+ return response.json();
+ },
+
+ async updateMockRoute(mockRoute) {
+ const response = await fetch(`${contextPath}api/mock-routes/update/${mockRoute.id}`, {
+ method: 'POST',
+ headers,
+ body: JSON.stringify(mockRoute)
+ });
+
+ if (!response.ok) throw new Error('Failed to update mock route');
+ return response.json();
}
+
};
};
diff --git a/TestMasterUI/vite.config.js b/TestMasterUI/vite.config.js
index 0e307b4..d358b72 100644
--- a/TestMasterUI/vite.config.js
+++ b/TestMasterUI/vite.config.js
@@ -1,63 +1,61 @@
-import path from "path"
-import react from '@vitejs/plugin-react'
-import { defineConfig } from 'vite'
-import monacoEditorPlugin from './vite.monaco-editor';
+import path from 'path';
+import react from '@vitejs/plugin-react';
+import {defineConfig} from 'vite';
export default defineConfig({
define: {
- global: {},
+ global: {}
},
- plugins: [react(),monacoEditorPlugin()],
+ plugins: [react()],
resolve: {
alias: {
- "@": path.resolve(__dirname, "./src"),
- },
+ '@': path.resolve(__dirname, './src')
+ }
},
server: {
proxy: {
'/mgmt': {
target: 'http://localhost:38080',
changeOrigin: true,
- secure: false,
+ secure: false
},
'/api': {
target: 'http://localhost:38080',
changeOrigin: true,
- secure: false,
+ secure: false
},
'/actionLogin.do': {
target: 'http://localhost:38080',
changeOrigin: true,
- secure: false,
+ secure: false
},
'/actionLogout.do': {
target: 'http://localhost:38080',
changeOrigin: true,
- secure: false,
+ secure: false
},
'/img': {
target: 'http://localhost:38080',
changeOrigin: true,
- secure: false,
+ secure: false
},
'/servers': {
target: 'http://localhost:38080',
changeOrigin: true,
- secure: false,
+ secure: false
}
}
},
optimizeDeps: {
- include: ['@monaco-editor/react'],
+ include: ['@monaco-editor/react']
},
build: {
rollupOptions: {
output: {
manualChunks: {
- // Split Monaco Editor into a separate chunk
- 'monaco-editor': ['monaco-editor'],
- },
- },
- },
- },
-})
+ 'monaco-editor': ['monaco-editor/esm/vs/editor/editor.api']
+ }
+ }
+ }
+ }
+});
diff --git a/TestMasterUI/vite.monaco-editor.js b/TestMasterUI/vite.monaco-editor.js
deleted file mode 100644
index 4c9b3da..0000000
--- a/TestMasterUI/vite.monaco-editor.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// vite.monaco-editor.js
-export default function monacoEditorPlugin() {
- return {
- name: 'monaco-editor-plugin',
- config(config) {
- config.define = {
- ...config.define,
- 'process.platform': JSON.stringify('darwin'),
- 'process.env': {},
- };
- },
- configureServer(server) {
- return () => {
- server.middlewares.use('/_monaco', (req, res) => {
- res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
- });
- };
- }
- };
-}