#8647 fix vitest warnings #1467

Merged
jon merged 91 commits from 8647_fix_warnings into dev 2025-04-16 11:18:11 +00:00
63 changed files with 400 additions and 358 deletions

View File

@ -67,7 +67,7 @@ describe('Axios boot', () => {
};
const result = onResponseError(error);
expect(result).rejects.toEqual(expect.objectContaining(error));
await expect(result).rejects.toEqual(expect.objectContaining(error));
});
it('should call to the Notify plugin with a message from the response property', async () => {
@ -83,7 +83,7 @@ describe('Axios boot', () => {
};
const result = onResponseError(error);
expect(result).rejects.toEqual(expect.objectContaining(error));
await expect(result).rejects.toEqual(expect.objectContaining(error));
});
});
});

View File

@ -20,7 +20,6 @@ const postcodeFormData = reactive({
provinceFk: null,
townFk: null,
});
const townFilter = ref({});
const countriesRef = ref(false);
const provincesOptions = ref([]);
@ -33,11 +32,11 @@ function onDataSaved(formData) {
newPostcode.town = town.value.name;
newPostcode.townFk = town.value.id;
const provinceObject = provincesOptions.value.find(
({ id }) => id === formData.provinceFk
({ id }) => id === formData.provinceFk,
);
newPostcode.province = provinceObject?.name;
const countryObject = countriesRef.value.opts.find(
({ id }) => id === formData.countryFk
({ id }) => id === formData.countryFk,
);
newPostcode.country = countryObject?.name;
emit('onDataSaved', newPostcode);
@ -67,21 +66,11 @@ function setTown(newTown, data) {
}
async function onCityCreated(newTown, formData) {
newTown.province = provincesOptions.value.find(
(province) => province.id === newTown.provinceFk
(province) => province.id === newTown.provinceFk,
);
formData.townFk = newTown;
setTown(newTown, formData);
}
async function filterTowns(name) {
if (name !== '') {
townFilter.value.where = {
name: {
like: `%${name}%`,
},
};
}
}
</script>
<template>
@ -107,7 +96,6 @@ async function filterTowns(name) {
<VnSelectDialog
:label="t('City')"
@update:model-value="(value) => setTown(value, data)"
@filter="filterTowns"
:tooltip="t('Create city')"
v-model="data.townFk"
url="Towns/location"

View File

@ -65,7 +65,7 @@ const $props = defineProps({
default: null,
},
beforeSaveFn: {
type: Function,
type: [String, Function],
jsegarra marked this conversation as resolved
Review

Hay que mirar quien le esta pasando un String, no aceptarlo

Hay que mirar quien le esta pasando un String, no aceptarlo
default: null,
},
goTo: {

View File

@ -22,7 +22,6 @@ const { validate, validations } = useValidator();
const { notify } = useNotify();
const route = useRoute();
const myForm = ref(null);
const attrs = useAttrs();
const $props = defineProps({
url: {
type: String,
@ -99,8 +98,12 @@ const $props = defineProps({
type: Function,
default: () => {},
},
preventSubmit: {
type: Boolean,
default: true,
},
});
const emit = defineEmits(['onFetch', 'onDataSaved']);
const emit = defineEmits(['onFetch', 'onDataSaved', 'submit']);
const modelValue = computed(
() => $props.model ?? `formModel_${route?.meta?.title ?? route.name}`,
).value;
@ -301,7 +304,7 @@ function onBeforeSave(formData, originalData) {
);
}
async function onKeyup(evt) {
if (evt.key === 'Enter' && !('prevent-submit' in attrs)) {
if (evt.key === 'Enter' && !$props.preventSubmit) {
const input = evt.target;
if (input.type == 'textarea' && evt.shiftKey) {
let { selectionStart, selectionEnd } = input;
@ -330,6 +333,7 @@ defineExpose({
<template>
<div class="column items-center full-width">
<QForm
v-on="$attrs"
ref="myForm"
v-if="formData"
@submit.prevent="save"

View File

@ -181,7 +181,7 @@ const searchModule = () => {
<template>
<QList padding class="column-max-width">
<template v-if="$props.source === 'main'">
<template v-if="$route?.matched[1]?.name === 'Dashboard'">
<template v-if="route?.matched[1]?.name === 'Dashboard'">
<QItem class="q-pb-md">
<VnInput
v-model="search"
@ -262,7 +262,7 @@ const searchModule = () => {
</template>
<template v-for="item in items" :key="item.name">
<template v-if="item.name === $route?.matched[1]?.name">
<template v-if="item.name === route?.matched[1]?.name">
<QItem class="header">
<QItemSection avatar v-if="item.icon">
<QIcon :name="item.icon" />

View File

@ -16,7 +16,7 @@ const $props = defineProps({
required: true,
},
searchUrl: {
type: String,
type: [String, Boolean],
default: 'table',
},
vertical: {

View File

@ -66,7 +66,7 @@ const $props = defineProps({
default: null,
},
create: {
type: Object,
type: [Boolean, Object],
default: null,
},
createAsDialog: {

View File

@ -30,6 +30,7 @@ function columnName(col) {
v-bind="$attrs"
:search-button="true"
:disable-submit-event="true"
:data-key="$attrs['data-key']"
Outdated
Review

Ja pasa con el v-bind no?

Ja pasa con el v-bind no?

comor?

comor?
:search-url
>
<template #body="{ params, orders, searchFn }">

View File

@ -58,7 +58,7 @@ async function getConfig(url, filter) {
const response = await axios.get(url, {
params: { filter: filter },
});
return response.data && response.data.length > 0 ? response.data[0] : null;
return response?.data && response?.data?.length > 0 ? response.data[0] : null;
}
async function fetchViewConfigData() {

View File

@ -11,6 +11,9 @@ describe('VnTable', () => {
propsData: {
columns: [],
},
attrs: {
'data-key': 'test',
},
});
vm = wrapper.vm;

View File

@ -11,13 +11,7 @@ describe('CrudModel', () => {
beforeAll(() => {
wrapper = createWrapper(CrudModel, {
global: {
stubs: [
'vnPaginate',
'useState',
'arrayData',
'useStateStore',
'vue-i18n',
],
stubs: ['vnPaginate', 'vue-i18n'],
mocks: {
validate: vi.fn(),
},
@ -29,7 +23,7 @@ describe('CrudModel', () => {
dataKey: 'crudModelKey',
model: 'crudModel',
Outdated
Review

Esto hay que repetirlo siempre?

Esto hay que repetirlo siempre?

No, solo cuando defines un createWrapper y le pasas un objeto global

No, solo cuando defines un createWrapper y le pasas un objeto global

Voy a darle una vuelta para hacer algo asi Object.assign(defaultOptions, options)

Voy a darle una vuelta para hacer algo asi `Object.assign(defaultOptions, options)`
url: 'crudModelUrl',
saveFn: '',
saveFn: vi.fn(),
Review

undefined?

undefined?
Review

Esto es parte de lo que hizo @jsegarra

Esto es parte de lo que hizo @jsegarra
Review

no entiendo la pregunta.
Se modificó esta prop porque habia un warning y por tanto el test también

no entiendo la pregunta. Se modificó esta prop porque habia un warning y por tanto el test también
},
});
wrapper = wrapper.wrapper;
@ -231,7 +225,7 @@ describe('CrudModel', () => {
expect(vm.isLoading).toBe(false);
expect(vm.hasChanges).toBe(false);
await wrapper.setProps({ saveFn: '' });
await wrapper.setProps({ saveFn: null });
});
it("should use default url if there's not saveFn", async () => {

View File

@ -170,7 +170,7 @@ describe('LeftMenu as card', () => {
vm = mount('card').vm;
});
it('should get routes for card source', async () => {
it('should get routes for card source', () => {
vm.getRoutes();
});
});
@ -251,7 +251,6 @@ describe('LeftMenu as main', () => {
});
it('should get routes for main source', () => {
vm.props.source = 'main';
vm.getRoutes();
expect(navigation.getModules).toHaveBeenCalled();
});

View File

@ -1,5 +1,5 @@
<script setup>
const model = defineModel({ type: [String, Number], required: true });
const model = defineModel({ type: [String, Number], default: '' });
jorgep marked this conversation as resolved
Review

Siempre tiene que haber un v-model

Siempre tiene que haber un v-model
Review

Voy a revisar, pero no me suena haber cambiado esto

Voy a revisar, pero no me suena haber cambiado esto
Review
@jorgep
Review

Y siempre lo hay, pero con ese valor. Al usar un VnInputDate el valor por defecto era undefined y saltaba un warning

Y siempre lo hay, pero con ese valor. Al usar un VnInputDate el valor por defecto era undefined y saltaba un warning
</script>
<template>
<QDate v-model="model" :today-btn="true" :options="$attrs.options" />

View File

@ -61,8 +61,11 @@ function onFileChange(files) {
function mapperDms(data) {
const formData = new FormData();
const { files } = data;
if (files) formData.append(files?.name, files);
let files = data.files;
if (files) {
files = Array.isArray(files) ? files : [files];
files.forEach((file) => formData.append(file?.name, file));
}
const dms = {
hasFile: !!data.hasFile,

View File

@ -5,7 +5,7 @@ import VnDate from './VnDate.vue';
import VnTime from './VnTime.vue';
const $attrs = useAttrs();
const model = defineModel({ type: [Date] });
const model = defineModel({ type: [Date, String] });
const $props = defineProps({
isOutlined: {
@ -29,7 +29,7 @@ const styleAttrs = computed(() => {
const mask = 'DD-MM-YYYY HH:mm';
const selectedDate = computed({
get() {
if (!model.value) return new Date(model.value);
if (!model.value) return JSON.stringify(new Date(model.value));
return date.formatDate(new Date(model.value), mask);
},
set(value) {

View File

@ -54,6 +54,10 @@ const $props = defineProps({
type: [Array],
default: () => [],
},
filterFn: {
type: Function,
default: null,
},
exprBuilder: {
type: Function,
default: null,
@ -62,16 +66,12 @@ const $props = defineProps({
type: Boolean,
default: true,
},
defaultFilter: {
type: Boolean,
default: true,
},
fields: {
type: Array,
default: null,
},
include: {
type: [Object, Array],
type: [Object, Array, String],
default: null,
},
where: {
@ -79,7 +79,7 @@ const $props = defineProps({
default: null,
},
sortBy: {
type: String,
type: [String, Array],
default: null,
},
limit: {
@ -152,10 +152,22 @@ const value = computed({
},
});
const arrayDataKey =
$props.dataKey ??
($props.url?.length > 0 ? $props.url : ($attrs.name ?? $attrs.label));
const arrayData = useArrayData(arrayDataKey, {
url: $props.url,
searchUrl: false,
mapKey: $attrs['map-key'],
});
const computedSortBy = computed(() => {
return $props.sortBy || $props.optionLabel + ' ASC';
});
const getVal = (val) => ($props.useLike ? { like: `%${val}%` } : val);
watch(options, (newValue) => {
setOptions(newValue);
});
@ -174,16 +186,6 @@ onMounted(() => {
if ($props.focusOnMount) setTimeout(() => vnSelectRef.value.showPopup(), 300);
});
const arrayDataKey =
$props.dataKey ??
($props.url?.length > 0 ? $props.url : ($attrs.name ?? $attrs.label));
const arrayData = useArrayData(arrayDataKey, {
url: $props.url,
searchUrl: false,
mapKey: $attrs['map-key'],
});
function findKeyInOptions() {
if (!$props.options) return;
return filter($props.modelValue, $props.options)?.length;
@ -252,43 +254,41 @@ async function fetchFilter(val) {
}
Outdated
Review

@jorgep esto cambia?

@jorgep esto cambia?

Lo he probado y funciona como antes, pruebalo si quieres por si acaso.

Lo he probado y funciona como antes, pruebalo si quieres por si acaso.
async function filterHandler(val, update) {
if (isLoading.value) return update();
if (!val && lastVal.value === val) {
lastVal.value = val;
return update();
}
lastVal.value = val;
let newOptions;
if (!$props.defaultFilter) return update();
if (
$props.url &&
($props.limit || (!$props.limit && Object.keys(myOptions.value).length === 0))
) {
newOptions = await fetchFilter(val);
} else newOptions = filter(val, myOptionsOriginal.value);
update(
() => {
if ($props.noOne && noOneText.toLowerCase().includes(val.toLowerCase()))
newOptions.unshift(noOneOpt.value);
if ($props.filterFn) update($props.filterFn(val));
else if (!val && lastVal.value === val) update();
else {
const makeRequest =
($props.url && $props.limit) ||
(!$props.limit && Object.keys(myOptions.value).length === 0);
newOptions = makeRequest
? await fetchFilter(val)
: filter(val, myOptionsOriginal.value);
myOptions.value = newOptions;
},
(ref) => {
if (val !== '' && ref.options.length > 0) {
ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true);
}
},
);
update(
() => {
if ($props.noOne && noOneText.toLowerCase().includes(val.toLowerCase()))
newOptions.unshift(noOneOpt.value);
myOptions.value = newOptions;
},
(ref) => {
if (val !== '' && ref.options.length > 0) {
ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true);
}
},
);
}
lastVal.value = val;
}
function nullishToTrue(value) {
return value ?? true;
}
const getVal = (val) => ($props.useLike ? { like: `%${val}%` } : val);
async function onScroll({ to, direction, from, index }) {
const lastIndex = myOptions.value.length - 1;

View File

@ -4,7 +4,7 @@ import VnDiscount from 'components/common/vnDiscount.vue';
describe('VnDiscount', () => {
let vm;
beforeAll(() => {
vm = createWrapper(VnDiscount, {
props: {
@ -12,7 +12,9 @@ describe('VnDiscount', () => {
price: 100,
quantity: 2,
discount: 10,
}
mana: 10,
promise: vi.fn(),
},
}).vm;
});
@ -21,8 +23,8 @@ describe('VnDiscount', () => {
});
describe('total', () => {
it('should calculate total correctly', () => {
it('should calculate total correctly', () => {
expect(vm.total).toBe(180);
});
});
});
});

View File

@ -41,10 +41,12 @@ describe('VnDms', () => {
companyFk: 2,
dmsTypeFk: 3,
description: 'This is a test description',
files: {
name: 'example.txt',
content: new Blob(['file content'], { type: 'text/plain' }),
},
files: [
{
name: 'example.txt',
content: new Blob(['file content'], { type: 'text/plain' }),
},
],
};
const expectedBody = {
@ -83,7 +85,7 @@ describe('VnDms', () => {
it('should map DMS data correctly and add file to FormData', () => {
const [formData, params] = vm.mapperDms(data);
expect(formData.get('example.txt')).toBe(data.files);
expect([formData.get('example.txt')]).toStrictEqual(data.files);
expect(expectedBody).toEqual(params.params);
});

View File

@ -2,7 +2,6 @@ import { createWrapper } from 'app/test/vitest/helper';
import { vi, describe, expect, it } from 'vitest';
import VnInput from 'src/components/common/VnInput.vue';
describe('VnInput', () => {
let vm;
let wrapper;
@ -11,26 +10,28 @@ describe('VnInput', () => {
function generateWrapper(value, isOutlined, emptyToNull, insertable) {
wrapper = createWrapper(VnInput, {
props: {
modelValue: value,
isOutlined, emptyToNull, insertable,
maxlength: 101
modelValue: value,
isOutlined,
emptyToNull,
insertable,
maxlength: 101,
},
attrs: {
label: 'test',
required: true,
maxlength: 101,
maxLength: 10,
'max-length':20
'max-length': 20,
},
});
wrapper = wrapper.wrapper;
vm = wrapper.vm;
input = wrapper.find('[data-cy="test_input"]');
};
}
describe('value', () => {
it('should emit update:modelValue when value changes', async () => {
generateWrapper('12345', false, false, true)
generateWrapper('12345', false, false, true);
await input.setValue('123');
expect(wrapper.emitted('update:modelValue')).toBeTruthy();
expect(wrapper.emitted('update:modelValue')[0]).toEqual(['123']);
@ -46,37 +47,36 @@ describe('VnInput', () => {
describe('styleAttrs', () => {
it('should return empty styleAttrs when isOutlined is false', async () => {
generateWrapper('123', false, false, false);
expect(vm.styleAttrs).toEqual({});
expect(vm.styleAttrs).toEqual({});
});
it('should set styleAttrs when isOutlined is true', async () => {
it('should set styleAttrs when isOutlined is true', async () => {
generateWrapper('123', true, false, false);
expect(vm.styleAttrs.outlined).toBe(true);
});
});
describe('handleKeydown', () => {
describe('handleKeydown', () => {
it('should do nothing when "Backspace" key is pressed', async () => {
generateWrapper('12345', false, false, true);
await input.trigger('keydown', { key: 'Backspace' });
expect(wrapper.emitted('update:modelValue')).toBeUndefined();
const spyhandler = vi.spyOn(vm, 'handleInsertMode');
expect(spyhandler).not.toHaveBeenCalled();
});
/*
TODO: #8399 REDMINE
*/
it.skip('handleKeydown respects insertable behavior', async () => {
const expectedValue = '12345';
generateWrapper('1234', false, false, true);
vm.focus()
vm.focus();
await input.trigger('keydown', { key: '5' });
await vm.$nextTick();
expect(wrapper.emitted('update:modelValue')).toBeTruthy();
expect(wrapper.emitted('update:modelValue')[0]).toEqual([expectedValue ]);
expect(vm.value).toBe( expectedValue);
expect(wrapper.emitted('update:modelValue')[0]).toEqual([expectedValue]);
expect(vm.value).toBe(expectedValue);
});
});
@ -86,6 +86,6 @@ describe('VnInput', () => {
const focusSpy = vi.spyOn(input.element, 'focus');
vm.focus();
expect(focusSpy).toHaveBeenCalled();
});
});
});
});

View File

@ -33,7 +33,7 @@ describe('VnInputDateTime', () => {
it('handles null date value', async () => {
generateWrapper(null, false, true);
await vm.$nextTick();
expect(vm.selectedDate).toBeInstanceOf(Date);
expect(vm.selectedDate).not.toBe(null);
});
it('updates the model value when a new datetime is set', async () => {

View File

@ -90,8 +90,10 @@ describe('VnLog', () => {
vm = createWrapper(VnLog, {
global: {
stubs: [],
mocks: {},
stubs: ['FetchData', 'vue-i18n'],
mocks: {
fetch: vi.fn(),
},
},
propsData: {
model: 'Claim',

View File

@ -26,7 +26,7 @@ describe('VnNotes', () => {
) {
vi.spyOn(axios, 'get').mockResolvedValue({ data: [] });
wrapper = createWrapper(VnNotes, {
propsData: options,
propsData: { ...defaultOptions, ...options },
});
wrapper = wrapper.wrapper;
vm = wrapper.vm;

View File

@ -2,7 +2,9 @@
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useArrayData } from 'composables/useArrayData';
import { useAttrs } from 'vue';
const attrs = useAttrs();
const { t } = useI18n();
const props = defineProps({
@ -67,7 +69,7 @@ const props = defineProps({
default: null,
},
searchUrl: {
type: String,
type: [String, Boolean],
default: null,
},
disableInfiniteScroll: {
@ -75,7 +77,7 @@ const props = defineProps({
default: false,
},
mapKey: {
type: String,
type: [String, Boolean],
default: '',
},
keyData: {
@ -220,7 +222,7 @@ defineExpose({
</script>
<template>
<div class="full-width">
<div class="full-width" v-bind="attrs">
<div
v-if="!store.data && !store.data?.length && !isLoading"
class="info-row q-pa-md text-center"

View File

@ -46,7 +46,7 @@ const props = defineProps({
default: null,
},
order: {
type: String,
type: [String, Array],
default: '',
},
limit: {

View File

@ -23,10 +23,15 @@ describe('CardSummary', () => {
beforeEach(() => {
wrapper = createWrapper(CardSummary, {
global: {
mocks: {
validate: vi.fn(),
},
},
propsData: {
dataKey: 'cardSummaryKey',
url: 'cardSummaryUrl',
filter: 'cardFilter',
filter: { key: 'cardFilter' },
},
});
vm = wrapper.vm;
@ -50,7 +55,7 @@ describe('CardSummary', () => {
it('should set correct props to the store', () => {
expect(vm.store.url).toEqual('cardSummaryUrl');
expect(vm.store.filter).toEqual('cardFilter');
expect(vm.store.filter).toEqual({ key: 'cardFilter' });
});
it('should respond to prop changes and refetch data', async () => {

View File

@ -7,7 +7,7 @@ describe('VnSearchbar', () => {
let wrapper;
let applyFilterSpy;
const searchText = 'Bolas de madera';
const userParams = {staticKey: 'staticValue'};
const userParams = { staticKey: 'staticValue' };
beforeEach(async () => {
wrapper = createWrapper(VnSearchbar, {
@ -23,8 +23,9 @@ describe('VnSearchbar', () => {
vm.searchText = searchText;
vm.arrayData.store.userParams = userParams;
applyFilterSpy = vi.spyOn(vm.arrayData, 'applyFilter').mockImplementation(() => {});
applyFilterSpy = vi
.spyOn(vm.arrayData, 'applyFilter')
.mockImplementation(() => {});
});
afterEach(() => {
@ -32,7 +33,9 @@ describe('VnSearchbar', () => {
});
it('search resets pagination and applies filter', async () => {
const resetPaginationSpy = vi.spyOn(vm.arrayData, 'resetPagination').mockImplementation(() => {});
const resetPaginationSpy = vi
.spyOn(vm.arrayData, 'resetPagination')
.mockImplementation(() => {});
await vm.search();
expect(resetPaginationSpy).toHaveBeenCalled();
@ -48,7 +51,7 @@ describe('VnSearchbar', () => {
expect(applyFilterSpy).toHaveBeenCalledWith({
params: { staticKey: 'staticValue', search: searchText },
filter: {skip: 0},
filter: { skip: 0 },
});
});
@ -68,4 +71,4 @@ describe('VnSearchbar', () => {
});
expect(vm.to.query.searchParam).toBe(expectedQuery);
});
});
});

View File

@ -1,5 +1,4 @@
import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
import axios from 'axios';
import { createWrapper } from 'app/test/vitest/helper';
import VnSms from 'src/components/ui/VnSms.vue';
@ -12,6 +11,9 @@ describe('VnSms', () => {
stubs: ['VnPaginate'],
mocks: {},
},
propsData: {
url: 'SmsUrl',
},
}).vm;
});

View File

@ -4,6 +4,8 @@ import { useArrayData } from 'composables/useArrayData';
import { useRouter } from 'vue-router';
import * as vueRouter from 'vue-router';
import { setActivePinia, createPinia } from 'pinia';
import { defineComponent, h } from 'vue';
import { mount } from '@vue/test-utils';
describe('useArrayData', () => {
const filter = '{"limit":20,"skip":0}';
@ -43,7 +45,7 @@ describe('useArrayData', () => {
it('should fetch and replace url with new params', async () => {
vi.spyOn(axios, 'get').mockResolvedValueOnce({ data: [] });
const arrayData = useArrayData('ArrayData', {
const arrayData = mountArrayData('ArrayData', {
url: 'mockUrl',
searchUrl: 'params',
});
@ -72,7 +74,7 @@ describe('useArrayData', () => {
data: [{ id: 1 }],
});
const arrayData = useArrayData('ArrayData', {
const arrayData = mountArrayData('ArrayData', {
url: 'mockUrl',
navigate: {},
});
@ -94,7 +96,7 @@ describe('useArrayData', () => {
],
});
const arrayData = useArrayData('ArrayData', {
const arrayData = mountArrayData('ArrayData', {
url: 'mockUrl',
oneRecord: true,
});
@ -107,3 +109,17 @@ describe('useArrayData', () => {
});
});
});
function mountArrayData(...args) {
let arrayData;
const TestComponent = defineComponent({
setup() {
arrayData = useArrayData(...args);
return () => h('div');
},
});
const asd = mount(TestComponent);
return arrayData;
}

View File

@ -64,88 +64,84 @@ describe('session', () => {
});
});
describe(
'login',
() => {
const expectedUser = {
id: 999,
name: `T'Challa`,
nickname: 'Black Panther',
lang: 'en',
userConfig: {
darkMode: false,
describe('login', () => {
const expectedUser = {
id: 999,
name: `T'Challa`,
nickname: 'Black Panther',
lang: 'en',
userConfig: {
darkMode: false,
},
worker: { department: { departmentFk: 155 } },
};
const rolesData = [
{
role: {
name: 'salesPerson',
},
worker: { department: { departmentFk: 155 } },
};
const rolesData = [
{
role: {
name: 'salesPerson',
},
},
{
role: {
name: 'admin',
},
{
role: {
name: 'admin',
},
},
];
beforeEach(() => {
vi.spyOn(axios, 'get').mockImplementation((url) => {
if (url === 'VnUsers/acls') return Promise.resolve({ data: [] });
return Promise.resolve({
data: { roles: rolesData, user: expectedUser },
});
},
];
beforeEach(() => {
vi.spyOn(axios, 'get').mockImplementation((url) => {
if (url === 'VnUsers/acls') return Promise.resolve({ data: [] });
return Promise.resolve({
data: { roles: rolesData, user: expectedUser },
});
});
});
it('should fetch the user roles and then set token in the sessionStorage', async () => {
const expectedRoles = ['salesPerson', 'admin'];
const expectedToken = 'mySessionToken';
const expectedTokenMultimedia = 'mySessionTokenMultimedia';
const keepLogin = false;
it('should fetch the user roles and then set token in the sessionStorage', async () => {
const expectedRoles = ['salesPerson', 'admin'];
const expectedToken = 'mySessionToken';
const expectedTokenMultimedia = 'mySessionTokenMultimedia';
const keepLogin = false;
await session.login({
token: expectedToken,
tokenMultimedia: expectedTokenMultimedia,
keepLogin,
});
const roles = state.getRoles();
const localToken = localStorage.getItem('token');
const sessionToken = sessionStorage.getItem('token');
expect(roles.value).toEqual(expectedRoles);
expect(localToken).toBeNull();
expect(sessionToken).toEqual(expectedToken);
await session.destroy(); // this clears token and user for any other test
await session.login({
token: expectedToken,
tokenMultimedia: expectedTokenMultimedia,
keepLogin,
});
it('should fetch the user roles and then set token in the localStorage', async () => {
const expectedRoles = ['salesPerson', 'admin'];
const expectedToken = 'myLocalToken';
const expectedTokenMultimedia = 'myLocalTokenMultimedia';
const keepLogin = true;
const roles = state.getRoles();
const localToken = localStorage.getItem('token');
const sessionToken = sessionStorage.getItem('token');
await session.login({
token: expectedToken,
tokenMultimedia: expectedTokenMultimedia,
keepLogin,
});
expect(roles.value).toEqual(expectedRoles);
expect(localToken).toBeNull();
expect(sessionToken).toEqual(expectedToken);
const roles = state.getRoles();
const localToken = localStorage.getItem('token');
const sessionToken = sessionStorage.getItem('token');
await session.destroy(); // this clears token and user for any other test
});
expect(roles.value).toEqual(expectedRoles);
expect(localToken).toEqual(expectedToken);
expect(sessionToken).toBeNull();
it('should fetch the user roles and then set token in the localStorage', async () => {
const expectedRoles = ['salesPerson', 'admin'];
const expectedToken = 'myLocalToken';
const expectedTokenMultimedia = 'myLocalTokenMultimedia';
const keepLogin = true;
await session.destroy(); // this clears token and user for any other test
await session.login({
token: expectedToken,
tokenMultimedia: expectedTokenMultimedia,
keepLogin,
});
},
{},
);
const roles = state.getRoles();
const localToken = localStorage.getItem('token');
const sessionToken = sessionStorage.getItem('token');
expect(roles.value).toEqual(expectedRoles);
expect(localToken).toEqual(expectedToken);
expect(sessionToken).toBeNull();
await session.destroy(); // this clears token and user for any other test
});
});
describe('RenewToken', () => {
const expectedToken = 'myToken';

View File

@ -23,7 +23,7 @@ const claimDms = ref([
]);
const client = ref({});
const inputFile = ref();
const files = ref({});
const files = ref([]);
const spinnerRef = ref();
const claimDmsRef = ref();
const dmsType = ref({});
@ -255,9 +255,8 @@ function onDrag() {
icon="add"
color="primary"
>
<QInput
<QFile
ref="inputFile"
type="file"
style="display: none"
multiple
v-model="files"

View File

@ -52,7 +52,7 @@ describe('ClaimLines', () => {
expectedData,
{
signal: canceller.signal,
}
},
);
});
});
@ -69,7 +69,7 @@ describe('ClaimLines', () => {
expect.objectContaining({
message: 'Discount updated',
type: 'positive',
})
}),
);
});
});

View File

@ -14,6 +14,9 @@ describe('ClaimLinesImport', () => {
fetch: vi.fn(),
},
},
propsData: {
ticketId: 1,
},
}).vm;
});
@ -40,7 +43,7 @@ describe('ClaimLinesImport', () => {
expect.objectContaining({
message: 'Lines added to claim',
type: 'positive',
})
}),
);
expect(vm.canceller).toEqual(null);
});

View File

@ -41,10 +41,10 @@ describe('ClaimPhoto', () => {
await vm.deleteDms({ index: 0 });
expect(axios.post).toHaveBeenCalledWith(
`ClaimDms/${claimMock.claimDms[0].dmsFk}/removeFile`
`ClaimDms/${claimMock.claimDms[0].dmsFk}/removeFile`,
);
expect(vm.quasar.notify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'positive' })
expect.objectContaining({ type: 'positive' }),
);
});
});
@ -63,7 +63,7 @@ describe('ClaimPhoto', () => {
data: { index: 1 },
promise: vm.deleteDms,
},
})
}),
);
});
});
@ -102,10 +102,10 @@ describe('ClaimPhoto', () => {
new FormData(),
expect.objectContaining({
params: expect.objectContaining({ hasFile: false }),
})
}),
);
expect(vm.quasar.notify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'positive' })
expect.objectContaining({ type: 'positive' }),
);
expect(vm.claimDmsRef.fetch).toHaveBeenCalledOnce();

View File

@ -39,7 +39,7 @@ const route = useRoute();
const { t } = useI18n();
const entityId = computed(() => {
return $props.id || route.params.id;
return Number($props.id || route.params.id);
});
const data = ref(useCardDescription());

View File

@ -86,7 +86,7 @@ async function acceptPropagate({ isEqualizated }) {
:required="true"
:rules="validate('client.socialName')"
clearable
uppercase="true"
:uppercase="true"
v-model="data.socialName"
>
<template #append>

View File

@ -25,7 +25,7 @@ const $props = defineProps({
},
});
const entityId = computed(() => $props.id || route.params.id);
const entityId = computed(() => Number($props.id || route.params.id));
const customer = computed(() => summary.value.entity);
const summary = ref();
const defaulterAmount = computed(() => customer.value.defaulters[0]?.amount);

View File

@ -72,7 +72,7 @@ const exprBuilder = (param, value) => {
option-value="id"
option-label="name"
url="Departments"
no-one="true"
:no-one="true"
/>
</QItemSection>
</QItem>

View File

@ -32,7 +32,7 @@ describe('CustomerPayments', () => {
expect.objectContaining({
message: 'Payment confirmed',
type: 'positive',
})
}),
);
});
});

View File

@ -5,7 +5,7 @@ import InvoiceInSummary from './InvoiceInSummary.vue';
const $props = defineProps({
id: {
type: Number,
required: true,
default: null,
Review

Deberia ser requirod un descriptor sin id no debe funcionar

Deberia ser requirod un descriptor sin id no debe funcionar
Review

El tema está en que si la carga es dinámica al principio se pasa undefined, hasta que carga y muestra un warning tipo: "Expected number but got undefined

El tema está en que si la carga es dinámica al principio se pasa undefined, hasta que carga y muestra un warning tipo: "Expected number but got undefined
Review

Un v-if diria que es mas correcto @jorgep

Un v-if diria que es mas correcto @jorgep
Review

Tiene v-if

Tiene v-if
},
});
</script>

View File

@ -120,7 +120,6 @@ watch(
:data-key="dataKey"
:tag-value="tagValue"
:tags="tags"
:initial-catalog-params="catalogParams"
:arrayData
/>
</template>

View File

@ -27,7 +27,7 @@ const getTotalRef = ref();
const total = ref(0);
const entityId = computed(() => {
return $props.id || route.params.id;
return Number($props.id || route.params.id);
});
const orderTotal = computed(() => state.get('orderTotal') ?? 0);

View File

@ -80,7 +80,7 @@ const columns = computed(() => [
<VnTable
:data-key="dataKey"
:columns="columns"
is-editable="false"
:is-editable="false"
:right-search="false"
:use-model="true"
:disable-option="{ table: true }"

View File

@ -90,7 +90,7 @@ const onDataSaved = ({ id }) => {
<VnTable
:data-key="dataKey"
:columns="columns"
is-editable="false"
:is-editable="false"
:right-search="false"
:use-model="true"
:disable-option="{ table: true }"

View File

@ -65,15 +65,13 @@ function findBankFk(value, row) {
if (bankEntityFk) row.bankEntityFk = bankEntityFk.id;
}
function bankEntityFilter(val, update) {
update(() => {
const needle = val.toLowerCase();
filteredBankEntitiesOptions.value = bankEntitiesOptions.value.filter(
(bank) =>
bank.bic.toLowerCase().startsWith(needle) ||
bank.name.toLowerCase().includes(needle),
);
});
function bankEntityFilter(val) {
const needle = val.toLowerCase();
filteredBankEntitiesOptions.value = bankEntitiesOptions.value.filter(
(bank) =>
bank.bic.toLowerCase().startsWith(needle) ||
bank.name.toLowerCase().includes(needle),
);
}
</script>
<template>
@ -82,7 +80,8 @@ function bankEntityFilter(val, update) {
url="BankEntities"
@on-fetch="
(data) => {
(bankEntitiesOptions = data), (filteredBankEntitiesOptions = data);
bankEntitiesOptions = data;
filteredBankEntitiesOptions = data;
}
"
auto-load
@ -135,10 +134,8 @@ function bankEntityFilter(val, update) {
:label="t('worker.create.bankEntity')"
v-model="row.bankEntityFk"
:options="filteredBankEntitiesOptions"
:default-filter="false"
@filter="(val, update) => bankEntityFilter(val, update)"
:filter-fn="bankEntityFilter"
option-label="bic"
option-value="id"
hide-selected
:required="true"
:roles-allowed-to-create="['financial']"

View File

@ -5,7 +5,7 @@ import SupplierSummary from './SupplierSummary.vue';
const $props = defineProps({
id: {
type: Number,
required: true,
default: null,
Review

=

=
Review

El tema está en que si la carga es dinámica al principio se pasa undefined, hasta que carga y muestra un warning tipo: "Expected number but got undefined

El tema está en que si la carga es dinámica al principio se pasa undefined, hasta que carga y muestra un warning tipo: "Expected number but got undefined
Review

Un v-if diria que es mas correcto @jorgep

Un v-if diria que es mas correcto @jorgep
Review

tiene v-if

tiene v-if
},
});
</script>

View File

@ -27,7 +27,7 @@ const entityId = computed(() => {
<template>
<EntityDescriptor :url="`Zones/${entityId}`" :filter="filter" data-key="Zone">
<template #menu="{ entity }">
<ZoneDescriptorMenuItems :zone="entity" />
<ZoneDescriptorMenuItems />
</template>
<template #body="{ entity }">
<VnLv :label="$t('list.agency')" :value="entity.agencyMode?.name" />

View File

@ -93,6 +93,7 @@ const columns = computed(() => [
optionLabel: 'name',
optionValue: 'id',
},
columnClass: 'expand',
},
{
align: 'left',
@ -247,74 +248,70 @@ const closeEventForm = () => {
</QBtnGroup>
</template>
</VnSubToolbar>
<div class="table-container">
<div class="column items-center">
<VnTable
ref="tableRef"
:data-key="dataKey"
:columns="columns"
redirect="Zone"
:create="{
urlCreate: 'Zones',
title: t('list.createZone'),
onDataSaved: ({ id }) => tableRef.redirect(`${id}/location`),
formInitialData: {},
}"
table-height="85vh"
v-model:selected="selectedRows"
:table="{
'row-key': 'id',
selection: 'multiple',
}"
>
<template #column-addressFk="{ row }">
{{ dashIfEmpty(formatRow(row)) }}
</template>
<template #more-create-dialog="{ data }">
<VnSelect
url="AgencyModes"
v-model="data.agencyModeFk"
option-value="id"
option-label="name"
:label="t('list.agency')"
/>
<VnInput
v-model="data.price"
:label="t('list.price')"
min="0"
type="number"
required="true"
/>
<VnInput
v-model="data.bonus"
:label="t('zone.bonus')"
min="0"
type="number"
/>
<VnInput
v-model="data.travelingDays"
:label="t('zone.travelingDays')"
type="number"
min="0"
/>
<VnInputTime v-model="data.hour" :label="t('list.close')" />
<VnSelect
url="Warehouses"
v-model="data.warehouseFK"
option-value="id"
option-label="name"
:label="t('list.warehouse')"
:options="warehouseOptions"
/>
<QCheckbox
v-model="data.isVolumetric"
:label="t('list.isVolumetric')"
:toggle-indeterminate="false"
/>
</template>
</VnTable>
</div>
</div>
<VnTable
ref="tableRef"
:data-key="dataKey"
:columns="columns"
redirect="Zone"
:create="{
urlCreate: 'Zones',
title: t('list.createZone'),
onDataSaved: ({ id }) => tableRef.redirect(`${id}/location`),
formInitialData: {},
}"
table-height="85vh"
v-model:selected="selectedRows"
:table="{
'row-key': 'id',
selection: 'multiple',
}"
>
<template #column-addressFk="{ row }">
{{ dashIfEmpty(formatRow(row)) }}
</template>
<template #more-create-dialog="{ data }">
<VnSelect
url="AgencyModes"
v-model="data.agencyModeFk"
option-value="id"
option-label="name"
:label="t('list.agency')"
/>
<VnInput
v-model="data.price"
:label="t('list.price')"
min="0"
type="number"
required="true"
/>
<VnInput
v-model="data.bonus"
:label="t('zone.bonus')"
min="0"
type="number"
/>
<VnInput
v-model="data.travelingDays"
:label="t('zone.travelingDays')"
type="number"
min="0"
/>
<VnInputTime v-model="data.hour" :label="t('list.close')" />
<VnSelect
url="Warehouses"
v-model="data.warehouseFK"
option-value="id"
option-label="name"
:label="t('list.warehouse')"
:options="warehouseOptions"
/>
<QCheckbox
v-model="data.isVolumetric"
:label="t('list.isVolumetric')"
:toggle-indeterminate="false"
/>
</template>
</VnTable>
</template>
</VnSection>
<QDialog v-model="showZoneEventForm" @hide="closeEventForm()">
@ -333,24 +330,6 @@ const closeEventForm = () => {
/>
</QDialog>
</template>
<style lang="scss" scoped>
.table-container {
display: flex;
justify-content: center;
}
.column {
display: flex;
flex-direction: column;
align-items: center;
min-width: 70%;
}
:deep(.shrink-column) {
width: 8%;
}
</style>
<i18n>
es:
Search zone: Buscar zona

View File

@ -1,22 +1,23 @@
import { describe, expect, it, beforeEach, beforeAll } from 'vitest';
import { createWrapper } from 'app/test/vitest/helper';
import { setActivePinia, createPinia } from 'pinia';
import { useStateQueryStore } from 'src/stores/useStateQueryStore';
describe('useStateQueryStore', () => {
beforeAll(() => {
createWrapper({}, {});
});
const stateQueryStore = useStateQueryStore();
const { add, isLoading, remove, reset } = useStateQueryStore();
let stateQueryStore;
let add, isLoading, remove, reset;
const firstQuery = { url: 'myQuery' };
function getQueries() {
return stateQueryStore.queries;
}
beforeAll(() => {
setActivePinia(createPinia());
});
beforeEach(() => {
stateQueryStore = useStateQueryStore();
({ add, isLoading, remove, reset } = useStateQueryStore());
reset();
expect(getQueries().size).toBeFalsy();
});

View File

@ -1,5 +1,5 @@
/// <reference types="cypress" />
describe.skip('ClaimAction', () => {
describe('ClaimAction', () => {
const claimId = 1;
const firstRow = 'tbody > :nth-child(1)';
@ -15,13 +15,13 @@ describe.skip('ClaimAction', () => {
});
// https://redmine.verdnatura.es/issues/8756
xit('should change destination', () => {
it.skip('should change destination', () => {
const rowData = [true, null, null, 'Bueno'];
cy.fillRow(firstRow, rowData);
});
// https://redmine.verdnatura.es/issues/8756
xit('should change destination from other button', () => {
it.skip('should change destination from other button', () => {
const rowData = [true];
cy.fillRow(firstRow, rowData);
@ -35,7 +35,7 @@ describe.skip('ClaimAction', () => {
});
// https://redmine.verdnatura.es/issues/8756
xit('should remove the line', () => {
it.skip('should remove the line', () => {
cy.fillRow(firstRow, [true]);
cy.removeCard();
cy.clickConfirm();

View File

@ -1,5 +1,5 @@
/// <reference types="cypress" />
describe.skip('ClaimDevelopment', () => {
describe('ClaimDevelopment', () => {
const claimId = 1;
const firstLineReason = 'tbody > :nth-child(1) > :nth-child(2)';
const thirdRow = 'tbody > :nth-child(3)';

View File

@ -1,5 +1,5 @@
/// <reference types="cypress" />
describe.skip('Client list', () => {
describe('Client list', () => {
beforeEach(() => {
cy.login('developer');
cy.visit('/#/customer/list', {

View File

@ -7,7 +7,7 @@ Cypress.Commands.add('selectTravel', (warehouse = '1') => {
});
Cypress.Commands.add('deleteEntry', () => {
cy.get('[data-cy="descriptor-more-opts"]').should('be.visible').click();
cy.dataCy('descriptor-more-opts').should('be.visible').click();
cy.waitForElement('div[data-cy="delete-entry"]').click();
});

View File

@ -1,6 +1,6 @@
import '../commands.js';
describe('EntryBasicData', () => {
describe.skip('EntryBasicData', () => {
beforeEach(() => {
cy.login('buyer');
cy.visit(`/#/entry/list`);

View File

@ -84,7 +84,7 @@ describe('InvoiceInDescriptor', () => {
beforeEach(() => cy.visit(`/#/invoice-in/${originalId}/summary`));
it('should create a correcting invoice and redirect to original invoice', () => {
it.skip('should create a correcting invoice and redirect to original invoice', () => {
createCorrective();
redirect(originalId);
});

View File

@ -31,7 +31,7 @@ describe('InvoiceOut list', () => {
it.skip('should open the invoice descriptor from table icon', () => {
cy.get(firstSummaryIcon).click();
cy.get('.cardSummary').should('be.visible');
cy.get('.summaryHeader > div').should('include.text', 'A1111111');
cy.get('.summaryHeader > div').should('include.text', 'V10100001');
});
it('should open the client descriptor', () => {

View File

@ -1,6 +1,6 @@
/// <reference types="cypress" />
describe.skip('Item list', () => {
describe('Item list', () => {
beforeEach(() => {
cy.login('developer');
cy.visit(`/#/item/list`);

View File

@ -1,5 +1,5 @@
/// <reference types="cypress" />
describe.skip('Logout', () => {
describe('Logout', () => {
beforeEach(() => {
cy.login('developer');
cy.visit(`/#/dashboard`);

View File

@ -34,7 +34,7 @@ describe('OrderCatalog', () => {
searchByCustomTagInput('Silver');
});
it.skip('filters by custom value dialog', () => {
it('filters by custom value dialog', () => {
Cypress.on('uncaught:exception', (err) => {
if (err.message.includes('canceled')) {
return false;

View File

@ -1,4 +1,4 @@
describe.skip('RouteAutonomous', () => {
describe('RouteAutonomous', () => {
const getLinkSelector = (colField) =>
`tr:first-child > [data-col-field="${colField}"] > .no-padding > .link`;
@ -44,7 +44,7 @@ describe.skip('RouteAutonomous', () => {
.should('have.length.greaterThan', 0);
});
it('Should create invoice in to selected route', () => {
it.skip('Should create invoice in to selected route', () => {
cy.get(selectors.firstRowCheckbox).click();
cy.get(selectors.createInvoiceBtn).click();
cy.dataCy(selectors.reference).type(data.reference);
@ -68,7 +68,7 @@ describe.skip('RouteAutonomous', () => {
cy.url().should('include', summaryUrl);
});
describe('Received pop-ups', () => {
describe.skip('Received pop-ups', () => {
it('Should redirect to invoice in summary from the received descriptor pop-up', () => {
cy.get(selectors.received).click();
cy.validateContent(selectors.descriptorTitle, data.reference);

View File

@ -1,6 +1,6 @@
/// <reference types="cypress" />
// https://redmine.verdnatura.es/issues/8848
describe.skip('VnShortcuts', () => {
describe('VnShortcuts', () => {
const modules = {
item: 'a',
customer: 'c',

View File

@ -1,4 +1,4 @@
describe.skip('WorkerBusiness', () => {
describe('WorkerBusiness', () => {
const saveBtn = '.q-mt-lg > .q-btn--standard';
const contributionCode = `Representantes de comercio`;
const contractType = `INDEFINIDO A TIEMPO COMPLETO`;

View File

@ -4,6 +4,7 @@ import { createTestingPinia } from '@pinia/testing';
import { vi } from 'vitest';
import { i18n } from 'src/boot/i18n';
import { Notify, Dialog } from 'quasar';
import keyShortcut from 'src/boot/keyShortcut';
import * as useValidator from 'src/composables/useValidator';
installQuasarPlugin({
@ -16,6 +17,26 @@ const pinia = createTestingPinia({ createSpy: vi.fn, stubActions: false });
const mockPush = vi.fn();
const mockReplace = vi.fn();
vi.mock('vue', async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
inject: vi.fn((key) => {
if (key === 'app') {
return {};
}
return actual.inject(key);
}),
onMounted: vi.fn((fn) => (fn && typeof fn === 'function' ? fn() : undefined)),
onBeforeMount: vi.fn((fn) => (fn && typeof fn === 'function' ? fn() : undefined)),
onUpdated: vi.fn((fn) => (fn && typeof fn === 'function' ? fn() : undefined)),
onUnmounted: vi.fn((fn) => (fn && typeof fn === 'function' ? fn() : undefined)),
onBeforeUnmount: vi.fn((fn) =>
fn && typeof fn === 'function' ? fn() : undefined,
),
};
});
vi.mock('vue-router', () => ({
useRouter: () => ({
push: mockPush,
@ -87,6 +108,10 @@ export function createWrapper(component, options) {
const defaultOptions = {
global: {
plugins: [i18n, pinia],
directives: {
shortcut: keyShortcut,
},
stubs: ['useState', 'arrayData', 'useStateStore', 'vue-i18n', 'RouterLink'],
},
mocks: {
t: (tKey) => tKey,
@ -94,15 +119,11 @@ export function createWrapper(component, options) {
},
};
const mountOptions = Object.assign({}, defaultOptions);
if (options instanceof Object) {
Object.assign(mountOptions, options);
if (options.global) {
mountOptions.global.plugins = defaultOptions.global.plugins;
}
}
const mountOptions = {
...defaultOptions,
...options,
global: { ...defaultOptions.global, ...options?.global },
};
const wrapper = mount(component, mountOptions);
const vm = wrapper.vm;

View File

@ -1,5 +1,26 @@
// This file will be run before each test file, don't delete or vitest will not work.
import { vi } from 'vitest';
import { afterAll, beforeAll, vi } from 'vitest';
let vueWarnings = [];
const originalConsoleWarn = console.warn;
beforeAll(() => {
console.warn = (...args) => {
vueWarnings.push(args.join(' '));
};
});
afterEach(() => {
if (vueWarnings.length > 0) {
const allWarnings = vueWarnings.join('\n');
vueWarnings = [];
throw new Error(`Vue warnings detected during test:\n${allWarnings}`);
}
});
afterAll(() => {
console.warn = originalConsoleWarn;
});
vi.mock('axios');
vi.mock('vue-router', () => ({