test.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /**
  2. * @fileoverview 单元测试
  3. */
  4. const path = require('path')
  5. const simulate = require('miniprogram-simulate')
  6. const html = require('./content') // 测试 html
  7. const dist = '../dev/mp-weixin/components/mp-html/index' // 组件目录
  8. const mpHtml = simulate.load(path.resolve(__dirname, dist), 'mp-html')
  9. // 渲染测试
  10. test('render', async () => {
  11. // 创建和渲染页面
  12. const id = simulate.load({
  13. data: {
  14. containerStyle: '',
  15. copyLink: true,
  16. lazyLoad: true,
  17. pauseVideo: true,
  18. previewImg: true,
  19. useAnchor: true
  20. },
  21. template:
  22. `<scroll-view id="scroll" style="height:100px" scroll-y scroll-top="{{top}}">
  23. <mp-html id="article" container-style="{{containerStyle}}" content="{{html}}" domain="https://mp-html.oss-cn-hangzhou.aliyuncs.com" copy-link="{{copyLink}}" loading-img="xxx" error-img="xxx" lazy-load="{{lazyLoad}}" pause-video="{{pauseVideo}}" preview-img="{{previewImg}}" scroll-table use-anchor="{{useAnchor}}">加载中...</mp-html>
  24. </scroll-view>`,
  25. usingComponents: {
  26. 'mp-html': mpHtml
  27. }
  28. })
  29. const page = simulate.render(id)
  30. // 设置数据
  31. page.setData({
  32. html
  33. })
  34. await simulate.sleep(1000)
  35. const comp = page.querySelector('#article')
  36. expect(comp.dom.tagName).toBe('MP-HTML')
  37. await simulate.sleep(50)
  38. // api 测试
  39. comp.instance.setContent(
  40. `<!-- 测试 base 标签 -->
  41. <base href="https://xxx.com">
  42. <!-- 测试 script 标签 -->
  43. <script>
  44. console.log('11')
  45. </script>
  46. <!-- 测试 embed 标签 -->
  47. <embed src="xxx.mp4" />
  48. <embed autostart src="xxx.m4a" />
  49. <!-- 测试 source 标签 -->
  50. <video src="xxx.mp4" style="height:auto" loop ></video>
  51. <!-- 测试 table 标签 -->
  52. <table align="center"></table>
  53. <table align="left" border="1">
  54. <td>
  55. <a>xxx</a>
  56. </td>
  57. </table>
  58. <table width="100%" border="1">
  59. <tr>
  60. <th style="vertical-align: bottom;" width="20%">标题1</th>
  61. <th width="80%">标题2</th>
  62. </tr>
  63. <tr>
  64. <td colspan="2" style="vertical-align:middle;text-align:right"><a>内容1</a></td>
  65. </tr>
  66. </table>
  67. <table>
  68. <tr>
  69. <td><img src="xxx.jpg" style="display:block">图片<td>
  70. <td><img src="xxx.jpg">图片<td>
  71. </tr>
  72. </table>
  73. <!-- 测试 font 标签、不同属性写法、实体 -->
  74. <font color='red' face = "宋体" size=8 >&#26356;&#x591a;</font >
  75. <font size=0>1 < 2</font>
  76. <font>&#aaa;&aaa;&</FONT>
  77. <!-- 测试 rpx 单位处理 -->
  78. <span id="anchor" style="font-size:30rpx">11</span>
  79. <!-- 测试 pre 标签处理(保留空白符) -->
  80. <div style="white-space:pre">
  81. <pre>var i = 0</pre>
  82. </div>
  83. <!-- 测试不同情况中的图片处理 -->
  84. <a data-test="test">
  85. <img src="//xxx.jpg">
  86. </a>
  87. <div style="display:inline-block !important;display:block">
  88. <img style="width:100%;" src="xxx.jpg">
  89. </p>
  90. </div>
  91. <div style="display:flex">
  92. <div style="flex:1">
  93. <img src="//xxx.jpg" style="display:inline">
  94. </div>
  95. </div>
  96. <img style="width:auto" src="data:image/png;base64,xxxx">
  97. <img src="xxx" style="width:20px" height="10">
  98. <img src="yyy.webp" style="width:1000px" ignore>
  99. <svg />
  100. <svg viewbox="0 0 1 1"><text>123</text><svg></svg></svg>
  101. <div class="ql-align-center" style="background-image:url(&quot;/xxx.jpg?a=2&amp;b=3&quot;)"></div>
  102. <![CDATA[<]]>
  103. <!-- 测试 flex 布局、未闭合标签、data- 属性处理 -->
  104. <div style="display:flex;width:1000px">
  105. <div style="flex:1" dir="rtl">123</div>
  106. </div>
  107. </br><div data-test="xxx" style="display:flex;display:-webkit-flex;"><div>
  108. <img data-src="/xxx.jpg" style="width:100%;height:100px"> `, true) // 补充测试
  109. expect(comp.instance.getText().includes('更多')).toBe(true) // 检查上方的实体是否被解码
  110. await comp.instance.getRect()
  111. await comp.instance.navigateTo('anchor') // 基于页面跳转
  112. comp.instance.in(page.instance) // 错误设置
  113. comp.instance.in(page.instance, '#scroll', 'top')
  114. await comp.instance.navigateTo('anchor') // 基于 scroll-view 滚动
  115. page.setData({
  116. useAnchor: false
  117. })
  118. await simulate.sleep(50)
  119. comp.instance.setContent('<span id="test">123</span>', true)
  120. try {
  121. await comp.instance.navigateTo('anchor') // 禁用锚点的情况下跳转
  122. } catch (e) { }
  123. page.setData({
  124. containerStyle: 'white-space:pre-wrap'
  125. })
  126. await simulate.sleep(50)
  127. comp.instance.setContent(' 空格\n换行')
  128. expect(comp.instance.getText().includes('\n')).toBe(true) // 检查换行是否被保留
  129. // 无图测试
  130. page.setData({
  131. lazyLoad: false
  132. })
  133. await simulate.sleep(50)
  134. comp.instance.setContent('<div>Hello world!</div>')
  135. simulate.sleep(50)
  136. // 长内容测试
  137. let content = '<div>1</div>'.repeat(50) + '<div>'
  138. for (let i = 0; i < 50; i++) {
  139. content += '<div>' + i + '</div>'
  140. }
  141. content += '<a>xxx</a>'
  142. for (let i = 0; i < 3; i++) {
  143. content += '<div>' + i + '</div>'
  144. }
  145. comp.instance.setContent(content)
  146. simulate.sleep(50)
  147. expect(comp.data.nodes.length).toBe(2) // 应该切分为 2 块
  148. expect(comp.data.nodes[1].children.length).toBe(5) // 应该切分为 5 块
  149. await simulate.sleep(50) // 等待异步 api 执行完毕
  150. // 移除节点
  151. comp.triggerLifeTime('detached')
  152. })
  153. // 事件测试
  154. test('event', async () => {
  155. // 模拟 api
  156. wx.createVideoContext = function () {
  157. // 模拟视频 context
  158. return {
  159. pause: function () { },
  160. playbackRate: function () { }
  161. }
  162. }
  163. // 测试失败回调
  164. wx.navigateTo = function (obj) {
  165. setTimeout(() => {
  166. if (typeof obj.fail === 'function') {
  167. obj.fail()
  168. }
  169. }, 0)
  170. }
  171. wx.switchTab = function (obj) {
  172. setTimeout(() => {
  173. if (typeof obj.fail === 'function') {
  174. obj.fail()
  175. }
  176. }, 0)
  177. }
  178. const comp = simulate.render(mpHtml)
  179. comp.setData({
  180. selectable: 'force',
  181. loadingImg: 'xxx'
  182. })
  183. await simulate.sleep(50)
  184. comp.instance.setContent(
  185. `<img src="xxx">
  186. <img src="yyy" width="100" height="50" ignore>
  187. <a href="#aaa"><img src="xxx"></a>
  188. <a href="https://github.com/jin-yufeng/mp-html">链接2</a>
  189. <a href="pages/test/test">链接3</a>
  190. <video src="xxx"></video>
  191. <video>
  192. <source src="/xxx">
  193. <source src="//yyy">
  194. </video>
  195. <base href="https://xxx.com">`)
  196. await simulate.sleep(100)
  197. const node = comp.querySelector('#_root')
  198. node.triggerLifeTime('attached')
  199. comp.instance._add({
  200. detail: node.instance
  201. })
  202. // 模拟图片加载完毕
  203. for (let i = 0; i <= 1; i++) {
  204. node.instance.imgLoad({
  205. target: {
  206. dataset: {
  207. i: i.toString()
  208. }
  209. },
  210. detail: {
  211. width: 100,
  212. height: 100
  213. }
  214. })
  215. // 模拟图片被点击
  216. node.instance.imgTap({
  217. target: {
  218. dataset: {
  219. i: i.toString()
  220. }
  221. }
  222. })
  223. }
  224. comp.setData({
  225. loadingImg: ''
  226. })
  227. await simulate.sleep(350)
  228. node.instance.imgLoad({
  229. target: {
  230. dataset: {
  231. i: '1'
  232. }
  233. }
  234. })
  235. // 模拟图片链接被点击
  236. node.instance.imgTap({
  237. target: {
  238. dataset: {
  239. i: '2_0'
  240. }
  241. }
  242. })
  243. node.instance.noop()
  244. // 模拟图片出错
  245. const imgError = () => node.instance.mediaError({
  246. target: {
  247. dataset: {
  248. i: '0'
  249. }
  250. },
  251. detail: {
  252. errMsg: 'test'
  253. }
  254. })
  255. imgError()
  256. comp.setData({
  257. errorImg: 'xxx'
  258. }, imgError)
  259. // 模拟链接被点击
  260. for (let i = 2; i <= 4; i++) {
  261. node.instance.linkTap({
  262. currentTarget: {
  263. dataset: {
  264. i: i.toString()
  265. }
  266. }
  267. })
  268. }
  269. // 模拟视频播放
  270. for (let i = 0; i < 3; i++) {
  271. node.instance.play({
  272. target: {
  273. id: 'v' + (i % 2)
  274. }
  275. })
  276. }
  277. // 视频倍速播放
  278. comp.instance.setPlaybackRate(1.5)
  279. node.instance.play({
  280. target: {
  281. id: 'v2'
  282. }
  283. })
  284. // 暂停视频播放
  285. comp.instance.pauseMedia()
  286. // 模拟视频出错
  287. node.instance.mediaError({
  288. target: {
  289. dataset: {
  290. i: '6'
  291. }
  292. },
  293. detail: {
  294. errMsg: 'test'
  295. }
  296. })
  297. // 禁用一些功能
  298. comp.setData({
  299. copyLink: false,
  300. pauseVideo: false,
  301. previewImg: false
  302. }, () => {
  303. // 禁用自动拷贝后点击外部链接
  304. node.instance.linkTap({
  305. currentTarget: {
  306. dataset: {
  307. i: '3'
  308. }
  309. }
  310. })
  311. // 禁用自动暂停后播放视频
  312. node.instance.play({
  313. target: {
  314. id: 'v0'
  315. }
  316. })
  317. // 禁用预览后点击图片
  318. node.instance.imgTap({
  319. target: {
  320. dataset: {
  321. i: '0'
  322. }
  323. }
  324. })
  325. })
  326. await simulate.sleep(100)
  327. })