Skip to content

Commit 67f2e74

Browse files
committed
iss1757 - More Jest tests
1 parent 82d53f2 commit 67f2e74

3 files changed

Lines changed: 284 additions & 0 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import calculation from '../../corsscripts/ascii/filters/calculation.js';
2+
3+
describe('calculation filter', () => {
4+
test('wraps text between @ in double stars', () => {
5+
expect(calculation('The answer is @x^2 + 1@ here')).toBe('The answer is **x^2 + 1** here');
6+
});
7+
8+
test('handles multiple @...@ in one line', () => {
9+
expect(calculation('A: @x@, B: @y@')).toBe('A: **x**, B: **y**');
10+
});
11+
12+
test('ignores @ with no closing @', () => {
13+
expect(calculation('A: @x, B: @y@')).toBe('A: **x, B: **y@');
14+
});
15+
16+
test('matches @ symbols in order and leaves an unpaired trailing @', () => {
17+
expect(calculation('A: @x B: @y@ and @z@')).toBe('A: **x B: **y** and **z@');
18+
});
19+
20+
test('does not match across newlines', () => {
21+
expect(calculation('A: @x\n@y@')).toBe('A: @x\n**y**');
22+
});
23+
24+
test('returns input unchanged if no @...@ present', () => {
25+
expect(calculation('No special markers')).toBe('No special markers');
26+
});
27+
28+
test('populates blockCollector with calculation blocks', () => {
29+
const collector = { blocks: [] };
30+
calculation('A: @x@, B: @y@', collector);
31+
expect(collector.blocks).toEqual([
32+
{ type: 'calculation', raw: 'x', rendered: '**x**' },
33+
{ type: 'calculation', raw: 'y', rendered: '**y**' }
34+
]);
35+
});
36+
37+
test('clears blockCollector.blocks if provided', () => {
38+
const collector = { blocks: [{ type: 'old', raw: 'z' }] };
39+
calculation('A: @x@', collector);
40+
expect(collector.blocks).toEqual([
41+
{ type: 'calculation', raw: 'x', rendered: '**x**' }
42+
]);
43+
});
44+
});
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import markdown from '../../corsscripts/ascii/filters/markdown.js';
2+
3+
describe('markdown filter', () => {
4+
beforeEach(() => {
5+
global.window = {
6+
AMparseMath: jest.fn((content) => content)
7+
};
8+
});
9+
10+
afterEach(() => {
11+
delete global.window;
12+
});
13+
14+
test('renders simple markdown to HTML', () => {
15+
const html = markdown('**bold**', null, {});
16+
expect(html).toContain('<strong>bold</strong>');
17+
});
18+
19+
test('applies transforms from op.transforms', () => {
20+
// latexwrap and boldfilter are in the transformLib, but we just check no error is thrown
21+
const html = markdown('`f(x) = x^2`', null, { transforms: 'latexwrap,boldfilter' });
22+
expect(typeof html).toBe('string');
23+
});
24+
25+
test('populates blockCollector if provided', () => {
26+
const collector = { blocks: [] };
27+
markdown('`x`\n`y`\nhdkfhds', collector, {});
28+
expect(Array.isArray(collector.blocks)).toBe(true);
29+
expect(collector.blocks.length).toBe(2);
30+
});
31+
32+
test('handles empty transforms', () => {
33+
const html = markdown('plain', null, { transforms: '' });
34+
expect(typeof html).toBe('string');
35+
});
36+
37+
test('updates shared state.transforms and state.collector per call', () => {
38+
jest.resetModules();
39+
40+
let capturedState = null;
41+
jest.doMock('../../corsscripts/ascii/filters/markdownitrules.js', () => ({
42+
__esModule: true,
43+
default: jest.fn((mdit, options) => {
44+
capturedState = options.state;
45+
})
46+
}));
47+
48+
const markdownModule = require('../../corsscripts/ascii/filters/markdown.js');
49+
const isolatedMarkdown = markdownModule.default || markdownModule;
50+
51+
const collector = { blocks: [] };
52+
isolatedMarkdown('plain', collector, { transforms: 'latexwrap, boldfilter' });
53+
54+
expect(capturedState).not.toBeNull();
55+
expect(capturedState.transforms).toEqual(['latexwrap', 'boldfilter']);
56+
expect(capturedState.collector).toBe(collector);
57+
expect(Object.keys(capturedState.transformLib).sort()).toEqual(['boldfilter', 'latexwrap']);
58+
});
59+
60+
test('resets shared state to empty transforms and null collector', () => {
61+
jest.resetModules();
62+
63+
let capturedState = null;
64+
jest.doMock('../../corsscripts/ascii/filters/markdownitrules.js', () => ({
65+
__esModule: true,
66+
default: jest.fn((mdit, options) => {
67+
capturedState = options.state;
68+
})
69+
}));
70+
71+
const markdownModule = require('../../corsscripts/ascii/filters/markdown.js');
72+
const isolatedMarkdown = markdownModule.default || markdownModule;
73+
74+
isolatedMarkdown('plain', { blocks: [] }, { transforms: 'latexwrap' });
75+
isolatedMarkdown('plain', null, { transforms: '' });
76+
77+
expect(capturedState).not.toBeNull();
78+
expect(capturedState.transforms).toEqual([]);
79+
expect(capturedState.collector).toBeNull();
80+
});
81+
});
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import markdownitrules from '../../corsscripts/ascii/filters/markdownitrules.js';
2+
3+
describe('markdownitrules filter', () => {
4+
beforeEach(() => {
5+
global.window = {
6+
AMparseMath: jest.fn((content) => content)
7+
};
8+
});
9+
10+
afterEach(() => {
11+
delete global.window;
12+
});
13+
14+
// We'll mock the mdit and options objects to test the plugin registration and rule effects.
15+
function makeFakeMdit() {
16+
return {
17+
core: { ruler: { push: jest.fn() } },
18+
renderer: { rules: {} },
19+
block: { ruler: { before: jest.fn() } }
20+
};
21+
}
22+
23+
function setup(stateOverrides = {}) {
24+
const mdit = makeFakeMdit();
25+
const state = {
26+
transforms: [],
27+
transformLib: {},
28+
collector: null,
29+
...stateOverrides
30+
};
31+
markdownitrules(mdit, { state });
32+
return { mdit, state };
33+
}
34+
35+
test('registers reset_collector and renderer rules', () => {
36+
const mdit = makeFakeMdit();
37+
const options = { state: { transforms: [], transformLib: {}, collector: null } };
38+
markdownitrules(mdit, options);
39+
expect(typeof mdit.renderer.rules.code_inline).toBe('function');
40+
expect(typeof mdit.renderer.rules.asciimath_block).toBe('function');
41+
expect(typeof mdit.renderer.rules.math_inline).toBe('function');
42+
expect(typeof mdit.renderer.rules.math_block).toBe('function');
43+
expect(mdit.core.ruler.push).toHaveBeenCalledWith('reset_collector', expect.any(Function));
44+
});
45+
46+
test('throws on unknown transform', () => {
47+
const mdit = makeFakeMdit();
48+
const options = { state: { transforms: ['notfound'], transformLib: {}, collector: null } };
49+
markdownitrules(mdit, options);
50+
// Simulate a call to applyTransforms
51+
const fn = mdit.renderer.rules.asciimath_block;
52+
expect(() => fn([{ content: 'x' }], 0)).toThrow(/unknown transform/);
53+
});
54+
55+
test('reset_collector clears collector blocks', () => {
56+
const collector = { blocks: [{ type: 'old', raw: 'x' }] };
57+
const { mdit } = setup({ collector });
58+
59+
const resetCollector = mdit.core.ruler.push.mock.calls[0][1];
60+
resetCollector();
61+
62+
expect(collector.blocks).toEqual([]);
63+
});
64+
65+
test('code_inline uses AMparseMath and pushes collector block', () => {
66+
window.AMparseMath.mockImplementation((content) => `PARSED(${content})`);
67+
const collector = { blocks: [] };
68+
const { mdit } = setup({ collector });
69+
70+
const rendered = mdit.renderer.rules.code_inline([{ content: 'x^2' }], 0);
71+
72+
expect(window.AMparseMath).toHaveBeenCalledWith('x^2', true);
73+
expect(rendered).toBe('\\(PARSED(x^2)\\)');
74+
expect(collector.blocks).toEqual([
75+
{ type: 'code_inline', raw: 'x^2', rendered: '\\(PARSED(x^2)\\)' }
76+
]);
77+
});
78+
79+
test('asciimath_block applies transforms in order and pushes collector block', () => {
80+
window.AMparseMath.mockImplementation((content) => `P(${content})`);
81+
const t1 = jest.fn(lines => lines.map(line => `T1:${line}`));
82+
const t2 = jest.fn(lines => lines.map(line => `T2:${line}`));
83+
const collector = { blocks: [] };
84+
const { mdit } = setup({
85+
transforms: ['t1', 't2'],
86+
transformLib: { t1, t2 },
87+
collector
88+
});
89+
90+
const raw = ' a \n\n b ';
91+
const rendered = mdit.renderer.rules.asciimath_block([{ content: raw }], 0);
92+
93+
expect(window.AMparseMath).toHaveBeenNthCalledWith(1, 'a', true);
94+
expect(window.AMparseMath).toHaveBeenNthCalledWith(2, 'b', true);
95+
expect(t1).toHaveBeenCalledWith(['P(a)', 'P(b)']);
96+
expect(t2).toHaveBeenCalledWith(['T1:P(a)', 'T1:P(b)']);
97+
expect(rendered).toBe('T2:T1:P(a)\nT2:T1:P(b)\n');
98+
expect(collector.blocks).toEqual([
99+
{ type: 'asciimath_block', raw, rendered: 'T2:T1:P(a)\nT2:T1:P(b)\n' }
100+
]);
101+
});
102+
103+
test('math_inline wraps raw content and does not call AMparseMath', () => {
104+
const collector = { blocks: [] };
105+
const { mdit } = setup({ collector });
106+
107+
const rendered = mdit.renderer.rules.math_inline([{ content: 'x + 1' }], 0);
108+
109+
expect(window.AMparseMath).not.toHaveBeenCalled();
110+
expect(rendered).toBe('\\(x + 1\\)');
111+
expect(collector.blocks).toEqual([
112+
{ type: 'math_inline', raw: 'x + 1', rendered: '\\(x + 1\\)' }
113+
]);
114+
});
115+
116+
test('math_block applies transforms without AMparseMath and pushes collector block', () => {
117+
const t1 = jest.fn(lines => lines.map(line => line.toUpperCase()));
118+
const collector = { blocks: [] };
119+
const { mdit } = setup({
120+
transforms: ['t1'],
121+
transformLib: { t1 },
122+
collector
123+
});
124+
125+
const raw = ' a\n\n b ';
126+
const rendered = mdit.renderer.rules.math_block([{ content: raw }], 0);
127+
128+
expect(window.AMparseMath).not.toHaveBeenCalled();
129+
expect(t1).toHaveBeenCalledWith(['a', 'b']);
130+
expect(rendered).toBe('A\nB\n');
131+
expect(collector.blocks).toEqual([
132+
{ type: 'math_block', raw, rendered: 'A\nB\n' }
133+
]);
134+
});
135+
136+
test('splitBlock trims lines and removes blank lines before rendering', () => {
137+
window.AMparseMath.mockImplementation((content) => `P(${content})`);
138+
const { mdit } = setup({ transforms: [], transformLib: {} });
139+
140+
const asciiRaw = ' first \r\n\r\n second \n ';
141+
const mathRaw = ' left \n\n right \n';
142+
143+
const asciiRendered = mdit.renderer.rules.asciimath_block([{ content: asciiRaw }], 0);
144+
const mathRendered = mdit.renderer.rules.math_block([{ content: mathRaw }], 0);
145+
146+
expect(window.AMparseMath).toHaveBeenNthCalledWith(1, 'first', true);
147+
expect(window.AMparseMath).toHaveBeenNthCalledWith(2, 'second', true);
148+
expect(asciiRendered).toBe('P(first)\nP(second)\n');
149+
expect(mathRendered).toBe('left\nright\n');
150+
});
151+
152+
test('applyTransforms with no content still returns trailing newline', () => {
153+
const { mdit } = setup({ transforms: [], transformLib: {} });
154+
155+
const rendered = mdit.renderer.rules.math_block([{ content: ' \n\n ' }], 0);
156+
157+
expect(rendered).toBe('\n');
158+
});
159+
});

0 commit comments

Comments
 (0)