renderers.spec.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import expect = require('expect.js');
  4. import {
  5. LatexRenderer, PDFRenderer, JavascriptRenderer,
  6. SVGRenderer, MarkdownRenderer, TextRenderer, HTMLRenderer, ImageRenderer
  7. } from '../../../lib/renderers';
  8. import {
  9. defaultSanitizer
  10. } from '../../../lib/sanitizer';
  11. const EXPECTED_MD = `<h1>Title first level</h1>\n<h2>Title second Level</h2>\n<h3>Title third level</h3>\n<h4>h4</h4>\n<h5>h5</h5>\n<h6>h6</h6>\n<h1>h1</h1>\n<h2>h2</h2>\n<h3>h3</h3>\n<h4>h4</h4>\n<h5>h6</h5>\n<p>This is just a sample paragraph<br>You can look at different level of nested unorderd list ljbakjn arsvlasc asc asc awsc asc ascd ascd ascd asdc asc</p>\n<ul>\n<li>level 1<ul>\n<li>level 2</li>\n<li>level 2</li>\n<li>level 2<ul>\n<li>level 3</li>\n<li>level 3<ul>\n<li>level 4<ul>\n<li>level 5<ul>\n<li>level 6</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>level 2</li>\n</ul>\n</li>\n<li>level 1</li>\n<li>level 1</li>\n<li>level 1<br>Ordered list</li>\n<li>level 1<ol>\n<li>level 1</li>\n<li>level 1<ol>\n<li>level 1</li>\n<li>level 1</li>\n<li>level 1<ol>\n<li>level 1</li>\n<li>level 1<ol>\n<li>level 1</li>\n<li>level 1</li>\n<li>level 1</li>\n</ol>\n</li>\n</ol>\n</li>\n</ol>\n</li>\n</ol>\n</li>\n<li>level 1</li>\n<li>level 1<br>some Horizontal line</li>\n</ul>\n<hr>\n<h2>and another one</h2>\n<p>Colons can be used to align columns.</p>\n<table>\n<thead>\n<tr>\n<th>Tables</th>\n<th>Are</th>\n<th>Cool</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>col 3 is</td>\n<td>right-aligned</td>\n<td>1600</td>\n</tr>\n<tr>\n<td>col 2 is</td>\n<td>centered</td>\n<td>12</td>\n</tr>\n<tr>\n<td>zebra stripes</td>\n<td>are neat</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>\n<p>There must be at least 3 dashes separating each header cell.<br>The outer pipes (|) are optional, and you don\'t need to make the<br>raw Markdown line up prettily. You can also use inline Markdown.</p>\n`;
  12. describe('renderers', () => {
  13. describe('TextRenderer', () => {
  14. describe('#mimetypes', () => {
  15. it('should have the text/plain and jupyter/console-text mimetype', () => {
  16. let mimetypes = ['text/plain', 'application/vnd.jupyter.console-text'];
  17. let t = new TextRenderer();
  18. expect(t.mimetypes).to.eql(mimetypes);
  19. });
  20. });
  21. describe('#sanitizable()', () => {
  22. it('should be `false`', () => {
  23. let t = new TextRenderer();
  24. expect(t.sanitizable('text/plain')).to.be(false);
  25. expect(t.sanitizable('application/vnd.jupyter.console-text')).to.be(false);
  26. });
  27. });
  28. describe('#isSafe()', () => {
  29. it('should be `true`', () => {
  30. let t = new TextRenderer();
  31. expect(t.isSafe('text/plain')).to.be(true);
  32. expect(t.isSafe('application/vnd.jupyter.console-text')).to.be(true);
  33. });
  34. });
  35. describe('#render()', () => {
  36. it('should output the correct HTML', () => {
  37. let t = new TextRenderer();
  38. let widget = t.render({ mimetype: 'text/plain', source: 'x = 2 ** a' });
  39. expect(widget.node.innerHTML).to.be('<pre>x = 2 ** a</pre>');
  40. });
  41. it('should output the correct HTML with ansi colors', () => {
  42. let t = new TextRenderer();
  43. let source = 'There is no text but \x1b[01;41;32mtext\x1b[00m.\nWoo.';
  44. let widget = t.render({
  45. mimetype: 'application/vnd.jupyter.console-text', source
  46. });
  47. expect(widget.node.innerHTML).to.be('<pre>There is no text but <span style="color:rgb(0, 255, 0);background-color:rgb(187, 0, 0)">text</span>.\nWoo.</pre>');
  48. });
  49. it('should escape inline html', () => {
  50. let t = new TextRenderer();
  51. let source = 'There is no text <script>window.x=1</script> but \x1b[01;41;32mtext\x1b[00m.\nWoo.';
  52. let widget = t.render({
  53. mimetype: 'application/vnd.jupyter.console-text', source
  54. });
  55. expect(widget.node.innerHTML).to.be('<pre>There is no text &lt;script&gt;window.x=1&lt;/script&gt; but <span style="color:rgb(0, 255, 0);background-color:rgb(187, 0, 0)">text</span>.\nWoo.</pre>');
  56. });
  57. });
  58. });
  59. describe('LatexRenderer', () => {
  60. describe('#mimetypes', () => {
  61. it('should have the text/latex mimetype', () => {
  62. let t = new LatexRenderer();
  63. expect(t.mimetypes).to.eql(['text/latex']);
  64. });
  65. });
  66. describe('#sanitizable()', () => {
  67. it('should be `false`', () => {
  68. let t = new LatexRenderer();
  69. expect(t.sanitizable('text/latex')).to.be(false);
  70. });
  71. });
  72. describe('#isSafe()', () => {
  73. it('should be `true`', () => {
  74. let t = new LatexRenderer();
  75. expect(t.isSafe('text/latex')).to.be(true);
  76. });
  77. });
  78. describe('#render()', () => {
  79. it('should set the textContent of the widget', () => {
  80. let source = '\sum\limits_{i=0}^{\infty} \frac{1}{n^2}';
  81. let t = new LatexRenderer();
  82. let widget = t.render({ mimetype: 'text/latex', source });
  83. expect(widget.node.textContent).to.be(source);
  84. });
  85. });
  86. });
  87. describe('PDFRenderer', () => {
  88. describe('#mimetypes', () => {
  89. it('should have the application/pdf mimetype', () => {
  90. let t = new PDFRenderer();
  91. expect(t.mimetypes).to.eql(['application/pdf']);
  92. });
  93. });
  94. describe('#sanitizable()', () => {
  95. it('should be `false`', () => {
  96. let t = new PDFRenderer();
  97. expect(t.sanitizable('application/pdf')).to.be(false);
  98. });
  99. });
  100. describe('#isSafe()', () => {
  101. it('should be `false`', () => {
  102. let t = new PDFRenderer();
  103. expect(t.isSafe('application/pdf')).to.be(false);
  104. });
  105. });
  106. describe('#render()', () => {
  107. it('should render the correct HTML', () => {
  108. let source = "I don't have a b64'd PDF";
  109. let t = new PDFRenderer();
  110. let w = t.render({ mimetype: 'application/pdf', source });
  111. expect(w.node.innerHTML.indexOf('data:application/pdf')).to.not.be(-1);
  112. });
  113. });
  114. });
  115. describe('JavascriptRenderer', () => {
  116. describe('#mimetypes', () => {
  117. it('should have the text/javascript mimetype', () => {
  118. let mimetypes = ['text/javascript', 'application/javascript'];
  119. let t = new JavascriptRenderer();
  120. expect(t.mimetypes).to.eql(mimetypes);
  121. });
  122. });
  123. describe('#sanitizable()', () => {
  124. it('should be `false`', () => {
  125. let t = new JavascriptRenderer();
  126. expect(t.sanitizable('text/javascript')).to.be(false);
  127. });
  128. });
  129. describe('#isSafe()', () => {
  130. it('should be `false`', () => {
  131. let t = new JavascriptRenderer();
  132. expect(t.isSafe('text/javascript')).to.be(false);
  133. });
  134. });
  135. describe('#render()', () => {
  136. it('should create a script tag', () => {
  137. let t = new JavascriptRenderer();
  138. let source = 'window.x = 1';
  139. let w = t.render({ mimetype: 'text/javascript', source });
  140. let el = w.node.firstChild as HTMLElement;
  141. expect(el.localName).to.be('script');
  142. expect(el.textContent).to.be(source);
  143. // Ensure script has not been run yet
  144. expect((window as any).x).to.be(void 0);
  145. // Put it on the DOM
  146. w.attach(document.body);
  147. // Should be evaluated now
  148. expect((window as any).x).to.be(1);
  149. w.dispose();
  150. });
  151. });
  152. });
  153. describe('SVGRenderer', () => {
  154. describe('#mimetypes', () => {
  155. it('should have the image/svg+xml mimetype', () => {
  156. let t = new SVGRenderer();
  157. expect(t.mimetypes).to.eql(['image/svg+xml']);
  158. });
  159. });
  160. describe('#sanitizable()', () => {
  161. it('should be `true`', () => {
  162. let t = new SVGRenderer();
  163. expect(t.sanitizable('image/svg+xml')).to.be(true);
  164. });
  165. });
  166. describe('#isSafe()', () => {
  167. it('should be `false`', () => {
  168. let t = new SVGRenderer();
  169. expect(t.isSafe('image/svg+xml')).to.be(false);
  170. });
  171. });
  172. describe('#render()', () => {
  173. it('should create an svg tag', () => {
  174. const source = '<svg></svg>';
  175. let t = new SVGRenderer();
  176. let w = t.render({ mimetype: 'image/svg+xml', source });
  177. let svgEl = w.node.getElementsByTagName('svg')[0];
  178. expect(svgEl).to.be.ok();
  179. });
  180. it('should sanitize when a sanitizer is given', () => {
  181. const source = '<svg><script>window.x = 1</script></svg>';
  182. let t = new SVGRenderer();
  183. let w = t.render({
  184. mimetype: 'image/svg+xml', source, sanitizer: defaultSanitizer
  185. });
  186. expect(w.node.innerHTML).to.be('<svg></svg>');
  187. });
  188. });
  189. });
  190. describe('MarkdownRenderer', () => {
  191. describe('#mimetypes', () => {
  192. it('should have the text/markdown mimetype', function() {
  193. let t = new MarkdownRenderer();
  194. expect(t.mimetypes).to.eql(['text/markdown']);
  195. });
  196. });
  197. describe('#sanitizable()', () => {
  198. it('should be `true`', () => {
  199. let t = new MarkdownRenderer();
  200. expect(t.sanitizable('text/markdown')).to.be(true);
  201. });
  202. });
  203. describe('#isSafe()', () => {
  204. it('should be `false`', () => {
  205. let t = new MarkdownRenderer();
  206. expect(t.isSafe('text/markdown')).to.be(false);
  207. });
  208. });
  209. describe('#render()', () => {
  210. it('should set the inner html', (done) => {
  211. let t = new MarkdownRenderer();
  212. let source = '<p>hello</p>';
  213. let widget = t.render({ mimetype: 'text/markdown', source });
  214. let loop = () => {
  215. if ((widget as any)._rendered) {
  216. expect(widget.node.innerHTML).to.be(source);
  217. done();
  218. return;
  219. }
  220. setTimeout(loop, 100);
  221. };
  222. setTimeout(loop, 100);
  223. });
  224. it('should sanitize if a sanitizer is given', (done) => {
  225. let source = require('../../../examples/filebrowser/sample.md') as string;
  226. let r = new MarkdownRenderer();
  227. let widget = r.render({
  228. mimetype: 'text/markdown', source, sanitizer: defaultSanitizer
  229. });
  230. let loop = () => {
  231. if ((widget as any)._rendered) {
  232. expect(widget.node.innerHTML).to.be(EXPECTED_MD);
  233. done();
  234. return;
  235. }
  236. setTimeout(loop, 100);
  237. };
  238. setTimeout(loop, 100);
  239. });
  240. });
  241. });
  242. describe('HTMLRenderer', () => {
  243. describe('#mimetypes', () => {
  244. it('should have the text/html mimetype', () => {
  245. let t = new HTMLRenderer();
  246. expect(t.mimetypes).to.eql(['text/html']);
  247. });
  248. });
  249. describe('#sanitizable()', () => {
  250. it('should be `true`', () => {
  251. let t = new HTMLRenderer();
  252. expect(t.sanitizable('text/html')).to.be(true);
  253. });
  254. });
  255. describe('#isSafe()', () => {
  256. it('should be `false`', () => {
  257. let t = new HTMLRenderer();
  258. expect(t.isSafe('text/html')).to.be(false);
  259. });
  260. });
  261. describe('#render()', () => {
  262. it('should set the inner HTML', () => {
  263. let t = new HTMLRenderer();
  264. const source = '<h1>This is great</h1>';
  265. let w = t.render({ mimetype: 'text/html', source });
  266. expect(w.node.innerHTML).to.be('<h1>This is great</h1>');
  267. });
  268. it('should execute a script tag when attached', () => {
  269. const source = '<script>window.y=3;</script>';
  270. let t = new HTMLRenderer();
  271. let w = t.render({ mimetype: 'text/html', source });
  272. expect((window as any).y).to.be(void 0);
  273. w.attach(document.body);
  274. expect((window as any).y).to.be(3);
  275. w.dispose();
  276. });
  277. it('should sanitize when a sanitizer is given', () => {
  278. const source = '<pre><script>window.y=3;</script></pre>';
  279. let t = new HTMLRenderer();
  280. let w = t.render({
  281. mimetype: 'text/html', source, sanitizer: defaultSanitizer
  282. });
  283. expect(w.node.innerHTML).to.be('<pre></pre>');
  284. });
  285. });
  286. });
  287. describe('ImageRenderer', () => {
  288. describe('#mimetypes', () => {
  289. it('should support multiple mimetypes', () => {
  290. let t = new ImageRenderer();
  291. expect(t.mimetypes).to.eql(['image/png', 'image/jpeg', 'image/gif']);
  292. });
  293. });
  294. describe('#sanitizable()', () => {
  295. it('should be `false`', () => {
  296. let t = new ImageRenderer();
  297. expect(t.sanitizable('image/png')).to.be(false);
  298. });
  299. });
  300. describe('#isSafe()', () => {
  301. it('should be `true`', () => {
  302. let t = new ImageRenderer();
  303. expect(t.isSafe('image/png')).to.be(true);
  304. });
  305. });
  306. describe('#render()', () => {
  307. it('should create an <img> with the right mimetype', () => {
  308. let source = 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
  309. let t = new ImageRenderer();
  310. let w = t.render({ mimetype: 'image/png', source });
  311. let el = w.node.firstChild as HTMLImageElement;
  312. expect(el.src).to.be('data:image/png;base64,' + source);
  313. expect(el.localName).to.be('img');
  314. expect(el.innerHTML).to.be('');
  315. source = 'R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=';
  316. w = t.render({ mimetype: 'image/gif', source });
  317. el = w.node.firstChild as HTMLImageElement;
  318. expect(el.src).to.be('data:image/gif;base64,' + source);
  319. expect(el.localName).to.be('img');
  320. expect(el.innerHTML).to.be('');
  321. });
  322. });
  323. });
  324. });