@thiagodiogo/pscode 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +2 -2
  2. package/bin/pscode.js +0 -0
  3. package/dist/cli/index.js +6 -7
  4. package/dist/commands/config.d.ts +4 -12
  5. package/dist/commands/config.js +69 -242
  6. package/dist/core/change-metadata/schema.d.ts +1 -0
  7. package/dist/core/change-metadata/schema.js +1 -0
  8. package/dist/core/{archive.d.ts → complete.d.ts} +2 -3
  9. package/dist/core/{archive.js → complete.js} +63 -64
  10. package/dist/core/completions/command-registry.js +5 -9
  11. package/dist/core/config-schema.d.ts +1 -5
  12. package/dist/core/config-schema.js +2 -5
  13. package/dist/core/global-config.d.ts +1 -3
  14. package/dist/core/global-config.js +1 -1
  15. package/dist/core/init.d.ts +2 -0
  16. package/dist/core/init.js +81 -21
  17. package/dist/core/jira-transition.d.ts +16 -0
  18. package/dist/core/jira-transition.js +29 -0
  19. package/dist/core/migration.d.ts +3 -12
  20. package/dist/core/migration.js +10 -72
  21. package/dist/core/presets/dixi.d.ts +32 -0
  22. package/dist/core/presets/dixi.js +405 -0
  23. package/dist/core/profile-sync-drift.js +9 -2
  24. package/dist/core/profiles.d.ts +23 -21
  25. package/dist/core/profiles.js +28 -25
  26. package/dist/core/shared/skill-generation.js +3 -5
  27. package/dist/core/shared/tool-detection.d.ts +2 -2
  28. package/dist/core/shared/tool-detection.js +1 -3
  29. package/dist/core/templates/skill-templates.d.ts +1 -2
  30. package/dist/core/templates/skill-templates.js +1 -2
  31. package/dist/core/templates/workflows/apply-change.js +3 -3
  32. package/dist/core/templates/workflows/archive-change.d.ts +2 -2
  33. package/dist/core/templates/workflows/archive-change.js +10 -10
  34. package/dist/core/templates/workflows/onboard.js +9 -9
  35. package/dist/core/update.d.ts +1 -6
  36. package/dist/core/update.js +5 -29
  37. package/dist/core/workspace/foundation.d.ts +1 -1
  38. package/dist/core/workspace/foundation.js +1 -1
  39. package/dist/core/workspace/legacy-state.js +1 -1
  40. package/dist/core/workspace/skills.d.ts +4 -3
  41. package/dist/core/workspace/skills.js +3 -3
  42. package/package.json +21 -22
  43. package/pscode/content/dixi/architectures/feature-sliced-react/eslint-architecture.mjs.template +44 -0
  44. package/pscode/content/dixi/architectures/feature-sliced-react/features/README.md.template +30 -0
  45. package/pscode/content/dixi/architectures/feature-sliced-react/skeleton.yaml +8 -0
  46. package/pscode/content/dixi/architectures/hexagonal-spring/ArchitectureTest.java.template +41 -0
  47. package/pscode/content/dixi/architectures/hexagonal-spring/skeleton.yaml +11 -0
  48. package/pscode/content/dixi/claude-runtime/CLAUDE.md.java.template +62 -0
  49. package/pscode/content/dixi/claude-runtime/CLAUDE.md.react.template +74 -0
  50. package/pscode/content/dixi/claude-runtime/commands/adr.md +75 -0
  51. package/pscode/content/dixi/claude-runtime/commands/arch-check.md +64 -0
  52. package/pscode/content/dixi/claude-runtime/commands/dod.md +66 -0
  53. package/pscode/content/dixi/claude-runtime/commands/jira-draft.md +80 -0
  54. package/pscode/content/dixi/claude-runtime/commands/jira-setup.md +105 -0
  55. package/pscode/content/dixi/claude-runtime/commands/jira-sync.md +69 -0
  56. package/pscode/content/dixi/claude-runtime/commands/rfc.md +73 -0
  57. package/pscode/content/dixi/claude-runtime/hooks/arch-guard.mjs +101 -0
  58. package/pscode/content/dixi/claude-runtime/hooks/jira-context.mjs +60 -0
  59. package/pscode/content/dixi/claude-runtime/skills/pstld-arch-guardian.md +101 -0
  60. package/pscode/content/dixi/claude-runtime/skills/pstld-commit-crafter.md +98 -0
  61. package/pscode/content/dixi/claude-runtime/skills/pstld-jira-context.md +64 -0
  62. package/pscode/content/dixi/context/java/architecture.md +143 -0
  63. package/pscode/content/dixi/context/java/naming.md +62 -0
  64. package/pscode/content/dixi/context/java/testing.md +162 -0
  65. package/pscode/content/dixi/context/react/architecture.md +119 -0
  66. package/pscode/content/dixi/context/react/naming.md +129 -0
  67. package/pscode/content/dixi/context/react/testing.md +141 -0
  68. package/pscode/content/dixi/context/shared/commits.md +47 -0
  69. package/pscode/content/dixi/context/shared/dev-flow.md +53 -0
  70. package/pscode/content/dixi/context/shared/dod.md +38 -0
  71. package/pscode/content/dixi/context/shared/pr-flow.md +53 -0
  72. package/pscode/content/dixi/kit/java/.editorconfig +25 -0
  73. package/pscode/content/dixi/kit/java/.github/workflows/ci-java.yml +68 -0
  74. package/pscode/content/dixi/kit/java/.husky/commit-msg +2 -0
  75. package/pscode/content/dixi/kit/react/.editorconfig +20 -0
  76. package/pscode/content/dixi/kit/react/.github/workflows/ci-react.yml +80 -0
  77. package/pscode/content/dixi/kit/react/.husky/commit-msg +2 -0
  78. package/pscode/content/dixi/kit/react/.husky/pre-commit +2 -0
  79. package/pscode/content/dixi/kit/react/lint-staged.config.mjs +4 -0
  80. package/pscode/content/dixi/kit/shared/.commitlintrc.yml +15 -0
  81. package/pscode/content/dixi/kit/shared/.github/pull_request_template.md +24 -0
  82. package/schemas/pstld-workflow/schema.yaml +67 -0
  83. package/schemas/pstld-workflow/templates/design.md +15 -0
  84. package/schemas/pstld-workflow/templates/rfc.md +26 -0
  85. package/schemas/pstld-workflow/templates/tasks.md +15 -0
  86. package/dist/core/templates/workflows/sync-specs.d.ts +0 -10
  87. package/dist/core/templates/workflows/sync-specs.js +0 -290
