@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.
Files changed (38) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/critica.md +77 -0
  3. package/dist/index.d.ts +4 -0
  4. package/dist/index.js +6 -2
  5. package/docs/4-ideias.md +66 -0
  6. package/docs/api-imperativo.md +125 -0
  7. package/docs/api-matematico.md +145 -0
  8. package/docs/api-narrativo.md +181 -0
  9. package/docs/guia-rapido.md +189 -0
  10. package/docs/whitepaper.md +246 -0
  11. package/examples/imperative.spec.ts +58 -0
  12. package/examples/math.spec.ts +52 -0
  13. package/examples/narrative.spec.ts +54 -0
  14. package/examples/polyglot-shopping-cart.spec.ts +200 -0
  15. package/index.html +427 -2
  16. package/infograficos/detalhado.png +0 -0
  17. package/infograficos/normal.png +0 -0
  18. package/logo.png +0 -0
  19. package/package.json +4 -2
  20. package/podcast/O_Matem/303/241tico,_o_Narrador_e_o_Engenheiro.json +0 -0
  21. package/podcast/O_Matem/303/241tico,_o_Narrador_e_o_Engenheiro.md +0 -0
  22. package/podcast/critica-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.json +0 -0
  23. package/podcast/critica-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.md +0 -0
  24. package/podcast/critica-Unificar_filosofia_e_pr/303/241tica_na_documenta/303/247/303/243o_(7_words__covers_t.md +1 -0
  25. package/podcast/critica-Unificar_filosofia_e_pr/303/241tica_na_documenta/303/247/303/243o__7_words__covers_t.ogg +0 -0
  26. package/podcast/critica2-Sil/303/252ncio_estrat/303/251gico_e_sobrecarga_em_READMEs.ogg +0 -0
  27. package/podcast/critica2.json +3191 -0
  28. package/podcast/critica2.md +1 -0
  29. package/podcast/debate-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.json +0 -0
  30. package/podcast/debate-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.md +0 -0
  31. package/readme.md +234 -141
  32. package/reports/01-01-2026_00-45.md +40 -0
  33. package/reports/31-12-2025_22-35.md +25 -0
  34. package/reports/31-12-2025_22-45.md +15 -0
  35. package/slides/Dialetos_de_Teste_Um_Executor_M/303/272ltiplos_Vocabul/303/241rios.pdf +0 -0
  36. package/src/cli.ts +413 -22
  37. package/src/index.ts +50 -1
  38. 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
- // End of library exports
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;
@@ -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)