@postman/postman-plugin 0.4.0 → 0.4.1-rc.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.
@@ -1,7 +1,7 @@
1
1
  # Import Report — <TEST PLAN NAME>
2
2
 
3
- > Generated by `jmx_to_postman.py`; sections below are auto-filled. After resolving
4
- > manual tasks, update the "Needs manual work" list and the summary counts.
3
+ > Fill in every section from the import. Once the manual tasks are resolved, update the
4
+ > "Needs manual work" list and the summary counts to match the collection you hand off.
5
5
 
6
6
  ## Summary
7
7
 
@@ -1,97 +1,95 @@
1
1
  # JMeter → Postman mapping table
2
2
 
3
- Fidelity: **exact** (behaviour preserved) · **approx** (closest supported equivalent, reported) · **manual** (needs the model/user) · **unsupported** (dropped, reported).
4
- Owner: **script** (deterministic Python) · **model** (judgment call).
3
+ Fidelity: **exact** (behaviour preserved) · **approx** (closest supported equivalent, reported) · **manual** (needs a judgment call, often with the user) · **unsupported** (dropped, reported).
5
4
 
6
5
  ## Structure
7
6
 
8
- | JMeter element | XML tag | Postman | Fidelity | Owner |
9
- |---|---|---|---|---|
10
- | Test Plan | `TestPlan` | Collection (`info` + `item`) | exact | script |
11
- | Thread Group | `ThreadGroup` | load config (VUs/duration/profile) + folder when >1 | approx | script |
12
- | setUp/tearDown Thread Group | `SetupThreadGroup`/`PostThreadGroup` | folder; suggest `--setup-collection`/`--teardown-collection` | approx | script+model |
13
- | Concurrency Thread Group | `...ConcurrencyThreadGroup` | VUs=`TargetLevel`, duration=`RampUp`+`Hold`, `ramp-up` | approx | script |
14
- | Stepping Thread Group | `kg.apc...SteppingThreadGroup` | VUs=`num_threads`, duration=`flighttime`, `ramp-up` | approx | script |
15
- | Ultimate Thread Group | `kg.apc...UltimateThreadGroup` | VUs=max row concurrency, duration=sum of row times | approx | script |
16
- | Arrivals Thread Group | `...ArrivalsThreadGroup` | treated like concurrency | approx | script |
17
- | Transaction Controller | `TransactionController` | folder | exact | script |
18
- | Simple/Generic Controller | `GenericController`/`SimpleController` | folder | exact | script |
19
- | Loop Controller | `LoopController` | flattened; loop count → duration | approx | script |
20
- | If Controller | `IfController` | each enclosed request gets a pre-request skip guard (`pm.execution.skipRequest`); `${var}` → `pm.variables.get(...)` | approx | script |
21
- | While Controller | `WhileController` | flattened, requests run unconditionally; restore looping with `pm.execution.setNextRequest` | approx | model |
22
- | Recording Controller | `RecordingController` | transparent (children recursed) | exact | script |
23
- | Module / Include Controller | `ModuleController`/`IncludeController` | flattened children if inline; external refs reported | manual | model |
7
+ | JMeter element | XML tag | Postman | Fidelity |
8
+ |---|---|---|---|
9
+ | Test Plan | `TestPlan` | Collection (`info` + `item`) | exact |
10
+ | Thread Group | `ThreadGroup` | load config (VUs/duration/profile) + folder when >1 | approx |
11
+ | setUp/tearDown Thread Group | `SetupThreadGroup`/`PostThreadGroup` | folder; suggest `--setup-collection`/`--teardown-collection` | approx |
12
+ | Concurrency Thread Group | `...ConcurrencyThreadGroup` | VUs=`TargetLevel`, duration=`RampUp`+`Hold`, `ramp-up` | approx |
13
+ | Stepping Thread Group | `kg.apc...SteppingThreadGroup` | VUs=`num_threads`, duration=`flighttime`, `ramp-up` | approx |
14
+ | Ultimate Thread Group | `kg.apc...UltimateThreadGroup` | VUs=max row concurrency, duration=sum of row times | approx |
15
+ | Arrivals Thread Group | `...ArrivalsThreadGroup` | treated like concurrency | approx |
16
+ | Transaction Controller | `TransactionController` | folder | exact |
17
+ | Simple/Generic Controller | `GenericController`/`SimpleController` | folder | exact |
18
+ | Loop Controller | `LoopController` | flattened; loop count → duration | approx |
19
+ | If Controller | `IfController` | each enclosed request gets a pre-request skip guard (`pm.execution.skipRequest`); `${var}` → `pm.variables.get(...)` | approx |
20
+ | While Controller | `WhileController` | flattened, requests run unconditionally; restore looping with `pm.execution.setNextRequest` | approx |
21
+ | Recording Controller | `RecordingController` | transparent (children recursed) | exact |
22
+ | Module / Include Controller | `ModuleController`/`IncludeController` | flattened children if inline; external refs reported | manual |
24
23
 
