@codexa/cli 9.0.2 → 9.0.4
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/commands/architect.test.ts +531 -0
- package/commands/architect.ts +75 -17
- package/commands/check.ts +7 -17
- package/commands/clear.ts +40 -1
- package/commands/decide.ts +37 -49
- package/commands/discover.ts +136 -28
- package/commands/knowledge.test.ts +160 -0
- package/commands/knowledge.ts +192 -102
- package/commands/patterns.test.ts +169 -0
- package/commands/patterns.ts +6 -13
- package/commands/plan.test.ts +73 -0
- package/commands/plan.ts +18 -66
- package/commands/product.ts +8 -17
- package/commands/research.ts +4 -3
- package/commands/review.ts +190 -28
- package/commands/spec-resolver.test.ts +119 -0
- package/commands/spec-resolver.ts +90 -0
- package/commands/standards.ts +7 -15
- package/commands/sync.ts +89 -0
- package/commands/task.ts +72 -167
- package/commands/utils.test.ts +100 -0
- package/commands/utils.ts +78 -706
- package/db/schema.test.ts +760 -0
- package/db/schema.ts +284 -130
- package/gates/validator.test.ts +675 -0
- package/gates/validator.ts +112 -27
- package/package.json +3 -1
- package/protocol/process-return.ts +25 -93
- package/protocol/subagent-protocol.test.ts +936 -0
- package/protocol/subagent-protocol.ts +19 -1
- package/workflow.ts +176 -67
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import { describe, it, expect } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
extractSection,
|
|
4
|
+
parseBabySteps,
|
|
5
|
+
parseRisks,
|
|
6
|
+
parseDiagrams,
|
|
7
|
+
parseDecisionsTable,
|
|
8
|
+
type BabyStep,
|
|
9
|
+
type Risk,
|
|
10
|
+
type ArchitecturalDecision,
|
|
11
|
+
} from "./architect";
|
|
12
|
+
|
|
13
|
+
describe("extractSection", () => {
|
|
14
|
+
it("should extract section with exact match (Tier 1)", () => {
|
|
15
|
+
const content = `## Solucao Proposta
|
|
16
|
+
|
|
17
|
+
This is the proposed solution.
|
|
18
|
+
|
|
19
|
+
## Other Section
|
|
20
|
+
Other content.`;
|
|
21
|
+
|
|
22
|
+
const result = extractSection(content, "Solucao Proposta");
|
|
23
|
+
expect(result).toBe("This is the proposed solution.");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should extract section with case-insensitive match (Tier 2)", () => {
|
|
27
|
+
const content = `## solucao proposta
|
|
28
|
+
|
|
29
|
+
This is the content.
|
|
30
|
+
|
|
31
|
+
## Next Section`;
|
|
32
|
+
|
|
33
|
+
const result = extractSection(content, "Solucao Proposta");
|
|
34
|
+
expect(result).toBe("This is the content.");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should extract section using alias (Tier 3)", () => {
|
|
38
|
+
const content = `## Proposed Solution
|
|
39
|
+
|
|
40
|
+
This is the solution content.
|
|
41
|
+
|
|
42
|
+
## Risks`;
|
|
43
|
+
|
|
44
|
+
const result = extractSection(content, "Solucao Proposta");
|
|
45
|
+
expect(result).toBe("This is the solution content.");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should return empty string when header not found", () => {
|
|
49
|
+
const content = `## Some Header
|
|
50
|
+
|
|
51
|
+
Content here.`;
|
|
52
|
+
|
|
53
|
+
const result = extractSection(content, "Nonexistent Section");
|
|
54
|
+
expect(result).toBe("");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should extract correct section when multiple sections exist", () => {
|
|
58
|
+
const content = `## Contexto e Entendimento
|
|
59
|
+
|
|
60
|
+
Context content.
|
|
61
|
+
|
|
62
|
+
## Solucao Proposta
|
|
63
|
+
|
|
64
|
+
Solution content.
|
|
65
|
+
|
|
66
|
+
## Baby Steps
|
|
67
|
+
|
|
68
|
+
Steps content.`;
|
|
69
|
+
|
|
70
|
+
const result = extractSection(content, "Solucao Proposta");
|
|
71
|
+
expect(result).toBe("Solution content.");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should extract section with multiple lines", () => {
|
|
75
|
+
const content = `## Riscos e Mitigacoes
|
|
76
|
+
|
|
77
|
+
Line 1
|
|
78
|
+
Line 2
|
|
79
|
+
Line 3
|
|
80
|
+
|
|
81
|
+
## Next Section`;
|
|
82
|
+
|
|
83
|
+
const result = extractSection(content, "Riscos e Mitigacoes");
|
|
84
|
+
expect(result).toBe("Line 1\nLine 2\nLine 3");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("should handle alias 'risks' for Riscos e Mitigacoes", () => {
|
|
88
|
+
const content = `## Risks
|
|
89
|
+
|
|
90
|
+
Risk content here.
|
|
91
|
+
|
|
92
|
+
## Other`;
|
|
93
|
+
|
|
94
|
+
const result = extractSection(content, "Riscos e Mitigacoes");
|
|
95
|
+
expect(result).toBe("Risk content here.");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should handle alias 'steps' for Baby Steps", () => {
|
|
99
|
+
const content = `## Steps
|
|
100
|
+
|
|
101
|
+
Step content.
|
|
102
|
+
|
|
103
|
+
## End`;
|
|
104
|
+
|
|
105
|
+
const result = extractSection(content, "Baby Steps");
|
|
106
|
+
expect(result).toBe("Step content.");
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe("parseBabySteps", () => {
|
|
111
|
+
it("should parse single basic step", () => {
|
|
112
|
+
const section = `### 1. Create schema
|
|
113
|
+
|
|
114
|
+
**O que**: Build database schema
|
|
115
|
+
**Por que**: Foundation for data layer`;
|
|
116
|
+
|
|
117
|
+
const result = parseBabySteps(section);
|
|
118
|
+
expect(result).toHaveLength(1);
|
|
119
|
+
expect(result[0]).toMatchObject({
|
|
120
|
+
number: 1,
|
|
121
|
+
name: "Create schema",
|
|
122
|
+
what: "Build database schema",
|
|
123
|
+
why: "Foundation for data layer",
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should parse step with 'Step N:' format", () => {
|
|
128
|
+
const section = `### Step 2: Setup API routes
|
|
129
|
+
|
|
130
|
+
**O que**: Create REST endpoints
|
|
131
|
+
**Por que**: Enable frontend communication`;
|
|
132
|
+
|
|
133
|
+
const result = parseBabySteps(section);
|
|
134
|
+
expect(result).toHaveLength(1);
|
|
135
|
+
expect(result[0]).toMatchObject({
|
|
136
|
+
number: 2,
|
|
137
|
+
name: "Setup API routes",
|
|
138
|
+
what: "Create REST endpoints",
|
|
139
|
+
why: "Enable frontend communication",
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("should parse step with files", () => {
|
|
144
|
+
const section = `### 3. Implement handlers
|
|
145
|
+
|
|
146
|
+
**O que**: Write business logic
|
|
147
|
+
**Arquivos**: \`src/handlers/user.ts\`, \`src/handlers/auth.ts\``;
|
|
148
|
+
|
|
149
|
+
const result = parseBabySteps(section);
|
|
150
|
+
expect(result).toHaveLength(1);
|
|
151
|
+
expect(result[0].files).toEqual(["src/handlers/user.ts", "src/handlers/auth.ts"]);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("should parse step with files using semicolon separator", () => {
|
|
155
|
+
const section = `### 1. Create files
|
|
156
|
+
|
|
157
|
+
**Arquivos**: \`app.ts\`; \`config.ts\`; \`index.ts\``;
|
|
158
|
+
|
|
159
|
+
const result = parseBabySteps(section);
|
|
160
|
+
expect(result[0].files).toEqual(["app.ts", "config.ts", "index.ts"]);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("should parse step with dependencies", () => {
|
|
164
|
+
const section = `### 4. Build UI
|
|
165
|
+
|
|
166
|
+
**O que**: Create components
|
|
167
|
+
**Depende de**: 1, 2, 3`;
|
|
168
|
+
|
|
169
|
+
const result = parseBabySteps(section);
|
|
170
|
+
expect(result[0].dependsOn).toEqual([1, 2, 3]);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("should parse step with agent", () => {
|
|
174
|
+
const section = `### 5. Deploy
|
|
175
|
+
|
|
176
|
+
**O que**: Setup infrastructure
|
|
177
|
+
**Agente**: database-postgres`;
|
|
178
|
+
|
|
179
|
+
const result = parseBabySteps(section);
|
|
180
|
+
expect(result[0].agent).toBe("database-postgres");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("should return empty array for empty section", () => {
|
|
184
|
+
const result = parseBabySteps("");
|
|
185
|
+
expect(result).toEqual([]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("should parse multiple steps", () => {
|
|
189
|
+
const section = `### 1. First step
|
|
190
|
+
|
|
191
|
+
**O que**: Do first thing
|
|
192
|
+
**Por que**: Needed first
|
|
193
|
+
|
|
194
|
+
### 2. Second step
|
|
195
|
+
|
|
196
|
+
**O que**: Do second thing
|
|
197
|
+
**Por que**: Needed second
|
|
198
|
+
**Depende de**: 1`;
|
|
199
|
+
|
|
200
|
+
const result = parseBabySteps(section);
|
|
201
|
+
expect(result).toHaveLength(2);
|
|
202
|
+
expect(result[0].number).toBe(1);
|
|
203
|
+
expect(result[0].name).toBe("First step");
|
|
204
|
+
expect(result[1].number).toBe(2);
|
|
205
|
+
expect(result[1].name).toBe("Second step");
|
|
206
|
+
expect(result[1].dependsOn).toEqual([1]);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("should parse step with all fields", () => {
|
|
210
|
+
const section = `### Step 6: Complete integration
|
|
211
|
+
|
|
212
|
+
**O que**: Integrate all systems
|
|
213
|
+
**Por que**: Final assembly needed
|
|
214
|
+
**Resultado**: Fully integrated system
|
|
215
|
+
**Arquivos**: \`src/main.ts\`, \`src/index.ts\`
|
|
216
|
+
**Agente**: frontend-next
|
|
217
|
+
**Depende de**: 4, 5`;
|
|
218
|
+
|
|
219
|
+
const result = parseBabySteps(section);
|
|
220
|
+
expect(result[0]).toMatchObject({
|
|
221
|
+
number: 6,
|
|
222
|
+
name: "Complete integration",
|
|
223
|
+
what: "Integrate all systems",
|
|
224
|
+
why: "Final assembly needed",
|
|
225
|
+
result: "Fully integrated system",
|
|
226
|
+
files: ["src/main.ts", "src/index.ts"],
|
|
227
|
+
agent: "frontend-next",
|
|
228
|
+
dependsOn: [4, 5],
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("should handle files without backticks", () => {
|
|
233
|
+
const section = `### 1. Create files
|
|
234
|
+
|
|
235
|
+
**Arquivos**: file1.ts, file2.ts`;
|
|
236
|
+
|
|
237
|
+
const result = parseBabySteps(section);
|
|
238
|
+
expect(result[0].files).toEqual(["file1.ts", "file2.ts"]);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe("parseRisks", () => {
|
|
243
|
+
it("should parse standard risk with R prefix", () => {
|
|
244
|
+
const section = `### R1. Data loss
|
|
245
|
+
|
|
246
|
+
**Probabilidade**: high
|
|
247
|
+
**Impacto**: high
|
|
248
|
+
**Mitigacao**: Implement automated backups`;
|
|
249
|
+
|
|
250
|
+
const result = parseRisks(section);
|
|
251
|
+
expect(result).toHaveLength(1);
|
|
252
|
+
expect(result[0]).toMatchObject({
|
|
253
|
+
id: "R1",
|
|
254
|
+
description: "Data loss",
|
|
255
|
+
probability: "high",
|
|
256
|
+
impact: "high",
|
|
257
|
+
mitigation: "Implement automated backups",
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("should parse risk without R prefix", () => {
|
|
262
|
+
const section = `### 1. Performance degradation
|
|
263
|
+
|
|
264
|
+
**Probabilidade**: medium
|
|
265
|
+
**Impacto**: high
|
|
266
|
+
**Mitigacao**: Add caching layer`;
|
|
267
|
+
|
|
268
|
+
const result = parseRisks(section);
|
|
269
|
+
expect(result[0]).toMatchObject({
|
|
270
|
+
id: "R1",
|
|
271
|
+
description: "Performance degradation",
|
|
272
|
+
probability: "medium",
|
|
273
|
+
impact: "high",
|
|
274
|
+
mitigation: "Add caching layer",
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("should return empty array for empty section", () => {
|
|
279
|
+
const result = parseRisks("");
|
|
280
|
+
expect(result).toEqual([]);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it("should default to medium for missing probability", () => {
|
|
284
|
+
const section = `### R2. Security breach
|
|
285
|
+
|
|
286
|
+
**Impacto**: high
|
|
287
|
+
**Mitigacao**: Use encryption`;
|
|
288
|
+
|
|
289
|
+
const result = parseRisks(section);
|
|
290
|
+
expect(result[0].probability).toBe("medium");
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("should default to medium for missing impact", () => {
|
|
294
|
+
const section = `### R3. API rate limiting
|
|
295
|
+
|
|
296
|
+
**Probabilidade**: low
|
|
297
|
+
**Mitigacao**: Implement retry logic`;
|
|
298
|
+
|
|
299
|
+
const result = parseRisks(section);
|
|
300
|
+
expect(result[0].impact).toBe("medium");
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("should handle Mitigação with cedilla", () => {
|
|
304
|
+
const section = `### R1. Test risk
|
|
305
|
+
|
|
306
|
+
**Probabilidade**: low
|
|
307
|
+
**Impacto**: low
|
|
308
|
+
**Mitigação**: Do something`;
|
|
309
|
+
|
|
310
|
+
const result = parseRisks(section);
|
|
311
|
+
expect(result[0].mitigation).toBe("Do something");
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("should parse multiple risks", () => {
|
|
315
|
+
const section = `### R1. First risk
|
|
316
|
+
|
|
317
|
+
**Probabilidade**: high
|
|
318
|
+
**Impacto**: medium
|
|
319
|
+
**Mitigacao**: Mitigation 1
|
|
320
|
+
|
|
321
|
+
### R2. Second risk
|
|
322
|
+
|
|
323
|
+
**Probabilidade**: low
|
|
324
|
+
**Impacto**: high
|
|
325
|
+
**Mitigacao**: Mitigation 2`;
|
|
326
|
+
|
|
327
|
+
const result = parseRisks(section);
|
|
328
|
+
expect(result).toHaveLength(2);
|
|
329
|
+
expect(result[0].id).toBe("R1");
|
|
330
|
+
expect(result[0].description).toBe("First risk");
|
|
331
|
+
expect(result[1].id).toBe("R2");
|
|
332
|
+
expect(result[1].description).toBe("Second risk");
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("should handle probability and impact variations", () => {
|
|
336
|
+
const section = `### R1. Test
|
|
337
|
+
|
|
338
|
+
**Probabilidade**: high
|
|
339
|
+
**Impacto**: low
|
|
340
|
+
**Mitigacao**: Fix`;
|
|
341
|
+
|
|
342
|
+
const result = parseRisks(section);
|
|
343
|
+
expect(result[0].probability).toBe("high");
|
|
344
|
+
expect(result[0].impact).toBe("low");
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
describe("parseDiagrams", () => {
|
|
349
|
+
it("should parse mermaid flowchart diagram", () => {
|
|
350
|
+
const section = `### System Architecture
|
|
351
|
+
|
|
352
|
+
Some description here.
|
|
353
|
+
|
|
354
|
+
\`\`\`mermaid
|
|
355
|
+
flowchart TD
|
|
356
|
+
A[Client] --> B[Server]
|
|
357
|
+
B --> C[Database]
|
|
358
|
+
\`\`\``;
|
|
359
|
+
|
|
360
|
+
const result = parseDiagrams(section);
|
|
361
|
+
expect(result).toHaveLength(1);
|
|
362
|
+
expect(result[0]).toMatchObject({
|
|
363
|
+
name: "System Architecture",
|
|
364
|
+
type: "flowchart",
|
|
365
|
+
content: "flowchart TD\n A[Client] --> B[Server]\n B --> C[Database]",
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it("should parse mermaid sequence diagram", () => {
|
|
370
|
+
const section = `### User Flow
|
|
371
|
+
|
|
372
|
+
\`\`\`mermaid
|
|
373
|
+
sequenceDiagram
|
|
374
|
+
User->>API: Request
|
|
375
|
+
API->>DB: Query
|
|
376
|
+
DB-->>API: Result
|
|
377
|
+
API-->>User: Response
|
|
378
|
+
\`\`\``;
|
|
379
|
+
|
|
380
|
+
const result = parseDiagrams(section);
|
|
381
|
+
expect(result[0]).toMatchObject({
|
|
382
|
+
name: "User Flow",
|
|
383
|
+
type: "sequenceDiagram",
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it("should return empty array when no diagrams present", () => {
|
|
388
|
+
const section = `### Some Section
|
|
389
|
+
|
|
390
|
+
No diagrams here.`;
|
|
391
|
+
|
|
392
|
+
const result = parseDiagrams(section);
|
|
393
|
+
expect(result).toEqual([]);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it("should parse multiple diagrams", () => {
|
|
397
|
+
const section = `### First Diagram
|
|
398
|
+
|
|
399
|
+
\`\`\`mermaid
|
|
400
|
+
flowchart LR
|
|
401
|
+
A --> B
|
|
402
|
+
\`\`\`
|
|
403
|
+
|
|
404
|
+
### Second Diagram
|
|
405
|
+
|
|
406
|
+
\`\`\`mermaid
|
|
407
|
+
graph TD
|
|
408
|
+
C --> D
|
|
409
|
+
\`\`\``;
|
|
410
|
+
|
|
411
|
+
const result = parseDiagrams(section);
|
|
412
|
+
expect(result).toHaveLength(2);
|
|
413
|
+
expect(result[0].name).toBe("First Diagram");
|
|
414
|
+
expect(result[0].type).toBe("flowchart");
|
|
415
|
+
expect(result[1].name).toBe("Second Diagram");
|
|
416
|
+
expect(result[1].type).toBe("graph");
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it("should handle diagram with complex content", () => {
|
|
420
|
+
const section = `### Component Diagram
|
|
421
|
+
|
|
422
|
+
\`\`\`mermaid
|
|
423
|
+
classDiagram
|
|
424
|
+
class User {
|
|
425
|
+
+String name
|
|
426
|
+
+String email
|
|
427
|
+
+login()
|
|
428
|
+
}
|
|
429
|
+
class Admin {
|
|
430
|
+
+deleteUser()
|
|
431
|
+
}
|
|
432
|
+
User <|-- Admin
|
|
433
|
+
\`\`\``;
|
|
434
|
+
|
|
435
|
+
const result = parseDiagrams(section);
|
|
436
|
+
expect(result[0].type).toBe("classDiagram");
|
|
437
|
+
expect(result[0].content).toContain("class User");
|
|
438
|
+
expect(result[0].content).toContain("User <|-- Admin");
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
describe("parseDecisionsTable", () => {
|
|
443
|
+
it("should parse standard decisions table", () => {
|
|
444
|
+
const section = `| Decisao | Justificativa |
|
|
445
|
+
| --- | --- |
|
|
446
|
+
| Use Drizzle ORM | Better TypeScript support and DX |
|
|
447
|
+
| Deploy on Vercel | Seamless Next.js integration |`;
|
|
448
|
+
|
|
449
|
+
const result = parseDecisionsTable(section);
|
|
450
|
+
expect(result).toHaveLength(2);
|
|
451
|
+
expect(result[0]).toMatchObject({
|
|
452
|
+
decision: "Use Drizzle ORM",
|
|
453
|
+
rationale: "Better TypeScript support and DX",
|
|
454
|
+
});
|
|
455
|
+
expect(result[1]).toMatchObject({
|
|
456
|
+
decision: "Deploy on Vercel",
|
|
457
|
+
rationale: "Seamless Next.js integration",
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it("should skip header row with 'Decisao'", () => {
|
|
462
|
+
const section = `| Decisao | Justificativa |
|
|
463
|
+
| --- | --- |
|
|
464
|
+
| Use PostgreSQL | Reliable and scalable |`;
|
|
465
|
+
|
|
466
|
+
const result = parseDecisionsTable(section);
|
|
467
|
+
expect(result).toHaveLength(1);
|
|
468
|
+
expect(result[0].decision).not.toBe("Decisao");
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it("should return empty array for empty section", () => {
|
|
472
|
+
const result = parseDecisionsTable("");
|
|
473
|
+
expect(result).toEqual([]);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it("should handle table with only header", () => {
|
|
477
|
+
const section = `| Decisao | Justificativa |
|
|
478
|
+
| --- | --- |`;
|
|
479
|
+
|
|
480
|
+
const result = parseDecisionsTable(section);
|
|
481
|
+
expect(result).toEqual([]);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it("should parse single decision", () => {
|
|
485
|
+
const section = `| Decisao | Justificativa |
|
|
486
|
+
| --- | --- |
|
|
487
|
+
| Use TypeScript | Type safety and better IDE support |`;
|
|
488
|
+
|
|
489
|
+
const result = parseDecisionsTable(section);
|
|
490
|
+
expect(result).toHaveLength(1);
|
|
491
|
+
expect(result[0]).toMatchObject({
|
|
492
|
+
decision: "Use TypeScript",
|
|
493
|
+
rationale: "Type safety and better IDE support",
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it("should handle decisions with pipes in content", () => {
|
|
498
|
+
const section = `| Decisao | Justificativa |
|
|
499
|
+
| --- | --- |
|
|
500
|
+
| Use A or B | Choose A if X, otherwise B |`;
|
|
501
|
+
|
|
502
|
+
const result = parseDecisionsTable(section);
|
|
503
|
+
expect(result).toHaveLength(1);
|
|
504
|
+
// Note: This will capture "Use A or B " due to split behavior
|
|
505
|
+
expect(result[0].decision).toContain("Use A or B");
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it("should trim whitespace from cells", () => {
|
|
509
|
+
const section = `| Decisao | Justificativa |
|
|
510
|
+
| --- | --- |
|
|
511
|
+
| Use React | Component-based architecture |`;
|
|
512
|
+
|
|
513
|
+
const result = parseDecisionsTable(section);
|
|
514
|
+
expect(result[0].decision).toBe("Use React");
|
|
515
|
+
expect(result[0].rationale).toBe("Component-based architecture");
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it("should handle alternative header names (not auto-skipped)", () => {
|
|
519
|
+
// Note: The code only skips rows where decision === "Decisao", not "Decision"
|
|
520
|
+
// So "Decision" header row is treated as a data row
|
|
521
|
+
const section = `| Decision | Rationale |
|
|
522
|
+
| --- | --- |
|
|
523
|
+
| Use REST API | Well-understood and widely supported |`;
|
|
524
|
+
|
|
525
|
+
const result = parseDecisionsTable(section);
|
|
526
|
+
// "Decision" row is included (not filtered), separator is skipped
|
|
527
|
+
expect(result).toHaveLength(2);
|
|
528
|
+
expect(result[0].decision).toBe("Decision");
|
|
529
|
+
expect(result[1].decision).toBe("Use REST API");
|
|
530
|
+
});
|
|
531
|
+
});
|