fix(workspace): menu contextuel après navigation partielle + sync sidebar/header au rename
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
const FD_BASE = process.env.FD_BASE_URL || 'http://localhost:8080';
|
||||
const FD_PASS = process.env.FD_PASS || 'e2e-secret-123';
|
||||
|
||||
test('diag v5: partial-nav ctx menu + rename sidebar/header sync', async ({ page }) => {
|
||||
test.setTimeout(180000);
|
||||
const errors = [];
|
||||
page.on('console', m => { if (m.type() === 'error') errors.push('CONSOLE: ' + m.text()); });
|
||||
page.on('pageerror', e => errors.push('PAGEERROR: ' + e.message));
|
||||
|
||||
// ── 1. Fresh user + workspace ──
|
||||
await page.goto(`${FD_BASE}/auth/login?provider=local`);
|
||||
await page.click('button:has-text("Register")');
|
||||
await page.fill('#name', 'Diag');
|
||||
await page.fill('#email', 'diag' + Date.now() + '@flowdeck.local');
|
||||
await page.fill('#password', FD_PASS);
|
||||
await page.click('.btn-primary');
|
||||
await page.waitForURL('**/workspaces', { timeout: 20000 });
|
||||
await page.waitForTimeout(1200);
|
||||
// Dismiss any onboarding agent dialog that may cover the page
|
||||
await page.keyboard.press('Escape').catch(() => {});
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
// Create + select a workspace via API (CSRF-exempt), no fragile UI dialogs.
|
||||
const ws = await page.evaluate(async () => {
|
||||
const r = await fetch('/api/workspaces', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name: 'Diag WS ' + Date.now() })
|
||||
});
|
||||
const d = await r.json();
|
||||
const s = await fetch('/api/workspaces/' + d.id + '/select', { method: 'POST' });
|
||||
return { id: d.id, ok: s.ok };
|
||||
});
|
||||
console.log('WS:', JSON.stringify(ws));
|
||||
await page.waitForTimeout(800);
|
||||
|
||||
// Seed items via API.
|
||||
await page.evaluate(async () => {
|
||||
const mk = (name, type) => fetch('/api/local-workspace/items', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, type })
|
||||
}).then(r => r.json());
|
||||
const p = await mk('RenameMe Doc', 'page');
|
||||
await mk('Folder A', 'folder');
|
||||
window.__seedPageId = p.id;
|
||||
});
|
||||
console.log('STEP1 ok — user + workspace + seeded (all via API)');
|
||||
|
||||
// ── 2. PARTIAL nav to /local-workspace (exactly the user's bug path) ──
|
||||
await page.evaluate(() => window.fdNavigate('/local-workspace'));
|
||||
await page.waitForURL('**/local-workspace**', { timeout: 10000 });
|
||||
await page.waitForFunction(() => document.querySelectorAll('[data-ws-id]').length >= 1, { timeout: 10000 });
|
||||
await page.waitForTimeout(1500);
|
||||
console.log('STEP2 ok — partial-nav LW tree items:', await page.locator('[data-ws-id]').count());
|
||||
|
||||
// ── 3. PARTIAL-nav ctx menu (local-workspace) ──
|
||||
const rows1 = page.locator('[data-ws-id]');
|
||||
if (await rows1.count()) {
|
||||
await rows1.first().click({ button: 'right' });
|
||||
await page.waitForTimeout(700);
|
||||
const st = await page.evaluate(() => {
|
||||
const s = window.Alpine && Alpine.store('fdCtx');
|
||||
return s ? { open: s.open, node: s.node && s.node.name } : { undefined: true };
|
||||
});
|
||||
const vis = await page.locator('.fd-ctx-menu').isVisible().catch(() => false);
|
||||
console.log('STEP3 LW ctx (partial-nav): visible=' + vis + ' fdCtx=' + JSON.stringify(st));
|
||||
await page.screenshot({ path: 'shots/diag_v5_lw_ctx.png' }).catch(() => {});
|
||||
await page.evaluate(() => { const s = window.Alpine && Alpine.store('fdCtx'); if (s) s.close(); });
|
||||
}
|
||||
|
||||
// ── 4. PARTIAL-nav ctx menu (library) ──
|
||||
await page.evaluate(() => window.fdNavigate('/library'));
|
||||
await page.waitForFunction(() => typeof window._libData !== 'undefined', { timeout: 10000 });
|
||||
await page.waitForTimeout(1500);
|
||||
const libRows = page.locator('.lib-row');
|
||||
console.log('STEP4 partial /library rows:', await libRows.count(),
|
||||
'libData:', await page.evaluate(() => typeof window._libData + ':' + typeof (window._libData && window._libData.onRowContextMenu)));
|
||||
if (await libRows.count()) {
|
||||
await libRows.first().click({ button: 'right' });
|
||||
await page.waitForTimeout(700);
|
||||
const vis = await page.locator('.fd-ctx-menu').isVisible().catch(() => false);
|
||||
const st = await page.evaluate(() => {
|
||||
const s = window.Alpine && Alpine.store('fdCtx');
|
||||
return s ? { open: s.open, node: s.node && s.node.name } : { undefined: true };
|
||||
});
|
||||
console.log('STEP5 LIB ctx (partial-nav): visible=' + vis + ' fdCtx=' + JSON.stringify(st));
|
||||
await page.screenshot({ path: 'shots/diag_v5_lib_ctx.png' }).catch(() => {});
|
||||
await page.evaluate(() => { const s = window.Alpine && Alpine.store('fdCtx'); if (s) s.close(); });
|
||||
}
|
||||
|
||||
// ── 5. Rename a page via local-workspace tree (partial-nav) ──
|
||||
await page.evaluate(() => window.fdNavigate('/local-workspace'));
|
||||
await page.waitForFunction(() => typeof window._wsData !== 'undefined' && typeof window._wsData.commitRename === 'function', { timeout: 10000 });
|
||||
await page.waitForFunction(() => document.querySelectorAll('[data-ws-id]').length >= 1, { timeout: 10000 });
|
||||
await page.waitForTimeout(1200);
|
||||
|
||||
const rn = await page.evaluate(async () => {
|
||||
const ws = window._wsData;
|
||||
const find = (name) => {
|
||||
const walk = (arr) => { if (!arr) return null; for (const n of arr) { if (n.name === name) return n; const c = walk(n.children); if (c) return c; } return null; };
|
||||
return walk(ws.displayTree) || walk(ws.tree);
|
||||
};
|
||||
const target = find('RenameMe Doc');
|
||||
if (!target) return { err: 'node RenameMe Doc not found' };
|
||||
ws.renamingId = target.id;
|
||||
ws.renamingName = 'Renamed Via LW';
|
||||
await ws.commitRename(target);
|
||||
return { id: target.id, name: target.name };
|
||||
});
|
||||
console.log('STEP6 rename page:', JSON.stringify(rn));
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const chk1 = await page.evaluate((pid) => {
|
||||
const row = [...document.querySelectorAll('[data-ws-id]')].find(n => String(n.getAttribute('data-ws-id')) === String(pid));
|
||||
const sb = document.querySelector('.sidebar-item.ws-tree-item[data-id="' + pid + '"] .page-name');
|
||||
const head = document.querySelector('.header-breadcrumb');
|
||||
const crumbs = head ? head.innerText.trim().split(/\s/).filter(Boolean) : [];
|
||||
return {
|
||||
inPageRow: row ? row.innerText.trim() : null,
|
||||
sidebarWsName: sb ? sb.textContent : null,
|
||||
headerCrumbs: crumbs,
|
||||
};
|
||||
}, rn.id);
|
||||
console.log('STEP7 after rename:', JSON.stringify(chk1, null, 2));
|
||||
|
||||
// ── 6. Rename the CURRENT folder → in-page breadcrumb must update (no reload) ──
|
||||
const fld = await page.evaluate(async () => {
|
||||
const ws = window._wsData;
|
||||
const walk = (arr) => { if (!arr) return null; for (const n of arr) { if (n.is_folder && n.name === 'Folder A') return n; const c = walk(n.children); if (c) return c; } return null; };
|
||||
const folder = walk(ws.displayTree) || walk(ws.tree);
|
||||
if (!folder) return { err: 'folder not found' };
|
||||
return { id: folder.id, name: folder.name };
|
||||
});
|
||||
// Partial-nav INTO the folder so the server renders the breadcrumb (folderStack)
|
||||
await page.evaluate((fid) => window.fdNavigate('/local-workspace?folder=' + fid), fld.id);
|
||||
await page.waitForFunction(() => {
|
||||
const ws = window._wsData;
|
||||
return ws && ws.folderStack && ws.folderStack.length >= 1;
|
||||
}, { timeout: 10000 });
|
||||
await page.waitForTimeout(1200);
|
||||
const beforeCrumb = await page.evaluate(() => {
|
||||
const c = document.querySelector('.unified-breadcrumb .bc-current');
|
||||
return c ? c.textContent.trim() : null;
|
||||
});
|
||||
console.log('STEP8 crumb before rename:', beforeCrumb);
|
||||
const rnf = await page.evaluate(async (fid) => {
|
||||
const ws = window._wsData;
|
||||
const cur = ws.folderStack && ws.folderStack[ws.folderStack.length - 1];
|
||||
if (!cur) return { err: 'no folderStack' };
|
||||
ws.renamingId = cur.id;
|
||||
ws.renamingName = 'Folder Renamed';
|
||||
await ws.commitRename(cur);
|
||||
return { id: cur.id, name: cur.name, bc: ws.breadcrumb.map(b => b.name) };
|
||||
}, fld.id);
|
||||
console.log('STEP9 rename folder:', JSON.stringify(rnf));
|
||||
await page.waitForTimeout(2000);
|
||||
const chk2 = await page.evaluate((fid) => {
|
||||
const crumb = document.querySelector('.unified-breadcrumb .bc-current');
|
||||
const sb = document.querySelector('.sidebar-item.ws-tree-item[data-id="' + fid + '"] .page-name');
|
||||
return {
|
||||
crumbLabel: crumb ? crumb.textContent.trim() : null,
|
||||
sidebarWsName: sb ? sb.textContent : null,
|
||||
};
|
||||
}, fld.id);
|
||||
console.log('STEP10 after folder rename:', JSON.stringify(chk2, null, 2));
|
||||
await page.screenshot({ path: 'shots/diag_v5_folder_rename.png' }).catch(() => {});
|
||||
|
||||
console.log('ERRORS:');
|
||||
errors.slice(0, 40).forEach(e => console.log(e));
|
||||
});
|
||||
Reference in New Issue
Block a user