mdast-util-growi-plugin.test.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. import { fromMarkdown } from 'mdast-util-from-markdown';
  2. import { toMarkdown } from 'mdast-util-to-markdown';
  3. import test from 'tape';
  4. import { removePosition } from 'unist-util-remove-position';
  5. import { directiveFromMarkdown, directiveToMarkdown } from '../src/mdast-util-growi-plugin/index.js';
  6. import { directive } from '../src/micromark-extension-growi-plugin/index.js';
  7. test('markdown -> mdast', (t) => {
  8. t.deepEqual(
  9. fromMarkdown('a $b[c]{d} e.', {
  10. extensions: [directive()],
  11. mdastExtensions: [directiveFromMarkdown],
  12. }).children[0],
  13. {
  14. type: 'paragraph',
  15. children: [
  16. {
  17. type: 'text',
  18. value: 'a ',
  19. position: {
  20. start: { line: 1, column: 1, offset: 0 },
  21. end: { line: 1, column: 3, offset: 2 },
  22. },
  23. },
  24. {
  25. type: 'textGrowiPluginDirective',
  26. name: 'b',
  27. attributes: { d: '' },
  28. children: [
  29. {
  30. type: 'text',
  31. value: 'c',
  32. position: {
  33. start: { line: 1, column: 6, offset: 5 },
  34. end: { line: 1, column: 7, offset: 6 },
  35. },
  36. },
  37. ],
  38. position: {
  39. start: { line: 1, column: 3, offset: 2 },
  40. end: { line: 1, column: 11, offset: 10 },
  41. },
  42. },
  43. {
  44. type: 'text',
  45. value: ' e.',
  46. position: {
  47. start: { line: 1, column: 11, offset: 10 },
  48. end: { line: 1, column: 14, offset: 13 },
  49. },
  50. },
  51. ],
  52. position: {
  53. start: { line: 1, column: 1, offset: 0 },
  54. end: { line: 1, column: 14, offset: 13 },
  55. },
  56. },
  57. 'should support directives (text)',
  58. );
  59. t.deepEqual(
  60. fromMarkdown('$a[b]{c}', {
  61. extensions: [directive()],
  62. mdastExtensions: [directiveFromMarkdown],
  63. }).children[0],
  64. {
  65. type: 'leafGrowiPluginDirective',
  66. name: 'a',
  67. attributes: { c: '' },
  68. children: [
  69. {
  70. type: 'text',
  71. value: 'b',
  72. position: {
  73. start: { line: 1, column: 4, offset: 3 },
  74. end: { line: 1, column: 5, offset: 4 },
  75. },
  76. },
  77. ],
  78. position: {
  79. start: { line: 1, column: 1, offset: 0 },
  80. end: { line: 1, column: 9, offset: 8 },
  81. },
  82. },
  83. 'should support directives (leaf)',
  84. );
  85. t.deepEqual(
  86. removePosition(
  87. fromMarkdown('x $a[b *c*\nd]', {
  88. extensions: [directive()],
  89. mdastExtensions: [directiveFromMarkdown],
  90. }),
  91. true,
  92. ),
  93. {
  94. type: 'root',
  95. children: [
  96. {
  97. type: 'paragraph',
  98. children: [
  99. { type: 'text', value: 'x ' },
  100. {
  101. type: 'textGrowiPluginDirective',
  102. name: 'a',
  103. attributes: {},
  104. children: [
  105. { type: 'text', value: 'b ' },
  106. { type: 'emphasis', children: [{ type: 'text', value: 'c' }] },
  107. { type: 'text', value: '\nd' },
  108. ],
  109. },
  110. ],
  111. },
  112. ],
  113. },
  114. 'should support content in a label',
  115. );
  116. t.deepEqual(
  117. removePosition(
  118. fromMarkdown('x $a{#b.c.d e=f g="h&i&unknown;j"}', {
  119. extensions: [directive()],
  120. mdastExtensions: [directiveFromMarkdown],
  121. }),
  122. true,
  123. ),
  124. {
  125. type: 'root',
  126. children: [
  127. {
  128. type: 'paragraph',
  129. children: [
  130. { type: 'text', value: 'x ' },
  131. {
  132. type: 'textGrowiPluginDirective',
  133. name: 'a',
  134. attributes: {
  135. id: 'b', class: 'c d', e: 'f', g: 'h&i&unknown;j',
  136. },
  137. children: [],
  138. },
  139. ],
  140. },
  141. ],
  142. },
  143. 'should support attributes',
  144. );
  145. t.deepEqual(
  146. removePosition(
  147. fromMarkdown('$a{b\nc="d\ne"}', {
  148. extensions: [directive()],
  149. mdastExtensions: [directiveFromMarkdown],
  150. }),
  151. true,
  152. ),
  153. {
  154. type: 'root',
  155. children: [
  156. {
  157. type: 'paragraph',
  158. children: [
  159. {
  160. type: 'textGrowiPluginDirective',
  161. name: 'a',
  162. attributes: { b: '', c: 'd\ne' },
  163. children: [],
  164. },
  165. ],
  166. },
  167. ],
  168. },
  169. 'should support EOLs in attributes',
  170. );
  171. t.end();
  172. });
  173. test('mdast -> markdown', (t) => {
  174. t.deepEqual(
  175. toMarkdown(
  176. {
  177. type: 'paragraph',
  178. children: [
  179. { type: 'text', value: 'a ' },
  180. // @ts-expect-error: `children`, `name` missing.
  181. { type: 'textGrowiPluginDirective' },
  182. { type: 'text', value: ' b.' },
  183. ],
  184. },
  185. { extensions: [directiveToMarkdown] },
  186. ),
  187. 'a $ b.\n',
  188. 'should try to serialize a directive (text) w/o `name`',
  189. );
  190. t.deepEqual(
  191. toMarkdown(
  192. {
  193. type: 'paragraph',
  194. children: [
  195. { type: 'text', value: 'a ' },
  196. // @ts-expect-error: `children` missing.
  197. { type: 'textGrowiPluginDirective', name: 'b' },
  198. { type: 'text', value: ' c.' },
  199. ],
  200. },
  201. { extensions: [directiveToMarkdown] },
  202. ),
  203. 'a $b c.\n',
  204. 'should serialize a directive (text) w/ `name`',
  205. );
  206. t.deepEqual(
  207. toMarkdown(
  208. {
  209. type: 'paragraph',
  210. children: [
  211. { type: 'text', value: 'a ' },
  212. {
  213. type: 'textGrowiPluginDirective',
  214. name: 'b',
  215. children: [{ type: 'text', value: 'c' }],
  216. },
  217. { type: 'text', value: ' d.' },
  218. ],
  219. },
  220. { extensions: [directiveToMarkdown] },
  221. ),
  222. 'a $b[c] d.\n',
  223. 'should serialize a directive (text) w/ `children`',
  224. );
  225. t.deepEqual(
  226. toMarkdown(
  227. {
  228. type: 'paragraph',
  229. children: [
  230. { type: 'text', value: 'a ' },
  231. {
  232. type: 'textGrowiPluginDirective',
  233. name: 'b',
  234. children: [{ type: 'text', value: 'c[d]e' }],
  235. },
  236. { type: 'text', value: ' f.' },
  237. ],
  238. },
  239. { extensions: [directiveToMarkdown] },
  240. ),
  241. 'a $b[c\\[d\\]e] f.\n',
  242. 'should escape brackets in a directive (text) label',
  243. );
  244. t.deepEqual(
  245. toMarkdown(
  246. {
  247. type: 'paragraph',
  248. children: [
  249. { type: 'text', value: 'a ' },
  250. {
  251. type: 'textGrowiPluginDirective',
  252. name: 'b',
  253. children: [{ type: 'text', value: 'c\nd' }],
  254. },
  255. { type: 'text', value: ' e.' },
  256. ],
  257. },
  258. { extensions: [directiveToMarkdown] },
  259. ),
  260. 'a $b[c\nd] e.\n',
  261. 'should support EOLs in a directive (text) label',
  262. );
  263. t.deepEqual(
  264. toMarkdown(
  265. {
  266. type: 'paragraph',
  267. children: [
  268. { type: 'text', value: 'a ' },
  269. {
  270. type: 'textGrowiPluginDirective',
  271. name: 'b',
  272. // @ts-expect-error: should contain only `string`s
  273. attributes: {
  274. c: 'd', e: 'f', g: '', h: null, i: undefined, j: 2,
  275. },
  276. children: [],
  277. },
  278. { type: 'text', value: ' k.' },
  279. ],
  280. },
  281. { extensions: [directiveToMarkdown] },
  282. ),
  283. 'a $b{c="d" e="f" g j="2"} k.\n',
  284. 'should serialize a directive (text) w/ `attributes`',
  285. );
  286. t.deepEqual(
  287. toMarkdown(
  288. {
  289. type: 'paragraph',
  290. children: [
  291. { type: 'text', value: 'a ' },
  292. {
  293. type: 'textGrowiPluginDirective',
  294. name: 'b',
  295. attributes: { class: 'a b\nc', id: 'd', key: 'value' },
  296. children: [],
  297. },
  298. { type: 'text', value: ' k.' },
  299. ],
  300. },
  301. { extensions: [directiveToMarkdown] },
  302. ),
  303. 'a $b{#d .a.b.c key="value"} k.\n',
  304. 'should serialize a directive (text) w/ `id`, `class` attributes',
  305. );
  306. t.deepEqual(
  307. toMarkdown(
  308. {
  309. type: 'paragraph',
  310. children: [
  311. { type: 'text', value: 'a ' },
  312. {
  313. type: 'textGrowiPluginDirective',
  314. name: 'b',
  315. attributes: { x: 'y"\'\r\nz' },
  316. children: [],
  317. },
  318. { type: 'text', value: ' k.' },
  319. ],
  320. },
  321. { extensions: [directiveToMarkdown] },
  322. ),
  323. 'a $b{x="y"\'\r\nz"} k.\n',
  324. 'should encode the quote in an attribute value (text)',
  325. );
  326. t.deepEqual(
  327. toMarkdown(
  328. {
  329. type: 'paragraph',
  330. children: [
  331. { type: 'text', value: 'a ' },
  332. {
  333. type: 'textGrowiPluginDirective',
  334. name: 'b',
  335. attributes: { x: 'y"\'\r\nz' },
  336. children: [],
  337. },
  338. { type: 'text', value: ' k.' },
  339. ],
  340. },
  341. { extensions: [directiveToMarkdown] },
  342. ),
  343. 'a $b{x="y"\'\r\nz"} k.\n',
  344. 'should encode the quote in an attribute value (text)',
  345. );
  346. t.deepEqual(
  347. toMarkdown(
  348. {
  349. type: 'paragraph',
  350. children: [
  351. { type: 'text', value: 'a ' },
  352. {
  353. type: 'textGrowiPluginDirective',
  354. name: 'b',
  355. attributes: { id: 'c#d' },
  356. children: [],
  357. },
  358. { type: 'text', value: ' e.' },
  359. ],
  360. },
  361. { extensions: [directiveToMarkdown] },
  362. ),
  363. 'a $b{id="c#d"} e.\n',
  364. 'should not use the `id` shortcut if impossible characters exist',
  365. );
  366. t.deepEqual(
  367. toMarkdown(
  368. {
  369. type: 'paragraph',
  370. children: [
  371. { type: 'text', value: 'a ' },
  372. {
  373. type: 'textGrowiPluginDirective',
  374. name: 'b',
  375. attributes: { class: 'c.d e<f' },
  376. children: [],
  377. },
  378. { type: 'text', value: ' g.' },
  379. ],
  380. },
  381. { extensions: [directiveToMarkdown] },
  382. ),
  383. 'a $b{class="c.d e<f"} g.\n',
  384. 'should not use the `class` shortcut if impossible characters exist',
  385. );
  386. t.deepEqual(
  387. toMarkdown(
  388. {
  389. type: 'paragraph',
  390. children: [
  391. { type: 'text', value: 'a ' },
  392. {
  393. type: 'textGrowiPluginDirective',
  394. name: 'b',
  395. attributes: { class: 'c.d e f<g hij' },
  396. children: [],
  397. },
  398. { type: 'text', value: ' k.' },
  399. ],
  400. },
  401. { extensions: [directiveToMarkdown] },
  402. ),
  403. 'a $b{.e.hij class="c.d f<g"} k.\n',
  404. 'should not use the `class` shortcut if impossible characters exist (but should use it for classes that don’t)',
  405. );
  406. t.deepEqual(
  407. // @ts-expect-error: `children`, `name` missing.
  408. toMarkdown({ type: 'leafGrowiPluginDirective' }, { extensions: [directiveToMarkdown] }),
  409. '$\n',
  410. 'should try to serialize a directive (leaf) w/o `name`',
  411. );
  412. t.deepEqual(
  413. toMarkdown(
  414. // @ts-expect-error: `children` missing.
  415. { type: 'leafGrowiPluginDirective', name: 'a' },
  416. { extensions: [directiveToMarkdown] },
  417. ),
  418. '$a\n',
  419. 'should serialize a directive (leaf) w/ `name`',
  420. );
  421. t.deepEqual(
  422. toMarkdown(
  423. {
  424. type: 'leafGrowiPluginDirective',
  425. name: 'a',
  426. children: [{ type: 'text', value: 'b' }],
  427. },
  428. { extensions: [directiveToMarkdown] },
  429. ),
  430. '$a[b]\n',
  431. 'should serialize a directive (leaf) w/ `children`',
  432. );
  433. t.deepEqual(
  434. toMarkdown(
  435. {
  436. type: 'leafGrowiPluginDirective',
  437. name: 'a',
  438. children: [{ type: 'text', value: 'b' }],
  439. },
  440. { extensions: [directiveToMarkdown] },
  441. ),
  442. '$a[b]\n',
  443. 'should serialize a directive (leaf) w/ `children`',
  444. );
  445. t.deepEqual(
  446. toMarkdown(
  447. {
  448. type: 'leafGrowiPluginDirective',
  449. name: 'a',
  450. children: [{ type: 'text', value: 'b\nc' }],
  451. },
  452. { extensions: [directiveToMarkdown] },
  453. ),
  454. '$a[b&#xA;c]\n',
  455. 'should serialize a directive (leaf) w/ EOLs in `children`',
  456. );
  457. t.deepEqual(
  458. toMarkdown(
  459. {
  460. type: 'leafGrowiPluginDirective',
  461. name: 'a',
  462. attributes: { id: 'b', class: 'c d', key: 'e\nf' },
  463. children: [],
  464. },
  465. { extensions: [directiveToMarkdown] },
  466. ),
  467. '$a{#b .c.d key="e&#xA;f"}\n',
  468. 'should serialize a directive (leaf) w/ EOLs in `attributes`',
  469. );
  470. t.deepEqual(
  471. toMarkdown(
  472. {
  473. type: 'paragraph',
  474. children: [{ type: 'text', value: 'a$b' }],
  475. },
  476. { extensions: [directiveToMarkdown] },
  477. ),
  478. 'a\\$b\n',
  479. 'should escape a `:` in phrasing when followed by an alpha',
  480. );
  481. t.deepEqual(
  482. toMarkdown(
  483. {
  484. type: 'paragraph',
  485. children: [{ type: 'text', value: 'a$9' }],
  486. },
  487. { extensions: [directiveToMarkdown] },
  488. ),
  489. 'a$9\n',
  490. 'should not escape a `:` in phrasing when followed by a non-alpha',
  491. );
  492. t.deepEqual(
  493. toMarkdown(
  494. {
  495. type: 'paragraph',
  496. children: [{ type: 'text', value: 'a$c' }],
  497. },
  498. { extensions: [directiveToMarkdown] },
  499. ),
  500. 'a\\$c\n',
  501. 'should not escape a `:` in phrasing when preceded by a colon',
  502. );
  503. t.deepEqual(
  504. toMarkdown(
  505. {
  506. type: 'paragraph',
  507. children: [{ type: 'text', value: '$\na' }],
  508. },
  509. { extensions: [directiveToMarkdown] },
  510. ),
  511. '$\na\n',
  512. 'should not escape a `:` at a break',
  513. );
  514. t.deepEqual(
  515. toMarkdown(
  516. {
  517. type: 'paragraph',
  518. children: [{ type: 'text', value: '$a' }],
  519. },
  520. { extensions: [directiveToMarkdown] },
  521. ),
  522. '\\$a\n',
  523. 'should not escape a `:` at a break when followed by an alpha',
  524. );
  525. t.deepEqual(
  526. toMarkdown(
  527. {
  528. type: 'paragraph',
  529. children: [{ type: 'text', value: '$\na' }],
  530. },
  531. { extensions: [directiveToMarkdown] },
  532. ),
  533. '$\na\n',
  534. 'should escape a `:` at a break when followed by a colon',
  535. );
  536. t.deepEqual(
  537. toMarkdown(
  538. {
  539. type: 'paragraph',
  540. children: [
  541. { type: 'textGrowiPluginDirective', name: 'red', children: [] },
  542. { type: 'text', value: '$' },
  543. ],
  544. },
  545. { extensions: [directiveToMarkdown] },
  546. ),
  547. '$red$\n',
  548. 'should escape a `:` after a text directive',
  549. );
  550. t.end();
  551. });