@glossarist/concept-browser 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glossarist/concept-browser",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Vue SPA for browsing Glossarist terminology datasets with cross-reference resolution, graph visualization, and multi-language support",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,92 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { renderAsciiDocLite } from '../utils/asciidoc-lite';
3
+
4
+ describe('renderAsciiDocLite', () => {
5
+ it('returns empty string for empty input', () => {
6
+ expect(renderAsciiDocLite('')).toBe('');
7
+ });
8
+
9
+ it('wraps plain text in <p>', () => {
10
+ expect(renderAsciiDocLite('Hello world')).toBe('<p>Hello world</p>');
11
+ });
12
+
13
+ it('creates separate paragraphs on blank lines', () => {
14
+ const result = renderAsciiDocLite('First\n\nSecond');
15
+ expect(result).toContain('<p>First</p>');
16
+ expect(result).toContain('<p>Second</p>');
17
+ });
18
+
19
+ it('renders headings (level + 1, h1 reserved)', () => {
20
+ expect(renderAsciiDocLite('== Heading 2')).toContain('<h3>Heading 2</h3>');
21
+ expect(renderAsciiDocLite('=== Heading 3')).toContain('<h4>Heading 3</h4>');
22
+ expect(renderAsciiDocLite('===== Heading 5')).toContain('<h6>Heading 5</h6>');
23
+ });
24
+
25
+ it('renders bold text', () => {
26
+ expect(renderAsciiDocLite('some *bold* text')).toContain('<strong>bold</strong>');
27
+ });
28
+
29
+ it('renders italic text', () => {
30
+ expect(renderAsciiDocLite('some _italic_ text')).toContain('<em>italic</em>');
31
+ });
32
+
33
+ it('renders monospace text', () => {
34
+ expect(renderAsciiDocLite('use `code` here')).toContain('<code>code</code>');
35
+ });
36
+
37
+ it('renders AsciiDoc links with label', () => {
38
+ const result = renderAsciiDocLite('see https://example.com[label] here');
39
+ expect(result).toContain('<a href="https://example.com"');
40
+ expect(result).toContain('>label</a>');
41
+ });
42
+
43
+ it('renders bare URLs as links', () => {
44
+ const result = renderAsciiDocLite('visit https://example.com now');
45
+ expect(result).toContain('<a href="https://example.com"');
46
+ });
47
+
48
+ it('renders unordered lists', () => {
49
+ const result = renderAsciiDocLite('* item one\n* item two');
50
+ expect(result).toContain('<ul>');
51
+ expect(result).toContain('<li');
52
+ expect(result).toContain('item one');
53
+ expect(result).toContain('item two');
54
+ });
55
+
56
+ it('renders ordered lists', () => {
57
+ const result = renderAsciiDocLite('. first\n. second');
58
+ expect(result).toContain('<ol>');
59
+ expect(result).toContain('first');
60
+ expect(result).toContain('second');
61
+ });
62
+
63
+ it('renders source blocks with ---- delimiter', () => {
64
+ const result = renderAsciiDocLite('----\nlet x = 1;\n----');
65
+ expect(result).toContain('<pre><code>');
66
+ expect(result).toContain('let x = 1;');
67
+ });
68
+
69
+ it('renders source blocks with .... delimiter', () => {
70
+ const result = renderAsciiDocLite('....\nsome text\n....');
71
+ expect(result).toContain('<pre><code>');
72
+ expect(result).toContain('some text');
73
+ });
74
+
75
+ it('escapes HTML in source blocks', () => {
76
+ const result = renderAsciiDocLite('----\n<a href="evil">\n----');
77
+ expect(result).toContain('&lt;a href=&quot;evil&quot;&gt;');
78
+ expect(result).not.toContain('<a href="evil">');
79
+ });
80
+
81
+ it('handles multi-line paragraphs', () => {
82
+ const result = renderAsciiDocLite('line one\nline two\n\nnew paragraph');
83
+ expect(result).toContain('<p>line one line two</p>');
84
+ expect(result).toContain('<p>new paragraph</p>');
85
+ });
86
+
87
+ it('handles nested list levels', () => {
88
+ const result = renderAsciiDocLite('* top\n** nested');
89
+ expect(result).toContain('list-level-1');
90
+ expect(result).toContain('list-level-2');
91
+ });
92
+ });
@@ -0,0 +1,88 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { renderMarkdown } from '../utils/markdown-lite';
3
+
4
+ describe('renderMarkdown', () => {
5
+ it('returns empty string for empty input', () => {
6
+ expect(renderMarkdown('')).toBe('');
7
+ });
8
+
9
+ it('wraps plain text in <p>', () => {
10
+ expect(renderMarkdown('Hello world')).toBe('<p>Hello world</p>');
11
+ });
12
+
13
+ it('creates separate paragraphs on blank lines', () => {
14
+ const result = renderMarkdown('First\n\nSecond');
15
+ expect(result).toContain('<p>First</p>');
16
+ expect(result).toContain('<p>Second</p>');
17
+ });
18
+
19
+ it('renders headings (level + 1, h1 reserved)', () => {
20
+ expect(renderMarkdown('## Heading 2')).toContain('<h3>Heading 2</h3>');
21
+ expect(renderMarkdown('### Heading 3')).toContain('<h4>Heading 3</h4>');
22
+ expect(renderMarkdown('#### Heading 4')).toContain('<h5>Heading 4</h5>');
23
+ });
24
+
25
+ it('renders bold text', () => {
26
+ expect(renderMarkdown('some **bold** text')).toContain('<strong>bold</strong>');
27
+ });
28
+
29
+ it('renders italic text', () => {
30
+ expect(renderMarkdown('some *italic* text')).toContain('<em>italic</em>');
31
+ });
32
+
33
+ it('renders inline code', () => {
34
+ expect(renderMarkdown('use `code` here')).toContain('<code>code</code>');
35
+ });
36
+
37
+ it('renders markdown links', () => {
38
+ const result = renderMarkdown('[label](https://example.com)');
39
+ expect(result).toContain('<a href="https://example.com"');
40
+ expect(result).toContain('>label</a>');
41
+ });
42
+
43
+ it('renders unordered lists', () => {
44
+ const result = renderMarkdown('- one\n- two');
45
+ expect(result).toContain('<ul>');
46
+ expect(result).toContain('<li>one</li>');
47
+ expect(result).toContain('<li>two</li>');
48
+ });
49
+
50
+ it('renders ordered lists', () => {
51
+ const result = renderMarkdown('1. first\n2. second');
52
+ expect(result).toContain('<ol>');
53
+ expect(result).toContain('<li>first</li>');
54
+ });
55
+
56
+ it('renders blockquotes', () => {
57
+ const result = renderMarkdown('> quoted text');
58
+ expect(result).toContain('<blockquote>');
59
+ expect(result).toContain('quoted text');
60
+ });
61
+
62
+ it('renders code fences', () => {
63
+ const result = renderMarkdown('```\nlet x = 1;\n```');
64
+ expect(result).toContain('<pre><code>');
65
+ expect(result).toContain('let x = 1;');
66
+ });
67
+
68
+ it('renders code fences with language', () => {
69
+ const result = renderMarkdown('```js\nconst x = 1;\n```');
70
+ expect(result).toContain('class="language-js"');
71
+ });
72
+
73
+ it('escapes HTML in code fences', () => {
74
+ const result = renderMarkdown('```\n<a href="evil">\n```');
75
+ expect(result).toContain('&lt;a href="evil"&gt;');
76
+ });
77
+
78
+ it('renders horizontal rules', () => {
79
+ const result = renderMarkdown('---');
80
+ expect(result).toContain('<hr>');
81
+ });
82
+
83
+ it('handles multi-line paragraphs', () => {
84
+ const result = renderMarkdown('line one\nline two\n\nnew paragraph');
85
+ expect(result).toContain('<p>line one line two</p>');
86
+ expect(result).toContain('<p>new paragraph</p>');
87
+ });
88
+ });
@@ -0,0 +1,71 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+
3
+ // Mock @plurimath/plurimath to avoid loading the 2.7MB Opal runtime in tests
4
+ vi.mock('@plurimath/plurimath', () => ({
5
+ default: class MockPlurimath {
6
+ constructor(private data: string, private format: string) {}
7
+ toMathml() {
8
+ if (this.data === 'ERROR') throw new Error('parse error');
9
+ return `<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><mi>${this.data}</mi></math>`;
10
+ }
11
+ toAsciimath() { return this.data; }
12
+ toLatex() { return this.data; }
13
+ toHtml() { return this.data; }
14
+ toOmml() { return this.data; }
15
+ toDisplay() { return this.data; }
16
+ },
17
+ }));
18
+
19
+ import { loadPlurimath, renderToMathML, mathToHtml } from '../utils/plurimath';
20
+
21
+ describe('loadPlurimath', () => {
22
+ it('loads and returns the Plurimath class', async () => {
23
+ const Plurimath = await loadPlurimath();
24
+ expect(Plurimath).toBeDefined();
25
+ const p = new Plurimath('x', 'asciimath');
26
+ expect(p.toMathml()).toContain('<math');
27
+ });
28
+
29
+ it('returns the same instance on subsequent calls', async () => {
30
+ const a = await loadPlurimath();
31
+ const b = await loadPlurimath();
32
+ expect(a).toBe(b);
33
+ });
34
+ });
35
+
36
+ describe('renderToMathML', () => {
37
+ it('returns MathML with inline display after loading', async () => {
38
+ await loadPlurimath();
39
+ const result = renderToMathML('x^2', 'asciimath');
40
+ expect(result).toContain('<math');
41
+ expect(result).toContain('display="inline"');
42
+ expect(result).not.toContain('display="block"');
43
+ });
44
+
45
+ it('returns null on parse error', async () => {
46
+ await loadPlurimath();
47
+ expect(renderToMathML('ERROR', 'asciimath')).toBeNull();
48
+ });
49
+ });
50
+
51
+ describe('mathToHtml', () => {
52
+ it('wraps MathML in math-inline span', async () => {
53
+ await loadPlurimath();
54
+ const result = mathToHtml('x', 'asciimath', false);
55
+ expect(result).toContain('class="math-inline"');
56
+ expect(result).toContain('<math');
57
+ });
58
+
59
+ it('adds math-bold class when bold is true', async () => {
60
+ await loadPlurimath();
61
+ const result = mathToHtml('x', 'asciimath', true);
62
+ expect(result).toContain('class="math-inline math-bold"');
63
+ });
64
+
65
+ it('returns fallback code element on error', async () => {
66
+ await loadPlurimath();
67
+ const result = mathToHtml('ERROR', 'asciimath', false);
68
+ expect(result).toContain('class="math-fallback"');
69
+ expect(result).toContain('ERROR');
70
+ });
71
+ });
@@ -0,0 +1,78 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+
3
+ // Mock plurimath module before importing v-math
4
+ vi.mock('../utils/plurimath', () => ({
5
+ loadPlurimath: vi.fn(() => {
6
+ // Simulate loading — resolve immediately
7
+ return Promise.resolve(MockPlurimath);
8
+ }),
9
+ mathToHtml: vi.fn((expr: string, format: string, bold: boolean) => {
10
+ if (expr === 'SKIP') return `<code class="math-fallback">${expr}</code>`;
11
+ return `<span class="math-inline${bold ? ' math-bold' : ''}"><math><mi>${expr}</mi></math></span>`;
12
+ }),
13
+ }));
14
+
15
+ class MockPlurimath {
16
+ constructor() {}
17
+ }
18
+
19
+ import { vMath } from '../directives/v-math';
20
+ import { loadPlurimath, mathToHtml } from '../utils/plurimath';
21
+
22
+ describe('v-math directive', () => {
23
+ let container: HTMLElement;
24
+
25
+ beforeEach(() => {
26
+ container = document.createElement('div');
27
+ vi.clearAllMocks();
28
+ });
29
+
30
+ it('does nothing when no math-pending elements exist', () => {
31
+ container.innerHTML = '<p>plain text</p>';
32
+ vMath.mounted!(container, {} as any);
33
+ expect(container.innerHTML).toBe('<p>plain text</p>');
34
+ });
35
+
36
+ it('triggers loadPlurimath when math-pending elements exist', async () => {
37
+ container.innerHTML = '<span class="math-pending" data-expr="x^2" data-format="asciimath">x^2</span>';
38
+ vMath.mounted!(container, {} as any);
39
+ expect(loadPlurimath).toHaveBeenCalled();
40
+ });
41
+
42
+ it('replaces math-pending elements after loading', async () => {
43
+ container.innerHTML = '<span class="math-pending" data-expr="x^2" data-format="asciimath">x^2</span>';
44
+ vMath.mounted!(container, {} as any);
45
+
46
+ // Wait for loadPlurimath promise to resolve and upgrade to run
47
+ await vi.waitFor(() => {
48
+ expect(mathToHtml).toHaveBeenCalledWith('x^2', 'asciimath', false);
49
+ });
50
+ });
51
+
52
+ it('handles bold math-pending elements', async () => {
53
+ container.innerHTML = '<span class="math-pending math-bold" data-expr="alpha" data-format="asciimath">alpha</span>';
54
+ vMath.mounted!(container, {} as any);
55
+
56
+ await vi.waitFor(() => {
57
+ expect(mathToHtml).toHaveBeenCalledWith('alpha', 'asciimath', true);
58
+ });
59
+ });
60
+
61
+ it('skips elements without data-expr', async () => {
62
+ container.innerHTML = '<span class="math-pending">no expr</span>';
63
+ vMath.mounted!(container, {} as any);
64
+
65
+ await vi.waitFor(() => {
66
+ expect(mathToHtml).not.toHaveBeenCalled();
67
+ });
68
+ });
69
+
70
+ it('uses default format asciimath when data-format is missing', async () => {
71
+ container.innerHTML = '<span class="math-pending" data-expr="x">x</span>';
72
+ vMath.mounted!(container, {} as any);
73
+
74
+ await vi.waitFor(() => {
75
+ expect(mathToHtml).toHaveBeenCalledWith('x', 'asciimath', false);
76
+ });
77
+ });
78
+ });