vuejs / vue-test-utils

Component Test Utils for Vue 2
https://vue-test-utils.vuejs.org
MIT License
3.57k stars 668 forks source link

test breaks when switching imports #780

Closed theNewFlesh closed 6 years ago

theNewFlesh commented 6 years ago

When I switch the import from "vue-test-utils" to "@vue/test-utils" one of my tests breaks.

wrapper.emitted() resolves to undefined in the latter case, and I get an "TypeError: undefined is not an object" error

Given that:

it works in the former case but not in the latter the wrapper API has not changed in any relevant ways the only changes to my code is the import statement it follows that it is something to do with @vue/test-utils itself. here is the component:

<template>
    <div ref="sliderContainer" class="slider-container" id="slider-container"></div>
</template>

<script lang="ts">
    import Vue from "vue";
    import { Component, Prop } from "vue-property-decorator";
    import "nouislider/distribute/nouislider.min.css";
    import * as noUiSlider from "nouislider";
    import * as utils from "../../../tools/utils";

    /*
    @param inPoint the position of the input (leftmost) marker
    @param playhead the default position of the playhead
    @param outPoint the position of the output (rightmost) marker
    @param step number of frames the playhead will step upon moving
    @param displayMarkers toggles display of input and output markers
    */
    @Component
    export default class Timeline extends Vue {

        @Prop({default: 0})
        public inPoint: number;

        @Prop({default: 0})
        public playhead: number;

        @Prop({default: 1000})
        public outPoint: number;

        @Prop({default: 1})
        public step: number;

        @Prop({default: true})
        public displayMarkers: boolean;

        private slider: noUiSlider;

        // These properties are needed for diffing noUiSlider event values
        private _inPoint: number;
        private _playhead: number;
        private _outPoint: number;

        /*
        @return tick step size relative to frame range
        */
        public get tickStep(): number {

            const delta: number = this._outPoint - this._inPoint;
            let tickSteps: number[] = utils.BASE_10_MARKERS.filter(
                (item) => (delta / item < 20)
            );
            return tickSteps[0];
        }

        /*
        @return frame range divided into (tickStep / 2) ticks
        */
        public get ticks(): number[] {
            return utils.range(
                this._inPoint,
                this._outPoint,
                this.tickStep / 2
            );
        }

        /*
        Splits noUiSlider update event into three events:
        - inPointChanged
        - playheadChanged
        - outPointChanged
        and emits them.
        @param event a marker update event to be processed
        @event inPointChanged in-point marker has been moved
        @event playheadChanged playhead marker has been moved
        @event outPointChanged out-point marker has been moved
        */
        public onMarkerUpdate(event) {
            // noUiSlider bundles all its update events in a single array
            // this method assumes three markers are present and decomposes
            // them into separate events

            event = event.map( (val) => Math.round(Number(val)) )
            const input: number = event[0];
            const playhead: number = event[1];
            const output: number = event[2];

            if (input != this._inPoint) {
                this._inPoint = input;
                this.$emit("inPointChanged", input);
            };

            if (playhead != this._playhead) {
                this._playhead = playhead;
                this.$emit("playheadChanged", playhead);
            };

            if (output != this._outPoint) {
                this._outPoint = output;
                this.$emit("outPointChanged", output);
            };
        }

        /*
        evaluates displayMarkers and toggles the display of the input and output markers
        */
        public toggleMarkers() {
            const container = this.$el as HTMLDivElement;
            const inpoint = container.querySelector(".noUi-handle.noUi-handle-lower");
            const outpoint = container.querySelector(".noUi-handle.noUi-handle-upper");

            if (!this.displayMarkers) {
                inpoint.setAttribute("style", "display: none");
                outpoint.setAttribute("style", "display: none");
            } else {
                inpoint.removeAttribute("style");
                outpoint.removeAttribute("style");
            };
        }

        /*
        assigns marker positions and errors on bad ones
        @throws Error if step not greater than 0
        @throws Error if tickStep is not greater than 0
        @throws Error if inPoint is less than outPoint
        */
        public created() {
            // these properties go undefined without assignment here
            this._inPoint = this.inPoint;
            this._playhead = this.playhead;
            this._outPoint = this.outPoint;

            if (this.step <= 0) {
                throw new Error("step must be greater than 0");
            };

            // TODO: determine if this is still needed
            if (this.tickStep <= 0) {
                throw new Error("tickStep must be greater than 0");
            };

            if (this._inPoint >= this._outPoint) {
                throw new Error(`inPoint must less than outPoint. ${this._inPoint} !< ${this._outPoint}`);
            };
        }

        /*
        build a noUISlider object and adds it to the DOM
        */
        public mounted() {
            const container = this.$refs.sliderContainer as HTMLDivElement;
            this.slider = noUiSlider.create(
                container,
                {
                    start: [this._inPoint, this._playhead, this._outPoint],
                    connect: [true, false, false, true],
                    step: this.step,
                    range: {
                        min: this._inPoint,
                        max: this._outPoint
                    },
                    pips: {
                        mode: "values",
                        values: this.ticks,
                        density: 1,
                        filter: (value, type) => value % this.tickStep ? 2: 1
                    }
                }
            );

            this.slider.on(
                "update",
                this.onMarkerUpdate
            );

            this.toggleMarkers();
        }
    }
