| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| import chai from 'chai' |
| import sinon from 'sinon' |
| import sinonChai from 'sinon-chai' |
| |
| const expect = chai.expect |
| chai.use(sinonChai) |
| |
| import { createRuntime, createApp, getCode } from './prepare' |
| |
| describe('test input and output', function () { |
| let runtime |
| |
| before(() => { |
| runtime = createRuntime() |
| sinon.stub(console, 'info') |
| sinon.stub(console, 'warn') |
| sinon.stub(console, 'error') |
| }) |
| |
| after(() => { |
| console.info.restore() |
| console.warn.restore() |
| console.error.restore() |
| }) |
| |
| describe('one-time rendering cases', () => { |
| let app |
| |
| beforeEach(() => { |
| app = createApp(runtime) |
| callNativeHandler = (...args) => app._target.callNative(...args) |
| }) |
| |
| afterEach(() => { |
| app = null |
| callNativeHandler = function () {} |
| }) |
| |
| function checkOutput (app, name) { |
| const source = getCode('basic/' + name + '.source.js') |
| const output = getCode('basic/' + name + '.output.js') |
| const result = eval('(' + output + ')') |
| |
| app.$create(source) |
| expect(app.getRealRoot()).eql(result) |
| app.$destroy() |
| } |
| |
| it('global Weex object', () => checkOutput(app, 'global-weex-object')) |
| |
| it('single case', () => checkOutput(app, 'foo')) |
| it('foo2 case', () => checkOutput(app, 'foo2')) |
| it('foo3 case', () => checkOutput(app, 'foo3')) |
| it('foo4 case', () => checkOutput(app, 'foo4')) |
| it('foo5 case', () => checkOutput(app, 'foo5')) |
| it('foo6 case', () => checkOutput(app, 'foo6')) |
| it('foo7 case', () => checkOutput(app, 'foo7')) |
| it('foo8 case', () => checkOutput(app, 'foo8')) |
| it('foo9 case', () => checkOutput(app, 'foo9')) |
| |
| it('static1 case', () => checkOutput(app, 'static1')) |
| it('static2 case', () => checkOutput(app, 'static2')) |
| it('static3 case', () => checkOutput(app, 'static3')) |
| it('static4 case', () => checkOutput(app, 'static4')) |
| |
| it('computed in repeat case', () => checkOutput(app, 'computed-in-repeat')) |
| it('backward(register/render) case', () => checkOutput(app, 'backward1')) |
| it('backward(define/require) case', () => checkOutput(app, 'backward2')) |
| it('append case', () => checkOutput(app, 'append')) |
| it('append-root case', () => checkOutput(app, 'append-root')) |
| |
| it('if case', () => checkOutput(app, 'if')) |
| it('if-repeat case', () => checkOutput(app, 'if-repeat')) |
| it('if in root element case', () => checkOutput(app, 'if-root')) |
| it('repeat in root element case', () => checkOutput(app, 'repeat-root')) |
| it('repeat with index case case', () => checkOutput(app, 'repeat-index')) |
| it('repeat with array v case', () => checkOutput(app, 'repeat-array-v')) |
| it('repeat with array kv case', () => checkOutput(app, 'repeat-array-kv')) |
| it('repeat with array no-kv case', () => checkOutput(app, 'repeat-array-no-kv')) |
| it('repeat with array non-obj case', () => checkOutput(app, 'repeat-array-non-obj')) |
| it('repeat watch case', () => checkOutput(app, 'repeat-watch')) |
| |
| it('id case', () => checkOutput(app, 'id')) |
| it('dynamic id case', () => checkOutput(app, 'dynamic-id')) |
| |
| it('reset style case', () => checkOutput(app, 'reset-style')) |
| it('dynamic type case', () => checkOutput(app, 'dynamic-type')) |
| it('dynamic property case', () => checkOutput(app, 'dynamic-property')) |
| |
| it('bind subvm', () => checkOutput(app, 'subvm')) |
| it('components options', () => checkOutput(app, 'components')) |
| it('change data when created', () => checkOutput(app, 'created')) |
| it('change data when ready', () => checkOutput(app, 'ready')) |
| |
| it('require 3rd', () => checkOutput(app, 'require')) |
| it('a correct transformer version', () => checkOutput(app, 'transformer1')) |
| |
| it('promise case', () => checkOutput(app, 'promise')) |
| }) |
| |
| describe('invalid usage', function () { |
| describe('strict mode', () => { |
| const readSource = name => getCode('throws/' + name + '.source.js') |
| |
| let app |
| beforeEach(() => { app = createApp(runtime) }) |
| afterEach(() => { app = null }) |
| |
| it('global variable 1', () => { |
| const sourceCode = readSource('global-variable1') |
| expect(() => app.$create(sourceCode)).to.throw(ReferenceError) |
| }) |
| |
| it('global variable 2', () => { |
| const sourceCode = readSource('global-variable2') |
| expect(() => app.$create(sourceCode)).to.throw(ReferenceError) |
| }) |
| |
| it('global variable 3', () => { |
| const sourceCode = readSource('global-variable3') |
| expect(() => app.$create(sourceCode)).to.throw(ReferenceError) |
| }) |
| }) |
| }) |
| |
| describe('complex cases', function () { |
| let app |
| |
| beforeEach(() => { |
| app = createApp(runtime) |
| callNativeHandler = (...args) => app._target.callNative(...args) |
| }) |
| |
| afterEach(() => { |
| app = null |
| callNativeHandler = function () {} |
| }) |
| |
| const readSource = name => getCode('complex/' + name + '.source.js') |
| const readOutput = name => getCode('complex/' + name + '.output.js') |
| |
| it('computed case', () => { |
| const name = 'computed' |
| const sourceCode = readSource(name) |
| const outputCode = readOutput(name) |
| |
| app.$create(sourceCode) |
| const expected = eval('(' + outputCode + ')') |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.$refresh({ x: 10 }) |
| expected.children[0].attr.value = 12 |
| expected.children[1].attr.value = 12 |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.$refresh({ m: 10 }) |
| expected.children[0].attr.value = 20 |
| expected.children[1].attr.value = 20 |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.$destroy() |
| }) |
| |
| it('append-root-event case', () => { |
| const name = 'append-root-event' |
| const sourceCode = readSource(name) |
| const outputCode = readOutput(name) |
| |
| app.$create(sourceCode) |
| const expected = eval('(' + outputCode + ')') |
| const actual = app.getRealRoot() |
| expect(actual).eql(expected) |
| |
| app.$fireEvent(app.doc.body.children[0].ref, 'click', {}) |
| const actual2 = app.getRealRoot() |
| expect(actual2.children[0].attr.value).eql(2) |
| |
| app.$destroy() |
| }) |
| |
| it('use append tree and sub components', (done) => { |
| const name = 'component-append-tree' |
| const sourceCode = readSource(name) |
| const outputCode = readOutput(name) |
| |
| app.$create(sourceCode) |
| const expected = eval('(' + outputCode + ')') |
| const actual = app.getRealRoot() |
| expect(actual).eql(expected) |
| |
| setTimeout(() => { |
| const actual2 = app.getRealRoot() |
| expect(actual2.children[0].children[0].attr.value).eql('timeout') |
| expect(actual2.children[1].children[0].attr.value).eql('timeout') |
| app.$destroy() |
| done() |
| }, 200) |
| }) |
| |
| it('repeat with array track-by case', () => { |
| const name = 'repeat-track-by' |
| const sourceCode = readSource(name) |
| const outputCode = readOutput(name) |
| |
| app.$create(sourceCode) |
| app.$refresh({ |
| titlelist: [ |
| { text: 'Hello World2' }, |
| { text: 'Hello World1' } |
| ] |
| }) |
| const expected = eval('(' + outputCode + ')') |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.$destroy() |
| }) |
| |
| it('if-refresh case', () => { |
| const name = 'if-refresh' |
| const sourceCode = readSource(name) |
| const outputCode = readOutput(name) |
| |
| app.$create(sourceCode) |
| app.$refresh({ showTitle: false }) |
| const expected = eval('(' + outputCode + ')') |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.$destroy() |
| }) |
| |
| it('if-repeat-refresh case', () => { |
| const name = 'if-repeat-refresh' |
| const sourceCode = readSource(name) |
| const outputCode = readOutput(name) |
| |
| app.$create(sourceCode) |
| app.$refresh({ |
| titlelist: [ |
| { showTitle: false, title: 'Hello World1' }, |
| { showTitle: true, title: 'Hello World2' }, |
| { showTitle: true, title: 'Hello World3' } |
| ] |
| }) |
| const expected = eval('(' + outputCode + ')') |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.$destroy() |
| }) |
| |
| it('click case', () => { |
| const name = 'click' |
| const sourceCode = readSource(name) |
| const outputCode = readOutput(name) |
| |
| app.$create(sourceCode) |
| const expected = eval('(' + outputCode + ')') |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.$fireEvent(app.doc.body.children[0].ref, 'click', {}) |
| |
| app.$destroy() |
| }) |
| |
| it('inline click case', () => { |
| const name = 'inline-click' |
| const sourceCode = readSource(name) |
| const outputCode = readOutput(name) |
| |
| app.$create(sourceCode) |
| const expected = eval('(' + outputCode + ')') |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.$fireEvent(app.doc.body.children[0].ref, 'click', {}) |
| |
| expected.children[0].attr.value = 'Hello World2' |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.$destroy() |
| }) |
| |
| it('refresh twice', () => { |
| const name = 'refresh2' |
| const sourceCode = readSource(name) |
| const outputCode = readOutput(name) |
| |
| app.$create(sourceCode) |
| expect(app.getRealRoot()).eql({ type: 'container' }) |
| |
| app.$refresh({ ext: { showbar1: false }}) |
| app.$refresh({ ext: { showbar1: true }}) |
| const expected = eval('(' + outputCode + ')') |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.$destroy() |
| }) |
| |
| it('a less wrong transformer version', () => { |
| const name = 'transformer2' |
| const sourceCode = readSource(name) |
| |
| const result = app.$create(sourceCode) |
| expect(result).to.be.an.instanceof(Error) |
| app.$destroy() |
| }) |
| |
| it('a bigger wrong transformer version', () => { |
| const name = 'transformer3' |
| const sourceCode = readSource(name) |
| |
| const result = app.$create(sourceCode) |
| expect(result).to.be.an.instanceof(Error) |
| app.$destroy() |
| }) |
| |
| it('input binding', () => { |
| const name = 'input-binding' |
| const sourceCode = readSource(name) |
| const outputCode = readOutput(name) |
| |
| app.$create(sourceCode) |
| const expected = eval('(' + outputCode + ')') |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.doc.body.children[0].attr.value = 'abcdefg' |
| app.$fireEvent(app.doc.body.children[0].ref, 'change', {}, { attrs: { value: 'abcdefg' }}) |
| expected.children[0].attr.value = 'abcdefg' |
| expected.children.push({ type: 'text', attr: { value: '1 - abcdefg' }}) |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.doc.body.children[0].attr.value = '12345' |
| app.$fireEvent(app.doc.body.children[0].ref, 'change', {}, { attrs: { value: '12345' }}) |
| expected.children[0].attr.value = '12345' |
| expected.children.push({ type: 'text', attr: { value: '2 - 12345' }}) |
| expect(app.getRealRoot()).eql(expected) |
| |
| app.$destroy() |
| }) |
| }) |
| |
| describe('multi page cases', function () { |
| let appA |
| let appB |
| |
| beforeEach(() => { |
| appA = createApp(runtime) |
| appB = createApp(runtime) |
| |
| callNativeHandler = (id, ...args) => { |
| switch (true) { |
| case (appA.id === id) : { appA._target.callNative(id, ...args) } break |
| case (appB.id === id) : { appB._target.callNative(id, ...args) } break |
| } |
| } |
| }) |
| |
| afterEach(() => { |
| appA = null |
| appB = null |
| callNativeHandler = function () {} |
| }) |
| |
| const readSource = name => getCode('multi/' + name + '.source.js') |
| const readOutput = name => getCode('multi/' + name + '.output.js') |
| |
| it('clear-module case', () => { |
| const nameA = 'clear-moduleA' |
| const nameB = 'clear-moduleB' |
| const sourceCodeA = readSource(nameA) |
| const sourceCodeB = readSource(nameB) |
| const expectedA = eval('(' + readOutput(nameA) + ')') |
| const expectedB = eval('(' + readOutput(nameB) + ')') |
| |
| appA.$create(sourceCodeA) |
| appB.$create(sourceCodeB) |
| |
| expect(appB.getRealRoot()).eql(expectedB) |
| |
| appB.$destroy() |
| appA.$fireEvent(appA.doc.body.children[0].ref, 'click', {}) |
| expect(appA.getRealRoot()).eql(expectedA) |
| |
| appA.$destroy() |
| }) |
| |
| it('clear-dep-target case', () => { |
| const nameError = 'clear-dep-target-error' |
| const nameFine = 'clear-dep-target-fine' |
| const sourceCodeError = readSource(nameError) |
| const sourceCodeFine = readSource(nameFine) |
| const expectedFine = eval('(' + readOutput(nameFine) + ')') |
| |
| // should throw |
| expect(() => { appA.$create(sourceCodeError) }).to.throw(TypeError) |
| appA.$destroy() |
| |
| // not throw |
| appB.$create(sourceCodeFine) |
| expect(appB.getRealRoot()).eql(expectedFine) |
| |
| appB.$destroy() |
| }) |
| }) |
| |
| describe('timer & callNative signals', function () { |
| let app |
| |
| const callNativeSpy = sinon.spy() |
| function genCallNativeWrapper (count) { |
| return (name, tasks, cbId) => { |
| callNativeSpy(tasks) |
| const length = callNativeSpy.callCount |
| if (length > count) { |
| return -1 |
| } |
| return length |
| } |
| } |
| beforeEach(() => { |
| app = createApp(runtime) |
| callNativeHandler = (...args) => app._target.callNative(...args) |
| }) |
| |
| afterEach(() => { |
| app = null |
| callNativeSpy.reset() |
| callNativeHandler = function () {} |
| }) |
| |
| const readSource = name => getCode('signals/' + name + '.source.js') |
| const readOutput = name => getCode('signals/' + name + '.output.js') |
| |
| it('use HTML5 timer API', function (done) { |
| this.timeout(5000) |
| const name = 'timer' |
| const sourceCode = readSource(name) |
| const outputCode = readOutput(name) |
| |
| app.$create(sourceCode) |
| const expected = eval('(' + outputCode + ')') |
| expect(app.getRealRoot()).eql(expected) |
| |
| setTimeout(_ => { |
| expect(app.getRealRoot()).eql(expected) |
| setTimeout(_ => { |
| expected.children[0].attr.value = 'bar' |
| expect(app.getRealRoot()).eql(expected) |
| app.$destroy() |
| done() |
| }, 1000) |
| }, 1000) |
| }) |
| |
| it('use modal API', function (done) { |
| this.timeout(5000) |
| const name = 'modal' |
| const sourceCode = readSource(name) |
| const outputCode = readOutput(name) |
| |
| app.$create(sourceCode) |
| const expected = eval('(' + outputCode + ')') |
| expect(app.getRealRoot()).eql(expected) |
| |
| // the test driver will hold the API callback about 1 sec |
| setTimeout(_ => { |
| expected.children[0].attr.value = 'bar' |
| expect(app.getRealRoot()).eql(expected) |
| app.$destroy() |
| done() |
| }, 1500) |
| }) |
| |
| it('signals control', function () { |
| this.timeout(15000) |
| |
| const name = 'signals' |
| const sourceCode = readSource(name) |
| |
| function run (calls) { |
| callNativeSpy.reset() |
| callNativeHandler = genCallNativeWrapper(calls) |
| app.$create(sourceCode) |
| app.$destroy() |
| expect(callNativeSpy.callCount).eql(calls + 2) |
| } |
| |
| for (let i = 5; i < 60; i++) { |
| run(i) |
| } |
| }) |
| |
| it('long signals control', function () { |
| this.timeout(500000) |
| |
| const name = 'signals-long' |
| const sourceCode = readSource(name) |
| |
| function run (calls) { |
| callNativeSpy.reset() |
| callNativeHandler = genCallNativeWrapper(calls) |
| app.$create(sourceCode) |
| app.$destroy() |
| expect(callNativeSpy.callCount).eql(calls + 2) |
| } |
| |
| run(10) |
| run(30) |
| run(90) |
| }) |
| }) |
| }) |