From ffc6dac172c0f2d612cd3482616ba3df5396a280 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Thu, 28 May 2026 18:46:10 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20frontend=20tests=20=E2=80=94=20import/e?= =?UTF-8?q?xport=20validator=20+=20unit=20tests=20+=20CI=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tests/frontend/validate-imports.mjs: 0 errors on 13 modules, 79 exports Detects: missing exports, broken imports, const reassignments - tests/frontend/unit.test.mjs: escapeHtml, state object, module syntax - Added to CI lint job (runs after Ruff + Mypy) --- .gitea/workflows/ci.yml | 6 + frontend/js/legacy.js | 8 +- tests/frontend/unit.test.mjs | 149 ++++++++++++++++++++++ tests/frontend/validate-imports.mjs | 191 ++++++++++++++++++++++++++++ 4 files changed, 350 insertions(+), 4 deletions(-) create mode 100644 tests/frontend/unit.test.mjs create mode 100644 tests/frontend/validate-imports.mjs diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 21eadef..c14e587 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -32,6 +32,12 @@ jobs: - name: Mypy (type checker) run: mypy backend/ --ignore-missing-imports || echo "mypy found type errors (advisory — 28 pre-existing issues)" + - name: Frontend validation + run: node tests/frontend/validate-imports.mjs + + - name: Frontend unit tests + run: node tests/frontend/unit.test.mjs + # ── Tests ───────────────────────────────────────────────────────── test: needs: lint diff --git a/frontend/js/legacy.js b/frontend/js/legacy.js index 3f6b7f4..a820873 100644 --- a/frontend/js/legacy.js +++ b/frontend/js/legacy.js @@ -44,7 +44,7 @@ function hideProgressBar() { // loadVaultSettings // ========================================================================= -async function loadVaultSettings() { +export async function loadVaultSettings() { try { const settings = await api("/api/vaults/settings/all"); state.vaultSettings = settings; @@ -84,7 +84,7 @@ function _isInputFocused() { // initSearch // ========================================================================= -function initSearch() { +export function initSearch() { const input = document.getElementById("search-input"); if (!input) return; const caseBtn = document.getElementById("search-case-btn"); @@ -309,7 +309,7 @@ function initSearch() { // showWelcome // ========================================================================= -function showWelcome() { +export function showWelcome() { hideProgressBar(); // Restore or rebuild the dashboard with tabbed sections @@ -422,7 +422,7 @@ function showWelcome() { // goHome // ========================================================================= -function goHome() { +export function goHome() { const searchInput = document.getElementById("search-input"); if (searchInput) searchInput.value = ""; diff --git a/tests/frontend/unit.test.mjs b/tests/frontend/unit.test.mjs new file mode 100644 index 0000000..39be6ce --- /dev/null +++ b/tests/frontend/unit.test.mjs @@ -0,0 +1,149 @@ +#!/usr/bin/env node +/** + * ObsiGate — Frontend unit tests (pure functions, no DOM required). + * Usage: node tests/frontend/unit.test.mjs + */ + +import { strict as assert } from 'assert'; + +// ── Test escapeHtml (from utils.js) ──────────────────────────────────────── +// We test the logic directly since utils.js imports from state.js which needs DOM +const escapeHtml = (str) => { + if (!str) return ''; + return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); +}; + +function testEscapeHtml() { + assert.strictEqual(escapeHtml(''), ''); + assert.strictEqual(escapeHtml(null), ''); + assert.strictEqual(escapeHtml(undefined), ''); + assert.strictEqual(escapeHtml('hello'), 'hello'); + assert.strictEqual(escapeHtml('