diff --git a/training-front-end/src/components/ValidatedDatepicker.vue b/training-front-end/src/components/ValidatedDatepicker.vue index 0ba678aa..dacb69de 100644 --- a/training-front-end/src/components/ValidatedDatepicker.vue +++ b/training-front-end/src/components/ValidatedDatepicker.vue @@ -41,7 +41,7 @@ const month = user_input.month; const day = user_input.day; if (year.length == 4 && month && day) { - const newDate = new Date(`${year}-${month}-${day}`); + const newDate = new Date(Date.UTC(year, month, day, '00', '00', '00')); if (!isNaN(newDate)) { emit('update:modelValue', newDate); } @@ -52,7 +52,7 @@ const newDate = new Date(newValue); if (!isNaN(newDate)) { user_input.day = newDate?.getDate() - user_input.month = newDate?.getMonth() + 1 //+1 to convert from 0 index array + user_input.month = newDate?.getMonth() user_input.year = newDate?.getFullYear() } else{ user_input.day = ''; @@ -110,40 +110,40 @@ - - - - - - - - - - - - diff --git a/training-front-end/src/components/__tests__/ValidatedDatepicker.spec.js b/training-front-end/src/components/__tests__/ValidatedDatepicker.spec.js new file mode 100644 index 00000000..e4bedcf3 --- /dev/null +++ b/training-front-end/src/components/__tests__/ValidatedDatepicker.spec.js @@ -0,0 +1,109 @@ +import { describe, it, expect } from "vitest"; +import { mount } from '@vue/test-utils'; +import ValidatedDatepicker from '../ValidatedDatepicker.vue'; + +describe('ValidatedDatepicker', () => { + it('renders properly', async () => { + const wrapper = mount(ValidatedDatepicker, { + props: { + modelValue: '', + validator: { + $error: false, + $errors: [] + }, + name: 'testName', + label: 'Test Label' + } + }); + + expect(wrapper.html()).toContain('Month'); + expect(wrapper.html()).toContain('Day'); + expect(wrapper.html()).toContain('Year'); + }); + + it('displays errors when validator has errors', async () => { + const wrapper = mount(ValidatedDatepicker, { + props: { + modelValue: '', + validator: { + $error: true, + $errors: [{ $property: 'name', $message: 'Name is required' }] + }, + name: 'testName', + label: 'Test Label' + } + }); + + expect(wrapper.find('.usa-error-message').text()).toBe('Name is required'); + }); + + it('emits update:modelValue event on input', async () => { + const wrapper = mount(ValidatedDatepicker, { + props: { + modelValue: '', + validator: { + $error: false, + $errors: [] + }, + name: 'testName', + label: 'Test Label' + } + }); + + const monthInput = wrapper.get('#testName-month'); + const dayInput = wrapper.get('#testName-day'); + const yearInput = wrapper.get('#testName-year'); + + await monthInput.setValue('1'); //zero index 1 = february + await dayInput.setValue('15'); + await yearInput.setValue('2023'); + + expect(wrapper.emitted()).toHaveProperty('update:modelValue'); + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([new Date('2023-02-15')]); + }); + + it('updates user_input when modelValue changes', async () => { + const wrapper = mount(ValidatedDatepicker, { + props: { + modelValue: '', + validator: { + $error: false, + $errors: [] + }, + name: 'testName', + label: 'Test Label' + } + }); + + await wrapper.vm.$nextTick(); + + // Simulate user input + const monthInput = wrapper.get('#testName-month'); + const dayInput = wrapper.get('#testName-day'); + const yearInput = wrapper.get('#testName-year'); + + // Simulate modelValue change + await wrapper.setProps({ + modelValue: new Date('2024-02-20') + }); + + await wrapper.vm.$nextTick(); + + // Ensure user_input is updated + expect(monthInput.element.value).toBe('1'); + expect(dayInput.element.value).toBe('20'); + expect(yearInput.element.value).toBe('2024'); + + // Simulate modelValue change to undefined + await wrapper.setProps({ + modelValue: undefined + }); + + await wrapper.vm.$nextTick(); + + // Ensure user_input is cleared + expect(monthInput.element.value).toBe(''); + expect(dayInput.element.value).toBe(''); + expect(yearInput.element.value).toBe(''); + }); +}); \ No newline at end of file diff --git a/training-front-end/src/components/__tests__/ValidatedInput.spec.js b/training-front-end/src/components/__tests__/ValidatedInput.spec.js new file mode 100644 index 00000000..beac3fbb --- /dev/null +++ b/training-front-end/src/components/__tests__/ValidatedInput.spec.js @@ -0,0 +1,60 @@ +import { describe, it, expect } from "vitest"; +import { mount } from '@vue/test-utils'; +import ValidatedInput from '../ValidatedInput.vue'; + +describe('ValidatedInput', () => { + it('renders properly', () => { + const wrapper = mount(ValidatedInput, { + props: { + modelValue: '', + validator: { + $error: false, + $errors: [] + }, + name: 'testName', + label: 'Test Label' + } + }) + + expect(wrapper.find('.usa-form-group').exists()).toBe(true) + expect(wrapper.find('.usa-label').text()).toBe('Test Label (*Required)') + expect(wrapper.find('input').exists()).toBe(true) + }) + + it('displays errors when validator has errors', async () => { + const wrapper = mount(ValidatedInput, { + props: { + modelValue: '', + validator: { + $error: true, + $errors: [{ $property: 'name', $message: 'Name is required' }] + }, + name: 'testName', + label: 'Test Label' + } + }) + + expect(wrapper.find('.usa-input--error').exists()).toBe(true) + expect(wrapper.find('.usa-error-message').text()).toBe('Name is required') + }) + + it('emits update:modelValue event on input', async () => { + const wrapper = mount(ValidatedInput, { + props: { + modelValue: '', + validator: { + $error: false, + $errors: [] + }, + name: 'testName', + label: 'Test Label' + } + }) + + const textarea = wrapper.find('input') + await textarea.setValue('new value') + + expect(wrapper.emitted('update:modelValue')).toBeTruthy() + expect(wrapper.emitted('update:modelValue')[0]).toEqual(['new value']) + }) +}) \ No newline at end of file diff --git a/training-front-end/src/components/__tests__/ValidatedSelect.spec.js b/training-front-end/src/components/__tests__/ValidatedSelect.spec.js new file mode 100644 index 00000000..1267eee3 --- /dev/null +++ b/training-front-end/src/components/__tests__/ValidatedSelect.spec.js @@ -0,0 +1,75 @@ +import { describe, it, expect } from "vitest"; +import { mount } from '@vue/test-utils'; +import ValidatedSelect from '../ValidatedSelect.vue'; + +describe('ValidatedSelect', () => { + it('renders properly', () => { + const wrapper = mount(ValidatedSelect, { + props: { + modelValue: '', + validator: { + $error: false, + $errors: [] + }, + name: 'testName', + label: 'Test Label', + options: [ + { id: 1, name: 'Option 1' }, + { id: 2, name: 'Option 2' }, + { id: 3, name: 'Option 3' } + ] + } + }) + + expect(wrapper.find('.usa-form-group').exists()).toBe(true) + expect(wrapper.find('.usa-label').text()).toBe('Test Label (*Required)') + expect(wrapper.findAll('option').length).toBe(4) // 3 options + default option + }) + + it('displays errors when validator has errors', async () => { + const wrapper = mount(ValidatedSelect, { + props: { + modelValue: '', + validator: { + $error: true, + $errors: [{ $property: 'name', $message: 'Name is required' }] + }, + name: 'testName', + label: 'Test Label', + options: [ + { id: 1, name: 'Option 1' }, + { id: 2, name: 'Option 2' }, + { id: 3, name: 'Option 3' } + ] + } + }) + + expect(wrapper.find('.usa-form-group--error').exists()).toBe(true) + expect(wrapper.find('.usa-error-message').text()).toBe('Name is required') + }) + + it('emits update:modelValue event on input', async () => { + const wrapper = mount(ValidatedSelect, { + props: { + modelValue: '', + validator: { + $error: false, + $errors: [] + }, + name: 'testName', + label: 'Test Label', + options: [ + { id: 1, name: 'Option 1' }, + { id: 2, name: 'Option 2' }, + { id: 3, name: 'Option 3' } + ] + } + }) + + const select = wrapper.find('select') + await select.setValue('1') + + expect(wrapper.emitted('update:modelValue')).toBeTruthy() + expect(wrapper.emitted('update:modelValue')[0]).toEqual(['1']) + }) +}) \ No newline at end of file diff --git a/training-front-end/src/components/__tests__/ValidatedTextArea.spec.js b/training-front-end/src/components/__tests__/ValidatedTextArea.spec.js new file mode 100644 index 00000000..626b820b --- /dev/null +++ b/training-front-end/src/components/__tests__/ValidatedTextArea.spec.js @@ -0,0 +1,60 @@ +import { describe, it, expect } from "vitest"; +import { mount } from "@vue/test-utils"; +import ValidatedTextArea from "../ValidatedTextArea.vue"; + +describe('ValidatedTextArea', () => { + it('renders properly', () => { + const wrapper = mount(ValidatedTextArea, { + props: { + modelValue: 'initial value', + validator: { + $error: false, + $errors: [] + }, + name: 'testName', + label: 'Test Label' + } + }) + + expect(wrapper.find('.usa-form-group').exists()).toBe(true) + expect(wrapper.find('.usa-label').text()).toBe('Test Label (*Required)') + expect(wrapper.find('textarea').exists()).toBe(true) + }) + + it('displays errors when validator has errors', async () => { + const wrapper = mount(ValidatedTextArea, { + props: { + modelValue: 'initial value', + validator: { + $error: true, + $errors: [{ $property: 'name', $message: 'Name is required' }] + }, + name: 'testName', + label: 'Test Label' + } + }) + + expect(wrapper.find('.usa-input--error').exists()).toBe(true) + expect(wrapper.find('.usa-error-message').text()).toBe('Name is required') + }) + + it('emits update:modelValue event on input', async () => { + const wrapper = mount(ValidatedTextArea, { + props: { + modelValue: 'initial value', + validator: { + $error: false, + $errors: [] + }, + name: 'testName', + label: 'Test Label' + } + }) + + const textarea = wrapper.find('textarea') + await textarea.setValue('new value') + + expect(wrapper.emitted('update:modelValue')).toBeTruthy() + expect(wrapper.emitted('update:modelValue')[0]).toEqual(['new value']) + }) +}) \ No newline at end of file diff --git a/training-front-end/vitest.config.js b/training-front-end/vitest.config.js index f2ddd6f8..f6f5cd0f 100644 --- a/training-front-end/vitest.config.js +++ b/training-front-end/vitest.config.js @@ -8,6 +8,7 @@ export default defineConfig({ base: process.env.BASEURL, plugins: [vue()], test: { + globalSetup: './vitest.global-setup.ts', environment: 'jsdom' }, envPrefix: "PUBLIC_" diff --git a/training-front-end/vitest.global-setup.ts b/training-front-end/vitest.global-setup.ts new file mode 100644 index 00000000..448f548a --- /dev/null +++ b/training-front-end/vitest.global-setup.ts @@ -0,0 +1,3 @@ +export const setup = () => { + process.env.TZ = 'UTC' + } \ No newline at end of file