test.js 8.9 KB

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