0
0
Fork 0

refs #5673 test(CrudModel): front and e2e

This commit is contained in:
Alex Moreno 2023-08-21 15:14:19 +02:00
parent 6c8329ec53
commit 98552bcd9e
11 changed files with 218 additions and 71 deletions

View File

@ -64,7 +64,7 @@ module.exports = {
}, },
overrides: [ overrides: [
{ {
files: ['test/cypress/**/*.spec.{js,ts}'], files: ['test/cypress/**/*.*'],
extends: [ extends: [
// Add Cypress-specific lint rules, globals and Cypress plugin // Add Cypress-specific lint rules, globals and Cypress plugin
// See https://github.com/cypress-io/eslint-plugin-cypress#rules // See https://github.com/cypress-io/eslint-plugin-cypress#rules

View File

@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

View File

@ -285,8 +285,8 @@ function isEmpty(obj) {
</template> </template>
</VnPaginate> </VnPaginate>
<SkeletonTable v-if="!formData" /> <SkeletonTable v-if="!formData" />
<Teleport to="#st-actions" v-if="stateStore.isSubToolbarShown()"> <Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
<QBtnGroup push class="q-gutter-x-sm"> <QBtnGroup push flat class="q-gutter-x-sm">
<slot name="moreActions" /> <slot name="moreActions" />
<QBtn <QBtn
:label="tMobile('globals.remove')" :label="tMobile('globals.remove')"

View File

@ -65,7 +65,7 @@ async function fetch() {
}); });
state.set($props.model, data); state.set($props.model, data);
originalData.value = Object.assign({}, data); originalData.value = data && JSON.parse(JSON.stringify(data));
watch(formData.value, () => (hasChanges.value = true)); watch(formData.value, () => (hasChanges.value = true));
@ -82,13 +82,14 @@ async function save() {
isLoading.value = true; isLoading.value = true;
await axios.patch($props.urlUpdate || $props.url, formData.value); await axios.patch($props.urlUpdate || $props.url, formData.value);
originalData.value = formData.value; originalData.value = JSON.parse(JSON.stringify(formData.value));
hasChanges.value = false; hasChanges.value = false;
isLoading.value = false; isLoading.value = false;
} }
function reset() { function reset() {
state.set($props.model, originalData.value); state.set($props.model, originalData.value);
watch(formData.value, () => (hasChanges.value = true));
hasChanges.value = false; hasChanges.value = false;
} }
// eslint-disable-next-line vue/no-dupe-keys // eslint-disable-next-line vue/no-dupe-keys
@ -120,7 +121,7 @@ watch(formUrl, async () => {
<QForm v-if="formData" @submit="save" @reset="reset" class="q-pa-md"> <QForm v-if="formData" @submit="save" @reset="reset" class="q-pa-md">
<slot name="form" :data="formData" :validate="validate" :filter="filter"></slot> <slot name="form" :data="formData" :validate="validate" :filter="filter"></slot>
</QForm> </QForm>
<Teleport to="#st-actions" v-if="stateStore.isSubToolbarShown()"> <Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
<div v-if="$props.defaultActions"> <div v-if="$props.defaultActions">
<QBtnGoup push class="q-gutter-x-sm"> <QBtnGoup push class="q-gutter-x-sm">
<slot name="moreActions" /> <slot name="moreActions" />

View File

@ -29,7 +29,6 @@ const claimSections = [
let salixUrl; let salixUrl;
onMounted(async () => { onMounted(async () => {
salixUrl = await getUrl(`claim/${entityId.value}`); salixUrl = await getUrl(`claim/${entityId.value}`);
stateStore.setSubtoolbar();
}); });
</script> </script>
<template> <template>
@ -65,13 +64,13 @@ onMounted(async () => {
</QScrollArea> </QScrollArea>
</QDrawer> </QDrawer>
<QPageContainer> <QPageContainer>
<QToolbar class="bg-vn-dark justify-end"> <QPage>
<div id="st-data"></div> <QToolbar class="bg-vn-dark justify-end">
<QSpace /> <div id="st-data"></div>
<div id="st-actions"></div> <QSpace />
</QToolbar> <div id="st-actions"></div>
<QPage class="q-pa-md"> </QToolbar>
<RouterView></RouterView> <div class="q-pa-md"><RouterView></RouterView></div>
</QPage> </QPage>
</QPageContainer> </QPageContainer>
</template> </template>

View File

@ -5,7 +5,6 @@ export const useStateStore = defineStore('stateStore', () => {
const isMounted = ref(false); const isMounted = ref(false);
const leftDrawer = ref(false); const leftDrawer = ref(false);
const rightDrawer = ref(false); const rightDrawer = ref(false);
const subToolbar = ref(false);
function toggleLeftDrawer() { function toggleLeftDrawer() {
leftDrawer.value = !leftDrawer.value; leftDrawer.value = !leftDrawer.value;
@ -19,10 +18,6 @@ export const useStateStore = defineStore('stateStore', () => {
isMounted.value = true; isMounted.value = true;
} }
function setSubtoolbar() {
subToolbar.value = true;
}
function isHeaderMounted() { function isHeaderMounted() {
return isMounted.value; return isMounted.value;
} }
@ -36,7 +31,10 @@ export const useStateStore = defineStore('stateStore', () => {
} }
function isSubToolbarShown() { function isSubToolbarShown() {
return subToolbar.value; return (
!!document.querySelector('#st-data') &&
!!document.querySelector('#st-actions')
);
} }
return { return {
@ -48,7 +46,6 @@ export const useStateStore = defineStore('stateStore', () => {
toggleRightDrawer, toggleRightDrawer,
isLeftDrawerShown, isLeftDrawerShown,
isRightDrawerShown, isRightDrawerShown,
setSubtoolbar,
isSubToolbarShown, isSubToolbarShown,
}; };
}); });

View File

@ -1,55 +1,77 @@
/// <reference types="cypress" /> /// <reference types="cypress" />
describe('ClaimPhoto', () => { describe('ClaimDevelopment', () => {
const claimId = 1;
beforeEach(() => { beforeEach(() => {
const claimId = 1; cy.viewport(1920, 1080);
cy.login('developer'); cy.login('developer');
cy.visit(`/#/claim/${claimId}/photos`); cy.visit(`/#/claim/${claimId}/development`);
}); });
it('should add new file', () => { it('should reset line', () => {
cy.get('label > .q-btn').click(); cy.get('tbody > :nth-child(1) > :nth-child(2)').click();
cy.get('label > .q-btn input').selectFile('test/cypress/fixtures/image.jpg', { cy.selectOption('Novato');
force: true, cy.get('[title="Reset"]').click();
});
cy.get('.q-notification__message').should('have.text', 'Data saved');
});
it('should add new file with drag and drop', () => {
cy.get('.container').selectFile('test/cypress/fixtures/image.jpg', {
action: 'drag-drop',
});
cy.get('.q-notification__message').should('have.text', 'Data saved');
});
it('should open first image dialog change to second and close', () => {
cy.get( cy.get(
':nth-child(1) > .q-card > .q-img > .q-img__container > .q-img__image' ':nth-child(1) > :nth-child(2) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native > span'
).click(); ).should('have.text', 'Prisas');
cy.get('.q-carousel__slide > .q-img > .q-img__container > .q-img__image').should(
'be.visible'
);
cy.get('.q-carousel__control > .q-btn > .q-btn__content > .q-icon').click();
cy.get(
'.q-dialog__inner > .q-toolbar > .q-btn > .q-btn__content > .q-icon'
).click();
cy.get('.q-carousel__slide > .q-img > .q-img__container > .q-img__image').should(
'not.be.visible'
);
}); });
it('should remove third and fourth file', () => { it('should edit line', () => {
cy.get( cy.get('tbody > :nth-child(1) > :nth-child(2)').click();
'.multimediaParent > :nth-child(3) > .q-btn > .q-btn__content > .q-icon' cy.selectOption('Novato');
).click(); cy.get('[title="Save"]').click();
cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
cy.get('.q-notification__message').should('have.text', 'Data deleted');
cy.visit(`/#/claim/${claimId}/development`);
cy.get( cy.get(
'.multimediaParent > :nth-child(3) > .q-btn > .q-btn__content > .q-icon' ':nth-child(1) > :nth-child(2) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native > span'
).click(); ).should('have.text', 'Novato');
cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
cy.get('.q-notification__message').should('have.text', 'Data deleted'); //Restart data
cy.get('tbody > :nth-child(1) > :nth-child(2)');
cy.selectOption('Prisas');
cy.get('[title="Save"]').click();
}); });
it('should add new line', () => {
//check third if row exist
cy.get('.q-page-sticky > div > .q-btn').click();
cy.get('tbody > :nth-child(3)').should('exist');
//fill in data
const rowData = ['', '', ''];
cy.fillTableRow(3, rowData);
});
// it('should remove last line', () => {
// cy.get(
// ':nth-child(1) > .q-card > .q-img > .q-img__container > .q-img__image'
// ).click();
// cy.get('.q-carousel__slide > .q-img > .q-img__container > .q-img__image').should(
// 'be.visible'
// );
// cy.get('.q-carousel__control > .q-btn > .q-btn__content > .q-icon').click();
// cy.get(
// '.q-dialog__inner > .q-toolbar > .q-btn > .q-btn__content > .q-icon'
// ).click();
// cy.get('.q-carousel__slide > .q-img > .q-img__container > .q-img__image').should(
// 'not.be.visible'
// );
// });
// it('should remove third and fourth file', () => {
// cy.get(
// '.multimediaParent > :nth-child(3) > .q-btn > .q-btn__content > .q-icon'
// ).click();
// cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
// cy.get('.q-notification__message').should('have.text', 'Data deleted');
// cy.get(
// '.multimediaParent > :nth-child(3) > .q-btn > .q-btn__content > .q-icon'
// ).click();
// cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
// cy.get('.q-notification__message').should('have.text', 'Data deleted');
// });
}); });

View File

@ -40,4 +40,71 @@ Cypress.Commands.add('login', (user) => {
window.localStorage.setItem('token', response.body.token); window.localStorage.setItem('token', response.body.token);
}); });
}); });
Cypress.Commands.add('selectOption', (option) => {
//cy.visit('/#/login');
cy.get('.q-item__label').then(() => {
cy.contains('.q-item__label', option).click();
});
});
// Cypress.Commands.add('fillRow', (row, options) => {
// //cy.visit('/#/login');
// for (let [i, option] of options.entries()) {
// i++;
// console.log(i);
// const selector = `tbody > :nth-child(${row}) > :nth-child(${i})`;
// if (cy.get(selector).should('have.class', 'q-select'))
// cy.selectOption(selector, option);
// }
// });
Cypress.Commands.add('fillTableRow', (rowNumber, data) => {
// Obtener todas las filas de la tabla
cy.get('table tbody tr').eq(rowNumber).as('currentRow');
// Iterar sobre cada dato en el array 'data'
data.forEach((value, index) => {
// Basándonos en el índice, encontramos la celda correspondiente y verificamos el tipo de input
cy.get('@currentRow')
.find('td')
.eq(index)
.find('input')
.invoke('attr', 'type')
.then((type) => {
switch (type) {
case 'text':
cy.get('@currentRow')
.find('td')
.eq(index)
.find('input[type="text"]')
.clear()
.type(value);
break;
case 'checkbox':
if (value) {
// Puede adaptar esto según cómo represente los valores booleanos en su array 'data'
cy.get('@currentRow')
.find('td')
.eq(index)
.find('input[type="checkbox"]')
.check();
} else {
cy.get('@currentRow')
.find('td')
.eq(index)
.find('input[type="checkbox"]')
.uncheck();
}
break;
// ... Puede agregar más casos para otros tipos de inputs según sea necesario
default:
// Manejar cualquier otro tipo de input o agregar lógica de error aquí si es necesario
break;
}
});
});
});
// registerCommands(); // registerCommands();

View File

@ -1,13 +1,16 @@
import { createWrapper } from 'app/test/vitest/helper'; import { createWrapper, axios } from 'app/test/vitest/helper';
import CrudModel from 'components/CrudModel.vue'; import CrudModel from 'components/CrudModel.vue';
import { vi, afterEach, beforeAll, describe, expect, it } from 'vitest'; import { vi, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
describe.only('CrudModel', () => { describe.only('CrudModel', () => {
let vm; let vm;
beforeAll(() => { beforeAll(() => {
vm = createWrapper(CrudModel, { vm = createWrapper(CrudModel, {
global: { global: {
stubs: ['VnPaginate', 'useState'], stubs: ['vnPaginate', 'useState', 'arrayData', 'useStateStore'],
mocks: {
fetch: vi.fn(),
},
}, },
propsData: { propsData: {
dataRequired: { dataRequired: {
@ -15,6 +18,15 @@ describe.only('CrudModel', () => {
name: 'name', name: 'name',
autoLoad: true, autoLoad: true,
}, },
dataKey: 'crudModelKey',
model: 'crudModel',
url: 'crudModelUrl',
},
attrs: {
url: 'crudModelUrl',
dataKey: 'CustomerList',
order: 'id DESC',
limit: 3,
}, },
}).vm; }).vm;
}); });
@ -24,12 +36,26 @@ describe.only('CrudModel', () => {
}); });
describe('insert()', () => { describe('insert()', () => {
it('should new element in list', () => { it('should new element in list with index 0 if formData not has data', () => {
vi.mock('src/composables/useValidator', () => ({
default: () => {},
fetch: () => {
vi.fn();
},
}));
vi.spyOn(axios, 'get').mockResolvedValue({
data: [
{ id: 1, name: 'Tony Stark' },
{ id: 2, name: 'Jessica Jones' },
{ id: 3, name: 'Bruce Wayne' },
],
});
vm.state.set('crudModel', []);
vm.insert(); vm.insert();
expect(vm.message).toEqual( expect(vm.formData.length).toEqual(1);
`A minimum amount of 50€ (VAT excluded) is required for your order ${orderId} of ${shipped} to receive it without additional shipping costs.` expect(vm.formData[0].id).toEqual(1);
); expect(vm.formData[0].$index).toEqual(0);
}); });
}); });
}); });

View File

@ -64,6 +64,10 @@ export function createWrapper(component, options) {
global: { global: {
plugins: [i18n, pinia], plugins: [i18n, pinia],
}, },
mocks: {
t: (tKey) => tKey,
$t: (tKey) => tKey,
},
}; };
const mountOptions = Object.assign({}, defaultOptions); const mountOptions = Object.assign({}, defaultOptions);
@ -75,6 +79,7 @@ export function createWrapper(component, options) {
mountOptions.global.plugins = defaultOptions.global.plugins; mountOptions.global.plugins = defaultOptions.global.plugins;
} }
} }
console.log(mountOptions);
const wrapper = mount(component, mountOptions); const wrapper = mount(component, mountOptions);
const vm = wrapper.vm; const vm = wrapper.vm;