Search code examples
unit-testingvue.jsmocha.jsvue-test-utils

Simulate backspace in Vue Test Utils


I've written a standalone reusable component in VueJS that is essentially just a wrapper for an input field, but that does smart real-time processing of the keyboard input based on props passed to it.

It's working like a charm, and I'm able to successfully test most of its functionality via the Vue Test Utils (Mocha flavor) but I'm also trying to test that it correctly responds to special keys (arrows, backspace, tab, etc.) and getting stumped.

Here's an edited down version of the component itself:

<template>
    <input type="text" v-model="internalValue" :placeholder="placeholder"
       @keydown="keyDownHandler"/>
</template>
<script>
    export default {
        name: "LimitedTextArea",
        props: {
            fieldname: '',
            value: { type: String, default: ""},
            placeholder: 'placeholder',
            ...
        },
        data: function() {
            return {
                internalValue: ''
            }
        },
        watch: {
            internalValue(newVal /*, oldVal*/ ) {
                this.$emit("input", this.fieldname, newVal);
            }
        },
        mounted: function() {
            ...
        },
        methods: {
            keyDownHandler(evt) {
                this.internalValue = this.value;
                if (!evt.metaKey && evt.keyCode >= 45) {
                    evt.preventDefault();
                    const inputChar = evt.key;
                    let newChar = '';
                    /* filtering logic here */
                    this.internalValue += newChar;
                } else {
                    // just for tracing this path during dev
                    console.log('will execute default action');
                }
            }
        }
    }
</script>

… and here's the test:

it('embedded: delete key functions normally', () => {
    const initialValue = '';
    const inputValue = 'omega';
    const outputValue = 'omeg';
    const deleteKeyEvent = {key: 'Backspace', keyCode: 8};
    const parent = mount({
        data: function() { return {
            textValue: initialValue
        }},
        template: \`<div>
           <limited-text-area :fieldname="'textValue'" :value="textValue" 
           @input="input"></limited-text-area>
        </div>`,
        components: { 'limited-text-area': LimitedTextArea },
        methods: {
            input(fieldname, value) {
                this[fieldname] = value;
            }
        }
    });
    const input = parent.find('input');
    typeStringIntoField(inputValue, input);

    // I've tried with and without this before sending the key event…
    input.element.focus();
    input.element.setSelectionRange(inputValue.length, inputValue.length);

    // I've tried doing it this way
    input.trigger('keydown', deleteKeyEvent);

    // And I've tried doing it this way, based on the Vue Test Utils guide 
    // for keyboard events
    input.trigger('keydown.up.backspace');
    expect(parent.vm.textValue).to.equal(outputValue);
});

Neither of the two approaches quoted above work. At this point I suspect that it may not be a simple question of calling the wrong method, and that I'm either:

  1. misunderstanding the DOM within Vue Test Utils (effectively treating it like it's PhantomJS); or
  2. using the wrong test methodology entirely for this case.

Any help would be greatly appreciated! Thanks.


Solution

  • Simulating BACKSPACE on <input> natively (outside Vue in a browser) doesn't actually delete text, so I don't see it working with Vue. The solutions I found simply check the keydown event for backspace, and programmatically delete a character from the input's text [1] [2]. So, I'd say this isn't something that could be effectively unit-tested.

    Note that vue-test-utils uses JSDom (not PhantomJS).