25
24
  ## Requests
26
25
 
27
- | JMeter | XML | Postman | Fidelity | Owner |
28
- |---|---|---|---|---|
29
- | HTTP Request | `HTTPSamplerProxy` | request item | exact | script |
30
- | method / path / domain / port / protocol | `HTTPSampler.*` | `request.method` + `url` (blank protocol → `http`) | exact | script |
31
- | Query/body params | `Arguments`/`HTTPArgument` | query (GET) / urlencoded (POST/PUT/PATCH) / formdata (multipart) | exact | script |
32
- | Raw body | `postBodyRaw=true` | `body.mode=raw` | exact | script |
33
- | Header Manager | `HeaderManager` | `request.header` (merged by scope, later wins) | exact | script |
34
- | HTTP Request Defaults | `ConfigTestElement`/`HttpDefaultsGui` | fallback for blank domain/port/protocol/path | exact | script |
35
- | Auth Manager Basic/Digest | `AuthManager` | `request.auth` basic/digest (first entry; multiples reported) | exact | script |
36
- | Auth Manager Kerberos/NTLM | `AuthManager` mechanism | — | unsupported | script |
37
- | Cookie / Cache / DNS Manager | `CookieManager`/... | auto per-VU cookies; presets not transferred | unsupported | script |
38
- | Non-HTTP samplers | `JDBC/FTP/LDAP/Java/TCP/Debug/Dummy...Sampler` | — | unsupported | script (reported) |
26
+ | JMeter | XML | Postman | Fidelity |
27
+ |---|---|---|---|
28
+ | HTTP Request | `HTTPSamplerProxy` | request item | exact |
29
+ | method / path / domain / port / protocol | `HTTPSampler.*` | `request.method` + `url` (blank protocol → `http`) | exact |
30
+ | Query/body params | `Arguments`/`HTTPArgument` | query (GET) / urlencoded (POST/PUT/PATCH) / formdata (multipart) | exact |
31
+ | Raw body | `postBodyRaw=true` | `body.mode=raw` | exact |
32
+ | Header Manager | `HeaderManager` | `request.header` (merged by scope, later wins) | exact |
33
+ | HTTP Request Defaults | `ConfigTestElement`/`HttpDefaultsGui` | fallback for blank domain/port/protocol/path | exact |
34
+ | Auth Manager Basic/Digest | `AuthManager` | `request.auth` basic/digest (first entry; multiples reported) | exact |
35
+ | Auth Manager Kerberos/NTLM | `AuthManager` mechanism | — | unsupported |
36
+ | Cookie / Cache / DNS Manager | `CookieManager`/... | auto per-VU cookies; presets not transferred | unsupported |
37
+ | Non-HTTP samplers | `JDBC/FTP/LDAP/Java/TCP/Debug/Dummy...Sampler` | — | unsupported |
39
38
 
40
39
  ## Extractors (post-response → test script `pm.collectionVariables.set`)
41
40
 
42
- | JMeter | XML | Fidelity | Owner |
43
- |---|---|---|---|
44
- | Regular Expression Extractor | `RegexExtractor` | exact (via `new RegExp`, group from `$n$` template) | script |
45
- | Boundary Extractor | `BoundaryExtractor` | exact (left/right → non-greedy regex) | script |
46
- | JSON Extractor (native) | `JSONPostProcessor` | exact for dot/index paths; complex → manual | script→model |
47
- | JSON Path Extractor (plugin) | `...JSONPathExtractor` | exact for simple paths | script→model |
48
- | XPath / XPath2 Extractor | `XPathExtractor`/`XPath2Extractor` | manual (no sandbox XPath) | model |
49
- | CSS/HTML Extractor | `HtmlExtractor` | manual (no sandbox CSS) | model |
41
+ | JMeter | XML | Fidelity |
42
+ |---|---|---|
43
+ | Regular Expression Extractor | `RegexExtractor` | exact (via `new RegExp`, group from `$n$` template) |
44
+ | Boundary Extractor | `BoundaryExtractor` | exact (left/right → non-greedy regex) |
45
+ | JSON Extractor (native) | `JSONPostProcessor` | exact for dot/index paths; complex → manual |
46
+ | JSON Path Extractor (plugin) | `...JSONPathExtractor` | exact for simple paths |
47
+ | XPath / XPath2 Extractor | `XPathExtractor`/`XPath2Extractor` | manual (no sandbox XPath) |
48
+ | CSS/HTML Extractor | `HtmlExtractor` | manual (no sandbox CSS) |
50
49
 