@@ -0,0 +1,62 @@
1
+ # Convenções de Nomenclatura — Java/Spring
2
+
3
+ ## Regra geral
4
+
5
+ O nome deve revelar o papel da classe na arquitetura hexagonal. Evite sufixos técnicos no domínio; use sufixos técnicos apenas na infraestrutura.
6
+
7
+ ## Domain
8
+
9
+ Substantivos sem sufixo técnico. Nomes de negócio, não de tecnologia.
10
+
11
+ | Tipo | Padrão | Exemplos válidos | Inválidos |
12
+ |------------------|----------------------|----------------------------------------|------------------------------------|
13
+ | Entidade | `NomeDominio` | `Pedido`, `Usuario`, `Produto` | `PedidoEntity`, `UsuarioModel` |
14
+ | Value Object | `NomeDominio` | `Email`, `CPF`, `PedidoId` | `EmailVO`, `CPFValue` |
15
+ | Exceção domínio | `Motivo + Exception` | `PedidoVazioException`, `SaldoInsuficienteException` | `PedidoError`, `InvalidPedido` |
16
+ | Porta de entrada | `Verbo + UseCase` | `ConfirmarPedidoUseCase`, `BuscarUsuarioUseCase` | `PedidoService`, `PedidoPort` |
17
+ | Porta de saída | `NomeDominio + Repository/Gateway` | `PedidoRepository`, `NotificacaoGateway` | `IPedidoRepo`, `PedidoDAO` |
18
+
19
+ ## Application
20
+
21
+ Implementações dos use cases. Verbo + sufixo `Service`.
22
+
23
+ | Tipo | Padrão | Exemplos válidos | Inválidos |
24
+ |-------------|---------------------|-------------------------------------------|----------------------------------|
25
+ | Use Case impl | `Verbo + Service` | `ConfirmarPedidoService`, `CadastrarUsuarioService` | `PedidoServiceImpl`, `OrderManager` |
26
+
27
+ ## Infrastructure
28
+
29
+ Sufixos técnicos que revelam o mecanismo de implementação.
30
+
31
+ | Tipo | Padrão | Exemplos válidos |
32
+ |------------------------|---------------------------|-----------------------------------------------|
33
+ | Controller REST | `Dominio + Controller` | `PedidoController`, `UsuarioController` |
34
+ | JPA Entity | `Dominio + JpaEntity` | `PedidoJpaEntity`, `UsuarioJpaEntity` |
35
+ | JPA Repository impl | `Dominio + JpaRepository` | `PedidoJpaRepository` |
36
+ | Spring Data interface | `Dominio + JpaEntityRepository` | `PedidoJpaEntityRepository` |
37
+ | HTTP Client | `Servico + Client` | `PagamentoClient`, `NotificacaoClient` |
38
+ | Message Consumer | `Evento + Consumer` | `PedidoCriadoConsumer` |
39
+ | Message Producer | `Evento + Producer` | `PedidoConfirmadoProducer` |
40
+ | Mapper | `Dominio + Mapper` | `PedidoMapper`, `UsuarioMapper` |
41
+ | DTO de entrada | `Acao + Request` | `ConfirmarPedidoRequest`, `CadastrarUsuarioRequest` |
42
+ | DTO de saída | `Dominio + Response` | `PedidoResponse`, `UsuarioResponse` |
43
+
44
+ ## Packages
45
+
46
+ Siga a estrutura hexagonal. Use nomes de domínio em lowercase, sem underscores.
47
+
48
+ ```
49
+ com.empresa.produto.pedido.domain.model
50
+ com.empresa.produto.pedido.domain.port.in
51
+ com.empresa.produto.pedido.application.usecase
52
+ com.empresa.produto.pedido.infrastructure.adapter.in
53
+ ```
54
+
55
+ ## Métodos
56
+
57
+ | Tipo | Padrão | Exemplos |
58
+ |------------------------|-----------------------|--------------------------------------------------|
59
+ | Use case (comando) | verbo no infinitivo | `confirmar()`, `cancelar()`, `processar()` |
60
+ | Use case (query) | `buscar/listar/obter` | `buscarPorId()`, `listarAtivos()`, `obterSaldo()`|
61
+ | Domain action | verbo de negócio | `ativar()`, `debitar()`, `aplicarDesconto()` |
62
+ | Factory method | `criar/novo` | `criar()`, `criarVazio()`, `novaInstancia()` |
@@ -0,0 +1,162 @@
1
+ # Testes — Java/Spring
2
+
3
+ ## Pirâmide de testes
4
+
5
+ ```
6
+ [E2E — RestAssured] ← poucos, fluxos críticos
7
+ [Integração — Testcontainers] ← adapters de saída, APIs
8
+ [Unitários — JUnit 5 + Mockito] ← domain e application (maioria)
9
+ ```
10
+
11
+ ## Nível 1 — Testes unitários (domain e application)
12
+
13
+ Testam lógica pura sem Spring, sem banco, sem rede.
14
+
15
+ **Ferramentas:** JUnit 5, Mockito
16
+
17
+ **Onde ficam:** `test/java/.../domain/` e `test/java/.../application/`
18
+
19
+ ```java
20
+ // Teste de domain
21
+ class PedidoTest {
22
+ @Test
23
+ void dado_pedido_com_itens_quando_confirmar_entao_status_confirmado() {
24
+ // Given
25
+ Pedido pedido = PedidoTestFactory.comUmItem();
26
+
27
+ // When
28
+ pedido.confirmar();
29
+
30
+ // Then
31
+ assertThat(pedido.getStatus()).isEqualTo(StatusPedido.CONFIRMADO);
32
+ }
33
+
34
+ @Test
35
+ void dado_pedido_vazio_quando_confirmar_entao_lanca_excecao() {
36
+ Pedido pedido = PedidoTestFactory.vazio();
37
+ assertThatThrownBy(pedido::confirmar)
38
+ .isInstanceOf(PedidoVazioException.class);
39
+ }
40
+ }
41
+ ```
42
+
43
+ ```java
44
+ // Teste de application (use case)
45
+ class ConfirmarPedidoServiceTest {
46
+ @Mock PedidoRepository repository;
47
+ @InjectMocks ConfirmarPedidoService service;
48
+
49
+ @Test
50
+ void dado_pedido_existente_quando_confirmar_entao_salva_e_retorna() {
51
+ // Given
52
+ Pedido pedido = PedidoTestFactory.comUmItem();
53
+ when(repository.findById(pedido.getId())).thenReturn(Optional.of(pedido));
54
+ when(repository.save(any())).thenAnswer(inv -> inv.getArgument(0));
55
+
56
+ // When
57
+ Pedido resultado = service.confirmar(pedido.getId());
58
+
59
+ // Then
60
+ assertThat(resultado.getStatus()).isEqualTo(StatusPedido.CONFIRMADO);
61
+ verify(repository).save(pedido);
62
+ }
63
+ }
64
+ ```
65
+
66
+ ## Nível 2 — Testes de integração (infrastructure)
67
+
68
+ Testam adapters com dependências reais (banco, mensageria, APIs externas).
69
+
70
+ **Ferramentas:** Testcontainers, `@SpringBootTest` com perfil de teste
71
+
72
+ **Onde ficam:** `test/java/.../infrastructure/`
73
+
74
+ ```java
75
+ @SpringBootTest
76
+ @Testcontainers
77
+ class PedidoJpaRepositoryIT {
78
+ @Container
79
+ static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
80
+
81
+ @DynamicPropertySource
82
+ static void props(DynamicPropertyRegistry r) {
83
+ r.add("spring.datasource.url", postgres::getJdbcUrl);
84
+ }
85
+
86
+ @Autowired PedidoRepository repository;
87
+
88
+ @Test
89
+ void dado_pedido_salvo_quando_buscar_por_id_entao_retorna() {
90
+ Pedido pedido = repository.save(PedidoTestFactory.comUmItem());
91
+ assertThat(repository.findById(pedido.getId())).isPresent();
92
+ }
93
+ }
94
+ ```
95
+
96
+ ## Nível 3 — Testes E2E (fluxos críticos)
97
+
98
+ Testam a API completa via HTTP, do controller ao banco.
99
+
100
+ **Ferramentas:** RestAssured, Testcontainers (banco real)
101
+
102
+ ```java
103
+ @SpringBootTest(webEnvironment = RANDOM_PORT)
104
+ @Testcontainers
105
+ class ConfirmarPedidoE2ETest {
106
+ @LocalServerPort int port;
107
+
108
+ @Test
109
+ void confirmar_pedido_retorna_200_com_status_confirmado() {
110
+ String pedidoId = criarPedidoViaApi();
111
+
112
+ given()
113
+ .port(port)
114
+ .contentType(ContentType.JSON)
115
+ .when()
116
+ .post("/pedidos/{id}/confirmar", pedidoId)
117
+ .then()
118
+ .statusCode(200)
119
+ .body("status", equalTo("CONFIRMADO"));
120
+ }
121
+ }
122
+ ```
123
+
124
+ ## Nomenclatura
125
+
126
+ Padrão: `dado_[contexto]_quando_[acao]_entao_[resultado]`
127
+
128
+ ```
129
+ dado_pedido_com_itens_quando_confirmar_entao_status_confirmado
130
+ dado_usuario_inativo_quando_autenticar_entao_lanca_UsuarioInativoException
131
+ dado_estoque_zerado_quando_reservar_entao_retorna_false
132
+ ```
133
+
134
+ ## Cobertura mínima
135
+
136
+ | Camada | Cobertura mínima |
137
+ |-----------------|-----------------|
138
+ | `domain` | 90% |
139
+ | `application` | 80% |
140
+ | `infrastructure`| 60% (integração complementa) |
141
+
142
+ Configure no `pom.xml` com JaCoCo:
143
+
144
+ ```xml
145
+ <configuration>
146
+ <rules>
147
+ <rule>
148
+ <element>PACKAGE</element>
149
+ <includes>
150
+ <include>**.domain.**</include>
151
+ <include>**.application.**</include>
152
+ </includes>
153
+ <limits>
154
+ <limit>
155
+ <counter>LINE</counter>
156
+ <minimum>0.80</minimum>
157
+ </limit>
158
+ </limits>
159
+ </rule>
160
+ </rules>
161
+ </configuration>
162
+ ```
@@ -0,0 +1,119 @@
1
+ # Arquitetura — React/Next.js (Feature-Sliced Design)
2
+
3
+ ## Camadas (de baixo para cima)
4
+
5
+ ```
6
+ pages / app ← Next.js routes, composição de features
7
+ features ← unidades funcionais independentes
8
+ entities ← modelos de negócio reutilizáveis
9
+ shared ← utilitários, UI base, types globais
10
+ ```
11
+
12
+ Camadas superiores podem importar das inferiores. **Camadas inferiores nunca importam das superiores.**
13
+
14
+ ## Estrutura de pastas
15
+
16
+ ```
17
+ src/
18
+ ├── app/ # Next.js App Router (ou pages/)
19
+ │ ├── layout.tsx
20
+ │ └── checkout/
21
+ │ └── page.tsx # Apenas composição de features
22
+ ├── features/
23
+ │ ├── checkout/ # Feature autocontida
24
+ │ │ ├── components/ # Componentes internos da feature
25
+ │ │ ├── hooks/ # Hooks internos
26
+ │ │ ├── services/ # Chamadas de API da feature
27
+ │ │ ├── types/ # Tipos TypeScript da feature
28
+ │ │ └── index.ts # Barrel export — única entrada pública
29
+ │ └── auth/
30
+ │ └── index.ts
31
+ ├── entities/
32
+ │ ├── product/
33
+ │ │ ├── model.ts # Tipos e schemas da entidade
34
+ │ │ └── index.ts
35
+ │ └── user/
36
+ │ └── index.ts
37
+ └── shared/
38
+ ├── ui/ # Componentes base (Button, Input, Modal)
39
+ ├── lib/ # Utilitários e helpers
40
+ ├── api/ # Cliente HTTP base (axios, fetch wrapper)
41
+ └── types/ # Tipos globais compartilhados
42
+ ```
43
+
44
+ ## Regras fundamentais
45
+
46
+ ### No cross-import entre features
47
+
48
+ Features **não podem importar umas das outras** diretamente:
49
+
50
+ ```typescript
51
+ // ❌ PROIBIDO
52
+ import { useCart } from '../cart'; // feature cart importando de outra feature
53
+
54
+ // ✅ CORRETO — mova o dado compartilhado para entities
55
+ import { Product } from '@/entities/product';
56
+ ```
57
+
58
+ Se duas features precisam de algo em comum, mova esse algo para `entities` ou `shared`.
59
+
60
+ ### Barrel export como contrato público
61
+
62
+ Tudo que sai de uma feature deve passar pelo `index.ts`:
63
+
64
+ ```typescript
65
+ // features/checkout/index.ts
66
+ export { CheckoutPage } from './components/CheckoutPage';
67
+ export { useCheckoutForm } from './hooks/useCheckoutForm';
68
+ export type { CheckoutFormData } from './types';
69
+ // Services e hooks internos NÃO são exportados
70
+ ```
71
+
72
+ Externos à feature importam **apenas do barrel**:
73
+
74
+ ```typescript
75
+ // ✅ CORRETO
76
+ import { CheckoutPage } from '@/features/checkout';
77
+
78
+ // ❌ PROIBIDO — acesso direto a módulo interno
79
+ import { CheckoutPage } from '@/features/checkout/components/CheckoutPage';
80
+ ```
81
+
82
+ ### Pages/App são apenas composição
83
+
84
+ Pages não contêm lógica de negócio — apenas montam features:
85
+
86
+ ```typescript
87
+ // app/checkout/page.tsx
88
+ import { CheckoutPage } from '@/features/checkout';
89
+ import { AuthGuard } from '@/features/auth';
90
+
91
+ export default function Page() {
92
+ return (
93
+ <AuthGuard>
94
+ <CheckoutPage />
95
+ </AuthGuard>
96
+ );
97
+ }
98
+ ```
99
+
100
+ ## Quando criar uma nova feature
101
+
102
+ Crie uma nova feature quando:
103
+ - A funcionalidade tem um domínio claramente delimitado
104
+ - Possui componentes, hooks e serviços próprios
105
+ - Não é reutilizável por natureza (se for reutilizável, considere `entities` ou `shared`)
106
+
107
+ ## Skeleton e guardrails automáticos (profile dixi)
108
+
109
+ `pscode init --profile dixi` cria automaticamente a estrutura feature-sliced com `.gitkeep` nos diretórios folha e `features/README.md` com as convenções documentadas. Também instala `eslint-architecture.mjs` na raiz com regras `no-restricted-imports` para:
110
+
111
+ - Isolar features entre si (features não importam umas das outras)
112
+ - Impedir que páginas importem lógica de negócio diretamente
113
+
114
+ Para ativar as regras no ESLint, adicione ao `eslint.config.js`:
115
+
116
+ ```js
117
+ import architectureRules from './eslint-architecture.mjs';
118
+ export default [...existingConfig, ...architectureRules];
119
+ ```
@@ -0,0 +1,129 @@
1
+ # Convenções de Nomenclatura — React/Next.js
2
+
3
+ ## Resumo rápido
4
+
5
+ | Tipo | Convenção | Exemplos |
6
+ |-----------------------|--------------------|----------------------------------------------|
7
+ | Componente React | `PascalCase` | `CheckoutForm`, `UserAvatar`, `ProductCard` |
8
+ | Hook | `use` + PascalCase | `useCheckoutForm`, `useAuth`, `useProductList`|
9
+ | Service / função util | `camelCase` | `submitCheckout`, `formatCurrency`, `parseDate`|
10
+ | Arquivo de componente | `kebab-case.tsx` | `checkout-form.tsx`, `user-avatar.tsx` |
11
+ | Arquivo de hook | `use-*.ts` | `use-checkout-form.ts`, `use-auth.ts` |
12
+ | Arquivo de service | `*-service.ts` | `checkout-service.ts`, `auth-service.ts` |
13
+ | Constante | `SCREAMING_SNAKE` | `MAX_ITEMS`, `API_BASE_URL`, `DEFAULT_TIMEOUT`|
14
+ | Tipo / Interface | `PascalCase` | `CheckoutFormData`, `UserProfile`, `ApiError`|
15
+ | Enum | `PascalCase` (valor `SCREAMING_SNAKE`) | `enum Status { ACTIVE = 'ACTIVE' }` |
16
+
17
+ ## Componentes React
18
+
19
+ **PascalCase** — sempre.
20
+
21
+ ```typescript
22
+ // ✅ Correto
23
+ export function CheckoutForm() { ... }
24
+ export const UserAvatar: React.FC<Props> = () => { ... }
25
+
26
+ // ❌ Errado
27
+ export function checkoutForm() { ... }
28
+ export function checkout_form() { ... }
29
+ ```
30
+
31
+ Arquivos de componente em **kebab-case**:
32
+ ```
33
+ checkout-form.tsx ✅
34
+ CheckoutForm.tsx ❌ (exceto se a convenção do projeto usar PascalCase em arquivos)
35
+ ```
36
+
37
+ ## Hooks
38
+
39
+ `use` + PascalCase. O nome deve descrever o que o hook faz ou gerencia.
40
+
41
+ ```typescript
42
+ // ✅ Correto
43
+ function useCheckoutForm() { ... }
44
+ function useProductList(categoryId: string) { ... }
45
+ function useAuth() { ... }
46
+
47
+ // ❌ Errado
48
+ function checkoutFormHook() { ... }
49
+ function UseCheckoutForm() { ... } // PascalCase não é hook
50
+ function useGetProductList() { ... } // evite "get" — já está implícito
51
+ ```
52
+
53
+ ## Services (funções de acesso a API ou lógica assíncrona)
54
+
55
+ **camelCase** para a função, arquivo em **kebab-case**:
56
+
57
+ ```typescript
58
+ // checkout-service.ts
59
+ export async function submitCheckout(data: CheckoutData): Promise<OrderResponse> { ... }
60
+ export async function fetchCheckoutSummary(cartId: string): Promise<Summary> { ... }
61
+
62
+ // ❌ Errado
63
+ export async function SubmitCheckout() { ... } // PascalCase é componente
64
+ export async function submit_checkout() { ... } // snake_case não é JS/TS
65
+ ```
66
+
67
+ ## Constantes
68
+
69
+ **SCREAMING_SNAKE_CASE** para valores imutáveis de escopo global ou de módulo:
70
+
71
+ ```typescript
72
+ // ✅ Correto
73
+ const MAX_RETRY_ATTEMPTS = 3;
74
+ const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL;
75
+ const DEFAULT_PAGE_SIZE = 20;
76
+
77
+ // ❌ Errado
78
+ const maxRetryAttempts = 3; // parece variável local mutável
79
+ const MaxRetryAttempts = 3; // PascalCase é componente ou tipo
80
+ ```
81
+
82
+ ## Tipos e Interfaces
83
+
84
+ **PascalCase**, com sufixo descritivo quando necessário para clareza:
85
+
86
+ ```typescript
87
+ // ✅ Correto
88
+ type CheckoutFormData = { email: string; items: CartItem[] };
89
+ interface UserProfile { id: string; name: string }
90
+ type ApiError = { code: string; message: string };
91
+
92
+ // Use sufixos quando o nome base é ambíguo
93
+ type CheckoutRequest = { ... }; // DTO de entrada
94
+ type CheckoutResponse = { ... }; // DTO de saída
95
+ type CheckoutState = { ... }; // estado do componente/hook
96
+ ```
97
+
98
+ ## Props de componentes
99
+
100
+ Sufixo `Props` no tipo:
101
+
102
+ ```typescript
103
+ type CheckoutFormProps = {
104
+ onSubmit: (data: CheckoutFormData) => void;
105
+ initialValues?: Partial<CheckoutFormData>;
106
+ };
107
+
108
+ function CheckoutForm({ onSubmit, initialValues }: CheckoutFormProps) { ... }
109
+ ```
110
+
111
+ ## Handlers de eventos
112
+
113
+ Prefixo `handle` para funções internas; prefixo `on` para props de callback:
114
+
115
+ ```typescript
116
+ // Props (contrato externo)
117
+ type CardProps = {
118
+ onSelect: (id: string) => void; // prefixo "on"
119
+ onDelete: () => void;
120
+ };
121
+
122
+ // Handlers internos
123
+ function ProductCard({ onSelect }: CardProps) {
124
+ function handleClick() { // prefixo "handle"
125
+ onSelect(product.id);
126
+ }
127
+ return <div onClick={handleClick} />;
128
+ }
129
+ ```
@@ -0,0 +1,141 @@
1
+ # Testes — React/Next.js
2
+
3
+ ## Pirâmide de testes
4
+
5
+ ```
6
+ [E2E — Playwright] ← fluxos críticos de usuário
7
+ [Componentes — RTL] ← comportamento visível ao usuário
8
+ [Unitários — Vitest] ← hooks, services, utils (maioria)
9
+ ```
10
+
11
+ ## Nível 1 — Testes unitários (hooks, services, utils)
12
+
13
+ Testam lógica pura sem renderizar componentes.
14
+
15
+ **Ferramenta:** Vitest
16
+
17
+ **Onde ficam:** ao lado do arquivo testado (`*.test.ts`) ou em `__tests__/`
18
+
19
+ ```typescript
20
+ // features/checkout/hooks/useCheckoutForm.test.ts
21
+ import { renderHook, act } from '@testing-library/react';
22
+ import { useCheckoutForm } from './useCheckoutForm';
23
+
24
+ describe('useCheckoutForm', () => {
25
+ it('deve inicializar com campos vazios', () => {
26
+ const { result } = renderHook(() => useCheckoutForm());
27
+ expect(result.current.values.email).toBe('');
28
+ });
29
+
30
+ it('deve validar email inválido', async () => {
31
+ const { result } = renderHook(() => useCheckoutForm());
32
+ await act(async () => {
33
+ result.current.setField('email', 'nao-e-email');
34
+ await result.current.validate();
35
+ });
36
+ expect(result.current.errors.email).toBeDefined();
37
+ });
38
+ });
39
+ ```
40
+
41
+ ```typescript
42
+ // features/checkout/services/checkoutService.test.ts
43
+ import { vi, describe, it, expect } from 'vitest';
44
+ import { submitCheckout } from './checkoutService';
45
+ import { apiClient } from '@/shared/api';
46
+
47
+ vi.mock('@/shared/api');
48
+
49
+ describe('submitCheckout', () => {
50
+ it('deve chamar a API com os dados corretos', async () => {
51
+ const mockPost = vi.mocked(apiClient.post).mockResolvedValue({ data: { orderId: '123' } });
52
+ const result = await submitCheckout({ email: 'test@test.com', items: [] });
53
+ expect(mockPost).toHaveBeenCalledWith('/checkout', expect.objectContaining({ email: 'test@test.com' }));
54
+ expect(result.orderId).toBe('123');
55
+ });
56
+ });
57
+ ```
58
+
59
+ ## Nível 2 — Testes de componentes (React Testing Library)
60
+
61
+ Testam o comportamento do componente do ponto de vista do usuário, não a implementação.
62
+
63
+ **Ferramenta:** React Testing Library (RTL) + Vitest
64
+
65
+ **Princípio:** Consulte elementos como o usuário os veria (por role, label, texto — não por classe CSS ou estrutura interna)
66
+
67
+ ```typescript
68
+ // features/checkout/components/CheckoutForm.test.tsx
69
+ import { render, screen, userEvent } from '@testing-library/react';
70
+ import { CheckoutForm } from './CheckoutForm';
71
+
72
+ describe('CheckoutForm', () => {
73
+ it('deve exibir erro quando email está vazio e o form é enviado', async () => {
74
+ render(<CheckoutForm onSubmit={vi.fn()} />);
75
+
76
+ await userEvent.click(screen.getByRole('button', { name: /confirmar/i }));
77
+
78
+ expect(screen.getByText(/email é obrigatório/i)).toBeInTheDocument();
79
+ });
80
+
81
+ it('deve chamar onSubmit com dados corretos quando form é válido', async () => {
82
+ const onSubmit = vi.fn();
83
+ render(<CheckoutForm onSubmit={onSubmit} />);
84
+
85
+ await userEvent.type(screen.getByLabelText(/e-mail/i), 'user@example.com');
86
+ await userEvent.click(screen.getByRole('button', { name: /confirmar/i }));
87
+
88
+ expect(onSubmit).toHaveBeenCalledWith(expect.objectContaining({ email: 'user@example.com' }));
89
+ });
90
+ });
91
+ ```
92
+
93
+ ## Nível 3 — Testes E2E (Playwright)
94
+
95
+ Testam fluxos críticos de ponta a ponta no browser real.
96
+
97
+ **Ferramenta:** Playwright
98
+
99
+ **Onde ficam:** `e2e/` na raiz do projeto
100
+
101
+ **Escopo:** apenas fluxos críticos de negócio (login, checkout, fluxo principal)
102
+
103
+ ```typescript
104
+ // e2e/checkout.spec.ts
105
+ import { test, expect } from '@playwright/test';
106
+
107
+ test('usuário consegue completar o checkout', async ({ page }) => {
108
+ await page.goto('/checkout');
109
+ await page.getByLabel('E-mail').fill('user@example.com');
110
+ await page.getByRole('button', { name: 'Confirmar pedido' }).click();
111
+
112
+ await expect(page.getByText('Pedido confirmado!')).toBeVisible();
113
+ await expect(page).toHaveURL(/\/confirmacao/);
114
+ });
115
+ ```
116
+
117
+ ## Configuração
118
+
119
+ ```typescript
120
+ // vitest.config.ts
121
+ import { defineConfig } from 'vitest/config';
122
+ import react from '@vitejs/plugin-react';
123
+
124
+ export default defineConfig({
125
+ plugins: [react()],
126
+ test: {
127
+ environment: 'jsdom',
128
+ setupFiles: ['./src/test/setup.ts'],
129
+ coverage: {
130
+ reporter: ['text', 'lcov'],
131
+ exclude: ['**/*.test.*', 'e2e/**', 'src/test/**'],
132
+ },
133
+ },
134
+ });
135
+ ```
136
+
137
+ ## O que NÃO testar
138
+
139
+ - Lógica de estilo (CSS, classes) — use Storybook para isso
140
+ - Detalhes de implementação interna (state do componente, refs)
141
+ - Snapshots de componentes complexos — ficam desatualizados rapidamente
@@ -0,0 +1,47 @@
1
+ # Convenção de Commits
2
+
3
+ ## Formato obrigatório
4
+
5
+ ```
6
+ tipo(escopo): descrição imperativa [PROJ-123]
7
+ ```
8
+
9
+ **Exemplos válidos:**
10
+ ```
11
+ feat(pagamento): adicionar integração com gateway PIX [PAY-456]
12
+ fix(auth): corrigir expiração de token JWT [AUTH-789]
13
+ refactor(pedido): extrair lógica de cálculo de frete [ORD-321]
14
+ test(usuario): adicionar testes de integração para cadastro [USR-100]
15
+ docs(readme): atualizar instruções de setup local
16
+ chore(deps): atualizar Spring Boot para 3.2.0
17
+ ```
18
+
19
+ ## Tipos válidos
20
+
21
+ | Tipo | Quando usar |
22
+ |------------|----------------------------------------------------|
23
+ | `feat` | Nova funcionalidade |
24
+ | `fix` | Correção de bug |
25
+ | `refactor` | Mudança de código sem alterar comportamento externo|
26
+ | `test` | Adição ou modificação de testes |
27
+ | `docs` | Documentação apenas |
28
+ | `chore` | Tarefas de manutenção, deps, build, CI |
29
+
30
+ ## Regras de escopo
31
+
32
+ O escopo deve corresponder ao módulo, bounded context ou feature afetada.
33
+
34
+ - Java/Spring: nome do bounded context ou pacote de domínio (ex: `pagamento`, `pedido`, `usuario`)
35
+ - React/Next.js: nome da feature ou camada (ex: `checkout`, `auth`, `shared`)
36
+
37
+ ## Ticket JIRA obrigatório
38
+
39
+ - **Obrigatório** em todos os commits **exceto** `docs` e `chore`
40
+ - Formato: `[PROJ-NNN]` ao final da primeira linha
41
+ - O ticket deve estar em estado "Em Desenvolvimento" ou "Em Revisão" antes do commit
42
+
43
+ ## Mensagem no imperativo
44
+
45
+ Use verbos no imperativo: "adicionar", "corrigir", "remover", "atualizar" (não "adicionado", "corrigido").
46
+
47
+ A mensagem deve completar a frase: *"Se aplicado, este commit vai [mensagem]"*