@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.
Files changed (150) hide show
  1. package/README.md +250 -0
  2. package/dist/analyzer/mocks/detector.d.ts +59 -0
  3. package/dist/analyzer/mocks/detector.d.ts.map +1 -0
  4. package/dist/analyzer/mocks/detector.js +264 -0
  5. package/dist/analyzer/mocks/detector.js.map +1 -0
  6. package/dist/analyzer/mocks/target-analyzer.d.ts +92 -0
  7. package/dist/analyzer/mocks/target-analyzer.d.ts.map +1 -0
  8. package/dist/analyzer/mocks/target-analyzer.js +305 -0
  9. package/dist/analyzer/mocks/target-analyzer.js.map +1 -0
  10. package/dist/analyzer/visual/element-analyzer.d.ts +44 -0
  11. package/dist/analyzer/visual/element-analyzer.d.ts.map +1 -0
  12. package/dist/analyzer/visual/element-analyzer.js +176 -0
  13. package/dist/analyzer/visual/element-analyzer.js.map +1 -0
  14. package/dist/analyzer/visual/inspector.d.ts +49 -0
  15. package/dist/analyzer/visual/inspector.d.ts.map +1 -0
  16. package/dist/analyzer/visual/inspector.js +109 -0
  17. package/dist/analyzer/visual/inspector.js.map +1 -0
  18. package/dist/cli/commands/generate.d.ts +13 -0
  19. package/dist/cli/commands/generate.d.ts.map +1 -0
  20. package/dist/cli/commands/generate.js +417 -0
  21. package/dist/cli/commands/generate.js.map +1 -0
  22. package/dist/core/generator.d.ts +32 -0
  23. package/dist/core/generator.d.ts.map +1 -0
  24. package/dist/core/generator.js +173 -0
  25. package/dist/core/generator.js.map +1 -0
  26. package/dist/core/js-parser.d.ts +48 -0
  27. package/dist/core/js-parser.d.ts.map +1 -0
  28. package/dist/core/js-parser.js +244 -0
  29. package/dist/core/js-parser.js.map +1 -0
  30. package/dist/core/mock-intelligence.d.ts +14 -0
  31. package/dist/core/mock-intelligence.d.ts.map +1 -0
  32. package/dist/core/mock-intelligence.js +140 -0
  33. package/dist/core/mock-intelligence.js.map +1 -0
  34. package/dist/core/orchestrator.d.ts +49 -0
  35. package/dist/core/orchestrator.d.ts.map +1 -0
  36. package/dist/core/orchestrator.js +315 -0
  37. package/dist/core/orchestrator.js.map +1 -0
  38. package/dist/core/parser.d.ts +9 -0
  39. package/dist/core/parser.d.ts.map +1 -0
  40. package/dist/core/parser.js +120 -0
  41. package/dist/core/parser.js.map +1 -0
  42. package/dist/core/recording-intelligence.d.ts +15 -0
  43. package/dist/core/recording-intelligence.d.ts.map +1 -0
  44. package/dist/core/recording-intelligence.js +178 -0
  45. package/dist/core/recording-intelligence.js.map +1 -0
  46. package/dist/core/resolver.d.ts +58 -0
  47. package/dist/core/resolver.d.ts.map +1 -0
  48. package/dist/core/resolver.js +291 -0
  49. package/dist/core/resolver.js.map +1 -0
  50. package/dist/core/scanner.d.ts +51 -0
  51. package/dist/core/scanner.d.ts.map +1 -0
  52. package/dist/core/scanner.js +310 -0
  53. package/dist/core/scanner.js.map +1 -0
  54. package/dist/core/scorer.d.ts +8 -0
  55. package/dist/core/scorer.d.ts.map +1 -0
  56. package/dist/core/scorer.js +76 -0
  57. package/dist/core/scorer.js.map +1 -0
  58. package/dist/core/validator.d.ts +134 -0
  59. package/dist/core/validator.d.ts.map +1 -0
  60. package/dist/core/validator.js +44 -0
  61. package/dist/core/validator.js.map +1 -0
  62. package/dist/core/verifier.d.ts +10 -0
  63. package/dist/core/verifier.d.ts.map +1 -0
  64. package/dist/core/verifier.js +30 -0
  65. package/dist/core/verifier.js.map +1 -0
  66. package/dist/core/writer.d.ts +15 -0
  67. package/dist/core/writer.d.ts.map +1 -0
  68. package/dist/core/writer.js +43 -0
  69. package/dist/core/writer.js.map +1 -0
  70. package/dist/generator/mocks/builder.d.ts +47 -0
  71. package/dist/generator/mocks/builder.d.ts.map +1 -0
  72. package/dist/generator/mocks/builder.js +335 -0
  73. package/dist/generator/mocks/builder.js.map +1 -0
  74. package/dist/generator/transforms/dialog-transform.d.ts +35 -0
  75. package/dist/generator/transforms/dialog-transform.d.ts.map +1 -0
  76. package/dist/generator/transforms/dialog-transform.js +293 -0
  77. package/dist/generator/transforms/dialog-transform.js.map +1 -0
  78. package/dist/index.d.ts +7 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +18 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/learner/analyzer.d.ts +13 -0
  83. package/dist/learner/analyzer.d.ts.map +1 -0
  84. package/dist/learner/analyzer.js +484 -0
  85. package/dist/learner/analyzer.js.map +1 -0
  86. package/dist/learner/index.d.ts +66 -0
  87. package/dist/learner/index.d.ts.map +1 -0
  88. package/dist/learner/index.js +247 -0
  89. package/dist/learner/index.js.map +1 -0
  90. package/dist/learner/storage.d.ts +68 -0
  91. package/dist/learner/storage.d.ts.map +1 -0
  92. package/dist/learner/storage.js +201 -0
  93. package/dist/learner/storage.js.map +1 -0
  94. package/dist/learner/types.d.ts +41 -0
  95. package/dist/learner/types.d.ts.map +1 -0
  96. package/dist/learner/types.js +31 -0
  97. package/dist/learner/types.js.map +1 -0
  98. package/dist/parser/recorder-parser.d.ts +40 -0
  99. package/dist/parser/recorder-parser.d.ts.map +1 -0
  100. package/dist/parser/recorder-parser.js +139 -0
  101. package/dist/parser/recorder-parser.js.map +1 -0
  102. package/dist/parser/steps/deduplicator.d.ts +19 -0
  103. package/dist/parser/steps/deduplicator.d.ts.map +1 -0
  104. package/dist/parser/steps/deduplicator.js +75 -0
  105. package/dist/parser/steps/deduplicator.js.map +1 -0
  106. package/dist/parser/steps/dialog-detector.d.ts +38 -0
  107. package/dist/parser/steps/dialog-detector.d.ts.map +1 -0
  108. package/dist/parser/steps/dialog-detector.js +290 -0
  109. package/dist/parser/steps/dialog-detector.js.map +1 -0
  110. package/dist/parser/steps/noise-filter.d.ts +21 -0
  111. package/dist/parser/steps/noise-filter.d.ts.map +1 -0
  112. package/dist/parser/steps/noise-filter.js +138 -0
  113. package/dist/parser/steps/noise-filter.js.map +1 -0
  114. package/dist/scorer/index.d.ts +43 -0
  115. package/dist/scorer/index.d.ts.map +1 -0
  116. package/dist/scorer/index.js +82 -0
  117. package/dist/scorer/index.js.map +1 -0
  118. package/dist/scorer/post-verify.d.ts +17 -0
  119. package/dist/scorer/post-verify.d.ts.map +1 -0
  120. package/dist/scorer/post-verify.js +163 -0
  121. package/dist/scorer/post-verify.js.map +1 -0
  122. package/dist/scorer/pre-audit.d.ts +32 -0
  123. package/dist/scorer/pre-audit.d.ts.map +1 -0
  124. package/dist/scorer/pre-audit.js +99 -0
  125. package/dist/scorer/pre-audit.js.map +1 -0
  126. package/dist/scorer/quality-gates.d.ts +17 -0
  127. package/dist/scorer/quality-gates.d.ts.map +1 -0
  128. package/dist/scorer/quality-gates.js +304 -0
  129. package/dist/scorer/quality-gates.js.map +1 -0
  130. package/dist/scorer/types.d.ts +27 -0
  131. package/dist/scorer/types.d.ts.map +1 -0
  132. package/dist/scorer/types.js +5 -0
  133. package/dist/scorer/types.js.map +1 -0
  134. package/dist/templates/test-template.d.ts +21 -0
  135. package/dist/templates/test-template.d.ts.map +1 -0
  136. package/dist/templates/test-template.js +92 -0
  137. package/dist/templates/test-template.js.map +1 -0
  138. package/dist/types/conventions.d.ts +49 -0
  139. package/dist/types/conventions.d.ts.map +1 -0
  140. package/dist/types/conventions.js +13 -0
  141. package/dist/types/conventions.js.map +1 -0
  142. package/dist/types/recording.d.ts +143 -0
  143. package/dist/types/recording.d.ts.map +1 -0
  144. package/dist/types/recording.js +5 -0
  145. package/dist/types/recording.js.map +1 -0
  146. package/dist/types/score.d.ts +18 -0
  147. package/dist/types/score.d.ts.map +1 -0
  148. package/dist/types/score.js +2 -0
  149. package/dist/types/score.js.map +1 -0
  150. 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