7124b16fc1
Docs: - AGENTS.md: updated toolchain section with js-lint/js-test tasks, expanded web/ui/ directory listing, added event delegation and Web Audio API to key patterns, updated testing section - README: marked issue #9 (Audio Preview Player) as done - .gitignore: added akai-fetch.test and cover.out Tests: - Moved groupWavs and cleanName to util.js (pure functions) - Added 10 new JS tests: 6 groupWavs, 4 cleanName - Total JS tests: 29 (up from 19)
161 lines
4.3 KiB
JavaScript
161 lines
4.3 KiB
JavaScript
const { describe, it } = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
const { esc, sanitize, formatBytes, formatTime, groupWavs, cleanName } = require('./util.js')
|
|
|
|
describe('esc', () => {
|
|
it('escapes HTML entities', () => {
|
|
assert.strictEqual(esc('<script>alert("xss")</script>'),
|
|
'<script>alert("xss")</script>')
|
|
})
|
|
|
|
it('handles ampersands', () => {
|
|
assert.strictEqual(esc('a & b'), 'a & b')
|
|
})
|
|
|
|
it('handles single quotes', () => {
|
|
assert.strictEqual(esc("it's"), 'it's')
|
|
})
|
|
|
|
it('returns empty string for falsy input', () => {
|
|
assert.strictEqual(esc(''), '')
|
|
assert.strictEqual(esc(null), '')
|
|
assert.strictEqual(esc(undefined), '')
|
|
})
|
|
|
|
it('passes through safe strings', () => {
|
|
assert.strictEqual(esc('Hello World'), 'Hello World')
|
|
})
|
|
})
|
|
|
|
describe('sanitize', () => {
|
|
it('replaces special characters with underscores', () => {
|
|
assert.strictEqual(sanitize('hello world!'), 'hello_world_')
|
|
})
|
|
|
|
it('preserves alphanumeric and dashes', () => {
|
|
assert.strictEqual(sanitize('test-123_abc'), 'test-123_abc')
|
|
})
|
|
|
|
it('sanitizes dots and slashes', () => {
|
|
assert.strictEqual(sanitize('path/to/file.name'),
|
|
'path_to_file_name')
|
|
})
|
|
})
|
|
|
|
describe('formatBytes', () => {
|
|
it('formats zero', () => {
|
|
assert.strictEqual(formatBytes(0), '0 B')
|
|
})
|
|
|
|
it('formats bytes', () => {
|
|
assert.strictEqual(formatBytes(500), '500 B')
|
|
})
|
|
|
|
it('formats KB', () => {
|
|
assert.strictEqual(formatBytes(1024), '1 KB')
|
|
})
|
|
|
|
it('formats MB', () => {
|
|
assert.strictEqual(formatBytes(1048576), '1 MB')
|
|
})
|
|
|
|
it('formats GB', () => {
|
|
assert.strictEqual(formatBytes(1073741824), '1 GB')
|
|
})
|
|
|
|
it('formats fractional', () => {
|
|
assert.strictEqual(formatBytes(1536), '1.5 KB')
|
|
})
|
|
})
|
|
|
|
describe('formatTime', () => {
|
|
it('formats zero', () => {
|
|
assert.strictEqual(formatTime(0), '0:00')
|
|
})
|
|
|
|
it('formats seconds', () => {
|
|
assert.strictEqual(formatTime(5), '0:05')
|
|
})
|
|
|
|
it('formats minutes and seconds', () => {
|
|
assert.strictEqual(formatTime(65), '1:05')
|
|
})
|
|
|
|
it('formats double-digit seconds', () => {
|
|
assert.strictEqual(formatTime(75), '1:15')
|
|
})
|
|
|
|
it('formats large times', () => {
|
|
assert.strictEqual(formatTime(3661), '61:01')
|
|
})
|
|
})
|
|
|
|
describe('groupWavs', () => {
|
|
it('groups by library and program', () => {
|
|
const paths = [
|
|
'LibA/Prog1/kick.wav',
|
|
'LibA/Prog1/snare.wav',
|
|
'LibA/Prog2/hat.wav'
|
|
]
|
|
const result = groupWavs(paths)
|
|
assert.strictEqual(Object.keys(result).length, 1)
|
|
assert.strictEqual(Object.keys(result.LibA).length, 2)
|
|
assert.strictEqual(result.LibA.Prog1.length, 2)
|
|
assert.strictEqual(result.LibA.Prog2.length, 1)
|
|
})
|
|
|
|
it('handles multiple libraries', () => {
|
|
const paths = [
|
|
'LibA/kick.wav',
|
|
'LibB/snare.wav'
|
|
]
|
|
const result = groupWavs(paths)
|
|
assert.strictEqual(Object.keys(result).length, 2)
|
|
assert.strictEqual(result.LibA.LibA[0].name, 'kick')
|
|
assert.strictEqual(result.LibB.LibB[0].name, 'snare')
|
|
})
|
|
|
|
it('strips .wav extension case-insensitively', () => {
|
|
const paths = ['Lib/Prog/KICK.WAV']
|
|
const result = groupWavs(paths)
|
|
assert.strictEqual(result.Lib.Prog[0].name, 'KICK')
|
|
})
|
|
|
|
it('handles deeply nested paths', () => {
|
|
const paths = ['A/B/C/D/file.wav']
|
|
const result = groupWavs(paths)
|
|
assert.strictEqual(result.A.B.length, 1)
|
|
assert.strictEqual(result.A.B[0].name, 'file')
|
|
assert.strictEqual(result.A.B[0].fullPath, 'A/B/C/D/file.wav')
|
|
})
|
|
|
|
it('handles flat paths with no directory', () => {
|
|
const paths = ['sound.wav']
|
|
const result = groupWavs(paths)
|
|
assert.strictEqual(Object.keys(result).length, 1)
|
|
assert.strictEqual(Object.keys(result['']).length, 1)
|
|
})
|
|
|
|
it('returns empty object for empty input', () => {
|
|
const result = groupWavs([])
|
|
assert.strictEqual(Object.keys(result).length, 0)
|
|
})
|
|
})
|
|
|
|
describe('cleanName', () => {
|
|
it('replaces underscores with spaces', () => {
|
|
assert.strictEqual(cleanName('hello_world'), 'hello world')
|
|
})
|
|
|
|
it('collapses multiple underscores', () => {
|
|
assert.strictEqual(cleanName('AMG_-_Black__II'), 'AMG - Black II')
|
|
})
|
|
|
|
it('trims leading/trailing spaces from underscores', () => {
|
|
assert.strictEqual(cleanName('_padded_'), 'padded')
|
|
})
|
|
|
|
it('preserves regular text', () => {
|
|
assert.strictEqual(cleanName('Kick Drum'), 'Kick Drum')
|
|
})
|
|
}) |