fix(settings, yjs, ux, ci): default settings, y-leveldb restart resilience, dashboard one-click open, delete error handling, Playwright CI

This commit is contained in:
2026-07-27 03:15:30 +02:00
parent 7f8695456c
commit 126277ab94
13 changed files with 129 additions and 45 deletions
+3 -1
View File
@@ -7,7 +7,9 @@
"dev": "vite --host 0.0.0.0 --port 5173",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "vitest run"
"test": "vitest run",
"test:e2e": "playwright test",
"test:e2e:ci": "playwright test playwright-tests/diagnose-editor.test.ts playwright-tests/tool-exhaustive.test.ts"
},
"dependencies": {
"dxf-parser": "^1.1.2",
@@ -40,10 +40,9 @@ test('diagnose editor DOM', async () => {
await page.waitForSelector('.dashboard-project-card', { timeout: 10000 });
await page.locator('.dashboard-project-card').first().click();
await page.waitForTimeout(500);
// Dashboard shows a detail panel on the right with 'Projekt öffnen'
await page.locator('button:has-text("Projekt öffnen")').first().click();
await page.waitForTimeout(3000);
// Card click now opens the project directly
await page.waitForSelector('canvas', { timeout: 10000 });
await page.waitForTimeout(1000);
// Take screenshot
const shot = '/tmp/web-cad-clone/frontend/playwright-tests/screenshots/diagnose-editor.png';
@@ -138,15 +138,7 @@ test.describe('Web CAD Exhaustive Browser Tests', () => {
test('01: open project and load CAD editor', async () => {
const stopCapture = captureConsoleErrors(page, 'open project');
await page.locator('.dashboard-project-card').first().click();
await waitForStable(page, 500);
// Dashboard opens detail panel on the right with 'Projekt öffnen'
const openBtn = await page.locator('button:has-text("Projekt öffnen")').first();
if (await openBtn.count() > 0 && await openBtn.isVisible().catch(() => false)) {
await openBtn.click();
} else {
logIssue('Dashboard', 'major', 'Projekt öffnen button not visible', '');
}
// Wait for editor to render: any tool button from the left sidebar is visible
// Card click now opens the project directly
try {
await page.waitForSelector('[data-tool="line"]', { timeout: 10000 });
await waitForStable(page, 1000);
@@ -111,7 +111,7 @@ test('exhaustive tool test v2', async () => {
await page.reload();
await page.waitForSelector('.dashboard-project-card', { timeout: 10000 });
await page.locator('.dashboard-project-card').first().click();
await page.locator('button:has-text("Projekt öffnen")').first().click();
// Card click now opens the project directly
await page.waitForSelector('canvas', { timeout: 10000 });
await page.waitForTimeout(2500);
+2 -2
View File
@@ -14,13 +14,13 @@ export default defineConfig({
},
webServer: [
{
command: 'cd /tmp/web-cad-clone/backend && npm run dev',
command: 'cd ../backend && npm run dev:ci',
url: 'http://localhost:3001/api/health',
reuseExistingServer: true,
timeout: 120000,
},
{
command: 'cd /tmp/web-cad-clone/frontend && npm run dev -- --port 5173',
command: 'npm run dev -- --port 5173',
url: 'http://localhost:5173',
reuseExistingServer: true,
timeout: 120000,
+1
View File
@@ -332,6 +332,7 @@ export function Dashboard({ onOpenProject }: DashboardProps) {
const handleProjectCardClick = (projectId: string) => {
setSelectedProjectId(projectId);
setMobileInfoOpen(true);
onOpenProject(projectId);
};
const handleSaveProject = async () => {
+12 -6
View File
@@ -102,10 +102,11 @@ export async function createProject(token: string, name: string, description?: s
}
export async function deleteProject(token: string, id: string): Promise<void> {
await fetch(`${API_BASE}/api/projects/${id}`, {
const res = await fetch(`${API_BASE}/api/projects/${id}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error('Failed to delete project');
}
export async function updateProject(token: string, id: string, updates: { name?: string; description?: string | null }): Promise<Project> {
@@ -225,10 +226,11 @@ export async function updateElement(token: string, elementId: string, patch: Par
}
export async function deleteElement(token: string, elementId: string): Promise<void> {
await fetch(`${API_BASE}/api/elements/${elementId}`, {
const res = await fetch(`${API_BASE}/api/elements/${elementId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error('Failed to delete element');
}
// ─── Layers ─────────────────────────────────────────────
@@ -259,10 +261,11 @@ export async function updateLayer(token: string, layerId: string, patch: Partial
}
export async function deleteLayer(token: string, layerId: string): Promise<void> {
await fetch(`${API_BASE}/api/layers/${layerId}`, {
const res = await fetch(`${API_BASE}/api/layers/${layerId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error('Failed to delete layer');
}
// ─── Blocks ─────────────────────────────────────────────
@@ -293,10 +296,11 @@ export async function updateBlock(token: string, blockId: string, patch: Partial
}
export async function deleteBlock(token: string, blockId: string): Promise<void> {
await fetch(`${API_BASE}/api/blocks/${blockId}`, {
const res = await fetch(`${API_BASE}/api/blocks/${blockId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error('Failed to delete block');
}
// ─── Composite: Load full project data ───────────────────
@@ -642,10 +646,11 @@ export async function renameGlobalFolder(token: string, id: string, name: string
}
export async function deleteGlobalFolder(token: string, id: string): Promise<void> {
await fetch(`${API_BASE}/api/global-folders/${id}`, {
const res = await fetch(`${API_BASE}/api/global-folders/${id}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error('Failed to delete global folder');
}
// ─── Global Blocks ────────────────────────────────────────
@@ -694,10 +699,11 @@ export async function renameGlobalBlock(token: string, id: string, name: string)
}
export async function deleteGlobalBlock(token: string, id: string): Promise<void> {
await fetch(`${API_BASE}/api/global-blocks/${id}`, {
const res = await fetch(`${API_BASE}/api/global-blocks/${id}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error('Failed to delete global block');
}
export { API_BASE };