How to test this component? - vue.js

I have a Page level component which implements a component BookingInformation with slots. In the Page component, it's got another component BookingInformationHeader with slots. header and default.
My question is, how should I set up my test so that I can test that the GoogleConversionTrackingImage is visible when #Reservation.State wasBookingJustMade changes to true?
<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
import { Reservation } from "#/store/vuex-decorators";
import { BookingInformation, BookingInformationHeader } from "#/components";
import GoogleConversionTrackingImage from './components/GoogleConversionTrackingImage.vue';
#Component({
components: {
BookingInformation,
BookingInformationHeader,
GoogleConversionTrackingImage
}
})
export default class ConfirmationPage extends Vue {
renderTrackingImage: boolean = false;
#Reservation.State wasBookingJustMade: boolean;
}
</script>
<template>
<booking-information page-type="confirmation-page" class="confirmation-page">
<template slot="header" slot-scope="bookingInfo">
<booking-information-header>
<template slot="buttons">
// some buttons
</template>
</booking-information-header>
<google-conversion-tracking-image v-if="wasBookingJustMade" />
</template>
</booking-information>
</template>

By using vue test utils https://vue-test-utils.vuejs.org/ and chai https://www.chaijs.com/ in your test file you can do something like:
import mount from "#vue/test-utils";
import expect from "chai";
const wrapper = mount(BookingInformation,<inner components you want to test>);
expect(googleImage.exists()).to.be.true;
wrapper.setData({
wasBookingJustMade: true,
});
const googleImage = wrapper.find("google-conversion-tracking-image");
expect(googleImage.exists()).to.be.false;
You'll probably need to import the page level component as well.
You can give an id to the component you want to find and then search by id.

Related

is it available to call the methods where in the vue component from the plugin?

I wanted to access the vue.data or methods in the plugin.
no matter what I tried several times, it didn't work.
such as eventBus, Mixin etc...
so I'm curious about the possibility to call the methods like that.
thank you for reading this question.
here is the custom component.
<template>
<div>
<v-overlay :value="isProcessing">
<v-progress-circular indeterminate size="64"></v-progress-circular>
</v-overlay>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
#Component
export default class ProgressCircular extends Vue {
private isProcessing: boolean;
startProcess() {
this.isProcessing = true;
}
}
</script>
and this is the plugin source.
import ProgressCircular from '#/components/ProgressCircular.vue';
import { VueConstructor } from 'vue';
import Vuetify from 'vuetify/lib';
import vuetify from './vuetify';
export default {
install(Vue: VueConstructor, options: any = {}) {
Vue.use(Vuetify);
options.vuetify = vuetify;
Vue.component('progress-circular', ProgressCircular);
Vue.prototype.$fireProgressing = function () {
// it didn't work
// I just wanted to access the method where in the Vue Component
// ProgressCircular.startProcess();
};
},
};
use the plugin syntax to extend vue like:
Vue.use({
install: Vue => {
Vue.prototype.$fireProgressing = () => {
};
}
});
or
Vue.use(YOURPLUGIN);
before you mount vue

Vue 3: component `:is` in for loop fails

I'm trying to loop over a list of component described by strings (I get the name of the component from another , like const componentTreeName = ["CompA", "CompA"].
My code is a simple as:
<script setup>
import CompA from './CompA.vue'
import { ref } from 'vue'
// I do NOT want to use [CompA, CompA] because my inputs are strings
const componentTreeName = ["CompA", "CompA"]
</script>
<template>
<h1>Demo</h1>
<template v-for="compName in componentTreeName">
<component :is="compName"></component>
</template>
</template>
Demo here
EDIT
I tried this with not much success.
Use resolveComponent() on the component name to look up the global component by name:
<script setup>
import { resolveComponent, markRaw } from 'vue'
const myGlobalComp = markRaw(resolveComponent('my-global-component'))
</script>
<template>
<component :is="myGlobalComp" />
<template>
demo 1
If you have a mix of locally and globally registered components, you can use a lookup for local components, and fall back to resolveComponent() for globals:
<script setup>
import LocalComponentA from '#/components/LocalComponentA.vue'
import LocalComponentB from '#/components/LocalComponentB.vue'
import { resolveComponent, markRaw } from 'vue'
const localComponents = {
LocalComponentA,
LocalComponentB,
}
const lookupComponent = name => {
const c = localComponents[name] ?? resolveComponent(name)
return markRaw(c)
}
const componentList = [
'GlobalComponentA',
'GlobalComponentB',
'LocalComponentA',
'LocalComponentB',
].map(lookupComponent)
</script>
<template>
<component :is="c" v-for="c in componentList" />
</template>
demo 2
Note: markRaw is used on the component definition because no reactivity is needed on it.
When using script setup, you need to reference the component and not the name or key.
To get it to work, I would use an object where the string can be used as a key to target the component from an object like this:
<script setup>
import CompA from './CompA.vue'
import { ref } from 'vue'
const components = {CompA};
// I do NOT want to use [CompA, CompA] because my inputs are strings
const componentTreeName = ["CompA", "CompA"]
</script>
<template>
<h1>Demo</h1>
<template v-for="compName in componentTreeName">
<component :is="components[compName]"></component>
</template>
</template>
To use a global component, you could assign components by pulling them from the app context. But this would require the app context to be available and the keys known.
example:
import { app } from '../MyApp.js'
const components = {
CompA: app.component('CompA')
}
I haven't tested this, but this might be worth a try to check with getCurrentInstance
import { ref,getCurrentInstance } from 'vue'
const components = getCurrentInstance().appContext.components;

