2
votes

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.

1
Does it work if you set the .native modifier like @keydown.native= keyDownHandler ?brainbag
Hey, @brainbag… I've tried a few variations on that, and no progress. As stated above, the component is working as expected (including the delete functionality, etc.) , it's the test I can't get working. So I suspect the event handling is okay, and instead I'm banging my thick head against the Test Utils instead.RiqueW
Thanks, @tony19. That really clarifies this situation. I'm taking your comment as an endorsement of "[I'm] misunderstanding the DOM within Vue Test Utils (effectively treating it like it's PhantomJS)." I like your workaround, but in this particular case I feel like I'd be 'testing the test.' I think I'm going to treat this as something that can't effectively be unit-tested, and leave it for an e2e test. Your comment could be reworded as an acceptable solution, hint hint.RiqueW
@RiqueW Ok, done :)tony19

1 Answers

2
votes

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).