vuetify + jest lists error messages although test is green - jestjs

As a vue newbie I wrote a test for a component. The test is green. However, when using vuetify in the component under test (v-layout, v-flex) error messages are listed in the console output. They disappear when removing vuetify in the component (v-layout, v-flex). How can I use vuetify and still avoid those messages?
The component TestForm
<script>
import "#/assets/Styles";
import {cloneDeep} from "lodash";
import VForm from "vuetify/es5/components/VForm";
import VBtn from "vuetify/es5/components/VBtn";
import {VContainer, VContent, VFlex, VLayout, VSpacer} from "vuetify/es5/components/VGrid";
import VTextField from "vuetify/es5/components/VTextField";
import {VCard, VCardText, VCardTitle} from "vuetify/es5/components/VCard";
import TestModelData from "#/api/model/example/TestModelData";
import TestData from "#/api/model/example/TestData";
import TestStatus from "#/api/model/example/TestStatus";
import TestStatusSelect from "#/components/common/TestStatusSelect";
export default {
components: {
VBtn,
VForm,
TestModelData, TestData, TestStatus, TestStatusSelect,
VCard, VCardTitle, VCardText,
VContainer, VContent, VLayout, VFlex, VSpacer,
VTextField
},
props: {
testModelData: TestModelData
},
data() {
return {
currentTestModelData: this.testModelData,
testData: this.testData ? cloneDeep(this.testData) : new TestData()
};
},
watch: {
"testModelData.testdata": function (val) {
console.log("Testdata has changed;", val);
if (val) {
this.testData = cloneDeep(val);
} else {
this.testData = new TestData();
}
}
},
computed: {
readOnly: function () {
if (this.testData.testStatus.id !== TestStatus.FIRST.id) {
return true;
} else {
return false;
}
}
},
methods: {
onFormChange(event) {
console.log("Changed: ", event);
this.$store.dispatch({
type: "testModelData/setTestData",
testData: this.testData
});
}
}
};
</script>
<template>
<v-form ref="form">
<v-layout wrap>
<v-flex xs12 lg6>
<TestStatusSelect
ref="testDataSelect"
v-model="testData.testStatus"
#change="onFormChange($event)"/>
</v-flex>
</v-layout>
</v-form>
<!-- when comment the above and uncomment the below the error messages disappear -->
<!--<v-form ref="form">-->
<!--<TestStatusSelect-->
<!--ref="testDataSelect"-->
<!--v-model="testData.testStatus"-->
<!--#change="onFormChange($event)"/>-->
<!--</v-form>-->
</template>
The jest-test
import VueTestUtils, {createLocalVue, mount} from "#vue/test-utils";
import Vuex from 'vuex';
import Vuetify from 'vuetify';
import TestForm from "#/components/example/TestForm";
import TestModelData from "#/api/model/example/TestModelData";
VueTestUtils.config.provide['$options'] = {};
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.use(Vuetify);
const TEST_MODEL_DATA = TestModelData.fromJSON({
"id": 1,
"testdata": {
"id": 1,
"name": "Foo",
"testStatus": 0,
}
});
describe('TestForm Tests', () => {
test('TestForm select testStatus', () => {
const setTestData = jest.fn();
const getters = {
"current": jest.fn().mockImplementation(() => {
return TEST_MODEL_DATA;
}),
"hasUnsavedChanges": jest.fn().mockReturnValue(false),
};
const store = new Vuex.Store({
modules: {
testModelData: {
namespaced: true,
getters: getters,
actions: {setTestData}
}
}
});
const wrapper = mount(TestForm, {
store, localVue, propsData: {
testModelData: TEST_MODEL_DATA
}
});
const first = wrapper.findAll('.v-list__tile--link').at(1);
first.trigger('click');
expect(setTestData).toHaveBeenCalled();
});
});
The component TestStatusSelect
<script>
import VSelect from "vuetify/es5/components/VSelect";
import TestStatus from "#/api/model/example/TestStatus";
export default {
components: {
VSelect
},
props: ["value", "disabled"],
data() {
return {
testStatuses: TestStatus.ALL,
testStatus: this.value ? this.value : TestStatus.FIRST
};
},
watch: {
value(val) {
if (this.testStatus.id !== val.id) {
console.log('VALUE');
this.testStatus = val;
}
},
testStatus(val, oldVal) {
if (val.id !== oldVal.id) {
this.$emit("input", val);
this.$emit("change", val);
}
}
}
};
</script>
<template>
<v-select
ref="testStatusSelect"
:disabled="disabled"
label="Result"
:items="testStatuses"
item-text="name"
item-value="id"
v-model="testStatus"
return-object>
</v-select>
</template>
The class TestModelData
import TestData from "#/api/model/example/TestData";
class TestModelData {
constructor() {
this.id = null;
this.testData = null;
}
fromJSON(json) {
this.id = json.id;
this.testData = TestData.fromJSON(json.testData);
}
toJSON() {
const o = {
id: this.id,
};
if (this.testData) {
a.testData = this.testData.toJSON();
}
return o;
}
static fromJSON(json) {
if (!json) {
return null;
} else {
const a = new TestModelData();
a.fromJSON(json);
return a;
}
}
}
export default TestModelData;
The class TestData
import TestStatus from "#/api/model/example/TestStatus";
class TestData {
constructor() {
this.id = null;
this.name = null;
this.testStatus = TestStatus.FIRST;
}
fromJSON(json) {
this.id = json.id;
this.name = json.name;
this.testStatus = json.testStatus !== null ? TestStatus.fromJSON(json.testStatus) : null;
}
toJSON() {
const o = {
id: this.id,
};
o.name = this.name;
o.testStatus = this.testStatus ? this.testStatus.toJSON() : null;
return o;
}
static fromJSON(json) {
if (!json) {
return null;
} else {
const a = new TestData();
a.fromJSON(json);
return a;
}
}
}
export default TestData;
The class TestStatus
import PropTypes from "prop-types";
import Definition from "../Definition";
class TestStatus extends Definition {
constructor(id, name) {
super();
this.id = id;
this.name = name;
}
static FIRST = new TestStatus(0, "first");
static SECOND = new TestStatus(1, "second");
static ALL = [
TestStatus.FIRST,
TestStatus.SECOND
];
toJSON() {
return this.id;
}
static fromJSON(json) {
if (json === TestStatus.FIRST.id) {
return TestStatus.FIRST;
}
else if (json === TestStatus.SECOND.id) {
return TestStatus.SECOND;
}
console.error("TestStatus unknown", json);
throw new Error(`TestStatus ${json} unknown`, json);
}
}
TestStatus.prototype.PROPTYPES = {
id: PropTypes.number,
name: PropTypes.string,
};
export default TestStatus;
The console output
console.error node_modules/#vue/test-utils/dist/vue-test-utils.js:15
[vue-test-utils]: an extended child component <VBtn> has been modified to ensure it has the correct instance properties. This means it is not possible to find the component with a component selector. To find the component, you must stub it manually using the stubs mounting option.
console.error node_modules/#vue/test-utils/dist/vue-test-utils.js:15
[vue-test-utils]: an extended child component <VCard> has been modified to ensure it has the correct instance properties. This means it is not possible to find the component with a component selector. To find the component, you must stub it manually using the stubs mounting option.
console.error node_modules/#vue/test-utils/dist/vue-test-utils.js:15
[vue-test-utils]: an extended child component <VCardTitle> has been modified to ensure it has the correct instance properties. This means it is not possible to find the component with a component selector. To find the component, you must stub it manually using the stubs mounting option.
console.error node_modules/#vue/test-utils/dist/vue-test-utils.js:15
[vue-test-utils]: an extended child component <VCardText> has been modified to ensure it has the correct instance properties. This means it is not possible to find the component with a component selector. To find the component, you must stub it manually using the stubs mounting option.
console.warn node_modules/vuetify/es5/util/console.js:32
[Vuetify] Unable to locate target [data-app]
found in
---> <VMenu>
<VSelect>
<TestStatusSelect>
<VForm>
<VCard>
<Anonymous>
<Root>
console.error node_modules/vue/dist/vue.common.js:593
[Vue warn]: $listeners is readonly.
found in
---> <VSelect>
<TestStatusSelect>
<VForm>
<VCard>
<Anonymous>
<Root>
console.log src/components/example/TestForm.vue:800
Changed: TestStatus {
_clazz: [Getter/Setter],
id: [Getter/Setter],
name: [Getter/Setter] }
console.error node_modules/vue/dist/vue.common.js:593
[Vue warn]: $listeners is readonly.
found in
---> <VSelect>
<TestStatusSelect>
<VForm>
<VCard>
<Anonymous>
<Root>
console.error node_modules/vue/dist/vue.common.js:593
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "value"
found in
---> <VSelect>
<TestStatusSelect>
<VForm>
<VCard>
<Anonymous>
<Root>
console.error node_modules/vue/dist/vue.common.js:593
[Vue warn]: $listeners is readonly.
found in
---> <VSelect>
<TestStatusSelect>
<VForm>
<VCard>
<Anonymous>
<Root>

