rendermime.spec.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  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. Widget
  6. } from 'phosphor-widget';
  7. import {
  8. LatexRenderer, PDFRenderer, JavascriptRenderer,
  9. SVGRenderer, MarkdownRenderer, TextRenderer, HTMLRenderer, ImageRenderer
  10. } from '../../../lib/renderers';
  11. import {
  12. RenderMime
  13. } from '../../../lib/rendermime';
  14. const TRANSFORMERS = [
  15. new JavascriptRenderer(),
  16. new MarkdownRenderer(),
  17. new HTMLRenderer(),
  18. new PDFRenderer(),
  19. new ImageRenderer(),
  20. new SVGRenderer(),
  21. new LatexRenderer(),
  22. new TextRenderer()
  23. ];
  24. export
  25. function defaultRenderMime(): RenderMime<Widget> {
  26. let renderers: RenderMime.MimeMap<RenderMime.IRenderer<Widget>> = {};
  27. let order: string[] = [];
  28. for (let t of TRANSFORMERS) {
  29. for (let m of t.mimetypes) {
  30. renderers[m] = t;
  31. order.push(m);
  32. }
  33. }
  34. return new RenderMime<Widget>({ renderers, order });
  35. }
  36. describe('jupyter-ui', () => {
  37. describe('RenderMime', () => {
  38. describe('#constructor()', () => {
  39. it('should accept a mapping and a default order', () => {
  40. let r = defaultRenderMime();
  41. expect(r instanceof RenderMime).to.be(true);
  42. });
  43. });
  44. describe('#render()', () => {
  45. it('should render a mimebundle', (done) => {
  46. let r = defaultRenderMime();
  47. r.render({ 'text/plain': 'foo' }).then(w => {
  48. expect(w instanceof Widget).to.be(true);
  49. }).then(done, done);
  50. });
  51. it('should return `undefined` for an unregistered mime type', (done) => {
  52. let r = defaultRenderMime();
  53. r.render({ 'text/fizz': 'buzz' }).then(value => {
  54. expect(value).to.be(void 0);
  55. }).then(done, done);
  56. });
  57. it('should render with the mimetype of highest precidence', (done) => {
  58. let bundle: RenderMime.MimeMap<string> = {
  59. 'text/plain': 'foo',
  60. 'text/html': '<h1>foo</h1>'
  61. };
  62. let r = defaultRenderMime();
  63. r.render(bundle, true).then(w => {
  64. let el = w.node.firstChild as HTMLElement;
  65. expect(el.localName).to.be('h1');
  66. }).then(done, done);
  67. });
  68. it('should render the mimetype that is safe', (done) => {
  69. let bundle: RenderMime.MimeMap<string> = {
  70. 'text/plain': 'foo',
  71. 'text/javascript': 'window.x = 1',
  72. 'image/png': 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
  73. };
  74. let r = defaultRenderMime();
  75. r.render(bundle, false).then(w => {
  76. let el = w.node.firstChild as HTMLElement;
  77. expect(el.localName).to.be('img');
  78. }).then(done, done);
  79. });
  80. it('should render the mimetype that is sanitizable', (done) => {
  81. let bundle: RenderMime.MimeMap<string> = {
  82. 'text/plain': 'foo',
  83. 'text/html': '<h1>foo</h1>'
  84. };
  85. let r = defaultRenderMime();
  86. r.render(bundle, false).then(w => {
  87. let el = w.node.firstChild as HTMLElement;
  88. expect(el.localName).to.be('h1');
  89. }).then(done, done);
  90. });
  91. it('should sanitize markdown', (done) => {
  92. let md = require('../../../examples/filebrowser/sample.md');
  93. let r = defaultRenderMime();
  94. r.render({ 'text/markdown': md as string }).then(widget => {
  95. expect(widget.node.innerHTML).to.be(`<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`);
  96. }).then(done, done);
  97. });
  98. it('should sanitize html', (done) => {
  99. let bundle: RenderMime.MimeMap<string> = {
  100. 'text/html': '<h1>foo <script>window.x=1></scrip></h1>'
  101. };
  102. let r = defaultRenderMime();
  103. r.render(bundle).then(widget => {
  104. expect(widget.node.innerHTML).to.be('<h1>foo </h1>');
  105. }).then(done, done);
  106. });
  107. it('should sanitize svg', (done) => {
  108. let bundle: RenderMime.MimeMap<string> = {
  109. 'image/svg+xml': '<svg><script>windox.x=1</script></svg>'
  110. };
  111. let r = defaultRenderMime();
  112. r.render(bundle).then(widget => {
  113. expect(widget.node.innerHTML.indexOf('svg')).to.not.be(-1);
  114. expect(widget.node.innerHTML.indexOf('script')).to.be(-1);
  115. }).then(done, done);
  116. });
  117. });
  118. describe('#preferredMimetype()', () => {
  119. it('should find the preferred mimetype in a bundle', () => {
  120. let bundle: RenderMime.MimeMap<string> = {
  121. 'text/plain': 'foo',
  122. 'text/html': '<h1>foo</h1>'
  123. };
  124. let r = defaultRenderMime();
  125. expect(r.preferredMimetype(bundle)).to.be('text/html');
  126. });
  127. it('should return `undefined` if there are no registered mimetypes', () => {
  128. let r = defaultRenderMime();
  129. expect(r.preferredMimetype({ 'text/fizz': 'buzz' })).to.be(void 0);
  130. });
  131. it('should select the mimetype that is safe', () => {
  132. let bundle: RenderMime.MimeMap<string> = {
  133. 'text/plain': 'foo',
  134. 'text/javascript': 'window.x = 1',
  135. 'image/png': 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
  136. };
  137. let r = defaultRenderMime();
  138. expect(r.preferredMimetype(bundle, false)).to.be('image/png');
  139. });
  140. it('should render the mimetype that is sanitizable', () => {
  141. let bundle: RenderMime.MimeMap<string> = {
  142. 'text/plain': 'foo',
  143. 'text/html': '<h1>foo</h1>'
  144. };
  145. let r = defaultRenderMime();
  146. expect(r.preferredMimetype(bundle, false)).to.be('text/html');
  147. });
  148. });
  149. describe('#clone()', () => {
  150. it('should clone the rendermime instance with shallow copies of data', () => {
  151. let r = defaultRenderMime();
  152. let c = r.clone();
  153. expect(c.order).to.eql(r.order);
  154. let t = new TextRenderer();
  155. c.addRenderer('text/foo', t);
  156. expect(r).to.not.be(c);
  157. });
  158. });
  159. describe('#addRenderer()', () => {
  160. it('should add a renderer by mimetype', () => {
  161. let r = defaultRenderMime();
  162. let t = new TextRenderer();
  163. r.addRenderer('text/foo', t);
  164. let index = r.order.indexOf('text/foo');
  165. expect(index).to.be(0);
  166. });
  167. it('should take an optional order index', () => {
  168. let r = defaultRenderMime();
  169. let t = new TextRenderer();
  170. let len = r.order.length;
  171. r.addRenderer('text/foo', t, 0);
  172. let index = r.order.indexOf('text/foo');
  173. expect(index).to.be(0);
  174. expect(r.order.length).to.be(len + 1);
  175. });
  176. });
  177. describe('#removeRenderer()', () => {
  178. it('should remove a renderer by mimetype', () => {
  179. let r = defaultRenderMime();
  180. r.removeRenderer('text/html');
  181. let bundle: RenderMime.MimeMap<string> = {
  182. 'text/html': '<h1>foo</h1>'
  183. };
  184. expect(r.preferredMimetype(bundle)).to.be(void 0);
  185. });
  186. it('should be a no-op if the mimetype is not registered', () => {
  187. let r = defaultRenderMime();
  188. r.removeRenderer('text/foo');
  189. });
  190. });
  191. describe('#order', () => {
  192. it('should get the ordered list of mimetypes', () => {
  193. let r = defaultRenderMime();
  194. expect(r.order.indexOf('text/html')).to.not.be(-1);
  195. });
  196. it('should set the ordered list of mimetypes', () => {
  197. let r = defaultRenderMime();
  198. let order = r.order.reverse();
  199. r.order = order;
  200. expect(r.order).to.eql(order);
  201. });
  202. });
  203. });
  204. });