Răsfoiți Sursa

test(weex): support testing the virtual dom generated form *.vue files (#6944)

Compile the *.vue file into js code, then run it in Weex context, and
compare the generate virtual dom.
It’s a black-box testing for `weex-template-compiler`,
`weex-styler`,`weex-vue-framework` and `weex-js-runtime`.
Hanks 8 ani în urmă
părinte
comite
232dd85f85

+ 2 - 1
package.json

@@ -125,7 +125,8 @@
     "typescript": "^2.5.2",
     "uglify-js": "^3.0.15",
     "webpack": "^2.6.1",
-    "weex-js-runtime": "^0.23.0"
+    "weex-js-runtime": "^0.23.0",
+    "weex-styler": "^0.3.0"
   },
   "config": {
     "commitizen": {

+ 72 - 0
test/weex/cases/cases.spec.js

@@ -0,0 +1,72 @@
+import fs from 'fs'
+import path from 'path'
+import {
+  compileVue,
+  createInstance,
+  getRoot,
+  getEvents,
+  fireEvent
+} from '../helpers'
+
+function readFile (filename) {
+  return fs.readFileSync(path.resolve(__dirname, filename), 'utf8')
+}
+
+function readObject (filename) {
+  return (new Function(`return ${readFile(filename)}`))()
+}
+
+// Create one-off render test case
+function createRenderTestCase (name) {
+  const source = readFile(`${name}.vue`)
+  const target = readObject(`${name}.vdom.js`)
+  return done => {
+    compileVue(source).then(code => {
+      const id = String(Date.now() * Math.random())
+      const instance = createInstance(id, code)
+      setTimeout(() => {
+        expect(getRoot(instance)).toEqual(target)
+        done()
+      }, 50)
+    }).catch(err => {
+      expect(err).toBe(null)
+      done()
+    })
+  }
+}
+
+// Create event test case, will trigger the first bind event
+function createEventTestCase (name) {
+  const source = readFile(`${name}.vue`)
+  const before = readObject(`${name}.before.vdom.js`)
+  const after = readObject(`${name}.after.vdom.js`)
+  return done => {
+    compileVue(source).then(code => {
+      const id = String(Date.now() * Math.random())
+      const instance = createInstance(id, code)
+      setTimeout(() => {
+        expect(getRoot(instance)).toEqual(before)
+        const event = getEvents(instance)[0]
+        fireEvent(instance, event.ref, event.type, {})
+        setTimeout(() => {
+          expect(getRoot(instance)).toEqual(after)
+          done()
+        }, 50)
+      }, 50)
+    }).catch(err => {
+      expect(err).toBe(null)
+      done()
+    })
+  }
+}
+
+describe('Usage', () => {
+  describe('render', () => {
+    it('sample', createRenderTestCase('render/sample'))
+  })
+
+  describe('event', () => {
+    it('click', createEventTestCase('event/click'))
+  })
+})
+

+ 10 - 0
test/weex/cases/event/click.after.vdom.js

@@ -0,0 +1,10 @@
+({
+  type: 'div',
+  event: ['click'],
+  children: [{
+    type: 'text',
+    attr: {
+      value: '43'
+    }
+  }]
+})

+ 10 - 0
test/weex/cases/event/click.before.vdom.js

@@ -0,0 +1,10 @@
+({
+  type: 'div',
+  event: ['click'],
+  children: [{
+    type: 'text',
+    attr: {
+      value: '42'
+    }
+  }]
+})

+ 20 - 0
test/weex/cases/event/click.vue

@@ -0,0 +1,20 @@
+<template>
+  <div @click="inc">
+    <text>{{count}}</text>
+  </div>
+</template>
+
+<script>
+  module.exports = {
+    data () {
+      return {
+        count: 42
+      }
+    },
+    methods: {
+      inc () {
+        this.count++
+      }
+    }
+  }
+</script>

+ 17 - 0
test/weex/cases/render/sample.vdom.js

@@ -0,0 +1,17 @@
+({
+  type: 'div',
+  style: {
+    justifyContent: 'center'
+  },
+  children: [{
+    type: 'text',
+    attr: {
+      value: 'Yo'
+    },
+    style: {
+      color: '#41B883',
+      fontSize: '233px',
+      textAlign: 'center'
+    }
+  }]
+})

+ 23 - 0
test/weex/cases/render/sample.vue

@@ -0,0 +1,23 @@
+<template>
+  <div style="justify-content:center">
+    <text class="freestyle">{{string}}</text>
+  </div>
+</template>
+
+<style scoped>
+  .freestyle {
+    color: #41B883;
+    font-size: 233px;
+    text-align: center;
+  }
+</style>
+
+<script>
+  module.exports = {
+    data () {
+      return {
+        string: 'Yo'
+      }
+    }
+  }
+</script>

+ 69 - 2
test/weex/helpers/index.js

@@ -1,6 +1,11 @@
 import * as Vue from '../../../packages/weex-vue-framework'
 import { compile } from '../../../packages/weex-template-compiler'
 import WeexRuntime from 'weex-js-runtime'
+import styler from 'weex-styler'
+
+const styleRE = /<\s*style\s*\w*>([^(<\/)]*)<\/\s*style\s*>/g
+const scriptRE = /<\s*script.*>([^]*)<\/\s*script\s*>/
+const templateRE = /<\s*template\s*>([^]*)<\/\s*template\s*>/
 
 console.debug = () => {}
 
@@ -10,6 +15,10 @@ export function strToRegExp (str) {
   return new RegExp(str.replace(matchOperatorsRe, '\\$&'))
 }
 
+function parseStatic (fns) {
+  return '[' + fns.map(fn => `function () { ${fn} }`).join(',') + ']'
+}
+
 export function compileAndStringify (template) {
   const { render, staticRenderFns } = compile(template)
   return {
@@ -18,8 +27,48 @@ export function compileAndStringify (template) {
   }
 }
 
-function parseStatic (fns) {
-  return '[' + fns.map(fn => `function () { ${fn} }`).join(',') + ']'
+/**
+ * Compile *.vue file into js code
+ * @param {string} source raw text of *.vue file
+ * @param {string} componentName whether compile to a component
+ */
+export function compileVue (source, componentName) {
+  return new Promise((resolve, reject) => {
+    if (!templateRE.test(source)) {
+      return reject('No Template!')
+    }
+    const scriptMatch = scriptRE.exec(source)
+    const script = scriptMatch ? scriptMatch[1] : ''
+    const { render, staticRenderFns } = compile(templateRE.exec(source)[1])
+
+    const generateCode = styles => (`
+      var test_case = Object.assign({
+        style: ${JSON.stringify(styles)},
+        render: function () { ${render} },
+        staticRenderFns: ${parseStatic(staticRenderFns)},
+      }, (function(){
+        var module = { exports: {} };
+        ${script};
+        return module.exports;
+      })());
+    ` + (componentName
+        ? `Vue.component('${componentName}', test_case);\n`
+        : `test_case.el = 'body';new Vue(test_case);`)
+    )
+
+    let cssText = ''
+    let styleMatch = null
+    while ((styleMatch = styleRE.exec(source))) {
+      cssText += `\n${styleMatch[1]}\n`
+    }
+    styler.parse(cssText, (error, result) => {
+      if (error) {
+        return reject(error)
+      }
+      resolve(generateCode(result.jsonStyle))
+    })
+    resolve(generateCode({}))
+  })
 }
 
 function isObject (object) {
@@ -47,6 +96,24 @@ export function getRoot (instance) {
   return omitUseless(instance.document.body.toJSON())
 }
 
+// Get all binding events in the instance
+export function getEvents (instance) {
+  const events = []
+  const recordEvent = node => {
+    if (!node) { return }
+    if (Array.isArray(node.event)) {
+      node.event.forEach(type => {
+        events.push({ ref: node.ref, type })
+      })
+    }
+    if (Array.isArray(node.children)) {
+      node.children.forEach(recordEvent)
+    }
+  }
+  recordEvent(instance.document.body.toJSON())
+  return events
+}
+
 export function fireEvent (instance, ref, type, event = {}) {
   const el = instance.document.getRef(ref)
   if (el) {