There are a few solutions / work arounds in this thread.
This is the one that worked for me, I added it at the top of my test body:
document.body.setAttribute('data-app', true)

find the button by id and triggering a click may occur the warning to resolve this issue consider the below code
let element_forgetBtn = wrapper.find("#forgotPasswordBtn");
> let app = document.createElement("div");
> app.setAttribute("data-app", true);
> document.body.append(app);
element_forgetBtn.trigger("click");
complete code is below
let element_forgetBtn = wrapper.find("#forgotPasswordBtn");
let app = document.createElement("div");
app.setAttribute("data-app", true);
document.body.append(app);
element_forgetBtn.trigger("click");

Related

Using CKEditor in a component with Laravel/Inertia

I'd like to use Ckeditor in my Laravel/Inertia project and i can't get it to work. I found a tutorial from LaraTips, but that was written for VueJS-2. I am working with the lastest version Inertia which uses VueJS-3.
I want to use Ckeditor in a separate component, and it (sort of) works, but i can't get the old data to show in the editor. I get an error "Uncaught (in promise) TypeError: Cannot read property 'setData' of undefined at Proxy.modelValue (app.js:29)"
What am i doing wrong?
This is my component:
<template>
<ckeditor :editor="editor" v-model="text" :config="editorConfig"></ckeditor>
</template>
<script>
import ClassicEditor from '#ckeditor/ckeditor5-build-classic';
export default {
data() {
return {
text: "",
editor: ClassicEditor,
editorConfig: {
// The configuration of the editor.
},
}
},
props: {
modelValue: {}
},
setup() {
},
watch: {
modelValue: {
immediate: true,
handler(modelValue) {
this.text = modelValue;
}
},
text(text) {
this.$emit('update:modelValue', text);
}
},
}
</script>
Any suggestions??
I am doing the same tutorial (i am using vueJS-3).
this may work for you:
in app.js include CKEditor:
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => require(`./Pages/${name}.vue`),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.use( CKEditor)
.mixin({ methods: { route } })
.mount(el);
},
});
In Components/CkEditor.vue check what are you emitting
look for this this.$emit("input", text);
<template>
<ckeditor :editor="editor" v-model="text" :config="editorConfig"></ckeditor>
</template>
<script>
import ClasicEditor from "#ckeditor/ckeditor5-build-classic";
export default {
props: {
value: {},
},
data() {
return {
text: "",
editor: ClasicEditor,
editorConfig: {
// The configuration of the editor.
},
};
},
watch: {
value:{
inmediate: true,
handler(value){
this.text = value;
}
},
text(text) {
this.$emit("input", text);
},
},
};
</script>
let me know if that worked for you
I looked at the answer here, and below is what worked for me:
Hope this helps! :)
I am using laravel/inertia with vue 3.
app.js
import './bootstrap';
import '../css/app.css';
import { createApp, h } from 'vue';
import { createInertiaApp } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
import { createPinia } from 'pinia';
import { _t } from './Utilities/translations';
import CKEditor from '#ckeditor/ckeditor5-vue';
const appName =
window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) =>
resolvePageComponent(
`./Pages/${name}.vue`,
import.meta.glob('./Pages/**/*.vue')
),
setup({ el, app, props, plugin }) {
const vue_app = createApp({ render: () => h(app, props) });
vue_app.use(plugin);
vue_app.use(ZiggyVue, Ziggy);
vue_app.use(createPinia());
// Register all base components globally
const components = import.meta.globEager('./Components/Base/*.vue');
for (const path in components) {
let componentName;
if (path.split) {
const split_componentName = path.split('/').pop();
if (split_componentName) {
componentName = split_componentName.replace(/\.\w+$/, '');
vue_app.component(componentName, components[path].default);
}
}
}
vue_app.config.globalProperties.$_t = _t;
vue_app.use(CKEditor);
vue_app.mount(el);
return vue_app;
}
});
InertiaProgress.init({ color: '#4B5563' });
CKEditor Component:
<template>
<div id="app">
<ckeditor
v-model="editor_data"
:editor="editor"
:config="editor_config"
></ckeditor>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from '#vue/reactivity';
import * as editor from '#ckeditor/ckeditor5-build-classic';
const editor_data = ref('');
const editor_config = {};
</script>

