-
Notifications
You must be signed in to change notification settings - Fork 0
test: increase branch coverage 78.32% → 79.94% (#159) #173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -206,6 +206,62 @@ describe('PlanosClient', () => { | |
| expect(screen.queryAllByTestId('card')).toHaveLength(0); | ||
| }); | ||
|
|
||
| it('opens form in edit mode when "Editar" is clicked', () => { | ||
| render(<PlanosClient initialPlanos={mockPlanos} />); | ||
| const editButtons = screen.getAllByText('P'); | ||
| fireEvent.click(editButtons[0]); | ||
| expect(screen.getByTestId('form-plano')).toBeTruthy(); | ||
| expect(screen.getByText('Editando')).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('calls updatePlanoAction on edit submit', async () => { | ||
| const { updatePlanoAction } = await import('@/lib/actions/planos'); | ||
| vi.mocked(updatePlanoAction).mockResolvedValue({ | ||
| success: true, | ||
| data: { id: 'plano-1', nome: 'Mensal', preco: 100, duracaoDias: 30 }, | ||
| }); | ||
|
|
||
| render(<PlanosClient initialPlanos={mockPlanos} />); | ||
| fireEvent.click(screen.getAllByText('P')[0]); | ||
| fireEvent.click(screen.getByTestId('submit-plano')); | ||
|
|
||
| await waitFor(() => { | ||
| expect(updatePlanoAction).toHaveBeenCalled(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The Prompt for AI agents |
||
| expect(mockNotify.success).toHaveBeenCalledWith('Plano atualizado!', expect.any(String)); | ||
| }); | ||
| }); | ||
|
Comment on lines
+209
to
+232
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Inspect mockPlanos fixture to confirm whether any plano.nome is literally "P"
rg -n -B2 -A20 "mockPlanos" src/app/dashboard/planos/planos-client.test.tsx | head -80Repository: EmiyaKiritsugu3/PWeb_Project Length of output: 3499 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the relevant component markup for the edit button text.
FILE="src/app/dashboard/planos/planos-client.tsx"
wc -l "$FILE"
sed -n '1,260p' "$FILE" | nl -ba | sed -n '1,220p'Repository: EmiyaKiritsugu3/PWeb_Project Length of output: 253 🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE="src/app/dashboard/planos/planos-client.tsx"
rg -n -A8 -B8 'Editar|Excluir|form-plano|submit-plano' "$FILE"Repository: EmiyaKiritsugu3/PWeb_Project Length of output: 2813 Use the actual edit label here 🤖 Prompt for AI Agents |
||
|
|
||
| it('shows error on delete failure', async () => { | ||
| const { deletePlanoAction } = await import('@/lib/actions/planos'); | ||
| vi.mocked(deletePlanoAction).mockResolvedValue({ success: false, error: 'Erro ao excluir' }); | ||
|
|
||
| render(<PlanosClient initialPlanos={mockPlanos} />); | ||
| fireEvent.click(screen.getAllByText('Excluir')[0]); | ||
| fireEvent.click(screen.getAllByText('Excluir')[screen.getAllByText('Excluir').length - 1]); | ||
|
|
||
| await waitFor(() => { | ||
| expect(mockNotify.error).toHaveBeenCalledWith('Erro ao excluir', 'Erro ao excluir'); | ||
| }); | ||
| }); | ||
|
|
||
| it('displays "mês" badge when duration is exactly 30 days', () => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: Custom agent: Enforce Pragmatic Test Coverage The new test 'displays "mês" badge when duration is exactly 30 days' duplicates coverage already provided by the existing 'displays formatted duration badges' test. Since mockPlanos already includes a plano with duracaoDias: 30 (plano-1), the existing test already verifies that a 30-day duration renders the 'mês' badge. This test adds no new behavioral coverage. Consider removing it to keep the test suite focused, or if you want explicit documentation of the 30-day → 'mês' mapping, consider merging it into a parameterized test alongside the other duration cases. Prompt for AI agents |
||
| const customPlanos: Plano[] = [{ id: 'p2', nome: 'Mensal', preco: 100, duracaoDias: 30 }]; | ||
| render(<PlanosClient initialPlanos={customPlanos} />); | ||
| expect(screen.getByText('mês')).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('displays "2 meses" badge for 60-day duration', () => { | ||
| const customPlanos: Plano[] = [{ id: 'p3', nome: 'Bimestral', preco: 180, duracaoDias: 60 }]; | ||
| render(<PlanosClient initialPlanos={customPlanos} />); | ||
| expect(screen.getByText('2 meses')).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('displays "dias" badge when duration is not a multiple of 30', () => { | ||
| const customPlanos: Plano[] = [{ id: 'p1', nome: 'Personal', preco: 200, duracaoDias: 15 }]; | ||
| render(<PlanosClient initialPlanos={customPlanos} />); | ||
| expect(screen.getByText('15 dias')).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('calls deletePlanoAction when delete is confirmed', async () => { | ||
| const { deletePlanoAction } = await import('@/lib/actions/planos'); | ||
| vi.mocked(deletePlanoAction).mockResolvedValue({ success: true }); | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,5 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { describe, it, expect, vi } from 'vitest'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { render, screen } from '@testing-library/react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { describe, it, expect, vi, beforeEach } from 'vitest'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { render, screen, fireEvent, waitFor } from '@testing-library/react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import LoginPage from './page'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { ReactNode } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -15,6 +15,18 @@ vi.mock('react', async () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mockSignInWithPassword = vi.fn(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mockGetSession = vi.fn(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| vi.mock('@/utils/supabase/client', () => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createClient: () => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auth: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| signInWithPassword: mockSignInWithPassword, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getSession: mockGetSession, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| vi.mock('next/link', () => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: ({ children, href }: { children: ReactNode; href: string }) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <a href={href}>{children}</a> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -72,6 +84,10 @@ vi.mock('@/components/ui/separator', () => ({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe('LoginPage', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| beforeEach(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| vi.clearAllMocks(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+87
to
+90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n## src/app/login/page.test.tsx (outline)\n'
ast-grep outline src/app/login/page.test.tsx --view expanded || true
printf '\n## src/app/login/page.tsx (outline)\n'
ast-grep outline src/app/login/page.tsx --view expanded || true
printf '\n## Relevant test file excerpt\n'
sed -n '1,260p' src/app/login/page.test.tsx | cat -n
printf '\n## Relevant component excerpt\n'
sed -n '1,260p' src/app/login/page.tsx | cat -nRepository: EmiyaKiritsugu3/PWeb_Project Length of output: 14872 Stub 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('renders the login form', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<LoginPage />); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(screen.getByText('Five Star')).toBeTruthy(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -98,4 +114,79 @@ describe('LoginPage', () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<LoginPage />); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(screen.getByText((content) => content.includes('Five Star Fitness'))).toBeTruthy(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('shows error on invalid credentials', async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockSignInWithPassword.mockResolvedValue({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error: new Error('Invalid login credentials'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<LoginPage />); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fireEvent.change(screen.getByLabelText('Email corporativo'), { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| target: { value: 'wrong@test.com' }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fireEvent.change(screen.getByLabelText('Senha de acesso'), { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| target: { value: 'wrong' }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fireEvent.submit(screen.getByLabelText('Email corporativo').closest('form')!); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await waitFor(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| screen.getByText('E-mail ou senha inválidos. Por favor, tente novamente.') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).toBeTruthy(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(screen.getByText('Entrar no Sistema')).toBeTruthy(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('navigates to dashboard on successful login', async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockSignInWithPassword.mockResolvedValue({ error: null }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockGetSession.mockResolvedValue({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: { session: { user: { id: 'u1' } } }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const originalLocation = window.location; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Object.defineProperty(window, 'location', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The Prompt for AI agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: { ...originalLocation, href: '' }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| writable: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<LoginPage />); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fireEvent.change(screen.getByLabelText('Email corporativo'), { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| target: { value: 'admin@test.com' }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fireEvent.change(screen.getByLabelText('Senha de acesso'), { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| target: { value: 'correct' }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fireEvent.submit(screen.getByLabelText('Email corporativo').closest('form')!); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await waitFor(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(window.location.href).toBe('/dashboard'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Object.defineProperty(window, 'location', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: originalLocation, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| writable: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('shows "Autenticando..." while submitting', async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let resolveAuth!: (v: { error: null | Error }) => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockSignInWithPassword.mockImplementation( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| () => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new Promise((resolve) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resolveAuth = resolve; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<LoginPage />); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fireEvent.submit(screen.getByLabelText('Email corporativo').closest('form')!); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await waitFor(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(screen.getByText('Autenticando...')).toBeTruthy(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+171
to
+186
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: 'Autenticando...' test relies on Prompt for AI agents
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resolveAuth({ error: null }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ponytail: session poll timeout path tested implicitly by success test. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Poll loop is UX fallback (10×500ms) — would timeout vitest's 5s. Skip. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2:
screen.getAllByText('P')queries for elements containing the text "P" — this likely matches avatar fallback initials rather than the "Editar" button the test title describes. If the component renders edit buttons with a pencil icon or "Editar" label, this query will either click the wrong element or break if the avatar text changes. Consider usingscreen.getAllByText('Editar')or a role-based query (getByRole('button', { name: /editar/i })) to target the intended edit action.Prompt for AI agents