adds tests
This commit is contained in:
113
tests/helpers.test.js
Normal file
113
tests/helpers.test.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
|
||||
// ── esc() ─────────────────────────────────────────────────────────────────────
|
||||
// Mirrors the implementation in ui.js exactly (DOM-based).
|
||||
// Tests the XSS contract — if the implementation changes, these define
|
||||
// what it must still guarantee.
|
||||
|
||||
function esc(str) {
|
||||
const d = document.createElement('div')
|
||||
d.textContent = (str == null) ? '' : String(str)
|
||||
return d.innerHTML
|
||||
}
|
||||
|
||||
describe('esc', () => {
|
||||
it('passes through plain strings unchanged', () => {
|
||||
expect(esc('plex')).toBe('plex')
|
||||
expect(esc('postgres-primary')).toBe('postgres-primary')
|
||||
})
|
||||
|
||||
it('escapes < and >', () => {
|
||||
expect(esc('<script>')).toBe('<script>')
|
||||
expect(esc('</script>')).toBe('</script>')
|
||||
})
|
||||
|
||||
it('neutralises a script injection payload', () => {
|
||||
const payload = '<script>alert(1)</script>'
|
||||
expect(esc(payload)).not.toContain('<script>')
|
||||
})
|
||||
|
||||
it('neutralises an img onerror payload', () => {
|
||||
const result = esc('<img src=x onerror=alert(1)>')
|
||||
expect(result).not.toContain('<img')
|
||||
expect(result).toContain('<img')
|
||||
expect(result).toContain('>')
|
||||
})
|
||||
|
||||
it('escapes ampersands', () => {
|
||||
expect(esc('a & b')).toBe('a & b')
|
||||
})
|
||||
|
||||
it('handles null without throwing', () => {
|
||||
expect(() => esc(null)).not.toThrow()
|
||||
expect(esc(null)).toBe('')
|
||||
})
|
||||
|
||||
it('handles undefined without throwing', () => {
|
||||
expect(() => esc(undefined)).not.toThrow()
|
||||
expect(esc(undefined)).toBe('')
|
||||
})
|
||||
|
||||
it('coerces numbers to string', () => {
|
||||
expect(esc(137)).toBe('137')
|
||||
})
|
||||
})
|
||||
|
||||
// ── fmtDate() ─────────────────────────────────────────────────────────────────
|
||||
|
||||
function fmtDate(d) {
|
||||
if (!d) return '—'
|
||||
try {
|
||||
return new Date(d).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })
|
||||
} catch (e) { return d }
|
||||
}
|
||||
|
||||
describe('fmtDate', () => {
|
||||
it('formats a valid ISO date string', () => {
|
||||
const result = fmtDate('2024-03-15T00:00:00')
|
||||
expect(result).toMatch(/Mar/)
|
||||
expect(result).toMatch(/15/)
|
||||
expect(result).toMatch(/2024/)
|
||||
})
|
||||
|
||||
it('returns — for null', () => {
|
||||
expect(fmtDate(null)).toBe('—')
|
||||
})
|
||||
|
||||
it('returns — for empty string', () => {
|
||||
expect(fmtDate('')).toBe('—')
|
||||
})
|
||||
|
||||
it('returns — for undefined', () => {
|
||||
expect(fmtDate(undefined)).toBe('—')
|
||||
})
|
||||
})
|
||||
|
||||
// ── fmtDateFull() ─────────────────────────────────────────────────────────────
|
||||
|
||||
function fmtDateFull(d) {
|
||||
if (!d) return '—'
|
||||
try {
|
||||
return new Date(d).toLocaleString('en-US', {
|
||||
year: 'numeric', month: 'short', day: 'numeric',
|
||||
hour: '2-digit', minute: '2-digit',
|
||||
})
|
||||
} catch (e) { return d }
|
||||
}
|
||||
|
||||
describe('fmtDateFull', () => {
|
||||
it('includes date and time components', () => {
|
||||
const result = fmtDateFull('2024-03-15T14:30:00')
|
||||
expect(result).toMatch(/Mar/)
|
||||
expect(result).toMatch(/2024/)
|
||||
expect(result).toMatch(/\d{1,2}:\d{2}/)
|
||||
})
|
||||
|
||||
it('returns — for null', () => {
|
||||
expect(fmtDateFull(null)).toBe('—')
|
||||
})
|
||||
|
||||
it('returns — for empty string', () => {
|
||||
expect(fmtDateFull('')).toBe('—')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user