51
50
  ## Assertions (→ `pm.test`)
52
51
 
53
- | JMeter | XML | Maps to | Fidelity | Owner |
54
- |---|---|---|---|---|
55
- | Response Assertion | `ResponseAssertion` | status / contains / equals / matches, honouring `test_type` bitmask + NOT | exact | script |
56
- | — test_field response_code | | `pm.response.code` | exact | script |
57
- | — test_field response_data | | `pm.response.text()` | exact | script |
58
- | — test_field headers/message | | `pm.response.headers`/`.status` | exact | script |
59
- | Duration Assertion | `DurationAssertion` | `pm.test` responseTime + `--pass-if less_than(p95, ms)` hint | exact | script |
60
- | Size Assertion | `SizeAssertion` | `pm.test` on `responseSize` | approx | script |
61
- | JSON Assertion | `JSONPathAssertion` | `pm.test` on simple path | approx | script→model |
52
+ | JMeter | XML | Maps to | Fidelity |
53
+ |---|---|---|---|
54
+ | Response Assertion | `ResponseAssertion` | status / contains / equals / matches, honouring `test_type` bitmask + NOT | exact |
55
+ | — test_field response_code | | `pm.response.code` | exact |
56
+ | — test_field response_data | | `pm.response.text()` | exact |
57
+ | — test_field headers/message | | `pm.response.headers`/`.status` | exact |
58
+ | Duration Assertion | `DurationAssertion` | `pm.test` responseTime + `--pass-if less_than(p95, ms)` hint | exact |
59
+ | Size Assertion | `SizeAssertion` | `pm.test` on `responseSize` | approx |
60
+ | JSON Assertion | `JSONPathAssertion` | `pm.test` on simple path | approx |
62
61
 
63
62
  `ResponseAssertion.test_type` bits: `MATCH=1, CONTAINS=2, NOT=4, EQUALS=8, SUBSTRING=16, OR=32`.
64
63
 
65
64
  ## Variables & data
66
65
 
67
- | JMeter | XML | Postman | Fidelity | Owner |
68
- |---|---|---|---|---|
69
- | User Defined Variables | `Arguments` under TestPlan/scope | collection `variable[]` | exact | script |
70
- | `${var}` references | inline | `{{var}}` | exact | script |
71
- | `${__UUID}` / `${__time}` | function | `{{$guid}}` / `{{$timestamp}}` | exact | script |
72
- | `${__Random(...)}` and other `__fn` | function | `{{$randomInt}}` or flagged reference | approx/manual | script→model |
73
- | CSV Data Set Config | `CSVDataSet` | `--data-file` + column list | approx (one file/run; naive CSV) | script |
74
- | Counter / Random Variable | `CounterConfig`/`RandomVariableConfig` | pre-request script or `{{$randomInt}}` | manual | model |
66
+ | JMeter | XML | Postman | Fidelity |
67
+ |---|---|---|---|
68
+ | User Defined Variables | `Arguments` under TestPlan/scope | collection `variable[]` | exact |
69
+ | `${var}` references | inline | `{{var}}` | exact |
70
+ | `${__UUID}` / `${__time}` | function | `{{$guid}}` / `{{$timestamp}}` | exact |
71
+ | `${__Random(...)}` and other `__fn` | function | `{{$randomInt}}` or flagged reference | approx/manual |
72
+ | CSV Data Set Config | `CSVDataSet` | `--data-file` + column list | approx (one file/run; naive CSV) |
73
+ | Counter / Random Variable | `CounterConfig`/`RandomVariableConfig` | pre-request script or `{{$randomInt}}` | manual |
75
74
 
76
75
  ## Scripting
77
76
 
