test: refs #7220 #7220 improve more tests

This commit is contained in:
Javier Segarra 2024-11-11 09:08:55 +01:00
parent 694c5494ef
commit 4cb4ad9532
39 changed files with 2432 additions and 139 deletions

View File

@ -1,8 +1,80 @@
import UserPanel from 'src/components/UserPanel.vue';
// UserPanel.spec.js
import UserPanel from 'src/components/common/UserPanel.vue';
import { ref } from 'vue';
describe.skip('<UserPanel />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<UserPanel />', () => {
beforeEach(() => {
// Mock composables and dependencies
cy.stub(window, 'useI18n').returns({
t: (key) => key,
locale: 'en',
});
cy.stub(window, 'useRouter').returns({});
cy.stub(window, 'useState').returns({});
cy.stub(window, 'useSession').returns({});
cy.stub(window, 'useClipboard').returns({
copyText: cy.stub().as('copyTextStub'),
});
cy.stub(window, 'useRole').returns({});
cy.stub(window, 'useNotify').returns({
notify: cy.stub().as('notifyStub'),
});
cy.stub(window, 'axios').as('axiosStub');
});
it('renders user panel components', () => {
cy.createWrapper(UserPanel);
cy.getComponent('VnSelect').should('exist');
cy.getComponent('VnRow').should('exist');
cy.getComponent('FetchData').should('exist');
cy.getComponent('VnAvatar').should('exist');
});
it('displays user locale correctly', () => {
cy.createWrapper(UserPanel);
cy.wrap(Cypress.vueWrapper.vm.userLocale).should('equal', 'en');
});
it('updates locale when userLocale is changed', () => {
cy.createWrapper(UserPanel);
cy.wrap(Cypress.vueWrapper.vm.userLocale).set('fr');
cy.wrap(Cypress.vueWrapper.vm.locale).should('equal', 'fr');
});
it('copies text to clipboard', () => {
cy.createWrapper(UserPanel);
cy.get('.copy-button').click();
cy.get('@copyTextStub').should('have.been.called');
});
it('notifies user on copy', () => {
cy.createWrapper(UserPanel);
cy.get('.copy-button').click();
cy.get('@notifyStub').should('have.been.called');
});
it('fetches user data on mount', () => {
cy.createWrapper(UserPanel);
cy.get('@axiosStub').should('have.been.called');
});
it('renders user avatar', () => {
cy.createWrapper(UserPanel);
cy.getComponent('VnAvatar').should('exist');
});
it('handles dark mode toggle', () => {
cy.createWrapper(UserPanel);
cy.get('.dark-mode-toggle').click();
cy.wrap(Cypress.vueWrapper.vm.$q.dark.isActive).should('be.true');
});
it('handles logout action', () => {
const routerPush = cy.spy().as('routerPush');
cy.stub(window, 'useRouter').returns({ push: routerPush });
cy.createWrapper(UserPanel);
cy.get('.logout-button').click();
cy.get('@routerPush').should('have.been.calledWith', '/login');
});
});

View File

@ -1,8 +1,60 @@
// RightMenu.spec.js
import RightMenu from 'src/components/common/RightMenu.vue';
describe.skip('<RightMenu />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<RightMenu />', () => {
beforeEach(() => {
// Mock stores and composables
cy.stub(window, 'useStateStore').returns({
rightDrawer: false,
isHeaderMounted: () => true,
});
cy.stub(window, 'useI18n').returns({ t: (key) => key });
});
it('renders correctly', () => {
cy.createWrapper(RightMenu);
cy.get('#actions-append').should('exist');
});
it('observes right panel content changes', () => {
cy.createWrapper(RightMenu);
const rightPanel = document.createElement('div');
rightPanel.id = 'right-panel';
document.body.appendChild(rightPanel);
const newChild = document.createElement('div');
rightPanel.appendChild(newChild);
cy.wrap(Cypress.vueWrapper.vm.hasContent).should('equal', 1);
rightPanel.remove();
});
it('hides right drawer when no content and no slot', () => {
cy.createWrapper(RightMenu);
cy.wrap(Cypress.vueWrapper.vm.stateStore.rightDrawer).should('be.false');
});
it('shows right drawer when slot content is provided', () => {
cy.createWrapper(RightMenu, {
slots: {
'right-panel': '<div>Slot Content</div>',
},
});
cy.wrap(Cypress.vueWrapper.vm.stateStore.rightDrawer).should('be.true');
});
it('teleports content to #actions-append when header is mounted', () => {
cy.createWrapper(RightMenu);
cy.get('#actions-append').should('exist');
});
it('does not teleport content when header is not mounted', () => {
cy.stub(window, 'useStateStore').returns({
rightDrawer: false,
isHeaderMounted: () => false,
});
cy.createWrapper(RightMenu);
cy.get('#actions-append').should('not.exist');
});
});

View File

