UNPKG

vue-async-properties

Version:

Vue Component Plugin for asynchronous data and computed properties.

1,225 lines (965 loc) 36.1 kB
import { expect } from 'chai' import { createLocalVue } from '@vue/test-utils' import { delay } from 'bluebird' import { reduce, cloneDeep } from 'lodash' import AsyncProperties from '../src/' const Vue = createLocalVue() 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 = {}, asyncMethodsOptions = {}) { 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 } }, asyncMethods: { grabValue: { get() { return delay(5).return(true) }, ...bothOptions, ...asyncMethodsOptions }, echoValue: { get(value) { return delay(5).return(value) }, ...bothOptions, ...asyncMethodsOptions } } }) } 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 or asyncMethods", 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(7) 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(7) 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(7) 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(7) 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(7) 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(7) 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(7) 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(7) 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(7) 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(7) 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(7) 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(7) 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 // TODO 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(7) 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(7) 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(3) 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(7) 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 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 c.upperMember$now() expect(c).property('upperMember$pending').to.be.false expect(c).property('upperMember$loading').to.be.true // after load await delay(7) 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(7) 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(7) 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(7) 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() 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(15) 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(7) 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(7) 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 // TODO 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(15) 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(7) 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(7) 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(7) 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 }) }) describe("asyncMethods", function() { it("has all correct intial properties", function() { c = new BaseComponent() expect(c).property('grabValue').a('function') expect(c).property('grabValue$invocations').equal(0) expect(c).property('grabValue$loading').equal(false) expect(c).property('grabValue$error').equal(null) expect(c).property('echoValue').a('function') expect(c).property('echoValue$invocations').equal(0) expect(c).property('echoValue$loading').equal(false) expect(c).property('echoValue$error').equal(null) c.$mount() expect(c).property('grabValue$invocations').equal(0) expect(c).property('grabValue$loading').equal(false) expect(c).property('grabValue$error').equal(null) expect(c).property('echoValue$invocations').equal(0) expect(c).property('echoValue$loading').equal(false) expect(c).property('echoValue$error').equal(null) }) it("has the correct base functionality", async function() { c = new BaseComponent() let returnValue c.$mount() returnValue = c.grabValue() expect(returnValue).property('then').a('function') expect(c).property('grabValue$invocations').equal(1) expect(c).property('grabValue$loading').equal(true) expect(c).property('grabValue$error').equal(null) returnValue = await returnValue expect(c).property('grabValue$invocations').equal(0) expect(c).property('grabValue$loading').equal(false) expect(c).property('grabValue$error').equal(null) expect(returnValue).equal(true) const echoValueArg = 'echo' returnValue = c.echoValue(echoValueArg) expect(returnValue).property('then').a('function') expect(c).property('echoValue$invocations').equal(1) expect(c).property('echoValue$loading').equal(true) expect(c).property('echoValue$error').equal(null) returnValue = await returnValue expect(c).property('echoValue$invocations').equal(0) expect(c).property('echoValue$loading').equal(false) expect(c).property('echoValue$error').equal(null) expect(returnValue).equal(echoValueArg) }) }) import AsyncVuex from '../src/vuex.js' Vue.use(AsyncVuex) const firstName = 'first' const secondName = 'second' function createVuex(asyncGuard = null, asyncStateOptions = {}, asyncGettersOptions = {}) { return new AsyncVuex.Store({ state: { name: firstName, triggerMember: false, triggerCollectionMember: false, guardFlag: false, }, mutations: { setName(state, name) { state.name = name }, setTriggerMember(state, val) { state.triggerMember = val }, setTriggerCollectionMember(state, val) { state.triggerCollectionMember = val }, setGuardFlag(state, val) { state.guardFlag = val }, watchThisMutation(state, val) { state.name += val }, }, actions: { async watchThisAction({ commit }, val) { await delay(5) commit('watchThisMutation', val) } }, asyncGuard, asyncState: { delayName: { get(state, getters) { return delay(5).return(state.name) }, ...asyncStateOptions }, delayCollection: { get(state, getters) { return delay(5).return([1, 2, 3]) }, more(state, getters) { return delay(5).return([4, 5, 6]) }, ...asyncStateOptions } }, asyncGetters: { upperName: { watch: (state, getters) => state.name, get(state, getters) { return delay(5).return(state.name.toUpperCase()) }, ...asyncGettersOptions }, twiceCollection: { watch: (state, getters) => state.triggerCollectionMember, get() { return delay(5).return([2, 4, 6]) }, more() { return delay(5).return([8, 10, 12]) }, ...asyncGettersOptions } } }) } describe("vuex asyncState", function() { it("has the correct base functionality", async function() { const store = createVuex() expect(store.state.name).to.eql(firstName) expect(store.state.delayName).to.eql(null) expect(store.state.delayName$loading).to.eql(true) await delay(7) expect(store.state.delayName).to.eql(firstName) expect(store.state.delayName$loading).to.eql(false) // doesn't respond to changes store.commit('setName', secondName) expect(store.state.name).to.eql(secondName) expect(store.state.delayName).to.eql(firstName) expect(store.state.delayName$loading).to.eql(false) await delay(7) expect(store.state.delayName).to.eql(firstName) // but we can refresh it const dispatchPromise = store.dispatch('delayName$refresh') expect(store.state.delayName).to.eql(firstName) expect(store.state.delayName$loading).to.eql(true) await delay(7) await dispatchPromise expect(store.state.delayName).to.eql(secondName) expect(store.state.delayName$loading).to.eql(false) }) it("waits for a guard", async function() { const store = createVuex((state, getters) => state.guardFlag) expect(store.state.name).to.eql(firstName) expect(store.state.delayName).to.eql(null) expect(store.state.delayName$loading).to.eql(false) await delay(7) expect(store.state.delayName).to.eql(null) expect(store.state.delayName$loading).to.eql(false) store.commit('setGuardFlag', true) expect(store.state.guardFlag).to.eql(true) await Vue.nextTick() expect(store.state.delayName).to.eql(null) expect(store.state.delayName$loading).to.eql(true) await delay(7) expect(store.state.delayName).to.eql(firstName) expect(store.state.delayName$loading).to.eql(false) }) it("can load more", async function() { const store = createVuex() expect(store.state.delayCollection).to.eql(null) expect(store.state.delayCollection$loading).to.eql(true) await delay(7) expect(store.state.delayCollection).to.eql([1, 2, 3]) expect(store.state.delayCollection$loading).to.eql(false) const morePromise = store.dispatch('delayCollection$more') expect(store.state.delayCollection).to.eql([1, 2, 3]) expect(store.state.delayCollection$loading).to.eql(true) const lastResult = await morePromise await delay(1) expect(lastResult).to.eql([4, 5, 6]) expect(store.state.delayCollection).to.eql([1, 2, 3, 4, 5, 6]) expect(store.state.delayCollection$loading).to.eql(false) }) it("respects lazy", async function() { const store = createVuex(undefined, { lazy: true }) expect(store.state.name).to.eql(firstName) expect(store.state.delayName).to.eql(null) expect(store.state.delayName$loading).to.eql(false) await delay(7) expect(store.state.delayName).to.eql(null) expect(store.state.delayName$loading).to.eql(false) const dispatchPromise = store.dispatch('delayName$refresh') expect(store.state.delayName).to.eql(null) expect(store.state.delayName$loading).to.eql(true) await Vue.nextTick() await delay(7) await dispatchPromise expect(store.state.delayName).to.eql(firstName) expect(store.state.delayName$loading).to.eql(false) }) }) describe("vuex asyncGetters", function() { it("has the correct base functionality", async function() { const store = createVuex() expect(store.state.name).to.eql(firstName) expect(store.state.upperName).to.eql(null) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(false) expect(store.state.upperName).to.eql(null) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(false) store.commit('setName', secondName) expect(store.state.name).to.eql(secondName) await Vue.nextTick() // pending after change expect(store.state.upperName).to.eql(null) expect(store.state.upperName$pending).to.eql(true) expect(store.state.upperName$loading).to.eql(false) await delay(7) // loading after pending expect(store.state.upperName).to.eql(null) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(true) await delay(7) // done expect(store.state.upperName).to.eql(secondName.toUpperCase()) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(false) }) it("responds immediately to watchClosely", async function() { const store = createVuex(undefined, undefined, { watchClosely: (state) => state.triggerMember }) expect(store.state.name).to.eql(firstName) expect(store.state.upperName).to.eql(null) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(false) store.commit('setTriggerMember', true) expect(store.state.triggerMember).to.eql(true) await Vue.nextTick() expect(store.state.upperName).to.eql(null) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(true) await delay(7) expect(store.state.upperName).to.eql(firstName.toUpperCase()) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(false) }) it("responds to mutations and actions", async function() { const watchAppend = 'watchAppend' const mutationStore = createVuex(undefined, undefined, { watch: 'watchThisMutation' }) expect(mutationStore.state.name).to.eql(firstName) expect(mutationStore.state.upperName).to.eql(null) expect(mutationStore.state.upperName$pending).to.eql(false) expect(mutationStore.state.upperName$loading).to.eql(false) mutationStore.commit('setName', secondName) expect(mutationStore.state.name).to.eql(secondName) await Vue.nextTick() expect(mutationStore.state.upperName).to.eql(null) expect(mutationStore.state.upperName$pending).to.eql(false) expect(mutationStore.state.upperName$loading).to.eql(false) mutationStore.commit('watchThisMutation', watchAppend) expect(mutationStore.state.name).to.eql(secondName + watchAppend) await Vue.nextTick() expect(mutationStore.state.upperName).to.eql(null) expect(mutationStore.state.upperName$pending).to.eql(true) expect(mutationStore.state.upperName$loading).to.eql(false) await delay(7) expect(mutationStore.state.upperName).to.eql(null) expect(mutationStore.state.upperName$pending).to.eql(false) expect(mutationStore.state.upperName$loading).to.eql(true) await delay(7) expect(mutationStore.state.upperName).to.eql((secondName + watchAppend).toUpperCase()) expect(mutationStore.state.upperName$pending).to.eql(false) expect(mutationStore.state.upperName$loading).to.eql(false) const actionStore = createVuex(undefined, undefined, { watch: 'watchThisAction' }) expect(actionStore.state.name).to.eql(firstName) expect(actionStore.state.upperName).to.eql(null) expect(actionStore.state.upperName$pending).to.eql(false) expect(actionStore.state.upperName$loading).to.eql(false) actionStore.commit('setName', secondName) expect(actionStore.state.name).to.eql(secondName) await Vue.nextTick() expect(actionStore.state.upperName).to.eql(null) expect(actionStore.state.upperName$pending).to.eql(false) expect(actionStore.state.upperName$loading).to.eql(false) actionStore.dispatch('watchThisAction', watchAppend) expect(actionStore.state.name).to.eql(secondName) await Vue.nextTick() expect(actionStore.state.name).to.eql(secondName) expect(actionStore.state.upperName).to.eql(null) expect(actionStore.state.upperName$pending).to.eql(true) expect(actionStore.state.upperName$loading).to.eql(false) await delay(7) expect(actionStore.state.name).to.eql(secondName + watchAppend) expect(actionStore.state.upperName).to.eql(null) expect(actionStore.state.upperName$pending).to.eql(false) expect(actionStore.state.upperName$loading).to.eql(true) await delay(7) // since the givenFunction is called at the beginning of the action instead of after it, // this receives the state value at the beginning expect(actionStore.state.upperName).to.eql(secondName.toUpperCase()) expect(actionStore.state.upperName$pending).to.eql(false) expect(actionStore.state.upperName$loading).to.eql(false) }) it("waits for a guard", async function() { const store = createVuex((state, getters) => state.guardFlag) expect(store.state.name).to.eql(firstName) expect(store.state.upperName).to.eql(null) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(false) await delay(7) expect(store.state.upperName).to.eql(null) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(false) store.commit('setName', secondName) expect(store.state.name).to.eql(secondName) await Vue.nextTick() // the guard isn't satisfied yet expect(store.state.upperName).to.eql(null) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(false) store.commit('setGuardFlag', true) await Vue.nextTick() expect(store.state.upperName).to.eql(null) expect(store.state.upperName$pending).to.eql(true) expect(store.state.upperName$loading).to.eql(false) await delay(7) expect(store.state.upperName).to.eql(null) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(true) await delay(7) expect(store.state.upperName).to.eql(secondName.toUpperCase()) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(false) }) it("can load more", async function() { const store = createVuex() store.commit('setTriggerCollectionMember', true) expect(store.state.triggerCollectionMember).to.eql(true) await Vue.nextTick() expect(store.state.twiceCollection).to.eql(null) expect(store.state.twiceCollection$pending).to.eql(true) expect(store.state.twiceCollection$loading).to.eql(false) await delay(7) expect(store.state.twiceCollection).to.eql(null) expect(store.state.twiceCollection$pending).to.eql(false) expect(store.state.twiceCollection$loading).to.eql(true) await delay(7) expect(store.state.twiceCollection).to.eql([2, 4, 6]) expect(store.state.twiceCollection$pending).to.eql(false) expect(store.state.twiceCollection$loading).to.eql(false) const morePromise = store.dispatch('twiceCollection$more') expect(store.state.twiceCollection).to.eql([2, 4, 6]) expect(store.state.twiceCollection$loading).to.eql(true) await Vue.nextTick() await delay(7) const lastResult = await morePromise expect(lastResult).to.eql([8, 10, 12]) expect(store.state.twiceCollection).to.eql([2, 4, 6, 8, 10, 12]) expect(store.state.twiceCollection$loading).to.eql(false) }) it("respects eager", async function() { const store = createVuex(undefined, undefined, { eager: true }) expect(store.state.name).to.eql(firstName) expect(store.state.upperName).to.eql(null) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(true) await delay(7) expect(store.state.upperName).to.eql(firstName.toUpperCase()) expect(store.state.upperName$pending).to.eql(false) expect(store.state.upperName$loading).to.eql(false) }) })