@vibe2founder/tests2dialects 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 (96) hide show
  1. package/dist/examples/imperative.spec.d.ts +1 -0
  2. package/dist/examples/imperative.spec.js +46 -0
  3. package/dist/examples/math.spec.d.ts +1 -0
  4. package/dist/examples/math.spec.js +39 -0
  5. package/dist/examples/narrative.spec.d.ts +1 -0
  6. package/dist/examples/narrative.spec.js +47 -0
  7. package/dist/examples/polyglot-shopping-cart.spec.d.ts +11 -0
  8. package/dist/examples/polyglot-shopping-cart.spec.js +161 -0
  9. package/dist/examples/sanity.spec.d.ts +1 -0
  10. package/dist/examples/sanity.spec.js +39 -0
  11. package/dist/examples/showcase-api.spec.d.ts +1 -0
  12. package/dist/examples/showcase-api.spec.js +62 -0
  13. package/dist/examples/test-api.d.ts +1 -0
  14. package/dist/examples/test-api.js +32 -0
  15. package/dist/packages/api-test-dialect/index.d.ts +28 -0
  16. package/dist/packages/api-test-dialect/index.js +102 -0
  17. package/dist/packages/reqify/index.d.ts +12 -0
  18. package/dist/packages/reqify/index.js +24 -0
  19. package/dist/src/cli.d.ts +6 -0
  20. package/dist/src/cli.js +330 -0
  21. package/dist/src/index.d.ts +134 -0
  22. package/dist/src/index.js +374 -0
  23. package/dist/src/semantic/core.d.ts +24 -0
  24. package/dist/src/semantic/core.js +16 -0
  25. package/{types/api-types.ts → dist/types/api-types.d.ts} +6 -11
  26. package/dist/types/api-types.js +1 -0
  27. package/package.json +59 -35
  28. package/packages/api-test-dialect/index.ts +132 -132
  29. package/readme.md +58 -58
  30. package/src/cli.ts +1 -1
  31. package/src/index.ts +19 -16
  32. package/src/semantic/core.ts +26 -0
  33. package/CHANGELOG.md +0 -73
  34. package/bun.lock +0 -22
  35. package/bunfig.toml +0 -2
  36. package/critica.md +0 -77
  37. package/docs/4-ideias.md +0 -66
  38. package/docs/api-api.md +0 -93
  39. package/docs/api-imperativo.md +0 -125
  40. package/docs/api-matematico.md +0 -145
  41. package/docs/api-narrativo.md +0 -181
  42. package/docs/guia-rapido.md +0 -189
  43. package/docs/whitepaper.md +0 -21
  44. package/examples/imperative.spec.ts +0 -58
  45. package/examples/math.spec.ts +0 -52
  46. package/examples/narrative.spec.ts +0 -61
  47. package/examples/polyglot-shopping-cart.spec.ts +0 -212
  48. package/examples/sanity.spec.ts +0 -54
  49. package/examples/showcase-api.spec.ts +0 -70
  50. package/examples/test-api.ts +0 -36
  51. package/infograficos/detalhado.png +0 -0
  52. package/infograficos/mobile.png +0 -0
  53. package/infograficos/normal.png +0 -0
  54. package/landing-page/README.md +0 -38
  55. package/landing-page/bun.lock +0 -609
  56. package/landing-page/eslint.config.js +0 -23
  57. package/landing-page/index.html +0 -17
  58. package/landing-page/package-lock.json +0 -2962
  59. package/landing-page/package.json +0 -34
  60. package/landing-page/postcss.config.js +0 -6
  61. package/landing-page/public/vite.svg +0 -1
  62. package/landing-page/src/App.tsx +0 -358
  63. package/landing-page/src/assets/react.svg +0 -1
  64. package/landing-page/src/index.css +0 -34
  65. package/landing-page/src/main.tsx +0 -10
  66. package/landing-page/tailwind.config.js +0 -59
  67. package/landing-page/tsconfig.app.json +0 -28
  68. package/landing-page/tsconfig.json +0 -7
  69. package/landing-page/tsconfig.node.json +0 -26
  70. package/landing-page/vite.config.ts +0 -7
  71. package/logo.png +0 -0
  72. package/output.log +0 -60
  73. package/podcast/O_Matem/303/241tico,_o_Narrador_e_o_Engenheiro.json +0 -0
  74. package/podcast/O_Matem/303/241tico,_o_Narrador_e_o_Engenheiro.md +0 -0
  75. package/podcast/critica-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.json +0 -0
  76. package/podcast/critica-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.md +0 -0
  77. package/podcast/critica-Unificar_filosofia_e_pr/303/241tica_na_documenta/303/247/303/243o_(7_words__covers_t.md +0 -1
  78. package/podcast/critica-Unificar_filosofia_e_pr/303/241tica_na_documenta/303/247/303/243o__7_words__covers_t.ogg +0 -0
  79. package/podcast/critica2-Sil/303/252ncio_estrat/303/251gico_e_sobrecarga_em_READMEs.ogg +0 -0
  80. package/podcast/critica2.json +0 -3191
  81. package/podcast/critica2.md +0 -1
  82. package/podcast/debate-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.json +0 -0
  83. package/podcast/debate-Dialetos_de_teste__inova/303/247/303/243o_ou_fragmenta/303/247/303/243o_.md +0 -0
  84. package/reports/01-01-2026_00-45.md +0 -40
  85. package/reports/01-01-2026_02-30.md +0 -37
  86. package/reports/03-02-2026_10-55.md +0 -8
  87. package/reports/03-02-2026_11-45.md +0 -13
  88. package/reports/03-02-2026_11-50.md +0 -10
  89. package/reports/26-01-2026_16-25.md +0 -31
  90. package/reports/26-01-2026_19-20.md +0 -27
  91. package/reports/31-12-2025_22-35.md +0 -25
  92. package/reports/31-12-2025_22-45.md +0 -15
  93. package/slides/Dialetos_de_Teste_Um_Executor_M/303/272ltiplos_Vocabul/303/241rios.pdf +0 -0
  94. package/tabela.html +0 -350
  95. package/tsconfig.json +0 -22
  96. package/www/index.html +0 -1344
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
1
+ import { ImperativeDialect } from "../src/index.js";
2
+ const { ensure, check, that, stub, initAll } = ImperativeDialect;
3
+ ensure("Sistema de Segurança e Compliance", () => {
4
+ // Stubs de Infraestrutura
5
+ const firewall = stub();
6
+ const logger = stub();
7
+ initAll(() => {
8
+ // Configuração Hardened
9
+ firewall.forceReturn({ allowed: true, threatLevel: 0 });
10
+ });
11
+ check("Deve bloquear conexões de IPs na blacklist", () => {
12
+ const maliciousIp = "192.168.1.666";
13
+ // Reconfigurando stub para este caso
14
+ firewall.executes((ip) => {
15
+ return ip === maliciousIp ? { allowed: false } : { allowed: true };
16
+ });
17
+ const access = firewall(maliciousIp);
18
+ that(access.allowed).is(false);
19
+ that(firewall).triggered();
20
+ that(firewall).calledWith(maliciousIp);
21
+ });
22
+ check("Deve registrar auditoria para cada acesso", () => {
23
+ logger("Acesso admin iniciado");
24
+ that(logger).triggeredCount(1);
25
+ that(logger).calledWith("Acesso admin iniciado");
26
+ });
27
+ });
28
+ ensure("Validação Estrutural da API V2", () => {
29
+ const apiResponse = {
30
+ status: 200,
31
+ data: {
32
+ id: "uuid-1234",
33
+ role: "admin",
34
+ meta: { version: 2 },
35
+ },
36
+ };
37
+ check("Payload deve conter metadados corretos", () => {
38
+ that(apiResponse.status).is(200);
39
+ that(apiResponse.data.role).is("admin");
40
+ that(apiResponse.data.meta.version).is(2);
41
+ });
42
+ check("Campos obrigatórios devem existir", () => {
43
+ that(apiResponse.data).isOk();
44
+ that(apiResponse.data.id).matches(/^uuid-/);
45
+ });
46
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ import { MathDialect } from "../src/index.js";
2
+ const { axiom, proof, implies, arbitrary } = MathDialect;
3
+ // --- Domain Logic: Financial Calculation ---
4
+ const calculateInterests = (principal, rate, time) => {
5
+ return principal * Math.pow(1 + rate, time);
6
+ };
7
+ axiom("Teoria Financeira: Juros Compostos", () => {
8
+ proof("O capital deve crescer exponencialmente", () => {
9
+ const principal = 1000;
10
+ const rate = 0.05; // 5%
11
+ const time = 2; // anos
12
+ const result = calculateInterests(principal, rate, time);
13
+ // 1000 * (1.05)^2 = 1000 * 1.1025 = 1102.5
14
+ implies(result).is(1102.5);
15
+ });
16
+ proof("Taxa zero implica em imutabilidade do capital", () => {
17
+ const principal = 500;
18
+ const result = calculateInterests(principal, 0, 10);
19
+ implies(result).is(principal);
20
+ });
21
+ });
22
+ axiom("Álgebra Booleana: Leis de De Morgan", () => {
23
+ const A = true;
24
+ const B = false;
25
+ proof("A negação da disjunção é a conjunção das negações", () => {
26
+ const lhs = !(A || B);
27
+ const rhs = !A && !B;
28
+ implies(lhs).is(rhs);
29
+ });
30
+ });
31
+ axiom("Análise de Funções Recursivas", () => {
32
+ const factorial = arbitrary();
33
+ // Configurando comportamento axiomático
34
+ factorial.derive((n) => (n <= 1 ? 1 : n * factorial(n - 1)));
35
+ proof("Fatorial de 5 converge para 120", () => {
36
+ const res = factorial(5);
37
+ implies(res).is(120);
38
+ });
39
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ import { NarrativeDialect } from "../src/index.js";
2
+ const { intend, detail, to, standIn, background, before } = NarrativeDialect;
3
+ intend("Jornada de Compra do Usuário", () => {
4
+ // Atores (Mocks)
5
+ const cart = standIn();
6
+ const paymentGateway = standIn();
7
+ const notificationService = standIn();
8
+ before(() => {
9
+ // Limpa estado para cada cena
10
+ cart.clear();
11
+ paymentGateway.clear();
12
+ notificationService.clear();
13
+ });
14
+ background(() => {
15
+ // Contexto: Carrinho tem itens e gateway está online
16
+ cart.getTotal.respondsWith(150.0); // Especifica o método
17
+ paymentGateway.process.respondsWith(true); // Especifica o método
18
+ });
19
+ detail("O cliente finaliza a compra com sucesso", () => {
20
+ // Ação: Usuário clica em 'Checkout'
21
+ const total = cart.getTotal();
22
+ const success = paymentGateway.process(total);
23
+ if (success) {
24
+ notificationService.sendEmail("Compra aprovada!");
25
+ }
26
+ // Expectativas narrativas
27
+ to(cart).wasCalled();
28
+ to(paymentGateway).uploaded(total); // Alias mental para 'received'
29
+ to(notificationService).received("Compra aprovada!");
30
+ });
31
+ detail("O sistema deve lidar com falha no pagamento", () => {
32
+ // Mudança de enredo: Gateway recusa
33
+ paymentGateway.process.respondsWith(false);
34
+ const success = paymentGateway.process(100);
35
+ to(success).be(false);
36
+ to(notificationService).not.wasCalled();
37
+ });
38
+ });
39
+ intend("Onboarding de Novos Membros", () => {
40
+ const database = standIn();
41
+ detail("Deve criar perfil padrão ao registrar", () => {
42
+ const newUser = { name: "Alice", email: "alice@wonder.land" };
43
+ // Simula o registro
44
+ database.save(newUser);
45
+ to(database).received(newUser);
46
+ });
47
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 🛒 Shopping Cart - Exemplo Poliglota
3
+ *
4
+ * Este arquivo demonstra como usar os 3 dialetos de forma semântica e coerente
5
+ * para testar diferentes camadas de uma mesma funcionalidade: um carrinho de compras.
6
+ *
7
+ * - 📐 Matemático: Testa a lógica PURA de cálculo (descontos, totais)
8
+ * - 📖 Narrativo: Testa a JORNADA do usuário (adicionar, remover, finalizar)
9
+ * - 🛡️ Imperativo: Testa a INTEGRAÇÃO com API (gateway de pagamento)
10
+ */
11
+ export {};
@@ -0,0 +1,161 @@
1
+ /**
2
+ * 🛒 Shopping Cart - Exemplo Poliglota
3
+ *
4
+ * Este arquivo demonstra como usar os 3 dialetos de forma semântica e coerente
5
+ * para testar diferentes camadas de uma mesma funcionalidade: um carrinho de compras.
6
+ *
7
+ * - 📐 Matemático: Testa a lógica PURA de cálculo (descontos, totais)
8
+ * - 📖 Narrativo: Testa a JORNADA do usuário (adicionar, remover, finalizar)
9
+ * - 🛡️ Imperativo: Testa a INTEGRAÇÃO com API (gateway de pagamento)
10
+ */
11
+ import {
12
+ // 📐 Matemático - para lógica pura
13
+ axiom, proof, implies, given,
14
+ // 📖 Narrativo - para jornada do usuário
15
+ intend, scenario, to, standIn, background,
16
+ // 🛡️ Imperativo - para integração de sistema
17
+ ensure, check, that, stub, initAll, reset, } from "../src/index.js";
18
+ // =============================================================================
19
+ // 📐 CAMADA MATEMÁTICA: Lógica Pura de Preços
20
+ // =============================================================================
21
+ // Aqui provamos verdades universais sobre cálculos financeiros.
22
+ // Não há dependências externas, não há estado. Apenas matemática.
23
+ axiom("Teoria de Cálculo de Preços", () => {
24
+ let calcDiscount;
25
+ let calcTotal;
26
+ let applyTax;
27
+ given(() => {
28
+ // Funções puras - determinísticas, sem side effects
29
+ calcDiscount = (price, percent) => price - price * (percent / 100);
30
+ calcTotal = (items) => items.reduce((sum, i) => sum + i.price * i.qty, 0);
31
+ applyTax = (subtotal, rate) => Math.round(subtotal * (1 + rate) * 100) / 100;
32
+ });
33
+ proof("Desconto de 10% em R$100 implica R$90", () => {
34
+ implies(calcDiscount(100, 10)).is(90);
35
+ });
36
+ proof("Desconto de 0% preserva o valor original", () => {
37
+ implies(calcDiscount(250, 0)).is(250);
38
+ });
39
+ proof("Desconto de 100% anula o preço", () => {
40
+ implies(calcDiscount(500, 100)).is(0);
41
+ });
42
+ proof("Total de carrinho vazio converge para zero", () => {
43
+ implies(calcTotal([])).is(0);
44
+ });
45
+ proof("Total de 3 itens a R$10 cada (qty=2) implica R$60", () => {
46
+ const items = [
47
+ { price: 10, qty: 2 },
48
+ { price: 10, qty: 2 },
49
+ { price: 10, qty: 2 },
50
+ ];
51
+ implies(calcTotal(items)).is(60);
52
+ });
53
+ proof("Imposto de 10% sobre R$100 resulta em R$110", () => {
54
+ implies(applyTax(100, 0.1)).is(110);
55
+ });
56
+ });
57
+ // =============================================================================
58
+ // 📖 CAMADA NARRATIVA: Jornada do Usuário
59
+ // =============================================================================
60
+ // Aqui contamos histórias que o PM pode ler e entender.
61
+ // Descrevemos a experiência do usuário em linguagem natural.
62
+ intend("Jornada de Compra do Usuário", () => {
63
+ const cart = standIn(); // Dublê do serviço de carrinho
64
+ const user = standIn(); // Dublê do usuário
65
+ background(() => {
66
+ // Preparação do cenário
67
+ cart.respondsWith({ items: [], total: 0 });
68
+ user.respondsWith({ name: "João", loggedIn: true });
69
+ });
70
+ scenario("Usuário adiciona primeiro produto ao carrinho", () => {
71
+ cart.add({ name: "Camiseta", price: 49.9 });
72
+ cart.respondsWith({
73
+ items: [{ name: "Camiseta", price: 49.9 }],
74
+ total: 49.9,
75
+ });
76
+ to(cart).wasCalled();
77
+ to(cart.items).have("length");
78
+ });
79
+ scenario("Usuário aplica cupom de desconto", () => {
80
+ const coupon = standIn();
81
+ coupon.respondsWith({ valid: true, discount: 15 });
82
+ // Link the mock logic for the purpose of this example
83
+ cart.applyCoupon.actsLike(() => coupon("DESCONTO15"));
84
+ cart.applyCoupon("DESCONTO15");
85
+ cart.respondsWith({ total: 42.42 }); // 49.9 - 15%
86
+ to(coupon).wasCalled();
87
+ to(cart.total).be(42.42);
88
+ });
89
+ scenario("Usuário remove item e carrinho fica vazio", () => {
90
+ cart.remove("Camiseta");
91
+ cart.respondsWith({ items: [], total: 0 });
92
+ to(cart.items).have("length");
93
+ to(cart.total).be(0);
94
+ });
95
+ scenario("Usuário não logado tenta finalizar compra", () => {
96
+ user.respondsWith({ loggedIn: false });
97
+ const checkout = standIn();
98
+ checkout.respondsWith({ error: "LOGIN_REQUIRED", status: 401 });
99
+ to(checkout.status).be(401);
100
+ });
101
+ });
102
+ // =============================================================================
103
+ // 🛡️ CAMADA IMPERATIVA: Integração com Gateway de Pagamento
104
+ // =============================================================================
105
+ // Aqui verificamos contratos rígidos com sistemas externos.
106
+ // A linguagem é autoritária: ensure, verify, check.
107
+ ensure("Conformidade com Gateway de Pagamento v2.1", () => {
108
+ const paymentGateway = stub();
109
+ const transactionLogger = stub();
110
+ initAll(() => {
111
+ // Setup de infraestrutura
112
+ paymentGateway.process.forceReturn({
113
+ status: 200,
114
+ transactionId: "tx_abc123",
115
+ timestamp: "2026-01-01T00:00:00Z",
116
+ });
117
+ transactionLogger.log.forceReturn(true);
118
+ });
119
+ reset(() => {
120
+ // Limpa estado entre testes para garantir contagens precisas
121
+ paymentGateway.clear();
122
+ transactionLogger.clear();
123
+ });
124
+ check("Transação bem-sucedida retorna status 200", () => {
125
+ const response = paymentGateway.process({
126
+ amount: 99.9,
127
+ currency: "BRL",
128
+ cardToken: "tok_visa_xxxx",
129
+ });
130
+ that(response.status).is(200);
131
+ that(response.transactionId).matches(/^tx_[a-z0-9]+$/);
132
+ });
133
+ check("TransactionId segue padrão RFC do contrato", () => {
134
+ const response = paymentGateway.process({ amount: 50 });
135
+ // Validação rigorosa do formato do ID
136
+ that(response.transactionId).matches(/^tx_[a-z0-9]{6,}$/);
137
+ that(response.timestamp).matches(/^\d{4}-\d{2}-\d{2}T/);
138
+ });
139
+ check("Toda transação deve ser logada para auditoria", () => {
140
+ paymentGateway.process({ amount: 100 });
141
+ transactionLogger.log({ event: "PAYMENT_PROCESSED" });
142
+ that(transactionLogger).triggered();
143
+ that(transactionLogger).calledWith({ event: "PAYMENT_PROCESSED" });
144
+ });
145
+ check("Gateway deve ser acionado exatamente uma vez por request", () => {
146
+ paymentGateway.process({ amount: 100 });
147
+ that(paymentGateway).triggeredCount(1);
148
+ });
149
+ });
150
+ // =============================================================================
151
+ // 🎭 RESUMO: Por que usar os 3 dialetos?
152
+ // =============================================================================
153
+ //
154
+ // | Camada | Dialeto | Por quê? |
155
+ // |--------------|-------------|----------------------------------------------|
156
+ // | Cálculos | Matemático | Provas de verdades universais, sem estado |
157
+ // | UX/Jornada | Narrativo | Documentação viva legível por PMs |
158
+ // | Integração | Imperativo | Contratos rígidos, conformidade, auditoria |
159
+ //
160
+ // Cada dialeto expressa a NATUREZA do problema que está resolvendo.
161
+ // A linguagem do teste espelha a linguagem do domínio.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ import { MathDialect, NarrativeDialect, ImperativeDialect, } from "../src/index.js";
2
+ console.log("---------------------------------------------------");
3
+ console.log("🚀 INICIANDO TEST SUITE POLIGLOTA TEMPLATE");
4
+ console.log("---------------------------------------------------");
5
+ // 1. Teste Matemático
6
+ const { axiom, proof, implies, arbitrary } = MathDialect;
7
+ axiom("Teoria dos Conjuntos (Dialeto Matemático)", () => {
8
+ const f = arbitrary();
9
+ f.yields(10); // Configuração usando nome matemático
10
+ proof("f deve mapear para 10", () => {
11
+ const res = f();
12
+ implies(res).is(10);
13
+ implies(f).wasEvaluated();
14
+ });
15
+ });
16
+ // 2. Teste Narrativo
17
+ const { intend, detail, to, standIn } = NarrativeDialect;
18
+ intend("Autenticação de Usuário (Dialeto Narrativo)", () => {
19
+ const login = standIn();
20
+ login.respondsWith(true); // Configuração narrativa
21
+ detail("login bem sucedido", () => {
22
+ login("user", "pass");
23
+ to(login).received("user", "pass");
24
+ to(login).wasCalled();
25
+ });
26
+ });
27
+ // 3. Teste Imperativo
28
+ const { ensure, check, that, stub } = ImperativeDialect;
29
+ ensure("Serviço de Pagamento (Dialeto Imperativo)", () => {
30
+ const api = stub();
31
+ api.forceReturn(200); // Configuração imperativa
32
+ check("status da API é 200", () => {
33
+ const status = api();
34
+ that(status).is(200);
35
+ that(api).triggeredCount(1);
36
+ });
37
+ });
38
+ console.log("\n---------------------------------------------------");
39
+ console.log("🏁 FIM DA EXECUÇÃO");
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,62 @@
1
+ import { ApiSpec } from "../packages/api-test-dialect/index.js";
2
+ /**
3
+ * 🌐 Teste de Showcase do Dialeto de API
4
+ * Mostrando todas as funcionalidades integradas:
5
+ * - GET, POST, PUT, DELETE
6
+ * - Headers customizados
7
+ * - Validação de Status Code
8
+ * - Validação de Schema (Nominal/Semântica)
9
+ */
10
+ async function runShowcase() {
11
+ const BASE_URL = "https://jsonplaceholder.typicode.com";
12
+ console.log("\n--- 🚀 INICIANDO SHOWCASE DE FUNCIONALIDADES API ---");
13
+ try {
14
+ // 1. Teste GET Simples
15
+ await ApiSpec.define("1. Buscar Post Único (GET)")
16
+ .from(BASE_URL)
17
+ .get("/posts/1")
18
+ .shouldReturn(200)
19
+ .matchingSchema({
20
+ id: 1,
21
+ userId: 1,
22
+ title: "string",
23
+ body: "string"
24
+ })
25
+ .run();
26
+ // 2. Teste POST com Body e Header
27
+ await ApiSpec.define("2. Criar Novo Recurso (POST)")
28
+ .from(BASE_URL)
29
+ .header("Content-Type", "application/json; charset=UTF-8")
30
+ .header("X-Custom-Header", "vibe2founderPower")
31
+ .post("/posts", {
32
+ title: "Showcase Test",
33
+ body: "Testando todas as features do dialeto",
34
+ userId: 1
35
+ })
36
+ .shouldReturn(201)
37
+ .run();
38
+ // 3. Teste PUT (Atualização)
39
+ await ApiSpec.define("3. Atualizar Recurso (PUT)")
40
+ .from(BASE_URL)
41
+ .put("/posts/1", {
42
+ id: 1,
43
+ title: "Título Atualizado",
44
+ body: "Conteúdo atualizado via PUT",
45
+ userId: 1
46
+ })
47
+ .shouldReturn(200)
48
+ .run();
49
+ // 4. Teste DELETE
50
+ await ApiSpec.define("4. Remover Recurso (DELETE)")
51
+ .from(BASE_URL)
52
+ .delete("/posts/1")
53
+ .shouldReturn(200)
54
+ .run();
55
+ console.log("\n--- ✅ SHOWCASE FINALIZADO COM SUCESSO ---");
56
+ }
57
+ catch (err) {
58
+ console.error("\n--- ❌ SHOWCASE FALHOU ---");
59
+ // O erro já é logado pelo dialeto no .run()
60
+ }
61
+ }
62
+ runShowcase();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,32 @@
1
+ import { ApiSpec } from "../packages/api-test-dialect/index.js";
2
+ // Simulating a test run against a public placeholder API (JSONPlaceholder)
3
+ async function runExample() {
4
+ try {
5
+ console.log("--- Starting API Dialect Test Example ---");
6
+ await ApiSpec.define("Get Single Post")
7
+ .from("https://jsonplaceholder.typicode.com")
8
+ .get("/posts/1")
9
+ .shouldReturn(200)
10
+ .matchingSchema({
11
+ id: 1,
12
+ userId: 1,
13
+ title: "string",
14
+ body: "string",
15
+ })
16
+ .run();
17
+ await ApiSpec.define("Create New Post")
18
+ .from("https://jsonplaceholder.typicode.com")
19
+ .post("/posts", {
20
+ title: "vibe2founder Test",
21
+ body: "Building dialects is fun",
22
+ userId: 1,
23
+ })
24
+ .shouldReturn(201)
25
+ .run();
26
+ console.log("--- All tests finished successfully ---");
27
+ }
28
+ catch (error) {
29
+ // Error is already logged by the dialect
30
+ }
31
+ }
32
+ runExample();
@@ -0,0 +1,28 @@
1
+ import { HttpMethod, HttpStatusCode, ApiUrl, TestName, SchemaDefinition } from "../../types/api-types.js";
2
+ import { request2httpResponse } from "../reqify/index.js";
3
+ export declare class ApiTestDialect {
4
+ private _name;
5
+ private _baseUrl;
6
+ private _method;
7
+ private _path;
8
+ private _body;
9
+ private _headers;
10
+ private _expectedStatus;
11
+ private _expectedSchema;
12
+ constructor(name: TestName);
13
+ static define(name: TestName): ApiTestDialect;
14
+ from(baseUrl: ApiUrl): this;
15
+ method(cmd: HttpMethod, path: string): this;
16
+ get(path: string): this;
17
+ post(path: string, body?: unknown): this;
18
+ put(path: string, body?: unknown): this;
19
+ delete(path: string): this;
20
+ withBody(body: unknown): this;
21
+ header(key: string, value: string): this;
22
+ shouldReturn(status: HttpStatusCode): this;
23
+ matchingSchema(schema: SchemaDefinition): this;
24
+ run(): Promise<request2httpResponse>;
25
+ private validate;
26
+ private validateSchema;
27
+ }
28
+ export declare const ApiSpec: typeof ApiTestDialect;
@@ -0,0 +1,102 @@
1
+ import { request2http } from "../reqify/index.js";
2
+ export class ApiTestDialect {
3
+ _name;
4
+ _baseUrl = "";
5
+ _method = "GET";
6
+ _path = "";
7
+ _body = null;
8
+ _headers = {};
9
+ _expectedStatus = null;
10
+ _expectedSchema = null;
11
+ constructor(name) {
12
+ this._name = name;
13
+ }
14
+ static define(name) {
15
+ return new ApiTestDialect(name);
16
+ }
17
+ from(baseUrl) {
18
+ this._baseUrl = baseUrl;
19
+ return this;
20
+ }
21
+ method(cmd, path) {
22
+ this._method = cmd;
23
+ this._path = path;
24
+ return this;
25
+ }
26
+ get(path) {
27
+ return this.method("GET", path);
28
+ }
29
+ post(path, body) {
30
+ this.method("POST", path);
31
+ if (body)
32
+ this.withBody(body);
33
+ return this;
34
+ }
35
+ put(path, body) {
36
+ this.method("PUT", path);
37
+ if (body)
38
+ this.withBody(body);
39
+ return this;
40
+ }
41
+ delete(path) {
42
+ return this.method("DELETE", path);
43
+ }
44
+ withBody(body) {
45
+ this._body = body;
46
+ return this;
47
+ }
48
+ header(key, value) {
49
+ this._headers[key] = value;
50
+ return this;
51
+ }
52
+ shouldReturn(status) {
53
+ this._expectedStatus = status;
54
+ return this;
55
+ }
56
+ matchingSchema(schema) {
57
+ this._expectedSchema = schema;
58
+ return this;
59
+ }
60
+ async run() {
61
+ const url = `${this._baseUrl}${this._path}`;
62
+ console.log(`🚀 [Test: ${this._name}] Running ${this._method} ${url}...`);
63
+ try {
64
+ const response = await request2http(url, {
65
+ method: this._method,
66
+ headers: this._headers,
67
+ body: this._body,
68
+ });
69
+ this.validate(response);
70
+ console.log(`✅ [Test: ${this._name}] Passed!`);
71
+ return response;
72
+ }
73
+ catch (error) {
74
+ console.error(`❌ [Test: ${this._name}] Failed: ${error.message}`);
75
+ throw error;
76
+ }
77
+ }
78
+ validate(response) {
79
+ if (this._expectedStatus !== null &&
80
+ response.status !== this._expectedStatus) {
81
+ throw new Error(`Expected status ${this._expectedStatus} but got ${response.status}`);
82
+ }
83
+ if (this._expectedSchema !== null) {
84
+ // Basic schema validation logic implemented locally
85
+ this.validateSchema(response.data, this._expectedSchema);
86
+ }
87
+ }
88
+ validateSchema(data, schema) {
89
+ // Simple key-based validation for the dialect example
90
+ for (const key in schema) {
91
+ if (!(key in data)) {
92
+ throw new Error(`Schema validation failed: Missing key "${key}" in response`);
93
+ }
94
+ const expectedType = typeof schema[key];
95
+ const actualType = typeof data[key];
96
+ if (expectedType !== actualType) {
97
+ throw new Error(`Schema validation failed: Key "${key}" expected type ${expectedType} but got ${actualType}`);
98
+ }
99
+ }
100
+ }
101
+ }
102
+ export const ApiSpec = ApiTestDialect;
@@ -0,0 +1,12 @@
1
+ import { HttpMethod, HttpStatusCode, ApiUrl } from "../../types/api-types.js";
2
+ export interface request2httpOptions {
3
+ method?: HttpMethod;
4
+ headers?: Record<string, string>;
5
+ body?: unknown;
6
+ }
7
+ export interface request2httpResponse<T = any> {
8
+ status: HttpStatusCode;
9
+ data: T;
10
+ headers: Headers;
11
+ }
12
+ export declare const request2http: <T = any>(url: ApiUrl, options?: request2httpOptions) => Promise<request2httpResponse<T>>;
@@ -0,0 +1,24 @@
1
+ export const request2http = async (url, options = {}) => {
2
+ const { method = "GET", headers = {}, body } = options;
3
+ const response = await fetch(url, {
4
+ method,
5
+ headers: {
6
+ "Content-Type": "application/json",
7
+ ...headers,
8
+ },
9
+ body: body ? JSON.stringify(body) : undefined,
10
+ });
11
+ let data;
12
+ const contentType = response.headers.get("content-type");
13
+ if (contentType && contentType.includes("application/json")) {
14
+ data = await response.json();
15
+ }
16
+ else {
17
+ data = await response.text();
18
+ }
19
+ return {
20
+ status: response.status,
21
+ data: data,
22
+ headers: response.headers,
23
+ };
24
+ };
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * 🧪 tests2dialects Test Runner
4
+ * Visual estilo Vitest com cronômetro e cores
5
+ */
6
+ export {};