78
- | JMeter | XML | Postman | Fidelity | Owner |
79
- |---|---|---|---|---|
80
- | JSR223 PreProcessor | `JSR223PreProcessor` | prerequest script (JS, IIFE-wrapped) | model-assisted | script+model |
81
- | JSR223 PostProcessor | `JSR223PostProcessor` | test script | model-assisted | script+model |
82
- | BeanShell Pre/PostProcessor | `BeanShell*Processor` | pre/test script | model-assisted | script+model |
83
- | JSR223 / BeanShell Sampler | `JSR223Sampler`/`BeanShellSampler` | script-only stub (no HTTP) | manual | script+model |
84
-
85
- ## Timing / pacing — approximated, always reported
86
-
87
- Postman has no native think time. A fixed delay is recovered where one exists;
88
- random/throughput/sync shaping is not reproduced.
89
-
90
- | JMeter | XML | Mapping |
91
- |---|---|---|
92
- | Constant Timer (per-request) | `ConstantTimer` under a sampler | pre-request pause of its delay |
93
- | Constant / Uniform / Gaussian / Poisson Timer (group scope) | `ConstantTimer`/`UniformRandomTimer`/... | `--delay-request <ms>` (fixed part only) |
94
- | Constant Throughput Timer | `ConstantThroughputTimer` | not reproduced; reported |
95
- | Throughput Shaping Timer | plugin | not reproduced; reported |
96
- | Synchronizing Timer | `SyncTimer` | not reproduced; reported |
97
- | Test Action (pause/stop) | `TestAction` | dropped; reported |
77
+ | JMeter | XML | Postman | Fidelity |
78
+ |---|---|---|---|
79
+ | JSR223 PreProcessor | `JSR223PreProcessor` | prerequest script (JS, IIFE-wrapped) | manual |
80
+ | JSR223 PostProcessor | `JSR223PostProcessor` | test script | manual |
81
+ | BeanShell Pre/PostProcessor | `BeanShell*Processor` | pre/test script | manual |
82
+ | JSR223 / BeanShell Sampler | `JSR223Sampler`/`BeanShellSampler` | script-only stub (no HTTP) | manual |
83
+
84
+ ## Timing / pacing — not supported, always reported
85
+
86
+ Postman performance runs have no think time and no flag for one. Every timer is
87
+ dropped and listed in the report; none is reproduced.
88
+
89
+ | JMeter | XML |
90
+ |---|---|
91
+ | Constant / Uniform / Gaussian / Poisson Timer | `ConstantTimer`/`UniformRandomTimer`/... |
92
+ | Constant Throughput Timer | `ConstantThroughputTimer` |
93
+ | Throughput Shaping Timer | plugin |
94
+ | Synchronizing Timer | `SyncTimer` |
95
+ | Test Action (pause/stop) | `TestAction` |
@@ -1,6 +1,6 @@
1
1
  # What Postman performance testing supports (the import target)
2
2
 
3
- Grounded in the `postman-cli`, `performance-test-api`, and `postman-performance-test` source — not the marketing docs. This is the ceiling the importer maps onto. When JMeter asks for something not here, it is reported, never faked.
3
+ Grounded in the `postman-cli`, `performance-test-api`, and `postman-performance-test` source — not the marketing docs. This is the ceiling an import maps onto. When JMeter asks for something not here, it is reported, never faked.
4
4
 
5
5
  ## `postman performance run` CLI flags that matter for import
6
6
 
@@ -26,7 +26,7 @@ Four named shapes only, each derived from `(vu-count, duration)`:
26
26
  - `spike` — low baseline, one spike to full mid-run, back down.
27
27
  - `peak` — baseline, ramp up, hold, ramp down.
28
28
 
29
- JMeter custom ramp curves, stepping schedules, and arrival-rate shapes **cannot be reproduced**. Pick the closest of the four and report the approximation. Rule used by the importer: any ramp/stepping/concurrency/ultimate/arrivals group → `ramp-up`; a flat group with zero ramp → `fixed`.
29
+ JMeter custom ramp curves, stepping schedules, and arrival-rate shapes **cannot be reproduced**. Pick the closest of the four and report the approximation. Rule: any ramp/stepping/concurrency/ultimate/arrivals group → `ramp-up`; a flat group with zero ramp → `fixed`.
30
30
 
31
31
  ## `--pass-if` metrics and operators
32
32
 
@@ -52,5 +52,5 @@ JMeter custom ramp curves, stepping schedules, and arrival-rate shapes **cannot
52
52
  1. One collection, repeated across VUs. **No concurrent thread groups.**
53
53
  2. Time-based, not iteration-based. **Loop counts → duration.**
