From fb3f6094988c08b8de9644522845cf6bca4228d7 Mon Sep 17 00:00:00 2001 From: juliaM <35346206+juliaam@users.noreply.github.com> Date: Wed, 3 Jul 2024 10:10:47 -0300 Subject: [PATCH] feat: petvetpage tests (#264) * feat: petvetpage tests * fix: error in methods * fix: tests and form validation * fix: stories * fix: correct petvet * fix: petvet and it tests * fix: tests describes --- src/components/Vaccine/index.js | 3 +- src/components/VaccineGroup/index.js | 3 +- .../pages/PetVet/images/cuidadosEspeciais.svg | 19 ++ .../app/pages/PetVet/images/estetoscopio.svg | 21 ++ src/layouts/app/pages/PetVet/index.js | 144 +++++++++++++ src/layouts/app/pages/PetVet/index.scss | 159 +++++++++++++++ src/layouts/app/pages/PetVet/index.spec.js | 191 ++++++++++++++++++ src/stories/PetRegister.stories.js | 2 +- src/stories/PetRegisterPage.stories.js | 2 +- src/stories/PetVetPage.stories.js | 2 +- src/stories/PetWeightPage.stories.js | 2 +- 11 files changed, 542 insertions(+), 6 deletions(-) create mode 100644 src/layouts/app/pages/PetVet/images/cuidadosEspeciais.svg create mode 100644 src/layouts/app/pages/PetVet/images/estetoscopio.svg create mode 100644 src/layouts/app/pages/PetVet/index.js create mode 100644 src/layouts/app/pages/PetVet/index.scss create mode 100644 src/layouts/app/pages/PetVet/index.spec.js diff --git a/src/components/Vaccine/index.js b/src/components/Vaccine/index.js index 1e07a5df..58dc22f5 100644 --- a/src/components/Vaccine/index.js +++ b/src/components/Vaccine/index.js @@ -77,10 +77,11 @@ Vaccine.prototype = Object.assign(Vaccine.prototype, Component.prototype, { listVaccines() { const vaccines = []; - this.groups.values().forEach((group) => { + Array.from(this.groups.values()).forEach((group) => { const items = group.listItems(); vaccines.push(...items); }); + return vaccines; }, openDrawer() { diff --git a/src/components/VaccineGroup/index.js b/src/components/VaccineGroup/index.js index b507d7c8..59a9e3a1 100644 --- a/src/components/VaccineGroup/index.js +++ b/src/components/VaccineGroup/index.js @@ -43,7 +43,7 @@ VaccineGroup.prototype = Object.assign( listItems() { const items = []; - this.items.values().forEach((item) => { + Array.from(this.items.values()).forEach((item) => { items.push({ id: item.id, title: item.getTitle(), @@ -51,6 +51,7 @@ VaccineGroup.prototype = Object.assign( date: item.getDate(), }); }); + return items; }, }, diff --git a/src/layouts/app/pages/PetVet/images/cuidadosEspeciais.svg b/src/layouts/app/pages/PetVet/images/cuidadosEspeciais.svg new file mode 100644 index 00000000..aa9abc9b --- /dev/null +++ b/src/layouts/app/pages/PetVet/images/cuidadosEspeciais.svg @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/src/layouts/app/pages/PetVet/images/estetoscopio.svg b/src/layouts/app/pages/PetVet/images/estetoscopio.svg new file mode 100644 index 00000000..ec64174e --- /dev/null +++ b/src/layouts/app/pages/PetVet/images/estetoscopio.svg @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/src/layouts/app/pages/PetVet/index.js b/src/layouts/app/pages/PetVet/index.js new file mode 100644 index 00000000..d54d54cb --- /dev/null +++ b/src/layouts/app/pages/PetVet/index.js @@ -0,0 +1,144 @@ +import { Component } from 'pet-dex-utilities'; +import Button from '../../../../components/Button'; +import Radio from '../../../../components/RadioButton'; +import Vaccine from '../../../../components/Vaccine'; + +import estetoscopio from './images/estetoscopio.svg'; +import cuidadosEspeciais from './images/cuidadosEspeciais.svg'; + +import './index.scss'; + +const html = ` +
+`; + +const events = ['submit']; + +function getAriaLabel(radioName) { + const arias = { + specialCare: 'cuidados-especiais', + neutered: 'castrado', + }; + return arias[radioName]; +} + +function getBooleanValue(value) { + const radiosValue = { + specialCare: true, + neutered: true, + }; + return value in radiosValue ? radiosValue[value] : false; +} + +function createAndMount({ name, text, mountTo, value }) { + const radio = new Radio({ name, text, value }); + radio.selected + .get('radio-button') + .setAttribute('aria-label', `${getAriaLabel(name)}-${text.toLowerCase()}`); + radio.mount(mountTo); + return radio; +} + +export default function PetVetPage({ vaccines = [] } = {}) { + Component.call(this, { html, events }); + + const $footer = this.selected.get('footer'); + const $specialCareRadio = this.selected.get('special-care-radio'); + const $neuteredRadio = this.selected.get('neutered-radio'); + const $cardGroup = this.selected.get('card-group'); + + this.vaccine = new Vaccine({ vaccines }); + this.vaccine.mount($cardGroup); + + createAndMount({ + name: 'specialCare', + text: 'Não', + mountTo: $specialCareRadio, + value: 'notSpecialCare', + }); + createAndMount({ + name: 'specialCare', + text: 'Sim', + mountTo: $specialCareRadio, + value: 'specialCare', + }); + createAndMount({ + name: 'neutered', + text: 'Não', + mountTo: $neuteredRadio, + value: 'notNeutered', + }); + createAndMount({ + name: 'neutered', + text: 'Sim', + mountTo: $neuteredRadio, + value: 'neutered', + }); + + this.button = new Button({ + text: 'Concluir', + isFullWidth: false, + isDisabled: false, + }); + + this.button.selected.get('button').classList.add('petvet-page__button'); + this.button.mount($footer); + + const form = { + isNeutered: undefined, + isSpecialCare: undefined, + specialCareText: '', + vaccines: undefined, + }; + + const emitForm = () => { + const neuteredValue = document.forms[0].elements.neutered.value; + const specialCareValue = document.forms[1].elements.specialCare.value; + + if (!neuteredValue || !specialCareValue) return; + + form.isNeutered = getBooleanValue(neuteredValue); + form.isSpecialCare = getBooleanValue(specialCareValue); + + form.vaccines = this.vaccine.listVaccines(); + this.emit('submit', form); + }; + + this.button.listen('click', emitForm); +} + +PetVetPage.prototype = Object.assign(PetVetPage.prototype, Component.prototype); diff --git a/src/layouts/app/pages/PetVet/index.scss b/src/layouts/app/pages/PetVet/index.scss new file mode 100644 index 00000000..f0136cd4 --- /dev/null +++ b/src/layouts/app/pages/PetVet/index.scss @@ -0,0 +1,159 @@ +@use '~styles/colors.scss' as colors; +@use '~styles/fonts.scss' as fonts; +@use '~styles/breakpoints' as breakpoints; + +.petvet-page { + height: 100%; + + display: flex; + flex-direction: column; + + align-items: center; + + &__header { + text-align: center; + + margin-bottom: 2.4rem; + + &--text { + font-family: fonts.$primaryFont; + color: colors.$gray800; + font-size: 1.8rem; + font-weight: fonts.$semiBold; + + line-height: 2.6; + } + + &--subtext { + font-family: fonts.$fourthFont; + color: colors.$gray700; + font-size: fonts.$sm; + font-weight: fonts.$regular; + + line-height: 2.2; + } + } + + &__card-group { + display: flex; + flex-direction: column; + + gap: 1.6rem; + } + + &__card { + font-family: fonts.$primaryFont; + color: colors.$gray800; + font-size: 1.4rem; + font-weight: fonts.$semiBold; + line-height: 2rem; + + padding: 1.6rem; + + box-shadow: 0 0 2px 2px rgba(50, 50, 71, 0.04); + + border-radius: 1.4rem; + } + + &__card-header { + display: flex; + gap: 1rem; + + align-items: center; + } + + &__card-content { + width: 100%; + + display: flex; + + flex-direction: column; + + gap: 2rem; + } + + &__img { + max-width: 5.4rem; + max-height: 5.4rem; + + display: flex; + + align-items: center; + } + + &__footer { + width: 100%; + + display: flex; + + justify-content: center; + } + + &__button { + width: min(100%, 42rem); + + margin-top: 2.4rem; + margin-bottom: 2.4rem; + } +} + +@include breakpoints.from667 { + .petvet-page { + &__img { + min-width: 6rem; + min-height: 6rem; + + display: flex; + + align-items: center; + } + + &__card { + padding: 2rem; + + border: 1px solid colors.$gray150; + + box-shadow: none; + border-radius: 1.8rem; + } + + &__card-header { + display: flex; + gap: 2rem; + + align-items: center; + } + + &__card-content { + flex-direction: row; + + align-items: center; + + justify-content: space-between; + } + + &__header { + &--subtext { + font-size: fonts.$sm; + } + } + + &__card-group { + gap: 2.4rem; + } + + &__button { + margin-bottom: 0; + } + } +} + +@container (min-height: 790px) { + @include breakpoints.from667 { + .petvet-page { + &__card-group { + overflow: auto; + } + } + } +} diff --git a/src/layouts/app/pages/PetVet/index.spec.js b/src/layouts/app/pages/PetVet/index.spec.js new file mode 100644 index 00000000..751d6b32 --- /dev/null +++ b/src/layouts/app/pages/PetVet/index.spec.js @@ -0,0 +1,191 @@ +import { describe, expect, it } from 'vitest'; +import { render, screen } from '@testing-library/vanilla'; +import { userEvent } from '@testing-library/user-event'; +import dayjs from 'dayjs'; +import PetVetPage from './index'; + +function formatDate(date) { + return date ? dayjs(date).format('MM/DD/YYYY') : ''; +} + +const mapVaccine = (vaccine) => ({ + id: vaccine.id, + title: vaccine.title, + date: formatDate(vaccine.date), + veterinary: vaccine.veterinary, +}); + +describe('PetVetPage', () => { + const mockVaccines = [ + { + id: '1', + veterinary: 'Dr octopus', + title: 'Antirrábica', + date: new Date().toISOString(), + }, + { + id: '2', + veterinary: 'Dr Felipa', + title: 'Raiva', + date: new Date(2023, 5, 2).toISOString(), + }, + { + id: '3', + veterinary: 'Dr octopus', + title: 'Raiva', + date: new Date(2023, 2, 2).toISOString(), + }, + ]; + const argsWithVaccine = { vaccines: mockVaccines }; + const argsWithVaccineEmpty = { vaccines: [] }; + const renderPetVetPage = (parameters) => render(new PetVetPage(parameters)); + + it('renders the main container', () => { + renderPetVetPage(argsWithVaccineEmpty); + const title = screen.getByText('Conte-nos um pouco mais do seu animal'); + + expect(title).toBeInTheDocument(); + }); + + describe('radios form', () => { + it('confirms that neutered "no" radio will be selected when clicked', async () => { + renderPetVetPage(argsWithVaccineEmpty); + + const falseRadio = screen.getByLabelText('castrado-não'); + + await userEvent.click(falseRadio); + + expect(falseRadio).toBeChecked(); + }); + + it('confirms that special care "yes" radio will be selected when clicked', async () => { + renderPetVetPage(argsWithVaccineEmpty); + + const trueRadio = screen.getByLabelText('cuidados-especiais-sim'); + + await userEvent.click(trueRadio); + expect(trueRadio).toBeChecked(); + }); + + it('confirms that both special care radios cannot be selected at the same time', async () => { + renderPetVetPage(argsWithVaccineEmpty); + + const falseRadio = screen.getByLabelText('cuidados-especiais-não'); + const trueRadio = screen.getByLabelText('cuidados-especiais-sim'); + + await userEvent.click(falseRadio); + expect(falseRadio).toBeChecked(); + expect(trueRadio).not.toBeChecked(); + + await userEvent.click(trueRadio); + expect(trueRadio).toBeChecked(); + expect(falseRadio).not.toBeChecked(); + }); + + it('confirms that both neutered radios cannot be selected at the same time', async () => { + renderPetVetPage(argsWithVaccineEmpty); + + const falseRadio = screen.getByLabelText('castrado-não'); + const trueRadio = screen.getByLabelText('castrado-sim'); + + await userEvent.click(falseRadio); + expect(falseRadio).toBeChecked(); + expect(trueRadio).not.toBeChecked(); + + await userEvent.click(trueRadio); + expect(trueRadio).toBeChecked(); + expect(falseRadio).not.toBeChecked(); + }); + }); + + describe('vaccine component', () => { + it('renders without vaccines list', () => { + renderPetVetPage(argsWithVaccineEmpty); + const vaccinesEvidence = screen.getByText('Vacinas'); + + expect(vaccinesEvidence).toBeInTheDocument(); + }); + + it('renders with vaccines list', () => { + renderPetVetPage(argsWithVaccine); + const vaccinesEvidence = screen.getByText('Vacinas'); + const someVaccineEvidence = screen.getByText(mockVaccines[0].title); + + expect(vaccinesEvidence).toBeInTheDocument(); + expect(someVaccineEvidence).toBeInTheDocument(); + }); + + describe('emits', () => { + it('emits the form data when every field is validated', async () => { + const petVetPage = renderPetVetPage(argsWithVaccine); + + const neuteredYesRadio = screen.getByLabelText('castrado-sim'); + const specialCareYesRadio = screen.getByLabelText( + 'cuidados-especiais-sim', + ); + + await userEvent.click(neuteredYesRadio); + await userEvent.click(specialCareYesRadio); + + const formExpected = { + isNeutered: true, + isSpecialCare: true, + specialCareText: '', + vaccines: mockVaccines.map(mapVaccine), + }; + + const callBackEmit = vi.fn(); + petVetPage.listen('submit', callBackEmit); + + const button = screen.getByRole('button'); + await userEvent.click(button); + + expect(callBackEmit).toHaveBeenCalledWith(formExpected); + }); + + it('does not emit the form data when special care radio are not check', async () => { + const petVetPage = renderPetVetPage(argsWithVaccine); + + const callBackEmit = vi.fn(); + petVetPage.listen('submit', callBackEmit); + + const neuteredYesRadio = screen.getByLabelText('castrado-sim'); + await userEvent.click(neuteredYesRadio); + + const button = screen.getByRole('button'); + await userEvent.click(button); + + expect(callBackEmit).not.toHaveBeenCalled(); + }); + + it('does not emit the form data when neutered radio are not check', async () => { + const petVetPage = renderPetVetPage(argsWithVaccine); + + const callBackEmit = vi.fn(); + petVetPage.listen('submit', callBackEmit); + + const neuteredYesRadio = screen.getByLabelText( + 'cuidados-especiais-sim', + ); + await userEvent.click(neuteredYesRadio); + + const button = screen.getByRole('button'); + await userEvent.click(button); + + expect(callBackEmit).not.toHaveBeenCalled(); + }); + + it('does not emit the form data when no radio button is selected', async () => { + const petVetPage = renderPetVetPage(argsWithVaccine); + + const callBackEmit = vi.fn(); + petVetPage.listen('submit', callBackEmit); + + const button = screen.getByRole('button'); + await userEvent.click(button); + + expect(callBackEmit).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/src/stories/PetRegister.stories.js b/src/stories/PetRegister.stories.js index 17073c6b..d909c332 100644 --- a/src/stories/PetRegister.stories.js +++ b/src/stories/PetRegister.stories.js @@ -1,4 +1,4 @@ -import PetRegister from '../home/components/PetRegister'; +import PetRegister from '../layouts/app/pages/PetRegister'; export default { title: 'Pages/PetProfile', diff --git a/src/stories/PetRegisterPage.stories.js b/src/stories/PetRegisterPage.stories.js index fe82bb33..4182e93c 100644 --- a/src/stories/PetRegisterPage.stories.js +++ b/src/stories/PetRegisterPage.stories.js @@ -1,4 +1,4 @@ -import PetRegisterPage from '../pages/layouts/PetRegisterPage'; +import PetRegisterPage from '../layouts/app/pages/PetRegister'; import afghanHound from './assets/petRegisterPage/afghanHound.svg'; import akita from './assets/petRegisterPage/akita.svg'; diff --git a/src/stories/PetVetPage.stories.js b/src/stories/PetVetPage.stories.js index b90912eb..bb533915 100644 --- a/src/stories/PetVetPage.stories.js +++ b/src/stories/PetVetPage.stories.js @@ -1,4 +1,4 @@ -import PetVetPage from '../home/components/PetVetPage'; +import PetVetPage from '../layouts/app/pages/PetVet'; export default { title: 'Pages/PetVetPage', diff --git a/src/stories/PetWeightPage.stories.js b/src/stories/PetWeightPage.stories.js index 77c0487a..7f27364f 100644 --- a/src/stories/PetWeightPage.stories.js +++ b/src/stories/PetWeightPage.stories.js @@ -1,4 +1,4 @@ -import PetWeightPage from '../pages/layouts/PetWeightPage'; +import PetWeightPage from '../layouts/app/pages/PetWeight'; export default { title: 'Pages/PetWeightPage',