42 lines
1.7 KiB
TypeScript
42 lines
1.7 KiB
TypeScript
import { deriveBlogListState } from '../../frontend/src/app/features/blog/blog.pure';
|
|
import { renderMarkdownToHtml } from '../../frontend/src/app/core/services/markdown.pure';
|
|
|
|
describe('deriveBlogListState', () => {
|
|
it('returns loading when loading is true (regardless of error/posts)', () => {
|
|
expect(deriveBlogListState({ loading: true, error: null, count: 0 }).kind).toBe('loading');
|
|
expect(deriveBlogListState({ loading: true, error: 'x', count: 3 }).kind).toBe('loading');
|
|
});
|
|
|
|
it('returns error when not loading and error message set', () => {
|
|
const s = deriveBlogListState({ loading: false, error: 'boom', count: 0 });
|
|
expect(s.kind).toBe('error');
|
|
expect(s.message).toBe('boom');
|
|
});
|
|
|
|
it('returns empty when not loading and no posts', () => {
|
|
expect(deriveBlogListState({ loading: false, error: null, count: 0 }).kind).toBe('empty');
|
|
});
|
|
|
|
it('returns list when posts exist', () => {
|
|
expect(deriveBlogListState({ loading: false, error: null, count: 3 }).kind).toBe('list');
|
|
});
|
|
});
|
|
|
|
describe('shared Markdown presentation', () => {
|
|
it('produces the same HTML for the same Markdown body (sanitized, no script)', () => {
|
|
const md = '# Title\n\nSome **bold** text and <script>alert(1)</script>';
|
|
const a = renderMarkdownToHtml(md);
|
|
const b = renderMarkdownToHtml(md);
|
|
expect(a).toBe(b);
|
|
expect(a).toContain('<h1>Title</h1>');
|
|
expect(a).toContain('<strong>bold</strong>');
|
|
expect(a).not.toContain('<script>');
|
|
});
|
|
|
|
it('strips dangerous javascript: hrefs in both contexts', () => {
|
|
const md = '[click](javascript:alert(1))';
|
|
const a = renderMarkdownToHtml(md);
|
|
expect(a).not.toMatch(/href="javascript:/i);
|
|
});
|
|
});
|