Skip to content

Commit db3b61b

Browse files
committed
add tests
1 parent cb42b90 commit db3b61b

File tree

2 files changed

+186
-7
lines changed

2 files changed

+186
-7
lines changed

src/marked.js

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,10 @@ function marked(src, opt, callback) {
7272

7373
if (!tokens.length) return done();
7474

75+
let pending = 0;
7576
marked.iterateTokens(tokens, function(token) {
7677
if (token.type === 'code') {
78+
pending++;
7779
highlight(token.text, token.lang, function(err, code) {
7880
if (err) {
7981
return done(err);
@@ -82,11 +84,20 @@ function marked(src, opt, callback) {
8284
token.text = code;
8385
token.escaped = true;
8486
}
87+
88+
pending--;
89+
if (pending === 0) {
90+
done();
91+
}
8592
});
8693
}
8794
});
8895

89-
return done();
96+
if (pending === 0) {
97+
done();
98+
}
99+
100+
return;
90101
}
91102

92103
try {
@@ -167,16 +178,20 @@ marked.iterateTokens = function(tokens, callback) {
167178
}
168179
switch (token.type) {
169180
case 'table': {
170-
ret = marked.iterateTokens(token.tokens.header, callback);
171-
if (ret === false) {
172-
return false;
173-
}
174-
for (const row of token.tokens.cell) {
175-
ret = marked.iterateTokens(row, callback);
181+
for (const cell of token.tokens.header) {
182+
ret = marked.iterateTokens(cell, callback);
176183
if (ret === false) {
177184
return false;
178185
}
179186
}
187+
for (const row of token.tokens.cells) {
188+
for (const cell of row) {
189+
ret = marked.iterateTokens(cell, callback);
190+
if (ret === false) {
191+
return false;
192+
}
193+
}
194+
}
180195
break;
181196
}
182197
case 'list': {

test/unit/marked-spec.js

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,167 @@ paragraph
229229
expect(html).toBe('arrow no options\nfunction options\nshorthand options\n');
230230
});
231231
});
232+
233+
describe('async highlight', () => {
234+
let highlight, markdown;
235+
beforeEach(() => {
236+
highlight = jasmine.createSpy('highlight', (text, lang, callback) => {
237+
setImmediate(() => {
238+
callback(null, `async ${text || ''}`);
239+
});
240+
});
241+
markdown = `
242+
\`\`\`lang1
243+
text 1
244+
\`\`\`
245+
246+
> \`\`\`lang2
247+
> text 2
248+
> \`\`\`
249+
250+
- \`\`\`lang3
251+
text 3
252+
\`\`\`
253+
`;
254+
});
255+
256+
it('should highlight codeblocks async', (done) => {
257+
highlight.and.callThrough();
258+
259+
marked(markdown, { highlight }, (err, html) => {
260+
if (err) {
261+
fail(err);
262+
}
263+
264+
expect(html).toBe(`<pre><code class="language-lang1">async text 1</code></pre>
265+
<blockquote>
266+
<pre><code class="language-lang2">async text 2</code></pre>
267+
</blockquote>
268+
<ul>
269+
<li><pre><code class="language-lang3">async text 3</code></pre>
270+
</li>
271+
</ul>
272+
`);
273+
done();
274+
});
275+
});
276+
277+
it('should call callback for each error in highlight', (done) => {
278+
highlight.and.callFake((lang, text, callback) => {
279+
callback(new Error('highlight error'));
280+
});
281+
282+
let numErrors = 0;
283+
marked(markdown, { highlight }, (err, html) => {
284+
expect(err).toBeTruthy();
285+
expect(html).toBeUndefined();
286+
287+
if (err) {
288+
numErrors++;
289+
}
290+
291+
if (numErrors === 3) {
292+
done();
293+
}
294+
});
295+
});
296+
});
297+
298+
describe('iterateTokens', () => {
299+
it('should iterate over every token', () => {
300+
const markdown = `
301+
paragraph
302+
303+
---
304+
305+
# heading
306+
307+
\`\`\`
308+
code
309+
\`\`\`
310+
311+
| a | b |
312+
|---|---|
313+
| 1 | 2 |
314+
| 3 | 4 |
315+
316+
> blockquote
317+
318+
- list
319+
320+
<div>html</div>
321+
322+
[link](https://example.com)
323+
324+
![image](https://example.com/image.jpg)
325+
326+
**strong**
327+
328+
*em*
329+
330+
\`codespan\`
331+
332+
~~del~~
333+
334+
br
335+
br
336+
`;
337+
const tokens = marked.lexer(markdown, { ...marked.getDefaults(), breaks: true });
338+
const tokensSeen = [];
339+
marked.iterateTokens(tokens, (token) => {
340+
tokensSeen.push([token.type, (token.raw || '').replace(/\n/g, '')]);
341+
});
342+
343+
expect(tokensSeen).toEqual([
344+
['paragraph', 'paragraph'],
345+
['text', 'paragraph'],
346+
['space', ''],
347+
['hr', '---'],
348+
['heading', '# heading'],
349+
['text', 'heading'],
350+
['code', '```code```'],
351+
['table', '| a | b ||---|---|| 1 | 2 || 3 | 4 |'],
352+
['text', 'a'],
353+
['text', 'b'],
354+
['text', '1'],
355+
['text', '2'],
356+
['text', '3'],
357+
['text', '4'],
358+
['blockquote', '> blockquote'],
359+
['paragraph', 'blockquote'],
360+
['text', 'blockquote'],
361+
['list', '- list'],
362+
['list_item', '- list'],
363+
['text', 'list'],
364+
['text', 'list'],
365+
['space', ''],
366+
['html', '<div>html</div>'],
367+
['paragraph', '[link](https://example.com)'],
368+
['link', '[link](https://example.com)'],
369+
['text', 'link'],
370+
['space', ''],
371+
['paragraph', '![image](https://example.com/image.jpg)'],
372+
['image', '![image](https://example.com/image.jpg)'],
373+
['space', ''],
374+
['paragraph', '**strong**'],
375+
['strong', '**strong**'],
376+
['text', 'strong'],
377+
['space', ''],
378+
['paragraph', '*em*'],
379+
['em', '*em*'],
380+
['text', 'em'],
381+
['space', ''],
382+
['paragraph', '`codespan`'],
383+
['codespan', '`codespan`'],
384+
['space', ''],
385+
['paragraph', '~~del~~'],
386+
['del', '~~del~~'],
387+
['text', 'del'],
388+
['space', ''],
389+
['paragraph', 'brbr'],
390+
['text', 'br'],
391+
['br', ''],
392+
['text', 'br']
393+
]);
394+
});
395+
});

0 commit comments

Comments
 (0)