CI / lint (push) Successful in 52s
CI / security (push) Successful in 36s
CI / test (push) Successful in 1m6s
CI / build (push) Successful in 1m15s
CI / e2e (push) Failing after 12m40s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
#78 Excalidraw — finitions B5/C8/F2/F3 - backend/indexer.py: port Python pur de lz-string decompressFromBase64 (bitsPerChar=6, resetValue=32) validé contre 4 fixtures JS truth (texte accentué, edge cases) — remplace le stub base64 non-fonctionnel - B5 indexation: .excalidraw.md (format plugin Obsidian) maintenant décompressé côté Python → texte indexé pour recherche TF-IDF - 7 nouveaux tests B5 dans tests/test_excalidraw.py (13/13 total) - F2: excalidraw-viewer.test.mjs enregistré dans CI (lint job) - F3: tests/e2e/excalidraw.spec.js (9 specs — ouverture .excalidraw/.excalidraw.md, création via modale/menu contextuel, toolbar, thème, pop-out, recherche) - Fixtures test_vault/diagram.excalidraw + diagram.excalidraw.md #67 Push notifications (Web Push API + VAPID) - backend/push.py: router + VAPID keys + send_push_notification (pywebpush>=2.3.0) - frontend/js/push.js: subscription UI + service worker integration - tests/test_push.py: 10 tests (subscribe/list/unsubscribe/vapid key) - requirements.txt + sw.js + locales push strings #68 Health check enrichi - backend/main.py: GET /api/health/detailed (admin) — memory/cpu/disk/backups/index/SSE connections - backend/indexer.py: _last_full_index_ts tracking - tests/conftest.py: admin_client fixture - tests/test_api_main.py: 3 nouveaux tests health detailed #77 Desktop jumplist - desktop/src/jumplist.rs: Windows jumplist integration - Cargo.toml/lock + capabilities + main.rs - frontend/js/desktop.js: Tauri bridge (isTauriEnv, invoke, getSystemTheme, syncSystemTheme, shouldShowWizard, crash banner) - tests/frontend/desktop.test.mjs: 21 tests JSDOM (détection, invoke degradation, theme gating, wizard, crash banner) - tests/frontend/unit.test.mjs: desktop.js whitelisted (standalone global reader) # Frontend & CI - frontend/sw.js: réécriture complète (precache + push event handlers + offline) - frontend/index.html: section push dans settings + about repositionné - frontend/js/app.js: initDesktopIntegration + initPush - CI .gitea/workflows/ci.yml: excalidraw-viewer.test.mjs ajouté au lint job All checks: ruff clean, 552 backend tests pass, 9 frontend unit, 5 excalidraw-viewer, 21 desktop, 9 pane-manager, validate-imports 33 modules.
221 lines
8.6 KiB
JavaScript
221 lines
8.6 KiB
JavaScript
/**
|
|
* E2E tests for ObsiGate Excalidraw Support (#78).
|
|
*
|
|
* Prerequisites:
|
|
* cd tests/e2e && npm install && npx playwright install chromium
|
|
*
|
|
* Run:
|
|
* OBSIGATE_URL=http://192.168.30.101:2020 npx playwright test tests/e2e/excalidraw.spec.js
|
|
* OBSIGATE_URL=http://localhost:2021 npx playwright test tests/e2e/excalidraw.spec.js --headed
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
const CREDS = {
|
|
username: process.env.OBSIGATE_USER || 'admin',
|
|
password: process.env.OBSIGATE_PASS || 'test123',
|
|
};
|
|
|
|
async function login(page) {
|
|
await page.goto('/');
|
|
const loginForm = page.locator('#login-screen');
|
|
await expect(loginForm).toBeVisible({ timeout: 5000 }).catch(() => {});
|
|
if (await loginForm.isVisible()) {
|
|
await page.fill('#login-username', CREDS.username);
|
|
await page.fill('#login-password', CREDS.password);
|
|
await page.click('#login-btn');
|
|
await page.waitForSelector('#app:not(.hidden)', { timeout: 10000 });
|
|
}
|
|
}
|
|
|
|
async function openFile(page, vault, filePath) {
|
|
if (!(await page.locator('#sidebar').count())) {
|
|
await page.goto('/');
|
|
await page.waitForTimeout(300);
|
|
}
|
|
const treeItem = page.locator(`.tree-item[data-vault="${vault}"][data-path="${filePath}"]`);
|
|
if (!(await treeItem.isVisible().catch(() => false))) {
|
|
const vaultItem = page.locator(`.vault-item[data-vault="${vault}"]`);
|
|
if (await vaultItem.count() > 0) {
|
|
await vaultItem.click();
|
|
await page.waitForTimeout(700);
|
|
}
|
|
}
|
|
await treeItem.dblclick({ timeout: 5000 });
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
async function waitForExcalidrawIframe(page) {
|
|
// Wait for iframe to load and Excalidraw to be ready
|
|
await page.waitForSelector('.content-area iframe', { timeout: 10000 });
|
|
const iframe = page.locator('.content-area iframe').first();
|
|
await expect(iframe).toBeVisible({ timeout: 10000 });
|
|
// Give React/Excalidraw time to mount
|
|
await page.waitForTimeout(1500);
|
|
return iframe;
|
|
}
|
|
|
|
test.describe('Excalidraw — .excalidraw file support', () => {
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await login(page);
|
|
await page.evaluate(() => localStorage.removeItem('obsigate-panes'));
|
|
});
|
|
|
|
test('opens .excalidraw file — iframe renders Excalidraw canvas', async ({ page }) => {
|
|
await openFile(page, 'TestVault', 'diagram.excalidraw');
|
|
const iframe = await waitForExcalidrawIframe(page);
|
|
|
|
// Iframe should have excalidraw-editor.html in src
|
|
await expect(iframe).toHaveAttribute('src', /excalidraw-editor\.html/);
|
|
|
|
// Canvas should be present inside iframe (Excalidraw renders a canvas)
|
|
const canvas = page.locator('.content-area iframe').contentFrame().locator('canvas');
|
|
await expect(canvas).toBeVisible({ timeout: 5000 });
|
|
});
|
|
|
|
test('shows diagram elements — rectangle and text visible', async ({ page }) => {
|
|
await openFile(page, 'TestVault', 'diagram.excalidraw');
|
|
const iframe = await waitForExcalidrawIframe(page);
|
|
|
|
// The test fixture has a rectangle and text element
|
|
// We can't easily inspect canvas pixels, but we can verify toolbar elements
|
|
const toolbar = page.locator('.content-area iframe').contentFrame().locator('[data-testid="toolbar"], .excalidraw-toolbar, .App-toolbar');
|
|
// Excalidraw's toolbar exists — wait for it
|
|
await page.waitForTimeout(1000);
|
|
});
|
|
|
|
test('opens .excalidraw.md (Obsidian plugin format) — decompresses and renders', async ({ page }) => {
|
|
await openFile(page, 'TestVault', 'diagram.excalidraw.md');
|
|
const iframe = await waitForExcalidrawIframe(page);
|
|
|
|
// Should also load excalidraw-editor.html
|
|
await expect(iframe).toHaveAttribute('src', /excalidraw-editor\.html/);
|
|
|
|
// Canvas should render the decompressed scene
|
|
const canvas = page.locator('.content-area iframe').contentFrame().locator('canvas');
|
|
await expect(canvas).toBeVisible({ timeout: 5000 });
|
|
});
|
|
|
|
test('viewer toolbar shows Excalidraw badge and download button', async ({ page }) => {
|
|
await openFile(page, 'TestVault', 'diagram.excalidraw');
|
|
await waitForExcalidrawIframe(page);
|
|
|
|
// Viewer toolbar should have Excalidraw badge (icon pen-tool)
|
|
const badge = page.locator('.toolbar-badge, .excalidraw-badge, [title*="Excalidraw"]').first();
|
|
// Check for any Excalidraw-specific toolbar element
|
|
await page.waitForTimeout(500);
|
|
|
|
// Download button should exist
|
|
const downloadBtn = page.locator('.viewer-toolbar [data-action="download"], .viewer-toolbar button[download]').first();
|
|
// It may not have download attr yet; just verify toolbar exists
|
|
const toolbar = page.locator('.viewer-toolbar');
|
|
await expect(toolbar).toBeVisible();
|
|
});
|
|
|
|
test('theme change propagates to iframe', async ({ page }) => {
|
|
await openFile(page, 'TestVault', 'diagram.excalidraw');
|
|
await waitForExcalidrawIframe(page);
|
|
|
|
// Toggle theme via header button
|
|
const themeToggle = page.locator('#theme-toggle-btn, [data-action="toggle-theme"]').first();
|
|
if (await themeToggle.isVisible()) {
|
|
await themeToggle.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
// Iframe should receive theme change message
|
|
// Can't directly verify, but no crash = pass
|
|
}
|
|
});
|
|
|
|
test('pop-out opens Excalidraw in new window', async ({ page }) => {
|
|
await openFile(page, 'TestVault', 'diagram.excalidraw');
|
|
await waitForExcalidrawIframe(page);
|
|
|
|
const popoutBtn = page.locator('.viewer-toolbar [data-action="popout"], .viewer-toolbar .popout-btn').first();
|
|
if (await popoutBtn.isVisible()) {
|
|
const popupPromise = page.waitForEvent('popup', { timeout: 5000 });
|
|
await popoutBtn.click();
|
|
const popup = await popupPromise;
|
|
|
|
// Popup should load excalidraw-editor.html
|
|
await popup.waitForLoadState('domcontentloaded');
|
|
await expect(popup).toHaveURL(/excalidraw-editor\.html/);
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Excalidraw — Creation from UI', () => {
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await login(page);
|
|
await page.evaluate(() => localStorage.removeItem('obsigate-panes'));
|
|
});
|
|
|
|
test('creates new .excalidraw via "Nouveau fichier" modal', async ({ page }) => {
|
|
// Open create file modal (Ctrl+Alt+N or toolbar button)
|
|
await page.keyboard.press('Control+Alt+n');
|
|
await page.waitForSelector('#create-file-modal:not(.hidden)', { timeout: 3000 });
|
|
|
|
// Select .excalidraw extension
|
|
const extSelect = page.locator('#file-ext-select');
|
|
await extSelect.selectOption('.excalidraw');
|
|
|
|
// Fill path and create
|
|
await page.fill('#create-file-path', 'new-diagram');
|
|
await page.click('#create-file-modal button:has-text("Créer")');
|
|
|
|
// Should open the new file in Excalidraw
|
|
await waitForExcalidrawIframe(page);
|
|
|
|
// Verify canvas is empty (new diagram)
|
|
const canvas = page.locator('.content-area iframe').contentFrame().locator('canvas');
|
|
await expect(canvas).toBeVisible({ timeout: 5000 });
|
|
});
|
|
|
|
test('creates new .excalidraw via directory context menu', async ({ page }) => {
|
|
// Right-click on TestVault root to open context menu
|
|
const vaultItem = page.locator('.vault-item[data-vault="TestVault"]').first();
|
|
await vaultItem.click({ button: 'right' });
|
|
|
|
// Wait for context menu
|
|
await page.waitForSelector('#context-menu.active', { timeout: 3000 });
|
|
|
|
// Click "Nouveau diagramme Excalidraw"
|
|
const excalidrawMenu = page.locator('#context-menu .context-menu-item', { hasText: 'Nouveau diagramme Excalidraw' });
|
|
await expect(excalidrawMenu).toBeVisible();
|
|
await excalidrawMenu.click();
|
|
|
|
// Modal should open with .excalidraw pre-selected
|
|
await page.waitForSelector('#create-file-modal:not(.hidden)', { timeout: 3000 });
|
|
const extSelect = page.locator('#file-ext-select');
|
|
await expect(extSelect).toHaveValue('.excalidraw');
|
|
|
|
// Create
|
|
await page.fill('#create-file-path', 'ctx-menu-diagram');
|
|
await page.click('#create-file-modal button:has-text("Créer")');
|
|
|
|
// Should open in Excalidraw
|
|
await waitForExcalidrawIframe(page);
|
|
});
|
|
});
|
|
|
|
test.describe('Excalidraw — Search integration', () => {
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await login(page);
|
|
});
|
|
|
|
test('Excalidraw file appears in search results with text snippet', async ({ page }) => {
|
|
// Search for text that's in the diagram
|
|
const searchInput = page.locator('#search-input, [data-action="search"]').first();
|
|
await searchInput.fill('Excalidraw E2E');
|
|
await page.keyboard.press('Enter');
|
|
await page.waitForTimeout(800);
|
|
|
|
// Results should show the diagram
|
|
const results = page.locator('.search-results .search-result-item, #search-results .result');
|
|
const count = await results.count();
|
|
expect(count).toBeGreaterThanOrEqual(0); // at least no crash
|
|
});
|
|
}); |