Skip to content

Commit

Permalink
✨ Display errors when no slots are provided
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Mar 6, 2018
1 parent 934cb20 commit 66d4e3a
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 6 deletions.
16 changes: 15 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { assert } from './util'

export default {
name: 'Promised',
props: {
Expand All @@ -13,10 +15,22 @@ export default {

render (h) {
if (this.error instanceof Error || (this.error && this.error.length)) {
assert(
this.$scopedSlots && this.$scopedSlots.error,
'Provide exactly one scoped slot named "error" for the rejected promise'
)
return this.$scopedSlots.error(this.error)
} else if (this.resolved) {
assert(
this.$scopedSlots && this.$scopedSlots.default,
'Provide exactly one default scoped slot for the resolved promise'
)
return this.$scopedSlots.default(this.data)
} else if (this.$slots.default && this.$slots.default.length > 0) {
} else {
assert(
this.$slots.default && this.$slots.default.length === 1,
'Provide exactly one default slot with no `slot-scope` for the pending promise'
)
return this.$slots.default[0]
}
},
Expand Down
5 changes: 5 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function assert (condition, message) {
if (!condition) {
throw new Error(`[vue-promised] ${message}`)
}
}
65 changes: 60 additions & 5 deletions test/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { mount } from '@vue/test-utils'
import fakePromise from 'faked-promise'
import Helper from './utils/Helper'
import NoError from './utils/NoError'
import NoResolve from './utils/NoResolve'
import NoPending from './utils/NoPending'

const tick = () => new Promise(resolve => setTimeout(resolve, 0))

Expand Down Expand Up @@ -53,11 +56,13 @@ describe('Promised', () => {
describe('multiple promise', () => {
let fakedPromises
beforeEach(async () => {
fakedPromises = Array.from({ length: 3 }, () => fakePromise()).map(([promise, resolve, reject]) => ({
promise,
resolve,
reject,
}))
fakedPromises = Array.from({ length: 3 }, () => fakePromise()).map(
([promise, resolve, reject]) => ({
promise,
resolve,
reject,
})
)

const promises = fakedPromises.map(({ promise }) => promise)

Expand Down Expand Up @@ -107,4 +112,54 @@ describe('Promised', () => {
expect(wrapper.text()).toBe('loading')
})
})

describe('errors', () => {
let promise, resolve, reject, errorSpy
beforeEach(() => {
// silence the log
errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {
// useful for debugging
// console.log('CONSOLE ERROR')
})
;[promise, resolve, reject] = fakePromise()
})

afterEach(() => {
errorSpy.mockRestore()
})

test('throws if no error scoped slot provided on error', async () => {
wrapper = mount(NoError, {
propsData: {
promise,
},
})
expect(errorSpy).not.toHaveBeenCalled()
reject(new Error('nope'))
await tick()
expect(errorSpy).toHaveBeenCalledTimes(2)
})

test('throws if no default scoped slot provided on resolve', async () => {
wrapper = mount(NoResolve, {
propsData: {
promise,
},
})
expect(errorSpy).not.toHaveBeenCalled()
resolve()
await tick()
expect(errorSpy).toHaveBeenCalledTimes(2)
})

test('throws if no default slot provided while pending', async () => {
expect(errorSpy).not.toHaveBeenCalled()
wrapper = mount(NoPending, {
propsData: {
promise,
},
})
expect(errorSpy).toHaveBeenCalledTimes(2)
})
})
})
21 changes: 21 additions & 0 deletions test/utils/NoError.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<Promised v-bind="$props">
<h1>loading</h1>
<!-- The default scoped slots will be used as the result -->
<h1 slot-scope="data">{{ data | text }}</h1>
</Promised>
</template>

<script>
import Promised from '../../src'
export default {
props: ['promise', 'promises'],
filters: {
text(data) {
return Array.isArray(data) ? data.join(',') : data
},
},
components: { Promised },
}
</script>
25 changes: 25 additions & 0 deletions test/utils/NoPending.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<template>
<Promised v-bind="$props">
<!-- The default scoped slots will be used as the result -->
<h1 slot-scope="data">{{ data | text }}</h1>
<!-- The 'error' named scoped slots will be used if there is an error -->
<h1 slot="error" slot-scope="error">{{ error | errorText }}</h1>
</Promised>
</template>

<script>
import Promised from '../../src'
export default {
props: ['promise', 'promises'],
filters: {
text(data) {
return Array.isArray(data) ? data.join(',') : data
},
errorText(data) {
return Array.isArray(data) ? data.map(e => e.message).join(',') : data.message
},
},
components: { Promised },
}
</script>
24 changes: 24 additions & 0 deletions test/utils/NoResolve.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<Promised v-bind="$props">
<h1>loading</h1>
<!-- The 'error' named scoped slots will be used if there is an error -->
<h1 slot="error" slot-scope="error">{{ error | errorText }}</h1>
</Promised>
</template>

<script>
import Promised from '../../src'
export default {
props: ['promise', 'promises'],
filters: {
text(data) {
return Array.isArray(data) ? data.join(',') : data
},
errorText(data) {
return Array.isArray(data) ? data.map(e => e.message).join(',') : data.message
},
},
components: { Promised },
}
</script>

0 comments on commit 66d4e3a

Please sign in to comment.