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`.
This commit is contained in:
Hanks 2017-10-30 19:29:16 -05:00 committed by Evan You
parent 8a784d8d23
commit 232dd85f85
8 changed files with 223 additions and 3 deletions

View File

@ -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": {

View File

@ -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'))
})
})

View File

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

View File

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

View File

@ -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>

View File

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

View File

@ -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>

View File

@ -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) {