richtext.spec.js 16 KB

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