124 lines
3.4 KiB
JavaScript
124 lines
3.4 KiB
JavaScript
/**
|
|
* Tests for js/utils/helpers.js
|
|
*/
|
|
|
|
import { test, describe, assertEquals, assertTrue } from './test-runner.js'
|
|
import { escapeHTML, formatRelativeTime, debounce, truncate } from '../js/utils/helpers.js'
|
|
|
|
describe('escapeHTML', () => {
|
|
test('escapes < and >', () => {
|
|
assertEquals(escapeHTML('<script>'), '<script>')
|
|
})
|
|
|
|
test('escapes quotes', () => {
|
|
assertEquals(escapeHTML('"hello"'), '"hello"')
|
|
})
|
|
|
|
test('escapes ampersand', () => {
|
|
assertEquals(escapeHTML('a & b'), 'a & b')
|
|
})
|
|
|
|
test('escapes single quotes', () => {
|
|
assertEquals(escapeHTML("it's"), "it's")
|
|
})
|
|
|
|
test('handles null', () => {
|
|
assertEquals(escapeHTML(null), '')
|
|
})
|
|
|
|
test('handles undefined', () => {
|
|
assertEquals(escapeHTML(undefined), '')
|
|
})
|
|
|
|
test('handles numbers by converting to string', () => {
|
|
assertEquals(escapeHTML(123), '123')
|
|
})
|
|
|
|
test('handles complex XSS attempt', () => {
|
|
const xss = '<img src="x" onerror="alert(1)">'
|
|
assertTrue(!escapeHTML(xss).includes('<'))
|
|
})
|
|
})
|
|
|
|
describe('formatRelativeTime', () => {
|
|
test('formats seconds ago', () => {
|
|
const date = new Date(Date.now() - 30000) // 30 seconds ago
|
|
const result = formatRelativeTime(date, 'en')
|
|
assertTrue(result.includes('second'))
|
|
})
|
|
|
|
test('formats minutes ago', () => {
|
|
const date = new Date(Date.now() - 5 * 60000) // 5 minutes ago
|
|
const result = formatRelativeTime(date, 'en')
|
|
assertTrue(result.includes('minute'))
|
|
})
|
|
|
|
test('formats hours ago', () => {
|
|
const date = new Date(Date.now() - 2 * 3600000) // 2 hours ago
|
|
const result = formatRelativeTime(date, 'en')
|
|
assertTrue(result.includes('hour'))
|
|
})
|
|
|
|
test('formats days ago', () => {
|
|
const date = new Date(Date.now() - 3 * 86400000) // 3 days ago
|
|
const result = formatRelativeTime(date, 'en')
|
|
assertTrue(result.includes('day'))
|
|
})
|
|
|
|
test('accepts string date', () => {
|
|
const date = new Date(Date.now() - 60000).toISOString()
|
|
const result = formatRelativeTime(date, 'en')
|
|
assertTrue(result.includes('minute'))
|
|
})
|
|
|
|
test('respects locale de', () => {
|
|
const date = new Date(Date.now() - 60000)
|
|
const result = formatRelativeTime(date, 'de')
|
|
assertTrue(result.includes('Minute'))
|
|
})
|
|
})
|
|
|
|
describe('truncate', () => {
|
|
test('truncates long strings', () => {
|
|
assertEquals(truncate('Hello World', 6), 'Hello…')
|
|
})
|
|
|
|
test('does not truncate short strings', () => {
|
|
assertEquals(truncate('Hi', 10), 'Hi')
|
|
})
|
|
|
|
test('handles exact length', () => {
|
|
assertEquals(truncate('Hello', 5), 'Hello')
|
|
})
|
|
|
|
test('handles null', () => {
|
|
assertEquals(truncate(null, 10), null)
|
|
})
|
|
|
|
test('handles empty string', () => {
|
|
assertEquals(truncate('', 10), '')
|
|
})
|
|
|
|
test('uses default maxLength of 100', () => {
|
|
const long = 'a'.repeat(150)
|
|
const result = truncate(long)
|
|
assertEquals(result.length, 100)
|
|
assertTrue(result.endsWith('…'))
|
|
})
|
|
})
|
|
|
|
describe('debounce', () => {
|
|
test('returns a function', () => {
|
|
const fn = debounce(() => {})
|
|
assertEquals(typeof fn, 'function')
|
|
})
|
|
|
|
test('delays execution', (done) => {
|
|
let called = false
|
|
const fn = debounce(() => { called = true }, 50)
|
|
|
|
fn()
|
|
assertEquals(called, false)
|
|
})
|
|
})
|