When run test, TypeError: Cannot destructure property 'travelDatas' of '(0 , _GetTravelDatas.getTravelDatas)(...)' as it is undefined

When I run test, it show TypeError: Cannot destructure property 'travelDatas' of '(0 , _GetTravelDatas.getTravelDatas)(...)' as it is undefined.
As you see the screenshot: unit test
There isn't any console error or warning.
Could anyone help please
travelListTest.spec.js
import { mount, shallowMount } from '#vue/test-utils'
import TravelList from '../../src/components/TravelList.vue'
import { getTravelDatas } from '../../src/composables/GetTravelDatas'
import ElementPlus from 'element-plus'
const wrapper = shallowMount(TravelList, {
global: {
plugins: [ElementPlus]
}
})
jest.mock('../../src/composables/GetTravelDatas')
describe('TravelList Test', () => {
test('click more will call GoToTravelDetailPage', () => {
wrapper.vm.GoToTravelDetailPage = jest.fn()
console.log(wrapper.html())
wrapper.find('.el-button').trigger('click')
expect(wrapper.vm.GoToTravelDetailPage).toHaveBeenCalled()
})
})
TravelList.vue
.....
<script>
import { ref } from '#vue/reactivity';
import { useRouter } from "vue-router";
import { getTravelDatas } from '../composables/GetTravelDatas'
export default {
name: 'TravelList',
setup() {
const { travelDatas } = getTravelDatas();
const router = useRouter();
function GoToTravelDetailPage(acctractionId) {
router.push({ path: `/travelDetail/${acctractionId}` })
}
return { travelDatas, GoToTravelDetailPage };
},
};
</script>
GetTravelDatas.js
import axios from "axios";
import { ref } from '#vue/runtime-core';
export function getTravelDatas() {
const travelDatas = ref([])
axios.get('https://localhost:5001/MyTravel/GetTravelData')
.then((response) => {
if (!response.data.success) {
alert(response.data.errorMessage)
}else{
travelDatas.value = response.data.travelDetail
}
}).catch((error) => {
alert('Unexpected Error: ', error.message)
console.log(error)
});
return { travelDatas }
}
You are mocking the GetTravelDatas module with an auto-mock version by calling:
jest.mock('../../src/composables/GetTravelDatas')
That means that all the methods exported from that module will be mocked (the real code of the method will not be called) and the mocked version will return undefined when called.
In the code you are testing you then have:
const { travelDatas } = getTravelDatas();
Reading the travelDatas property from undefined is causing the error you are seeing.
You should mock the getTravelDatas method so that it returns the appropriate data. For example, returning an empty array would look like:
getTravelDatas.mockReturnValue([]);

