render-html.test.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. import { parseHTML } from 'linkedom';
  2. import { loadFixture } from '../../../astro/test/test-utils.js';
  3. import assert from 'node:assert/strict';
  4. import { after, before, describe, it } from 'node:test';
  5. async function getFixture(name) {
  6. return await loadFixture({
  7. root: new URL(`./fixtures/${name}/`, import.meta.url),
  8. });
  9. }
  10. describe('Markdoc - render html', () => {
  11. let fixture;
  12. before(async () => {
  13. fixture = await getFixture('render-html');
  14. });
  15. describe('dev', () => {
  16. let devServer;
  17. before(async () => {
  18. devServer = await fixture.startDevServer();
  19. });
  20. after(async () => {
  21. await devServer.stop();
  22. });
  23. it('renders content - simple', async () => {
  24. const res = await fixture.fetch('/simple');
  25. const html = await res.text();
  26. renderSimpleChecks(html);
  27. });
  28. it('renders content - nested-html', async () => {
  29. const res = await fixture.fetch('/nested-html');
  30. const html = await res.text();
  31. renderNestedHTMLChecks(html);
  32. });
  33. it('renders content - components interleaved with html', async () => {
  34. const res = await fixture.fetch('/components');
  35. const html = await res.text();
  36. renderComponentsHTMLChecks(html);
  37. });
  38. it('renders content - randomly cased html attributes', async () => {
  39. const res = await fixture.fetch('/randomly-cased-html-attributes');
  40. const html = await res.text();
  41. renderRandomlyCasedHTMLAttributesChecks(html);
  42. });
  43. });
  44. describe('build', () => {
  45. before(async () => {
  46. await fixture.build();
  47. });
  48. it('renders content - simple', async () => {
  49. const html = await fixture.readFile('/simple/index.html');
  50. renderSimpleChecks(html);
  51. });
  52. it('renders content - nested-html', async () => {
  53. const html = await fixture.readFile('/nested-html/index.html');
  54. renderNestedHTMLChecks(html);
  55. });
  56. it('renders content - components interleaved with html', async () => {
  57. const html = await fixture.readFile('/components/index.html');
  58. renderComponentsHTMLChecks(html);
  59. });
  60. it('renders content - randomly cased html attributes', async () => {
  61. const html = await fixture.readFile('/randomly-cased-html-attributes/index.html');
  62. renderRandomlyCasedHTMLAttributesChecks(html);
  63. });
  64. });
  65. });
  66. /** @param {string} html */
  67. function renderSimpleChecks(html) {
  68. const { document } = parseHTML(html);
  69. const h2 = document.querySelector('h2');
  70. assert.equal(h2.textContent, 'Simple post header');
  71. const spanInsideH2 = document.querySelector('h2 > span');
  72. assert.equal(spanInsideH2.textContent, 'post');
  73. assert.equal(spanInsideH2.className, 'inside-h2');
  74. assert.equal(spanInsideH2.style.color, 'fuscia');
  75. const p1 = document.querySelector('article > p:nth-of-type(1)');
  76. assert.equal(p1.children.length, 1);
  77. assert.equal(p1.textContent, 'This is a simple Markdoc post.');
  78. const p2 = document.querySelector('article > p:nth-of-type(2)');
  79. assert.equal(p2.children.length, 0);
  80. assert.equal(p2.textContent, 'This is a paragraph!');
  81. const p3 = document.querySelector('article > p:nth-of-type(3)');
  82. assert.equal(p3.children.length, 1);
  83. assert.equal(p3.textContent, 'This is a span inside a paragraph!');
  84. }
  85. /** @param {string} html */
  86. function renderNestedHTMLChecks(html) {
  87. const { document } = parseHTML(html);
  88. const p1 = document.querySelector('p:nth-of-type(1)');
  89. assert.equal(p1.id, 'p1');
  90. assert.equal(p1.textContent, 'before inner after');
  91. assert.equal(p1.children.length, 1);
  92. const p1Span1 = p1.querySelector('span');
  93. assert.equal(p1Span1.textContent, 'inner');
  94. assert.equal(p1Span1.id, 'inner1');
  95. assert.equal(p1Span1.className, 'inner-class');
  96. assert.equal(p1Span1.style.color, 'hotpink');
  97. const p2 = document.querySelector('p:nth-of-type(2)');
  98. assert.equal(p2.id, 'p2');
  99. assert.equal(p2.textContent, '\n before\n inner\n after\n');
  100. assert.equal(p2.children.length, 1);
  101. const divL1 = document.querySelector('div:nth-of-type(1)');
  102. assert.equal(divL1.id, 'div-l1');
  103. assert.equal(divL1.children.length, 2);
  104. const divL2_1 = divL1.querySelector('div:nth-of-type(1)');
  105. assert.equal(divL2_1.id, 'div-l2-1');
  106. assert.equal(divL2_1.children.length, 1);
  107. const p3 = divL2_1.querySelector('p:nth-of-type(1)');
  108. assert.equal(p3.id, 'p3');
  109. assert.equal(p3.textContent, 'before inner after');
  110. assert.equal(p3.children.length, 1);
  111. const divL2_2 = divL1.querySelector('div:nth-of-type(2)');
  112. assert.equal(divL2_2.id, 'div-l2-2');
  113. assert.equal(divL2_2.children.length, 2);
  114. const p4 = divL2_2.querySelector('p:nth-of-type(1)');
  115. assert.equal(p4.id, 'p4');
  116. assert.equal(p4.textContent, 'before inner after');
  117. assert.equal(p4.children.length, 1);
  118. const p5 = divL2_2.querySelector('p:nth-of-type(2)');
  119. assert.equal(p5.id, 'p5');
  120. assert.equal(p5.textContent, 'before inner after');
  121. assert.equal(p5.children.length, 1);
  122. }
  123. /**
  124. *
  125. * @param {string} html */
  126. function renderRandomlyCasedHTMLAttributesChecks(html) {
  127. const { document } = parseHTML(html);
  128. const td1 = document.querySelector('#td1');
  129. const td2 = document.querySelector('#td1');
  130. const td3 = document.querySelector('#td1');
  131. const td4 = document.querySelector('#td1');
  132. // all four <td>'s which had randomly cased variants of colspan/rowspan should all be rendered lowercased at this point
  133. assert.equal(td1.getAttribute('colspan'), '3');
  134. assert.equal(td1.getAttribute('rowspan'), '2');
  135. assert.equal(td2.getAttribute('colspan'), '3');
  136. assert.equal(td2.getAttribute('rowspan'), '2');
  137. assert.equal(td3.getAttribute('colspan'), '3');
  138. assert.equal(td3.getAttribute('rowspan'), '2');
  139. assert.equal(td4.getAttribute('colspan'), '3');
  140. assert.equal(td4.getAttribute('rowspan'), '2');
  141. }
  142. /**
  143. * Asserts that the rendered HTML tags with interleaved Markdoc tags (both block and inline) rendered in the expected nested graph of elemements
  144. *
  145. * @param {string} html */
  146. function renderComponentsHTMLChecks(html) {
  147. const { document } = parseHTML(html);
  148. const naturalP1 = document.querySelector('article > p:nth-of-type(1)');
  149. assert.equal(naturalP1.textContent, 'This is a inline mark in regular Markdown markup.');
  150. assert.equal(naturalP1.children.length, 1);
  151. const p1 = document.querySelector('article > p:nth-of-type(2)');
  152. assert.equal(p1.id, 'p1');
  153. assert.equal(p1.textContent, 'This is a inline mark under some HTML');
  154. assert.equal(p1.children.length, 1);
  155. assertInlineMark(p1.children[0]);
  156. const div1p1 = document.querySelector('article > #div1 > p:nth-of-type(1)');
  157. assert.equal(div1p1.id, 'div1-p1');
  158. assert.equal(div1p1.textContent, 'This is a inline mark under some HTML');
  159. assert.equal(div1p1.children.length, 1);
  160. assertInlineMark(div1p1.children[0]);
  161. const div1p2 = document.querySelector('article > #div1 > p:nth-of-type(2)');
  162. assert.equal(div1p2.id, 'div1-p2');
  163. assert.equal(div1p2.textContent, 'This is a inline mark under some HTML');
  164. assert.equal(div1p2.children.length, 1);
  165. const div1p2span1 = div1p2.querySelector('span');
  166. assert.equal(div1p2span1.id, 'div1-p2-span1');
  167. assert.equal(div1p2span1.textContent, 'inline mark');
  168. assert.equal(div1p2span1.children.length, 1);
  169. assertInlineMark(div1p2span1.children[0]);
  170. const aside1 = document.querySelector('article > aside:nth-of-type(1)');
  171. const aside1Title = aside1.querySelector('p.title');
  172. assert.equal(aside1Title.textContent.trim(), 'Aside One');
  173. const aside1Section = aside1.querySelector('section');
  174. const aside1SectionP1 = aside1Section.querySelector('p:nth-of-type(1)');
  175. assert.equal(
  176. aside1SectionP1.textContent,
  177. "I'm a Markdown paragraph inside an top-level aside tag"
  178. );
  179. const aside1H2_1 = aside1Section.querySelector('h2:nth-of-type(1)');
  180. assert.equal(aside1H2_1.id, 'im-an-h2-via-markdown-markup'); // automatic slug
  181. assert.equal(aside1H2_1.textContent, "I'm an H2 via Markdown markup");
  182. const aside1H2_2 = aside1Section.querySelector('h2:nth-of-type(2)');
  183. assert.equal(aside1H2_2.id, 'h-two');
  184. assert.equal(aside1H2_2.textContent, "I'm an H2 via HTML markup");
  185. const aside1SectionP2 = aside1Section.querySelector('p:nth-of-type(2)');
  186. assert.equal(aside1SectionP2.textContent, 'Markdown bold vs HTML bold');
  187. assert.equal(aside1SectionP2.children.length, 2);
  188. const aside1SectionP2Strong1 = aside1SectionP2.querySelector('strong:nth-of-type(1)');
  189. assert.equal(aside1SectionP2Strong1.textContent, 'Markdown bold');
  190. const aside1SectionP2Strong2 = aside1SectionP2.querySelector('strong:nth-of-type(2)');
  191. assert.equal(aside1SectionP2Strong2.textContent, 'HTML bold');
  192. const article = document.querySelector('article');
  193. assert.equal(article.textContent.includes('RENDERED'), true);
  194. assert.notEqual(article.textContent.includes('NOT RENDERED'), true);
  195. const section1 = document.querySelector('article > #section1');
  196. const section1div1 = section1.querySelector('#div1');
  197. const section1Aside1 = section1div1.querySelector('aside:nth-of-type(1)');
  198. const section1Aside1Title = section1Aside1.querySelector('p.title');
  199. assert.equal(section1Aside1Title.textContent.trim(), 'Nested un-indented Aside');
  200. const section1Aside1Section = section1Aside1.querySelector('section');
  201. const section1Aside1SectionP1 = section1Aside1Section.querySelector('p:nth-of-type(1)');
  202. assert.equal(section1Aside1SectionP1.textContent, 'regular Markdown markup');
  203. const section1Aside1SectionP4 = section1Aside1Section.querySelector('p:nth-of-type(2)');
  204. assert.equal(section1Aside1SectionP4.textContent, 'nested inline mark content');
  205. assert.equal(section1Aside1SectionP4.children.length, 1);
  206. assertInlineMark(section1Aside1SectionP4.children[0]);
  207. const section1div2 = section1.querySelector('#div2');
  208. const section1Aside2 = section1div2.querySelector('aside:nth-of-type(1)');
  209. const section1Aside2Title = section1Aside2.querySelector('p.title');
  210. assert.equal(section1Aside2Title.textContent.trim(), 'Nested indented Aside 💀');
  211. const section1Aside2Section = section1Aside2.querySelector('section');
  212. const section1Aside2SectionP1 = section1Aside2Section.querySelector('p:nth-of-type(1)');
  213. assert.equal(section1Aside2SectionP1.textContent, 'regular Markdown markup');
  214. const section1Aside1SectionP5 = section1Aside2Section.querySelector('p:nth-of-type(2)');
  215. assert.equal(section1Aside1SectionP5.id, 'p5');
  216. assert.equal(section1Aside1SectionP5.children.length, 1);
  217. const section1Aside1SectionP5Span1 = section1Aside1SectionP5.children[0];
  218. assert.equal(section1Aside1SectionP5Span1.textContent, 'inline mark');
  219. assert.equal(section1Aside1SectionP5Span1.children.length, 1);
  220. const section1Aside1SectionP5Span1Span1 = section1Aside1SectionP5Span1.children[0];
  221. assert.equal(section1Aside1SectionP5Span1Span1.textContent, ' mark');
  222. }
  223. /** @param {HTMLElement | null | undefined} el */
  224. function assertInlineMark(el) {
  225. assert.ok(el);
  226. assert.equal(el.children.length, 0);
  227. assert.equal(el.textContent, 'inline mark');
  228. assert.equal(el.className, 'mark');
  229. assert.equal(el.style.color, 'hotpink');
  230. }