@thiagodiogo/pscode 1.0.0 → 1.0.1
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.
- package/README.md +2 -2
- package/dist/cli/index.js +6 -6
- package/dist/commands/config.d.ts +4 -12
- package/dist/commands/config.js +69 -242
- package/dist/core/change-metadata/schema.d.ts +1 -0
- package/dist/core/change-metadata/schema.js +1 -0
- package/dist/core/{archive.d.ts → complete.d.ts} +2 -2
- package/dist/core/{archive.js → complete.js} +28 -5
- package/dist/core/completions/command-registry.js +5 -5
- package/dist/core/config-schema.d.ts +1 -5
- package/dist/core/config-schema.js +2 -5
- package/dist/core/global-config.d.ts +1 -3
- package/dist/core/global-config.js +1 -1
- package/dist/core/init.d.ts +2 -0
- package/dist/core/init.js +81 -20
- package/dist/core/jira-transition.d.ts +16 -0
- package/dist/core/jira-transition.js +29 -0
- package/dist/core/migration.d.ts +3 -12
- package/dist/core/migration.js +10 -72
- package/dist/core/presets/dixi.d.ts +32 -0
- package/dist/core/presets/dixi.js +405 -0
- package/dist/core/profile-sync-drift.js +9 -1
- package/dist/core/profiles.d.ts +23 -21
- package/dist/core/profiles.js +28 -24
- package/dist/core/shared/skill-generation.js +3 -3
- package/dist/core/shared/tool-detection.d.ts +1 -1
- package/dist/core/shared/tool-detection.js +1 -1
- package/dist/core/templates/skill-templates.d.ts +1 -1
- package/dist/core/templates/skill-templates.js +1 -1
- package/dist/core/templates/workflows/apply-change.js +3 -3
- package/dist/core/templates/workflows/archive-change.d.ts +2 -2
- package/dist/core/templates/workflows/archive-change.js +10 -10
- package/dist/core/templates/workflows/onboard.js +9 -9
- package/dist/core/update.d.ts +1 -6
- package/dist/core/update.js +5 -29
- package/dist/core/workspace/foundation.d.ts +1 -1
- package/dist/core/workspace/foundation.js +1 -1
- package/dist/core/workspace/legacy-state.js +1 -1
- package/dist/core/workspace/skills.d.ts +4 -3
- package/dist/core/workspace/skills.js +3 -3
- package/package.json +4 -3
- package/pscode/content/dixi/architectures/feature-sliced-react/eslint-architecture.mjs.template +44 -0
- package/pscode/content/dixi/architectures/feature-sliced-react/features/README.md.template +30 -0
- package/pscode/content/dixi/architectures/feature-sliced-react/skeleton.yaml +8 -0
- package/pscode/content/dixi/architectures/hexagonal-spring/ArchitectureTest.java.template +41 -0
- package/pscode/content/dixi/architectures/hexagonal-spring/skeleton.yaml +11 -0
- package/pscode/content/dixi/claude-runtime/CLAUDE.md.java.template +62 -0
- package/pscode/content/dixi/claude-runtime/CLAUDE.md.react.template +74 -0
- package/pscode/content/dixi/claude-runtime/commands/adr.md +75 -0
- package/pscode/content/dixi/claude-runtime/commands/arch-check.md +64 -0
- package/pscode/content/dixi/claude-runtime/commands/dod.md +66 -0
- package/pscode/content/dixi/claude-runtime/commands/jira-draft.md +80 -0
- package/pscode/content/dixi/claude-runtime/commands/jira-setup.md +105 -0
- package/pscode/content/dixi/claude-runtime/commands/jira-sync.md +69 -0
- package/pscode/content/dixi/claude-runtime/commands/rfc.md +73 -0
- package/pscode/content/dixi/claude-runtime/hooks/arch-guard.mjs +101 -0
- package/pscode/content/dixi/claude-runtime/hooks/jira-context.mjs +60 -0
- package/pscode/content/dixi/claude-runtime/skills/pstld-arch-guardian.md +101 -0
- package/pscode/content/dixi/claude-runtime/skills/pstld-commit-crafter.md +98 -0
- package/pscode/content/dixi/claude-runtime/skills/pstld-jira-context.md +64 -0
- package/pscode/content/dixi/context/java/architecture.md +143 -0
- package/pscode/content/dixi/context/java/naming.md +62 -0
- package/pscode/content/dixi/context/java/testing.md +162 -0
- package/pscode/content/dixi/context/react/architecture.md +119 -0
- package/pscode/content/dixi/context/react/naming.md +129 -0
- package/pscode/content/dixi/context/react/testing.md +141 -0
- package/pscode/content/dixi/context/shared/commits.md +47 -0
- package/pscode/content/dixi/context/shared/dev-flow.md +53 -0
- package/pscode/content/dixi/context/shared/dod.md +38 -0
- package/pscode/content/dixi/context/shared/pr-flow.md +53 -0
- package/pscode/content/dixi/kit/java/.editorconfig +25 -0
- package/pscode/content/dixi/kit/java/.github/workflows/ci-java.yml +68 -0
- package/pscode/content/dixi/kit/java/.husky/commit-msg +2 -0
- package/pscode/content/dixi/kit/react/.editorconfig +20 -0
- package/pscode/content/dixi/kit/react/.github/workflows/ci-react.yml +80 -0
- package/pscode/content/dixi/kit/react/.husky/commit-msg +2 -0
- package/pscode/content/dixi/kit/react/.husky/pre-commit +2 -0
- package/pscode/content/dixi/kit/react/lint-staged.config.mjs +4 -0
- package/pscode/content/dixi/kit/shared/.commitlintrc.yml +15 -0
- package/pscode/content/dixi/kit/shared/.github/pull_request_template.md +24 -0
- package/schemas/pstld-workflow/schema.yaml +67 -0
- package/schemas/pstld-workflow/templates/design.md +15 -0
- package/schemas/pstld-workflow/templates/rfc.md +26 -0
- package/schemas/pstld-workflow/templates/tasks.md +15 -0
|
@@ -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]"*
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Fluxo de Desenvolvimento
|
|
2
|
+
|
|
3
|
+
## Visão geral
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
RFC → Design → Tasks → Apply → Revisão → Deploy
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Use `/pstld:rfc` para iniciar o fluxo, `/pstld:dod` para validar antes de abrir o PR.
|
|
10
|
+
|
|
11
|
+
## Quando criar RFC
|
|
12
|
+
|
|
13
|
+
Crie uma RFC quando a mudança:
|
|
14
|
+
|
|
15
|
+
- Afeta a arquitetura de um módulo ou a interface entre módulos
|
|
16
|
+
- Introduz uma nova dependência externa
|
|
17
|
+
- Muda o contrato de uma API pública
|
|
18
|
+
- Demora mais de 1 dia de implementação
|
|
19
|
+
|
|
20
|
+
Vá direto para task quando:
|
|
21
|
+
|
|
22
|
+
- É um bug fix isolado com causa clara
|
|
23
|
+
- É uma tarefa mecânica sem impacto arquitetural (atualização de cópia, ajuste de estilo, etc.)
|
|
24
|
+
- É uma task de refactor de escopo delimitado aprovada previamente
|
|
25
|
+
|
|
26
|
+
## Fase RFC
|
|
27
|
+
|
|
28
|
+
1. Use `/pstld:rfc` para gerar o rascunho da RFC
|
|
29
|
+
2. A RFC deve conter: contexto, problema, solução proposta, alternativas consideradas, trade-offs
|
|
30
|
+
3. Solicite revisão dos tech leads antes de avançar
|
|
31
|
+
4. RFC aprovada → avançar para Design
|
|
32
|
+
|
|
33
|
+
## Fase Design
|
|
34
|
+
|
|
35
|
+
1. Use `/ps:propose` para gerar o design e as specs
|
|
36
|
+
2. O design deve ser revisado antes de iniciar a implementação
|
|
37
|
+
3. Specs descrevem comportamento esperado (não implementação)
|
|
38
|
+
|
|
39
|
+
## Fase Tasks + Apply
|
|
40
|
+
|
|
41
|
+
1. Use `/ps:apply` para iniciar a implementação
|
|
42
|
+
2. Marque cada task como concluída ao terminar
|
|
43
|
+
3. Mantenha o escopo — não adicione tarefas não planejadas sem atualizar o design
|
|
44
|
+
|
|
45
|
+
## Validação antes do PR
|
|
46
|
+
|
|
47
|
+
Execute `/pstld:dod` para verificar o DoD antes de abrir o PR.
|
|
48
|
+
|
|
49
|
+
O slash command verifica automaticamente:
|
|
50
|
+
- Testes passando
|
|
51
|
+
- Cobertura mantida
|
|
52
|
+
- Ticket JIRA referenciado nos commits
|
|
53
|
+
- Checklist de DoD completo
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Definition of Done
|
|
2
|
+
|
|
3
|
+
## Feature
|
|
4
|
+
|
|
5
|
+
Uma feature está pronta quando:
|
|
6
|
+
|
|
7
|
+
- [ ] RFC aprovada pelos tech leads (quando mudança significativa)
|
|
8
|
+
- [ ] Design revisado e aceito antes da implementação
|
|
9
|
+
- [ ] Todas as tasks do `/ps:apply` marcadas como concluídas
|
|
10
|
+
- [ ] Testes passando (unitários, integração e E2E conforme pirâmide)
|
|
11
|
+
- [ ] Cobertura de testes mantida ou melhorada
|
|
12
|
+
- [ ] PR aprovado por pelo menos 1 revisor
|
|
13
|
+
- [ ] CHANGELOG ou changeset atualizado
|
|
14
|
+
- [ ] Ticket JIRA movido para "Em Teste"
|
|
15
|
+
|
|
16
|
+
## Bug Fix
|
|
17
|
+
|
|
18
|
+
Um bug fix está pronto quando:
|
|
19
|
+
|
|
20
|
+
- [ ] Root cause identificado e documentado no PR ou ticket JIRA
|
|
21
|
+
- [ ] Teste de regressão adicionado que reproduz o bug antes da correção
|
|
22
|
+
- [ ] Correção implementada e testes passando
|
|
23
|
+
- [ ] PR aprovado por pelo menos 1 revisor
|
|
24
|
+
- [ ] Ticket JIRA movido para "Em Teste"
|
|
25
|
+
|
|
26
|
+
## Refactor
|
|
27
|
+
|
|
28
|
+
Um refactor está pronto quando:
|
|
29
|
+
|
|
30
|
+
- [ ] Comportamento externo preservado (testes existentes continuam passando sem modificação)
|
|
31
|
+
- [ ] Cobertura de testes mantida ou melhorada
|
|
32
|
+
- [ ] Nenhuma lógica de negócio foi alterada (se houver, deve virar um `feat` ou `fix`)
|
|
33
|
+
- [ ] PR aprovado por pelo menos 1 revisor
|
|
34
|
+
- [ ] Sem regressões em funcionalidades adjacentes
|
|
35
|
+
|
|
36
|
+
## Critério de "Em Produção"
|
|
37
|
+
|
|
38
|
+
O ciclo só fecha quando a feature ou fix está deployada e monitorada por pelo menos 24h sem alertas.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Fluxo de Pull Request
|
|
2
|
+
|
|
3
|
+
## Template de PR
|
|
4
|
+
|
|
5
|
+
```markdown
|
|
6
|
+
## O que muda
|
|
7
|
+
<!-- Descreva de forma concisa o que este PR altera -->
|
|
8
|
+
|
|
9
|
+
## Por que
|
|
10
|
+
<!-- Explique a motivação. Referencie o ticket JIRA: [PROJ-123] -->
|
|
11
|
+
|
|
12
|
+
## Como testar
|
|
13
|
+
<!-- Passo a passo para o revisor validar manualmente -->
|
|
14
|
+
1.
|
|
15
|
+
2.
|
|
16
|
+
3.
|
|
17
|
+
|
|
18
|
+
## Checklist
|
|
19
|
+
- [ ] Testes adicionados/atualizados
|
|
20
|
+
- [ ] Cobertura mantida ou melhorada
|
|
21
|
+
- [ ] DoD verificado (`/pstld:dod`)
|
|
22
|
+
- [ ] Sem TODOs temporários deixados no código
|
|
23
|
+
- [ ] Documentação atualizada (se aplicável)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Referenciando tickets JIRA
|
|
27
|
+
|
|
28
|
+
- Inclua `[PROJ-123]` no título do PR
|
|
29
|
+
- Use `Closes PROJ-123` ou `Refs PROJ-123` na descrição quando a integração JIRA estiver configurada
|
|
30
|
+
- O ticket deve estar em "Em Revisão" quando o PR for aberto
|
|
31
|
+
|
|
32
|
+
## Processo de revisão
|
|
33
|
+
|
|
34
|
+
1. Abra o PR apontando para a branch de destino (geralmente `main` ou `develop`)
|
|
35
|
+
2. Solicite revisão de pelo menos 1 membro do time
|
|
36
|
+
3. Responda todos os comentários antes de fazer merge
|
|
37
|
+
4. Após aprovação, faça squash merge ou merge commit conforme a convenção do projeto
|
|
38
|
+
|
|
39
|
+
## Critérios de merge
|
|
40
|
+
|
|
41
|
+
Um PR pode ser mergeado quando:
|
|
42
|
+
|
|
43
|
+
- CI passou (build, lint, testes, cobertura)
|
|
44
|
+
- Pelo menos 1 aprovação de revisor
|
|
45
|
+
- Todos os comentários resolvidos
|
|
46
|
+
- Sem conflitos com a branch de destino
|
|
47
|
+
- DoD verificado
|
|
48
|
+
|
|
49
|
+
## Tamanho ideal de PR
|
|
50
|
+
|
|
51
|
+
Prefira PRs com menos de 400 linhas modificadas. PRs grandes aumentam o tempo de revisão e o risco de bugs passarem despercebidos.
|
|
52
|
+
|
|
53
|
+
Se o PR estiver muito grande, considere quebrá-lo em partes menores com dependência sequencial.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
charset = utf-8
|
|
5
|
+
end_of_line = lf
|
|
6
|
+
insert_final_newline = true
|
|
7
|
+
trim_trailing_whitespace = true
|
|
8
|
+
|
|
9
|
+
[*.java]
|
|
10
|
+
indent_style = space
|
|
11
|
+
indent_size = 4
|
|
12
|
+
charset = utf-8
|
|
13
|
+
end_of_line = lf
|
|
14
|
+
|
|
15
|
+
[*.{yml,yaml}]
|
|
16
|
+
indent_style = space
|
|
17
|
+
indent_size = 2
|
|
18
|
+
|
|
19
|
+
[*.xml]
|
|
20
|
+
indent_style = space
|
|
21
|
+
indent_size = 4
|
|
22
|
+
|
|
23
|
+
[*.properties]
|
|
24
|
+
indent_style = space
|
|
25
|
+
indent_size = 4
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: CI Java
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, develop]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
name: Build
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-java@v4
|
|
16
|
+
with:
|
|
17
|
+
java-version: '21'
|
|
18
|
+
distribution: temurin
|
|
19
|
+
cache: maven
|
|
20
|
+
- name: Compile
|
|
21
|
+
run: mvn compile
|
|
22
|
+
|
|
23
|
+
test:
|
|
24
|
+
name: Test
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
needs: build
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: actions/setup-java@v4
|
|
30
|
+
with:
|
|
31
|
+
java-version: '21'
|
|
32
|
+
distribution: temurin
|
|
33
|
+
cache: maven
|
|
34
|
+
- name: Run tests
|
|
35
|
+
run: mvn test
|
|
36
|
+
|
|
37
|
+
archunit:
|
|
38
|
+
name: ArchUnit
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
needs: build
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
- uses: actions/setup-java@v4
|
|
44
|
+
with:
|
|
45
|
+
java-version: '21'
|
|
46
|
+
distribution: temurin
|
|
47
|
+
cache: maven
|
|
48
|
+
- name: Run architecture tests
|
|
49
|
+
run: mvn test -Dtest=ArchitectureTest
|
|
50
|
+
|
|
51
|
+
coverage:
|
|
52
|
+
name: Coverage
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
needs: test
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/checkout@v4
|
|
57
|
+
- uses: actions/setup-java@v4
|
|
58
|
+
with:
|
|
59
|
+
java-version: '21'
|
|
60
|
+
distribution: temurin
|
|
61
|
+
cache: maven
|
|
62
|
+
- name: Generate coverage report
|
|
63
|
+
run: mvn jacoco:report
|
|
64
|
+
- name: Upload coverage artifact
|
|
65
|
+
uses: actions/upload-artifact@v4
|
|
66
|
+
with:
|
|
67
|
+
name: jacoco-report
|
|
68
|
+
path: target/site/jacoco/
|