Defining types of Vue 2.x components

I use Vue 2.6 in my project
I want to define props of component for specific cases (for dynamic component in child component with passed props)
Here is some sample code
// child component
<template>
<component :is="someProp.passedComponent" />
</template>
import { Component, Vue, Prop } from 'vue-property-decorator'
#Component({
name: 'SomeComponent'
})
export default class SomeComponent extends Vue {
#Prop() readonly someProp!: { passedComponent: Vue }
}
// parent component
<template>
<some-component :some-prop="someDataForProp">
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import DummyComponent from './components/DummyComponent.vue'
#Component({
name: 'ParentComponent'
})
export default class ParentComponent extends Vue {
private someDataForProp = { passedComponent: DummyComponent }
}
</script>
// DummyComponent.vue
<template>
<div>{{ title }}</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
#Component({
name: 'DummyComponent'
})
export default class DummyComponent extends Vue {
private title = 'This is dummy component for example'
}
</script>
But when I define Component type as Vue, It makes some error like this
Type 'typeof DummyComponent' is missing the following properties from type 'Vue': $el, $options, $parent, $root, and 39 more.
I wonder if there is one type that can encompass all components defined with #Component.
passedComponent is expected to be a component itself that is used <component>, while Vue type refers to Vue component instance, i.e. component ref.
It should be:
#Prop() readonly someProp!: { passedComponent: typeof Vue }

Vue 3 as a class component

currently in the project we uses Vue 2.x and our components works in such way
#Component({
template: `
<div>
some code ....
<div> `
})
export default class class1 extends Vue {
#Prop() data: IsomeData;
}
vue-class-component and vue-property-decorator allows us to right in this way, according the docs, #Component was replaced to #Options({}).
How can I migrate to Vue3 without headbreaking refactoring?
Try this.
<template>
<div>
some code ....
<div>
</template>
<script>
import { Vue } from "vue-class-component";
import { Prop } from "vue-property-decorator";
export default class Home extends Vue {
#Prop() data: IsomeData;
}
</script>

Vue global component not defined

I am creating a Vue plugin that adds a custom component to the global scope like so:
import CustomComponent from './CustomComponent.vue';
const MyPlugin = {
install(Vue, options) {
Vue.component('CustomComponent', CustomComponent);
}
}
and the component itself is simply:
<template>
<h1>Hi from the custom component</h1>
</template>
<script>
export default {
name: 'CustomComponent',
mounted() {
console.log('hello console');
}
}
</script>
And then finally I import the plugin into my main.js file (I'm using Gridsome):
export default function (Vue, { router, head, isClient }) {
Vue.use(MyPlugin);
}
But now I expect that when I make a component I can extend the CustomComponent since it's in the global scope like so:
<template>
<h2>Hi again</h2>
</template>
<script>
export default {
name: 'RegularComponent',
extends: CustomComponent
}
</script>
But this gives me an error of Custom Component not defined and it seems like it's because CustomComponent isn't in the global scope because if I import the CustomComponent vue file in my RegularComponent vue file it works. However, this is not the behavior I would like and I cannot figure out why CustomComponent is not just globally available.
CustomComponent is a javascript object, you still need to import to use it
<script>
import CustomComponent from './CustomComponent.vue'
export default {
name: 'RegularComponent',
extends: CustomComponent
}
</script>
I think when define component as global, that means you can use it in template without re-declare:
<template>
<h2>Hi again</h2>
<custom-component />
</template>