framework.spec.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. import * as framework from '../../../packages/weex-vue-framework'
  2. import { DEFAULT_ENV, Runtime, Instance } from 'weex-vdom-tester'
  3. import { config } from 'weex-js-runtime'
  4. import {
  5. syncPromise,
  6. checkRefresh
  7. } from '../helpers/index'
  8. let sendTasksHandler = () => {}
  9. const { Document, Element, Comment } = config
  10. function sendTasks () {
  11. sendTasksHandler.apply(null, arguments)
  12. }
  13. describe('framework APIs', () => {
  14. let runtime
  15. beforeEach(() => {
  16. Document.handler = sendTasks
  17. framework.init({ Document, Element, Comment, sendTasks })
  18. runtime = new Runtime(framework)
  19. sendTasksHandler = function () {
  20. runtime.target.callNative(...arguments)
  21. }
  22. })
  23. afterEach(() => {
  24. delete Document.handler
  25. framework.reset()
  26. })
  27. it('createInstance', () => {
  28. const instance = new Instance(runtime)
  29. framework.createInstance(instance.id, `
  30. new Vue({
  31. render: function (createElement) {
  32. return createElement('div', {}, [
  33. createElement('text', { attrs: { value: 'Hello' }}, [])
  34. ])
  35. },
  36. el: "body"
  37. })
  38. `)
  39. expect(instance.getRealRoot()).toEqual({
  40. type: 'div',
  41. children: [{ type: 'text', attr: { value: 'Hello' }}]
  42. })
  43. })
  44. it('createInstance with config', () => {
  45. const instance = new Instance(runtime)
  46. framework.createInstance(instance.id, `
  47. new Vue({
  48. render: function (createElement) {
  49. return createElement('div', {}, [
  50. createElement('text', { attrs: { value: JSON.stringify(this.$getConfig()) }}, [])
  51. ])
  52. },
  53. el: "body"
  54. })
  55. `, { bundleUrl: 'http://example.com/', a: 1, b: 2 })
  56. expect(instance.getRealRoot()).toEqual({
  57. type: 'div',
  58. children: [{ type: 'text', attr: { value: '{"bundleUrl":"http://example.com/","a":1,"b":2}' }}]
  59. })
  60. })
  61. it('createInstance with external data', () => {
  62. const instance = new Instance(runtime)
  63. framework.createInstance(instance.id, `
  64. new Vue({
  65. data: {
  66. a: 1,
  67. b: 2
  68. },
  69. render: function (createElement) {
  70. return createElement('div', {}, [
  71. createElement('text', { attrs: { value: this.a + '-' + this.b }}, [])
  72. ])
  73. },
  74. el: "body"
  75. })
  76. `, undefined, { a: 111 })
  77. expect(instance.getRealRoot()).toEqual({
  78. type: 'div',
  79. children: [{ type: 'text', attr: { value: '111-2' }}]
  80. })
  81. })
  82. it('destroyInstance', (done) => {
  83. const instance = new Instance(runtime)
  84. framework.createInstance(instance.id, `
  85. new Vue({
  86. data: {
  87. x: 'Hello'
  88. },
  89. render: function (createElement) {
  90. return createElement('div', {}, [
  91. createElement('text', { attrs: { value: this.x }}, [])
  92. ])
  93. },
  94. el: "body"
  95. })
  96. `)
  97. expect(instance.getRealRoot()).toEqual({
  98. type: 'div',
  99. children: [{ type: 'text', attr: { value: 'Hello' }}]
  100. })
  101. syncPromise([
  102. checkRefresh(instance, { x: 'World' }, result => {
  103. expect(result).toEqual({
  104. type: 'div',
  105. children: [{ type: 'text', attr: { value: 'World' }}]
  106. })
  107. framework.destroyInstance(instance.id)
  108. }),
  109. checkRefresh(instance, { x: 'Weex' }, result => {
  110. expect(result).toEqual({
  111. type: 'div',
  112. children: [{ type: 'text', attr: { value: 'World' }}]
  113. })
  114. done()
  115. })
  116. ])
  117. })
  118. it('refreshInstance', (done) => {
  119. const instance = new Instance(runtime)
  120. framework.createInstance(instance.id, `
  121. new Vue({
  122. data: {
  123. x: 'Hello'
  124. },
  125. render: function (createElement) {
  126. return createElement('div', {}, [
  127. createElement('text', { attrs: { value: this.x }}, [])
  128. ])
  129. },
  130. el: "body"
  131. })
  132. `)
  133. expect(instance.getRealRoot()).toEqual({
  134. type: 'div',
  135. children: [{ type: 'text', attr: { value: 'Hello' }}]
  136. })
  137. framework.refreshInstance(instance.id, { x: 'World' })
  138. setTimeout(() => {
  139. expect(instance.getRealRoot()).toEqual({
  140. type: 'div',
  141. children: [{ type: 'text', attr: { value: 'World' }}]
  142. })
  143. framework.destroyInstance(instance.id)
  144. const result = framework.refreshInstance(instance.id, { x: 'Weex' })
  145. expect(result instanceof Error).toBe(true)
  146. expect(result).toMatch(/refreshInstance/)
  147. expect(result).toMatch(/not found/)
  148. setTimeout(() => {
  149. expect(instance.getRealRoot()).toEqual({
  150. type: 'div',
  151. children: [{ type: 'text', attr: { value: 'World' }}]
  152. })
  153. done()
  154. })
  155. })
  156. })
  157. it('getRoot', () => {
  158. const instance = new Instance(runtime)
  159. framework.createInstance(instance.id, `
  160. new Vue({
  161. data: {
  162. x: 'Hello'
  163. },
  164. render: function (createElement) {
  165. return createElement('div', {}, [
  166. createElement('text', { attrs: { value: this.x }}, [])
  167. ])
  168. },
  169. el: "body"
  170. })
  171. `)
  172. let root = framework.getRoot(instance.id)
  173. expect(root.ref).toEqual('_root')
  174. expect(root.type).toEqual('div')
  175. expect(root.children.length).toEqual(1)
  176. expect(root.children[0].type).toEqual('text')
  177. expect(root.children[0].attr).toEqual({ value: 'Hello' })
  178. framework.destroyInstance(instance.id)
  179. root = framework.getRoot(instance.id)
  180. expect(root instanceof Error).toBe(true)
  181. expect(root).toMatch(/getRoot/)
  182. expect(root).toMatch(/not found/)
  183. })
  184. it('receiveTasks: fireEvent', (done) => {
  185. const instance = new Instance(runtime)
  186. framework.createInstance(instance.id, `
  187. new Vue({
  188. data: {
  189. x: 'Hello'
  190. },
  191. methods: {
  192. update: function (e) {
  193. this.x = 'World'
  194. }
  195. },
  196. render: function (createElement) {
  197. return createElement('div', {}, [
  198. createElement('text', { attrs: { value: this.x }, on: { click: this.update }}, [])
  199. ])
  200. },
  201. el: "body"
  202. })
  203. `)
  204. expect(instance.getRealRoot()).toEqual({
  205. type: 'div',
  206. children: [{
  207. type: 'text',
  208. attr: { value: 'Hello' },
  209. event: ['click']
  210. }]
  211. })
  212. const textRef = framework.getRoot(instance.id).children[0].ref
  213. framework.receiveTasks(instance.id, [
  214. { method: 'fireEvent', args: [textRef, 'click'] }
  215. ])
  216. setTimeout(() => {
  217. expect(instance.getRealRoot()).toEqual({
  218. type: 'div',
  219. children: [{
  220. type: 'text',
  221. attr: { value: 'World' },
  222. event: ['click']
  223. }]
  224. })
  225. framework.destroyInstance(instance.id)
  226. const result = framework.receiveTasks(instance.id, [
  227. { method: 'fireEvent', args: [textRef, 'click'] }
  228. ])
  229. expect(result instanceof Error).toBe(true)
  230. expect(result).toMatch(/receiveTasks/)
  231. expect(result).toMatch(/not found/)
  232. done()
  233. })
  234. })
  235. it('receiveTasks: callback', (done) => {
  236. framework.registerModules({
  237. foo: ['a', 'b', 'c']
  238. })
  239. const instance = new Instance(runtime)
  240. framework.createInstance(instance.id, `
  241. const moduleFoo = __weex_require_module__('foo')
  242. new Vue({
  243. data: {
  244. x: 'Hello'
  245. },
  246. methods: {
  247. update: function (data = {}) {
  248. this.x = data.value || 'World'
  249. }
  250. },
  251. mounted: function () {
  252. moduleFoo.a(data => {
  253. this.update(data)
  254. })
  255. },
  256. render: function (createElement) {
  257. return createElement('div', {}, [
  258. createElement('text', { attrs: { value: this.x }}, [])
  259. ])
  260. },
  261. el: "body"
  262. })
  263. `)
  264. expect(instance.getRealRoot()).toEqual({
  265. type: 'div',
  266. children: [{
  267. type: 'text',
  268. attr: { value: 'Hello' }
  269. }]
  270. })
  271. let callbackId
  272. instance.history.callNative.some(task => {
  273. if (task.module === 'foo' && task.method === 'a') {
  274. callbackId = task.args[0]
  275. return true
  276. }
  277. })
  278. framework.receiveTasks(instance.id, [
  279. { method: 'callback', args: [callbackId, undefined, true] }
  280. ])
  281. setTimeout(() => {
  282. expect(instance.getRealRoot()).toEqual({
  283. type: 'div',
  284. children: [{
  285. type: 'text',
  286. attr: { value: 'World' }
  287. }]
  288. })
  289. framework.receiveTasks(instance.id, [
  290. { method: 'callback', args: [callbackId, { value: 'Weex' }, true] }
  291. ])
  292. setTimeout(() => {
  293. expect(instance.getRealRoot()).toEqual({
  294. type: 'div',
  295. children: [{
  296. type: 'text',
  297. attr: { value: 'Weex' }
  298. }]
  299. })
  300. framework.receiveTasks(instance.id, [
  301. { method: 'callback', args: [callbackId] }
  302. ])
  303. setTimeout(() => {
  304. expect(instance.getRealRoot()).toEqual({
  305. type: 'div',
  306. children: [{
  307. type: 'text',
  308. attr: { value: 'World' }
  309. }]
  310. })
  311. framework.destroyInstance(instance.id)
  312. const result = framework.receiveTasks(instance.id, [
  313. { method: 'callback', args: [callbackId] }
  314. ])
  315. expect(result instanceof Error).toBe(true)
  316. expect(result).toMatch(/receiveTasks/)
  317. expect(result).toMatch(/not found/)
  318. done()
  319. })
  320. })
  321. })
  322. })
  323. it('registerModules', () => {
  324. framework.registerModules({
  325. foo: ['a', 'b', 'c'],
  326. bar: [
  327. { name: 'a', args: ['string'] },
  328. { name: 'b', args: ['number'] },
  329. { name: 'c', args: ['string', 'number'] }
  330. ]
  331. })
  332. const instance = new Instance(runtime)
  333. framework.createInstance(instance.id, `
  334. const moduleFoo = __weex_require_module__('foo')
  335. const moduleBar = __weex_require_module__('bar')
  336. const moduleBaz = __weex_require_module__('baz')
  337. new Vue({
  338. render: function (createElement) {
  339. const value = []
  340. if (typeof moduleFoo === 'object') {
  341. value.push('foo')
  342. value.push(Object.keys(moduleFoo))
  343. }
  344. if (typeof moduleBar === 'object') {
  345. value.push('bar')
  346. value.push(Object.keys(moduleBar))
  347. }
  348. if (typeof moduleBaz === 'object') {
  349. value.push('baz')
  350. value.push(Object.keys(moduleBaz))
  351. }
  352. return createElement('div', {}, [
  353. createElement('text', { attrs: { value: value.toString() }}, [])
  354. ])
  355. },
  356. mounted: function () {
  357. moduleFoo.a(1, '2', true)
  358. moduleBar.b(1)
  359. },
  360. el: "body"
  361. })
  362. `)
  363. expect(instance.getRealRoot()).toEqual({
  364. type: 'div',
  365. children: [{
  366. type: 'text',
  367. attr: { value: 'foo,a,b,c,bar,a,b,c,baz,' }
  368. }]
  369. })
  370. expect(instance.history.callNative
  371. .filter(task => task.module === 'foo')
  372. .map(task => `${task.method}(${task.args})`)
  373. ).toEqual(['a(1,2,true)'])
  374. expect(instance.history.callNative
  375. .filter(task => task.module === 'bar')
  376. .map(task => `${task.method}(${task.args})`)
  377. ).toEqual(['b(1)'])
  378. })
  379. it('registerComponents', () => {
  380. framework.registerComponents(['foo', { type: 'bar' }, 'text'])
  381. const instance = new Instance(runtime)
  382. framework.createInstance(instance.id, `
  383. new Vue({
  384. render: function (createElement) {
  385. return createElement('div', {}, [
  386. createElement('text', {}, []),
  387. createElement('foo', {}, []),
  388. createElement('bar', {}, []),
  389. createElement('baz', {}, [])
  390. ])
  391. },
  392. el: "body"
  393. })
  394. `)
  395. expect(instance.getRealRoot()).toEqual({
  396. type: 'div',
  397. children: [{ type: 'text' }, { type: 'foo' }, { type: 'bar' }, { type: 'baz' }]
  398. })
  399. })
  400. it('vm.$getConfig', () => {
  401. const instance = new Instance(runtime)
  402. instance.$create(`
  403. new Vue({
  404. render: function (createElement) {
  405. return createElement('div', {}, [
  406. createElement('text', { attrs: { value: JSON.stringify(this.$getConfig()) }}, [])
  407. ])
  408. },
  409. el: "body"
  410. })
  411. `)
  412. expect(JSON.parse(instance.getRealRoot().children[0].attr.value)).toEqual({ env: DEFAULT_ENV })
  413. const instance2 = new Instance(runtime)
  414. instance2.$create(`
  415. new Vue({
  416. render: function (createElement) {
  417. return createElement('div', {}, [
  418. createElement('text', { attrs: { value: JSON.stringify(this.$getConfig()) }}, [])
  419. ])
  420. },
  421. el: "body"
  422. })
  423. `, { a: 1, b: 2 })
  424. expect(JSON.parse(instance2.getRealRoot().children[0].attr.value)).toEqual({ a: 1, b: 2, env: DEFAULT_ENV })
  425. })
  426. it('Timer', (done) => {
  427. const instance = new Instance(runtime)
  428. instance.$create(`
  429. new Vue({
  430. data: {
  431. x: 0,
  432. y: 0
  433. },
  434. render: function (createElement) {
  435. return createElement('div', {}, [
  436. createElement('text', { attrs: { value: this.x + '-' + this.y }}, [])
  437. ])
  438. },
  439. mounted: function () {
  440. const now = Date.now()
  441. let timer, timer2
  442. setTimeout(() => {
  443. this.x = 1
  444. clearTimeout(timer)
  445. clearInterval(timer2)
  446. setInterval(() => {
  447. this.y++
  448. }, 600)
  449. }, 2000)
  450. timer = setTimeout(() => {
  451. this.x = 3
  452. }, 3000)
  453. setTimeout(() => {
  454. this.x = 3
  455. }, 4000)
  456. timer2 = setInterval(() => {
  457. this.y++
  458. }, 900)
  459. },
  460. el: "body"
  461. })
  462. `)
  463. expect(instance.getRealRoot()).toEqual({
  464. type: 'div',
  465. children: [{ type: 'text', attr: { value: '0-0' }}]
  466. })
  467. setTimeout(() => {
  468. expect(instance.getRealRoot().children[0].attr.value).toEqual('0-1')
  469. }, 950)
  470. setTimeout(() => {
  471. expect(instance.getRealRoot().children[0].attr.value).toEqual('0-2')
  472. }, 1850)
  473. setTimeout(() => {
  474. expect(instance.getRealRoot().children[0].attr.value).toEqual('1-2')
  475. }, 2050)
  476. setTimeout(() => {
  477. expect(instance.getRealRoot().children[0].attr.value).toEqual('1-3')
  478. }, 2650)
  479. setTimeout(() => {
  480. expect(instance.getRealRoot().children[0].attr.value).toEqual('1-4')
  481. }, 3250)
  482. setTimeout(() => {
  483. framework.destroyInstance(instance.id)
  484. }, 3500)
  485. setTimeout(() => {
  486. expect(instance.getRealRoot().children[0].attr.value).toEqual('1-4')
  487. done()
  488. }, 4100)
  489. })
  490. it('send function param', () => {
  491. framework.registerModules({
  492. foo: ['a']
  493. })
  494. const instance = new Instance(runtime)
  495. framework.createInstance(instance.id, `
  496. const moduleFoo = __weex_require_module__('foo')
  497. new Vue({
  498. mounted: function () {
  499. moduleFoo.a(a => a + 1)
  500. },
  501. render: function (createElement) {
  502. return createElement('div', {}, [
  503. createElement('text', { attrs: { value: 'Hello' }}, [])
  504. ])
  505. },
  506. el: "body"
  507. })
  508. `)
  509. let callbackId
  510. instance.history.callNative.some(task => {
  511. if (task.module === 'foo' && task.method === 'a') {
  512. callbackId = task.args[0]
  513. return true
  514. }
  515. })
  516. expect(typeof callbackId).toEqual('string')
  517. })
  518. it('send Element param', () => {
  519. framework.registerModules({
  520. foo: ['a']
  521. })
  522. const instance = new Instance(runtime)
  523. framework.createInstance(instance.id, `
  524. const moduleFoo = __weex_require_module__('foo')
  525. new Vue({
  526. mounted: function () {
  527. moduleFoo.a(this.$refs.x)
  528. },
  529. render: function (createElement) {
  530. return createElement('div', {}, [
  531. createElement('text', { attrs: { value: 'Hello' }, ref: 'x' }, [])
  532. ])
  533. },
  534. el: "body"
  535. })
  536. `)
  537. let callbackId
  538. instance.history.callNative.some(task => {
  539. if (task.module === 'foo' && task.method === 'a') {
  540. callbackId = task.args[0]
  541. return true
  542. }
  543. })
  544. expect(typeof callbackId).toEqual('string')
  545. })
  546. it('registering global assets', () => {
  547. const instance = new Instance(runtime)
  548. framework.createInstance(instance.id, `
  549. Vue.component('test', {
  550. render (h) {
  551. return h('div', 'Hello')
  552. }
  553. })
  554. new Vue({
  555. render (h) {
  556. return h('test')
  557. },
  558. el: 'body'
  559. })
  560. `)
  561. expect(instance.getRealRoot()).toEqual({
  562. type: 'div',
  563. children: [{ type: 'text', attr: { value: 'Hello' }}]
  564. })
  565. })
  566. it('adding prototype methods', () => {
  567. const instance = new Instance(runtime)
  568. framework.createInstance(instance.id, `
  569. Vue.prototype.$test = () => 'Hello'
  570. const Test = {
  571. render (h) {
  572. return h('div', this.$test())
  573. }
  574. }
  575. new Vue({
  576. render (h) {
  577. return h(Test)
  578. },
  579. el: 'body'
  580. })
  581. `)
  582. expect(instance.getRealRoot()).toEqual({
  583. type: 'div',
  584. children: [{ type: 'text', attr: { value: 'Hello' }}]
  585. })
  586. })
  587. it('using global mixins', () => {
  588. const instance = new Instance(runtime)
  589. framework.createInstance(instance.id, `
  590. Vue.mixin({
  591. created () {
  592. this.test = true
  593. }
  594. })
  595. const Test = {
  596. data: () => ({ test: false }),
  597. render (h) {
  598. return h('div', this.test ? 'Hello' : 'nope')
  599. }
  600. }
  601. new Vue({
  602. data: { test: false },
  603. render (h) {
  604. return this.test ? h(Test) : h('p')
  605. },
  606. el: 'body'
  607. })
  608. `)
  609. expect(instance.getRealRoot()).toEqual({
  610. type: 'div',
  611. children: [{ type: 'text', attr: { value: 'Hello' }}]
  612. })
  613. })
  614. })