Shreerang
Shreerang

Reputation: 387

Vue Component Unit Testing

I have a counter component - pretty straight forward.

<div> + </div>
    <input type="text" v-model="countData" />
<div> - </div>

Detailed code is here - https://github.com/Shreerang/Vue-Nuggets/blob/master/src/components/QuantitySelector/QuantitySelector.vue

I am trying to unit test this component.

it('Renders a default quantity selector with the max count set to 6', () => {
    const wrapper = shallowMount(QuantitySelector);
    wrapper.find('input[type="text"]').setValue('1');
    expect(wrapper.find('input[type="text"]').element.value).toBe('1'); // notice here that the value is a String, whereas I expect it to be a Number

    wrapper.findAll('div').at(2).trigger('click');
    expect(wrapper.vm.countData).toBe('2'); // This fails as countData becomes "11" instead of doing a 1 + 1 = 2 and then becoming Number 2.

    expect(wrapper.find('input[type="text"]').element.value).toBe(2); // You can notice the same thing as above.

    wrapper.find('input[type="text"]').setValue(wrapper.vm.countData); // Do I have to do this? This does not seem right to me :frowning:
});

I am not able to get this unit test to work! Any help with this is appreciated!

Upvotes: 0

Views: 226

Answers (1)

Roy J
Roy J

Reputation: 43881

Text fields contain text values. Note that you even specified a text value: setValue('1'). If you change the value in the input manually, (say, to 3) and press the increment button, it becomes 31. Your test is telling you the truth.

You need to change your functions to convert to number. [Update] As your comment informed me, Vue has a .number modifier for v-model for this very purpose

new Vue({
  el: '#app',
    name: 'quantity-selector',
    props: {
        count: {
            type: Number,
            default: 1,
        }, // Makes sense to have default product count value
        maxCount: {
            type: Number,
            default: 6,
        }, // maxCount makes sense when you have a restriction on the max quantity for a product
        iconDimensions: {
            type: Number,
            default: 15,
        },
        minusIconFillColor: {
            type: String,
            default: '#000',
        },
        plusIconFillColor: {
            type: String,
            default: '#000',
        },
        isCountEditable: {
            type: Boolean,
            default: true,
        },
    },
    data() {
        return {
            countData: this.count,
        };
    },
    computed: {
        minusIconColor: function() {
            return this.countData === this.count ? '#CCC' : this.minusIconFillColor;
        },
        plusIconColor: function() {
            return this.countData === this.maxCount ? '#CCC' : this.plusIconFillColor;
        },
    },
    methods: {
        decrement: function() {
            if (this.countData > this.count) {
                this.countData -= 1;
            }
        },
        increment: function() {
            if (this.countData < this.maxCount) {
                this.countData += 1;
            }
        },
        adjustCount: function() {
            if (this.countData > this.maxCount) {
                this.countData = this.maxCount;
            } else if (this.countData < this.count) {
                this.countData = this.count;
            } else {
                if (isNaN(Number(this.countData))) {
                    this.countData = this.count;
                }
            }
        },
    }
});
.nugget-quantity-counter {
    display: inline-flex;
}
.nugget-quantity-counter div:first-child {
    border: solid 1px #ccc;
    border-radius: 5px 0px 0px 5px;
}
.nugget-quantity-counter div:nth-child(2) {
    border-top: solid 1px #ccc;
    border-bottom: solid 1px #ccc;
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.nugget-quantity-counter input[type='text'] {
    border-top: solid 1px #ccc;
    border-bottom: solid 1px #ccc;
    border-left: none;
    border-right: none;
    text-align: center;
    width: 20px;
    padding: 12px;
    margin: 0;
    font-size: 16px;
}
.nugget-quantity-counter div:last-child {
    border: solid 1px #ccc;
    border-radius: 0px 5px 5px 0px;
}
.nugget-quantity-counter > div {
    cursor: pointer;
    padding: 12px;
    width: 20px;
    text-align: center;
}
.nugget-quantity-counter > div > svg {
    height: 100%;
}
<script src="https://unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
  <div @click="decrement">
    <svg viewBox="0 0 24 24" :width="iconDimensions" :height="iconDimensions">
                <g>
                    <path d='M64 0 M2 11 L2 13 L22 13 L22 11 Z' :fill="minusIconColor" />
                </g>
            </svg>
  </div>
  <input v-if="isCountEditable" type="text" v-model.number="countData" @blur="adjustCount" />
  <div v-else>{{countData}}</div>
  <div @click="increment">
    <svg viewBox="0 0 24 24" :width="iconDimensions" :height="iconDimensions">
                <g>
                    <path d="M 11 2 L 11 11 L 2 11 L 2 13 L 11 13 L 11 22 L 13 22 L 13 13 L 22 13 L 22 11 L 13 11 L 13 2 Z" :fill="plusIconColor" />
                </g>
            </svg>
  </div>
</div>

Upvotes: 1

Related Questions