feat: intégration TikTak #197-200 + scoped tokens, webhooks filtrés, listes mtime/size ; fix e2e-server BUG-112
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Sonde des gestes de redimensionnement — instance Docker locale.
|
||||
* 1. presser dans le CORPS près d'une limite de colonne / ligne
|
||||
* → standards : ça doit SÉLECTIONNER une cellule, pas redimensionner ;
|
||||
* 2. double-clic sur un bord d'en-tête
|
||||
* → standards (Excel / Sheets) : ajustement automatique au contenu ;
|
||||
* 3. glisser un bord d'en-tête → doit toujours redimensionner (inchangé).
|
||||
*/
|
||||
import { chromium } from '@playwright/test';
|
||||
|
||||
const BASE = process.env.BASE_URL || 'http://localhost:2020';
|
||||
const results = [];
|
||||
const check = (name, ok, detail = '') => {
|
||||
results.push({ name, ok, detail });
|
||||
console.log(`${ok ? 'PASS' : 'FAIL'} — ${name}${detail ? ` (${detail})` : ''}`);
|
||||
};
|
||||
|
||||
const browser = await chromium.launch();
|
||||
const context = await browser.newContext({ viewport: { width: 1600, height: 1000 } });
|
||||
const page = await context.newPage();
|
||||
const styleCalls = [];
|
||||
page.on('request', (r) => {
|
||||
if (r.url().includes('/xlsx/style') && r.method() === 'PUT') {
|
||||
styleCalls.push(r.postDataJSON ? r.postDataJSON() : null);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await page.goto(BASE, { waitUntil: 'domcontentloaded' });
|
||||
const login = page.locator('#login-screen');
|
||||
await login.waitFor({ state: 'visible', timeout: 20000 }).catch(() => {});
|
||||
if (await login.isVisible().catch(() => false)) {
|
||||
await page.fill('#login-username', 'admin');
|
||||
await page.fill('#login-password', 'test123');
|
||||
await page.click('#login-btn');
|
||||
}
|
||||
await page.waitForFunction(() => window.__OBSIGATE_BOOTED === true, { timeout: 30000 });
|
||||
const treeItem = page.locator('.tree-item[data-vault="TestVault"][data-path="budget.xlsx"]');
|
||||
if (!(await treeItem.count())) {
|
||||
await page.locator('.tree-item.vault-item[data-vault="TestVault"]').first().click();
|
||||
await treeItem.waitFor({ state: 'attached', timeout: 10000 });
|
||||
}
|
||||
await treeItem.dblclick({ timeout: 10000 });
|
||||
await page.locator('#content-area .xlsx-panel:visible .xlsx-table').waitFor({ state: 'visible', timeout: 20000 });
|
||||
await page.waitForTimeout(600);
|
||||
|
||||
const ghostVisible = () =>
|
||||
page.evaluate(() => {
|
||||
const g = document.querySelector('.xlsx-resize-ghost');
|
||||
return !!g && !g.hidden;
|
||||
});
|
||||
const selectedCount = () =>
|
||||
page.evaluate(() => {
|
||||
const p = [...document.querySelectorAll('.xlsx-panel')].find((x) => getComputedStyle(x).display !== 'none');
|
||||
return p ? p.querySelectorAll('td.xlsx-selected').length : 0;
|
||||
});
|
||||
const colWidthPx = (letter) =>
|
||||
page.evaluate((L) => {
|
||||
const p = [...document.querySelectorAll('.xlsx-panel')].find((x) => getComputedStyle(x).display !== 'none');
|
||||
const th = [...p.querySelectorAll('thead th')].find((n) => n.textContent.trim() === L);
|
||||
return th ? Math.round(th.getBoundingClientRect().width) : -1;
|
||||
}, letter);
|
||||
|
||||
// ── 1. presser dans le CORPS, à 3 px de la limite A|B ──
|
||||
const abEdge = await page.evaluate(() => {
|
||||
const p = [...document.querySelectorAll('.xlsx-panel')].find((x) => getComputedStyle(x).display !== 'none');
|
||||
const ths = [...p.querySelectorAll('thead th')];
|
||||
const a = ths.find((t) => t.textContent.trim() === 'A');
|
||||
const r = a.getBoundingClientRect();
|
||||
return { x: r.right - 3, y: r.bottom + 60 }; // 60 px sous l'en-tête : dans le corps
|
||||
});
|
||||
await page.mouse.move(abEdge.x, abEdge.y);
|
||||
await page.mouse.down();
|
||||
await page.waitForTimeout(120);
|
||||
const bodyGhost = await ghostVisible();
|
||||
await page.mouse.up();
|
||||
await page.waitForTimeout(200);
|
||||
const bodySel = await selectedCount();
|
||||
check(
|
||||
'pression dans le corps près d\'une limite : SÉLECTION (pas de redimensionnement)',
|
||||
!bodyGhost,
|
||||
`ghost pendant la pression=${bodyGhost}, cellules sélectionnées=${bodySel}`,
|
||||
);
|
||||
|
||||
// ── 2. glisser le bord d'en-tête A|B : doit redimensionner ──
|
||||
styleCalls.length = 0;
|
||||
const headerEdge = await page.evaluate(() => {
|
||||
const p = [...document.querySelectorAll('.xlsx-panel')].find((x) => getComputedStyle(x).display !== 'none');
|
||||
const ths = [...p.querySelectorAll('thead th')];
|
||||
const a = ths.find((t) => t.textContent.trim() === 'A');
|
||||
const r = a.getBoundingClientRect();
|
||||
return { x: r.right, y: r.top + r.height / 2, w: Math.round(r.width) };
|
||||
});
|
||||
await page.mouse.move(headerEdge.x, headerEdge.y);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(headerEdge.x + 40, headerEdge.y, { steps: 5 });
|
||||
const ghostDuringDrag = await ghostVisible();
|
||||
await page.mouse.up();
|
||||
await page.waitForTimeout(500);
|
||||
const wAfterDrag = await colWidthPx('A');
|
||||
check(
|
||||
'glisser un bord d\'en-tête : ghost + écriture de la largeur',
|
||||
ghostDuringDrag && wAfterDrag > headerEdge.w,
|
||||
`ghost=${ghostDuringDrag}, A: ${headerEdge.w}px → ${wAfterDrag}px, PUT=${styleCalls.length}`,
|
||||
);
|
||||
|
||||
// ── 3. double-clic sur le bord d'en-tête A|B → ajustement automatique ──
|
||||
styleCalls.length = 0;
|
||||
const beforeDbl = await colWidthPx('A');
|
||||
await page.mouse.dblclick(headerEdge.x, headerEdge.y, { delay: 80 });
|
||||
await page.waitForTimeout(700);
|
||||
const afterDbl = await colWidthPx('A');
|
||||
const fitOp = styleCalls.some((c) => c && Array.isArray(c.ops) && c.ops.some((o) => o.op === 'col_width'));
|
||||
check(
|
||||
'double-clic sur un bord d\'en-tête : ajustement automatique (PUT col_width)',
|
||||
fitOp,
|
||||
`A: ${beforeDbl}px → ${afterDbl}px, PUT=${JSON.stringify(styleCalls)}`,
|
||||
);
|
||||
|
||||
// ── 4. double-clic sur le bord d'une ligne (gouttière des numéros) ──
|
||||
styleCalls.length = 0;
|
||||
const rowEdge = await page.evaluate(() => {
|
||||
const p = [...document.querySelectorAll('.xlsx-panel')].find((x) => getComputedStyle(x).display !== 'none');
|
||||
const th = p.querySelector('th.xlsx-rownum');
|
||||
const r = th.getBoundingClientRect();
|
||||
return { x: r.left + 10, y: r.bottom };
|
||||
});
|
||||
await page.mouse.dblclick(rowEdge.x, rowEdge.y, { delay: 80 });
|
||||
await page.waitForTimeout(700);
|
||||
const rowFitOp = styleCalls.some((c) => c && Array.isArray(c.ops) && c.ops.some((o) => o.op === 'row_height'));
|
||||
check(
|
||||
'double-clic sur un bord de ligne : ajustement automatique (PUT row_height)',
|
||||
rowFitOp,
|
||||
`PUT=${JSON.stringify(styleCalls)}`,
|
||||
);
|
||||
} catch (err) {
|
||||
check('exécution sans erreur', false, String(err && err.message ? err.message : err));
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
const failed = results.filter((r) => !r.ok);
|
||||
console.log(`\n${results.length - failed.length}/${results.length} vérifications OK`);
|
||||
process.exit(failed.length ? 1 : 0);
|
||||
Reference in New Issue
Block a user