test: add JS test framework using Node built-in test runner

Extracted pure utility functions (esc, sanitize, formatBytes, formatTime)
into web/ui/util.js with CommonJS export for Node testing compatibility.
Added 19 tests across 4 suites using node:test + node:assert (zero deps).

- util.js: browser-compatible utility functions
- util.test.js: 19 tests (5 esc, 3 sanitize, 6 formatBytes, 5 formatTime)
- package.json: 'test' script → node --test util.test.js
- mise.toml: js-test task
- app.js: removed duplicate helper functions (now in util.js)
- index.html: load util.js before app.js
This commit was merged in pull request #14.
This commit is contained in:
2026-06-22 02:23:24 -07:00
parent d7a17eb054
commit b5650c53ba
6 changed files with 125 additions and 28 deletions
-26
View File
@@ -307,12 +307,6 @@ function updateProgress() {
}
}
function formatTime(secs) {
const m = Math.floor(secs / 60);
const s = Math.floor(secs % 60);
return m + ':' + s.toString().padStart(2, '0');
}
function setActiveItem(filename) {
document.querySelectorAll('.wav-item').forEach(el => {
el.classList.toggle('playing', el.dataset.file === filename);
@@ -325,26 +319,6 @@ function clearActiveItem() {
});
}
// ─── Helpers ──────────────────────────────────────────────────────
function esc(s) {
if (!s) return '';
return s.replace(/&/g, '&amp;').replace(/</g, '&lt;')
.replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
}
function sanitize(s) {
return s.replace(/[^a-zA-Z0-9_-]/g, '_');
}
function formatBytes(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
}
// Initial load
doSearch();