54
54
  3. One `--pass-if`.
55
- 4. **No native think time** — timers are approximated (`--delay-request` for group scope, a pre-request pause for per-request), not reproduced exactly.
55
+ 4. **No think time** — timers are not reproduced; each one is reported.
56
56
  5. **HTTP(S) only.**
@@ -1,6 +1,6 @@
1
1
  # Translating JSR223 / BeanShell scripts to Postman JavaScript
2
2
 
3
- `scripts/groovy_to_js.py` does a best-effort mechanical pass and the importer wraps every result in an IIFE so it is always syntactically valid. It is **never trusted** — each imported script is flagged as a manual task. This file is how you (the model) finish the job.
3
+ Every JSR223/BeanShell script is translated by hand and treated as a manual task until it's checked. Wrap each translation in an IIFE (`(function () { ... })();`) so a leftover `return` stays legal.
4
4
 
5
5
  ## Execution-context map
6
6
 
@@ -23,7 +23,7 @@ JMeter runs these inside a sampler's processor. Postman runs them as a request's
23
23
  | `UUID.randomUUID().toString()` | `require('uuid').v4()` | both |
24
24
  | `Integer.parseInt(x)` | `parseInt(x)` | both |
25
25
 
26
- ## What the mechanical pass cannot do — fix these by hand
26
+ ## What doesn't map line by line
27
27
 