</script>

<style lang="scss">
</style>

here is the test:

import Vue from "vue";
import Timeline from "../src/components/Timeline.vue";

import * as vtu from "@vue/test-utils";
import * as utils from "../../tools/utils";
// -----------------------------------------------------------------------------

/*
convenience function for getting a good timeline instance
@return a wrapped Timeline mounted with easily testable prop data
*/
function getWrapper1(): any {
    return vtu.mount(Timeline,
        {
            propsData: {
                inPoint: 2,
                outPoint: 6,
                step: 2,
                displayMarkers: true
            }
        }
    );
}

/*
like getWrapper1, but with props that make testing tick and tickStep easy
@return a wrapped Timeline mounted with easily testable prop data
*/
function getWrapper2(): any {
    return vtu.mount(Timeline,
        {
            propsData: {
                inPoint: 0,
                outPoint: 100,
                step: 1,
                displayMarkers: true
            }
        }
    );
}
// -----------------------------------------------------------------------------

describe('Timeline', () => {
    describe("events", () => {
        const wrapper = getWrapper1();

        it("onMarkerUpdate emits correct events and values", () => {
            wrapper.vm.onMarkerUpdate(["1", "2", "3"]);
            const emitted = wrapper.emitted();
            expect(emitted.inPointChanged[0][0]).toBe(1);
            expect(emitted.playheadChanged[0][0]).toBe(2);
            expect(emitted.outPointChanged[0][0]).toBe(3);
        });
    });
});
vue-bot commented 6 years ago

Hello, your issue has been closed because it does not conform to our issue requirements. In order to ensure every issue provides the necessary information for us to investigate, we require the use of the Issue Helper when creating new issues. Thank you!

theNewFlesh commented 6 years ago

here is the component:

<template>
    <div ref="sliderContainer" class="slider-container" id="slider-container"></div>
</template>

