@tayo-dev/rtl 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +250 -0
- package/dist/analyzer/mocks/detector.d.ts +59 -0
- package/dist/analyzer/mocks/detector.d.ts.map +1 -0
- package/dist/analyzer/mocks/detector.js +264 -0
- package/dist/analyzer/mocks/detector.js.map +1 -0
- package/dist/analyzer/mocks/target-analyzer.d.ts +92 -0
- package/dist/analyzer/mocks/target-analyzer.d.ts.map +1 -0
- package/dist/analyzer/mocks/target-analyzer.js +305 -0
- package/dist/analyzer/mocks/target-analyzer.js.map +1 -0
- package/dist/analyzer/visual/element-analyzer.d.ts +44 -0
- package/dist/analyzer/visual/element-analyzer.d.ts.map +1 -0
- package/dist/analyzer/visual/element-analyzer.js +176 -0
- package/dist/analyzer/visual/element-analyzer.js.map +1 -0
- package/dist/analyzer/visual/inspector.d.ts +49 -0
- package/dist/analyzer/visual/inspector.d.ts.map +1 -0
- package/dist/analyzer/visual/inspector.js +109 -0
- package/dist/analyzer/visual/inspector.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +13 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +417 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/core/generator.d.ts +32 -0
- package/dist/core/generator.d.ts.map +1 -0
- package/dist/core/generator.js +173 -0
- package/dist/core/generator.js.map +1 -0
- package/dist/core/js-parser.d.ts +48 -0
- package/dist/core/js-parser.d.ts.map +1 -0
- package/dist/core/js-parser.js +244 -0
- package/dist/core/js-parser.js.map +1 -0
- package/dist/core/mock-intelligence.d.ts +14 -0
- package/dist/core/mock-intelligence.d.ts.map +1 -0
- package/dist/core/mock-intelligence.js +140 -0
- package/dist/core/mock-intelligence.js.map +1 -0
- package/dist/core/orchestrator.d.ts +49 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +315 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/parser.d.ts +9 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +120 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/core/recording-intelligence.d.ts +15 -0
- package/dist/core/recording-intelligence.d.ts.map +1 -0
- package/dist/core/recording-intelligence.js +178 -0
- package/dist/core/recording-intelligence.js.map +1 -0
- package/dist/core/resolver.d.ts +58 -0
- package/dist/core/resolver.d.ts.map +1 -0
- package/dist/core/resolver.js +291 -0
- package/dist/core/resolver.js.map +1 -0
- package/dist/core/scanner.d.ts +51 -0
- package/dist/core/scanner.d.ts.map +1 -0
- package/dist/core/scanner.js +310 -0
- package/dist/core/scanner.js.map +1 -0
- package/dist/core/scorer.d.ts +8 -0
- package/dist/core/scorer.d.ts.map +1 -0
- package/dist/core/scorer.js +76 -0
- package/dist/core/scorer.js.map +1 -0
- package/dist/core/validator.d.ts +134 -0
- package/dist/core/validator.d.ts.map +1 -0
- package/dist/core/validator.js +44 -0
- package/dist/core/validator.js.map +1 -0
- package/dist/core/verifier.d.ts +10 -0
- package/dist/core/verifier.d.ts.map +1 -0
- package/dist/core/verifier.js +30 -0
- package/dist/core/verifier.js.map +1 -0
- package/dist/core/writer.d.ts +15 -0
- package/dist/core/writer.d.ts.map +1 -0
- package/dist/core/writer.js +43 -0
- package/dist/core/writer.js.map +1 -0
- package/dist/generator/mocks/builder.d.ts +47 -0
- package/dist/generator/mocks/builder.d.ts.map +1 -0
- package/dist/generator/mocks/builder.js +335 -0
- package/dist/generator/mocks/builder.js.map +1 -0
- package/dist/generator/transforms/dialog-transform.d.ts +35 -0
- package/dist/generator/transforms/dialog-transform.d.ts.map +1 -0
- package/dist/generator/transforms/dialog-transform.js +293 -0
- package/dist/generator/transforms/dialog-transform.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/learner/analyzer.d.ts +13 -0
- package/dist/learner/analyzer.d.ts.map +1 -0
- package/dist/learner/analyzer.js +484 -0
- package/dist/learner/analyzer.js.map +1 -0
- package/dist/learner/index.d.ts +66 -0
- package/dist/learner/index.d.ts.map +1 -0
- package/dist/learner/index.js +247 -0
- package/dist/learner/index.js.map +1 -0
- package/dist/learner/storage.d.ts +68 -0
- package/dist/learner/storage.d.ts.map +1 -0
- package/dist/learner/storage.js +201 -0
- package/dist/learner/storage.js.map +1 -0
- package/dist/learner/types.d.ts +41 -0
- package/dist/learner/types.d.ts.map +1 -0
- package/dist/learner/types.js +31 -0
- package/dist/learner/types.js.map +1 -0
- package/dist/parser/recorder-parser.d.ts +40 -0
- package/dist/parser/recorder-parser.d.ts.map +1 -0
- package/dist/parser/recorder-parser.js +139 -0
- package/dist/parser/recorder-parser.js.map +1 -0
- package/dist/parser/steps/deduplicator.d.ts +19 -0
- package/dist/parser/steps/deduplicator.d.ts.map +1 -0
- package/dist/parser/steps/deduplicator.js +75 -0
- package/dist/parser/steps/deduplicator.js.map +1 -0
- package/dist/parser/steps/dialog-detector.d.ts +38 -0
- package/dist/parser/steps/dialog-detector.d.ts.map +1 -0
- package/dist/parser/steps/dialog-detector.js +290 -0
- package/dist/parser/steps/dialog-detector.js.map +1 -0
- package/dist/parser/steps/noise-filter.d.ts +21 -0
- package/dist/parser/steps/noise-filter.d.ts.map +1 -0
- package/dist/parser/steps/noise-filter.js +138 -0
- package/dist/parser/steps/noise-filter.js.map +1 -0
- package/dist/scorer/index.d.ts +43 -0
- package/dist/scorer/index.d.ts.map +1 -0
- package/dist/scorer/index.js +82 -0
- package/dist/scorer/index.js.map +1 -0
- package/dist/scorer/post-verify.d.ts +17 -0
- package/dist/scorer/post-verify.d.ts.map +1 -0
- package/dist/scorer/post-verify.js +163 -0
- package/dist/scorer/post-verify.js.map +1 -0
- package/dist/scorer/pre-audit.d.ts +32 -0
- package/dist/scorer/pre-audit.d.ts.map +1 -0
- package/dist/scorer/pre-audit.js +99 -0
- package/dist/scorer/pre-audit.js.map +1 -0
- package/dist/scorer/quality-gates.d.ts +17 -0
- package/dist/scorer/quality-gates.d.ts.map +1 -0
- package/dist/scorer/quality-gates.js +304 -0
- package/dist/scorer/quality-gates.js.map +1 -0
- package/dist/scorer/types.d.ts +27 -0
- package/dist/scorer/types.d.ts.map +1 -0
- package/dist/scorer/types.js +5 -0
- package/dist/scorer/types.js.map +1 -0
- package/dist/templates/test-template.d.ts +21 -0
- package/dist/templates/test-template.d.ts.map +1 -0
- package/dist/templates/test-template.js +92 -0
- package/dist/templates/test-template.js.map +1 -0
- package/dist/types/conventions.d.ts +49 -0
- package/dist/types/conventions.d.ts.map +1 -0
- package/dist/types/conventions.js +13 -0
- package/dist/types/conventions.js.map +1 -0
- package/dist/types/recording.d.ts +143 -0
- package/dist/types/recording.d.ts.map +1 -0
- package/dist/types/recording.js +5 -0
- package/dist/types/recording.js.map +1 -0
- package/dist/types/score.d.ts +18 -0
- package/dist/types/score.d.ts.map +1 -0
- package/dist/types/score.js +2 -0
- package/dist/types/score.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Taro
|
|
2
|
+
|
|
3
|
+
Generate React Testing Library tests from Chrome Recorder recordings — automatically.
|
|
4
|
+
|
|
5
|
+
## Introduction
|
|
6
|
+
|
|
7
|
+
Taro is a CLI tool that reads Chrome DevTools Recorder exports (JSON) and Testing Library Recorder JS files and generates RTL test files. It scores its own output, learns your project's test conventions from existing files, and stores per-project state in a local `.taro/` directory. No server, no cloud — just files.
|
|
8
|
+
|
|
9
|
+
### Who it is for
|
|
10
|
+
|
|
11
|
+
- React developers who write tests with `@testing-library/react`
|
|
12
|
+
- Developers who use Chrome DevTools Recorder to capture user flows
|
|
13
|
+
- Teams that want test coverage without spending hours writing boilerplate
|
|
14
|
+
|
|
15
|
+
### The problem it solves
|
|
16
|
+
|
|
17
|
+
Recording a user flow in Chrome takes 30 seconds. Translating that recording into a well-structured RTL test takes 20–40 minutes and requires knowing which queries to use, how to assert, and how to match your project's test conventions. Taro closes that gap.
|
|
18
|
+
|
|
19
|
+
### How it works
|
|
20
|
+
|
|
21
|
+
1. Record a user flow in Chrome DevTools → Recorder panel.
|
|
22
|
+
2. Export via the Testing Library Recorder extension (`.js`) or as native Chrome Recorder JSON (`.json`).
|
|
23
|
+
3. Run `taro generate ./recording.js`.
|
|
24
|
+
4. Taro writes a `.test.tsx` file next to your recording, scored and convention-aware.
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### Prerequisites
|
|
29
|
+
|
|
30
|
+
- Node.js 18 or later
|
|
31
|
+
- A React project using `@testing-library/react`
|
|
32
|
+
- Chrome DevTools Recorder (built into Chrome — no extension needed for JSON exports)
|
|
33
|
+
|
|
34
|
+
### Step 1 — Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install --save-dev @tayo-dev/rtl
|
|
38
|
+
# or use npx to skip install entirely
|
|
39
|
+
npx @tayo-dev/rtl generate ./my-recording.js
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Step 2 — Record a user flow
|
|
43
|
+
|
|
44
|
+
Open Chrome DevTools → Recorder panel → click "Start new recording" → perform your user flow (clicks, form fills, navigation) → click "End recording". Then either:
|
|
45
|
+
|
|
46
|
+
- Export as JSON: click the export button → "JSON" → save as `recording.json`
|
|
47
|
+
- Export via Testing Library Recorder extension: install the extension, click its export button, save as `recording.js`
|
|
48
|
+
|
|
49
|
+
### Step 3 — Generate the test
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Using npx (no install required)
|
|
53
|
+
npx @tayo-dev/rtl generate ./recording.js
|
|
54
|
+
|
|
55
|
+
# Or if installed globally
|
|
56
|
+
taro generate ./recording.js
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Expected output:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
Parsed: my user flow — 8 steps
|
|
63
|
+
[taro] Score: 78/100 (B) — query: 80, assertions: 70, structure: 85
|
|
64
|
+
Created: src/components/MyComponent.test.tsx
|
|
65
|
+
[taro] ✓ post-write verified
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### What happens next
|
|
69
|
+
|
|
70
|
+
Taro writes a `.test.tsx` file. On subsequent runs in the same project, it reads `.taro/conventions.json` to match your test style (import style, mock pattern, folder structure) automatically.
|
|
71
|
+
|
|
72
|
+
## CLI Reference
|
|
73
|
+
|
|
74
|
+
### `taro generate <file>`
|
|
75
|
+
|
|
76
|
+
Generates a React Testing Library test from a Chrome Recorder export.
|
|
77
|
+
|
|
78
|
+
**Arguments:**
|
|
79
|
+
|
|
80
|
+
| Argument | Description |
|
|
81
|
+
|----------|-------------|
|
|
82
|
+
| `<file>` | Path to the recording file. Accepts Chrome Recorder JSON exports (`.json`) or Testing Library Recorder JS files (`.js`). |
|
|
83
|
+
|
|
84
|
+
**Options:**
|
|
85
|
+
|
|
86
|
+
| Flag | Short | Default | Description |
|
|
87
|
+
|------|-------|---------|-------------|
|
|
88
|
+
| `--output <path>` | `-o` | Same directory as input, `{name}.test.tsx` | Override the output file path for the generated test. |
|
|
89
|
+
| `--dry-run` | `-d` | `false` | Print the generated test to stdout and show the score without writing to disk. Useful for previewing output before committing. |
|
|
90
|
+
| `--force` | `-f` | `false` | Overwrite an existing test file. Without this flag, Taro exits with an error if the output file already exists. |
|
|
91
|
+
| `--version` | `-v` | — | Print the installed version and exit. |
|
|
92
|
+
| `--help` | `-h` | — | Display command help and exit. |
|
|
93
|
+
|
|
94
|
+
**Examples:**
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Generate and write a test next to the recording
|
|
98
|
+
taro generate ./recordings/checkout-flow.json
|
|
99
|
+
|
|
100
|
+
# Preview without writing (dry run)
|
|
101
|
+
taro generate --dry-run ./recordings/checkout-flow.json
|
|
102
|
+
|
|
103
|
+
# Write to a specific path
|
|
104
|
+
taro generate --output src/__tests__/checkout.test.tsx ./recordings/checkout-flow.json
|
|
105
|
+
|
|
106
|
+
# Overwrite an existing test
|
|
107
|
+
taro generate --force ./recordings/checkout-flow.json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Output file naming:**
|
|
111
|
+
If `--output` is not provided, Taro derives the output path from the input file: `{input-dir}/{input-basename}.test.tsx`. For example, `./recordings/login.json` → `./recordings/login.test.tsx`.
|
|
112
|
+
|
|
113
|
+
**Supported input formats:**
|
|
114
|
+
- Chrome Recorder JSON (`.json`) — exported directly from Chrome DevTools Recorder
|
|
115
|
+
- Testing Library Recorder JS (`.js`) — exported via the Testing Library Recorder Chrome extension; detected by `.js` extension or `@jest-environment-options` header
|
|
116
|
+
|
|
117
|
+
## Worked Example
|
|
118
|
+
|
|
119
|
+
### Input: Chrome Recorder export (`login-flow.json`)
|
|
120
|
+
|
|
121
|
+
Here is a typical Chrome Recorder JSON export capturing a login flow.
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"title": "login flow",
|
|
126
|
+
"steps": [
|
|
127
|
+
{ "type": "navigate", "url": "http://localhost:3000/login" },
|
|
128
|
+
{ "type": "click", "selectors": [["aria/Email address"]] },
|
|
129
|
+
{ "type": "change", "value": "user@example.com", "selectors": [["#email"]] },
|
|
130
|
+
{ "type": "click", "selectors": [["aria/Password"]] },
|
|
131
|
+
{ "type": "change", "value": "secret123", "selectors": [["#password"]] },
|
|
132
|
+
{ "type": "click", "selectors": [["aria/Sign in[role=\"button\"]"]] },
|
|
133
|
+
{ "type": "waitForElement", "selectors": [["aria/Welcome back"]] }
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Command
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
taro generate ./login-flow.json
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Terminal output
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
Parsed: login flow — 7 steps
|
|
148
|
+
[taro] Score: 82/100 (B) — query: 90, assertions: 75, structure: 80
|
|
149
|
+
Created: login-flow.test.tsx
|
|
150
|
+
[taro] ✓ post-write verified
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Output: Generated test (`login-flow.test.tsx`)
|
|
154
|
+
|
|
155
|
+
Taro generates a convention-aware RTL test with accessible queries:
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { render, screen } from '@testing-library/react'
|
|
159
|
+
import userEvent from '@testing-library/user-event'
|
|
160
|
+
import { LoginPage } from '../LoginPage'
|
|
161
|
+
|
|
162
|
+
describe('login flow', () => {
|
|
163
|
+
it('should complete login flow', async () => {
|
|
164
|
+
const user = userEvent.setup()
|
|
165
|
+
render(<LoginPage />)
|
|
166
|
+
|
|
167
|
+
await user.click(screen.getByRole('textbox', { name: /email address/i }))
|
|
168
|
+
await user.type(screen.getByRole('textbox', { name: /email address/i }), 'user@example.com')
|
|
169
|
+
await user.click(screen.getByRole('textbox', { name: /password/i }))
|
|
170
|
+
await user.type(screen.getByRole('textbox', { name: /password/i }), 'secret123')
|
|
171
|
+
await user.click(screen.getByRole('button', { name: /sign in/i }))
|
|
172
|
+
|
|
173
|
+
expect(screen.getByText(/welcome back/i)).toBeInTheDocument()
|
|
174
|
+
})
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### What Taro did here
|
|
179
|
+
|
|
180
|
+
- Parsed the navigate step and inferred the component under test
|
|
181
|
+
- Upgraded CSS selectors (`#email`, `#password`) to accessible `getByRole` queries using aria attributes from the recording
|
|
182
|
+
- Inferred `userEvent.type()` from change steps and `userEvent.click()` from click steps
|
|
183
|
+
- Mapped the `waitForElement` step to a `toBeInTheDocument()` assertion
|
|
184
|
+
- Scored the output (82/100) and emitted no blocking errors
|
|
185
|
+
|
|
186
|
+
> **Note:** The component import path (`../LoginPage`) is a placeholder. Taro generates a comment in the file indicating where to update it.
|
|
187
|
+
|
|
188
|
+
## Using Taro as a Claude Code Skill
|
|
189
|
+
|
|
190
|
+
### Overview
|
|
191
|
+
|
|
192
|
+
Taro works naturally as a Claude Code skill. You can instruct Claude to run `taro generate` on a recording file and it will generate the test, report the score, and surface any quality hints — all in a single agent turn.
|
|
193
|
+
|
|
194
|
+
### Option A: Direct invocation (no setup required)
|
|
195
|
+
|
|
196
|
+
Claude Code can invoke Taro directly using the Bash tool. No skill configuration is needed — Claude calls npx inline. Simply give Claude a prompt like:
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
Run: npx @tayo-dev/rtl generate ./recordings/checkout-flow.js
|
|
200
|
+
Then report the score and the path of the generated file.
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Option B: Register as a Claude Code skill
|
|
204
|
+
|
|
205
|
+
Registering Taro as a skill lets Claude invoke it by name without knowing the full command.
|
|
206
|
+
|
|
207
|
+
**Step 1** — Create the skill file at `.claude/skills/taro/SKILL.md` in your project:
|
|
208
|
+
|
|
209
|
+
```markdown
|
|
210
|
+
# Taro — RTL Test Generator
|
|
211
|
+
|
|
212
|
+
## Purpose
|
|
213
|
+
Generate a React Testing Library test from a Chrome Recorder export.
|
|
214
|
+
|
|
215
|
+
## Invocation
|
|
216
|
+
Run: taro generate <recording-file>
|
|
217
|
+
|
|
218
|
+
## Flags
|
|
219
|
+
- `--dry-run` (-d): Preview the generated test without writing to disk
|
|
220
|
+
- `--output <path>` (-o): Override the output file path
|
|
221
|
+
- `--force` (-f): Overwrite an existing test file
|
|
222
|
+
|
|
223
|
+
## Output
|
|
224
|
+
Writes `{recording-name}.test.tsx` next to the recording file.
|
|
225
|
+
Reports score (0-100) and any quality hints.
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Step 2** — Ensure Taro is installed in the project:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
npm install --save-dev @tayo-dev/rtl
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Step 3** — Ask Claude to use the skill:
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
Use the taro skill to generate a test from ./recordings/login-flow.js
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Tips for agent use
|
|
241
|
+
|
|
242
|
+
- Use `--dry-run` first to preview output before committing generated files
|
|
243
|
+
- If you record multiple flows, run Taro on each to build up convention state in `.taro/conventions.json` — later runs benefit from earlier ones
|
|
244
|
+
- Pass `--force` when re-recording an updated flow to overwrite the old test
|
|
245
|
+
- The `.taro/` directory should be committed to your repo so convention learning persists across team members
|
|
246
|
+
|
|
247
|
+
### Notes
|
|
248
|
+
|
|
249
|
+
- Taro does not require network access at generation time (DOM inspection via Playwright is optional and only runs when a live URL is in the recording)
|
|
250
|
+
- All state is local to `.taro/` — no external service is contacted
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Call Detector - Identifies API calls in recordings and codebase
|
|
3
|
+
*
|
|
4
|
+
* Detects fetch, XMLHttpRequest, and common API patterns to determine
|
|
5
|
+
* which network calls need mocking in tests.
|
|
6
|
+
*/
|
|
7
|
+
import type { NormalizedRecording } from '../../types/recording.js';
|
|
8
|
+
/**
|
|
9
|
+
* Information about a detected API call
|
|
10
|
+
*/
|
|
11
|
+
export interface ApiCallInfo {
|
|
12
|
+
/** Unique identifier for this API call */
|
|
13
|
+
id: string;
|
|
14
|
+
/** Type of API call detected */
|
|
15
|
+
method: 'fetch' | 'XMLHttpRequest' | 'axios' | 'fetch-jsonp' | 'unknown';
|
|
16
|
+
/** The URL or endpoint being called */
|
|
17
|
+
url?: string;
|
|
18
|
+
/** HTTP method if detectable */
|
|
19
|
+
httpMethod?: string;
|
|
20
|
+
/** Whether this is an external API (not same origin) */
|
|
21
|
+
isExternal: boolean;
|
|
22
|
+
/** Source where this was detected */
|
|
23
|
+
source: 'recording' | 'codebase' | 'both';
|
|
24
|
+
/** The step in recording where this appears (if applicable) */
|
|
25
|
+
recordingStepId?: string;
|
|
26
|
+
/** File and line where this appears in codebase (if applicable) */
|
|
27
|
+
codebaseLocation?: {
|
|
28
|
+
file: string;
|
|
29
|
+
line: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Detect API calls from a normalized recording
|
|
34
|
+
* Looks for network-related actions or URLs in step data
|
|
35
|
+
*/
|
|
36
|
+
export declare function detectApiCallsFromRecording(recording: NormalizedRecording): ApiCallInfo[];
|
|
37
|
+
/**
|
|
38
|
+
* Scan source code files for API calls
|
|
39
|
+
*/
|
|
40
|
+
export declare function detectApiCallsFromCodebase(files: {
|
|
41
|
+
path: string;
|
|
42
|
+
content: string;
|
|
43
|
+
}[]): ApiCallInfo[];
|
|
44
|
+
/**
|
|
45
|
+
* Main detection function - combines recording and codebase analysis
|
|
46
|
+
*/
|
|
47
|
+
export declare function detectApiCalls(recording?: NormalizedRecording, codebaseFiles?: {
|
|
48
|
+
path: string;
|
|
49
|
+
content: string;
|
|
50
|
+
}[]): ApiCallInfo[];
|
|
51
|
+
/**
|
|
52
|
+
* Filter API calls that need mocking (external only)
|
|
53
|
+
*/
|
|
54
|
+
export declare function filterMockableCalls(apiCalls: ApiCallInfo[]): ApiCallInfo[];
|
|
55
|
+
/**
|
|
56
|
+
* Group API calls by domain for organized mocking
|
|
57
|
+
*/
|
|
58
|
+
export declare function groupApiCallsByDomain(apiCalls: ApiCallInfo[]): Map<string, ApiCallInfo[]>;
|
|
59
|
+
//# sourceMappingURL=detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../../src/analyzer/mocks/detector.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAiB,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEnF;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,MAAM,EAAE,OAAO,GAAG,gBAAgB,GAAG,OAAO,GAAG,aAAa,GAAG,SAAS,CAAC;IACzE,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,UAAU,EAAE,OAAO,CAAC;IACpB,qCAAqC;IACrC,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;IAC1C,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,gBAAgB,CAAC,EAAE;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAkDD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,mBAAmB,GAAG,WAAW,EAAE,CAoCzF;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,WAAW,EAAE,CA0DpG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,SAAS,CAAC,EAAE,mBAAmB,EAC/B,aAAa,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,GAClD,WAAW,EAAE,CA6Bf;AAkED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAE1E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAuBzF"}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Call Detector - Identifies API calls in recordings and codebase
|
|
3
|
+
*
|
|
4
|
+
* Detects fetch, XMLHttpRequest, and common API patterns to determine
|
|
5
|
+
* which network calls need mocking in tests.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Patterns that indicate an API call in code
|
|
9
|
+
*/
|
|
10
|
+
const API_PATTERNS = {
|
|
11
|
+
fetch: [
|
|
12
|
+
/\bfetch\s*\(\s*['"`]/i,
|
|
13
|
+
/await\s+fetch\s*\(/i,
|
|
14
|
+
/window\.fetch\s*\(/i,
|
|
15
|
+
],
|
|
16
|
+
xmlHttpRequest: [
|
|
17
|
+
/new\s+XMLHttpRequest\s*\(\s*\)/i,
|
|
18
|
+
/xhr\s*\.\s*open\s*\(/i,
|
|
19
|
+
],
|
|
20
|
+
axios: [
|
|
21
|
+
/axios\.(get|post|put|patch|delete|request)\s*\(/i,
|
|
22
|
+
/await\s+axios\s*\(/i,
|
|
23
|
+
],
|
|
24
|
+
fetchJsonp: [
|
|
25
|
+
/jsonp\s*\(/i,
|
|
26
|
+
/\.jsonp\s*\(/i,
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Common API endpoint patterns
|
|
31
|
+
*/
|
|
32
|
+
const API_ENDPOINT_PATTERNS = [
|
|
33
|
+
/\/api\//i,
|
|
34
|
+
/\/v\d+\//i,
|
|
35
|
+
/\/graphql/i,
|
|
36
|
+
/\/rest\//i,
|
|
37
|
+
/\/rpc\//i,
|
|
38
|
+
/\.(json|xml)\s*$/i,
|
|
39
|
+
/\?.*=/i, // Query string
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* External API domains (common third-party services)
|
|
43
|
+
*/
|
|
44
|
+
const EXTERNAL_API_DOMAINS = [
|
|
45
|
+
'api.',
|
|
46
|
+
'://',
|
|
47
|
+
'.com/',
|
|
48
|
+
'.io/',
|
|
49
|
+
'.net/',
|
|
50
|
+
'localhost:', // Treat localhost as external for testing
|
|
51
|
+
];
|
|
52
|
+
/**
|
|
53
|
+
* Detect API calls from a normalized recording
|
|
54
|
+
* Looks for network-related actions or URLs in step data
|
|
55
|
+
*/
|
|
56
|
+
export function detectApiCallsFromRecording(recording) {
|
|
57
|
+
const apiCalls = [];
|
|
58
|
+
for (const step of recording.steps) {
|
|
59
|
+
// Look for URL in step metadata or value
|
|
60
|
+
const potentialUrl = step.metadata?.url
|
|
61
|
+
|| step.value
|
|
62
|
+
|| step.selector;
|
|
63
|
+
if (potentialUrl && isApiUrl(potentialUrl)) {
|
|
64
|
+
const method = detectMethodFromUrl(potentialUrl);
|
|
65
|
+
apiCalls.push({
|
|
66
|
+
id: `recording-${step.id}`,
|
|
67
|
+
method: method || 'unknown',
|
|
68
|
+
url: potentialUrl,
|
|
69
|
+
isExternal: isExternalUrl(potentialUrl),
|
|
70
|
+
source: 'recording',
|
|
71
|
+
recordingStepId: step.id,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
// Check for network-related actions in metadata
|
|
75
|
+
if (step.metadata?.networkCall) {
|
|
76
|
+
apiCalls.push({
|
|
77
|
+
id: `recording-network-${step.id}`,
|
|
78
|
+
method: step.metadata.networkMethod || 'unknown',
|
|
79
|
+
url: step.metadata.networkUrl,
|
|
80
|
+
isExternal: isExternalUrl(step.metadata.networkUrl),
|
|
81
|
+
source: 'recording',
|
|
82
|
+
recordingStepId: step.id,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return apiCalls;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Scan source code files for API calls
|
|
90
|
+
*/
|
|
91
|
+
export function detectApiCallsFromCodebase(files) {
|
|
92
|
+
const apiCalls = [];
|
|
93
|
+
for (const file of files) {
|
|
94
|
+
const lines = file.content.split('\n');
|
|
95
|
+
for (let i = 0; i < lines.length; i++) {
|
|
96
|
+
const line = lines[i];
|
|
97
|
+
const lineNumber = i + 1;
|
|
98
|
+
// Check for fetch
|
|
99
|
+
for (const pattern of API_PATTERNS.fetch) {
|
|
100
|
+
if (pattern.test(line)) {
|
|
101
|
+
const url = extractUrlFromLine(line, 'fetch');
|
|
102
|
+
apiCalls.push({
|
|
103
|
+
id: `codebase-${file.path}-${lineNumber}`,
|
|
104
|
+
method: 'fetch',
|
|
105
|
+
url,
|
|
106
|
+
httpMethod: extractHttpMethod(line),
|
|
107
|
+
isExternal: url ? isExternalUrl(url) : true,
|
|
108
|
+
source: 'codebase',
|
|
109
|
+
codebaseLocation: { file: file.path, line: lineNumber },
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Check for XMLHttpRequest
|
|
114
|
+
for (const pattern of API_PATTERNS.xmlHttpRequest) {
|
|
115
|
+
if (pattern.test(line)) {
|
|
116
|
+
apiCalls.push({
|
|
117
|
+
id: `codebase-${file.path}-${lineNumber}`,
|
|
118
|
+
method: 'XMLHttpRequest',
|
|
119
|
+
isExternal: true,
|
|
120
|
+
source: 'codebase',
|
|
121
|
+
codebaseLocation: { file: file.path, line: lineNumber },
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Check for axios
|
|
126
|
+
for (const pattern of API_PATTERNS.axios) {
|
|
127
|
+
if (pattern.test(line)) {
|
|
128
|
+
const url = extractUrlFromLine(line, 'axios');
|
|
129
|
+
apiCalls.push({
|
|
130
|
+
id: `codebase-${file.path}-${lineNumber}`,
|
|
131
|
+
method: 'axios',
|
|
132
|
+
httpMethod: extractAxiosMethod(line),
|
|
133
|
+
url,
|
|
134
|
+
isExternal: url ? isExternalUrl(url) : true,
|
|
135
|
+
source: 'codebase',
|
|
136
|
+
codebaseLocation: { file: file.path, line: lineNumber },
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return apiCalls;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Main detection function - combines recording and codebase analysis
|
|
146
|
+
*/
|
|
147
|
+
export function detectApiCalls(recording, codebaseFiles) {
|
|
148
|
+
const results = [];
|
|
149
|
+
// Detect from recording
|
|
150
|
+
if (recording) {
|
|
151
|
+
const recordingCalls = detectApiCallsFromRecording(recording);
|
|
152
|
+
results.push(...recordingCalls);
|
|
153
|
+
}
|
|
154
|
+
// Detect from codebase
|
|
155
|
+
if (codebaseFiles) {
|
|
156
|
+
const codebaseCalls = detectApiCallsFromCodebase(codebaseFiles);
|
|
157
|
+
results.push(...codebaseCalls);
|
|
158
|
+
}
|
|
159
|
+
// Deduplicate by URL
|
|
160
|
+
const uniqueByUrl = new Map();
|
|
161
|
+
for (const call of results) {
|
|
162
|
+
if (call.url) {
|
|
163
|
+
const key = `${call.method}:${call.url}`;
|
|
164
|
+
if (!uniqueByUrl.has(key)) {
|
|
165
|
+
uniqueByUrl.set(key, { ...call, source: 'both' });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
uniqueByUrl.set(call.id, call);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return Array.from(uniqueByUrl.values());
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Check if a string looks like an API URL
|
|
176
|
+
*/
|
|
177
|
+
function isApiUrl(str) {
|
|
178
|
+
if (!str || typeof str !== 'string')
|
|
179
|
+
return false;
|
|
180
|
+
// Must be a URL-like string
|
|
181
|
+
return API_ENDPOINT_PATTERNS.some(pattern => pattern.test(str));
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Detect HTTP method from URL patterns
|
|
185
|
+
*/
|
|
186
|
+
function detectMethodFromUrl(url) {
|
|
187
|
+
if (url.includes('.json'))
|
|
188
|
+
return 'fetch';
|
|
189
|
+
if (url.includes('graphql'))
|
|
190
|
+
return 'fetch';
|
|
191
|
+
if (url.includes('jsonp'))
|
|
192
|
+
return 'fetch-jsonp';
|
|
193
|
+
return 'fetch'; // Default to fetch for modern apps
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Check if URL is external (different origin)
|
|
197
|
+
*/
|
|
198
|
+
function isExternalUrl(url) {
|
|
199
|
+
if (!url)
|
|
200
|
+
return true;
|
|
201
|
+
return EXTERNAL_API_DOMAINS.some(domain => url.includes(domain));
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Extract URL from a fetch/axios line
|
|
205
|
+
*/
|
|
206
|
+
function extractUrlFromLine(line, type) {
|
|
207
|
+
// Match quoted strings (single or double quotes, or backticks)
|
|
208
|
+
const urlMatch = line.match(/['"`(](https?:\/\/[^'")`]+)['"`)]/);
|
|
209
|
+
if (urlMatch) {
|
|
210
|
+
return urlMatch[1];
|
|
211
|
+
}
|
|
212
|
+
// For dynamic URLs, try to find variable names
|
|
213
|
+
const dynamicMatch = line.match(new RegExp(`${type}\\s*\\(\\s*(\\w+)`));
|
|
214
|
+
if (dynamicMatch) {
|
|
215
|
+
return `[dynamic - \${${dynamicMatch[1]}}]`;
|
|
216
|
+
}
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Extract HTTP method from fetch options
|
|
221
|
+
*/
|
|
222
|
+
function extractHttpMethod(line) {
|
|
223
|
+
const methodMatch = line.match(/method:\s*['"](\w+)['"]/i);
|
|
224
|
+
return methodMatch ? methodMatch[1].toUpperCase() : undefined;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Extract axios method (get, post, etc.)
|
|
228
|
+
*/
|
|
229
|
+
function extractAxiosMethod(line) {
|
|
230
|
+
const methodMatch = line.match(/axios\.(get|post|put|patch|delete|request)\s*\(/i);
|
|
231
|
+
return methodMatch ? methodMatch[1].toUpperCase() : undefined;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Filter API calls that need mocking (external only)
|
|
235
|
+
*/
|
|
236
|
+
export function filterMockableCalls(apiCalls) {
|
|
237
|
+
return apiCalls.filter(call => call.isExternal);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Group API calls by domain for organized mocking
|
|
241
|
+
*/
|
|
242
|
+
export function groupApiCallsByDomain(apiCalls) {
|
|
243
|
+
const groups = new Map();
|
|
244
|
+
for (const call of apiCalls) {
|
|
245
|
+
if (!call.url) {
|
|
246
|
+
const unknown = 'unknown';
|
|
247
|
+
const existing = groups.get(unknown) || [];
|
|
248
|
+
groups.set(unknown, [...existing, call]);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
const url = new URL(call.url);
|
|
253
|
+
const domain = url.hostname;
|
|
254
|
+
const existing = groups.get(domain) || [];
|
|
255
|
+
groups.set(domain, [...existing, call]);
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
const existing = groups.get('unknown') || [];
|
|
259
|
+
groups.set('unknown', [...existing, call]);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return groups;
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../../../src/analyzer/mocks/detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock Target Analyzer - Identifies appropriate mock targets from codebase
|
|
3
|
+
*
|
|
4
|
+
* Analyzes the codebase to determine which mock libraries are available
|
|
5
|
+
* (msw, jest.fn, sinon) and suggests appropriate mock targets based
|
|
6
|
+
* on the detected API calls.
|
|
7
|
+
*/
|
|
8
|
+
import type { ApiCallInfo } from './detector.js';
|
|
9
|
+
/**
|
|
10
|
+
* Information about a mock target
|
|
11
|
+
*/
|
|
12
|
+
export interface MockTarget {
|
|
13
|
+
/** Unique identifier */
|
|
14
|
+
id: string;
|
|
15
|
+
/** The API call this mock targets */
|
|
16
|
+
apiCallId: string;
|
|
17
|
+
/** URL or endpoint to mock */
|
|
18
|
+
url: string;
|
|
19
|
+
/** HTTP method */
|
|
20
|
+
method: string;
|
|
21
|
+
/** Recommended mock library */
|
|
22
|
+
mockLibrary: 'msw' | 'jest.fn' | 'sinon' | 'fetch-mock' | 'undici' | 'nock';
|
|
23
|
+
/** Whether to inline the mock or extract to separate file */
|
|
24
|
+
extractionRecommendation: 'inline' | 'extracted' | 'shared';
|
|
25
|
+
/** Rationale for recommendations */
|
|
26
|
+
rationale: string;
|
|
27
|
+
/** Suggested mock file path (if extracted) */
|
|
28
|
+
suggestedFilePath?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Mock library detected in the codebase
|
|
32
|
+
*/
|
|
33
|
+
export interface MockLibrary {
|
|
34
|
+
/** Library name */
|
|
35
|
+
name: 'msw' | 'jest.fn' | 'sinon' | 'fetch-mock' | 'undici' | 'nock';
|
|
36
|
+
/** Version if detectable */
|
|
37
|
+
version?: string;
|
|
38
|
+
/** File where it was detected */
|
|
39
|
+
sourceFile?: string;
|
|
40
|
+
/** Whether it's configured/initialized */
|
|
41
|
+
isConfigured: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Configuration for mock target analysis
|
|
45
|
+
*/
|
|
46
|
+
export interface MockTargetAnalysisConfig {
|
|
47
|
+
/** Preferred mock library (if multiple available) */
|
|
48
|
+
preferredLibrary?: MockLibrary['name'];
|
|
49
|
+
/** Maximum inline mock complexity */
|
|
50
|
+
maxInlineComplexity?: number;
|
|
51
|
+
/** Shared mocks directory */
|
|
52
|
+
sharedMocksDir?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Detect available mock libraries from package.json dependencies
|
|
56
|
+
*/
|
|
57
|
+
export declare function detectMockLibraries(packageJson: Record<string, unknown>): MockLibrary[];
|
|
58
|
+
/**
|
|
59
|
+
* Analyze code files to see which mock libraries are actually used
|
|
60
|
+
*/
|
|
61
|
+
export declare function analyzeMockLibraryUsage(files: {
|
|
62
|
+
path: string;
|
|
63
|
+
content: string;
|
|
64
|
+
}[]): MockLibrary[];
|
|
65
|
+
/**
|
|
66
|
+
* Determine the best mock library for a given API call
|
|
67
|
+
*/
|
|
68
|
+
export declare function selectMockLibrary(apiCall: ApiCallInfo, availableLibraries: MockLibrary[], config?: MockTargetAnalysisConfig): MockLibrary['name'];
|
|
69
|
+
/**
|
|
70
|
+
* Decide whether to inline or extract a mock
|
|
71
|
+
*/
|
|
72
|
+
export declare function decideMockExtraction(apiCall: ApiCallInfo, existingMocks: string[]): 'inline' | 'extracted' | 'shared';
|
|
73
|
+
/**
|
|
74
|
+
* Generate suggested file path for extracted mock
|
|
75
|
+
*/
|
|
76
|
+
export declare function suggestMockFilePath(apiCall: ApiCallInfo, baseDir?: string): string;
|
|
77
|
+
/**
|
|
78
|
+
* Main function to analyze mock targets
|
|
79
|
+
*/
|
|
80
|
+
export declare function analyzeMockTargets(apiCalls: ApiCallInfo[], options?: {
|
|
81
|
+
packageJson?: Record<string, unknown>;
|
|
82
|
+
codebaseFiles?: {
|
|
83
|
+
path: string;
|
|
84
|
+
content: string;
|
|
85
|
+
}[];
|
|
86
|
+
config?: MockTargetAnalysisConfig;
|
|
87
|
+
}): MockTarget[];
|
|
88
|
+
/**
|
|
89
|
+
* Group mock targets by recommended approach
|
|
90
|
+
*/
|
|
91
|
+
export declare function groupMockTargetsByApproach(targets: MockTarget[]): Map<string, MockTarget[]>;
|
|
92
|
+
//# sourceMappingURL=target-analyzer.d.ts.map
|