diff --git a/.eslintrc.js b/.eslintrc.js index 41edb10d5..867fa935e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,17 +4,20 @@ module.exports = { node: true, jest: true }, - extends: ['plugin:vue/vue3-essential', 'eslint:recommended', 'prettier'], + extends: ['plugin:nuxt/recommended', 'plugin:vue/vue3-essential', 'prettier'], parserOptions: { parser: '@babel/eslint-parser', requireConfigFile: false }, plugins: ['prettier'], + ignorePatterns: ['**/public/**', '/layouts/AppDocumentation.vue'], rules: { 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-fallthrough': 'off', 'vue/this-in-template': ['error', 'never'], + 'vue/multi-word-component-names': 'off', + 'vue/no-reserved-component-names': 'off', 'vue/component-tags-order': [ 'error', { diff --git a/components/accordion/Accordion.spec.js b/components/accordion/Accordion.spec.js index 264af4b36..dd790c75b 100644 --- a/components/accordion/Accordion.spec.js +++ b/components/accordion/Accordion.spec.js @@ -1,7 +1,8 @@ import { mount } from '@vue/test-utils'; +import { expect, it } from 'vitest'; import AccordionTab from '../accordiontab/AccordionTab.vue'; import Accordion from './Accordion.vue'; - +vi.mock('primevue/utils'); describe('Accordion.vue', () => { let wrapper; @@ -14,25 +15,29 @@ describe('Accordion.vue', () => { }, slots: { default: ` - -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation - ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. - Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-
- -

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi - architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione - voluptatem sequi nesciunt. Consectetur, adipisci velit, sed quia non numquam eius modi.

-
- -

At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati - cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. - Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus.

-
` + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+ +

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi + architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione + voluptatem sequi nesciunt. Consectetur, adipisci velit, sed quia non numquam eius modi.

+
+ +

At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati + cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. + Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus.

+
` } }); }); + afterEach(() => { + vi.clearAllMocks(); + }); + it('should Accordion and AccordionTab component exist', () => { expect(wrapper.find('.p-accordion.p-component').exists()).toBe(true); expect(wrapper.find('.p-accordion-tab').exists()).toBe(true); @@ -47,4 +52,122 @@ describe('Accordion.vue', () => { expect(allTabs[0].classes()).not.toContain('p-accordion-tab-active'); expect(allTabs[1].classes()).toContain('p-accordion-tab-active'); }); + + it('should work tab click', async () => { + const firstHeader = wrapper.find('a.p-accordion-header-link'); + + await firstHeader.trigger('click'); + + expect(wrapper.emitted()['update:activeIndex'][0]).toEqual([0]); + expect(wrapper.emitted()['tab-click'][0][0].index).toEqual(0); + }); + + it('When invalid key triggered OnTabKey should break', async () => { + const keydownOptions = ['onTabHomeKey', 'onTabEnterKey', 'onTabEndKey', 'onTabArrowDownKey', 'onTabArrowUpKey']; + const firstHeader = wrapper.find('a.p-accordion-header-link'); + + await firstHeader.trigger('keydown', { code: 'ArrowRight' }); + + for (const keydownOption of keydownOptions) { + expect(vi.spyOn(wrapper.vm, keydownOption)).not.toHaveBeenCalled(); + } + }); + + it('When keydown enter is triggered on component header changeActiveIndex should be triggered', async () => { + const firstHeader = wrapper.find('a.p-accordion-header-link'); + const changeActiveIndexSpy = vi.spyOn(wrapper.vm, 'changeActiveIndex'); + + await firstHeader.trigger('keydown', { code: 'Enter' }); + + expect(changeActiveIndexSpy).toHaveBeenCalled(); + }); + + it('When keydown end is triggered on component header changeFocusedTab should be triggered', async () => { + const firstHeader = wrapper.find('a.p-accordion-header-link'); + const changeFocusedTabSpy = vi.spyOn(wrapper.vm, 'changeFocusedTab'); + const findLastHeaderActionSpy = vi.spyOn(wrapper.vm, 'findLastHeaderAction'); + + await firstHeader.trigger('keydown', { code: 'End' }); + + expect(changeFocusedTabSpy).toHaveBeenCalled(); + expect(findLastHeaderActionSpy).toHaveBeenCalled(); + }); + + it('When keydown home is triggered on component header changeFocusedTab should be triggered', async () => { + const firstHeader = wrapper.find('a.p-accordion-header-link'); + const changeFocusedTabSpy = vi.spyOn(wrapper.vm, 'changeFocusedTab'); + const findFirstHeaderActionSpy = vi.spyOn(wrapper.vm, 'findFirstHeaderAction'); + + await firstHeader.trigger('keydown', { code: 'Home' }); + + expect(changeFocusedTabSpy).toHaveBeenCalled(); + expect(findFirstHeaderActionSpy).toHaveBeenCalled(); + }); + + it('When keydown ArrowUp is triggered and findPrevHeaderAction is true changeFocusedTab should be triggered', async () => { + const findPrevHeaderActionSpy = vi.spyOn(wrapper.vm, 'findPrevHeaderAction').mockImplementation(() => true); + const onTabEndKeySpy = vi.spyOn(wrapper.vm, 'onTabEndKey'); + const changeFocusedTabSpy = vi.spyOn(wrapper.vm, 'changeFocusedTab').mockImplementation(() => true); + const firstHeader = wrapper.find('a.p-accordion-header-link'); + + await firstHeader.trigger('keydown', { code: 'ArrowUp' }); + + expect(findPrevHeaderActionSpy).toHaveBeenCalled(); + expect(changeFocusedTabSpy).toHaveBeenCalled(); + expect(onTabEndKeySpy).toHaveBeenCalledTimes(0); + }); + + it('When keydown ArrowUp is triggered and findPrevHeaderAction is false onTabEndKey should be triggered', async () => { + const findPrevHeaderActionSpy = vi.spyOn(wrapper.vm, 'findPrevHeaderAction').mockImplementation(() => false); + const onTabEndKeySpy = vi.spyOn(wrapper.vm, 'onTabEndKey'); + const firstHeader = wrapper.find('a.p-accordion-header-link'); + + await firstHeader.trigger('keydown', { code: 'ArrowUp' }); + + expect(findPrevHeaderActionSpy).toHaveBeenCalled(); + expect(onTabEndKeySpy).toHaveBeenCalled(); + }); + + it('When keydown ArrowDown is triggered and nextHeaderAction is true changeFocusedTab should be triggered', async () => { + const findNextHeaderActionSpy = vi.spyOn(wrapper.vm, 'findNextHeaderAction').mockImplementation(() => true); + const onTabHomeKeySpy = vi.spyOn(wrapper.vm, 'onTabHomeKey'); + const changeFocusedTabSpy = vi.spyOn(wrapper.vm, 'changeFocusedTab').mockImplementation(() => true); + const firstHeader = wrapper.find('a.p-accordion-header-link'); + + await firstHeader.trigger('keydown', { code: 'ArrowDown' }); + + expect(findNextHeaderActionSpy).toHaveBeenCalled(); + expect(changeFocusedTabSpy).toHaveBeenCalled(); + expect(onTabHomeKeySpy).toHaveBeenCalledTimes(0); + }); + + it('When keydown ArrowDown is triggered and nextHeaderAction is false onTabHomeKey should be triggered', async () => { + const findNextHeaderActionSpy = vi.spyOn(wrapper.vm, 'findNextHeaderAction').mockImplementation(() => false); + const onTabHomeKeySpy = vi.spyOn(wrapper.vm, 'onTabHomeKey'); + const firstHeader = wrapper.find('a.p-accordion-header-link'); + + await firstHeader.trigger('keydown', { code: 'ArrowDown' }); + + expect(findNextHeaderActionSpy).toHaveBeenCalled(); + expect(onTabHomeKeySpy).toHaveBeenCalled(); + }); + + it('When changeFocusedTab triggered and selectOnFocus is true changeActiveIndex should be triggered with valid parameters', async () => { + await wrapper.setProps({ selectOnFocus: true }); + const changeActiveIndexSpy = vi.spyOn(wrapper.vm, 'changeActiveIndex'); + const event = {}; + const element = { + parentElement: { + parentElement: { + dataset: { + index: 0 + } + } + } + }; + + await wrapper.vm.changeFocusedTab(event, element); + + expect(changeActiveIndexSpy).toHaveBeenCalledWith({}, wrapper.vm.tabs[0], 0); + }); }); diff --git a/components/avatar/Avatar.spec.js b/components/avatar/Avatar.spec.js index ef67af6ad..c598d7c8e 100644 --- a/components/avatar/Avatar.spec.js +++ b/components/avatar/Avatar.spec.js @@ -1,20 +1,35 @@ import { mount } from '@vue/test-utils'; +import { beforeEach } from 'vitest'; import Avatar from './Avatar.vue'; +let wrapper = null; + +beforeEach(() => { + wrapper = mount(Avatar, { + props: { + label: 'T', + size: 'large', + shape: 'circle', + image: 'test' + } + }); +}); describe('Avatar.vue', () => { it('should exist', () => { - const wrapper = mount(Avatar, { - props: { - label: 'T', - size: 'large', - shape: 'circle' - } - }); - expect(wrapper.find('.p-avatar.p-component').exists()).toBe(true); expect(wrapper.find('.p-avatar-lg').exists()).toBe(true); expect(wrapper.find('.p-avatar-circle').exists()).toBe(true); expect(wrapper.find('.p-avatar-text').exists()).toBe(true); expect(wrapper.find('.p-avatar-text').text()).toBe('T'); }); + + it('should exist', async () => { + await wrapper.setProps({ image: 'imageTest' }); + const image = wrapper.find('.p-avatar-image'); + + await wrapper.vm.onError(); + + expect(image.exists()).toBe(true); + expect(wrapper.emitted().error.length).toBe(1); + }); }); diff --git a/components/badge/Badge.spec.js b/components/badge/Badge.spec.js index 95d0c99f4..ae92fdecc 100644 --- a/components/badge/Badge.spec.js +++ b/components/badge/Badge.spec.js @@ -1,33 +1,35 @@ import { mount } from '@vue/test-utils'; -import Button from 'primevue/button'; +import { expect } from 'vitest'; import Badge from './Badge.vue'; +let wrapper = null; + describe('Badge.vue', () => { - it('should exist', () => { - const wrapper = mount(Badge, { + beforeEach(() => { + wrapper = mount(Badge, { props: { value: '29', severity: 'warning', size: 'large' } }); - + }); + it('should exist', () => { expect(wrapper.find('.p-badge.p-component').exists()).toBe(true); expect(wrapper.find('.p-badge-warning').exists()).toBe(true); expect(wrapper.find('.p-badge-lg').exists()).toBe(true); expect(wrapper.find('.p-badge-no-gutter').exists()).toBe(false); + + expect(wrapper.vm.containerClass).not.toBe('p-overlay-badge'); }); it('badge classes should exist', () => { - const wrapper = mount({ - template: ' - + beforeEach(() => { + wrapper = mount(BlockUI); + }); - - -

The story begins as Don Vito Corleone, the head of a New York Mafia family.

-
-
- `, - components: { - BlockUI, - Panel, - Button - }, - data() { - return { - blockedPanel: false - }; - }, - methods: { - blockPanel() { - this.blockedPanel = true; - }, - unblockPanel() { - this.blockedPanel = false; - } + afterEach(() => { + vi.clearAllMocks(); + }); + + it('When blocked props is true, block method should be triggered on mounted hook', async () => { + const blockUiSpy = vi.spyOn(BlockUI.methods, 'block'); + + wrapper = mount(BlockUI, { + props: { + blocked: true } }); - expect(wrapper.find('.p-blockui-container').exists()).toBe(true); + expect(blockUiSpy).toHaveBeenCalled(); + }); - const buttons = wrapper.findAll('.p-button'); + it('When blocked props value is changed, block or unblock method should be triggered', async () => { + const blockSpy = vi.spyOn(wrapper.vm, 'block'); + const unblockSpy = vi.spyOn(wrapper.vm, 'unblock'); - await buttons[0].trigger('click'); + await wrapper.setProps({ blocked: true }); - expect(wrapper.find('.p-blockui').exists()).toBe(true); - expect(wrapper.find('.p-blockui').classes()).toContain('p-component-overlay-enter'); - expect(wrapper.find('.p-blockui').attributes().style).toEqual('z-index: 1101;'); + expect(blockSpy).toHaveBeenCalled(); - await buttons[1].trigger('click'); + await wrapper.setProps({ blocked: false }); - expect(wrapper.find('.p-blockui').classes()).toContain('p-component-overlay-leave'); + expect(unblockSpy).toHaveBeenCalled(); + }); + + it('When block method triggered, mask should be added on DOM', async () => { + await wrapper.setProps({ fullScreen: true }); + await wrapper.vm.block(); + + expect(document.querySelector('.p-blockui')).not.toBe(null); + }); + + it('When removeMask method triggered, isBlocked should be false and emitted', async () => { + wrapper = mount(BlockUI, { + props: { + blocked: true + }, + slots: { + default: 'test' + } + }); + await wrapper.vm.removeMask(); + + expect(wrapper.vm.isBlocked).toBe(false); + expect(wrapper.emitted().unblock.length).toBe(1); }); }); diff --git a/components/calendar/Calendar.vue b/components/calendar/Calendar.vue index b35803717..e17b8843d 100755 --- a/components/calendar/Calendar.vue +++ b/components/calendar/Calendar.vue @@ -2503,7 +2503,7 @@ export default { if (cell) { cell.tabIndex = '0'; - if (!this.preventFocus && (!this.navigationState || !this.navigationState.button) && !this.timePickerChange) { + if (!this.inline && (!this.navigationState || !this.navigationState.button) && !this.timePickerChange) { cell.focus(); } diff --git a/components/confirmationservice/ConfirmationService.d.ts b/components/confirmationservice/ConfirmationService.d.ts index 0fec0231a..27d8cb94f 100644 --- a/components/confirmationservice/ConfirmationService.d.ts +++ b/components/confirmationservice/ConfirmationService.d.ts @@ -1,4 +1,4 @@ -import Vue, { Plugin } from 'vue'; +import { Plugin } from 'vue'; import { ConfirmationOptions } from '../confirmationoptions'; declare const plugin: Plugin; diff --git a/components/rating/Rating.d.ts b/components/rating/Rating.d.ts index f6108e35d..c7d037ecc 100755 --- a/components/rating/Rating.d.ts +++ b/components/rating/Rating.d.ts @@ -1,3 +1,4 @@ +import { VNode } from 'vue'; import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; export interface RatingChangeEvent { diff --git a/components/rating/Rating.spec.js b/components/rating/Rating.spec.js index 0402ded00..f7718a649 100644 --- a/components/rating/Rating.spec.js +++ b/components/rating/Rating.spec.js @@ -1,4 +1,5 @@ import { config, mount } from '@vue/test-utils'; +import { expect } from 'vitest'; import Rating from './Rating.vue'; config.global.mocks = { @@ -43,4 +44,32 @@ describe('Rating.vue', () => { expect(wrapper.find('.p-rating-cancel').exists()).toBe(false); }); + + it('When star is clicked, onOptionClick method should triggered', async () => { + await wrapper.find('.p-rating-item').trigger('click'); + + expect(wrapper.find('.p-focus').exists()).toBe(true); + }); + + it('When input focused, focusedOptionIndex value should changed', async () => { + await wrapper.vm.onFocus(true, 5); + + expect(wrapper.vm.focusedOptionIndex).toEqual(5); + expect(wrapper.emitted().focus[0]).toEqual([true]); + }); + + it('When input changed, onOptionSelect method should triggered', async () => { + const onOptionSelectSpy = vi.spyOn(wrapper.vm, 'onOptionSelect'); + + await wrapper.vm.onChange(); + + expect(onOptionSelectSpy).toHaveBeenCalled(); + }); + + it('When input changed, onOptionSelect method should triggered', async () => { + await wrapper.setProps({ onIcon: 'test-icon' }); + await wrapper.setProps({ modelValue: 5 }); + + expect(wrapper.find('.test-icon').exists()).toBe(true); + }); }); diff --git a/components/sidebar/Sidebar.spec.js b/components/sidebar/Sidebar.spec.js index 667721749..c6b1b6dcd 100644 --- a/components/sidebar/Sidebar.spec.js +++ b/components/sidebar/Sidebar.spec.js @@ -1,5 +1,6 @@ import { mount } from '@vue/test-utils'; import PrimeVue from 'primevue/config'; +import { describe, expect, it } from 'vitest'; import Sidebar from './Sidebar.vue'; describe('Sidebar.vue', () => { @@ -18,7 +19,8 @@ describe('Sidebar.vue', () => { bazeZIndex: 1000 }, slots: { - default: `

Left Sidebar

` + default: `

Left Sidebar

`, + header: `Header Template` } }); }); @@ -58,4 +60,58 @@ describe('Sidebar.vue', () => { expect(icon.classes()).toContain('pi-discord'); }); + + it('should header slot rendered', () => { + expect(wrapper.find('.p-sidebar-header').exists()).toBe(true); + expect(wrapper.find('.p-sidebar-header-content').exists()).toBe(true); + expect(wrapper.find('span.header').exists()).toBe(true); + expect(wrapper.find('span.header').text()).toBe('Header Template'); + }); + + it('should default slot rendered', () => { + expect(wrapper.find('h3').exists()).toBe(true); + expect(wrapper.find('h3').text()).toBe('Left Sidebar'); + }); + + it('should keydown work', async () => { + const event = { code: 'Escape' }; + + await wrapper.vm.onKeydown(event); + + expect(wrapper.emitted()['update:visible'][0]).toEqual([false]); + }); +}); + +describe('when visible is false', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Sidebar, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true, + transition: false + } + }, + props: { + visible: true, + bazeZIndex: 1000 + } + }); + }); + + it('should show and hide emit work', async () => { + expect(wrapper.emitted()['show'][0]).toEqual([]); + + await wrapper.setProps({ visible: false }); + + expect(wrapper.emitted()['hide'][0]).toEqual([]); + }); + + it('should be destroyed', () => { + wrapper.unmount(); + expect(wrapper.componentVM.container).toBe(null); + expect(wrapper.componentVM.mask).toBe(null); + }); }); diff --git a/components/splitter/Splitter.vue b/components/splitter/Splitter.vue index 310ab4e72..1b305763d 100644 --- a/components/splitter/Splitter.vue +++ b/components/splitter/Splitter.vue @@ -1,6 +1,6 @@