145 lines
6.3 KiB
JavaScript
145 lines
6.3 KiB
JavaScript
/**
|
|
* 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);
|