@ -1,9 +1,7 @@
import VnAccountNumber from 'src/components/common/VnAccountNumber.vue';
import { Quasar, QInput } from 'quasar';
describe('<VnAccountNumber />', () => {
// VNAccountNumber.spec.js
describe.only('<VnAccountNumber />', () => {
const mountComponent = (props = {}) => {
cy.createWeapper(VnAccountNumber, {
global: {

View File

@ -67,9 +67,9 @@ describe('<VnBreadcrumbs />', () => {
router.push('/nometa');
router.isReady().then(() => {
mount(VnBreadcrumbs, {
cy.createWrapper(VnBreadcrumbs, {
global: {
plugins: [router, Quasar],
plugins: [router],
},
});
cy.get('.breadcrumbs').should('not.exist');

View File

@ -1,8 +1,89 @@
import VnCard from 'src/components/common/VnCard.vue';
describe.skip('<VnCard />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnCard);
describe.only('<VnCard />', () => {
beforeEach(() => {
// Montar el componente antes de cada prueba con valores predeterminados
cy.mount(VnCard, {
props: {
element: { name: 'Item' },
id: 1,
isSelected: false,
title: 'Card Title',
showCheckbox: true,
hasInfoIcons: true,
},
});
});
it('should display title and ID chip', () => {
// Verificar que el título esté presente
cy.contains('Card Title').should('be.visible');
// Verificar que la etiqueta ID esté presente y visible
cy.contains('ID: 1').should('be.visible');
});
it('should display checkbox when showCheckbox is true', () => {
// Verificar que el checkbox esté visible cuando showCheckbox es verdadero
cy.get('.q-checkbox').should('exist');
});
it('should emit toggleCardCheck event when checkbox is clicked', () => {
// Espiar el evento `toggleCardCheck`
const onToggleCardCheck = cy.spy().as('toggleCardCheckEvent');
cy.mount({
props: {
element: { name: 'Item' },
showCheckbox: true,
},
listeners: {
toggleCardCheck: onToggleCardCheck,
},
});
// Hacer clic en el checkbox
cy.get('.q-checkbox').click();
// Asegurarse de que el evento `toggleCardCheck` se emitió
cy.get('@toggleCardCheckEvent').should('have.been.calledOnce');
});
it('should display info-icons slot content when hasInfoIcons is true', () => {
// Montar el componente con el slot info-icons
cy.mount({
props: {
hasInfoIcons: true,
},
slots: {
'info-icons': `<div class="info-icon-slot">Info Icon</div>`,
},
});
// Verificar que el contenido del slot `info-icons` esté visible
cy.get('.info-icon-slot').should('be.visible');
});
it('should display list-items slot content', () => {
// Montar el componente con el slot `list-items`
cy.mount({
slots: {
'list-items': `<div class="list-item-slot">List Item</div>`,
},
});
// Verificar que el contenido del slot `list-items` esté visible
cy.get('.list-item-slot').should('be.visible');
});
it('should display actions slot content', () => {
// Montar el componente con el slot `actions`
cy.mount({
slots: {
actions: `<button class="action-button">Action</button>`,
},
});
// Verificar que el contenido del slot `actions` esté visible
cy.get('.action-button').should('be.visible');
});
});

View File

@ -1,8 +1,107 @@
// VnComponent.spec.js
import VnComponent from 'src/components/common/VnComponent.vue';
describe.skip('<VnComponent />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnComponent);
describe.only('<VnComponent />', () => {
const basicProp = {
component: 'TestComponent',
attrs: { test: 'value' },
};
const basicComponents = {
TestComponent: {
component: 'CustomTestComponent',
attrs: { customAttr: 'customValue' },
},
};
it('renders single prop component', () => {
cy.createWrapper(VnComponent, {
props: {
prop: basicProp,
modelValue: 'test',
},
});
cy.get('TestComponent').should('exist');
});
it('renders array of prop components', () => {
cy.createWrapper(VnComponent, {
props: {
prop: [basicProp, { ...basicProp, component: 'SecondComponent' }],
modelValue: 'test',
},
});
cy.get('TestComponent').should('exist');
cy.get('SecondComponent').should('exist');
});
it('mixes component attributes correctly', () => {
cy.createWrapper(VnComponent, {
props: {
prop: basicProp,
components: basicComponents,
modelValue: 'test',
},
});
cy.getComponent('CustomTestComponent').invoke('props').should('deep.include', {
test: 'value',
customAttr: 'customValue',
});
});
it('handles event definitions', () => {
const eventProp = {
...basicProp,
event: 'custom-event',
};
const onCustomEvent = cy.spy().as('eventSpy');
cy.createWrapper(VnComponent, {
props: {
prop: eventProp,
modelValue: 'test',
onCustomEvent,
},
});
cy.getComponent('TestComponent').trigger('custom-event');
cy.get('@eventSpy').should('have.been.called');
});
it('applies forced attributes from components', () => {
const componentsWithForced = {
TestComponent: {
...basicComponents.TestComponent,
forceAttrs: { forcedAttr: 'forced' },
},
};
cy.createWrapper(VnComponent, {
props: {
prop: basicProp,
components: componentsWithForced,
modelValue: 'test',
},
});
cy.getComponent('CustomTestComponent')
.invoke('props')
.should('deep.include', { forcedAttr: 'forced' });
});
it('handles value bindings', () => {
cy.createWrapper(VnComponent, {
props: {
prop: basicProp,
value: { testValue: 123 },
modelValue: 'test',
},
});
cy.getComponent('TestComponent')
.invoke('props')
.should('deep.include', { testValue: 123 });
});
});

View File

@ -1,8 +1,94 @@
// VnDms.spec.js
import VnDms from 'src/components/common/VnDms.vue';
describe.skip('<VnDms />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnDms);
describe.only('<VnDms />', () => {
beforeEach(() => {
cy.stub(window, 'useRoute').returns({});
cy.stub(window, 'useI18n').returns({ t: (key) => key });
cy.stub(window, 'axios').as('axiosStub');
});
it('renders with required props', () => {
cy.createWrapper(VnDms, {
props: {
model: 'testModel',
},
});
cy.get('.vn-dms').should('exist');
});
it('initializes with default DMS code', () => {
cy.createWrapper(VnDms, {
props: {
model: 'testModel',
defaultDmsCode: 'DMS001',
},
});
cy.getComponent('VnSelect').should('have.value', 'DMS001');
});
it('loads form with initial data', () => {
const initialData = { field: 'value' };
cy.createWrapper(VnDms, {
props: {
model: 'testModel',
formInitialData: initialData,
},
});
cy.getComponent('FormModelPopup')
.invoke('props', 'initialData')
.should('deep.equal', initialData);
});
it('fetches data from custom URL when provided', () => {
cy.createWrapper(VnDms, {
props: {
model: 'testModel',
url: '/api/custom-dms',
},
});
cy.get('@axiosStub').should(
'have.been.calledWith',
Cypress.sinon.match({
url: '/api/custom-dms',
})
);
});
it('emits onDataSaved event when form is submitted', () => {
const onDataSaved = cy.spy().as('savedSpy');
cy.createWrapper(VnDms, {
props: {
model: 'testModel',
onDataSaved,
},
});
cy.getComponent('FormModelPopup').trigger('saved');
cy.get('@savedSpy').should('have.been.called');
});
it('renders child components correctly', () => {
cy.createWrapper(VnDms, {
props: {
model: 'testModel',
},
});
cy.getComponent('VnRow').should('exist');
cy.getComponent('VnSelect').should('exist');
cy.getComponent('VnInput').should('exist');
cy.getComponent('FormModelPopup').should('exist');
});
it('handles fetch data errors gracefully', () => {
cy.get('@axiosStub').rejects(new Error('Fetch Error'));
cy.createWrapper(VnDms, {
props: {
model: 'testModel',
},
});
// Verify error handling UI elements
cy.get('.error-message').should('exist');
});
});

View File

@ -1,8 +1,112 @@
// VnDmsList.spec.js
import VnDmsList from 'src/components/common/VnDmsList.vue';
describe.skip('<VnDmsList />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnDmsList);
describe.only('<VnDmsList />', () => {
beforeEach(() => {
cy.stub(window, 'useRoute').returns({});
cy.stub(window, 'useQuasar').returns({});
cy.stub(window, 'useI18n').returns({ t: (key) => key });
cy.stub(window, 'useSession').returns({
getTokenMultimedia: () => 'test-token',
});
cy.stub(window, 'axios').as('axiosStub');
});
it('renders with required model prop', () => {
cy.createWrapper(VnDmsList, {
props: {
model: 'testModel',
},
});
cy.get('.vn-dms-list').should('exist');
});
it('displays paginated data', () => {
cy.createWrapper(VnDmsList, {
props: {
model: 'testModel',
},
});
cy.getComponent('VnPaginate').should('exist');
});
it('handles file download', () => {
cy.stub(window, 'downloadFile').as('downloadStub');
cy.createWrapper(VnDmsList, {
props: {
model: 'testModel',
},
});
cy.get('.download-button').click();
cy.get('@downloadStub').should('have.been.called');
});
it('shows DMS form dialog', () => {
cy.createWrapper(VnDmsList, {
props: {
model: 'testModel',
},
});
cy.get('.add-dms-button').click();
cy.getComponent('VnDms').should('exist');
});
it('handles document deletion with confirmation', () => {
cy.createWrapper(VnDmsList, {
props: {
model: 'testModel',
deleteModel: 'deleteTestModel',
},
});
cy.get('.delete-button').click();
cy.getComponent('VnConfirm').should('exist');
cy.get('.confirm-delete-button').click();
cy.get('@axiosStub').should('have.been.called');
});
it('displays user information using VnUserLink', () => {
const testData = {
rows: [{ id: 1, user: { id: 1, name: 'Test User' } }],
};
cy.get('@axiosStub').resolves({ data: testData });
cy.createWrapper(VnDmsList, {
props: {
model: 'testModel',
},
});
cy.getComponent('VnUserLink').should('exist');
});
it('shows document preview with VnImg', () => {
cy.createWrapper(VnDmsList, {
props: {
model: 'testModel',
},
});
cy.getComponent('VnImg').should('exist');
});
it('allows date filtering', () => {
cy.createWrapper(VnDmsList, {
props: {
model: 'testModel',
},
});
cy.getComponent('VnInputDate').type('2024-03-20');
cy.get('@axiosStub').should(
'have.been.calledWith',
Cypress.sinon.match({
params: { date: '2024-03-20' },
})
);
});
});

View File

@ -1,8 +1,101 @@
// VnInput.spec.js
import VnInput from 'src/components/common/VnInput.vue';
describe.skip('<VnInput />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<VnInput />', () => {
beforeEach(() => {
cy.stub(window, 'useI18n').returns({ t: (key) => key });
cy.stub(window, 'useValidator').returns({
validations: () => ({
required: (isRequired, value) => (isRequired ? !!value : true),
}),
});
});
it('renders correctly with default props', () => {
cy.createWrapper(VnInput);
cy.get('.q-field').should('exist');
cy.get('input').should('exist');
});
it('binds model value correctly', () => {
const testValue = 'test input';
cy.createWrapper(VnInput, {
props: {
modelValue: testValue,
},
});
cy.get('input').should('have.value', testValue);
});
it('emits update:modelValue on input', () => {
const onModelValueUpdate = cy.spy().as('modelUpdateSpy');
cy.createWrapper(VnInput, {
props: {
'onUpdate:modelValue': onModelValueUpdate,
},
});
cy.get('input').type('new value');
cy.get('@modelUpdateSpy').should('have.been.calledWith', 'new value');
});
it('applies outlined style when isOutlined is true', () => {
cy.createWrapper(VnInput, {
props: {
isOutlined: true,
},
});
cy.get('.q-field--outlined').should('exist');
});
it('displays info text when provided', () => {
cy.createWrapper(VnInput, {
props: {
info: 'Helper text',
},
});
cy.get('.q-field__info').should('contain', 'Helper text');
});
it('shows clear button when clearable is true and has value', () => {
cy.createWrapper(VnInput, {
props: {
modelValue: 'test',
clearable: true,
},
});
cy.get('.q-field__clear').should('exist');
});
it('clears input when clear button is clicked', () => {
const onModelValueUpdate = cy.spy().as('modelUpdateSpy');
cy.createWrapper(VnInput, {
props: {
modelValue: 'test',
clearable: true,
'onUpdate:modelValue': onModelValueUpdate,
},
});
cy.get('.q-field__clear').click();
cy.get('@modelUpdateSpy').should('have.been.calledWith', null);
});
it('emits keyup.enter event', () => {
const onEnter = cy.spy().as('enterSpy');
cy.createWrapper(VnInput, {
props: {
'onKeyup.enter': onEnter,
},
});
cy.get('input').type('{enter}');
cy.get('@enterSpy').should('have.been.called');
});
it('handles numeric input', () => {
cy.createWrapper(VnInput, {
props: {
modelValue: 123,
},
});
cy.get('input').should('have.value', '123');
});
});

View File

@ -1,8 +1,103 @@
// VnInputDate.spec.js
import VnInputDate from 'src/components/common/VnInputDate.vue';
describe.skip('<VnInputDate />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<VnInputDate />', () => {
beforeEach(() => {
cy.stub(window, 'useI18n').returns({ t: (key) => key });
cy.stub(window, 'useValidator').returns({
validations: () => ({
required: (isRequired, value) => (isRequired ? !!value : true),
}),
});
});
it('renders correctly with default props', () => {
cy.createWrapper(VnInputDate);
cy.getComponent('VnDate').should('exist');
cy.get('input').should('exist');
});
it('formats date correctly', () => {
const testDate = '2024-03-20';
cy.createWrapper(VnInputDate, {
props: {
modelValue: testDate,
},
});
cy.get('input').should('have.value', '20/03/2024');
});
it('validates required field', () => {
cy.createWrapper(VnInputDate, {
attrs: {
required: true,
},
});
cy.get('input').clear();
cy.get('.q-field__messages').should('contain', 'Field is required');
});
it('opens date picker popup on click', () => {
cy.createWrapper(VnInputDate);
cy.get('input').click();
cy.get('.q-date').should('be.visible');
});
it('updates model value when date is selected', () => {
const onModelUpdate = cy.spy().as('modelUpdateSpy');
cy.createWrapper(VnInputDate, {
props: {
'onUpdate:modelValue': onModelUpdate,
},
});
cy.get('input').click();
cy.get('.q-date__calendar-item').first().click();
cy.get('@modelUpdateSpy').should('have.been.called');
});
it('applies outlined style when isOutlined is true', () => {
cy.createWrapper(VnInputDate, {
props: {
isOutlined: true,
},
});
cy.get('.q-field--outlined').should('exist');
});
it('handles custom validation rules', () => {
const customRule = (val) =>
new Date(val) <= new Date() || 'Date cannot be in the future';
cy.createWrapper(VnInputDate, {
attrs: {
rules: [customRule],
},
});
const futureDate = '31/12/2025';
cy.get('input').type(futureDate);
cy.get('.q-field__messages').should('contain', 'Date cannot be in the future');
});
it('shows hover state', () => {
cy.createWrapper(VnInputDate);
cy.get('input').trigger('mouseenter');
cy.get('.q-field--hover').should('exist');
});
it('handles disabled state', () => {
cy.createWrapper(VnInputDate, {
attrs: {
disabled: true,
},
});
cy.get('input').should('be.disabled');
});
it('hides event indicator when showEvent is false', () => {
cy.createWrapper(VnInputDate, {
props: {
showEvent: false,
},
});
cy.get('.q-field__event-indicator').should('not.exist');
});
});

View File

@ -1,8 +1,47 @@
// VnInputNumber.spec.js
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
describe.skip('<VnInputNumber />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<VnInputNumber />', () => {
it('renders correctly', () => {
cy.createWrapper(VnInputNumber);
cy.getComponent('VnInput').should('exist');
cy.get('input[type="number"]').should('exist');
});
it('accepts numeric input', () => {
cy.createWrapper(VnInputNumber);
cy.get('input').type('123');
cy.get('input').should('have.value', '123');
});
it('converts string input to number', () => {
const onModelUpdate = cy.spy().as('modelUpdateSpy');
cy.createWrapper(VnInputNumber, {
props: {
'onUpdate:modelValue': onModelUpdate,
},
});
cy.get('input').type('456');
cy.get('@modelUpdateSpy').should('have.been.calledWith', 456);
});
it('forwards attributes to VnInput', () => {
cy.createWrapper(VnInputNumber, {
attrs: {
placeholder: 'Enter number',
min: '0',
max: '100',
},
});
cy.get('input')
.should('have.attr', 'placeholder', 'Enter number')
.and('have.attr', 'min', '0')
.and('have.attr', 'max', '100');
});
it('handles invalid input', () => {
cy.createWrapper(VnInputNumber);
cy.get('input').type('abc');
cy.get('input').should('have.value', '');
});
});

View File

@ -1,8 +1,93 @@
// VnInputTime.spec.js
import VnInputTime from 'src/components/common/VnInputTime.vue';
describe.skip('<VnInputTime />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<VnInputTime />', () => {
beforeEach(() => {
cy.stub(window, 'useI18n').returns({ t: (key) => key });
cy.stub(window, 'useValidator').returns({
validations: () => ({
required: (isRequired, value) => (isRequired ? !!value : true),
}),
});
});
it('renders correctly with default props', () => {
cy.createWrapper(VnInputTime);
cy.getComponent('VnTime').should('exist');
cy.get('input').should('exist');
});
it('formats time correctly', () => {
const initialTime = '14:30';
cy.createWrapper(VnInputTime, {
props: {
modelValue: initialTime,
},
});
cy.get('input').should('have.value', initialTime);
});
it('applies outlined style when isOutlined is true', () => {
cy.createWrapper(VnInputTime, {
props: {
isOutlined: true,
},
});
cy.get('.q-field--outlined').should('exist');
});
it('validates required field', () => {
cy.createWrapper(VnInputTime, {
attrs: {
required: true,
},
});
cy.get('input').clear();
cy.get('.q-field__messages').should('contain', 'Field is required');
});
it('opens time picker popup on click', () => {
cy.createWrapper(VnInputTime);
cy.get('input').click();
cy.get('.q-time').should('be.visible');
});
it('updates model value when time is selected', () => {
const onModelUpdate = cy.spy().as('modelUpdateSpy');
cy.createWrapper(VnInputTime, {
props: {
'onUpdate:modelValue': onModelUpdate,
},
});
cy.get('input').click();
cy.get('.q-time__clock--hours .q-time__clock-position').first().click();
cy.get('@modelUpdateSpy').should('have.been.called');
});
it('shows hover state', () => {
cy.createWrapper(VnInputTime);
cy.get('input').trigger('mouseenter');
cy.get('.q-field--hover').should('exist');
});
it('applies custom validation rules', () => {
const customRule = (val) => val < '20:00' || 'Time must be before 20:00';
cy.createWrapper(VnInputTime, {
attrs: {
rules: [customRule],
},
});
cy.get('input').type('21:00');
cy.get('.q-field__messages').should('contain', 'Time must be before 20:00');
});
it('displays time-only format when timeOnly is true', () => {
cy.createWrapper(VnInputTime, {
props: {
timeOnly: true,
},
});
cy.get('input').type('14:30');
cy.get('input').should('have.value', '14:30');
});
});

View File

@ -1,5 +1,5 @@
import VnLocation from 'src/components/common/VnLocation.vue';
describe.skip('<VnLocation />', () => {
describe.only('<VnLocation />', () => {
const location = {
postcode: '46000',
city: 'Valencia',

View File

@ -1,8 +1,73 @@
import VnLogFilter from 'src/components/common/VnLogFilter.vue';
describe.skip('<VnLogFilter />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnLogFilter);
describe.only('<VnLogFilter />', () => {
const mountComponent = () => {
// Stub del componente FetchData
const FetchDataStub = {
template: '<div class="fetch-data-stub"></div>',
props: ['url', 'filter', 'autoLoad'],
};
// Stub del componente VnFilterPanel
const VnFilterPanelStub = {
template:
'<div class="vn-filter-panel-stub"><slot name="tags"></slot><slot name="body"></slot></div>',
props: ['dataKey', 'searchButton'],
};
cy.createWrapper(VnLogFilter, {
global: {
components: {
FetchData: FetchDataStub,
VnFilterPanel: VnFilterPanelStub,
},
},
props: {
dataKey: 'testDataKey',
},
});
};
it('renderiza correctamente el componente', () => {
mountComponent();
cy.get('.vn-filter-panel-stub').should('exist');
});
it('pasa las props correctas a FetchData', () => {
const fetchDataSpy = cy.spy().as('fetchDataSpy');
const FetchDataStub = {
template: '<div class="fetch-data-stub"></div>',
props: ['url', 'filter', 'autoLoad'],
setup(props) {
fetchDataSpy(props);
},
};
mountComponent({
global: {
components: {
FetchData: FetchDataStub,
VnFilterPanel: {
template: '<div class="vn-filter-panel-stub"></div>',
props: ['dataKey', 'searchButton'],
},
},
},
props: {
dataKey: 'testDataKey',
},
});
cy.get('@fetchDataSpy').should('have.been.calledWithMatch', {
url: 'Workers/activeWithInheritedRole',
filter: { where: { role: 'salesPerson' } },
autoLoad: true,
});
});
it('renderiza los slots de VnFilterPanel correctamente', () => {
mountComponent();
cy.get('.vn-filter-panel-stub').should('exist');
// Aquí puedes agregar más verificaciones para los contenidos de los slots si es necesario
});
});

View File

@ -1,8 +1,35 @@
import VnPopup from 'src/components/common/VnPopup.vue';
describe.skip('<VnPopup />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnPopup);
describe('<VnPopup />', () => {
const mountComponent = (props = {}, slots = {}) => {
cy.createWrapper(VnPopup, {
props,
slots,
});
};
it('renderiza correctamente el componente', () => {
mountComponent();
cy.get('.q-popup-proxy').should('exist');
});
it('muestra el título y contenido proporcionados en las props', () => {
const title = 'Título de Prueba';
const content = 'Contenido de Prueba';
mountComponent({ title, content });
cy.get('.header').should('contain', title);
cy.get('.change-detail').should('contain', content);
});
it('renderiza los slots de título y contenido personalizados', () => {
mountComponent(
{},
{
title: '<div class="custom-title">Título Personalizado</div>',
content: '<div class="custom-content">Contenido Personalizado</div>',
}
);
cy.get('.custom-title').should('contain', 'Título Personalizado');
cy.get('.custom-content').should('contain', 'Contenido Personalizado');
});
});

View File

@ -1,6 +1,6 @@
import VnProgressModal from 'src/components/common/VnProgressModal.vue';
describe.skip('<VnProgressModal />', () => {
describe.only('<VnProgressModal />', () => {
const mountComponent = (opt) => {
cy.createWrapper(VnProgressModal, opt);
};

View File

@ -1,8 +1,42 @@
import VnRadio from 'src/components/common/VnRadio.vue';
describe.skip('<VnRadio />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnRadio);
describe.only('<VnRadio />', () => {
const mountComponent = (options = {}) => {
cy.createWrapper(VnRadio, {
...options,
});
};
it('renders correctly', () => {
mountComponent();
cy.get('.q-radio').should('exist');
});
it('applies dark mode correctly', () => {
mountComponent();
cy.get('.q-radio').should('have.class', '--dark');
});
it('updates model value when clicked', () => {
const modelValue = false;
mountComponent({ modelValue });
cy.get('.q-radio').click();
cy.get('.q-radio').should('have.class', 'q-radio--checked');
});
it('passes custom attributes through v-bind', () => {
const customProps = {
color: 'primary',
label: 'Test Label',
};
mountComponent({ attrs: customProps });
cy.get('.q-radio')
.should('have.class', 'text-primary')
.and('contain', 'Test Label');
});
it('maintains dense property', () => {
mountComponent();
cy.get('.q-radio').should('have.class', 'q-radio--dense');
});
});

View File

@ -1,12 +1,70 @@
// VnSectionMain.spec.js
import VnSectionMain from 'src/components/common/VnSectionMain.vue';
describe.skip('<VnSectionMain />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<VnSectionMain />', () => {
beforeEach(() => {
// Mock stores and composables
cy.stub(window, 'useStateStore').returns({
leftDrawer: false,
});
cy.stub(window, 'useQuasar').returns({
screen: {
gt: {
xs: true,
},
},
});
});
it('renders main layout components', () => {
cy.createWrapper(VnSectionMain);
cy.get('.q-drawer').should('exist');
cy.get('.q-page-container').should('exist');
cy.get('.q-page').should('exist');
});
it('initializes left drawer based on screen size and prop', () => {
cy.createWrapper(VnSectionMain, {
props: {
leftDrawer: true,
},
});
cy.wrap(Cypress.vueWrapper.vm.stateStore.leftDrawer).should('be.true');
});
it('hides drawer on small screens regardless of prop', () => {
cy.stub(window, 'useQuasar').returns({
screen: {
gt: {
xs: false,
},
},
});
cy.createWrapper(VnSectionMain, {
props: {
leftDrawer: true,
},
});
cy.wrap(Cypress.vueWrapper.vm.stateStore.leftDrawer).should('be.false');
});
it('renders LeftMenu inside drawer', () => {
cy.createWrapper(VnSectionMain);
cy.getComponent('LeftMenu').should('exist');
});
it('renders RouterView for main content', () => {
cy.createWrapper(VnSectionMain);
cy.get('.q-page').should('exist');
cy.getComponent('RouterView').should('exist');
});
it('applies correct drawer width', () => {
cy.createWrapper(VnSectionMain);
cy.get('.q-drawer').should('have.attr', 'width', '256');
});
});

View File

@ -1,8 +1,129 @@
// VnSelect.spec.js
import VnSelect from 'src/components/common/VnSelect.vue';
describe.skip('<VnSelect />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<VnSelect />', () => {
beforeEach(() => {
cy.stub(window, 'useI18n').returns({ t: (key) => key });
cy.stub(window, 'useValidator').returns({
validations: () => ({
required: (isRequired, value) => (isRequired ? !!value : true),
}),
});
});
const defaultOptions = [
{ id: 1, name: 'Option 1' },
{ id: 2, name: 'Option 2' },
];
it('renders correctly with default props', () => {
cy.createWrapper(VnSelect);
cy.get('.q-select').should('exist');
});
it('displays options with default option label and value', () => {
cy.createWrapper(VnSelect, {
props: {
options: defaultOptions,
},
});
cy.get('.q-select').click();
cy.get('.q-item').should('have.length', 2);
cy.get('.q-item').first().should('contain', 'Option 1');
});
it('uses custom option label and value', () => {
const customOptions = [
{ code: 'A', title: 'Title A' },
{ code: 'B', title: 'Title B' },
];
cy.createWrapper(VnSelect, {
props: {
options: customOptions,
optionLabel: 'title',
optionValue: 'code',
},
});
cy.get('.q-select').click();
cy.get('.q-item').first().should('contain', 'Title A');
});
it('emits update:modelValue when option is selected', () => {
const onModelUpdate = cy.spy().as('modelUpdateSpy');
cy.createWrapper(VnSelect, {
props: {
options: defaultOptions,
'onUpdate:modelValue': onModelUpdate,
},
});
cy.get('.q-select').click();
cy.get('.q-item').first().click();
cy.get('@modelUpdateSpy').should('have.been.calledWith', 1);
});
it('filters options based on optionFilter and optionFilterValue', () => {
const filteredOptions = [
{ id: 1, name: 'Option 1', type: 'A' },
{ id: 2, name: 'Option 2', type: 'B' },
];
cy.createWrapper(VnSelect, {
props: {
options: filteredOptions,
optionFilter: 'type',
optionFilterValue: 'A',
},
});
cy.get('.q-select').click();
cy.get('.q-item').should('have.length', 1);
cy.get('.q-item').should('contain', 'Option 1');
});
it('loads options from URL when provided', () => {
cy.createWrapper(VnSelect, {
props: {
url: '/api/options',
},
});
cy.getComponent('FetchData').should('exist');
cy.get('@axiosStub').should(
'have.been.calledWith',
Cypress.sinon.match({
url: '/api/options',
})
);
});
it('emits remove event when clear button is clicked', () => {
const onRemove = cy.spy().as('removeSpy');
cy.createWrapper(VnSelect, {
props: {
modelValue: 1,
options: defaultOptions,
onRemove,
},
});
cy.get('.q-field__clear').click();
cy.get('@removeSpy').should('have.been.called');
});
it('updates options externally', () => {
cy.createWrapper(VnSelect, {
props: {
options: defaultOptions,
},
});
const newOptions = [{ id: 3, name: 'Option 3' }];
cy.wrap(Cypress.vueWrapper).invoke('setProps', { options: newOptions });
cy.get('.q-select').click();
cy.get('.q-item').should('have.length', 1);
cy.get('.q-item').should('contain', 'Option 3');
});
});

View File

@ -1,8 +1,70 @@
import VnSelectCache from 'src/components/common/VnSelectCache.vue';
// VnSectionMain.spec.js
import VnSectionMain from 'src/components/common/VnSectionMain.vue';
describe.skip('<VnSelectCache />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnSelectCache);
describe.only('<VnSectionMain />', () => {
beforeEach(() => {
// Mock stores and composables
cy.stub(window, 'useStateStore').returns({
leftDrawer: false,
});
cy.stub(window, 'useQuasar').returns({
screen: {
gt: {
xs: true,
},
},
});
});
it('renders main layout components', () => {
cy.createWrapper(VnSectionMain);
cy.get('.q-drawer').should('exist');
cy.get('.q-page-container').should('exist');
cy.get('.q-page').should('exist');
});
it('initializes left drawer based on screen size and prop', () => {
cy.createWrapper(VnSectionMain, {
props: {
leftDrawer: true,
},
});
cy.wrap(Cypress.vueWrapper.vm.stateStore.leftDrawer).should('be.true');
});
it('hides drawer on small screens regardless of prop', () => {
cy.stub(window, 'useQuasar').returns({
screen: {
gt: {
xs: false,
},
},
});
cy.createWrapper(VnSectionMain, {
props: {
leftDrawer: true,
},
});
cy.wrap(Cypress.vueWrapper.vm.stateStore.leftDrawer).should('be.false');
});
it('renders LeftMenu inside drawer', () => {
cy.createWrapper(VnSectionMain);
cy.getComponent('LeftMenu').should('exist');
});
it('renders RouterView for main content', () => {
cy.createWrapper(VnSectionMain);
cy.get('.q-page').should('exist');
cy.getComponent('RouterView').should('exist');
});
it('applies correct drawer width', () => {
cy.createWrapper(VnSectionMain);
cy.get('.q-drawer').should('have.attr', 'width', '256');
});
});

View File

@ -1,8 +1,85 @@
// VnSelectDialog.spec.js
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
describe.skip('<VnSelectDialog />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<VnSelectDialog />', () => {
beforeEach(() => {
// Mock role and ACL composables
cy.stub(window, 'useRole').returns({
hasAny: cy.stub().as('roleHasAnySpy'),
});
cy.stub(window, 'useAcl').returns({
hasAny: cy.stub().as('aclHasAnySpy'),
});
});
it('renders select component', () => {
cy.createWrapper(VnSelectDialog);
cy.getComponent('VnSelect').should('exist');
});
it('allows creation for developer role by default', () => {
cy.get('@roleHasAnySpy').returns(true);
cy.createWrapper(VnSelectDialog);
cy.get('.create-action-button').should('exist');
});
it('hides creation button when user lacks role permission', () => {
cy.get('@roleHasAnySpy').returns(false);
cy.createWrapper(VnSelectDialog);
cy.get('.create-action-button').should('not.exist');
});
it('checks ACL permissions when provided', () => {
cy.get('@aclHasAnySpy').returns(true);
cy.createWrapper(VnSelectDialog, {
props: {
acls: ['create:item'],
},
});
cy.get('.create-action-button').should('exist');
cy.get('@aclHasAnySpy').should('have.been.calledWith', ['create:item']);
});
it('displays custom action icon', () => {
cy.get('@roleHasAnySpy').returns(true);
cy.createWrapper(VnSelectDialog, {
props: {
actionIcon: 'edit',
},
});
cy.get('.create-action-button .q-icon').should('have.attr', 'name', 'edit');
});
it('shows tooltip when provided', () => {
cy.get('@roleHasAnySpy').returns(true);
cy.createWrapper(VnSelectDialog, {
props: {
tooltip: 'Add new item',
},
});
cy.get('.create-action-button').trigger('mouseenter');
cy.get('.q-tooltip').should('contain', 'Add new item');
});
it('emits model update when value changes', () => {
const onModelUpdate = cy.spy().as('modelUpdateSpy');
cy.createWrapper(VnSelectDialog, {
props: {
'onUpdate:modelValue': onModelUpdate,
},
});
cy.getComponent('VnSelect').trigger('update:modelValue', 'newValue');
cy.get('@modelUpdateSpy').should('have.been.calledWith', 'newValue');
});
});

View File

@ -1,8 +1,100 @@
// VnSelectEnum.spec.js
import VnSelectEnum from 'src/components/common/VnSelectEnum.vue';
describe.skip('<VnSelectEnum />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnSelectEnum);
describe.only('<VnSelectEnum />', () => {
beforeEach(() => {
cy.stub(window, 'axios').as('axiosStub');
});
it('renders without errors', () => {
cy.createWrapper(VnSelectEnum, {
props: {
table: 'test_table',
column: 'test_column',
},
});
cy.getComponent('VnSelect').should('exist');
});
it('loads enum options from API on mount', () => {
const mockEnumData = ['OPTION1', 'OPTION2'];
cy.get('@axiosStub').resolves({ data: mockEnumData });
cy.createWrapper(VnSelectEnum, {
props: {
table: 'test_table',
column: 'test_column',
},
});
cy.get('@axiosStub').should('have.been.calledWith', {
method: 'get',
url: '/api/enums/vn/test_table/test_column',
});
});
it('uses custom schema when provided', () => {
cy.createWrapper(VnSelectEnum, {
props: {
schema: 'custom',
table: 'test_table',
column: 'test_column',
},
});
cy.get('@axiosStub').should('have.been.calledWith', {
method: 'get',
url: '/api/enums/custom/test_table/test_column',
});
});
it('uses translation function when provided', () => {
const translation = (value) => `Translated ${value}`;
const mockEnumData = ['OPTION1'];
cy.get('@axiosStub').resolves({ data: mockEnumData });
cy.createWrapper(VnSelectEnum, {
props: {
table: 'test_table',
column: 'test_column',
translation,
},
});
cy.getComponent('VnSelect')
.invoke('props', 'options')
.should('deep.equal', [{ label: 'Translated OPTION1', value: 'OPTION1' }]);
});
it('uses default options when provided', () => {
const defaultOptions = [
{ label: 'Default 1', value: 'DEFAULT1' },
{ label: 'Default 2', value: 'DEFAULT2' },
];
cy.createWrapper(VnSelectEnum, {
props: {
table: 'test_table',
column: 'test_column',
defaultOptions,
},
});
cy.getComponent('VnSelect')
.invoke('props', 'options')
.should('deep.equal', defaultOptions);
});
it('handles API error gracefully', () => {
cy.get('@axiosStub').rejects(new Error('API Error'));
cy.createWrapper(VnSelectEnum, {
props: {
table: 'test_table',
column: 'test_column',
},
});
cy.getComponent('VnSelect').invoke('props', 'options').should('deep.equal', []);
});
});

View File

@ -2,7 +2,7 @@
/* eslint-disable cypress/no-assigning-return-values */
import VnSmsDialog from 'src/components/common/VnSmsDialog.vue';
describe.skip('<VnSmsDialog />', () => {
describe.only('<VnSmsDialog />', () => {
const defaultProps = {
phone: '123456789',
subject: 'Test Subject',

View File

@ -1,8 +1,58 @@
// VnSummaryDialog.spec.js
import VnSummaryDialog from 'src/components/common/VnSummaryDialog.vue';
describe.skip('<VnSummaryDialog />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnSummaryDialog);
describe.only('<VnSummaryDialog />', () => {
const TestSummaryComponent = {
template: '<div class="test-summary">Test Summary Content</div>',
props: ['id'],
};
beforeEach(() => {
cy.stub(window, 'useDialogPluginComponent').returns({
dialogRef: cy.stub(),
onDialogHide: cy.stub().as('onDialogHideStub'),
});
});
it('renders correctly with required props', () => {
cy.createWrapper(VnSummaryDialog, {
props: {
id: 1,
summary: TestSummaryComponent,
},
});
cy.get('.q-dialog').should('exist');
});
it('renders dynamic summary component with correct props', () => {
cy.createWrapper(VnSummaryDialog, {
props: {
id: 123,
summary: TestSummaryComponent,
},
});
cy.get('.test-summary').should('exist');
cy.get('.test-summary').invoke('prop', 'id').should('equal', 123);
});
it('handles dialog hide event', () => {
cy.createWrapper(VnSummaryDialog, {
props: {
id: 1,
summary: TestSummaryComponent,
},
});
cy.get('.q-dialog').trigger('hide');
cy.get('@onDialogHideStub').should('have.been.called');
});
it('applies full-width class to dialog', () => {
cy.createWrapper(VnSummaryDialog, {
props: {
id: 1,
summary: TestSummaryComponent,
},
});
cy.get('.q-dialog').should('have.class', 'full-width');
});
});

View File

@ -1,8 +1,94 @@
// VnWeekdayPicker.spec.js
import VnWeekdayPicker from 'src/components/common/VnWeekdayPicker.vue';
describe.skip('<VnWeekdayPicker />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnWeekdayPicker);
describe.only('<VnWeekdayPicker />', () => {
beforeEach(() => {
// Mock weekday store
cy.stub(window, 'useWeekdayStore').returns({
getLocalesMap: [
{ index: 0, localeChar: 'M' },
{ index: 1, localeChar: 'T' },
{ index: 2, localeChar: 'W' },
{ index: 3, localeChar: 'T' },
{ index: 4, localeChar: 'F' },
{ index: 5, localeChar: 'S' },
{ index: 6, localeChar: 'S' },
],
});
});
it('renders all weekday buttons', () => {
cy.createWrapper(VnWeekdayPicker, {
props: {
wdays: [false, false, false, false, false, false, false],
},
});
cy.get('.q-btn').should('have.length', 7);
});
it('displays correct weekday labels', () => {
cy.createWrapper(VnWeekdayPicker, {
props: {
wdays: [false, false, false, false, false, false, false],
},
});
cy.get('.q-btn').first().should('contain', 'M');
cy.get('.q-btn').last().should('contain', 'S');
});
it('toggles weekday selection on click', () => {
const onUpdateWdays = cy.spy().as('updateSpy');
cy.createWrapper(VnWeekdayPicker, {
props: {
wdays: [false, false, false, false, false, false, false],
'onUpdate:wdays': onUpdateWdays,
},
});
cy.get('.q-btn').first().click();
cy.get('@updateSpy').should('have.been.calledWith', [
true,
false,
false,
false,
false,
false,
false,
]);
});
it('applies primary color to selected weekdays', () => {
cy.createWrapper(VnWeekdayPicker, {
props: {
wdays: [true, false, true, false, false, false, false],
},
});
cy.get('.q-btn').eq(0).should('have.class', 'bg-primary');
cy.get('.q-btn').eq(1).should('not.have.class', 'bg-primary');
cy.get('.q-btn').eq(2).should('have.class', 'bg-primary');
});
it('maintains selection state after multiple toggles', () => {
const onUpdateWdays = cy.spy().as('updateSpy');
cy.createWrapper(VnWeekdayPicker, {
props: {
wdays: [false, false, false, false, false, false, false],
'onUpdate:wdays': onUpdateWdays,
},
});
cy.get('.q-btn').first().click();
cy.get('.q-btn').first().click();
cy.get('@updateSpy').should('have.been.calledWith', [
false,
false,
false,
false,
false,
false,
false,
]);
});
});

View File

@ -1,6 +1,6 @@
import CardSummary from 'src/components/ui/CardSummary.vue';
import { createRouter, createWebHistory } from 'vue-router';
describe.skip('<CardSummary />', () => {
describe.only('<CardSummary />', () => {
let router, wrapper;
beforeEach(() => {

View File

@ -1,8 +1,71 @@
import VnConfirm from 'src/components/ui/VnConfirm.vue';
describe.skip('<VnConfirm />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<VnConfirm />', () => {
it('renders with default props', () => {
cy.createWrapper(VnConfirm);
cy.get('.q-dialog').should('exist');
});
it('renders with custom icon and title', () => {
cy.createWrapper(VnConfirm, {
props: {
icon: 'warning',
title: 'Test Title',
},
});
cy.get('.q-icon').should('contain', 'warning');
cy.get('.dialog-title').should('contain', 'Test Title');
});
it('displays custom message', () => {
cy.createWrapper(VnConfirm, {
props: {
message: 'Test Message',
},
});
cy.get('.dialog-content').should('contain', 'Test Message');
});
it('emits confirm event when confirmed', () => {
const onConfirm = cy.spy().as('confirmSpy');
cy.createWrapper(VnConfirm, {
props: {
onConfirm: onConfirm,
},
});
cy.get('.confirm-button').click();
cy.get('@confirmSpy').should('have.been.called');
});
it('handles promise execution', () => {
const testPromise = cy.stub().resolves('success');
cy.createWrapper(VnConfirm, {
props: {
promise: testPromise,
data: { testKey: 'testValue' },
},
});
cy.get('.confirm-button').click();
cy.wrap(testPromise).should('have.been.calledWith', { testKey: 'testValue' });
});
it('shows and hides dialog via exposed methods', () => {
cy.createWrapper(VnConfirm).then((wrapper) => {
wrapper.show();
cy.get('.q-dialog').should('be.visible');
wrapper.hide();
cy.get('.q-dialog').should('not.be.visible');
});
});
it('handles dialog plugin events', () => {
const onHide = cy.spy().as('hideSpy');
cy.createWrapper(VnConfirm, {
props: {
onHide: onHide,
},
});
cy.get('.cancel-button').click();
cy.get('@hideSpy').should('have.been.called');
});
});

View File

@ -1,8 +1,90 @@
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
// VnFilterPanel.spec.js
import VnFilterPanel from 'src/components/common/VnFilterPanel.vue';
import { toDate } from 'src/filters';
describe.skip('<VnFilterPanel />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnFilterPanel);
describe.only('<VnFilterPanel />', () => {
beforeEach(() => {
cy.stub(window, 'useRoute').returns({
query: {},
});
});
const mountComponent = (props = {}) => {
cy.createWrapper(VnFilterPanel, {
props: {
dataKey: 'test-key',
...props,
},
});
};
it('renders correctly with required props', () => {
mountComponent();
cy.get('.vn-filter-panel').should('exist');
});
it('handles modelValue changes', () => {
const modelValue = { search: 'test' };
mountComponent({ modelValue });
cy.get('.filter-chip').should('exist');
cy.get('.filter-chip').should('contain', 'test');
});
it('shows search button when searchButton prop is true', () => {
mountComponent({ searchButton: true });
cy.get('.search-button').should('exist');
});
it('hides search button when searchButton prop is false', () => {
mountComponent({ searchButton: false });
cy.get('.search-button').should('not.exist');
});
it('prevents removal of unremovable params', () => {
const modelValue = { fixed: 'value', removable: 'test' };
mountComponent({
modelValue,
unremovableParams: ['fixed'],
});
cy.get('.filter-chip[data-param="fixed"] .remove-button').should('not.exist');
cy.get('.filter-chip[data-param="removable"] .remove-button').should('exist');
});
it('emits update:modelValue when filter is removed', () => {
const modelValue = { param: 'value' };
const onUpdateModelValue = cy.spy().as('updateSpy');
mountComponent({
modelValue,
'onUpdate:modelValue': onUpdateModelValue,
});
cy.get('.filter-chip .remove-button').click();
cy.get('@updateSpy').should('have.been.calledWith', {});
});
it('shows all filters when showAll is true', () => {
const modelValue = { param1: 'value1', param2: 'value2' };
mountComponent({
modelValue,
showAll: true,
});
cy.get('.filter-chip').should('have.length', 2);
});
it('formats date values correctly', () => {
const modelValue = { date: '2024-03-20' };
mountComponent({ modelValue });
cy.get('.filter-chip').should('contain', toDate('2024-03-20'));
});
it('syncs with route query params', () => {
cy.stub(window, 'useRoute').returns({
query: { search: 'queryTest' },
});
mountComponent();
cy.get('.filter-chip').should('contain', 'queryTest');
});
});

View File

@ -1,8 +1,49 @@
import VnFilterPanelChip from 'src/components/ui/VnFilterPanelChip.vue';
// VnFilterPanelChip.spec.js
import VnFilterPanelChip from 'src/components/common/VnFilterPanelChip.vue';
describe.skip('<VnFilterPanelChip />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnFilterPanelChip);
describe.only('<VnFilterPanelChip />', () => {
const mountComponent = (props = {}, slots = {}) => {
cy.createWrapper(VnFilterPanelChip, {
props,
slots,
});
};
it('renders correctly with default props', () => {
mountComponent();
cy.get('.q-chip').should('exist');
});
it('applies default classes and attributes', () => {
mountComponent();
cy.get('.q-chip')
.should('have.class', 'text-dark')
.and('have.class', 'bg-primary')
.and('have.attr', 'size', 'sm');
});
it('displays the label icon', () => {
mountComponent();
cy.get('.q-chip .q-icon').should('contain', 'label');
});
it('renders slot content correctly', () => {
mountComponent(
{},
{
default: 'Test Content',
}
);
cy.get('.q-chip').should('contain', 'Test Content');
});
it('forwards attributes via v-bind', () => {
mountComponent({
class: 'custom-class',
'data-test': 'test-value',
});
cy.get('.q-chip')
.should('have.class', 'custom-class')
.and('have.attr', 'data-test', 'test-value');
});
});

View File

@ -1,8 +1,82 @@
import VnImg from 'src/components/ui/VnImg.vue';
describe.skip('<VnImg />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnImg);
describe.only('<VnImg />', () => {
beforeEach(() => {
// Mock useSession composable
cy.stub(window, 'useSession').returns({
getTokenMultimedia: () => 'mock-token',
});
});
const mountComponent = (props = {}) => {
cy.createWrapper(VnImg, {
props,
});
};
it('renders with default props', () => {
mountComponent({ id: 1 });
cy.get('img')
.should('have.attr', 'src')
.and('include', 'Images')
.and('include', 'catalog')
.and('include', '200x200');
});
it('renders with custom storage and collection', () => {
mountComponent({
props: {
id: 1,
storage: 'CustomStorage',
collection: 'custom-collection',
resolution: '300x300',
},
});
cy.get('img')
.should('have.attr', 'src')
.and('include', 'CustomStorage')
.and('include', 'custom-collection')
.and('include', '300x300');
});
it('shows zoom functionality when enabled', () => {
mountComponent({
props: {
id: 1,
zoom: true,
zoomResolution: '800x800',
},
});
cy.get('img').click();
cy.get('.zoomed-image').should('exist');
});
it('disables zoom functionality when configured', () => {
mountComponent({
props: {
id: 1,
zoom: false,
},
});
cy.get('img').click();
cy.get('.zoomed-image').should('not.exist');
});
it('includes authentication token in image URL', () => {
mountComponent({
props: {
id: 1,
},
});
cy.get('img').should('have.attr', 'src').and('include', 'mock-token');
});
it('shows fallback image when loading fails', () => {
mountComponent({
props: {
id: 1,
},
});
cy.get('img').trigger('error');
cy.get('img').should('have.attr', 'src', '/no-user.png');
});
});

View File

@ -1,8 +1,46 @@
import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
describe.skip('<VnLinkPhone />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnLinkPhone);
describe.only('<VnLinkPhone />', () => {
const mountComponent = (props = {}) => {
cy.createWrapper(VnLinkPhone, {
props,
});
};
it('should not render button when phone number is null', () => {
mountComponent();
cy.get('.q-btn').should('not.exist');
});
it('should render button when phone number is provided', () => {
mountComponent({ phoneNumber: '1234567890' });
cy.get('.q-btn').should('exist');
});
it('should generate correct SIP link from phone number', () => {
mountComponent({ phoneNumber: '1234567890' });
cy.get('.q-btn').should('have.attr', 'href', 'sip:1234567890');
});
it('should have correct button attributes', () => {
mountComponent({ phoneNumber: '1234567890' });
cy.get('.q-btn')
.should('have.class', 'q-btn--flat')
.should('have.class', 'q-btn--round')
.should('have.class', 'text-primary')
.should('have.attr', 'padding', 'none');
});
it('should display phone icon', () => {
mountComponent({ phoneNumber: '1234567890' });
cy.get('.q-btn .q-icon').should('contain', 'phone');
});
it('should stop click event propagation', () => {
const onClick = cy.spy().as('clickSpy');
mountComponent({ phoneNumber: '1234567890' });
cy.get('.q-btn').parent().on('click', onClick);
cy.get('.q-btn').click();
cy.get('@clickSpy').should('not.have.been.called');
});
});

View File

@ -1,8 +1,93 @@
import VnNotes from 'src/components/ui/VnNotes.vue';
// VnNotes.spec.js
import VnNotes from 'src/components/common/VnNotes.vue';
describe.skip('<VnNotes />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<VnNotes />', () => {
beforeEach(() => {
// Mock axios to prevent real HTTP requests
cy.stub(window, 'axios').as('axiosStub');
});
it('renders without errors', () => {
cy.createWrapper(VnNotes);
cy.get('.vn-notes').should('exist');
});
it('loads notes data correctly on mount', () => {
cy.createWrapper(VnNotes);
cy.get('@axiosStub').should('have.been.called');
});
it('displays the add note section when addNote prop is true', () => {
cy.createWrapper(VnNotes, {
props: {
addNote: true,
},
});
cy.get('.add-note-section').should('be.visible');
});
it('hides the add note section when addNote prop is false', () => {
cy.createWrapper(VnNotes, {
props: {
addNote: false,
},
});
cy.get('.add-note-section').should('not.exist');
});
it('allows the user to add a new note', () => {
cy.createWrapper(VnNotes, {
props: {
addNote: true,
},
});
cy.get('.note-input').type('Test note');
cy.get('.submit-note-button').click();
cy.get('@axiosStub').should('have.been.calledWithMatch', {
method: 'post',
url: '/api/notes',
});
});
it('displays an error message if adding a note fails', () => {
cy.get('@axiosStub').throws(new Error('Network Error'));
cy.createWrapper(VnNotes, {
props: {
addNote: true,
},
});
cy.get('.note-input').type('Test note');
cy.get('.submit-note-button').click();
cy.get('.error-message').should('contain', 'Network Error');
});
it('paginates notes correctly', () => {
cy.createWrapper(VnNotes);
cy.get('.pagination').should('exist');
cy.get('.pagination .next-page').click();
cy.get('@axiosStub').should('have.been.called');
});
it('displays observation types when selectType prop is true', () => {
cy.createWrapper(VnNotes, {
props: {
selectType: true,
},
});
cy.get('.observation-type-select').should('be.visible');
});
it('handles before route leave with unsaved changes', () => {
const onBeforeRouteLeave = cy.stub();
cy.createWrapper(VnNotes, {
listeners: {
'before-route-leave': onBeforeRouteLeave,
},
});
cy.get('.note-input').type('Unsaved note');
cy.window().then((win) => {
win.dispatchEvent(new Event('beforeunload'));
expect(onBeforeRouteLeave).to.have.been.called;
});
});
});

View File

@ -1,8 +1,55 @@
import VnOutForm from 'src/components/ui/VnOutForm.vue';
// VnOutForm.spec.js
import VnOutForm from 'src/components/common/VnOutForm.vue';
describe.skip('<VnOutForm />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnOutForm);
describe.only('<VnOutForm />', () => {
const mountComponent = (props = {}, slots = {}) => {
cy.createWrapper(VnOutForm, {
props,
slots,
});
};
it('renders without errors', () => {
mountComponent({ title: 'Test Title' });
cy.get('.formCard').should('exist');
});
it('displays the title prop', () => {
mountComponent({ title: 'Test Title' });
cy.get('h5').should('contain', 'Test Title');
});
it('renders the default icon when icon prop is not false', () => {
mountComponent({ title: 'Test Title' });
cy.get('.q-icon').should('exist');
cy.get('.q-icon').should('have.attr', 'name', 'phonelink_lock');
});
it('does not render the icon when icon prop is false', () => {
mountComponent({ title: 'Test Title', icon: false });
cy.get('.q-icon').should('not.exist');
});
it('emits submit event when form is submitted', () => {
const onSubmit = cy.spy().as('submitSpy');
mountComponent({ title: 'Test Title', onSubmit });
cy.get('form').submit();
cy.get('@submitSpy').should('have.been.called');
});
it('renders default slot content', () => {
mountComponent(
{ title: 'Test Title' },
{ default: '<div class="slot-content">Default Slot Content</div>' }
);
cy.get('.slot-content').should('contain', 'Default Slot Content');
});
it('renders buttons slot content', () => {
mountComponent(
{ title: 'Test Title' },
{ buttons: '<button class="slot-buttons">Button Slot Content</button>' }
);
cy.get('.slot-buttons').should('contain', 'Button Slot Content');
});
});

View File

@ -1,8 +1,116 @@
import VnPaginate from 'src/components/ui/VnPaginate.vue';
// VnPaginate.spec.js
import VnPaginate from 'src/components/common/VnPaginate.vue';
describe.skip('<VnPaginate />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnPaginate);
describe.only('<VnPaginate />', () => {
beforeEach(() => {
cy.stub(window, 'axios').as('axiosStub');
});
const mountComponent = (props = {}) => {
cy.createWrapper(VnPaginate, {
props: {
dataKey: 'test-key',
...props,
},
});
};
it('renders without errors', () => {
mountComponent();
cy.get('.vn-paginate').should('exist');
});
it('loads data automatically when autoLoad is true', () => {
const url = '/api/test';
mountComponent({
url,
autoLoad: true,
});
cy.get('@axiosStub').should('have.been.called');
});
it('does not load data automatically when autoLoad is false', () => {
mountComponent({
url: '/api/test',
autoLoad: false,
});
cy.get('@axiosStub').should('not.have.been.called');
});
it('handles static data array', () => {
const testData = [
{ id: 1, name: 'Test 1' },
{ id: 2, name: 'Test 2' },
];
mountComponent({
data: testData,
});
cy.get('.vn-paginate').should('contain', 'Test 1');
});
it('applies filters correctly', () => {
const testFilter = { search: 'test' };
mountComponent({
url: '/api/test',
filter: testFilter,
});
cy.get('@axiosStub').should(
'have.been.calledWith',
Cypress.sinon.match({
params: Cypress.sinon.match(testFilter),
})
);
});
it('handles pagination events', () => {
mountComponent({
url: '/api/test',
});
cy.get('.pagination-next').click();
cy.get('@axiosStub').should(
'have.been.calledWith',
Cypress.sinon.match({
params: Cypress.sinon.match({
page: 2,
}),
})
);
});
it('applies custom classes', () => {
mountComponent({
class: 'custom-class',
});
cy.get('.vn-paginate').should('have.class', 'custom-class');
});
it('handles user filters', () => {
const userFilter = { status: 'active' };
mountComponent({
url: '/api/test',
userFilter,
});
cy.get('@axiosStub').should(
'have.been.calledWith',
Cypress.sinon.match({
params: Cypress.sinon.match(userFilter),
})
);
});
it('watches for filter changes and reloads data', () => {
// eslint-disable-next-line cypress/no-assigning-return-values
const wrapper = cy.createWrapper(VnPaginate, {
props: {
dataKey: 'test-key',
url: '/api/test',
},
});
wrapper.setProps({
filter: { newFilter: 'value' },
});
cy.get('@axiosStub').should('have.been.calledTwice');
});
});

View File

@ -1,8 +1,48 @@
import VnRow from 'src/components/ui/VnRow.vue';
// VnRow.spec.js
import VnRow from 'src/components/common/VnRow.vue';
describe.skip('<VnRow />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnRow);
describe.only('<VnRow />', () => {
const mountComponent = (props = {}, slots = {}) => {
cy.createWrapper(VnRow, {
props,
slots,
});
};
it('renders without errors', () => {
mountComponent();
cy.get('.vn-row').should('exist');
});
it('applies flex display style', () => {
mountComponent();
cy.get('.vn-row').should('have.css', 'display', 'flex');
});
it('renders default slot content', () => {
const slotContent = '<div class="slot-content">Test Content</div>';
mountComponent({}, { default: slotContent });
cy.get('.vn-row .slot-content').should('contain', 'Test Content');
});
it('applies wrap class when wrap prop is true', () => {
mountComponent({ wrap: true });
cy.get('.vn-row').should('have.class', 'wrap');
});
it('does not apply wrap class when wrap prop is false', () => {
mountComponent({ wrap: false });
cy.get('.vn-row').should('not.have.class', 'wrap');
});
it('child elements have flex: 1 style', () => {
const slotContent = `
<div class="child-1">Child 1</div>
<div class="child-2">Child 2</div>
`;
mountComponent({}, { default: slotContent });
cy.get('.vn-row > *').each(($el) => {
cy.wrap($el).should('have.css', 'flex', '1 1 0%');
});
});
});

View File

@ -1,8 +1,85 @@
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
// VnSearchbar.spec.js
import VnSearchbar from 'src/components/common/VnSearchbar.vue';
describe.skip('<VnSearchbar />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnSearchbar);
describe.only('<VnSearchbar />', () => {
beforeEach(() => {
// Mock required composables and stores
cy.stub(window, 'useQuasar').returns({});
cy.stub(window, 'useI18n').returns({ t: (key) => key });
cy.stub(window, 'useStateStore').returns({});
cy.stub(window, 'useArrayData').returns({});
});
const mountComponent = (props = {}) => {
cy.createWrapper(VnSearchbar, {
props: {
dataKey: 'test-key',
...props,
},
});
};
it('renders with default props', () => {
mountComponent();
cy.getComponent('VnInput').should('exist');
cy.get('input').should('have.attr', 'placeholder', 'Search');
});
it('displays custom label', () => {
mountComponent({ label: 'Custom Search' });
cy.get('input').should('have.attr', 'placeholder', 'Custom Search');
});
it('shows info text when provided', () => {
mountComponent({ info: 'Search Info' });
cy.get('.info-text').should('contain', 'Search Info');
});
it('handles search input', () => {
mountComponent();
cy.get('input').type('test search');
cy.get('input').should('have.value', 'test search');
});
it('triggers search with url when provided', () => {
const searchUrl = '/api/search';
mountComponent({ url: searchUrl });
cy.get('input').type('test{enter}');
cy.get('@axiosStub').should(
'have.been.calledWith',
Cypress.sinon.match({
url: searchUrl,
})
);
});
it('handles redirect prop', () => {
const router = {
push: cy.spy().as('routerPush'),
};
cy.stub(window, 'useRouter').returns(router);
mountComponent({ redirect: true });
cy.get('input').type('test{enter}');
cy.get('@routerPush').should('have.been.called');
});
it('applies custom filter', () => {
const customFilter = { category: 'test' };
mountComponent({ filter: customFilter });
cy.get('input').type('search{enter}');
cy.get('@axiosStub').should(
'have.been.calledWith',
Cypress.sinon.match({
params: Cypress.sinon.match(customFilter),
})
);
});
it('clears search input', () => {
mountComponent();
cy.get('input').type('test');
cy.get('.clear-button').click();
cy.get('input').should('have.value', '');
});
});

View File

@ -1,8 +1,69 @@
import VnSms from 'src/components/ui/VnSms.vue';
// VnSms.spec.js
import VnSms from 'src/components/common/VnSms.vue';
describe.skip('<VnSms />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<VnSms />', () => {
beforeEach(() => {
// Stub axios to prevent real HTTP requests
cy.stub(window, 'axios').as('axiosStub');
});
it('renders without errors', () => {
cy.createWrapper(VnSms);
cy.get('.vn-sms').should('exist');
});
it('computes the correct filter based on props', () => {
const whereProp = { status: 'delivered' };
cy.createWrapper(VnSms, {
props: {
where: whereProp,
},
});
cy.wrap(Cypress.vueWrapper.setupState.filter).then((filter) => {
expect(filter.value.where).to.deep.equal(whereProp);
expect(filter.value.fields).to.include('smsFk');
expect(filter.value.include.relation).to.equal('sms');
});
});
it('displays SMS data correctly', () => {
const smsData = [
{
smsFk: 1,
sms: {
senderFk: 1,
sender: 'Jane Smith',
destination: '+1234567890',
message: 'Test message content',
statusCode: 'sent',
status: 'Sent',
created: '2023-10-10T10:00:00Z',
// sender: {
// name: 'Jane Smith',
// },
},
},
];
cy.intercept('GET', '**/api/**', { body: smsData }).as('getSmsData');
cy.createWrapper(VnSms);
cy.wait('@getSmsData');
cy.get('.sms-message').should('contain', 'Test message content');
cy.get('.sms-destination').should('contain', '+1234567890');
});
it('handles pagination through VnPaginate', () => {
cy.createWrapper(VnSms);
cy.get('.vn-paginate').should('exist');
cy.get('.paginate-next').click();
cy.get('@axiosStub').should('have.been.called');
});
it('renders VnAvatar and VnUserLink components', () => {
cy.createWrapper(VnSms);
cy.getComponent('VnAvatar').should('exist');
cy.getComponent('VnUserLink').should('exist');
});
});

View File

@ -1,8 +1,67 @@
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
// VnSubToolbar.spec.js
import VnSubToolbar from 'src/components/common/VnSubToolbar.vue';
describe.skip('<VnSubToolbar />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
describe.only('<VnSubToolbar />', () => {
beforeEach(() => {
// Mock store
cy.stub(window, 'useStateStore').returns({
toggleSubToolbar: cy.stub().as('toggleSubToolbarStub'),
});
// Mock DOM elements
const actionDiv = document.createElement('div');
actionDiv.id = 'st-actions';
const dataDiv = document.createElement('div');
dataDiv.id = 'st-data';
document.body.appendChild(actionDiv);
document.body.appendChild(dataDiv);
});
afterEach(() => {
document.querySelector('#st-actions')?.remove();
document.querySelector('#st-data')?.remove();
});
it('renders toolbar correctly', () => {
cy.createWrapper(VnSubToolbar);
cy.get('#subToolbar').should('exist');
});
it('calls toggleSubToolbar on mount', () => {
cy.createWrapper(VnSubToolbar);
cy.get('@toggleSubToolbarStub').should('have.been.calledOnce');
});
it('calls toggleSubToolbar on unmount', () => {
// eslint-disable-next-line cypress/no-assigning-return-values
const wrapper = cy.createWrapper(VnSubToolbar);
wrapper.unmount();
cy.get('@toggleSubToolbarStub').should('have.been.calledTwice');
});
it('updates hasContent when actions content changes', () => {
cy.createWrapper(VnSubToolbar);
const actionsEl = document.querySelector('#st-actions');
const newChild = document.createElement('div');
actionsEl.appendChild(newChild);
cy.wrap(Cypress.vueWrapper.vm.hasContent).should('equal', 1);
});
it('updates hasContent when data content changes', () => {
cy.createWrapper(VnSubToolbar);
const dataEl = document.querySelector('#st-data');
const newChild = document.createElement('div');
dataEl.appendChild(newChild);
cy.wrap(Cypress.vueWrapper.vm.hasContent).should('equal', 1);
});
it('handles missing action and data elements', () => {
document.querySelector('#st-actions')?.remove();
document.querySelector('#st-data')?.remove();
cy.createWrapper(VnSubToolbar);
cy.wrap(Cypress.vueWrapper.vm.hasContent).should('equal', false);
});
});

View File

@ -1,8 +1,50 @@
import VnUserLink from 'src/components/ui/VnUserLink.vue';
describe.skip('<VnUserLink />', () => {
it('TODO: boilerplate', () => {
// see: https://on.cypress.io/mounting-vue
cy.createWrapper(VnUserLink);
describe.only('<VnUserLink />', () => {
const mountComponent = (props = {}, slots = {}) => {
cy.createWrapper(VnUserLink, {
props,
slots,
});
};
it('renders with default props', () => {
mountComponent();
cy.get('span').should('exist');
cy.get('span').should('not.have.class', 'link');
cy.get('span').should('contain', '');
});
it('displays name when provided and defaultName is false', () => {
mountComponent({ name: 'John Doe' });
cy.get('span').should('contain', 'John Doe');
});
it('displays default name when defaultName is true', () => {
mountComponent({ name: null, defaultName: true });
cy.get('span').should('contain', 'globals.system');
});
it('adds link class when workerId is provided', () => {
mountComponent({ workerId: 123 });
cy.get('span').should('have.class', 'link');
});
it('renders WorkerDescriptorProxy when workerId is provided', () => {
mountComponent({ workerId: 123 });
cy.getComponent('WorkerDescriptorProxy').should('exist');
cy.getComponent('WorkerDescriptorProxy')
.invoke('props')
.should('deep.include', { id: 123 });
});
it('uses custom content from link slot', () => {
mountComponent(
{},
{
link: '<span class="custom-link">Custom Link Content</span>',
}
);
cy.get('.custom-link').should('contain', 'Custom Link Content');
});
});