Files

65 lines
2.9 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:c59345089e27fb5a471d676d1783b760f5131e156f381aef6e04e76979a27c21
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
const { alertHtml } = await import('../../client/ui/alert.js');
describe('alertHtml: the four kinds', () => {
it('renders kind, icon, title and body in the shape the stylesheet expects', () => {
const html = alertHtml({ kind: 'error', title: 'Mislukt', body: 'Bestand onleesbaar.' });
assert.match(html, /^<div class="alert alert-error"><span class="alert-icon">⚠️<\/span>/);
assert.match(html, /<div class="alert-content"><div class="alert-title">Mislukt<\/div>/);
assert.match(html, /<div class="alert-body">Bestand onleesbaar\.<\/div><\/div><\/div>$/);
});
it('gives each kind its own icon, and neutral none', () => {
assert.match(alertHtml({ kind: 'info', body: 'x' }), /alert-icon">ℹ️</);
assert.match(alertHtml({ kind: 'success', body: 'x' }), /alert-icon">✓</);
assert.match(alertHtml({ kind: 'error', body: 'x' }), /alert-icon">⚠️</);
assert.doesNotMatch(alertHtml({ kind: 'neutral', body: 'x' }), /alert-icon/);
});
it('lets a caller override the icon, and suppress it with null', () => {
assert.match(alertHtml({ kind: 'info', icon: '✨', body: 'x' }), /alert-icon">✨</);
assert.doesNotMatch(alertHtml({ kind: 'error', icon: null, body: 'x' }), /alert-icon/);
});
it('REFUSES a kind that does not exist', () => {
assert.throws(() => alertHtml({ kind: 'warning', body: 'x' }), /Unknown alert kind/);
});
});
describe('alertHtml: what is left out leaves nothing behind', () => {
it('writes no title element without a title', () => {
assert.doesNotMatch(alertHtml({ kind: 'error', body: 'x' }), /alert-title/);
});
it('writes no body element without a body', () => {
assert.doesNotMatch(alertHtml({ kind: 'error', title: 'x' }), /alert-body/);
});
it('still renders the content wrapper, because the flex layout needs it', () => {
assert.match(alertHtml({ kind: 'neutral', body: 'x' }), /<div class="alert-content">/);
});
it('adds an extra class only when asked', () => {
assert.match(alertHtml({ kind: 'info', className: 'mt-lg', body: 'x' }), /"alert alert-info mt-lg"/);
assert.match(alertHtml({ kind: 'info', body: 'x' }), /"alert alert-info"/);
});
});
describe('alertHtml: title and body are text, extra is markup', () => {
it('escapes the title and the body', () => {
const html = alertHtml({ kind: 'error', title: '<b>t</b>', body: '<img src=x onerror=alert(1)>' });
assert.doesNotMatch(html, /<b>|<img/);
});
it('escapes a database message put in the body', () => {
assert.doesNotMatch(alertHtml({ kind: 'error', body: 'no such column: <script>' }), /<script>/);
});
it('does NOT escape extra', () => {
assert.match(alertHtml({ kind: 'info', body: 'x', extra: '<button id="go">Go</button>' }),
/<button id="go">Go<\/button>/);
});
});