richtext.spec.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. import {
  2. compileAndStringify,
  3. getRoot,
  4. fireEvent,
  5. createInstance
  6. } from '../../helpers/index'
  7. function compileSnippet (snippet, additional) {
  8. const { render, staticRenderFns } = compileAndStringify(`<div>${snippet}</div>`)
  9. const id = String(Date.now() * Math.random())
  10. const instance = createInstance(id, `
  11. new Vue({
  12. el: 'body',
  13. render: ${render},
  14. staticRenderFns: ${staticRenderFns},
  15. ${additional}
  16. })
  17. `)
  18. return getRoot(instance).children[0]
  19. }
  20. describe('richtext component', () => {
  21. it('with no child', () => {
  22. expect(compileSnippet(`
  23. <richtext></richtext>
  24. `)).toEqual({
  25. type: 'richtext'
  26. })
  27. })
  28. it('with single text node', () => {
  29. expect(compileSnippet(`
  30. <richtext>single</richtext>
  31. `)).toEqual({
  32. type: 'richtext',
  33. attr: {
  34. value: [{
  35. type: 'span',
  36. attr: {
  37. value: 'single'
  38. }
  39. }]
  40. }
  41. })
  42. })
  43. describe('span', () => {
  44. it('single node', () => {
  45. expect(compileSnippet(`
  46. <richtext>
  47. <span>single</span>
  48. </richtext>
  49. `)).toEqual({
  50. type: 'richtext',
  51. attr: {
  52. value: [{
  53. type: 'span',
  54. attr: {
  55. value: 'single'
  56. }
  57. }]
  58. }
  59. })
  60. })
  61. it('multiple node', () => {
  62. expect(compileSnippet(`
  63. <richtext>
  64. <span>AAA</span>
  65. <span>BBB</span>
  66. </richtext>
  67. `)).toEqual({
  68. type: 'richtext',
  69. attr: {
  70. value: [{
  71. type: 'span',
  72. attr: { value: 'AAA' }
  73. }, {
  74. type: 'span',
  75. attr: { value: 'BBB' }
  76. }]
  77. }
  78. })
  79. })
  80. it('with raw text', () => {
  81. expect(compileSnippet(`
  82. <richtext>
  83. AAA
  84. <span>BBB</span>CCC
  85. <span>DDD</span>
  86. </richtext>
  87. `)).toEqual({
  88. type: 'richtext',
  89. attr: {
  90. value: [{
  91. type: 'span',
  92. attr: { value: 'AAA' }
  93. }, {
  94. type: 'span',
  95. attr: { value: 'BBB' }
  96. }, {
  97. type: 'span',
  98. attr: { value: 'CCC' }
  99. }, {
  100. type: 'span',
  101. attr: { value: 'DDD' }
  102. }]
  103. }
  104. })
  105. })
  106. })
  107. describe('a', () => {
  108. it('single node', () => {
  109. expect(compileSnippet(`
  110. <richtext>
  111. <a href="http://whatever.com"></a>
  112. </richtext>
  113. `)).toEqual({
  114. type: 'richtext',
  115. attr: {
  116. value: [{
  117. type: 'a',
  118. attr: { href: 'http://whatever.com' }
  119. }]
  120. }
  121. })
  122. })
  123. it('multiple node', () => {
  124. expect(compileSnippet(`
  125. <richtext>
  126. <a href="http://a.whatever.com"></a>
  127. <a href="http://b.whatever.com"></a>
  128. </richtext>
  129. `)).toEqual({
  130. type: 'richtext',
  131. attr: {
  132. value: [{
  133. type: 'a',
  134. attr: { href: 'http://a.whatever.com' }
  135. }, {
  136. type: 'a',
  137. attr: { href: 'http://b.whatever.com' }
  138. }]
  139. }
  140. })
  141. })
  142. })
  143. describe('image', () => {
  144. it('single node', () => {
  145. expect(compileSnippet(`
  146. <richtext>
  147. <image src="path/to/profile.png"></image>
  148. </richtext>
  149. `)).toEqual({
  150. type: 'richtext',
  151. attr: {
  152. value: [{
  153. type: 'image',
  154. attr: { src: 'path/to/profile.png' }
  155. }]
  156. }
  157. })
  158. })
  159. it('multiple node', () => {
  160. expect(compileSnippet(`
  161. <richtext>
  162. <image src="path/to/A.png"></image>
  163. <image src="path/to/B.png"></image>
  164. </richtext>
  165. `)).toEqual({
  166. type: 'richtext',
  167. attr: {
  168. value: [{
  169. type: 'image',
  170. attr: { src: 'path/to/A.png' }
  171. }, {
  172. type: 'image',
  173. attr: { src: 'path/to/B.png' }
  174. }]
  175. }
  176. })
  177. })
  178. it('with width and height', () => {
  179. expect(compileSnippet(`
  180. <richtext>
  181. <image
  182. style="width:150px;height:150px;"
  183. src="path/to/profile.png">
  184. </image>
  185. </richtext>
  186. `)).toEqual({
  187. type: 'richtext',
  188. attr: {
  189. value: [{
  190. type: 'image',
  191. style: { width: '150px', height: '150px' },
  192. attr: { src: 'path/to/profile.png' }
  193. }]
  194. }
  195. })
  196. })
  197. })
  198. describe('nested', () => {
  199. it('span', () => {
  200. expect(compileSnippet(`
  201. <richtext>
  202. <span>AAA
  203. <span>
  204. <span>BBB</span>
  205. <span><span>CCC</span>DDD</span>
  206. </span>
  207. </span>
  208. </richtext>
  209. `)).toEqual({
  210. type: 'richtext',
  211. attr: {
  212. value: [{
  213. type: 'span',
  214. children: [{
  215. type: 'span',
  216. attr: { value: 'AAA' }
  217. }, {
  218. type: 'span',
  219. children: [{
  220. type: 'span',
  221. attr: { value: 'BBB' }
  222. }, {
  223. type: 'span',
  224. children: [{
  225. type: 'span',
  226. attr: { value: 'CCC' }
  227. }, {
  228. type: 'span',
  229. attr: { value: 'DDD' }
  230. }]
  231. }]
  232. }]
  233. }]
  234. }
  235. })
  236. })
  237. it('image and a', () => {
  238. expect(compileSnippet(`
  239. <richtext>
  240. <span>title</span>
  241. <a href="http://remote.com/xx.js">
  242. <span><span>name</span></span>
  243. <image src="path/to/yy.gif"></image>
  244. </a>
  245. </richtext>
  246. `)).toEqual({
  247. type: 'richtext',
  248. attr: {
  249. value: [{
  250. type: 'span',
  251. attr: { value: 'title' }
  252. }, {
  253. type: 'a',
  254. attr: { href: 'http://remote.com/xx.js' },
  255. children: [{
  256. type: 'span',
  257. children: [{
  258. type: 'span',
  259. attr: { value: 'name' }
  260. }]
  261. }, {
  262. type: 'image',
  263. attr: { src: 'path/to/yy.gif' }
  264. }]
  265. }]
  266. }
  267. })
  268. })
  269. })
  270. describe('with styles', () => {
  271. it('inline', () => {
  272. expect(compileSnippet(`
  273. <richtext>
  274. <span style="font-size:16px;color:#FF6600;">ABCD</span>
  275. <image style="width:33.33px;height:66.67px" src="path/to/A.png"></image>
  276. </richtext>
  277. `)).toEqual({
  278. type: 'richtext',
  279. attr: {
  280. value: [{
  281. type: 'span',
  282. style: { fontSize: '16px', color: '#FF6600' },
  283. attr: { value: 'ABCD' }
  284. }, {
  285. type: 'image',
  286. style: { width: '33.33px', height: '66.67px' },
  287. attr: { src: 'path/to/A.png' }
  288. }]
  289. }
  290. })
  291. })
  292. it('class list', () => {
  293. expect(compileSnippet(`
  294. <richtext>
  295. <image class="icon" src="path/to/A.png"></image>
  296. <span class="title large">ABCD</span>
  297. </richtext>
  298. `, `
  299. style: {
  300. title: { color: '#FF6600' },
  301. large: { fontSize: 24 },
  302. icon: { width: 40, height: 60 }
  303. }
  304. `)).toEqual({
  305. type: 'richtext',
  306. attr: {
  307. value: [{
  308. type: 'image',
  309. style: { width: 40, height: 60 },
  310. attr: { src: 'path/to/A.png' }
  311. }, {
  312. type: 'span',
  313. style: { fontSize: 24, color: '#FF6600' },
  314. attr: { value: 'ABCD' }
  315. }]
  316. }
  317. })
  318. })
  319. })
  320. describe('data binding', () => {
  321. it('simple', () => {
  322. expect(compileSnippet(`
  323. <richtext>
  324. <span>{{name}}</span>
  325. </richtext>
  326. `, `data: { name: 'ABCDEFG' }`)).toEqual({
  327. type: 'richtext',
  328. attr: {
  329. value: [{
  330. type: 'span',
  331. attr: { value: 'ABCDEFG' }
  332. }]
  333. }
  334. })
  335. })
  336. it('nested', () => {
  337. expect(compileSnippet(`
  338. <richtext>
  339. <span>{{a}}</span>
  340. <span>{{b}}<span>{{c.d}}</span></span>
  341. <span>{{e}}</span>
  342. </richtext>
  343. `, `
  344. data: { a: 'A', b: 'B', c: { d: 'CD' }, e: 'E' }
  345. `)).toEqual({
  346. type: 'richtext',
  347. attr: {
  348. value: [{
  349. type: 'span',
  350. attr: { value: 'A' }
  351. }, {
  352. type: 'span',
  353. children: [{
  354. type: 'span',
  355. attr: { value: 'B' }
  356. }, {
  357. type: 'span',
  358. attr: { value: 'CD' }
  359. }]
  360. }, {
  361. type: 'span',
  362. attr: { value: 'E' }
  363. }]
  364. }
  365. })
  366. })
  367. it('update', () => {
  368. expect(compileSnippet(`
  369. <richtext>
  370. <span>{{name}}</span>
  371. </richtext>
  372. `, `
  373. data: { name: 'default' },
  374. created: function () {
  375. this.name = 'updated'
  376. }
  377. `)).toEqual({
  378. type: 'richtext',
  379. attr: {
  380. value: [{
  381. type: 'span',
  382. attr: { value: 'updated' }
  383. }]
  384. }
  385. })
  386. })
  387. it('attribute', () => {
  388. expect(compileSnippet(`
  389. <richtext>
  390. <span :label="label">{{name}}</span>
  391. </richtext>
  392. `, `
  393. data: {
  394. label: 'uid',
  395. name: '10100'
  396. }
  397. `)).toEqual({
  398. type: 'richtext',
  399. attr: {
  400. value: [{
  401. type: 'span',
  402. attr: {
  403. label: 'uid',
  404. value: '10100'
  405. }
  406. }]
  407. }
  408. })
  409. })
  410. it('update attribute', () => {
  411. expect(compileSnippet(`
  412. <richtext>
  413. <span :label="label">{{name}}</span>
  414. </richtext>
  415. `, `
  416. data: {
  417. label: 'name',
  418. name: 'Hanks'
  419. },
  420. created: function () {
  421. this.label = 'uid';
  422. this.name = '10100';
  423. }
  424. `)).toEqual({
  425. type: 'richtext',
  426. attr: {
  427. value: [{
  428. type: 'span',
  429. attr: {
  430. label: 'uid',
  431. value: '10100'
  432. }
  433. }]
  434. }
  435. })
  436. })
  437. it('inline style', () => {
  438. expect(compileSnippet(`
  439. <richtext>
  440. <span :style="styleObject">ABCD</span>
  441. <span :style="{ textAlign: align, color: 'red' }">EFGH</span>
  442. </richtext>
  443. `, `
  444. data: {
  445. styleObject: { fontSize: '32px', color: '#F6F660' },
  446. align: 'center'
  447. }
  448. `)).toEqual({
  449. type: 'richtext',
  450. attr: {
  451. value: [{
  452. type: 'span',
  453. style: { fontSize: '32px', color: '#F6F660' },
  454. attr: { value: 'ABCD' }
  455. }, {
  456. type: 'span',
  457. style: { textAlign: 'center', color: 'red' },
  458. attr: { value: 'EFGH' }
  459. }]
  460. }
  461. })
  462. })
  463. it('class list', () => {
  464. expect(compileSnippet(`
  465. <richtext>
  466. <image :class="classList" src="path/to/A.png"></image>
  467. <span :class="['title', size]">ABCD</span>
  468. <span class="large" style="color:#F6F0F4">EFGH</span>
  469. </richtext>
  470. `, `
  471. style: {
  472. title: { color: '#FF6600' },
  473. large: { fontSize: 24 },
  474. icon: { width: 40, height: 60 }
  475. },
  476. data: {
  477. classList: ['unknown'],
  478. size: 'small'
  479. },
  480. created: function () {
  481. this.classList = ['icon'];
  482. this.size = 'large';
  483. }
  484. `)).toEqual({
  485. type: 'richtext',
  486. attr: {
  487. value: [{
  488. type: 'image',
  489. style: { width: 40, height: 60 },
  490. attr: { src: 'path/to/A.png' }
  491. }, {
  492. type: 'span',
  493. style: { fontSize: 24, color: '#FF6600' },
  494. attr: { value: 'ABCD' }
  495. }, {
  496. type: 'span',
  497. style: { fontSize: 24, color: '#F6F0F4' },
  498. attr: { value: 'EFGH' }
  499. }]
  500. }
  501. })
  502. })
  503. it('update inline style', () => {
  504. expect(compileSnippet(`
  505. <richtext>
  506. <span :style="styleObject">ABCD</span>
  507. <span :style="{ textAlign: align, color: 'red' }">EFGH</span>
  508. </richtext>
  509. `, `
  510. data: {
  511. styleObject: { fontSize: '32px', color: '#F6F660' }
  512. },
  513. created: function () {
  514. this.styleObject = { fontSize: '24px', color: 'blue' }
  515. this.styleObject.color = '#ABCDEF'
  516. this.align = 'left'
  517. }
  518. `)).toEqual({
  519. type: 'richtext',
  520. attr: {
  521. value: [{
  522. type: 'span',
  523. style: { fontSize: '24px', color: '#ABCDEF' },
  524. attr: { value: 'ABCD' }
  525. }, {
  526. type: 'span',
  527. style: { textAlign: 'left', color: 'red' },
  528. attr: { value: 'EFGH' }
  529. }]
  530. }
  531. })
  532. })
  533. })
  534. describe('itself', () => {
  535. it('inline styles', () => {
  536. expect(compileSnippet(`
  537. <richtext style="background-color:red">
  538. <span>empty</span>
  539. </richtext>
  540. `)).toEqual({
  541. type: 'richtext',
  542. style: { backgroundColor: 'red' },
  543. attr: {
  544. value: [{
  545. type: 'span',
  546. attr: { value: 'empty' }
  547. }]
  548. }
  549. })
  550. })
  551. it('class list', () => {
  552. expect(compileSnippet(`
  553. <richtext class="title">
  554. <span class="large">ABCD</span>
  555. </richtext>
  556. `, `
  557. style: {
  558. title: { backgroundColor: '#FF6600', height: 200 },
  559. large: { fontSize: 24 }
  560. }
  561. `)).toEqual({
  562. type: 'richtext',
  563. classList: ['title'],
  564. attr: {
  565. value: [{
  566. type: 'span',
  567. style: { fontSize: 24 },
  568. attr: { value: 'ABCD' }
  569. }]
  570. }
  571. })
  572. })
  573. it('update styles', () => {
  574. expect(compileSnippet(`
  575. <richtext :class="classList" :style="{ backgroundColor: color }">
  576. <span class="large">ABCD</span>
  577. </richtext>
  578. `, `
  579. data: { classList: ['unknow'], color: '#FF6600' },
  580. style: {
  581. title: { height: 200 },
  582. large: { fontSize: 24 }
  583. },
  584. created: function () {
  585. this.classList = ['title']
  586. }
  587. `)).toEqual({
  588. type: 'richtext',
  589. classList: ['title'],
  590. style: { backgroundColor: '#FF6600' },
  591. attr: {
  592. value: [{
  593. type: 'span',
  594. style: { fontSize: 24 },
  595. attr: { value: 'ABCD' }
  596. }]
  597. }
  598. })
  599. })
  600. it('bind events', (done) => {
  601. const { render, staticRenderFns } = compileAndStringify(`
  602. <div>
  603. <richtext @click="handler">
  604. <span>Label: {{label}}</span>
  605. </richtext>
  606. </div>
  607. `)
  608. const id = String(Date.now() * Math.random())
  609. const instance = createInstance(id, `
  610. new Vue({
  611. el: 'body',
  612. render: ${render},
  613. staticRenderFns: ${staticRenderFns},
  614. data: { label: 'AAA' },
  615. methods: {
  616. handler: function () {
  617. this.label = 'BBB'
  618. }
  619. }
  620. })
  621. `)
  622. const richtext = instance.document.body.children[0]
  623. fireEvent(instance, richtext.ref, 'click')
  624. setTimeout(() => {
  625. expect(getRoot(instance).children[0]).toEqual({
  626. type: 'richtext',
  627. event: ['click'],
  628. attr: {
  629. value: [{
  630. type: 'span',
  631. attr: { value: 'Label: BBB' }
  632. }]
  633. }
  634. })
  635. done()
  636. }, 0)
  637. })
  638. it('v-for', () => {
  639. expect(compileSnippet(`
  640. <div>
  641. <richtext v-for="k in labels">
  642. <span>{{k}}</span>
  643. </richtext>
  644. </div>
  645. `, `
  646. data: {
  647. labels: ['A', 'B', 'C']
  648. }
  649. `)).toEqual({
  650. type: 'div',
  651. children: [{
  652. type: 'richtext',
  653. attr: { value: [{ type: 'span', attr: { value: 'A' }}] }
  654. }, {
  655. type: 'richtext',
  656. attr: { value: [{ type: 'span', attr: { value: 'B' }}] }
  657. }, {
  658. type: 'richtext',
  659. attr: { value: [{ type: 'span', attr: { value: 'C' }}] }
  660. }]
  661. })
  662. })
  663. })
  664. })