diff --git a/src/index.js b/src/index.js index 3a0f670..34988c6 100644 --- a/src/index.js +++ b/src/index.js @@ -5,12 +5,18 @@ export default { props: { promise: Promise, promises: Array, + pendingDelay: { + type: [Number, String], + default: 200, + }, }, data: () => ({ resolved: false, data: null, error: null, + + isDelayElapsed: false, }), render (h) { @@ -27,7 +33,7 @@ export default { 'Provide exactly one default/then scoped slot for the resolved promise' ) return slot.call(this, this.data) - } else { + } else if (this.isDelayElapsed) { assert( (this.$slots.default && this.$slots.default.length === 1) || (this.$slots.pending && this.$slots.pending.length === 1), @@ -35,6 +41,9 @@ export default { ) return this.$slots.default ? this.$slots.default[0] : this.$slots.pending[0] } + + // do not render anything + return h() }, watch: { @@ -43,6 +52,7 @@ export default { if (!promise) return this.resolved = false this.error = null + this.setupDelay() promise .then(data => { if (this.promise === promise) { @@ -63,6 +73,7 @@ export default { this.resolved = false this.error = [] this.data = [] + this.setupDelay() promises.forEach(p => { p .then(data => { @@ -79,4 +90,16 @@ export default { immediate: true, }, }, + + methods: { + setupDelay () { + if (this.pendingDelay > 0) { + this.isDelayElapsed = false + if (this.timerId) clearTimeout(this.timerId) + this.timerId = setTimeout(() => this.isDelayElapsed = true, this.pendingDelay) + } else { + this.isDelayElapsed = true + } + }, + }, } diff --git a/test/index.spec.js b/test/index.spec.js index bcd6529..a620fc9 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -6,7 +6,10 @@ import NoError from './utils/NoError' import NoResolve from './utils/NoResolve' import NoPending from './utils/NoPending' -const tick = () => new Promise(resolve => setTimeout(resolve, 0)) +// keep a real setTimeout +const timeout = setTimeout +const tick = () => new Promise(resolve => timeout(resolve, 0)) +jest.useFakeTimers() describe('Promised', () => { let wrapper @@ -17,6 +20,7 @@ describe('Promised', () => { wrapper = mount(Helper, { propsData: { promise, + pendingDelay: 0, }, }) }) @@ -70,6 +74,7 @@ describe('Promised', () => { wrapper = mount(Helper, { propsData: { promises, + pendingDelay: 0, }, }) }) @@ -133,6 +138,7 @@ describe('Promised', () => { wrapper = mount(NoError, { propsData: { promise, + pendingDelay: 0, }, }) expect(errorSpy).not.toHaveBeenCalled() @@ -146,6 +152,7 @@ describe('Promised', () => { wrapper = mount(NoResolve, { propsData: { promise, + pendingDelay: 0, }, }) expect(errorSpy).not.toHaveBeenCalled() @@ -160,6 +167,7 @@ describe('Promised', () => { wrapper = mount(NoPending, { propsData: { promise, + pendingDelay: 0, }, }) expect(errorSpy).toHaveBeenCalledTimes(2) @@ -172,7 +180,10 @@ describe('Promised', () => { beforeEach(() => { [promise, resolve, reject] = fakePromise() wrapper = mount(NamedSlots, { - propsData: { promise }, + propsData: { + promise, + pendingDelay: 0, + }, }) }) @@ -192,4 +203,76 @@ describe('Promised', () => { expect(wrapper.text()).toBe('nope') }) }) + + describe('pendingDelay', () => { + describe('single promise', () => { + let promise + beforeEach(() => { + clearTimeout.mockClear() + ;[promise] = fakePromise() + wrapper = mount(Helper, { + propsData: { + promise, + pendingDelay: 300, + }, + }) + }) + + test('displays nothing before the delay', async () => { + expect(wrapper.text()).toBe('') + jest.runAllTimers() + await tick() + expect(wrapper.text()).toBe('loading') + }) + + test('custom pendingDelay', async () => { + expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 300) + ;[promise] = fakePromise() + wrapper.setProps({ + promise, + pendingDelay: 100, + }) + expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 100) + }) + + test('cancels previous timeouts', () => { + expect(clearTimeout).not.toHaveBeenCalled() + ;[promise] = fakePromise() + wrapper.setProps({ + promise, + pendingDelay: 100, + }) + expect(clearTimeout).toHaveBeenCalled() + }) + }) + + describe('multiple promises', () => { + let fakedPromises + beforeEach(async () => { + fakedPromises = Array.from({ length: 3 }, () => fakePromise()).map( + ([promise, resolve, reject]) => ({ + promise, + resolve, + reject, + }) + ) + + const promises = fakedPromises.map(({ promise }) => promise) + + wrapper = mount(Helper, { + propsData: { + promises, + pendingDelay: 300, + }, + }) + }) + + test('displays nothing before the delay', async () => { + expect(wrapper.text()).toBe('') + jest.runAllTimers() + await tick() + expect(wrapper.text()).toBe('loading') + }) + }) + }) }) diff --git a/test/utils/common.js b/test/utils/common.js index 2753a46..5ddb940 100644 --- a/test/utils/common.js +++ b/test/utils/common.js @@ -1,7 +1,7 @@ import Promised from '../../src' export default { - props: ['promise', 'promises'], + props: ['promise', 'promises', 'pendingDelay'], filters: { text (data) { return Array.isArray(data) ? data.join(',') : data