pular para o conteúdo principal
paste
bin
.ca
type · paste · share
⌘
K
Família
A família bin
pastebin.ca
central
Share text and code with expiry and privacy controls.
imagebin.ca
Upload and share images with direct links.
filebin.ca
Drop a file and get a shareable link.
notebin.ca
Write Markdown notes with durable links.
turl.ca
Short, reputation-checked links.
attn.ca
Notifications and alerts for your services.
voicebin.ca
Record and share short voice clips.
dnsbin.ca
Inspect DNS and debug records.
Docs
Entrar
?
← voltar para a publicação
›
Editar / bifurcar
Publicação sem título
#78UpCZW64J
public / public
nova versão
anônimo
criado 5 days ago
Expira em 2 days
11.6 KB
sintaxe:
text
Suas alterações criam uma nova publicação vinculada a esta — a original não é alterada.
nova versão
Suas alterações criam uma nova publicação vinculada a esta — a original não é alterada.
Título (opcional)
Nome do arquivo
Sintaxe
text
text
bash
c
cpp
css
diff
dockerfile
go
html
ini
java
javascript
json
kotlin
lua
makefile
markdown
nginx
php
python
ruby
rust
shellscript
sql
swift
toml
typescript
xml
yaml
Visibilidade
Feed público
Acesso
public
Expira
7 dias
10 min
1 hora
1 dia
7 dias
30 dias
90 dias
personalizada…
Expiração personalizada
Nota da alteração
(opcional)
Esta publicação aparecerá no feed público. Altere Visibilidade se quiser compartilhar apenas por link.
Criar nova versão
Cancelar
Cole ou digite…
--- name: project-quality-plan description: "Plano de auditoria e correção de boas práticas do vrg-backoffice-app — 3 fases: services, testes, código-fonte" metadata: node_type: memory type: project originSessionId: 9d13e1c6-72fb-4c0c-9f01-47d24d9469e4 modified: 2026-08-03T12:54:53.064Z --- Plano de execução em `/home/pauloweskley/workspace/backoffice/QUALITY_PLAN.md`. **Why:** Auditoria identificou violações sistemáticas nas três frentes abaixo. A correção parte dos services (causa raiz) para que testes e código-fonte sejam resolvidos em cascata. **How to apply:** Antes de qualquer tarefa no vrg-backoffice-app, verificar em qual fase o arquivo envolvido se enquadra e aplicar a correção correspondente. ## Fase 1 — Services: retornar tipos de domínio Todos os services (exceto `session.service.ts`) retornam `AxiosResponse<any>` sem desempacotar. Padrão correto: ```ts getSession: async (): Promise<Session> => { const res = await httpClient.get<{ data: Session }>("auth/session"); return res.data.data; } ``` Services pendentes: `auth`, `access-profiles`, `business-units`, `contracts`, `dashboard`, `employees`, `users`, `processes`, `user-profiles`. ## Fase 2 — Testes: corrigir violações Para cada arquivo de teste: 1. Criar `<nome>.mock.ts` na mesma pasta com todos os `jest.fn()` e fixtures tipadas 2. Remover `as never` / `as any` — usar tipo de domínio (disponível após Fase 1) 3. Adicionar comentários AAA em todos os `it()` com 4+ linhas 4. Traduzir descrições de `it()` / `describe()` para inglês 5. Achatar múltiplos `describe` em um único por arquivo Arquivos com múltiplos `describe` para achatar: - `config/http/http-client.test.ts`, `utils/permissions.test.ts`, `components/TableFooter/TableFooter.test.tsx`, `utils/format.util.test.ts`, `utils/parse-case.util.test.ts`, `compositions/Table/Table.test.tsx`, `ModuleFederated.test.tsx`, `layouts/AppLayout/AppLayout.test.tsx` ## Fase 3 — Código-fonte: naming, tipos, extração de hooks - **3a.** Renomear campos Zod em português → inglês (`senha` → `password`, `confirmacaoSenha` → `passwordConfirmation`) e atualizar consumidores - **3b.** `export interface` → `export type` em `table-cell-action.type.ts` - **3c.** Separar arquivos com múltiplos exports: `drawer-props.type.ts`, `table.stories.type.ts` - **3d.** Mover tipos single-use para inline: `table-cell.type.ts`, `top-loading-bar.type.ts`, `sidebar-resize.type.ts`, `auth-events.type.ts`, `http-client.type.ts` - **3e.** Extrair lógica para hooks: `useUserFormModal`, `useContractCancelModal`, `useContractChangeRootModal`, `useTopLoadingBar` ## Verificação final ```bash cd vrg-backoffice-app npx eslint src/ --max-warnings 0 npx tsc --noEmit npx jest --passWithNoTests ``` Relacionado: [[feedback-unit-tests]], [[feedback-code-quality-standards]], [[feedback-english-naming]] -------------- --- name: feedback-unit-tests description: Regras obrigatórias para testes unitários — padrão AAA, tipagem sem any, cobertura total, tipos do arquivo testado metadata: type: feedback originSessionId: current modified: 2026-07-24T15:07:42.279Z --- ## Um único `describe` por arquivo de teste Cada arquivo de teste deve ter **exatamente um** bloco `describe` — sem aninhamento, sem múltiplos describes no mesmo arquivo. Todos os `it()` ficam diretamente dentro desse único `describe`. ```ts // ERRADO — múltiplos describes aninhados describe("useAuthState", () => { describe("persistSession", () => { it("should ...", () => { ... }); }); describe("refresh", () => { it("should ...", () => { ... }); }); }); // CORRETO — um describe, todos os its diretos describe("useAuthState", () => { it("persistSession: should set cookies and mark session as authenticated", () => { ... }); it("refresh: should set session after successful call", () => { ... }); }); ``` **Why:** Padrão do projeto; múltiplos describes por arquivo foram explicitamente proibidos pelo usuário. **How to apply:** Ao criar qualquer arquivo de teste, usar um único `describe` com o nome do elemento testado. Para diferenciar contextos, incluir o contexto no nome do `it()` (ex: `"persistSession: should ..."`). ## Nunca criar funções auxiliares para fazer testes funcionarem Se for necessário criar uma função helper só para construir objetos de teste (ex: `createAxiosResponse`), isso é sinal de que a implementação está errada — não o teste. A causa raiz quase sempre é uma das seguintes: - O service está retornando a estrutura bruta do HTTP (`AxiosResponse`) em vez do dado limpo - O state/hook está desempacotando a resposta em vez de delegar isso ao service - O código de produção depende de detalhes de infraestrutura (Axios, fetch) que não devem vazar para camadas superiores **Solução correta:** corrigir a implementação para que o service retorne o tipo de domínio, e o mock passe o dado limpo diretamente. ```ts // ERRADO — service retorna AxiosResponse, state desempacota // session.service.ts getSession: () => httpClient.get<{ data: Session }>("auth/session") // auth.state.ts const res = await sessionService.getSession(); set({ session: res.data.data }) // teste precisa de helper artificial: mockSessionService.getSession.mockResolvedValue(createAxiosResponse({ data: fakeSession })) // CORRETO — service desempacota, state recebe dado limpo // session.service.ts getSession: async (): Promise<Session> => { const res = await httpClient.get<{ data: Session }>("auth/session"); return res.data.data; } // auth.state.ts const session = await sessionService.getSession(); set({ session }) // teste é simples e direto: mockSessionService.getSession.mockResolvedValue(fakeSession) ``` **Why:** Funções auxiliares de teste são workarounds para design ruim. Spies e mocks são os únicos artefatos que pertencem aos testes. Se precisar de mais do que isso, corrija a implementação. **How to apply:** Antes de criar qualquer helper de teste, perguntar: "por que o mock não consegue retornar o tipo de domínio diretamente?" A resposta aponta o que deve ser corrigido na implementação. ## Casting de mocks — nunca `as never`, nunca cast direto incompatível Ao mockar retornos de funções que retornam `Promise<AxiosResponse<...>>` (ou qualquer tipo sem sobreposição com o objeto literal), usar `as unknown as TipoAlvo` — nunca `as never` e nunca cast direto que o TypeScript rejeite. ```ts // ERRADO — cast direto sem sobreposição suficiente mockSessionService.getSession.mockResolvedValue({ data: { data: fakeSession }, } as jest.Mocked<ReturnType<typeof mockSessionService.getSession>>); // ERRADO — as never mockService.method.mockResolvedValue(result as never); // CORRETO — double cast via unknown import type { AxiosResponse } from "axios"; mockSessionService.getSession.mockResolvedValue( { data: { data: fakeSession } } as unknown as AxiosResponse, ); ``` **Why:** O TypeScript exige sobreposição suficiente de tipos em casts diretos; sem ela o compilador rejeita. `as unknown as T` é o padrão correto para forçar o tipo sem `any` ou `never`. **How to apply:** Sempre que `mockResolvedValue` / `mockReturnValue` receber um objeto literal incompatível com o tipo inferido, usar `as unknown as AxiosResponse` (ou o tipo correto do retorno da função mockada). ## Padrão AAA obrigatório Todo teste deve seguir estritamente o padrão **Arrange / Act / Assert**: ```ts it('should return user data when getById is called with valid id', () => { // Arrange const userId = 1; const expectedUser: User = { id: 1, name: 'John', email: 'john@example.com' }; mockGetById.mockResolvedValue(expectedUser); // Act const result = service.getById(userId); // Assert expect(result).resolves.toEqual(expectedUser); }); ``` **Why:** Padronização que facilita leitura, manutenção e revisão dos testes. **How to apply:** Toda vez que escrever um `it(...)` ou `test(...)`, estruturar com os três blocos comentados. ## Tipagem — nunca `any` - Nunca tipar variáveis, mocks, parâmetros ou retornos com `any` nos testes. - Usar os tipos que já existem nos arquivos sendo testados — importar diretamente deles. - **Não criar types novos dentro do arquivo de teste** — se o type precisa existir, ele deve estar no arquivo fonte. ```ts // ERRADO const user: any = { id: 1, name: 'John' }; const mockFn = jest.fn() as any; // CORRETO — usar o type importado do arquivo fonte import type { User } from '@types/user.type'; const user: User = { id: 1, name: 'John', email: 'john@example.com' }; const mockFn = jest.fn<Promise<User>, [number]>(); ``` **Why:** `any` elimina a segurança de tipos e esconde erros que seriam pegos em compile time. **How to apply:** Ao escrever qualquer variável em teste, verificar qual type ela deve ter e importar do arquivo testado ou de seus tipos relacionados. ## Nomes autosugestivos em testes Variáveis, funções auxiliares e descrições de `it()`/`describe()` devem ser autosugestivos: ```ts // ERRADO const data = { id: 1 }; const fn = jest.fn(); it('test 1', () => { ... }); // CORRETO const activeUser: User = { id: 1, name: 'Alice', email: 'alice@vr.com' }; const mockFetchUserById = jest.fn<Promise<User>, [number]>(); it('should throw NotFoundError when user does not exist', () => { ... }); ``` **Why:** Testes são documentação viva; nomes obscuros tornam diagnóstico de falhas muito mais lento. **How to apply:** Toda variável de teste deve revelar o que representa (ex: `inactiveContract`, `expiredToken`, `emptyUserList`). ## Cobertura obrigatória ao criar arquivos Ao criar **qualquer** arquivo de componente, service, hook, provider ou utilitário, criar obrigatoriamente o arquivo de teste correspondente cobrindo **todos os cenários**: - Caminho feliz (happy path) - Casos de borda (edge cases): lista vazia, valor nulo, undefined, strings vazias - Cenários de erro: exceções lançadas, falhas de rede, estado inválido - Variações de props (para componentes): obrigatórias vs opcionais, valores extremos ``` MyComponent/ MyComponent.tsx MyComponent.module.css MyComponent.test.tsx ← obrigatório ``` **Why:** O usuário exige que qualquer entrega de código venha acompanhada de testes que garantam todos os cenários do comportamento implementado. **How to apply:** Antes de reportar qualquer arquivo como concluído, verificar se o arquivo de teste correspondente foi criado e se cobre todos os branches de lógica. ## Mocks em arquivo separado `.mock.ts` Mocks de dependências externas (services, cookies, eventos, providers) devem ficar em um arquivo `<nome>.mock.ts` na **mesma pasta** do arquivo testado — nunca declarados inline no arquivo de teste. ``` states/ auth.state.ts auth.state.mock.ts ← exporta todos os mocks necessários auth.state.test.ts ← importa do .mock.ts components/UserList/ UserList.tsx UserList.module.css UserList.mock.ts ← mocks de hooks, services usados pelo componente UserList.test.tsx ``` ```ts // auth.state.mock.ts export const mockGetSession = jest.fn(); export const mockLogout = jest.fn(); export const mockSetCookie = jest.fn(); export const mockEraseCookie = jest.fn(); export const mockRecoveryCookie = jest.fn(); // auth.state.test.ts import { mockGetSession, mockLogout } from './auth.state.mock'; ``` **Why:** Mantém o arquivo de teste menor e legível; mocks ficam reutilizáveis por outros testes que dependem das mesmas dependências (ex: componente que usa o mesmo store ou service). **How to apply:** Ao criar qualquer arquivo de teste, extrair todos os `jest.fn()` e configurações de mock para um `<nome>.mock.ts` colocado ao lado do arquivo testado. O arquivo de teste só importa e usa. Relacionado: [[feedback-code-quality-standards]], [[feedback-english-naming]]