Search code examples
unit-testingvue.jsavoriaz

Vue.js ( w Avoriaz) How to catch error in parent component when thrown from child component


Testing the child component with an empty input value, the error is thrown, but not caught in the parent component Error: Uncaught Error: New Item without a text

How can I catch it ? and where ? thanks for feedback

PARENT COMPONENT ShoppingListComponent.vue

    <template>
      <div>
        <h2>{{ title }}</h2>
        <add-item-component :id='id' @add="addItem"></add-item-component>
        <items-component :items="items" :id="id"></items-component>
      </div>
    </template>

    <script>
      import AddItemComponent from './AddItemComponent'
      import ItemsComponent from './ItemsComponent'

      export default {
        components: {
          AddItemComponent,
          ItemsComponent
        },
        props: ['id', 'title', 'items'],
        methods: {
          addItem (text) {
            console.log('SHOPLIST addItem:', text)
            if (text.length > 0) {
              this.items.push({
                text: text,
                checked: false
              })
            }
          }
        }
      }
    </script>

CHILD COMPONENT AddItemComponent.vue

    <template>
      <div class="input-group">
        <input type="text" @keyup.enter="addItem" v-model="newItem" placeholder="add shopping list item" class="form-control">
        <span class="input-group-btn">
          <button @click="addItem" class="btn btn-default" type="button">Add!</button>
        </span>
      </div>
    </template>

    <script>
      export default {
        props: ['id'],
        data () {
          return {
            newItem: ''
          }
        },
        methods: {
          addItem () {. // see @keyup.enter in this template
            var text

            text = this.newItem.trim()
            if (text.length > 0) {
              this.$emit('add', this.newItem). // see @add in parent template
              this.newItem = ''
              this.$store.dispatch('updateList', this.id)
            } else {
              console.log('ERROR EMPTY TEXT'). // debugging
              throw new Error('New Item without a text')
           }
          }
        }
      }
    </script>

UNIT TEST AddItemComponent.spec.js

    import Vue from 'vue'
    import AddItemComponent from '@/components/AddItemComponent'
    import Vuex from 'vuex'

    import sinon from 'sinon'
    import { mount } from 'avoriaz'

    Vue.use(Vuex)

    describe('AddItemComponent.vue', () => {
      let actions
      let store
      let wrapper

      describe('add New Item', () => {
        beforeEach(() => {
          actions = {
            actionClick: sinon.stub(),
            addItem: sinon.stub()
          }
          store = new Vuex.Store({
            state: {},
            actions
          })
          wrapper = mount(AddItemComponent, { store })
          sinon.stub(wrapper.vm, '$emit')
          sinon.stub(store, 'dispatch')
        })

        it('should throw an error  when input value is null and an input event is fired', () => {
          const input = wrapper.find('input')[0]
          wrapper.setProps({ id: 'niceId' })
          wrapper.setData({ newItem: '' })
          wrapper.setProps({ items: [] })
          input.trigger('keyup.enter')

          expect(() => {
            wrapper.vm.addItem()
          }).to.throw('New Item without a text')
        })
      })
    })

CONSOLE UNIT TEST

    LOG LOG: 'ERROR EMPTY TEXT'

      AddItemComponent.vue
        add New Item
          ✗ should throw an error  when input value is null and an input event is fired
            Error: Uncaught Error: New Item without a text (webpack:///src/components/AddItemComponent.vue:33:0 <- index.js:26197)
                at Wrapper.trigger (webpack:///~/avoriaz/dist/avoriaz.js:597:0 <- index.js:16436:16)
                at Context.<anonymous> (webpack:///test/unit/specs/components/AddItemComponent.spec.js:46:12 <- index.js:25368:13)

Solution

  • Vue provides a way to define a global error handler in it's config:

    Vue.config.errorHandler = function (err, vm, info) {
      // Do stuff with error
    }
    

    You can see this in action here: https://jsfiddle.net/ydx7q4m3/

    See: https://v2.vuejs.org/v2/api/#errorHandler