vuejs-templates / webpack

A full-featured Webpack + vue-loader setup with hot reload, linting, testing & css extraction.
MIT License
9.7k stars 4.39k forks source link

Moutned vue component in unit test has empty HTML #1043

Closed robertmain closed 6 years ago

robertmain commented 6 years ago

So I think there may be an issue with the way the testing setup is configured here.

Given this component:

<template>
    <div class="clock">
        <span class="time">{{dateTimeObj.format(timeFormat)}}</span><br />
        <span class="date">{{dateTimeObj.format(dateFormat)}}</span>
    </div>
</template>

<script>
import * as moment from 'moment';

export default {
    name: 'Clock',
    props: {
        timeFormat: {
            type: String,
            default: 'hh:mm:ss a'
        },
        dateFormat: {
            type: String,
            deafult: 'dddd[,] MMMM Do[,] YYYY'
        }
    },
    data () {
        return {
            dateTimeObj : null
        }
    },
    mounted () {
        this.interval = setInterval(() => {
            this.dateTimeObj = moment()
        }, 500);
    },
    destroyed () {
        clearInterval(this.interval);
    }
};
</script>

And this test:

import Vue from 'vue';
import Clock from '@/components/Clock';
import * as chai from 'chai';

const expect = chai.expect;

describe('clock', () => {
    it('displays the time in the requested format', (done) => {
        const Constructor = Vue.extend(Clock);

        const comp = new Constructor({
            timeFormat: 'hh:mm a'
        }).$mount();

        Vue.nextTick(() => {
            expect(comp.$el.textContent.length)
                .to.be.greaterThan(0);
            // Since we're doing this asynchronously, we need to call done() to tell Mocha that we've finished the test.
            done();
        });
    });
});

It seems that comp.$el.textContent is equal to <!-- -->. I can't tell if this is a bug with vue, or a bug with the testing setup here - but I think something is definitely amiss.

LinusBorg commented 6 years ago

You have to mount to an actual element. Create one with document.createElement

robertmain commented 6 years ago

@LinusBorg what do you mean? I'm mounting the component with $mount() - also document wont't exist in that context (my unit test) surely?

LinusBorg commented 6 years ago

what do you mean? I'm mounting the component with $mount()

You are mounting, but not to an element:

const comp = new Constructor({
  timeFormat: 'hh:mm a'
}).$mount(document.createElement('DIV'));

also document wont't exist in that context (my unit test) surely?

Of course it exists, the unit tests run in a browser with the karma runner.

robertmain commented 6 years ago

OK - so it's still coming up as <!-- -->

Are there some docs somewhere? I feel like I'm maybe missing something obvious.

LinusBorg commented 6 years ago

Oh, well you are calling .textContent on the root element of your template which is a <div> and that element doesn't contain any text - it only contains two <span> elements. so there's no textContent.

robertmain commented 6 years ago

Right, but in this case comp.$el is still just showing up as <!-- -->...like I said - are there docs?

LinusBorg commented 6 years ago

There's a very basic guide on the main website, and we have a guide specific to our test utility:

http://vue-test-utils.vuejs.org

However this is still confusing as this should simply work as it is.

robertmain commented 6 years ago

Yeah, it is weird...

Btw - vuejd.org is a non-existant domain - did you mean vuejs.org? :)

LinusBorg commented 6 years ago

Obviously :-P

robertmain commented 6 years ago

I mention it because I haven't finished my second coffee yet and it took me a good 15 minutes to figure out why it didn't work :P

robertmain commented 6 years ago

Either way - I'm not using vue-test-utils...just whatever comes bundled with this template...not sure if that's relevant...

LinusBorg commented 6 years ago

Nope. I could take a look if you could share a repository, but as far as looking at code snippets goes, I got nothing more to contribute.

robertmain commented 6 years ago

Sure, thanks - repo is here: https://glow.dev.maio.me/impulse/dashboard/tree/sjohn/date-component

LinusBorg commented 6 years ago

That component looks a bit different and there's no spec in the unit folder - are you sure this is the latest commit?

robertmain commented 6 years ago

Whoops - try now. I committed and pushed

robertmain commented 6 years ago

OK...so it looks like the test is the problem. My component works just fine in the browser.

This prints out the prop data(in this case hh:mm a:

const Constructor = Vue.extend(Clock);

const vm = new Constructor({
    propsData: {
        timeFormat: 'hh:mm a'
    }
}).$mount(document.createElement('div'));
//eslint-disable-next-line
console.error(vm.timeFormat);

...however - this:

const Constructor = Vue.extend(Clock);

const vm = new Constructor({
        timeFormat: 'hh:mm a'
}).$mount(document.createElement('div'));
//eslint-disable-next-line
console.error(vm.timeFormat);

Just prints out undefined

Also worth noting that if, in the first example - I do this:

const Constructor = Vue.extend(Clock);

const vm = new Constructor({
    propsData: {
        timeFormat: 'hh:mm a'
    }
}).$mount(document.createElement('div'));
//eslint-disable-next-line
console.error(vm.$el);

It prints out <!-- --> ....meanwhile console.log(vm.$el.children) simply produces undefined

LinusBorg commented 6 years ago

You still need $nextTick if I remember correctly.

robertmain commented 6 years ago

Tried that

const Constructor = Vue.extend(Clock);

const vm = new Constructor({
    propsData: {
        timeFormat: 'hh:mm a'
    }
}).$mount(document.createElement('div'));
Vue.nextTick(() => {
    //eslint-disable-next-line
    console.error(vm.$children);
    expect(0).to.equal(0);
    done();
});

I get [] in my terminal

LinusBorg commented 6 years ago

$children contains child components. Your component doesn't have any child components.

robertmain commented 6 years ago

...oh - I would've thought it would contain child elements...

Either way - vm.$el is still showing as <!-- -->

Surely I should have something showing up?

LinusBorg commented 6 years ago

Can'T say more, couldn't test your app so yet.

robertmain commented 6 years ago

Ah, ok. I did commit the component and it's tests up earlier I think.

Either way - I appreciate you doing this. I originally thought this was an issue with the testing config in the template...but it seems to be just my understanding of how to test Vue components :)

robertmain commented 6 years ago

Solved it in the end - ended up upgrading to the latest version of the template and using jest instead of mocha and chai.

iberflow commented 6 years ago

This is still an issue for the rest of us :(

LinusBorg commented 6 years ago

You're the first to come across it as far as I'm concerned, so i don't know that "rest of us".

OPs reproduction is no longer avaliable, and I never had such issues.

If you can provide a clean repository with a minimal reproduction, open a new issue and I am happy to take a look.

mjvezzani commented 6 years ago

Apologies that it isn't a clean repository with a minimal reproduction, but I'm getting the same issue. If you care to look:

https://github.com/mjvezzani/redx_application/blob/master/vue_app/test/unit/specs/ProfileForm.spec.js

The console.log(vm.$el) in that test outputs <!---->. Unsure why that is, seeing as I used Header.vue and Header.spec.js as templates for my ProfileForm.vue and ProfileForm.spec.js files. When I console.log(vm.$el) in the Header.spec.js test, I get a set of DOM elements, but that just isn't the case in for ProfileForm.spec.js.