28
28
  - **BeanShell implicit result vars** (`ResponseCode`, `ResponseMessage`, `IsSuccess`, `bsh.args`, `Parameters`). No pm.* equivalent in a processor. For a sampler that sets `IsSuccess=false`, express it as a `pm.test(...)` that fails. For `return X`, delete the return (the IIFE makes it legal, but it's a no-op).
29
29
  - **Groovy closures / collection methods** (`.each {}`, `.collect {}`, `->`). Rewrite as `for...of` / `.map()` / arrow functions.
@@ -33,11 +33,10 @@ JMeter runs these inside a sampler's processor. Postman runs them as a request's
33
33
 
34
34
  ## Procedure
35
35
 
36
- 1. Read the `// --- imported ... (REVIEW) ---` block in the request's event script.
37
- 2. Compare against the original `<script>` text in the `.jmx` (the IR keeps the element's name).
38
- 3. Rewrite line by line using the table. Preserve variable names so `{{var}}` references elsewhere still resolve.
39
- 4. Remove the `// REVIEW` marker once correct.
40
- 5. Re-run the node syntax gate from `SKILL.md` step 3.
36
+ 1. Read the original `<script>` text in the `.jmx` (note the element's name, so the report can point at it).
37
+ 2. Rewrite line by line using the table. Preserve variable names so `{{var}}` references elsewhere still resolve.
38
+ 3. Mark anything you're unsure of with a `// REVIEW` comment, and remove it once the logic is confirmed.
39
+ 4. Run the syntax check from `README.md` step 4.
41
40
 
42
41
  ## Example
43
42
 
@@ -57,9 +56,9 @@ pm.collectionVariables.set("authToken", token);
57
56
  console.log("got " + token);
58
57
  ```
59
58
 
60
- ## Canonical JS templates (match the script byte-for-byte in fallback mode)
59
+ ## Canonical JS templates
61
60
 
62
- When importing by hand, emit exactly these so hand output equals the script's. `<default>` empty-string if JMeter has none. Source is `pm.response.text()` normally, or `pm.response.headers.toString()` when the extractor's `useHeaders` is true.
61
+ Emit exactly these so every import produces the same code. `<default>` empty-string if JMeter has none. Source is `pm.response.text()` normally, or `pm.response.headers.toString()` when the extractor's `useHeaders` is true.
63
62
 
64
63
  **Regex extractor** (`$n$` template → group `n`):
65
64
  ```javascript
@@ -1,83 +0,0 @@
1
- """Best-effort, deterministic Groovy / BeanShell -> JavaScript translation.
2
-
3
- This handles the common mechanical rewrites so the model only has to review and
4
- fix the genuinely ambiguous parts. Every script that passes through here is ALSO
5
- flagged as a manual task, because no regex pass can guarantee semantic fidelity
6
- for arbitrary JSR223/BeanShell code.
7
-
8
- Keep the rules ordered from most specific to least specific. The model may extend
9
- this table; see references/script-translation.md for the patterns and rationale.
10
- """
11
- import re
12
-
13
- # (pattern, replacement) applied in order. Patterns are plain regex on source text.
14
- RULES = [
15
- # --- JMeter bound variables -> pm equivalents ---
16
- # vars["x"] / vars['x'] -> pm.variables.get("x") (bracket form, must precede .get/.put rules)
17
- (r'\bvars\[\s*([\'"][^\'"]*[\'"])\s*\]', r'pm.variables.get(\1)'),
18
- (r'\bprops\[\s*([\'"][^\'"]*[\'"])\s*\]', r'pm.variables.get(\1)'),
19
- (r'\bvars\.get\(', 'pm.variables.get('),
20
- (r'\bvars\.put\(', 'pm.variables.set('),
21
- (r'\bvars\.getObject\(', 'pm.variables.get('),
22
- (r'\bvars\.putObject\(', 'pm.variables.set('),
23
- (r'\bprops\.get\(', 'pm.variables.get('),
24
- (r'\bprops\.put\(', 'pm.variables.set('),
25
-
26
- # --- logging ---
27
- (r'\blog\.info\(', 'console.log('),
28
- (r'\blog\.warn\(', 'console.warn('),
29
- (r'\blog\.error\(', 'console.error('),
30
- (r'\blog\.debug\(', 'console.log('),
31
-
32
- # --- previous-sample result accessors (post-processor context) ---
33
- (r'\bprev\.getResponseDataAsString\(\)', 'pm.response.text()'),
34
- (r'\bprev\.getResponseCode\(\)', 'String(pm.response.code)'),
35
- (r'\bprev\.getResponseMessage\(\)', 'pm.response.status'),
36
- (r'\bprev\.getResponseHeaders\(\)', 'pm.response.headers.toString()'),
37
- (r'\bprev\.getTime\(\)', 'pm.response.responseTime'),
38
- (r'\bctx\.getPreviousResult\(\)', 'pm.response'),
39
-
40
- # --- sampler mutation (pre-processor context) ---
41
- (r'\bsampler\.getUrl\(\)', 'pm.request.url.toString()'),
42
- (r'\bsampler\.addArgument\(', '// [manual] sampler.addArgument -> set a query/body param; '),
43
-
44
- # --- JDK calls that have JS equivalents ---
45
- (r'\bSystem\.currentTimeMillis\(\)', 'Date.now()'),
46
- (r'\bUUID\.randomUUID\(\)\.toString\(\)', 'require("uuid").v4()'),
47
- (r'\bInteger\.parseInt\(', 'parseInt('),
48
- (r'\bLong\.parseLong\(', 'parseInt('),
49
- (r'\bDouble\.parseDouble\(', 'parseFloat('),
50
- (r'\bString\.valueOf\(', 'String('),
51
- (r'\bMath\.', 'Math.'), # identity, keeps it explicit
52
-
53
- # --- declarations: Groovy def / typed decls -> let ---
54
- (r'\bdef\s+', 'let '),
55
- (r'\b(?:String|int|long|double|float|boolean|Object|var)\s+(\w+\s*=)', r'let \1'),
56
-
57
- # --- Groovy GString interpolation "${x}" stays valid-ish; leave for model ---
58
- ]
59
-
60
- # Tokens that, if present after translation, almost certainly need human review.
61
- REVIEW_MARKERS = [
62
- 'import ', 'new ', 'class ', '.each', '.collect', '->', 'bsh.', 'ctx.',
63
- 'OUT.', 'SampleResult', 'Thread.sleep', 'try {', 'catch', '@', '::',
64
- ]
65
-
66
-
67
- def translate(source: str, lang: str = 'groovy') -> dict:
68
- """Return {'js': <translated>, 'needs_review': bool, 'reasons': [...]}."""
69
- if source is None:
70
- return {'js': '', 'needs_review': False, 'reasons': []}
71
- js = source
72
- for pat, repl in RULES:
73
- js = re.sub(pat, repl, js)
74
-
75
- # BeanShell implicit result vars have no clean pm.* mapping.
76
- reasons = []
77
- if lang and 'beanshell' in lang.lower():
78
- reasons.append('BeanShell script: implicit vars (ResponseCode, IsSuccess, bsh.args) have no pm.* equivalent')
79
- for marker in REVIEW_MARKERS:
80
- if marker in js:
81
- reasons.append(f'contains "{marker.strip()}" which needs manual translation')
82
- needs_review = True # always true: no mechanical pass is trustworthy for scripts
83
- return {'js': js, 'needs_review': needs_review, 'reasons': sorted(set(reasons))}