fixing rolodex and search

This commit is contained in:
HotSwapp
2025-08-11 21:58:25 -05:00
parent 278eb7c5d4
commit c76b68d009
25 changed files with 1651 additions and 915 deletions

View File

@@ -0,0 +1,58 @@
/** @jest-environment jsdom */
const path = require('path');
// Load sanitizer utility first so alerts can delegate to it
require(path.join(__dirname, '..', 'sanitizer.js'));
// Load the alerts module (IIFE attaches itself to window)
require(path.join(__dirname, '..', 'alerts.js'));
describe('alerts._sanitize', () => {
const sanitize = window.alerts && window.alerts._sanitize;
it('should be a function', () => {
expect(typeof sanitize).toBe('function');
});
it('removes <script> tags and event-handler attributes', () => {
const dirty = '<img src="x" onerror="alert(1)"><script>alert("x")</script><p>Hello</p>';
const clean = sanitize(dirty);
expect(clean).toContain('<img src="x">');
expect(clean).toContain('<p>Hello</p>');
expect(clean).not.toMatch(/<script/i);
expect(clean).not.toMatch(/onerror/i);
});
it('uses DOMPurify after it is lazily loaded', async () => {
// Ensure DOMPurify is not present initially
delete window.DOMPurify;
const mockPurify = {
sanitize: jest.fn((html) => `CLEAN:${html}`)
};
// Spy on the shared sanitizer loader and inject DOMPurify once called
const loaderSpy = jest
.spyOn(window.htmlSanitizer, 'ensureDOMPurifyLoaded')
.mockImplementation(() => {
window.DOMPurify = mockPurify;
return Promise.resolve(mockPurify);
});
const dirty = '<span onclick="evil()">Hi</span>';
// First call: fallback sanitizer, DOMPurify not used yet
const first = sanitize(dirty);
expect(mockPurify.sanitize).not.toHaveBeenCalled();
expect(loaderSpy).toHaveBeenCalledTimes(1);
// Wait for loader promise to resolve
await loaderSpy.mock.results[0].value;
// Second call: should use DOMPurify
const second = sanitize(dirty);
expect(mockPurify.sanitize).toHaveBeenCalledTimes(1);
expect(second).toBe(`CLEAN:${dirty}`);
loaderSpy.mockRestore();
});
});

View File

@@ -0,0 +1,56 @@
/** @jest-environment jsdom */
require('../sanitizer.js');
describe('htmlSanitizer', () => {
it('escape() encodes special HTML chars', () => {
const { escape } = window.htmlSanitizer;
expect(escape('<div>')).toBe('&lt;div&gt;');
expect(escape('Tom & Jerry')).toBe('Tom &amp; Jerry');
expect(escape('"quotes" and \'apostrophes\'')).toContain('&quot;');
});
it('sanitize() returns safe HTML and does not double-escape plain text', () => {
const { sanitize, escape } = window.htmlSanitizer;
const dirty = '<img src=x onerror=alert(1)><p>Hello</p>';
const clean = sanitize(dirty);
expect(clean).toContain('<img');
expect(clean).toContain('<p>Hello</p>');
expect(clean).not.toMatch(/onerror/i);
const text = '<b>bold</b>';
const escaped = escape(text);
const sanitizedEscaped = sanitize(escaped);
expect(sanitizedEscaped).toBe(escaped);
});
it('setSafeHTML sets sanitized HTML on the element', () => {
const el = document.createElement('div');
const dirty = '<img src=x onerror=alert(1)><p>Hello</p>';
window.setSafeHTML(el, dirty);
expect(el.innerHTML).toContain('<img');
expect(el.innerHTML).toContain('<p>Hello</p>');
expect(el.innerHTML).not.toMatch(/onerror/i);
});
it('setSafeHTML uses DOMPurify when it becomes available after first call', () => {
// Ensure not present initially
delete window.DOMPurify;
const el = document.createElement('div');
const html = '<em>hello</em>';
// First call: fallback sanitizer (no DOMPurify)
window.setSafeHTML(el, html);
// Now make DOMPurify available
const mockPurify = { sanitize: jest.fn((h) => `CLEAN:${h}`) };
window.DOMPurify = mockPurify;
// Second call should use DOMPurify
window.setSafeHTML(el, html);
expect(mockPurify.sanitize).toHaveBeenCalledTimes(1);
expect(el.innerHTML).toBe(`CLEAN:${html}`);
});
});