Custom Card rendering when no longer presented?

I'm new to building custom cards for HASS so this might be obvious.
I have a basic clock card and I have put a console message on the render method - it seems to be writing to the log even when the card is no longer being presented? (i.e. you've moved to another lovelace view).
I'm using a setTimeout to trigger a property change - am I meant to stop the timeout at some point of the lifecycle, or is there some teardown in the lifecycle, etc?
Here's my code:
/* eslint-disable #typescript-eslint/no-explicit-any */
import {
LitElement,
html,
customElement,
property,
CSSResult,
TemplateResult,
css,
PropertyValues,
internalProperty,
} from 'lit-element';
import {
HomeAssistant,
hasConfigOrEntityChanged,
hasAction,
ActionHandlerEvent,
handleAction,
LovelaceCardEditor,
getLovelace,
LovelaceCard,
} from 'custom-card-helpers'; // This is a community maintained npm module with common helper functions/types
import { hass, provideHass } from "card-tools/src/hass";
import './editor';
import type { BoilerplateCardConfig } from './types';
import { actionHandler } from './action-handler-directive';
import { CARD_VERSION } from './const';
import { localize } from './localize/localize';
/* eslint no-console: 0 */
console.info(
`%c BOILERPLATE-CARD \n%c ${localize('common.version')} ${CARD_VERSION} `,
'color: orange; font-weight: bold; background: black',
'color: white; font-weight: bold; background: dimgray',
);
// This puts your card into the UI card picker dialog
(window as any).customCards = (window as any).customCards || [];
(window as any).customCards.push({
type: 'boilerplate-card',
name: 'Boilerplate Card',
description: 'A template custom card for you to create something awesome',
});
// TODO Name your custom element
#customElement('boilerplate-card')
export class BoilerplateCard extends LitElement {
CUSTOM_TYPE_PREFIX = "custom:";
constructor() {
super();
this.date = new Date();
setInterval(() => {
this.date = new Date();
}, 1000);
}
public static async getConfigElement(): Promise<LovelaceCardEditor> {
return document.createElement('boilerplate-card-editor');
}
public static getStubConfig(): object {
return {};
}
// TODO Add any properities that should cause your element to re-render here
// https://lit-element.polymer-project.org/guide/properties
#property({ attribute: false }) public hass!: HomeAssistant;
#internalProperty() private date: Date;
#internalProperty() private config!: BoilerplateCardConfig;
// https://lit-element.polymer-project.org/guide/properties#accessors-custom
public setConfig(config: BoilerplateCardConfig): void {
// TODO Check for required fields and that they are of the proper format
if (!config) {
throw new Error(localize('common.invalid_configuration'));
}
if (config.test_gui) {
getLovelace().setEditMode(true);
}
this.config = {
name: 'Boilerplate',
...config,
};
}
// https://lit-element.polymer-project.org/guide/lifecycle#shouldupdate
protected shouldUpdate(changedProps: PropertyValues): boolean {
return hasConfigOrEntityChanged(this, changedProps, true);
}
// https://lit-element.polymer-project.org/guide/templates
protected render(): TemplateResult | void {
const timeFormatter: Intl.DateTimeFormatOptions = {
year: undefined,
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
}
console.info("Draw")
return html`
<ha-card
.header=${this.config.name}
.actionHandler=${actionHandler({
hasHold: hasAction(this.config.hold_action),
hasDoubleClick: hasAction(this.config.double_tap_action),
})}
tabindex="0"
.label=${`Boilerplate: ${this.config.entity || 'No Entity Defined'}`}
>
<h1>${new Intl.DateTimeFormat(undefined, timeFormatter).format(this.date)}</h1>
${this.config.cards.map((card) => {
let tag = card.type;
if (tag.startsWith(this.CUSTOM_TYPE_PREFIX)) {
tag = tag.substr(this.CUSTOM_TYPE_PREFIX.length);
} else {
tag = `hui-${tag}-card`;
}
const cardElement = document.createElement(tag) as LovelaceCard;
cardElement.setConfig(card);
cardElement.hass = hass();
return cardElement
})}
</ha-card>
`;
}
// https://lit-element.polymer-project.org/guide/styles
static get styles(): CSSResult {
return css``;
}
}
Use connectedCallback and disconnectedCallback to start and stop your timer:
#customElement('boilerplate-card')
export class BoilerplateCard extends LitElement {
connectedCallback() {
super.connectedCallback();
this.date = new Date();
this.interval = setInterval(() => {
this.date = new Date();
}, 1000);
}
disconnectedCallback() {
super.disconnectedCallback();
clearInterval(this.interval);
}
...
}

Is it possible to override the DEFAULT_TEARDOWN function inside exceptions-zone.ts?

I'm trying to create an application with NestJS framework and I'd like to check if a specific Service is in the application context but, the framework default behavior is exiting the process when it doesn't find the Service inside the context.
I've surrounded the get method with try...catch but it doesn't work because of the process.exit(1) execution.
private async loadConfigService(server: INestApplication): Promise<void> {
try {
this.configService = await server.get<ConfigService>(ConfigService, { strict: false });
} catch (error) {
this.logger.debug('Server does not have a config module loaded. Loading defaults...');
}
this.port = this.configService ? this.configService.port : DEFAULT_PORT;
this.environment = this.configService ? this.configService.environment : Environment[process.env.NODE_ENV] || Environment.DEVELOPMENT;
this.isProduction = this.configService ? this.configService.isProduction : false;
}
I'd like to catch the exception to manage the result instead of exiting the process.
Here's what I came up with:
import { NestFactory } from '#nestjs/core';
export class CustomNestFactory {
constructor() {}
public static create(module, serverOrOptions, options) {
const ob = NestFactory as any;
ob.__proto__.createExceptionZone = (receiver, prop) => {
return (...args) => {
const result = receiver[prop](...args);
return result;
};
};
return NestFactory.create(module, serverOrOptions, options);
}
}
Now, instead of using the default NestFactory I can use my CustomNestFactory
Dirty, but it works
Here is my code to solve same issue:
import { ExceptiinsZone } from '#nestjs/core/errors/exceptions-zone';
ExceptionsZone.run = callback => {
try {
callback();
} catch (e) {
ExceptionsZone.exceptionHandler.handle(e);
throw e;
}
};
You may need to override asyncRun() also for other method.

Uncaught TypeError: Cannot set property 'rowsdata' of undefined

I am stuck at some point working with angular 2 with node js. below is my code. It couldn't set rows(variable) to this.rowsdata (variable).
I think the reason behind is that node Js uses asynchronous call. May be that's why this.rows data get undefined for rows.
/// <reference path="../typings/node/node.d.ts" />
import { Component } from 'angular2/core';
import { OnInit } from 'angular2/core';
declare var module : any;
declare var jQuery : any;
var rowsdata : any[] = [];
// self = this;
interface ROWS {
name : string,
current_balance : number
}
#Component({
selector : 'portfolioList',
templateUrl : '../app/view/portfoliolist.html',
moduleId : module.id
})
export class PortfolioList implements OnInit {
// self : any = this;
constructor() {
// var self : this;
}
ngOnInit(): any {
this.showdata();
// console.log(this.rowsdata);
}
showdata() {
console.log("called");
var portfolioList = require('../app/api/showPortfolio.js');
portfolioList.showPortfolio(function(err:any, rows:any) {
console.log(rows);
// self.rowsdata = rows;
this.rowsdata = rows;
});
// console.log(self.rowsdata);
console.log(rowsdata);
}
}
here is the showPortfolio function
exports.showPortfolio = function(callback) {
db.all(squel .select()
.from("portfolios")
.field("name")
.field("current_balance") .toString() , function(err, rows) { callback(null, rows) });
}
In your code :
portfolioList.showPortfolio(function(err:any, rows:any) {
Change this to a fat arrow:
portfolioList.showPortfolio((err:any, rows:any) => {
More
https://basarat.gitbooks.io/typescript/content/docs/arrow-functions.html
i cant post a comment with all this code, so i post an answer.
it should work like this. i've put some extra logs.
showdata() {
console.log("called");
var _this = this;
console.log(_this);
var portfolioList = require('../app/api/showPortfolio.js');
portfolioList.showPortfolio(function(err:any, rows:any) {
console.log(rows);
console.log(_this);
_this.rowsdata = rows;
});
// console.log(self.rowsdata);
console.log(rowsdata);
}
}

Resources