From 70c9a5ae0ad3fb0ad8845de54955c55f22e0614c Mon Sep 17 00:00:00 2001 From: Joker Date: Thu, 3 Sep 2026 06:13:10 +0000 Subject: [PATCH] fix(dashboard): Register error prop pasa '' a MUI TextField (card 9151895a) error={confirmPassword && ...} evalua a '' (string) cuando confirmPassword esta vacio; MUI FormControl/TextField espera boolean y emite 'Invalid prop `error` of type `string`' en desarrollo. Envuelto en Boolean(...). Anadidos tests vitest+RTL: captura del propTypes warning via spy de console.error (printf-style, texto en args secundarios), mismatch muestra helper y match no muestra error. Card: 9151895a-5745-4a0d-af3b-f439a564db9e --- .../frontend/src/pages/Register.test.tsx | 79 +++++++++++++++++++ dashboard/frontend/src/pages/Register.tsx | 2 +- 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 dashboard/frontend/src/pages/Register.test.tsx diff --git a/dashboard/frontend/src/pages/Register.test.tsx b/dashboard/frontend/src/pages/Register.test.tsx new file mode 100644 index 00000000..85c18358 --- /dev/null +++ b/dashboard/frontend/src/pages/Register.test.tsx @@ -0,0 +1,79 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { MemoryRouter } from 'react-router-dom' +import { QueryClient, QueryClientProvider } from 'react-query' +import Register from './Register' + +// Register usa useNavigate/Link (Router) y useMutation (QueryClient), +// por eso hay que envolverlo antes de renderizar. +function renderRegister() { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { retry: false }, + mutations: { retry: false }, + }, + }) + return render( + + + + + + ) +} + +// React 18 emite los propTypes warnings estilo printf: +// console.error('Warning: Failed %s type: %s%s', 'prop', 'Invalid prop `error` ...', stack) +// El texto util esta repartido entre argumentos, por eso se inspeccionan todos. +// Nota: React deduplica estos warnings por componente dentro del proceso, asi que +// este test DEBE renderizar Register antes que los demas (orden del describe). +function hasPropTypesErrorWarning(spy: ReturnType): boolean { + return spy.mock.calls.some((call) => + call.some((arg) => String(arg).includes('Invalid prop `error`')) + ) +} + +describe('Register - prop error de TextField/FormControl (card 9151895a)', () => { + let consoleErrorSpy: ReturnType + + beforeEach(() => { + // MUI valida propTypes cuando NODE_ENV !== production: los warnings de + // React llegan via console.error, asi que espiamos ahi. + consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) + }) + + afterEach(() => { + consoleErrorSpy.mockRestore() + }) + + it('con confirmPassword vacio no emite propTypes warning por error:string', () => { + renderRegister() + + expect(hasPropTypesErrorWarning(consoleErrorSpy)).toBe(false) + }) + + it('muestra helper de mismatch cuando password y confirm no coinciden', async () => { + const user = userEvent.setup() + renderRegister() + + await user.type(screen.getByLabelText('Username'), 'joker') + await user.type(screen.getByLabelText('Email'), 'joker@test.com') + await user.type(screen.getByLabelText('Password'), 'password123') + await user.type(screen.getByLabelText('Confirm Password'), 'password999') + + expect(await screen.findByText('Passwords do not match')).toBeInTheDocument() + }) + + it('no muestra helper de error cuando las passwords coinciden', async () => { + const user = userEvent.setup() + renderRegister() + + await user.type(screen.getByLabelText('Password'), 'password123') + await user.type(screen.getByLabelText('Confirm Password'), 'password123') + + expect( + screen.queryByText('Passwords do not match') + ).not.toBeInTheDocument() + }) +}) diff --git a/dashboard/frontend/src/pages/Register.tsx b/dashboard/frontend/src/pages/Register.tsx index 6903e7c6..5626cdb5 100644 --- a/dashboard/frontend/src/pages/Register.tsx +++ b/dashboard/frontend/src/pages/Register.tsx @@ -175,7 +175,7 @@ export default function Register() { margin="normal" value={formData.confirmPassword} onChange={handleChange('confirmPassword')} - error={formData.confirmPassword && formData.password !== formData.confirmPassword} + error={Boolean(formData.confirmPassword && formData.password !== formData.confirmPassword)} helperText={ formData.confirmPassword && formData.password !== formData.confirmPassword ? 'Passwords do not match'