opencroc 0.1.6
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/LICENSE +21 -0
- package/README.md +282 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +170 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +253 -0
- package/dist/index.js +123 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 opencroc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/assets/logo-placeholder.png" alt="OpenCroc" width="200" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">OpenCroc</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>AI-native E2E testing framework that reads your source code, generates tests, and self-heals failures.</strong>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/opencroc"><img src="https://img.shields.io/npm/v/opencroc?color=green" alt="npm version" /></a>
|
|
13
|
+
<a href="https://github.com/opencroc/opencroc/blob/main/LICENSE"><img src="https://img.shields.io/github/license/opencroc/opencroc" alt="MIT License" /></a>
|
|
14
|
+
<a href="https://opencroc.com"><img src="https://img.shields.io/badge/docs-opencroc.com-blue" alt="Documentation" /></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What is OpenCroc?
|
|
20
|
+
|
|
21
|
+
OpenCroc is an **AI-native end-to-end testing framework** built on top of [Playwright](https://playwright.dev). Instead of writing test scripts by hand, OpenCroc **reads your backend source code** (models, controllers, DTOs) and automatically generates complete E2E test suites — including API chains, seed data, request bodies, and assertions.
|
|
22
|
+
|
|
23
|
+
When tests fail, OpenCroc doesn't just report errors — it **traces the root cause** through the full request chain, **generates fix patches**, and **re-runs tests to verify the fix** — all autonomously.
|
|
24
|
+
|
|
25
|
+
### Key Capabilities
|
|
26
|
+
|
|
27
|
+
| Capability | Description |
|
|
28
|
+
|---|---|
|
|
29
|
+
| **Source-Aware Test Generation** | Parses Sequelize/TypeORM models, Express/NestJS controllers, and DTO decorators via [ts-morph](https://ts-morph.com) to understand your API surface |
|
|
30
|
+
| **AI-Driven Configuration** | LLM generates request body templates, seed data, parameter mappings — validated by 3-layer verification (schema → semantic → dry-run) |
|
|
31
|
+
| **Intelligent Chain Planning** | Builds API dependency DAGs, performs topological sorting, and plans test chains with greedy coverage optimization |
|
|
32
|
+
| **Log-Driven Completion Detection** | Goes beyond `networkidle` — verifies request completion by matching backend execution logs (`api_exec end`) |
|
|
33
|
+
| **Failure Chain Attribution** | Traces failures through the full call chain: network errors → slow APIs → backend logs → root cause |
|
|
34
|
+
| **Controlled Self-Healing** | `backup → AI patch → dry-run → apply → re-run → verify → rollback` — with safety gates at every step |
|
|
35
|
+
| **Impact Analysis** | BFS traversal of foreign key relations to map blast radius, auto-generates Mermaid diagrams |
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
### Prerequisites
|
|
40
|
+
|
|
41
|
+
- Node.js >= 18
|
|
42
|
+
- A backend project with Express/NestJS + Sequelize/TypeORM
|
|
43
|
+
|
|
44
|
+
### Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install opencroc --save-dev
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Initialize
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx opencroc init
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This will:
|
|
57
|
+
1. Scan your project structure
|
|
58
|
+
2. Detect your ORM and framework
|
|
59
|
+
3. Create `opencroc.config.ts` with sensible defaults
|
|
60
|
+
4. Generate a sample test suite
|
|
61
|
+
|
|
62
|
+
### Generate Tests
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Generate tests for a single module
|
|
66
|
+
npx opencroc generate --module=knowledge-base
|
|
67
|
+
|
|
68
|
+
# Generate tests for all detected modules
|
|
69
|
+
npx opencroc generate --all
|
|
70
|
+
|
|
71
|
+
# Dry-run (preview without writing files)
|
|
72
|
+
npx opencroc generate --all --dry-run
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Run Tests
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Run all generated tests
|
|
79
|
+
npx opencroc test
|
|
80
|
+
|
|
81
|
+
# Run specific module
|
|
82
|
+
npx opencroc test --module=knowledge-base
|
|
83
|
+
|
|
84
|
+
# Run with self-healing enabled
|
|
85
|
+
npx opencroc test --self-heal
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Validate AI Configs
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Validate generated configurations
|
|
92
|
+
npx opencroc validate --all
|
|
93
|
+
|
|
94
|
+
# Compare AI-generated vs baseline results
|
|
95
|
+
npx opencroc compare --baseline=report-a.json --current=report-b.json
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Architecture
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
102
|
+
│ CLI / Orchestrator │
|
|
103
|
+
├──────────┬──────────┬──────────┬──────────┬─────────────────┤
|
|
104
|
+
│ Source │ Chain │ Test │ Execution│ Self-Healing │
|
|
105
|
+
│ Parser │ Planner │Generator │ Engine │ Engine │
|
|
106
|
+
│ │ │ │ │ │
|
|
107
|
+
│ ts-morph │ DAG + │ Template │Playwright│ AI Attribution │
|
|
108
|
+
│ Model │ Topo │ + AI │ + Log │ + Controlled │
|
|
109
|
+
│ Controller│ Sort + │ Config │ Driven │ Fix + Verify │
|
|
110
|
+
│ DTO │ Greedy │ Merge │ Assert │ + Rollback │
|
|
111
|
+
├──────────┴──────────┴──────────┴──────────┴─────────────────┤
|
|
112
|
+
│ Observation Bus (Network + Backend Logs) │
|
|
113
|
+
├──────────────────────────────────────────────────────────────┤
|
|
114
|
+
│ Report Engine (HTML / JSON / Markdown) │
|
|
115
|
+
└──────────────────────────────────────────────────────────────┘
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 6-Stage Pipeline
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
Source Scan → ER Diagram → API Analysis → Chain Planning → Test Generation → Failure Analysis
|
|
122
|
+
│ │ │ │ │ │
|
|
123
|
+
ts-morph Mermaid Dependency Topological Playwright + Root Cause +
|
|
124
|
+
parsing erDiagram DAG builder + greedy AI body/seed Impact map
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## How It Works
|
|
128
|
+
|
|
129
|
+
### 1. Source Parsing
|
|
130
|
+
|
|
131
|
+
OpenCroc uses [ts-morph](https://ts-morph.com) to statically analyze your backend:
|
|
132
|
+
|
|
133
|
+
- **Models**: Extracts table names, column types, indexes, and foreign keys from Sequelize `Model.init()` / TypeORM `@Entity()`
|
|
134
|
+
- **Controllers**: Extracts routes, HTTP methods, path parameters from Express `router.get/post/put/delete`
|
|
135
|
+
- **DTOs**: Extracts validation rules from `@IsString()`, `@IsNumber()`, `@IsOptional()` decorators
|
|
136
|
+
|
|
137
|
+
### 2. AI Configuration Generation
|
|
138
|
+
|
|
139
|
+
For each module, OpenCroc calls an LLM (OpenAI / ZhiPu / any OpenAI-compatible API) to generate:
|
|
140
|
+
|
|
141
|
+
- **Request body templates** — field-accurate POST/PUT payloads
|
|
142
|
+
- **Seed data** — `beforeAll` setup steps with correct API sequences
|
|
143
|
+
- **Parameter mappings** — path parameter aliases (`/:id` → `categoryId`)
|
|
144
|
+
- **ID aliases** — preventing ID conflicts across multi-resource chains
|
|
145
|
+
|
|
146
|
+
Each config is validated through **3 layers**:
|
|
147
|
+
1. **Schema validation** — JSON structure completeness
|
|
148
|
+
2. **Semantic validation** — field values match source code metadata
|
|
149
|
+
3. **Dry-run validation** — TypeScript compilation check
|
|
150
|
+
|
|
151
|
+
Failed configs are **automatically fixed** (up to 3 rounds) before being written.
|
|
152
|
+
|
|
153
|
+
### 3. Log-Driven Completion
|
|
154
|
+
|
|
155
|
+
Instead of relying on fragile `networkidle` signals:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
Frontend Request → Backend api_exec start log → Backend processing → api_exec end log
|
|
159
|
+
↓
|
|
160
|
+
OpenCroc polls end logs to confirm completion
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
This catches cases where the frontend appears idle but the backend is still processing.
|
|
164
|
+
|
|
165
|
+
### 4. Self-Healing Loop
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
Test Failure
|
|
169
|
+
→ AI Attribution (LLM + heuristic fallback)
|
|
170
|
+
→ Generate Fix Patch
|
|
171
|
+
→ Dry-Run Validation
|
|
172
|
+
→ Apply Patch (with backup)
|
|
173
|
+
→ Re-run Failed Tests
|
|
174
|
+
→ Verify Fix
|
|
175
|
+
→ Commit or Rollback
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Configuration
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// opencroc.config.ts
|
|
182
|
+
import { defineConfig } from 'opencroc';
|
|
183
|
+
|
|
184
|
+
export default defineConfig({
|
|
185
|
+
// Backend source paths
|
|
186
|
+
backend: {
|
|
187
|
+
modelsDir: 'src/models',
|
|
188
|
+
controllersDir: 'src/controllers',
|
|
189
|
+
servicesDir: 'src/services',
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
// Target application
|
|
193
|
+
baseUrl: 'http://localhost:3000',
|
|
194
|
+
apiBaseUrl: 'http://localhost:3000/api',
|
|
195
|
+
|
|
196
|
+
// AI configuration
|
|
197
|
+
ai: {
|
|
198
|
+
provider: 'openai', // 'openai' | 'zhipu' | 'custom'
|
|
199
|
+
apiKey: process.env.AI_API_KEY,
|
|
200
|
+
model: 'gpt-4o-mini',
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
// Test execution
|
|
204
|
+
execution: {
|
|
205
|
+
workers: 4,
|
|
206
|
+
timeout: 30_000,
|
|
207
|
+
retries: 1,
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
// Log-driven completion (requires backend instrumentation)
|
|
211
|
+
logCompletion: {
|
|
212
|
+
enabled: true,
|
|
213
|
+
endpoint: '/internal/test-logs',
|
|
214
|
+
pollIntervalMs: 500,
|
|
215
|
+
timeoutMs: 10_000,
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
// Self-healing
|
|
219
|
+
selfHealing: {
|
|
220
|
+
enabled: false,
|
|
221
|
+
fixScope: 'config-only', // 'config-only' | 'config-and-source'
|
|
222
|
+
maxFixRounds: 3,
|
|
223
|
+
dryRunFirst: true,
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Supported Tech Stacks
|
|
229
|
+
|
|
230
|
+
| Layer | Supported | Planned |
|
|
231
|
+
|---|---|---|
|
|
232
|
+
| **ORM** | Sequelize | TypeORM, Prisma, Drizzle |
|
|
233
|
+
| **Framework** | Express | NestJS, Fastify, Koa |
|
|
234
|
+
| **Test Runner** | Playwright | — |
|
|
235
|
+
| **LLM** | OpenAI, ZhiPu (GLM) | Anthropic, Ollama (local) |
|
|
236
|
+
| **Database** | MySQL, PostgreSQL | SQLite, MongoDB |
|
|
237
|
+
|
|
238
|
+
## Comparison
|
|
239
|
+
|
|
240
|
+
| Feature | OpenCroc | Playwright | Metersphere | auto-playwright |
|
|
241
|
+
|---|---|---|---|---|
|
|
242
|
+
| Source-aware generation | ✅ | ❌ | ❌ | ❌ |
|
|
243
|
+
| AI config generation + validation | ✅ | ❌ | ❌ | ❌ |
|
|
244
|
+
| Log-driven completion | ✅ | ❌ | ❌ | ❌ |
|
|
245
|
+
| Failure chain attribution | ✅ | ❌ | Partial | ❌ |
|
|
246
|
+
| Self-healing with rollback | ✅ | ❌ | ❌ | ❌ |
|
|
247
|
+
| API dependency DAG | ✅ | ❌ | ❌ | ❌ |
|
|
248
|
+
| Zero-config test generation | ✅ | Codegen only | Manual | NL→action |
|
|
249
|
+
| Impact blast radius analysis | ✅ | ❌ | ❌ | ❌ |
|
|
250
|
+
|
|
251
|
+
## Roadmap
|
|
252
|
+
|
|
253
|
+
- [x] 6-stage source-to-test pipeline
|
|
254
|
+
- [x] AI configuration generation with 3-layer validation
|
|
255
|
+
- [x] Controlled self-healing loop
|
|
256
|
+
- [x] Log-driven completion detection
|
|
257
|
+
- [x] Failure chain attribution + impact analysis
|
|
258
|
+
- [ ] TypeORM / Prisma adapter
|
|
259
|
+
- [ ] NestJS controller parser
|
|
260
|
+
- [ ] Visual dashboard (opencroc.com)
|
|
261
|
+
- [ ] GitHub Actions integration
|
|
262
|
+
- [ ] VS Code extension
|
|
263
|
+
- [ ] Ollama local LLM support
|
|
264
|
+
|
|
265
|
+
## Documentation
|
|
266
|
+
|
|
267
|
+
Visit **[opencroc.com](https://opencroc.com)** for full documentation, or browse:
|
|
268
|
+
|
|
269
|
+
- [Architecture Guide](docs/architecture.md)
|
|
270
|
+
- [Configuration Reference](docs/configuration.md)
|
|
271
|
+
- [Backend Instrumentation Guide](docs/backend-instrumentation.md)
|
|
272
|
+
- [AI Provider Setup](docs/ai-providers.md)
|
|
273
|
+
- [Self-Healing Guide](docs/self-healing.md)
|
|
274
|
+
- [Troubleshooting](docs/troubleshooting.md)
|
|
275
|
+
|
|
276
|
+
## Contributing
|
|
277
|
+
|
|
278
|
+
We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
[MIT](LICENSE) © 2026 OpenCroc Contributors
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// node_modules/tsup/assets/esm_shims.js
|
|
13
|
+
import path from "path";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
var init_esm_shims = __esm({
|
|
16
|
+
"node_modules/tsup/assets/esm_shims.js"() {
|
|
17
|
+
"use strict";
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// src/cli/commands/init.ts
|
|
22
|
+
var init_exports = {};
|
|
23
|
+
__export(init_exports, {
|
|
24
|
+
initProject: () => initProject
|
|
25
|
+
});
|
|
26
|
+
import chalk from "chalk";
|
|
27
|
+
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
28
|
+
import { join } from "path";
|
|
29
|
+
async function initProject() {
|
|
30
|
+
const cwd = process.cwd();
|
|
31
|
+
const configPath = join(cwd, "opencroc.config.ts");
|
|
32
|
+
if (existsSync(configPath)) {
|
|
33
|
+
console.log(chalk.yellow("opencroc.config.ts already exists. Skipping."));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
writeFileSync(configPath, CONFIG_TEMPLATE, "utf-8");
|
|
37
|
+
console.log(chalk.green("\u2713 Created opencroc.config.ts"));
|
|
38
|
+
const outDir = join(cwd, "opencroc-output");
|
|
39
|
+
if (!existsSync(outDir)) {
|
|
40
|
+
mkdirSync(outDir, { recursive: true });
|
|
41
|
+
console.log(chalk.green("\u2713 Created opencroc-output/"));
|
|
42
|
+
}
|
|
43
|
+
console.log("");
|
|
44
|
+
console.log(chalk.cyan("Next steps:"));
|
|
45
|
+
console.log(" 1. Edit opencroc.config.ts with your project settings");
|
|
46
|
+
console.log(" 2. Run: npx opencroc generate --all");
|
|
47
|
+
console.log(" 3. Run: npx opencroc test");
|
|
48
|
+
}
|
|
49
|
+
var CONFIG_TEMPLATE;
|
|
50
|
+
var init_init = __esm({
|
|
51
|
+
"src/cli/commands/init.ts"() {
|
|
52
|
+
"use strict";
|
|
53
|
+
init_esm_shims();
|
|
54
|
+
CONFIG_TEMPLATE = `import { defineConfig } from 'opencroc';
|
|
55
|
+
|
|
56
|
+
export default defineConfig({
|
|
57
|
+
backendRoot: './backend',
|
|
58
|
+
adapter: 'sequelize',
|
|
59
|
+
llm: {
|
|
60
|
+
provider: 'openai',
|
|
61
|
+
model: 'gpt-4o-mini',
|
|
62
|
+
},
|
|
63
|
+
selfHealing: {
|
|
64
|
+
enabled: true,
|
|
65
|
+
maxIterations: 3,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
`;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// src/cli/commands/generate.ts
|
|
73
|
+
var generate_exports = {};
|
|
74
|
+
__export(generate_exports, {
|
|
75
|
+
generate: () => generate
|
|
76
|
+
});
|
|
77
|
+
import chalk2 from "chalk";
|
|
78
|
+
async function generate(opts) {
|
|
79
|
+
console.log(chalk2.cyan("\u{1F40A} OpenCroc \u2014 Generating E2E tests...\n"));
|
|
80
|
+
console.log(chalk2.yellow("Generation pipeline is under development."));
|
|
81
|
+
console.log("Options:", opts);
|
|
82
|
+
}
|
|
83
|
+
var init_generate = __esm({
|
|
84
|
+
"src/cli/commands/generate.ts"() {
|
|
85
|
+
"use strict";
|
|
86
|
+
init_esm_shims();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// src/cli/commands/test.ts
|
|
91
|
+
var test_exports = {};
|
|
92
|
+
__export(test_exports, {
|
|
93
|
+
runTests: () => runTests
|
|
94
|
+
});
|
|
95
|
+
import chalk3 from "chalk";
|
|
96
|
+
async function runTests(opts) {
|
|
97
|
+
console.log(chalk3.cyan("\u{1F40A} OpenCroc \u2014 Running E2E tests...\n"));
|
|
98
|
+
console.log(chalk3.yellow("Test runner is under development."));
|
|
99
|
+
console.log("Options:", opts);
|
|
100
|
+
}
|
|
101
|
+
var init_test = __esm({
|
|
102
|
+
"src/cli/commands/test.ts"() {
|
|
103
|
+
"use strict";
|
|
104
|
+
init_esm_shims();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// src/cli/commands/validate.ts
|
|
109
|
+
var validate_exports = {};
|
|
110
|
+
__export(validate_exports, {
|
|
111
|
+
validate: () => validate
|
|
112
|
+
});
|
|
113
|
+
import chalk4 from "chalk";
|
|
114
|
+
async function validate(opts) {
|
|
115
|
+
console.log(chalk4.cyan("\u{1F40A} OpenCroc \u2014 Validating configurations...\n"));
|
|
116
|
+
console.log(chalk4.yellow("Validation pipeline is under development."));
|
|
117
|
+
console.log("Options:", opts);
|
|
118
|
+
}
|
|
119
|
+
var init_validate = __esm({
|
|
120
|
+
"src/cli/commands/validate.ts"() {
|
|
121
|
+
"use strict";
|
|
122
|
+
init_esm_shims();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// src/cli/commands/heal.ts
|
|
127
|
+
var heal_exports = {};
|
|
128
|
+
__export(heal_exports, {
|
|
129
|
+
heal: () => heal
|
|
130
|
+
});
|
|
131
|
+
import chalk5 from "chalk";
|
|
132
|
+
async function heal(opts) {
|
|
133
|
+
console.log(chalk5.cyan("\u{1F40A} OpenCroc \u2014 Running self-healing loop...\n"));
|
|
134
|
+
console.log(chalk5.yellow("Self-healing loop is under development."));
|
|
135
|
+
console.log("Options:", opts);
|
|
136
|
+
}
|
|
137
|
+
var init_heal = __esm({
|
|
138
|
+
"src/cli/commands/heal.ts"() {
|
|
139
|
+
"use strict";
|
|
140
|
+
init_esm_shims();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// src/cli/index.ts
|
|
145
|
+
init_esm_shims();
|
|
146
|
+
import { Command } from "commander";
|
|
147
|
+
var program = new Command();
|
|
148
|
+
program.name("opencroc").description("AI-native E2E testing framework").version("0.1.0");
|
|
149
|
+
program.command("init").description("Initialize OpenCroc in the current project").action(async () => {
|
|
150
|
+
const { initProject: initProject2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
151
|
+
await initProject2();
|
|
152
|
+
});
|
|
153
|
+
program.command("generate").description("Generate E2E test cases from source code").option("-m, --module <name>", "Generate for a specific module").option("-a, --all", "Generate for all discovered modules").option("--steps <steps>", "Run specific pipeline steps (comma-separated)").option("--dry-run", "Preview without writing files").action(async (opts) => {
|
|
154
|
+
const { generate: generate2 } = await Promise.resolve().then(() => (init_generate(), generate_exports));
|
|
155
|
+
await generate2(opts);
|
|
156
|
+
});
|
|
157
|
+
program.command("test").description("Run generated E2E tests").option("-m, --module <name>", "Run tests for a specific module").option("--headed", "Run in headed browser mode").action(async (opts) => {
|
|
158
|
+
const { runTests: runTests2 } = await Promise.resolve().then(() => (init_test(), test_exports));
|
|
159
|
+
await runTests2(opts);
|
|
160
|
+
});
|
|
161
|
+
program.command("validate").description("Validate module configurations and generated tests").option("-m, --module <name>", "Validate a specific module").action(async (opts) => {
|
|
162
|
+
const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), validate_exports));
|
|
163
|
+
await validate2(opts);
|
|
164
|
+
});
|
|
165
|
+
program.command("heal").description("Run self-healing loop on failed tests").option("-m, --module <name>", "Heal a specific module").option("--max-iterations <n>", "Maximum healing iterations", "3").action(async (opts) => {
|
|
166
|
+
const { heal: heal2 } = await Promise.resolve().then(() => (init_heal(), heal_exports));
|
|
167
|
+
await heal2(opts);
|
|
168
|
+
});
|
|
169
|
+
program.parse();
|
|
170
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../node_modules/tsup/assets/esm_shims.js","../../src/cli/commands/init.ts","../../src/cli/commands/generate.ts","../../src/cli/commands/test.ts","../../src/cli/commands/validate.ts","../../src/cli/commands/heal.ts","../../src/cli/index.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import chalk from 'chalk';\nimport { writeFileSync, mkdirSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nconst CONFIG_TEMPLATE = `import { defineConfig } from 'opencroc';\n\nexport default defineConfig({\n backendRoot: './backend',\n adapter: 'sequelize',\n llm: {\n provider: 'openai',\n model: 'gpt-4o-mini',\n },\n selfHealing: {\n enabled: true,\n maxIterations: 3,\n },\n});\n`;\n\nexport async function initProject(): Promise<void> {\n const cwd = process.cwd();\n const configPath = join(cwd, 'opencroc.config.ts');\n\n if (existsSync(configPath)) {\n console.log(chalk.yellow('opencroc.config.ts already exists. Skipping.'));\n return;\n }\n\n writeFileSync(configPath, CONFIG_TEMPLATE, 'utf-8');\n console.log(chalk.green('✓ Created opencroc.config.ts'));\n\n const outDir = join(cwd, 'opencroc-output');\n if (!existsSync(outDir)) {\n mkdirSync(outDir, { recursive: true });\n console.log(chalk.green('✓ Created opencroc-output/'));\n }\n\n console.log('');\n console.log(chalk.cyan('Next steps:'));\n console.log(' 1. Edit opencroc.config.ts with your project settings');\n console.log(' 2. Run: npx opencroc generate --all');\n console.log(' 3. Run: npx opencroc test');\n}\n","import chalk from 'chalk';\n\ninterface GenerateOptions {\n module?: string;\n all?: boolean;\n steps?: string;\n dryRun?: boolean;\n}\n\nexport async function generate(opts: GenerateOptions): Promise<void> {\n console.log(chalk.cyan('🐊 OpenCroc — Generating E2E tests...\\n'));\n\n // TODO: Load config, create pipeline, and run generation\n console.log(chalk.yellow('Generation pipeline is under development.'));\n console.log('Options:', opts);\n}\n","import chalk from 'chalk';\n\ninterface TestOptions {\n module?: string;\n headed?: boolean;\n}\n\nexport async function runTests(opts: TestOptions): Promise<void> {\n console.log(chalk.cyan('🐊 OpenCroc — Running E2E tests...\\n'));\n\n // TODO: Load config, discover generated tests, and run with Playwright\n console.log(chalk.yellow('Test runner is under development.'));\n console.log('Options:', opts);\n}\n","import chalk from 'chalk';\n\ninterface ValidateOptions {\n module?: string;\n}\n\nexport async function validate(opts: ValidateOptions): Promise<void> {\n console.log(chalk.cyan('🐊 OpenCroc — Validating configurations...\\n'));\n\n // TODO: Load config, validate module configs, and report results\n console.log(chalk.yellow('Validation pipeline is under development.'));\n console.log('Options:', opts);\n}\n","import chalk from 'chalk';\n\ninterface HealOptions {\n module?: string;\n maxIterations?: string;\n}\n\nexport async function heal(opts: HealOptions): Promise<void> {\n console.log(chalk.cyan('🐊 OpenCroc — Running self-healing loop...\\n'));\n\n // TODO: Load config, detect failures, and run controlled fix loop\n console.log(chalk.yellow('Self-healing loop is under development.'));\n console.log('Options:', opts);\n}\n","#!/usr/bin/env node\n\nimport { Command } from 'commander';\n\nconst program = new Command();\n\nprogram\n .name('opencroc')\n .description('AI-native E2E testing framework')\n .version('0.1.0');\n\nprogram\n .command('init')\n .description('Initialize OpenCroc in the current project')\n .action(async () => {\n const { initProject } = await import('./commands/init.js');\n await initProject();\n });\n\nprogram\n .command('generate')\n .description('Generate E2E test cases from source code')\n .option('-m, --module <name>', 'Generate for a specific module')\n .option('-a, --all', 'Generate for all discovered modules')\n .option('--steps <steps>', 'Run specific pipeline steps (comma-separated)')\n .option('--dry-run', 'Preview without writing files')\n .action(async (opts) => {\n const { generate } = await import('./commands/generate.js');\n await generate(opts);\n });\n\nprogram\n .command('test')\n .description('Run generated E2E tests')\n .option('-m, --module <name>', 'Run tests for a specific module')\n .option('--headed', 'Run in headed browser mode')\n .action(async (opts) => {\n const { runTests } = await import('./commands/test.js');\n await runTests(opts);\n });\n\nprogram\n .command('validate')\n .description('Validate module configurations and generated tests')\n .option('-m, --module <name>', 'Validate a specific module')\n .action(async (opts) => {\n const { validate } = await import('./commands/validate.js');\n await validate(opts);\n });\n\nprogram\n .command('heal')\n .description('Run self-healing loop on failed tests')\n .option('-m, --module <name>', 'Heal a specific module')\n .option('--max-iterations <n>', 'Maximum healing iterations', '3')\n .action(async (opts) => {\n const { heal } = await import('./commands/heal.js');\n await heal(opts);\n });\n\nprogram.parse();\n"],"mappings":"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","names":["chalk","chalk","chalk","chalk","initProject","generate","runTests","validate","heal"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
interface OpenCrocConfig {
|
|
2
|
+
/** Path to the backend source code root */
|
|
3
|
+
backendRoot: string;
|
|
4
|
+
/** Test output directory (default: ./opencroc-output) */
|
|
5
|
+
outDir?: string;
|
|
6
|
+
/** Backend adapter: 'sequelize' | 'typeorm' | 'prisma' | custom BackendAdapter */
|
|
7
|
+
adapter?: string | BackendAdapter;
|
|
8
|
+
/** LLM provider configuration */
|
|
9
|
+
llm?: LlmConfig;
|
|
10
|
+
/** Playwright configuration overrides */
|
|
11
|
+
playwright?: PlaywrightOverrides;
|
|
12
|
+
/** Module filter — only process these modules */
|
|
13
|
+
modules?: string[];
|
|
14
|
+
/** Pipeline step selection */
|
|
15
|
+
steps?: PipelineStep[];
|
|
16
|
+
/** Self-healing configuration */
|
|
17
|
+
selfHealing?: SelfHealingConfig;
|
|
18
|
+
/** Report configuration */
|
|
19
|
+
report?: ReportConfig;
|
|
20
|
+
}
|
|
21
|
+
interface ResolvedConfig extends Required<OpenCrocConfig> {
|
|
22
|
+
_resolved: true;
|
|
23
|
+
}
|
|
24
|
+
interface LlmConfig {
|
|
25
|
+
provider: 'openai' | 'zhipu' | 'ollama' | 'custom';
|
|
26
|
+
apiKey?: string;
|
|
27
|
+
baseUrl?: string;
|
|
28
|
+
model?: string;
|
|
29
|
+
maxTokens?: number;
|
|
30
|
+
temperature?: number;
|
|
31
|
+
}
|
|
32
|
+
interface PlaywrightOverrides {
|
|
33
|
+
baseURL?: string;
|
|
34
|
+
headless?: boolean;
|
|
35
|
+
timeout?: number;
|
|
36
|
+
}
|
|
37
|
+
interface SelfHealingConfig {
|
|
38
|
+
enabled?: boolean;
|
|
39
|
+
maxIterations?: number;
|
|
40
|
+
mode?: 'config-only' | 'config-and-source';
|
|
41
|
+
}
|
|
42
|
+
interface ReportConfig {
|
|
43
|
+
format?: ('html' | 'json' | 'markdown')[];
|
|
44
|
+
outputDir?: string;
|
|
45
|
+
}
|
|
46
|
+
type PipelineStep = 'scan' | 'er-diagram' | 'api-chain' | 'plan' | 'codegen' | 'validate';
|
|
47
|
+
interface ModuleDefinition {
|
|
48
|
+
name: string;
|
|
49
|
+
label?: string;
|
|
50
|
+
modelDir: string;
|
|
51
|
+
controllerDir: string;
|
|
52
|
+
associationFile?: string;
|
|
53
|
+
}
|
|
54
|
+
interface RouteEntry {
|
|
55
|
+
method: string;
|
|
56
|
+
path: string;
|
|
57
|
+
handler: string;
|
|
58
|
+
controllerClass: string;
|
|
59
|
+
}
|
|
60
|
+
interface FieldSchema {
|
|
61
|
+
name: string;
|
|
62
|
+
type: string;
|
|
63
|
+
allowNull?: boolean;
|
|
64
|
+
defaultValue?: unknown;
|
|
65
|
+
primaryKey?: boolean;
|
|
66
|
+
unique?: boolean;
|
|
67
|
+
comment?: string;
|
|
68
|
+
}
|
|
69
|
+
interface TableSchema {
|
|
70
|
+
tableName: string;
|
|
71
|
+
className: string;
|
|
72
|
+
fields: FieldSchema[];
|
|
73
|
+
indexes?: IndexSchema[];
|
|
74
|
+
}
|
|
75
|
+
interface IndexSchema {
|
|
76
|
+
name?: string;
|
|
77
|
+
unique?: boolean;
|
|
78
|
+
fields: string[];
|
|
79
|
+
}
|
|
80
|
+
interface ForeignKeyRelation {
|
|
81
|
+
sourceTable: string;
|
|
82
|
+
targetTable: string;
|
|
83
|
+
sourceColumn: string;
|
|
84
|
+
targetColumn: string;
|
|
85
|
+
type: 'belongsTo' | 'hasMany' | 'hasOne' | 'belongsToMany';
|
|
86
|
+
}
|
|
87
|
+
interface ApiEndpoint {
|
|
88
|
+
method: string;
|
|
89
|
+
path: string;
|
|
90
|
+
handler: string;
|
|
91
|
+
module: string;
|
|
92
|
+
params?: string[];
|
|
93
|
+
bodyFields?: string[];
|
|
94
|
+
}
|
|
95
|
+
interface ApiDependency {
|
|
96
|
+
from: ApiEndpoint;
|
|
97
|
+
to: ApiEndpoint;
|
|
98
|
+
reason: string;
|
|
99
|
+
}
|
|
100
|
+
interface ERDiagramResult {
|
|
101
|
+
tables: TableSchema[];
|
|
102
|
+
relations: ForeignKeyRelation[];
|
|
103
|
+
mermaidText: string;
|
|
104
|
+
}
|
|
105
|
+
interface TestStep {
|
|
106
|
+
order: number;
|
|
107
|
+
action: string;
|
|
108
|
+
endpoint: ApiEndpoint;
|
|
109
|
+
description: string;
|
|
110
|
+
assertions: string[];
|
|
111
|
+
}
|
|
112
|
+
interface TestChain {
|
|
113
|
+
name: string;
|
|
114
|
+
module: string;
|
|
115
|
+
steps: TestStep[];
|
|
116
|
+
}
|
|
117
|
+
interface ChainPlanResult {
|
|
118
|
+
chains: TestChain[];
|
|
119
|
+
totalSteps: number;
|
|
120
|
+
}
|
|
121
|
+
interface GeneratedTestFile {
|
|
122
|
+
filePath: string;
|
|
123
|
+
content: string;
|
|
124
|
+
module: string;
|
|
125
|
+
chain: string;
|
|
126
|
+
}
|
|
127
|
+
interface PipelineRunResult {
|
|
128
|
+
modules: string[];
|
|
129
|
+
erDiagrams: Map<string, ERDiagramResult>;
|
|
130
|
+
chainPlans: Map<string, ChainPlanResult>;
|
|
131
|
+
generatedFiles: GeneratedTestFile[];
|
|
132
|
+
validationErrors: ValidationError[];
|
|
133
|
+
duration: number;
|
|
134
|
+
}
|
|
135
|
+
interface ChainFailureResult {
|
|
136
|
+
chain: string;
|
|
137
|
+
failedStep: number;
|
|
138
|
+
error: string;
|
|
139
|
+
rootCause?: string;
|
|
140
|
+
impactedChains: string[];
|
|
141
|
+
}
|
|
142
|
+
interface ImpactReport {
|
|
143
|
+
affectedModules: string[];
|
|
144
|
+
affectedChains: string[];
|
|
145
|
+
affectedEndpoints: ApiEndpoint[];
|
|
146
|
+
mermaidText: string;
|
|
147
|
+
}
|
|
148
|
+
interface ValidationError {
|
|
149
|
+
module: string;
|
|
150
|
+
field: string;
|
|
151
|
+
message: string;
|
|
152
|
+
severity: 'error' | 'warning';
|
|
153
|
+
}
|
|
154
|
+
interface BackendAdapter {
|
|
155
|
+
name: string;
|
|
156
|
+
parseModels(dir: string): Promise<TableSchema[]>;
|
|
157
|
+
parseAssociations(file: string): Promise<ForeignKeyRelation[]>;
|
|
158
|
+
parseControllers(dir: string): Promise<RouteEntry[]>;
|
|
159
|
+
}
|
|
160
|
+
interface LlmProvider {
|
|
161
|
+
name: string;
|
|
162
|
+
chat(messages: Array<{
|
|
163
|
+
role: string;
|
|
164
|
+
content: string;
|
|
165
|
+
}>): Promise<string>;
|
|
166
|
+
estimateTokens(text: string): number;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Define an OpenCroc configuration with type checking.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* // opencroc.config.ts
|
|
175
|
+
* import { defineConfig } from 'opencroc';
|
|
176
|
+
*
|
|
177
|
+
* export default defineConfig({
|
|
178
|
+
* backendRoot: './backend',
|
|
179
|
+
* adapter: 'sequelize',
|
|
180
|
+
* llm: {
|
|
181
|
+
* provider: 'openai',
|
|
182
|
+
* model: 'gpt-4o-mini',
|
|
183
|
+
* },
|
|
184
|
+
* });
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
declare function defineConfig(config: OpenCrocConfig): OpenCrocConfig;
|
|
188
|
+
|
|
189
|
+
interface Pipeline {
|
|
190
|
+
run(steps?: PipelineStep[]): Promise<PipelineRunResult>;
|
|
191
|
+
}
|
|
192
|
+
declare function createPipeline(_config: OpenCrocConfig): Pipeline;
|
|
193
|
+
|
|
194
|
+
interface ModelParser {
|
|
195
|
+
parseFile(filePath: string): Promise<TableSchema>;
|
|
196
|
+
parseDirectory(dirPath: string): Promise<TableSchema[]>;
|
|
197
|
+
}
|
|
198
|
+
declare function createModelParser(): ModelParser;
|
|
199
|
+
|
|
200
|
+
interface ControllerParser {
|
|
201
|
+
parseFile(filePath: string): Promise<RouteEntry[]>;
|
|
202
|
+
parseDirectory(dirPath: string): Promise<RouteEntry[]>;
|
|
203
|
+
}
|
|
204
|
+
declare function createControllerParser(): ControllerParser;
|
|
205
|
+
|
|
206
|
+
interface AssociationParser {
|
|
207
|
+
parseFile(filePath: string): Promise<ForeignKeyRelation[]>;
|
|
208
|
+
}
|
|
209
|
+
declare function createAssociationParser(): AssociationParser;
|
|
210
|
+
|
|
211
|
+
interface TestCodeGenerator {
|
|
212
|
+
generate(chains: TestChain[]): GeneratedTestFile[];
|
|
213
|
+
}
|
|
214
|
+
declare function createTestCodeGenerator(): TestCodeGenerator;
|
|
215
|
+
|
|
216
|
+
interface MockDataGenerator {
|
|
217
|
+
generateForTable(schema: TableSchema): Record<string, unknown>;
|
|
218
|
+
generateForTables(schemas: TableSchema[]): Map<string, Record<string, unknown>[]>;
|
|
219
|
+
}
|
|
220
|
+
declare function createMockDataGenerator(): MockDataGenerator;
|
|
221
|
+
|
|
222
|
+
interface ERDiagramGenerator {
|
|
223
|
+
generate(tables: TableSchema[], relations: ForeignKeyRelation[]): ERDiagramResult;
|
|
224
|
+
}
|
|
225
|
+
declare function createERDiagramGenerator(): ERDiagramGenerator;
|
|
226
|
+
|
|
227
|
+
interface ApiChainAnalyzer {
|
|
228
|
+
analyze(endpoints: ApiEndpoint[]): {
|
|
229
|
+
dependencies: ApiDependency[];
|
|
230
|
+
topologicalOrder: ApiEndpoint[];
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
declare function createApiChainAnalyzer(): ApiChainAnalyzer;
|
|
234
|
+
|
|
235
|
+
interface ImpactReporter {
|
|
236
|
+
analyze(failedEndpoints: string[]): Promise<ImpactReport>;
|
|
237
|
+
}
|
|
238
|
+
declare function createImpactReporter(): ImpactReporter;
|
|
239
|
+
|
|
240
|
+
declare function validateConfig(_config: Record<string, unknown>): ValidationError[];
|
|
241
|
+
|
|
242
|
+
interface SelfHealingLoop {
|
|
243
|
+
run(testResultsDir: string): Promise<SelfHealingResult>;
|
|
244
|
+
}
|
|
245
|
+
interface SelfHealingResult {
|
|
246
|
+
iterations: number;
|
|
247
|
+
fixed: string[];
|
|
248
|
+
remaining: string[];
|
|
249
|
+
totalTokensUsed: number;
|
|
250
|
+
}
|
|
251
|
+
declare function createSelfHealingLoop(_config: SelfHealingConfig): SelfHealingLoop;
|
|
252
|
+
|
|
253
|
+
export { type ApiDependency, type ApiEndpoint, type BackendAdapter, type ChainFailureResult, type ChainPlanResult, type ERDiagramResult, type FieldSchema, type ForeignKeyRelation, type GeneratedTestFile, type ImpactReport, type LlmProvider, type ModuleDefinition, type OpenCrocConfig, type PipelineRunResult, type ResolvedConfig, type RouteEntry, type TableSchema, type TestChain, type TestStep, createApiChainAnalyzer, createAssociationParser, createControllerParser, createERDiagramGenerator, createImpactReporter, createMockDataGenerator, createModelParser, createPipeline, createSelfHealingLoop, createTestCodeGenerator, defineConfig, validateConfig };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
function defineConfig(config) {
|
|
3
|
+
return config;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/pipeline/index.ts
|
|
7
|
+
function createPipeline(_config) {
|
|
8
|
+
return {
|
|
9
|
+
async run(_steps) {
|
|
10
|
+
throw new Error("Pipeline not yet implemented");
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/parsers/model-parser.ts
|
|
16
|
+
function createModelParser() {
|
|
17
|
+
return {
|
|
18
|
+
async parseFile(_filePath) {
|
|
19
|
+
throw new Error("Model parser not yet implemented");
|
|
20
|
+
},
|
|
21
|
+
async parseDirectory(_dirPath) {
|
|
22
|
+
throw new Error("Model parser not yet implemented");
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/parsers/controller-parser.ts
|
|
28
|
+
function createControllerParser() {
|
|
29
|
+
return {
|
|
30
|
+
async parseFile(_filePath) {
|
|
31
|
+
throw new Error("Controller parser not yet implemented");
|
|
32
|
+
},
|
|
33
|
+
async parseDirectory(_dirPath) {
|
|
34
|
+
throw new Error("Controller parser not yet implemented");
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/parsers/association-parser.ts
|
|
40
|
+
function createAssociationParser() {
|
|
41
|
+
return {
|
|
42
|
+
async parseFile(_filePath) {
|
|
43
|
+
throw new Error("Association parser not yet implemented");
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/generators/test-code-generator.ts
|
|
49
|
+
function createTestCodeGenerator() {
|
|
50
|
+
return {
|
|
51
|
+
generate(_chains) {
|
|
52
|
+
throw new Error("Test code generator not yet implemented");
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/generators/mock-data-generator.ts
|
|
58
|
+
function createMockDataGenerator() {
|
|
59
|
+
return {
|
|
60
|
+
generateForTable(_schema) {
|
|
61
|
+
throw new Error("Mock data generator not yet implemented");
|
|
62
|
+
},
|
|
63
|
+
generateForTables(_schemas) {
|
|
64
|
+
throw new Error("Mock data generator not yet implemented");
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/generators/er-diagram-generator.ts
|
|
70
|
+
function createERDiagramGenerator() {
|
|
71
|
+
return {
|
|
72
|
+
generate(_tables, _relations) {
|
|
73
|
+
throw new Error("ER diagram generator not yet implemented");
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/analyzers/api-chain-analyzer.ts
|
|
79
|
+
function createApiChainAnalyzer() {
|
|
80
|
+
return {
|
|
81
|
+
analyze(_endpoints) {
|
|
82
|
+
throw new Error("API chain analyzer not yet implemented");
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/analyzers/impact-reporter.ts
|
|
88
|
+
function createImpactReporter() {
|
|
89
|
+
return {
|
|
90
|
+
async analyze(_failedEndpoints) {
|
|
91
|
+
throw new Error("Impact reporter not yet implemented");
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/validators/config-validator.ts
|
|
97
|
+
function validateConfig(_config) {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/self-healing/index.ts
|
|
102
|
+
function createSelfHealingLoop(_config) {
|
|
103
|
+
return {
|
|
104
|
+
async run(_testResultsDir) {
|
|
105
|
+
throw new Error("Self-healing loop not yet implemented");
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
createApiChainAnalyzer,
|
|
111
|
+
createAssociationParser,
|
|
112
|
+
createControllerParser,
|
|
113
|
+
createERDiagramGenerator,
|
|
114
|
+
createImpactReporter,
|
|
115
|
+
createMockDataGenerator,
|
|
116
|
+
createModelParser,
|
|
117
|
+
createPipeline,
|
|
118
|
+
createSelfHealingLoop,
|
|
119
|
+
createTestCodeGenerator,
|
|
120
|
+
defineConfig,
|
|
121
|
+
validateConfig
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/pipeline/index.ts","../src/parsers/model-parser.ts","../src/parsers/controller-parser.ts","../src/parsers/association-parser.ts","../src/generators/test-code-generator.ts","../src/generators/mock-data-generator.ts","../src/generators/er-diagram-generator.ts","../src/analyzers/api-chain-analyzer.ts","../src/analyzers/impact-reporter.ts","../src/validators/config-validator.ts","../src/self-healing/index.ts"],"sourcesContent":["import type { OpenCrocConfig } from './types.js';\n\n/**\n * Define an OpenCroc configuration with type checking.\n *\n * @example\n * ```ts\n * // opencroc.config.ts\n * import { defineConfig } from 'opencroc';\n *\n * export default defineConfig({\n * backendRoot: './backend',\n * adapter: 'sequelize',\n * llm: {\n * provider: 'openai',\n * model: 'gpt-4o-mini',\n * },\n * });\n * ```\n */\nexport function defineConfig(config: OpenCrocConfig): OpenCrocConfig {\n return config;\n}\n","import type {\n OpenCrocConfig,\n PipelineRunResult,\n PipelineStep,\n} from '../types.js';\n\nexport interface Pipeline {\n run(steps?: PipelineStep[]): Promise<PipelineRunResult>;\n}\n\nexport function createPipeline(_config: OpenCrocConfig): Pipeline {\n return {\n async run(_steps) {\n // TODO: Implement 6-stage pipeline\n // 1. Scan — discover modules via adapter\n // 2. ER Diagram — parse models and generate relationship graphs\n // 3. API Chain — analyze controller routes and build dependency DAG\n // 4. Plan — generate test chains with topological ordering\n // 5. Codegen — emit Playwright test files from chain plans\n // 6. Validate — run multi-layer validation on generated configs\n throw new Error('Pipeline not yet implemented');\n },\n };\n}\n","import type { TableSchema } from '../types.js';\n\nexport interface ModelParser {\n parseFile(filePath: string): Promise<TableSchema>;\n parseDirectory(dirPath: string): Promise<TableSchema[]>;\n}\n\nexport function createModelParser(): ModelParser {\n return {\n async parseFile(_filePath) {\n // TODO: Use ts-morph to parse Sequelize/TypeORM/Prisma model files\n throw new Error('Model parser not yet implemented');\n },\n async parseDirectory(_dirPath) {\n throw new Error('Model parser not yet implemented');\n },\n };\n}\n","import type { RouteEntry } from '../types.js';\n\nexport interface ControllerParser {\n parseFile(filePath: string): Promise<RouteEntry[]>;\n parseDirectory(dirPath: string): Promise<RouteEntry[]>;\n}\n\nexport function createControllerParser(): ControllerParser {\n return {\n async parseFile(_filePath) {\n // TODO: Use ts-morph to extract route definitions from controllers\n throw new Error('Controller parser not yet implemented');\n },\n async parseDirectory(_dirPath) {\n throw new Error('Controller parser not yet implemented');\n },\n };\n}\n","import type { ForeignKeyRelation } from '../types.js';\n\nexport interface AssociationParser {\n parseFile(filePath: string): Promise<ForeignKeyRelation[]>;\n}\n\nexport function createAssociationParser(): AssociationParser {\n return {\n async parseFile(_filePath) {\n // TODO: Use ts-morph to extract association definitions\n throw new Error('Association parser not yet implemented');\n },\n };\n}\n","import type { GeneratedTestFile, TestChain } from '../types.js';\n\nexport interface TestCodeGenerator {\n generate(chains: TestChain[]): GeneratedTestFile[];\n}\n\nexport function createTestCodeGenerator(): TestCodeGenerator {\n return {\n generate(_chains) {\n // TODO: Generate Playwright test files from chain plans\n throw new Error('Test code generator not yet implemented');\n },\n };\n}\n","import type { TableSchema } from '../types.js';\n\nexport interface MockDataGenerator {\n generateForTable(schema: TableSchema): Record<string, unknown>;\n generateForTables(schemas: TableSchema[]): Map<string, Record<string, unknown>[]>;\n}\n\nexport function createMockDataGenerator(): MockDataGenerator {\n return {\n generateForTable(_schema) {\n // TODO: Generate realistic mock data based on field types and constraints\n throw new Error('Mock data generator not yet implemented');\n },\n generateForTables(_schemas) {\n throw new Error('Mock data generator not yet implemented');\n },\n };\n}\n","import type { ERDiagramResult, TableSchema, ForeignKeyRelation } from '../types.js';\n\nexport interface ERDiagramGenerator {\n generate(tables: TableSchema[], relations: ForeignKeyRelation[]): ERDiagramResult;\n}\n\nexport function createERDiagramGenerator(): ERDiagramGenerator {\n return {\n generate(_tables, _relations) {\n // TODO: Generate Mermaid ER diagram from parsed schemas\n throw new Error('ER diagram generator not yet implemented');\n },\n };\n}\n","import type { ApiEndpoint, ApiDependency } from '../types.js';\n\nexport interface ApiChainAnalyzer {\n analyze(endpoints: ApiEndpoint[]): {\n dependencies: ApiDependency[];\n topologicalOrder: ApiEndpoint[];\n };\n}\n\nexport function createApiChainAnalyzer(): ApiChainAnalyzer {\n return {\n analyze(_endpoints) {\n // TODO: Build DAG of API dependencies and compute topological sort\n throw new Error('API chain analyzer not yet implemented');\n },\n };\n}\n","import type { ImpactReport } from '../types.js';\n\nexport interface ImpactReporter {\n analyze(failedEndpoints: string[]): Promise<ImpactReport>;\n}\n\nexport function createImpactReporter(): ImpactReporter {\n return {\n async analyze(_failedEndpoints) {\n // TODO: Trace failure impact through dependency chains\n throw new Error('Impact reporter not yet implemented');\n },\n };\n}\n","import type { ValidationError } from '../types.js';\n\nexport function validateConfig(_config: Record<string, unknown>): ValidationError[] {\n // TODO: Implement 3-layer validation (schema → semantic → dry-run)\n return [];\n}\n","import type { SelfHealingConfig } from '../types.js';\n\nexport interface SelfHealingLoop {\n run(testResultsDir: string): Promise<SelfHealingResult>;\n}\n\nexport interface SelfHealingResult {\n iterations: number;\n fixed: string[];\n remaining: string[];\n totalTokensUsed: number;\n}\n\nexport function createSelfHealingLoop(_config: SelfHealingConfig): SelfHealingLoop {\n return {\n async run(_testResultsDir) {\n // TODO: Implement the dialog loop:\n // 1. Parse test failures\n // 2. Attribute root cause via LLM\n // 3. Generate controlled fix (config-only or config-and-source)\n // 4. Re-run tests\n // 5. Repeat until success or max iterations\n throw new Error('Self-healing loop not yet implemented');\n },\n };\n}\n"],"mappings":";AAoBO,SAAS,aAAa,QAAwC;AACnE,SAAO;AACT;;;ACZO,SAAS,eAAe,SAAmC;AAChE,SAAO;AAAA,IACL,MAAM,IAAI,QAAQ;AAQhB,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAAA,EACF;AACF;;;AChBO,SAAS,oBAAiC;AAC/C,SAAO;AAAA,IACL,MAAM,UAAU,WAAW;AAEzB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AAAA,IACA,MAAM,eAAe,UAAU;AAC7B,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AAAA,EACF;AACF;;;ACVO,SAAS,yBAA2C;AACzD,SAAO;AAAA,IACL,MAAM,UAAU,WAAW;AAEzB,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAAA,IACA,MAAM,eAAe,UAAU;AAC7B,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAAA,EACF;AACF;;;ACXO,SAAS,0BAA6C;AAC3D,SAAO;AAAA,IACL,MAAM,UAAU,WAAW;AAEzB,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AAAA,EACF;AACF;;;ACPO,SAAS,0BAA6C;AAC3D,SAAO;AAAA,IACL,SAAS,SAAS;AAEhB,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAAA,EACF;AACF;;;ACNO,SAAS,0BAA6C;AAC3D,SAAO;AAAA,IACL,iBAAiB,SAAS;AAExB,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAAA,IACA,kBAAkB,UAAU;AAC1B,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAAA,EACF;AACF;;;ACXO,SAAS,2BAA+C;AAC7D,SAAO;AAAA,IACL,SAAS,SAAS,YAAY;AAE5B,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAAA,EACF;AACF;;;ACJO,SAAS,yBAA2C;AACzD,SAAO;AAAA,IACL,QAAQ,YAAY;AAElB,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AAAA,EACF;AACF;;;ACVO,SAAS,uBAAuC;AACrD,SAAO;AAAA,IACL,MAAM,QAAQ,kBAAkB;AAE9B,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAAA,EACF;AACF;;;ACXO,SAAS,eAAe,SAAqD;AAElF,SAAO,CAAC;AACV;;;ACQO,SAAS,sBAAsB,SAA6C;AACjF,SAAO;AAAA,IACL,MAAM,IAAI,iBAAiB;AAOzB,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAAA,EACF;AACF;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencroc",
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "AI-native E2E testing framework — source-aware test generation, intelligent validation, and self-healing",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"e2e",
|
|
7
|
+
"testing",
|
|
8
|
+
"ai",
|
|
9
|
+
"playwright",
|
|
10
|
+
"test-generation",
|
|
11
|
+
"self-healing",
|
|
12
|
+
"source-aware"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://opencroc.com",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/opencroc/opencroc/issues"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/opencroc/opencroc.git"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "OpenCroc Contributors",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"bin": {
|
|
28
|
+
"opencroc": "./dist/cli/index.js"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsup",
|
|
37
|
+
"dev": "tsup --watch",
|
|
38
|
+
"lint": "eslint src/",
|
|
39
|
+
"lint:fix": "eslint src/ --fix",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"test:coverage": "vitest run --coverage",
|
|
43
|
+
"typecheck": "tsc --noEmit",
|
|
44
|
+
"prepublishOnly": "npm run build"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"chalk": "^5.3.0",
|
|
48
|
+
"commander": "^12.1.0",
|
|
49
|
+
"cosmiconfig": "^9.0.0",
|
|
50
|
+
"glob": "^10.3.10",
|
|
51
|
+
"ts-morph": "^22.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^20.11.0",
|
|
55
|
+
"eslint": "^9.0.0",
|
|
56
|
+
"tsup": "^8.0.0",
|
|
57
|
+
"typescript": "^5.4.0",
|
|
58
|
+
"vitest": "^1.6.0"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"@playwright/test": ">=1.40.0"
|
|
62
|
+
},
|
|
63
|
+
"peerDependenciesMeta": {
|
|
64
|
+
"@playwright/test": {
|
|
65
|
+
"optional": false
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=18.0.0"
|
|
70
|
+
}
|
|
71
|
+
}
|