@purecore/one-spec-4-all 0.1.0 → 0.2.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.
- package/CHANGELOG.md +33 -0
- package/critica.md +77 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +6 -2
- package/docs/4-ideias.md +66 -0
- package/docs/api-imperativo.md +125 -0
- package/docs/api-matematico.md +145 -0
- package/docs/api-narrativo.md +181 -0
- package/docs/guia-rapido.md +189 -0
- package/docs/whitepaper.md +246 -0
- package/examples/imperative.spec.ts +58 -0
- package/examples/math.spec.ts +52 -0
- package/examples/narrative.spec.ts +54 -0
- package/examples/polyglot-shopping-cart.spec.ts +200 -0
- package/index.html +427 -2
- package/infograficos/detalhado.png +0 -0
- package/infograficos/normal.png +0 -0
- package/logo.png +0 -0
- package/package.json +4 -2
- package/podcast/O_Matem/303/241tico,_o_Narrador_e_o_Engenheiro.json +0 -0
- package/podcast/O_Matem/303/241tico,_o_Narrador_e_o_Engenheiro.md +0 -0
- package/podcast/critica-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.json +0 -0
- package/podcast/critica-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.md +0 -0
- package/podcast/critica-Unificar_filosofia_e_pr/303/241tica_na_documenta/303/247/303/243o_(7_words__covers_t.md +1 -0
- package/podcast/critica-Unificar_filosofia_e_pr/303/241tica_na_documenta/303/247/303/243o__7_words__covers_t.ogg +0 -0
- package/podcast/critica2-Sil/303/252ncio_estrat/303/251gico_e_sobrecarga_em_READMEs.ogg +0 -0
- package/podcast/critica2.json +3191 -0
- package/podcast/critica2.md +1 -0
- package/podcast/debate-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.json +0 -0
- package/podcast/debate-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.md +0 -0
- package/readme.md +234 -141
- package/reports/01-01-2026_00-45.md +40 -0
- package/reports/31-12-2025_22-35.md +25 -0
- package/reports/31-12-2025_22-45.md +15 -0
- package/slides/Dialetos_de_Teste_Um_Executor_M/303/272ltiplos_Vocabul/303/241rios.pdf +0 -0
- package/src/cli.ts +413 -22
- package/src/index.ts +50 -1
- package/tabela.html +350 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.1.0]
|
|
6
|
+
|
|
7
|
+
### What's Changed
|
|
8
|
+
|
|
9
|
+
- :memo: **docs**: Expanded README with detailed philosophies for each test dialect.
|
|
10
|
+
- :memo: **docs**: Added complete, realistic code examples for Math, Narrative, and Imperative dialects.
|
|
11
|
+
- :recycle: **refactor**: Updated `src/index.ts` to export dialect functions as top-level exports, simplifying imports.
|
|
12
|
+
- :wrench: **chore**: Standardized package naming in documentation to `@purecore/one-spec-4-all`.
|
|
13
|
+
- :art: **style**: Converted comparison table in README from HTML to Markdown for better portability.
|
|
14
|
+
- :sparkles: **feat**: Complete README restructure following podcast feedback:
|
|
15
|
+
- Added "Cansado de Descrever?" hook connecting philosophy to practice
|
|
16
|
+
- Added "Quick Start" section for 5-minute first test victory
|
|
17
|
+
- Added visual dialect chooser flowchart
|
|
18
|
+
- Added "A Dor Que Resolvemos" pain-point sections before each dialect
|
|
19
|
+
- Moved advanced topics (Rosetta Table, Polyglot Mode) to the end
|
|
20
|
+
- Added gradual adoption/migration guide
|
|
21
|
+
- :sparkles: **feat**: Complete CLI rewrite with Vitest-style output:
|
|
22
|
+
- ANSI color palette (green/red/yellow/cyan)
|
|
23
|
+
- Spinner animation during test execution
|
|
24
|
+
- Per-file timer showing elapsed time
|
|
25
|
+
- Grouped test results with indentation
|
|
26
|
+
- Summary statistics (files, tests, duration)
|
|
27
|
+
- Exit codes based on test results
|
|
28
|
+
- :sparkles: **feat**: README restructure following critica2.md podcast:
|
|
29
|
+
- Visual proof of legacy coexistence (Jest + dialeto) at the very top
|
|
30
|
+
- New "Por Que Adotar no Seu Time?" section for tech leads/managers
|
|
31
|
+
- Progressive disclosure: API summaries in README, full docs linked separately
|
|
32
|
+
- Created `/docs/api-imperativo.md`, `/docs/api-matematico.md`, `/docs/api-narrativo.md`
|
|
33
|
+
- Reordered sections: Trust → Strategy → Quick Start → Choose Dialect
|
package/critica.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Crítica e Esclarecimento: A Filosofia Aditiva do one-spec-4-all Tester
|
|
2
|
+
|
|
3
|
+
Uma crítica comum a novos frameworks é a sensação de que "precisamos jogar tudo fora e reaprender a programar". Este documento existe para esclarecer que o **one-spec-4-all Tester** foi desenhado com uma filosofia oposta: a **Filosofia Aditiva**.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Não é uma Negação do Passado
|
|
8
|
+
|
|
9
|
+
A maior preocupação de qualquer equipe é o **código legado**. Se você tem 5.000 testes escritos em Jest padrão (`describe`, `it`, `expect`), o one-spec-4-all Tester **não exige que você reescreva uma linha sequer**.
|
|
10
|
+
|
|
11
|
+
O framework funciona como um **superset** (superconjunto). Ele entende nativamente a sintaxe do Jest.
|
|
12
|
+
|
|
13
|
+
> **Resumo:** Seus testes antigos continuam passando. Seu conhecimento de Jest continua válido.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 2. O Mito da "Torre de Babel"
|
|
18
|
+
|
|
19
|
+
Quando dizemos "one-spec-4-all", **não esperamos que cada desenvolvedor seja fluente em todos os dialetos**. Isso seria ineficiente e aumentaria a carga cognitiva.
|
|
20
|
+
|
|
21
|
+
A proposta é a **Especialização por Contexto**:
|
|
22
|
+
|
|
23
|
+
- O **Cientista de Dados** só precisa aprender o `MathDialect`. Ele não precisa saber o que é um `scenario` ou `looks`.
|
|
24
|
+
- O **Designer/Frontend** foca no `VisualDialect`. Ele ignora completamente a existência dos axiomas matemáticos.
|
|
25
|
+
- O **Engenheiro de Backend** usa o `ImperativeDialect`.
|
|
26
|
+
|
|
27
|
+
> **Você não precisa aprender 4 idiomas.** Você escolhe um que se adapta ao seu projeto e ignora o resto. O framework é poliglota; você não precisa ser.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 3. Um Executor para Todos (One Runner)
|
|
32
|
+
|
|
33
|
+
Tecnicamente, não há "mágica" pesada rodando por trás. O one-spec-4-all Tester **unifica a execução**. Isso significa que você não precisa de pipelines de CI/CD separados para "Testes Jest Antigos" e "Testes Novos".
|
|
34
|
+
|
|
35
|
+
Um único comando:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm test
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Executará:
|
|
42
|
+
|
|
43
|
+
- ✅ Seus arquivos `.spec.ts` antigos (Jest Clássico).
|
|
44
|
+
- ✅ Seus novos arquivos de regras de negócio (`MathDialect`).
|
|
45
|
+
- ✅ Seus novos testes de componentes (`VisualDialect`).
|
|
46
|
+
|
|
47
|
+
Todos aparecem no mesmo relatório, com a mesma cobertura de código, na mesma janela de terminal.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 4. Exemplo Prático de Coexistência
|
|
52
|
+
|
|
53
|
+
Imagine um arquivo de teste que está sendo migrado ou refatorado. O código abaixo é **100% válido** e executável pelo one-spec-4-all Tester:
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
// Legado: Ninguém quer mexer nisso agora
|
|
57
|
+
describe("Módulo de Login (Legacy)", () => {
|
|
58
|
+
it("deve validar senha", () => {
|
|
59
|
+
expect(validar("123")).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Novo: Nova funcionalidade de criptografia adicionada hoje
|
|
64
|
+
import { axiom, implies } from "@purecore/one-spec-4-all";
|
|
65
|
+
|
|
66
|
+
axiom("Nova Criptografia SHA-256", () => {
|
|
67
|
+
// Usamos o dialeto novo apenas para a feature nova
|
|
68
|
+
// Não quebramos nada do anterior
|
|
69
|
+
implies(hash("123")).matches(/^[a-f0-9]{64}$/);
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Conclusão
|
|
76
|
+
|
|
77
|
+
O **one-spec-4-all Tester** não veio para destruir o Jest ou o Mocha. Ele veio para **dar vocabulário onde o vocabulário padrão falha em expressar a intenção**. É uma ferramenta de **adição**, não de substituição.
|
package/dist/index.d.ts
CHANGED
|
@@ -110,4 +110,8 @@ export declare const ClassicDialect: {
|
|
|
110
110
|
beforeEach: (f: VoidFn) => void;
|
|
111
111
|
afterEach: (f: VoidFn) => void;
|
|
112
112
|
};
|
|
113
|
+
export declare const axiom: (n: string, f: VoidFn) => void, proof: (n: string, f: VoidFn) => void, implies: <T>(val: T) => UniversalAssertion<T>, arbitrary: typeof createAtomicMock, lambda: typeof createAtomicMock, monitor: typeof createAtomicSpy, postulate: (f: VoidFn) => void, conclude: (f: VoidFn) => void, given: (f: VoidFn) => void;
|
|
114
|
+
export declare const intend: (n: string, f: VoidFn) => void, story: (n: string, f: VoidFn) => void, detail: (n: string, f: VoidFn) => void, scenario: (n: string, f: VoidFn) => void, to: <T>(val: T) => UniversalAssertion<T>, dummy: typeof createAtomicMock, standIn: typeof createAtomicMock, watch: typeof createAtomicSpy, shadow: typeof createAtomicSpy, background: (f: VoidFn) => void, cleanup: (f: VoidFn) => void, before: (f: VoidFn) => void;
|
|
115
|
+
export declare const ensure: (n: string, f: VoidFn) => void, suite: (n: string, f: VoidFn) => void, check: (n: string, f: VoidFn) => void, verify: (n: string, f: VoidFn) => void, that: <T>(val: T) => UniversalAssertion<T>, stub: typeof createAtomicMock, mock: typeof createAtomicMock, inspect: typeof createAtomicSpy, spy: typeof createAtomicSpy, initAll: (f: VoidFn) => void, disposeAll: (f: VoidFn) => void, reset: (f: VoidFn) => void;
|
|
116
|
+
export declare const describe: (n: string, f: VoidFn) => void, it: (n: string, f: VoidFn) => void, test: (n: string, f: VoidFn) => void, expect: <T>(val: T) => UniversalAssertion<T>, beforeAll: (f: VoidFn) => void, afterAll: (f: VoidFn) => void, beforeEach: (f: VoidFn) => void, afterEach: (f: VoidFn) => void;
|
|
113
117
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// 1. ATOMIC CORE ENGINE (O Motor)
|
|
4
4
|
// ============================================================================
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ClassicDialect = exports.ImperativeDialect = exports.NarrativeDialect = exports.MathDialect = void 0;
|
|
6
|
+
exports.afterEach = exports.beforeEach = exports.afterAll = exports.beforeAll = exports.expect = exports.test = exports.it = exports.describe = exports.reset = exports.disposeAll = exports.initAll = exports.spy = exports.inspect = exports.mock = exports.stub = exports.that = exports.verify = exports.check = exports.suite = exports.ensure = exports.before = exports.cleanup = exports.background = exports.shadow = exports.watch = exports.standIn = exports.dummy = exports.to = exports.scenario = exports.detail = exports.story = exports.intend = exports.given = exports.conclude = exports.postulate = exports.monitor = exports.lambda = exports.arbitrary = exports.implies = exports.proof = exports.axiom = exports.ClassicDialect = exports.ImperativeDialect = exports.NarrativeDialect = exports.MathDialect = void 0;
|
|
7
7
|
class AtomicCore {
|
|
8
8
|
static instance;
|
|
9
9
|
rootSuite = {
|
|
@@ -297,4 +297,8 @@ exports.ClassicDialect = {
|
|
|
297
297
|
beforeEach: (f) => core.addHook("beforeEach", f),
|
|
298
298
|
afterEach: (f) => core.addHook("afterEach", f),
|
|
299
299
|
};
|
|
300
|
-
//
|
|
300
|
+
// --- Top-Level Exports for Ease of Use ---
|
|
301
|
+
exports.axiom = exports.MathDialect.axiom, exports.proof = exports.MathDialect.proof, exports.implies = exports.MathDialect.implies, exports.arbitrary = exports.MathDialect.arbitrary, exports.lambda = exports.MathDialect.lambda, exports.monitor = exports.MathDialect.monitor, exports.postulate = exports.MathDialect.postulate, exports.conclude = exports.MathDialect.conclude, exports.given = exports.MathDialect.given;
|
|
302
|
+
exports.intend = exports.NarrativeDialect.intend, exports.story = exports.NarrativeDialect.story, exports.detail = exports.NarrativeDialect.detail, exports.scenario = exports.NarrativeDialect.scenario, exports.to = exports.NarrativeDialect.to, exports.dummy = exports.NarrativeDialect.dummy, exports.standIn = exports.NarrativeDialect.standIn, exports.watch = exports.NarrativeDialect.watch, exports.shadow = exports.NarrativeDialect.shadow, exports.background = exports.NarrativeDialect.background, exports.cleanup = exports.NarrativeDialect.cleanup, exports.before = exports.NarrativeDialect.before;
|
|
303
|
+
exports.ensure = exports.ImperativeDialect.ensure, exports.suite = exports.ImperativeDialect.suite, exports.check = exports.ImperativeDialect.check, exports.verify = exports.ImperativeDialect.verify, exports.that = exports.ImperativeDialect.that, exports.stub = exports.ImperativeDialect.stub, exports.mock = exports.ImperativeDialect.mock, exports.inspect = exports.ImperativeDialect.inspect, exports.spy = exports.ImperativeDialect.spy, exports.initAll = exports.ImperativeDialect.initAll, exports.disposeAll = exports.ImperativeDialect.disposeAll, exports.reset = exports.ImperativeDialect.reset;
|
|
304
|
+
exports.describe = exports.ClassicDialect.describe, exports.it = exports.ClassicDialect.it, exports.test = exports.ClassicDialect.test, exports.expect = exports.ClassicDialect.expect, exports.beforeAll = exports.ClassicDialect.beforeAll, exports.afterAll = exports.ClassicDialect.afterAll, exports.beforeEach = exports.ClassicDialect.beforeEach, exports.afterEach = exports.ClassicDialect.afterEach;
|
package/docs/4-ideias.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
4 Ideias Contraintuitivas do one-spec-4-all que Vão Mudar a Forma Como Você Escreve Testes
|
|
2
|
+
|
|
3
|
+
Adotar um novo framework de testes muitas vezes vem com um custo alto: a sensação de que precisamos "jogar tudo fora e reaprender a programar". É um processo que paralisa equipes e desvaloriza o código construído ao longo de anos. O framework one-spec-4-all chega com uma filosofia surpreendentemente diferente, a "Filosofia Aditiva" — uma abordagem que respeita seu tempo e seu código. Neste post, vamos explorar as quatro ideias mais impactantes e contraintuitivas por trás dessa nova forma de pensar que promete expandir, e não substituir, seu ecossistema de testes.
|
|
4
|
+
|
|
5
|
+
1. Você não precisa reescrever uma linha de código legado
|
|
6
|
+
|
|
7
|
+
A primeira e mais radical ideia do one-spec-4-all é o respeito absoluto pelo seu trabalho existente. A "Filosofia Aditiva" significa que o framework funciona como um superset (superconjunto) do Jest. Ele não exige a reescrita de testes antigos.
|
|
8
|
+
|
|
9
|
+
Isso quer dizer que os milhares de testes describe/it/expect que sua equipe já possui continuarão funcionando perfeitamente, com um único comando de execução. Não há migração forçada, nem quebra de compatibilidade.
|
|
10
|
+
|
|
11
|
+
O one-spec-4-all Tester não veio para destruir o Jest ou o Mocha. Ele veio para dar vocabulário onde o vocabulário padrão falha em expressar a intenção. É uma ferramenta de adição, não de substituição.
|
|
12
|
+
|
|
13
|
+
Essa abordagem é crucial porque reduz drasticamente o risco de adoção. Ela respeita o trabalho já feito pela equipe e permite que novas funcionalidades e conceitos sejam introduzidos de forma gradual e segura, sem a necessidade de um "big bang" ou de uma refatoração massiva.
|
|
14
|
+
|
|
15
|
+
2. A linguagem do seu teste deveria refletir a do seu problema
|
|
16
|
+
|
|
17
|
+
A frustração central que o one-spec-4-all resolve é o desalinhamento semântico. A linguagem padrão de testes, com describe e it, foi projetada para descrever comportamentos de componentes, mas se torna inadequada em outros contextos, como provar a correção de um algoritmo ou verificar a conformidade de um contrato de API.
|
|
18
|
+
|
|
19
|
+
Veja como a linguagem padrão soa "errada" e informal ao testar uma função de criptografia pura: describe("SHA-256", () => { it("should produce a valid hash"...) })
|
|
20
|
+
|
|
21
|
+
Em contraste, o Dialeto Matemático do framework reflete o rigor necessário com uma sintaxe de prova axiomática, alinhada à natureza do problema:
|
|
22
|
+
|
|
23
|
+
axiom("Teoria de Hash SHA-256", () => {
|
|
24
|
+
proof("Hash de string vazia converge para constante conhecida", () => {
|
|
25
|
+
implies(sha256("")).is("e3b0c44...");
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
A linguagem importa. Ela molda o pensamento.
|
|
30
|
+
|
|
31
|
+
Utilizar a semântica correta aumenta fundamentalmente a clareza, o rigor e, mais importante, a intenção do teste. A equipe não está mais descrevendo um comportamento; está, de fato, provando uma verdade universal, o que alinha a mentalidade do desenvolvedor com o rigor matemático do problema. O código passa a comunicar o que está fazendo de forma muito mais precisa.
|
|
32
|
+
|
|
33
|
+
3. O framework é poliglota para que você não precise ser
|
|
34
|
+
|
|
35
|
+
A ideia de múltiplos "dialetos" de teste pode soar como um convite ao caos, o chamado "Mito da Torre de Babel". No entanto, a proposta do one-spec-4-all é o oposto: a ideia não é que cada desenvolvedor aprenda todos os dialetos, mas que cada especialista use a ferramenta mais afiada para sua tarefa, adotando a mentalidade certa para o trabalho.
|
|
36
|
+
|
|
37
|
+
O conceito é a Especialização por Contexto:
|
|
38
|
+
|
|
39
|
+
- Cientista de Dados: Foca no MathDialect, adotando uma mentalidade científica e axiomática para provar a correção de algoritmos.
|
|
40
|
+
- Product Manager e Designer: Utilizam o NarrativeDialect para descrever fluxos de usuário de forma legível, focando na experiência humana e no comportamento do sistema.
|
|
41
|
+
- Engenheiro de Backend/Sistemas: Usa o ImperativeDialect para garantir a conformidade de APIs, adotando uma mentalidade rigorosa de engenharia de sistemas.
|
|
42
|
+
|
|
43
|
+
A mensagem é clara: "Você não precisa aprender 4 idiomas. Você escolhe um que se adapta ao seu projeto e ignora o resto." Essa abordagem reduz a carga cognitiva, permitindo que as equipes trabalhem com ferramentas especializadas e de alta precisão sem nunca sair do mesmo ecossistema de testes.
|
|
44
|
+
|
|
45
|
+
4. Seus testes podem virar a documentação que seu time de produto entende
|
|
46
|
+
|
|
47
|
+
Uma das barreiras mais comuns no desenvolvimento de software é a comunicação entre a equipe de produto e a de engenharia. Os testes, que deveriam ser a fonte da verdade sobre o comportamento do sistema, são frequentemente ilegíveis para pessoas não-técnicas.
|
|
48
|
+
|
|
49
|
+
O Dialeto Narrativo do one-spec-4-all foi criado para destruir essa barreira. Veja a diferença:
|
|
50
|
+
|
|
51
|
+
// Antes (Ilegível para não-técnicos)
|
|
52
|
+
it("should return 403", () => { /_ ... _/ });
|
|
53
|
+
|
|
54
|
+
// Depois (Claro para todos)
|
|
55
|
+
scenario("Usuário sem permissão tenta acessar o painel de Admin", () => {
|
|
56
|
+
const response = attemptAccessAsUnauthorizedUser();
|
|
57
|
+
to(response.status).be(403);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
Essa mudança transforma os testes em um "contrato vivo" e uma "documentação viva". Product Managers e Designers podem agora ler os testes e validar se as regras de negócio foram implementadas conforme o esperado. Os testes se tornam a fonte única de verdade para as regras de negócio, quebrando a barreira clássica entre produto e engenharia e garantindo que o produto construído é, de fato, o produto desejado.
|
|
61
|
+
|
|
62
|
+
Conclusão
|
|
63
|
+
|
|
64
|
+
O one-spec-4-all não se apresenta como um substituto revolucionário, mas como uma evolução inteligente. Sua filosofia é adicionar poder de expressão ao ecossistema que já conhecemos, uma ferramenta que lhe devolve o poder de escolher a palavra certa para o problema certo. Ele nos dá o vocabulário para alinhar a linguagem do teste à natureza do problema.
|
|
65
|
+
|
|
66
|
+
Depois de ver essa abordagem, a pergunta que fica é: será que a forma como sempre escrevemos testes tem limitado a forma como pensamos sobre nossos próprios sistemas?
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# 🛡️ Dialeto Imperativo - API Completa
|
|
2
|
+
|
|
3
|
+
> **Filosofia:** Design by Contract e Engenharia de Sistemas.
|
|
4
|
+
>
|
|
5
|
+
> **Vibe:** Técnica, Rigorosa, "Crachá de Engenheiro".
|
|
6
|
+
>
|
|
7
|
+
> **Ideal para:** Engenheiros de backend, DevOps, validação de APIs, conformidade (compliance).
|
|
8
|
+
|
|
9
|
+
## Estrutura
|
|
10
|
+
|
|
11
|
+
| Função | Descrição | Equivalente Jest |
|
|
12
|
+
| ------------------ | ------------------------------- | ---------------- |
|
|
13
|
+
| `ensure(name, fn)` | Garante um requisito de sistema | `describe` |
|
|
14
|
+
| `suite(name, fn)` | Uma suite de verificações | `describe` |
|
|
15
|
+
| `check(name, fn)` | Uma checagem pontual | `test` / `it` |
|
|
16
|
+
| `verify(name, fn)` | Verificação de conformidade | `test` / `it` |
|
|
17
|
+
|
|
18
|
+
## Asserções
|
|
19
|
+
|
|
20
|
+
| Função | Descrição | Equivalente Jest |
|
|
21
|
+
| ----------------------------- | --------------------- | ---------------------------------------- |
|
|
22
|
+
| `that(val).is(expected)` | "Que o valor é..." | `expect(val).toBe(expected)` |
|
|
23
|
+
| `that(val).isOk()` | Verifica "truthiness" | `expect(val).toBeTruthy()` |
|
|
24
|
+
| `that(val).matches(regex)` | Validação de padrão | `expect(val).toMatch(regex)` |
|
|
25
|
+
| `that(val).triggered()` | Verifica disparo | `expect(val).toHaveBeenCalled()` |
|
|
26
|
+
| `that(val).calledWith(args)` | Verifica payload | `expect(val).toHaveBeenCalledWith(args)` |
|
|
27
|
+
| `that(val).triggeredCount(n)` | Contagem exata | `expect(val).toHaveBeenCalledTimes(n)` |
|
|
28
|
+
|
|
29
|
+
## Mocks
|
|
30
|
+
|
|
31
|
+
| Função | Descrição | Equivalente Jest |
|
|
32
|
+
| ---------------------- | -------------------------------- | ------------------------- |
|
|
33
|
+
| `stub()` | Cria um stub de infraestrutura | `jest.fn()` |
|
|
34
|
+
| `mock()` | Alias para stub | `jest.fn()` |
|
|
35
|
+
| `inspect(obj, method)` | Inspeciona um método interno | `jest.spyOn(obj, method)` |
|
|
36
|
+
| `spy(obj, method)` | Alias clássico | `jest.spyOn(obj, method)` |
|
|
37
|
+
| `s.forceReturn(val)` | Força um retorno imediato | `mockReturnValue(val)` |
|
|
38
|
+
| `s.resolveWith(val)` | Resolve promessa (network) | `mockResolvedValue(val)` |
|
|
39
|
+
| `s.executes(fn)` | Executa implementação substituta | `mockImplementation(fn)` |
|
|
40
|
+
|
|
41
|
+
## Lifecycle
|
|
42
|
+
|
|
43
|
+
| Função | Descrição | Equivalente Jest |
|
|
44
|
+
| ---------------- | ------------------------------- | ---------------- |
|
|
45
|
+
| `initAll(fn)` | Inicialização de sistema | `beforeAll(fn)` |
|
|
46
|
+
| `reset(fn)` | Reset de estado (antes de cada) | `beforeEach(fn)` |
|
|
47
|
+
| `disposeAll(fn)` | Descarte de recursos | `afterAll(fn)` |
|
|
48
|
+
|
|
49
|
+
## Exemplo Completo
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
import {
|
|
53
|
+
ensure,
|
|
54
|
+
check,
|
|
55
|
+
verify,
|
|
56
|
+
that,
|
|
57
|
+
stub,
|
|
58
|
+
initAll,
|
|
59
|
+
reset,
|
|
60
|
+
} from "@purecore/one-spec-4-all";
|
|
61
|
+
|
|
62
|
+
ensure("Conformidade do Gateway de Pagamento", () => {
|
|
63
|
+
let apiGateway;
|
|
64
|
+
let transactionLogger;
|
|
65
|
+
|
|
66
|
+
initAll(() => {
|
|
67
|
+
apiGateway = stub();
|
|
68
|
+
transactionLogger = stub();
|
|
69
|
+
|
|
70
|
+
apiGateway.forceReturn({
|
|
71
|
+
status: 200,
|
|
72
|
+
transactionId: "tx_abc123",
|
|
73
|
+
timestamp: "2026-01-01T00:00:00Z",
|
|
74
|
+
});
|
|
75
|
+
transactionLogger.forceReturn(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
reset(() => {
|
|
79
|
+
// Limpa estado entre testes se necessário
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
check("Transação bem-sucedida retorna status 200", () => {
|
|
83
|
+
const response = apiGateway.process({
|
|
84
|
+
amount: 99.9,
|
|
85
|
+
currency: "BRL",
|
|
86
|
+
cardToken: "tok_visa_xxxx",
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
that(response.status).is(200);
|
|
90
|
+
that(response.transactionId).matches(/^tx_[a-z0-9]+$/);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
verify("TransactionId segue padrão RFC do contrato", () => {
|
|
94
|
+
const response = apiGateway.process({ amount: 50 });
|
|
95
|
+
|
|
96
|
+
that(response.transactionId).matches(/^tx_[a-z0-9]{6,}$/);
|
|
97
|
+
that(response.timestamp).matches(/^\d{4}-\d{2}-\d{2}T/);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
check("Toda transação deve ser logada para auditoria", () => {
|
|
101
|
+
apiGateway.process({ amount: 100 });
|
|
102
|
+
transactionLogger.log({ event: "PAYMENT_PROCESSED" });
|
|
103
|
+
|
|
104
|
+
that(transactionLogger).triggered();
|
|
105
|
+
that(transactionLogger).calledWith({ event: "PAYMENT_PROCESSED" });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
verify("Gateway deve ser acionado exatamente uma vez por request", () => {
|
|
109
|
+
that(apiGateway).triggeredCount(1);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Quando Usar
|
|
115
|
+
|
|
116
|
+
- ✅ Integrações de API
|
|
117
|
+
- ✅ Drivers de banco de dados
|
|
118
|
+
- ✅ Validação de contratos RFC
|
|
119
|
+
- ✅ Conformidade com regulamentos (PCI-DSS, LGPD, etc.)
|
|
120
|
+
- ✅ Testes de infraestrutura
|
|
121
|
+
|
|
122
|
+
## Quando NÃO Usar
|
|
123
|
+
|
|
124
|
+
- ❌ Algoritmos matemáticos puros → Use o [Dialeto Matemático](./api-matematico.md)
|
|
125
|
+
- ❌ Fluxos de usuário legíveis por PMs → Use o [Dialeto Narrativo](./api-narrativo.md)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# 📐 Dialeto Matemático - API Completa
|
|
2
|
+
|
|
3
|
+
> **Filosofia:** Lógica Formal e Programação Funcional.
|
|
4
|
+
>
|
|
5
|
+
> **Vibe:** Científica, Imutável, Axiomática.
|
|
6
|
+
>
|
|
7
|
+
> **Ideal para:** Cientistas de dados, engenheiros de algoritmos, bibliotecas de utilitários, cálculos financeiros.
|
|
8
|
+
|
|
9
|
+
## Estrutura
|
|
10
|
+
|
|
11
|
+
| Função | Descrição | Equivalente Jest |
|
|
12
|
+
| ----------------- | ---------------------------------------- | ---------------- |
|
|
13
|
+
| `axiom(name, fn)` | Define um grupo de verdades fundamentais | `describe` |
|
|
14
|
+
| `proof(name, fn)` | Uma prova individual de um caso | `test` / `it` |
|
|
15
|
+
| `lemma(name, fn)` | Uma prova auxiliar (menor) | `test` / `it` |
|
|
16
|
+
|
|
17
|
+
## Asserções
|
|
18
|
+
|
|
19
|
+
| Função | Descrição | Equivalente Jest |
|
|
20
|
+
| --------------------------------- | ----------------------------------- | ---------------------------------------- |
|
|
21
|
+
| `implies(val).is(expected)` | "O valor implica ser..." | `expect(val).toBe(expected)` |
|
|
22
|
+
| `implies(val).wasEvaluated()` | Verifica se a função foi computada | `expect(val).toHaveBeenCalled()` |
|
|
23
|
+
| `implies(val).appliedTo(args)` | Verifica os argumentos da aplicação | `expect(val).toHaveBeenCalledWith(args)` |
|
|
24
|
+
| `implies(val).evaluated(n).times` | Frequência de avaliação | `expect(val).toHaveBeenCalledTimes(n)` |
|
|
25
|
+
| `implies(val).matches(regex)` | Corresponde a um padrão | `expect(val).toMatch(regex)` |
|
|
26
|
+
|
|
27
|
+
## Mocks
|
|
28
|
+
|
|
29
|
+
| Função | Descrição | Equivalente Jest |
|
|
30
|
+
| ---------------------- | ---------------------------------------- | ------------------------- |
|
|
31
|
+
| `arbitrary()` | Cria uma função genérica para teste | `jest.fn()` |
|
|
32
|
+
| `lambda()` | Alias para arbitrary | `jest.fn()` |
|
|
33
|
+
| `monitor(obj, method)` | Monitora uma função existente | `jest.spyOn(obj, method)` |
|
|
34
|
+
| `f.yields(val)` | Define o resultado produzido pela função | `mockReturnValue(val)` |
|
|
35
|
+
| `f.mapsTo(val)` | Alias para yields | `mockReturnValue(val)` |
|
|
36
|
+
| `f.convergesTo(val)` | Define o resultado assíncrono (limite) | `mockResolvedValue(val)` |
|
|
37
|
+
| `f.derive(fn)` | Define a implementação lógica | `mockImplementation(fn)` |
|
|
38
|
+
|
|
39
|
+
## Lifecycle
|
|
40
|
+
|
|
41
|
+
| Função | Descrição | Equivalente Jest |
|
|
42
|
+
| --------------- | ----------------------------------- | ---------------- |
|
|
43
|
+
| `postulate(fn)` | Define premissas iniciais globais | `beforeAll(fn)` |
|
|
44
|
+
| `setup(fn)` | Alias para postulate | `beforeAll(fn)` |
|
|
45
|
+
| `given(fn)` | "Dado que..." (antes de cada prova) | `beforeEach(fn)` |
|
|
46
|
+
| `conclude(fn)` | Conclusões finais / limpeza | `afterAll(fn)` |
|
|
47
|
+
|
|
48
|
+
## Exemplo Completo
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
import {
|
|
52
|
+
axiom,
|
|
53
|
+
proof,
|
|
54
|
+
lemma,
|
|
55
|
+
implies,
|
|
56
|
+
arbitrary,
|
|
57
|
+
monitor,
|
|
58
|
+
postulate,
|
|
59
|
+
given,
|
|
60
|
+
conclude,
|
|
61
|
+
} from "@purecore/one-spec-4-all";
|
|
62
|
+
|
|
63
|
+
axiom("Teoria de Juros Compostos", () => {
|
|
64
|
+
let calcInterest;
|
|
65
|
+
let applyTax;
|
|
66
|
+
const logger = arbitrary();
|
|
67
|
+
|
|
68
|
+
postulate(() => {
|
|
69
|
+
// Premissas globais para todos os teoremas deste axioma
|
|
70
|
+
console.log("Estabelecendo premissas...");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
given(() => {
|
|
74
|
+
// Definimos as funções puras a serem testadas a cada ciclo
|
|
75
|
+
calcInterest = (p, r, t) => Math.floor(p * Math.pow(1 + r, t));
|
|
76
|
+
applyTax = (subtotal, rate) =>
|
|
77
|
+
Math.round(subtotal * (1 + rate) * 100) / 100;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
proof("Capital de 1000 a 5% por 2 anos implica montante de 1102", () => {
|
|
81
|
+
const result = calcInterest(1000, 0.05, 2);
|
|
82
|
+
implies(result).is(1102);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
proof("Taxa zero implica preservação do capital", () => {
|
|
86
|
+
const result = calcInterest(500, 0, 10);
|
|
87
|
+
implies(result).is(500);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
proof("Desconto de 100% anula o preço", () => {
|
|
91
|
+
const calcDiscount = (price, percent) => price - price * (percent / 100);
|
|
92
|
+
implies(calcDiscount(500, 100)).is(0);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
lemma("Imposto de 10% sobre R$100 resulta em R$110", () => {
|
|
96
|
+
implies(applyTax(100, 0.1)).is(110);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
proof("Logger arbitrário registra cálculo", () => {
|
|
100
|
+
logger.yields(true);
|
|
101
|
+
logger("calc_start");
|
|
102
|
+
|
|
103
|
+
implies(logger).wasEvaluated();
|
|
104
|
+
implies(logger).appliedTo("calc_start");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
conclude(() => {
|
|
108
|
+
console.log("Teoremas provados com sucesso.");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
axiom("Teoria de Fibonacci", () => {
|
|
113
|
+
const fib = arbitrary();
|
|
114
|
+
|
|
115
|
+
given(() => {
|
|
116
|
+
// Definição recursiva de Fibonacci
|
|
117
|
+
fib.derive((n) => (n <= 1 ? n : fib(n - 1) + fib(n - 2)));
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
proof("Fibonacci(0) implica 0", () => {
|
|
121
|
+
implies(fib(0)).is(0);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
proof("Fibonacci(1) implica 1", () => {
|
|
125
|
+
implies(fib(1)).is(1);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
proof("Fibonacci(10) implica 55", () => {
|
|
129
|
+
implies(fib(10)).is(55);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Quando Usar
|
|
135
|
+
|
|
136
|
+
- ✅ Algoritmos matemáticos puros
|
|
137
|
+
- ✅ Funções de criptografia
|
|
138
|
+
- ✅ Cálculos financeiros
|
|
139
|
+
- ✅ Regras de negócio determinísticas
|
|
140
|
+
- ✅ Bibliotecas de utilitários
|
|
141
|
+
|
|
142
|
+
## Quando NÃO Usar
|
|
143
|
+
|
|
144
|
+
- ❌ Integrações de API com contratos → Use o [Dialeto Imperativo](./api-imperativo.md)
|
|
145
|
+
- ❌ Fluxos de usuário legíveis por PMs → Use o [Dialeto Narrativo](./api-narrativo.md)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# 📖 Dialeto Narrativo - API Completa
|
|
2
|
+
|
|
3
|
+
> **Filosofia:** BDD (Behavior Driven Development) e Storytelling.
|
|
4
|
+
>
|
|
5
|
+
> **Vibe:** Fluida, Humana, Descritiva.
|
|
6
|
+
>
|
|
7
|
+
> **Ideal para:** Designers, Product Managers, times ágeis, testes de fluxos de usuário (User Journeys).
|
|
8
|
+
|
|
9
|
+
## Estrutura
|
|
10
|
+
|
|
11
|
+
| Função | Descrição | Equivalente Jest |
|
|
12
|
+
| -------------------- | ----------------------------------- | ---------------- |
|
|
13
|
+
| `intend(name, fn)` | Define a intenção do bloco | `describe` |
|
|
14
|
+
| `story(name, fn)` | Alias para agrupamento de histórias | `describe` |
|
|
15
|
+
| `detail(name, fn)` | Detalha um comportamento específico | `test` / `it` |
|
|
16
|
+
| `scenario(name, fn)` | Um cenário de uso | `test` / `it` |
|
|
17
|
+
|
|
18
|
+
## Asserções
|
|
19
|
+
|
|
20
|
+
| Função | Descrição | Equivalente Jest |
|
|
21
|
+
| ------------------------- | ------------------------------ | ---------------------------------------- |
|
|
22
|
+
| `to(val).be(expected)` | "Para o valor ser..." | `expect(val).toBe(expected)` |
|
|
23
|
+
| `to(val).have(prop)` | Verifica posse de propriedade | `expect(val).toHaveProperty(prop)` |
|
|
24
|
+
| `to(val).wasCalled()` | Verifica se o ator foi chamado | `expect(val).toHaveBeenCalled()` |
|
|
25
|
+
| `to(val).received(args)` | Verifica o que foi recebido | `expect(val).toHaveBeenCalledWith(args)` |
|
|
26
|
+
| `to(val).called(n).times` | Contagem de chamadas | `expect(val).toHaveBeenCalledTimes(n)` |
|
|
27
|
+
|
|
28
|
+
## Mocks (Dublês/Atores)
|
|
29
|
+
|
|
30
|
+
| Função | Descrição | Equivalente Jest |
|
|
31
|
+
| ---------------------------- | -------------------------------- | ------------------------- |
|
|
32
|
+
| `dummy()` | Um dublê (ator) no lugar do real | `jest.fn()` |
|
|
33
|
+
| `standIn()` | Alias para dummy | `jest.fn()` |
|
|
34
|
+
| `watch(obj, method)` | Observa um ator existente | `jest.spyOn(obj, method)` |
|
|
35
|
+
| `shadow(obj, method)` | Segue (shadows) um método | `jest.spyOn(obj, method)` |
|
|
36
|
+
| `actor.respondsWith(val)` | Define a resposta do ator | `mockReturnValue(val)` |
|
|
37
|
+
| `actor.eventuallyGives(val)` | Resposta futura (promessa) | `mockResolvedValue(val)` |
|
|
38
|
+
| `actor.actsLike(fn)` | Define como o ator deve agir | `mockImplementation(fn)` |
|
|
39
|
+
|
|
40
|
+
## Lifecycle
|
|
41
|
+
|
|
42
|
+
| Função | Descrição | Equivalente Jest |
|
|
43
|
+
| ---------------- | ----------------------------- | ---------------- |
|
|
44
|
+
| `background(fn)` | Contexto de fundo da história | `beforeAll(fn)` |
|
|
45
|
+
| `before(fn)` | Antes de cada cena | `beforeEach(fn)` |
|
|
46
|
+
| `cleanup(fn)` | Limpeza após a história | `afterAll(fn)` |
|
|
47
|
+
|
|
48
|
+
## Exemplo Completo
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
import {
|
|
52
|
+
intend,
|
|
53
|
+
story,
|
|
54
|
+
scenario,
|
|
55
|
+
detail,
|
|
56
|
+
to,
|
|
57
|
+
standIn,
|
|
58
|
+
dummy,
|
|
59
|
+
watch,
|
|
60
|
+
background,
|
|
61
|
+
before,
|
|
62
|
+
cleanup,
|
|
63
|
+
} from "@purecore/one-spec-4-all";
|
|
64
|
+
|
|
65
|
+
intend("Fluxo de Autenticação do Usuário", () => {
|
|
66
|
+
const authService = standIn();
|
|
67
|
+
const database = standIn();
|
|
68
|
+
const emailSender = dummy();
|
|
69
|
+
|
|
70
|
+
background(() => {
|
|
71
|
+
// Configura o cenário de fundo para toda a história
|
|
72
|
+
authService.respondsWith({ token: "abc-123", expiresIn: 3600 });
|
|
73
|
+
database.respondsWith(true);
|
|
74
|
+
emailSender.respondsWith({ sent: true });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
before(() => {
|
|
78
|
+
// Reset de estado antes de cada cena
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
scenario("Login com credenciais válidas deve retornar token", () => {
|
|
82
|
+
const response = authService.login("usuario", "senha_secreta");
|
|
83
|
+
|
|
84
|
+
to(response).have("token");
|
|
85
|
+
to(response.token).be("abc-123");
|
|
86
|
+
to(response).have("expiresIn");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
scenario("Login deve registrar tentativa no banco de dados", () => {
|
|
90
|
+
authService.login("usuario", "senha_secreta");
|
|
91
|
+
database.logAttempt("usuario", "success");
|
|
92
|
+
|
|
93
|
+
to(database).wasCalled();
|
|
94
|
+
to(database).received("logAttempt", "usuario", "success");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
scenario("Login bem-sucedido envia email de boas-vindas", () => {
|
|
98
|
+
authService.login("novo_usuario", "senha123");
|
|
99
|
+
emailSender.send("novo_usuario@email.com", "Bem-vindo!");
|
|
100
|
+
|
|
101
|
+
to(emailSender).wasCalled();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
cleanup(() => {
|
|
105
|
+
console.log("Histórias de autenticação concluídas.");
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
story("Jornada de Compra do Usuário", () => {
|
|
110
|
+
const cart = standIn();
|
|
111
|
+
const user = standIn();
|
|
112
|
+
const paymentGateway = standIn();
|
|
113
|
+
|
|
114
|
+
background(() => {
|
|
115
|
+
cart.respondsWith({ items: [], total: 0 });
|
|
116
|
+
user.respondsWith({ name: "João", loggedIn: true });
|
|
117
|
+
paymentGateway.respondsWith({ status: "approved" });
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
scenario("Usuário adiciona primeiro produto ao carrinho", () => {
|
|
121
|
+
cart.add({ name: "Camiseta", price: 49.9, quantity: 1 });
|
|
122
|
+
cart.respondsWith({
|
|
123
|
+
items: [{ name: "Camiseta", price: 49.9, quantity: 1 }],
|
|
124
|
+
total: 49.9,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
to(cart).wasCalled();
|
|
128
|
+
to(cart.items).have("length");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
scenario("Usuário aplica cupom de desconto", () => {
|
|
132
|
+
const coupon = standIn();
|
|
133
|
+
coupon.respondsWith({ valid: true, discount: 15 });
|
|
134
|
+
|
|
135
|
+
cart.applyCoupon("DESCONTO15");
|
|
136
|
+
cart.respondsWith({ total: 42.42 }); // 49.9 - 15%
|
|
137
|
+
|
|
138
|
+
to(coupon).wasCalled();
|
|
139
|
+
to(cart.total).be(42.42);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
detail("Carrinho vazio mostra mensagem amigável", () => {
|
|
143
|
+
cart.respondsWith({
|
|
144
|
+
items: [],
|
|
145
|
+
total: 0,
|
|
146
|
+
message: "Seu carrinho está vazio",
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
to(cart.message).be("Seu carrinho está vazio");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
scenario("Usuário não logado tenta finalizar compra", () => {
|
|
153
|
+
user.respondsWith({ loggedIn: false });
|
|
154
|
+
const checkout = standIn();
|
|
155
|
+
checkout.respondsWith({ error: "LOGIN_REQUIRED", status: 401 });
|
|
156
|
+
|
|
157
|
+
to(checkout.status).be(401);
|
|
158
|
+
to(checkout.error).be("LOGIN_REQUIRED");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
scenario("Pagamento aprovado finaliza a compra", () => {
|
|
162
|
+
cart.checkout();
|
|
163
|
+
|
|
164
|
+
to(paymentGateway.status).be("approved");
|
|
165
|
+
to(cart).wasCalled();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Quando Usar
|
|
171
|
+
|
|
172
|
+
- ✅ Fluxos de usuário (User Journeys)
|
|
173
|
+
- ✅ Testes E2E legíveis
|
|
174
|
+
- ✅ Documentação viva para PMs
|
|
175
|
+
- ✅ Regras de negócio de alto nível
|
|
176
|
+
- ✅ Testes de aceitação
|
|
177
|
+
|
|
178
|
+
## Quando NÃO Usar
|
|
179
|
+
|
|
180
|
+
- ❌ Algoritmos matemáticos puros → Use o [Dialeto Matemático](./api-matematico.md)
|
|
181
|
+
- ❌ Integrações técnicas com contratos rígidos → Use o [Dialeto Imperativo](./api-imperativo.md)
|