<script lang="ts">
    import Vue from "vue";
    import { Component, Prop } from "vue-property-decorator";
    import "nouislider/distribute/nouislider.min.css";
    import * as noUiSlider from "nouislider";
    import * as utils from "../../../tools/utils";

    /*
    @param inPoint the position of the input (leftmost) marker
    @param playhead the default position of the playhead
    @param outPoint the position of the output (rightmost) marker
    @param step number of frames the playhead will step upon moving
    @param displayMarkers toggles display of input and output markers
    */
    @Component
    export default class Timeline extends Vue {

        @Prop({default: 0})
        public inPoint: number;

        @Prop({default: 0})
        public playhead: number;

        @Prop({default: 1000})
        public outPoint: number;

        @Prop({default: 1})
        public step: number;

        @Prop({default: true})
        public displayMarkers: boolean;

        private slider: noUiSlider;

        // These properties are needed for diffing noUiSlider event values
        private _inPoint: number;
        private _playhead: number;
        private _outPoint: number;

        /*
        @return tick step size relative to frame range
        */
        public get tickStep(): number {

            const delta: number = this._outPoint - this._inPoint;
            let tickSteps: number[] = utils.BASE_10_MARKERS.filter(
                (item) => (delta / item < 20)
            );
            return tickSteps[0];
        }

        /*
        @return frame range divided into (tickStep / 2) ticks
        */
        public get ticks(): number[] {
            return utils.range(
                this._inPoint,
                this._outPoint,
                this.tickStep / 2
            );
        }

        /*
        Splits noUiSlider update event into three events:
        - inPointChanged
        - playheadChanged
        - outPointChanged
        and emits them.
        @param event a marker update event to be processed
        @event inPointChanged in-point marker has been moved
        @event playheadChanged playhead marker has been moved
        @event outPointChanged out-point marker has been moved
        */
        public onMarkerUpdate(event) {
            // noUiSlider bundles all its update events in a single array
            // this method assumes three markers are present and decomposes
            // them into separate events

            event = event.map( (val) => Math.round(Number(val)) )
            const input: number = event[0];
            const playhead: number = event[1];
            const output: number = event[2];

            if (input != this._inPoint) {
                this._inPoint = input;
                this.$emit("inPointChanged", input);
            };

            if (playhead != this._playhead) {
                this._playhead = playhead;
                this.$emit("playheadChanged", playhead);
            };

            if (output != this._outPoint) {
                this._outPoint = output;
                this.$emit("outPointChanged", output);
            };
        }

        /*
        evaluates displayMarkers and toggles the display of the input and output markers
        */
        public toggleMarkers() {
            const container = this.$el as HTMLDivElement;
            const inpoint = container.querySelector(".noUi-handle.noUi-handle-lower");
            const outpoint = container.querySelector(".noUi-handle.noUi-handle-upper");

            if (!this.displayMarkers) {
                inpoint.setAttribute("style", "display: none");
                outpoint.setAttribute("style", "display: none");
            } else {
                inpoint.removeAttribute("style");
                outpoint.removeAttribute("style");
            };
        }

        /*
        assigns marker positions and errors on bad ones
        @throws Error if step not greater than 0
        @throws Error if tickStep is not greater than 0
        @throws Error if inPoint is less than outPoint
        */
        public created() {
            // these properties go undefined without assignment here
            this._inPoint = this.inPoint;
            this._playhead = this.playhead;
            this._outPoint = this.outPoint;

            if (this.step <= 0) {
                throw new Error("step must be greater than 0");
            };

            // TODO: determine if this is still needed
            if (this.tickStep <= 0) {
                throw new Error("tickStep must be greater than 0");
            };

            if (this._inPoint >= this._outPoint) {
                throw new Error(`inPoint must less than outPoint. ${this._inPoint} !< ${this._outPoint}`);
            };
        }

        /*
        build a noUISlider object and adds it to the DOM
        */
        public mounted() {
            const container = this.$refs.sliderContainer as HTMLDivElement;
            this.slider = noUiSlider.create(
                container,
                {
                    start: [this._inPoint, this._playhead, this._outPoint],
                    connect: [true, false, false, true],
                    step: this.step,
                    range: {
                        min: this._inPoint,
                        max: this._outPoint
                    },
                    pips: {
                        mode: "values",
                        values: this.ticks,
                        density: 1,
                        filter: (value, type) => value % this.tickStep ? 2: 1
                    }
                }
            );

            this.slider.on(
                "update",
                this.onMarkerUpdate
            );

            this.toggleMarkers();
        }
    }
</script>

<style lang="scss">
</style>

here is the test:

import Vue from "vue";
import Timeline from "../src/components/Timeline.vue";

import * as vtu from "@vue/test-utils";
import * as utils from "../../tools/utils";
// -----------------------------------------------------------------------------

/*
convenience function for getting a good timeline instance
@return a wrapped Timeline mounted with easily testable prop data
*/
function getWrapper1(): any {
    return vtu.mount(Timeline,
        {
            propsData: {
                inPoint: 2,
                outPoint: 6,
                step: 2,
                displayMarkers: true
            }
        }
    );
}

/*
like getWrapper1, but with props that make testing tick and tickStep easy
@return a wrapped Timeline mounted with easily testable prop data
*/
function getWrapper2(): any {
    return vtu.mount(Timeline,
        {
            propsData: {
                inPoint: 0,
                outPoint: 100,
                step: 1,
                displayMarkers: true
            }
        }
    );
}
// -----------------------------------------------------------------------------

describe('Timeline', () => {
    describe("events", () => {
        const wrapper = getWrapper1();

        it("onMarkerUpdate emits correct events and values", () => {
            wrapper.vm.onMarkerUpdate(["1", "2", "3"]);
            const emitted = wrapper.emitted();
            expect(emitted.inPointChanged[0][0]).toBe(1);
            expect(emitted.playheadChanged[0][0]).toBe(2);
            expect(emitted.outPointChanged[0][0]).toBe(3);
        });
    });
});