| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- import Vue from 'vue'
- describe('Options errorCaptured', () => {
- let globalSpy
- beforeEach(() => {
- globalSpy = Vue.config.errorHandler = vi.fn()
- })
- afterEach(() => {
- Vue.config.errorHandler = undefined
- })
- it('should capture error from child component', () => {
- const spy = vi.fn()
- let child
- let err
- const Child = {
- created() {
- child = this
- err = new Error('child')
- throw err
- },
- render() {}
- }
- new Vue({
- errorCaptured: spy,
- render: h => h(Child)
- }).$mount()
- expect(spy).toHaveBeenCalledWith(err, child, 'created hook')
- // should propagate by default
- expect(globalSpy).toHaveBeenCalledWith(err, child, 'created hook')
- })
- it('should be able to render the error in itself', done => {
- let child
- const Child = {
- created() {
- child = this
- throw new Error('error from child')
- },
- render() {}
- }
- const vm = new Vue({
- data: {
- error: null
- },
- errorCaptured(e, vm, info) {
- expect(vm).toBe(child)
- this.error = e.toString() + ' in ' + info
- },
- render(h) {
- if (this.error) {
- return h('pre', this.error)
- }
- return h(Child)
- }
- }).$mount()
- waitForUpdate(() => {
- expect(vm.$el.textContent).toContain('error from child')
- expect(vm.$el.textContent).toContain('in created hook')
- }).then(done)
- })
- it('should not propagate to global handler when returning true', () => {
- const spy = vi.fn()
- let child
- let err
- const Child = {
- created() {
- child = this
- err = new Error('child')
- throw err
- },
- render() {}
- }
- new Vue({
- errorCaptured(err, vm, info) {
- spy(err, vm, info)
- return false
- },
- render: h => h(Child, {})
- }).$mount()
- expect(spy).toHaveBeenCalledWith(err, child, 'created hook')
- // should not propagate
- expect(globalSpy).not.toHaveBeenCalled()
- })
- it('should propagate to global handler if itself throws error', () => {
- let child
- let err
- const Child = {
- created() {
- child = this
- err = new Error('child')
- throw err
- },
- render() {}
- }
- let err2
- const vm = new Vue({
- errorCaptured() {
- err2 = new Error('foo')
- throw err2
- },
- render: h => h(Child, {})
- }).$mount()
- expect(globalSpy).toHaveBeenCalledWith(err, child, 'created hook')
- expect(globalSpy).toHaveBeenCalledWith(err2, vm, 'errorCaptured hook')
- })
- it('should work across multiple parents, mixins and extends', () => {
- const calls: any[] = []
- const Child = {
- created() {
- throw new Error('child')
- },
- render() {}
- }
- const ErrorBoundaryBase = {
- errorCaptured() {
- calls.push(1)
- }
- }
- const mixin = {
- errorCaptured() {
- calls.push(2)
- }
- }
- const ErrorBoundaryExtended = {
- extends: ErrorBoundaryBase,
- mixins: [mixin],
- errorCaptured() {
- calls.push(3)
- },
- render: h => h(Child)
- }
- Vue.config.errorHandler = () => {
- calls.push(5)
- }
- new Vue({
- errorCaptured() {
- calls.push(4)
- },
- render: h => h(ErrorBoundaryExtended)
- }).$mount()
- expect(calls).toEqual([1, 2, 3, 4, 5])
- })
- it('should work across multiple parents, mixins and extends with return false', () => {
- const calls: any[] = []
- const Child = {
- created() {
- throw new Error('child')
- },
- render() {}
- }
- const ErrorBoundaryBase = {
- errorCaptured() {
- calls.push(1)
- }
- }
- const mixin = {
- errorCaptured() {
- calls.push(2)
- }
- }
- const ErrorBoundaryExtended = {
- extends: ErrorBoundaryBase,
- mixins: [mixin],
- errorCaptured() {
- calls.push(3)
- return false
- },
- render: h => h(Child)
- }
- Vue.config.errorHandler = () => {
- calls.push(5)
- }
- new Vue({
- errorCaptured() {
- calls.push(4)
- },
- render: h => h(ErrorBoundaryExtended)
- }).$mount()
- expect(calls).toEqual([1, 2, 3])
- })
- // ref: https://github.com/vuejs/vuex/issues/1505
- it('should not add watchers to render deps if they are referred from errorCaptured callback', done => {
- const store = new Vue({
- data: {
- errors: []
- }
- })
- const Child = {
- computed: {
- test() {
- throw new Error('render error')
- }
- },
- render(h) {
- return h('div', {
- attrs: {
- 'data-test': this.test
- }
- })
- }
- }
- new Vue({
- errorCaptured(error) {
- store.errors.push(error)
- },
- render: h => h(Child)
- }).$mount()
- // Ensure not to trigger infinite loop
- waitForUpdate(() => {
- expect(store.errors.length).toBe(1)
- expect(store.errors[0]).toEqual(new Error('render error'))
- }).then(done)
- })
- it('should capture error from watcher', done => {
- const spy = vi.fn()
- let child
- let err
- const Child = {
- data() {
- return {
- foo: null
- }
- },
- watch: {
- foo() {
- err = new Error('userWatcherCallback error')
- throw err
- }
- },
- created() {
- child = this
- },
- render() {}
- }
- new Vue({
- errorCaptured: spy,
- render: h => h(Child)
- }).$mount()
- child.foo = 'bar'
- waitForUpdate(() => {
- expect(spy).toHaveBeenCalledWith(err, child, 'callback for watcher "foo"')
- expect(globalSpy).toHaveBeenCalledWith(
- err,
- child,
- 'callback for watcher "foo"'
- )
- }).then(done)
- })
- it('should capture promise error from watcher', done => {
- const spy = vi.fn()
- let child
- let err
- const Child = {
- data() {
- return {
- foo: null
- }
- },
- watch: {
- foo() {
- err = new Error('userWatcherCallback error')
- return Promise.reject(err)
- }
- },
- created() {
- child = this
- },
- render() {}
- }
- new Vue({
- errorCaptured: spy,
- render: h => h(Child)
- }).$mount()
- child.foo = 'bar'
- child.$nextTick(() => {
- waitForUpdate(() => {
- expect(spy).toHaveBeenCalledWith(
- err,
- child,
- 'callback for watcher "foo" (Promise/async)'
- )
- expect(globalSpy).toHaveBeenCalledWith(
- err,
- child,
- 'callback for watcher "foo" (Promise/async)'
- )
- }).then(done)
- })
- })
- it('should capture error from immediate watcher', done => {
- const spy = vi.fn()
- let child
- let err
- const Child = {
- data() {
- return {
- foo: 'foo'
- }
- },
- watch: {
- foo: {
- immediate: true,
- handler() {
- err = new Error('userImmediateWatcherCallback error')
- throw err
- }
- }
- },
- created() {
- child = this
- },
- render() {}
- }
- new Vue({
- errorCaptured: spy,
- render: h => h(Child)
- }).$mount()
- waitForUpdate(() => {
- expect(spy).toHaveBeenCalledWith(
- err,
- child,
- 'callback for immediate watcher "foo"'
- )
- expect(globalSpy).toHaveBeenCalledWith(
- err,
- child,
- 'callback for immediate watcher "foo"'
- )
- }).then(done)
- })
- it('should capture promise error from immediate watcher', done => {
- const spy = vi.fn()
- let child
- let err
- const Child = {
- data() {
- return {
- foo: 'foo'
- }
- },
- watch: {
- foo: {
- immediate: true,
- handler() {
- err = new Error('userImmediateWatcherCallback error')
- return Promise.reject(err)
- }
- }
- },
- created() {
- child = this
- },
- render() {}
- }
- new Vue({
- errorCaptured: spy,
- render: h => h(Child)
- }).$mount()
- waitForUpdate(() => {
- expect(spy).toHaveBeenCalledWith(
- err,
- child,
- 'callback for immediate watcher "foo" (Promise/async)'
- )
- expect(globalSpy).toHaveBeenCalledWith(
- err,
- child,
- 'callback for immediate watcher "foo" (Promise/async)'
- )
- }).then(done)
- })
- })
|