UNPKG

vue-async-everything

Version:

Vue Component Plugin for asynchronous data and computed properties.

768 lines (584 loc) 21 kB
import { expect } from 'chai' import Vue from 'vue' import { delay } from 'bluebird' import { reduce, cloneDeep } from 'lodash' import AsyncProperties from '../src/' Vue.use(AsyncProperties, { debounce: 5, transform: null }) const defaultString = 'defaultString' const oneString = 'oneString' const twoString = 'twoString' const transformCompound = 'transformed' const errorMessage = 'error message' function mixinAndExtend(bothOptions = {}, asyncDataOptions = {}, asyncComputedOptions = {}) { return Vue.extend({ render(h) { return h('div', [h('span', this.member), h('span', this.delayMember), h('span', this.upperMember)]) }, data() { return { member: oneString, triggerMember: false, triggerCollectionMember: false, transformCompound, errorMessage, otherErrorContainer: null, } }, asyncData: { delayMember: { get() { return delay(5).return(this.member) }, ...bothOptions, ...asyncDataOptions }, delayCollection: { get() { return delay(5).return([1, 2, 3]) }, more() { return delay(5).return([4, 5, 6]) }, ...bothOptions, ...asyncDataOptions } }, asyncComputed: { upperMember: { watch: 'member', get() { return delay(5).return(this.member.toUpperCase()) }, ...bothOptions, ...asyncComputedOptions }, twiceCollection: { watch: 'triggerCollectionMember', get() { return delay(5).return([2, 4, 6]) }, more() { return delay(5).return([8, 10, 12]) }, ...bothOptions, ...asyncComputedOptions } } }) } const NoneComponent = Vue.extend({ render(h) { return h('div', [h('span', this.member)]) }, data() { return { member: oneString, } } }) const BaseComponent = mixinAndExtend() const ValueNotPromiseComponent = mixinAndExtend(undefined, { get() { return this.member } }, { get() { return this.member.toUpperCase() } } ) const LazyEagerComponent = mixinAndExtend(undefined, { lazy: true }, { eager: true }) const WithDefaultComponent = mixinAndExtend(undefined, { default: defaultString }, { default: defaultString }) const ErrorHandlerComponent = mixinAndExtend({ error(e) { this.otherErrorContainer = this.errorMessage } }, { get() { return delay(5).then(() => {throw new Error(this.member)}) } }, { get() { return delay(5).then(() => {throw new Error(this.member)}) } }) const NoDebounceComponent = mixinAndExtend(undefined, undefined, { debounce: null }) const WatchCloselyComponent = mixinAndExtend(undefined, undefined, { watchClosely: 'triggerMember' }) const TransformComponent = mixinAndExtend({ transform(result) { return `${this.transformCompound} ${result}` } }, undefined, undefined) const LoadMoreComponent = mixinAndExtend(undefined, undefined, undefined) const LoadMoreAppendComponent = mixinAndExtend(undefined, { more: { get() { return delay(5).return([4, 5, 6]) }, concat: (collection, newCollection) => { return collection.concat([reduce(newCollection, (sum, n) => sum + n, 0)]) } } }, { more: { get() { return delay(5).return([8, 10, 12]) }, concat: (collection, newCollection) => { return collection.concat([reduce(newCollection, (sum, n) => sum + n, 0)]) } } }) const conflictingMixin = { asyncData: { notFoo() { return delay(5).return(this.member) } }, asyncComputed: { notBar: { get() { return delay(5).return(this.member.toUpperCase()) }, watch: 'member' } } } const OverlappingMixinsComponent = Vue.extend({ mixins: [ conflictingMixin, ], asyncData: { foo() { return delay(5).return(this.member) } }, asyncComputed: { bar: { get() { return delay(5).return(this.member.toUpperCase()) }, watch: 'member' } }, render(h) { return h('div', [h('span', this.member)]) }, data() { return { member: oneString, } } }) let c describe("component without either asyncData or asyncComputed", function() { it("doesn't error", function() { c = new NoneComponent() expect(() => { c.$mount() }).to.not.throw() }) }) describe("component that uses mixins with asyncData or asyncComputed", function() { it("has merged options, from current and mixin", function() { c = new OverlappingMixinsComponent() expect(c).property('member').to.eql(oneString) expect(c).property('foo$default').to.be.null expect(c).property('foo').to.eql(c.foo$default) expect(c).property('foo$error').to.be.null expect(c).property('foo$loading').to.be.false expect(c).property('foo$refresh').to.be.a('function') expect(c).property('bar$default').to.be.null expect(c).property('bar').to.eql(c.bar$default) expect(c).property('bar$error').to.be.null expect(c).property('bar$loading').to.be.false expect(c).property('bar$pending').to.be.false expect(c).property('bar$cancel').to.be.a('function') expect(c).property('bar$now').to.be.a('function') expect(c).property('notFoo$default').to.be.null expect(c).property('notFoo').to.eql(c.notFoo$default) expect(c).property('notFoo$error').to.be.null expect(c).property('notFoo$loading').to.be.false expect(c).property('notFoo$refresh').to.be.a('function') expect(c).property('notBar$default').to.be.null expect(c).property('notBar').to.eql(c.notBar$default) expect(c).property('notBar$error').to.be.null expect(c).property('notBar$loading').to.be.false expect(c).property('notBar$pending').to.be.false expect(c).property('notBar$cancel').to.be.a('function') expect(c).property('notBar$now').to.be.a('function') }) }) describe("asyncData", function() { it("has correct base functionality", async function() { c = new BaseComponent() // after creation expect(c).property('member').to.eql(oneString) expect(c).property('delayMember$default').to.be.null expect(c).property('delayMember').to.eql(c.delayMember$default) expect(c).property('delayMember$loading').to.be.false expect(c).property('delayMember$error').to.be.null expect(c).property('delayMember$refresh').to.be.a('function') // after mount c.$mount() expect(c).property('delayMember$loading').to.be.true // after load await delay(10) expect(c).property('delayMember').to.eql(oneString) expect(c).property('delayMember$loading').to.be.false expect(c).property('delayMember$error').to.be.null // after refresh c.delayMember$refresh() expect(c).property('delayMember$loading').to.be.true // after load await delay(10) expect(c).property('delayMember').to.eql(oneString) expect(c).property('delayMember$loading').to.be.false expect(c).property('delayMember$error').to.be.null // after change c.member = twoString expect(c).property('delayMember$loading').to.be.false c.delayMember$refresh() expect(c).property('delayMember$loading').to.be.true // after load await delay(10) expect(c).property('delayMember').to.eql(twoString) expect(c).property('delayMember$loading').to.be.false expect(c).property('delayMember$error').to.be.null }) it("respects lazy option", async function() { c = new LazyEagerComponent() c.$mount() expect(c).property('delayMember$loading').to.be.false await delay(10) expect(c).property('delayMember$loading').to.be.false expect(c).property('delayMember').to.eql(c.delayMember$default) c.delayMember$refresh() expect(c).property('delayMember$loading').to.be.true await delay(10) expect(c).property('delayMember').to.eql(oneString) expect(c).property('delayMember$loading').to.be.false expect(c).property('delayMember$error').to.be.null }) // it("default", function() { // }) it("can perform load mores", async function() { c = new LoadMoreComponent() // after creation expect(c).property('delayCollection$default').to.be.null expect(c).property('delayCollection').to.eql(c.delayCollection$default) expect(c).property('delayCollection$loading').to.be.false expect(c).property('delayCollection$error').to.be.null expect(c).property('delayCollection$refresh').to.be.a('function') expect(c).property('delayCollection$more').to.be.a('function') // after mount c.$mount() expect(c).property('delayCollection$loading').to.be.true // can get a limited number first // after load await delay(10) expect(c).property('delayCollection').to.eql([1, 2, 3]) expect(c).property('delayCollection$loading').to.be.false expect(c).property('delayCollection$error').to.be.null // can get more and append them c.delayCollection$more() expect(c).property('delayCollection$loading').to.be.true await delay(10) expect(c).property('delayCollection').to.eql([1, 2, 3, 4, 5, 6]) expect(c).property('delayCollection$loading').to.be.false expect(c).property('delayCollection$error').to.be.null c.delayCollection$more() expect(c).property('delayCollection$loading').to.be.true await delay(10) expect(c).property('delayCollection').to.eql([1, 2, 3, 4, 5, 6, 4, 5, 6]) expect(c).property('delayCollection$loading').to.be.false expect(c).property('delayCollection$error').to.be.null // can refresh to a complete reset c.delayCollection$refresh() expect(c).property('delayCollection$loading').to.be.true await delay(10) expect(c).property('delayCollection').to.eql([1, 2, 3]) expect(c).property('delayCollection$loading').to.be.false expect(c).property('delayCollection$error').to.be.null // can get last response from $more let response = await c.delayCollection$more() expect(response).to.be.an('array').and.eql([4, 5, 6]) expect(c).property('delayCollection').to.eql([1, 2, 3, 4, 5, 6]) expect(c).property('delayCollection$loading').to.be.false expect(c).property('delayCollection$error').to.be.null c = new LoadMoreAppendComponent() c.$mount() // can do custom appends expect(c).property('delayCollection$loading').to.be.true // can get a limited number first // after load await delay(10) expect(c).property('delayCollection').to.eql([1, 2, 3]) expect(c).property('delayCollection$loading').to.be.false expect(c).property('delayCollection$error').to.be.null // can get more and append them c.delayCollection$more() expect(c).property('delayCollection$loading').to.be.true await delay(10) expect(c).property('delayCollection').to.eql([1, 2, 3, 15]) expect(c).property('delayCollection$loading').to.be.false expect(c).property('delayCollection$error').to.be.null c.delayCollection$more() expect(c).property('delayCollection$loading').to.be.true await delay(10) expect(c).property('delayCollection').to.eql([1, 2, 3, 15, 15]) expect(c).property('delayCollection$loading').to.be.false expect(c).property('delayCollection$error').to.be.null // triggers event on reset }) it("transforms properly", async function() { c = new TransformComponent() c.$mount() expect(c).property('delayMember$loading').to.be.true // after load await delay(10) expect(c).property('delayMember').to.eql(`${transformCompound} ${oneString}`) }) it("has a functioning error handler", async function() { c = new ErrorHandlerComponent() c.$mount() // after load await delay(16) expect(c).property('delayMember$error').to.have.property('message').that.eql(oneString) expect(c).property('otherErrorContainer').to.eql(errorMessage) }) it("doesn't load with a value instead of a promise", function() { c = new ValueNotPromiseComponent() // after mount c.$mount() expect(c).property('delayMember').to.eql(oneString) expect(c).property('delayMember$loading').to.be.false expect(c).property('delayMember$error').to.be.null // after refresh c.member = twoString c.delayMember$refresh() expect(c).property('delayMember').to.eql(twoString) expect(c).property('delayMember$loading').to.be.false expect(c).property('delayMember$error').to.be.null }) }) describe("asyncComputed", function() { it("has correct base functionality", async function() { c = new BaseComponent() // after creation expect(c).property('member').to.eql(oneString) expect(c).property('upperMember$default').to.be.null expect(c).property('upperMember').to.eql(c.upperMember$default) expect(c).property('upperMember$loading').to.be.false expect(c).property('upperMember$error').to.be.null expect(c).property('upperMember$cancel').to.be.a('function') expect(c).property('upperMember$now').to.be.a('function') expect(c).property('upperMember$pending').to.be.false // after mount c.$mount() expect(c).property('upperMember$loading').to.be.false expect(c).property('upperMember$pending').to.be.false expect(c).property('upperMember$error').to.be.null // still after mount await delay(10) expect(c).property('upperMember$loading').to.be.false expect(c).property('upperMember$pending').to.be.false // after change c.member = twoString await Vue.nextTick() expect(c).property('upperMember$pending').to.be.true expect(c).property('upperMember$loading').to.be.false // after debounce await delay(7) expect(c).property('upperMember$pending').to.be.false expect(c).property('upperMember$loading').to.be.true // after load await delay(5) expect(c).property('upperMember$pending').to.be.false expect(c).property('upperMember$loading').to.be.false expect(c).property('upperMember').to.eql(twoString.toUpperCase()) expect(c).property('upperMember$error').to.be.null // after cancel c.member = oneString await Vue.nextTick() expect(c).property('upperMember$pending').to.be.true expect(c).property('upperMember$loading').to.be.false await delay(1) c.upperMember$cancel() expect(c).property('upperMember$pending').to.be.false expect(c).property('upperMember$loading').to.be.false expect(c).property('upperMember$error').to.be.null // after now c.member = twoString await Vue.nextTick() expect(c).property('upperMember$pending').to.be.true expect(c).property('upperMember$loading').to.be.false await delay(1) c.upperMember$now() expect(c).property('upperMember$pending').to.be.false expect(c).property('upperMember$loading').to.be.true // after load await delay(5) expect(c).property('upperMember$pending').to.be.false expect(c).property('upperMember$loading').to.be.false expect(c).property('upperMember').to.eql(twoString.toUpperCase()) expect(c).property('upperMember$error').to.be.null }) it("respects the eager option", async function() { c = new LazyEagerComponent() c.$mount() expect(c).property('upperMember$pending').to.be.false expect(c).property('upperMember$loading').to.be.true // after load await delay(6) expect(c).property('upperMember$pending').to.be.false expect(c).property('upperMember$loading').to.be.false expect(c).property('upperMember').to.eql(oneString.toUpperCase()) expect(c).property('upperMember$error').to.be.null }) it("can perform load mores", async function() { c = new LoadMoreComponent() // after creation expect(c).property('twiceCollection$default').to.be.null expect(c).property('twiceCollection').to.eql(c.twiceCollection$default) expect(c).property('twiceCollection$loading').to.be.false expect(c).property('twiceCollection$error').to.be.null expect(c).property('twiceCollection$more').to.be.a('function') // after mount c.$mount() expect(c).property('twiceCollection$loading').to.be.false expect(c).property('twiceCollection$pending').to.be.false // can get a limited number first // after load c.triggerCollectionMember = true await Vue.nextTick() expect(c).property('twiceCollection$loading').to.be.false expect(c).property('twiceCollection$pending').to.be.true await delay(12) expect(c).property('twiceCollection').to.eql([2, 4, 6]) expect(c).property('twiceCollection$loading').to.be.false expect(c).property('twiceCollection$error').to.be.null // can get more and append them c.twiceCollection$more() await Vue.nextTick() expect(c).property('twiceCollection$loading').to.be.true await delay(12) expect(c).property('twiceCollection').to.eql([2, 4, 6, 8, 10, 12]) expect(c).property('twiceCollection$loading').to.be.false expect(c).property('twiceCollection$error').to.be.null c.twiceCollection$more() await Vue.nextTick() expect(c).property('twiceCollection$loading').to.be.true await delay(12) expect(c).property('twiceCollection').to.eql([2, 4, 6, 8, 10, 12, 8, 10, 12]) expect(c).property('twiceCollection$loading').to.be.false expect(c).property('twiceCollection$error').to.be.null // can refresh to a complete reset c.triggerCollectionMember = false await Vue.nextTick() await delay(12) expect(c).property('twiceCollection').to.eql([2, 4, 6]) expect(c).property('twiceCollection$loading').to.be.false expect(c).property('twiceCollection$error').to.be.null // can get last response from $more let response = await c.twiceCollection$more() // await Vue.nextTick() expect(response).to.be.an('array').and.eql([8, 10, 12]) expect(c).property('twiceCollection').to.eql([2, 4, 6, 8, 10, 12]) expect(c).property('twiceCollection$loading').to.be.false expect(c).property('twiceCollection$error').to.be.null c = new LoadMoreAppendComponent() c.$mount() // can get a limited number first // after load c.triggerCollectionMember = true await Vue.nextTick() await delay(12) expect(c).property('twiceCollection').to.eql([2, 4, 6]) expect(c).property('twiceCollection$loading').to.be.false expect(c).property('twiceCollection$error').to.be.null // can get more and append them c.twiceCollection$more() await Vue.nextTick() expect(c).property('twiceCollection$loading').to.be.true await delay(12) expect(c).property('twiceCollection').to.eql([2, 4, 6, 30]) expect(c).property('twiceCollection$loading').to.be.false expect(c).property('twiceCollection$error').to.be.null c.twiceCollection$more() await Vue.nextTick() expect(c).property('twiceCollection$loading').to.be.true await delay(12) expect(c).property('twiceCollection').to.eql([2, 4, 6, 30, 30]) expect(c).property('twiceCollection$loading').to.be.false expect(c).property('twiceCollection$error').to.be.null c.triggerCollectionMember = false await Vue.nextTick() await delay(12) expect(c).property('twiceCollection').to.eql([2, 4, 6]) expect(c).property('twiceCollection$loading').to.be.false expect(c).property('twiceCollection$error').to.be.null // triggers event on reset }) // it("default", function() { // }) it("transforms properly", async function() { c = new TransformComponent() c.$mount() // after change c.member = twoString await Vue.nextTick() // after debounce and load await delay(12) expect(c).property('upperMember').to.eql(`${transformCompound} ${twoString.toUpperCase()}`) }) it("has a functioning error handler", async function() { c = new ErrorHandlerComponent() c.$mount() // after change c.member = twoString await Vue.nextTick() // after debounce and load await delay(12) expect(c).property('delayMember$error').to.have.property('message').that.eql(twoString) expect(c).property('otherErrorContainer').to.eql(errorMessage) }) it("doesn't load with a value instead of a promise", async function() { c = new ValueNotPromiseComponent() c.$mount() // after change c.member = twoString await Vue.nextTick() expect(c).property('member').to.be.eql(twoString) expect(c).property('upperMember$pending').to.be.true expect(c).property('upperMember$loading').to.be.false // after debounce await delay(6) expect(c).property('upperMember').to.eql(twoString.toUpperCase()) expect(c).property('upperMember$loading').to.be.false expect(c).property('upperMember$error').to.be.null }) it("doesn't debounce when debounce is null", async function() { c = new NoDebounceComponent() expect(c).to.not.have.property('upperMember$pending') expect(c).to.not.have.property('upperMember$cancel') expect(c).to.not.have.property('upperMember$now') c.$mount() // after change c.member = twoString await Vue.nextTick() expect(c).property('upperMember$loading').to.be.true // after load await delay(6) expect(c).property('upperMember$loading').to.be.false expect(c).property('upperMember').to.eql(twoString.toUpperCase()) expect(c).property('upperMember$error').to.be.null }) it("invokes immediately when watchClosely changes", async function() { c = new WatchCloselyComponent() c.$mount() // after change c.triggerMember = true await Vue.nextTick() expect(c).property('upperMember$loading').to.be.true expect(c).property('upperMember$pending').to.be.false // after load await delay(6) expect(c).property('upperMember$loading').to.be.false expect(c).property('upperMember$pending').to.be.false expect(c).property('upperMember').to.eql(oneString.toUpperCase()) expect(c).property('upperMember$error').to.be.null }) })