Commit 13a4a9d9 authored by Mark Florian's avatar Mark Florian

Merge branch 'ipython_katex_comparison_operators' into 'master'

IPython KaTeX rendering of comparison operators for markdown

See merge request gitlab-org/gitlab!59132
parents 7d7614dc 80384c46
...@@ -37,6 +37,11 @@ const katexRegexString = `( ...@@ -37,6 +37,11 @@ const katexRegexString = `(
.replace(/\s/g, '') .replace(/\s/g, '')
.trim(); .trim();
function deHTMLify(t) {
// get some specific characters back, that are allowed for KaTex rendering
const text = t.replace(/&#39;/g, "'").replace(/&lt;/g, '<').replace(/&gt;/g, '>');
return text;
}
function renderKatex(t) { function renderKatex(t) {
let text = t; let text = t;
let numInline = 0; // number of successfull converted math formulas let numInline = 0; // number of successfull converted math formulas
...@@ -57,9 +62,7 @@ function renderKatex(t) { ...@@ -57,9 +62,7 @@ function renderKatex(t) {
while (matches !== null) { while (matches !== null) {
try { try {
const renderedKatex = katex.renderToString( const renderedKatex = katex.renderToString(deHTMLify(matches[0].replace(/\$/g, '')));
matches[0].replace(/\$/g, '').replace(/&#39;/g, "'"),
); // get the tick ' back again from HTMLified string
text = `${text.replace(matches[0], ` ${renderedKatex}`)}`; text = `${text.replace(matches[0], ` ${renderedKatex}`)}`;
} catch { } catch {
numInline -= 1; numInline -= 1;
...@@ -68,7 +71,7 @@ function renderKatex(t) { ...@@ -68,7 +71,7 @@ function renderKatex(t) {
} }
} else { } else {
try { try {
text = katex.renderToString(matches[2].replace(/&#39;/g, "'")); text = katex.renderToString(deHTMLify(matches[2]));
} catch (error) { } catch (error) {
numInline -= 1; numInline -= 1;
} }
......
---
title: IPython KaTeX rendering of comparison operators for markdown
merge_request: 59132
author: Reinhold Gschweicher <pyro4hell@gmail.com>
type: fixed
...@@ -39,60 +39,56 @@ describe('Markdown component', () => { ...@@ -39,60 +39,56 @@ describe('Markdown component', () => {
expect(vm.$el.querySelector('.markdown h1')).not.toBeNull(); expect(vm.$el.querySelector('.markdown h1')).not.toBeNull();
}); });
it('sanitizes output', () => { it('sanitizes output', async () => {
Object.assign(cell, { Object.assign(cell, {
source: [ source: [
'[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ+Cg==)\n', '[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ+Cg==)\n',
], ],
}); });
return vm.$nextTick().then(() => { await vm.$nextTick();
expect(vm.$el.querySelector('a').getAttribute('href')).toBeNull(); expect(vm.$el.querySelector('a').getAttribute('href')).toBeNull();
}); });
});
describe('katex', () => { describe('katex', () => {
beforeEach(() => { beforeEach(() => {
json = getJSONFixture('blob/notebook/math.json'); json = getJSONFixture('blob/notebook/math.json');
}); });
it('renders multi-line katex', () => { it('renders multi-line katex', async () => {
vm = new Component({ vm = new Component({
propsData: { propsData: {
cell: json.cells[0], cell: json.cells[0],
}, },
}).$mount(); }).$mount();
return vm.$nextTick().then(() => { await vm.$nextTick();
expect(vm.$el.querySelector('.katex')).not.toBeNull(); expect(vm.$el.querySelector('.katex')).not.toBeNull();
}); });
});
it('renders inline katex', () => { it('renders inline katex', async () => {
vm = new Component({ vm = new Component({
propsData: { propsData: {
cell: json.cells[1], cell: json.cells[1],
}, },
}).$mount(); }).$mount();
return vm.$nextTick().then(() => { await vm.$nextTick();
expect(vm.$el.querySelector('p:first-child .katex')).not.toBeNull(); expect(vm.$el.querySelector('p:first-child .katex')).not.toBeNull();
}); });
});
it('renders multiple inline katex', () => { it('renders multiple inline katex', async () => {
vm = new Component({ vm = new Component({
propsData: { propsData: {
cell: json.cells[1], cell: json.cells[1],
}, },
}).$mount(); }).$mount();
return vm.$nextTick().then(() => { await vm.$nextTick();
expect(vm.$el.querySelectorAll('p:nth-child(2) .katex').length).toBe(4); expect(vm.$el.querySelectorAll('p:nth-child(2) .katex')).toHaveLength(4);
});
}); });
it('output cell in case of katex error', () => { it('output cell in case of katex error', async () => {
vm = new Component({ vm = new Component({
propsData: { propsData: {
cell: { cell: {
...@@ -103,14 +99,13 @@ describe('Markdown component', () => { ...@@ -103,14 +99,13 @@ describe('Markdown component', () => {
}, },
}).$mount(); }).$mount();
return vm.$nextTick().then(() => { await vm.$nextTick();
// expect one paragraph with no katex formula in it // expect one paragraph with no katex formula in it
expect(vm.$el.querySelectorAll('p').length).toBe(1); expect(vm.$el.querySelectorAll('p')).toHaveLength(1);
expect(vm.$el.querySelectorAll('p .katex').length).toBe(0); expect(vm.$el.querySelectorAll('p .katex')).toHaveLength(0);
});
}); });
it('output cell and render remaining formula in case of katex error', () => { it('output cell and render remaining formula in case of katex error', async () => {
vm = new Component({ vm = new Component({
propsData: { propsData: {
cell: { cell: {
...@@ -121,14 +116,13 @@ describe('Markdown component', () => { ...@@ -121,14 +116,13 @@ describe('Markdown component', () => {
}, },
}).$mount(); }).$mount();
return vm.$nextTick().then(() => { await vm.$nextTick();
// expect one paragraph with no katex formula in it // expect one paragraph with no katex formula in it
expect(vm.$el.querySelectorAll('p').length).toBe(1); expect(vm.$el.querySelectorAll('p')).toHaveLength(1);
expect(vm.$el.querySelectorAll('p .katex').length).toBe(1); expect(vm.$el.querySelectorAll('p .katex')).toHaveLength(1);
});
}); });
it('renders math formula in list object', () => { it('renders math formula in list object', async () => {
vm = new Component({ vm = new Component({
propsData: { propsData: {
cell: { cell: {
...@@ -139,14 +133,13 @@ describe('Markdown component', () => { ...@@ -139,14 +133,13 @@ describe('Markdown component', () => {
}, },
}).$mount(); }).$mount();
return vm.$nextTick().then(() => { await vm.$nextTick();
// expect one list with a katex formula in it // expect one list with a katex formula in it
expect(vm.$el.querySelectorAll('li').length).toBe(1); expect(vm.$el.querySelectorAll('li')).toHaveLength(1);
expect(vm.$el.querySelectorAll('li .katex').length).toBe(2); expect(vm.$el.querySelectorAll('li .katex')).toHaveLength(2);
});
}); });
it("renders math formula with tick ' in it", () => { it("renders math formula with tick ' in it", async () => {
vm = new Component({ vm = new Component({
propsData: { propsData: {
cell: { cell: {
...@@ -157,11 +150,44 @@ describe('Markdown component', () => { ...@@ -157,11 +150,44 @@ describe('Markdown component', () => {
}, },
}).$mount(); }).$mount();
return vm.$nextTick().then(() => { await vm.$nextTick();
// expect one list with a katex formula in it
expect(vm.$el.querySelectorAll('li')).toHaveLength(1);
expect(vm.$el.querySelectorAll('li .katex')).toHaveLength(2);
});
it('renders math formula with less-than-operator < in it', async () => {
vm = new Component({
propsData: {
cell: {
cell_type: 'markdown',
metadata: {},
source: ['- list with inline $a=2$ inline formula $a + b < c$\n', '\n'],
},
},
}).$mount();
await vm.$nextTick();
// expect one list with a katex formula in it // expect one list with a katex formula in it
expect(vm.$el.querySelectorAll('li').length).toBe(1); expect(vm.$el.querySelectorAll('li')).toHaveLength(1);
expect(vm.$el.querySelectorAll('li .katex').length).toBe(2); expect(vm.$el.querySelectorAll('li .katex')).toHaveLength(2);
}); });
it('renders math formula with greater-than-operator > in it', async () => {
vm = new Component({
propsData: {
cell: {
cell_type: 'markdown',
metadata: {},
source: ['- list with inline $a=2$ inline formula $a + b > c$\n', '\n'],
},
},
}).$mount();
await vm.$nextTick();
// expect one list with a katex formula in it
expect(vm.$el.querySelectorAll('li')).toHaveLength(1);
expect(vm.$el.querySelectorAll('li .katex')).toHaveLength(2);
}); });
}); });
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment