qase-javascript-commons 2.5.4 → 2.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.HttpInterceptor = void 0;
7
+ exports.extractRequestInfo = extractRequestInfo;
8
+ exports.headersToRecord = headersToRecord;
9
+ exports.buildRequestStep = buildRequestStep;
10
+ const node_http_1 = __importDefault(require("node:http"));
11
+ const node_https_1 = __importDefault(require("node:https"));
12
+ const uuid_1 = require("uuid");
13
+ const test_step_1 = require("../models/test-step");
14
+ const step_execution_1 = require("../models/step-execution");
15
+ /**
16
+ * Handles the 3 `http.request()` call signatures:
17
+ * - (url: string, options?, callback?)
18
+ * - (url: URL, options?, callback?)
19
+ * - (options: RequestOptions, callback?)
20
+ *
21
+ * Returns `{ method, url }`. Default method is 'GET'.
22
+ */
23
+ function extractRequestInfo(args) {
24
+ const first = args[0];
25
+ if (typeof first === 'string') {
26
+ // Signature: (url: string, options?, callback?)
27
+ const options = args[1];
28
+ const method = options && typeof options === 'object' && !('emit' in options) && 'method' in options && typeof options.method === 'string'
29
+ ? options.method
30
+ : 'GET';
31
+ return { method, url: first };
32
+ }
33
+ if (first instanceof URL) {
34
+ // Signature: (url: URL, options?, callback?)
35
+ const options = args[1];
36
+ const method = options && typeof options === 'object' && !('emit' in options) && 'method' in options && typeof options.method === 'string'
37
+ ? options.method
38
+ : 'GET';
39
+ return { method, url: first.toString() };
40
+ }
41
+ // Signature: (options: RequestOptions, callback?)
42
+ const opts = first;
43
+ const method = opts.method ?? 'GET';
44
+ const protocol = opts.protocol ?? 'http:';
45
+ const hostname = opts.hostname ?? opts.host ?? 'localhost';
46
+ const port = opts.port !== undefined ? `:${String(opts.port)}` : '';
47
+ const path = opts.path ?? '/';
48
+ const url = `${protocol}//${hostname}${port}${path}`;
49
+ return { method, url };
50
+ }
51
+ // ─── Utility: headersToRecord ─────────────────────────────────────────────────
52
+ /**
53
+ * Converts `http.IncomingHttpHeaders` (which has `string | string[] | undefined` values)
54
+ * to `Record<string, string>`. Multi-value headers joined with `', '`. Undefined values skipped.
55
+ */
56
+ function headersToRecord(headers) {
57
+ const result = {};
58
+ for (const [key, value] of Object.entries(headers)) {
59
+ if (value === undefined)
60
+ continue;
61
+ if (Array.isArray(value)) {
62
+ result[key] = value.join(', ');
63
+ }
64
+ else {
65
+ result[key] = value;
66
+ }
67
+ }
68
+ return result;
69
+ }
70
+ // ─── Utility: buildRequestStep ───────────────────────────────────────────────
71
+ function buildRequestStep(params) {
72
+ const step = new test_step_1.TestStepType(test_step_1.StepType.REQUEST);
73
+ step.id = (0, uuid_1.v4)();
74
+ const data = step.data;
75
+ data.request_method = params.method;
76
+ data.request_url = params.url;
77
+ data.request_headers = null;
78
+ data.request_body = null;
79
+ data.status_code = params.statusCode;
80
+ data.response_body = params.responseBody;
81
+ data.response_headers = params.responseHeaders;
82
+ step.execution.start_time = params.startTime;
83
+ step.execution.end_time = params.endTime;
84
+ step.execution.duration = params.endTime - params.startTime;
85
+ step.execution.status = params.statusCode >= 400 ? step_execution_1.StepStatusEnum.failed : step_execution_1.StepStatusEnum.passed;
86
+ return step;
87
+ }
88
+ // ─── HttpInterceptor ─────────────────────────────────────────────────────────
89
+ /**
90
+ * HttpInterceptor monkey-patches Node.js http/https module functions to capture
91
+ * outgoing requests as REQUEST-type steps in the AsyncLocalStorage store.
92
+ *
93
+ * Pattern:
94
+ * 1. `install()` — saves originals, replaces with wrappers
95
+ * 2. `uninstall()` — restores originals
96
+ */
97
+ class HttpInterceptor {
98
+ store;
99
+ profiler;
100
+ origHttpRequest = null;
101
+ origHttpGet = null;
102
+ origHttpsRequest = null;
103
+ origHttpsGet = null;
104
+ constructor(store, profiler) {
105
+ this.store = store;
106
+ this.profiler = profiler;
107
+ }
108
+ install() {
109
+ this.origHttpRequest = node_http_1.default.request;
110
+ this.origHttpGet = node_http_1.default.get;
111
+ this.origHttpsRequest = node_https_1.default.request;
112
+ this.origHttpsGet = node_https_1.default.get;
113
+ const wrappedHttpRequest = this.wrapRequestFn(this.origHttpRequest);
114
+ const wrappedHttpGet = this.wrapRequestFn(this.origHttpGet);
115
+ const wrappedHttpsRequest = this.wrapRequestFn(this.origHttpsRequest);
116
+ const wrappedHttpsGet = this.wrapRequestFn(this.origHttpsGet);
117
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
118
+ node_http_1.default.request = wrappedHttpRequest;
119
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
120
+ node_http_1.default.get = wrappedHttpGet;
121
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
122
+ node_https_1.default.request = wrappedHttpsRequest;
123
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
124
+ node_https_1.default.get = wrappedHttpsGet;
125
+ }
126
+ uninstall() {
127
+ if (this.origHttpRequest !== null) {
128
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
129
+ node_http_1.default.request = this.origHttpRequest;
130
+ this.origHttpRequest = null;
131
+ }
132
+ if (this.origHttpGet !== null) {
133
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
134
+ node_http_1.default.get = this.origHttpGet;
135
+ this.origHttpGet = null;
136
+ }
137
+ if (this.origHttpsRequest !== null) {
138
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
139
+ node_https_1.default.request = this.origHttpsRequest;
140
+ this.origHttpsRequest = null;
141
+ }
142
+ if (this.origHttpsGet !== null) {
143
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
144
+ node_https_1.default.get = this.origHttpsGet;
145
+ this.origHttpsGet = null;
146
+ }
147
+ }
148
+ /**
149
+ * Creates a wrapper around a http.request-like function.
150
+ * The wrapper:
151
+ * 1. Calls the original function to get the ClientRequest
152
+ * 2. Checks if there's an active ALS context — if not, returns unchanged
153
+ * 3. Checks shouldSkip — if true, returns unchanged
154
+ * 4. Intercepts the 'response' event via req.emit override
155
+ * 5. Builds a TestStepType on response end and pushes to accumulator
156
+ */
157
+ wrapRequestFn(
158
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
159
+ origFn) {
160
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
161
+ const self = this;
162
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
163
+ return function (...args) {
164
+ // Call original first to get the ClientRequest
165
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
166
+ const req = origFn.apply(this, args);
167
+ // Extract request info
168
+ let reqInfo;
169
+ try {
170
+ reqInfo = extractRequestInfo(args);
171
+ }
172
+ catch {
173
+ return req;
174
+ }
175
+ // Check if this domain should be skipped
176
+ if (self.profiler.shouldSkip(reqInfo.url)) {
177
+ return req;
178
+ }
179
+ const startTime = Date.now();
180
+ const origEmit = req.emit.bind(req);
181
+ // Override req.emit to intercept the 'response' event
182
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
183
+ req.emit = function (event, ...emitArgs) {
184
+ if (event === 'response') {
185
+ const res = emitArgs[0];
186
+ try {
187
+ const chunks = [];
188
+ const isError = (res.statusCode ?? 0) >= 400;
189
+ // Register data listener BEFORE calling original emit (Pitfall 2: stream consumption race)
190
+ if (isError && self.profiler.trackOnFail) {
191
+ res.on('data', (chunk) => {
192
+ try {
193
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
194
+ }
195
+ catch {
196
+ // silent failure
197
+ }
198
+ });
199
+ }
200
+ res.on('end', () => {
201
+ try {
202
+ const endTime = Date.now();
203
+ const statusCode = res.statusCode ?? 0;
204
+ const captureBody = statusCode >= 400 && self.profiler.trackOnFail;
205
+ const responseBody = captureBody ? Buffer.concat(chunks).toString('utf8') : null;
206
+ const responseHeaders = statusCode >= 400 ? headersToRecord(res.headers) : null;
207
+ const step = buildRequestStep({
208
+ method: reqInfo.method,
209
+ url: reqInfo.url,
210
+ statusCode,
211
+ responseBody,
212
+ responseHeaders,
213
+ startTime,
214
+ endTime,
215
+ });
216
+ // Push to accumulator — re-fetch in case context is still active
217
+ const acc = self.store.getStore();
218
+ if (acc !== undefined && acc !== null) {
219
+ acc.push(step);
220
+ }
221
+ else {
222
+ // No ALS context — push to fallback accumulator (for Jest/Vitest/WDIO workers)
223
+ self.profiler.fallbackSteps.push(step);
224
+ }
225
+ }
226
+ catch {
227
+ // INTC-06: silent failure — do not propagate to test
228
+ }
229
+ });
230
+ }
231
+ catch {
232
+ // INTC-06: silent failure in setup — do not propagate
233
+ }
234
+ // CRITICAL: Call original emit LAST (after registering data listeners)
235
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
236
+ return origEmit(event, ...emitArgs);
237
+ }
238
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
239
+ return origEmit(event, ...emitArgs);
240
+ };
241
+ return req;
242
+ };
243
+ }
244
+ }
245
+ exports.HttpInterceptor = HttpInterceptor;
@@ -0,0 +1,5 @@
1
+ export { AbstractProfiler } from './abstract-profiler';
2
+ export { NetworkProfiler } from './network-profiler';
3
+ export type { NetworkProfilerOptions } from './network-profiler';
4
+ export { HttpInterceptor } from './http-interceptor';
5
+ export { FetchInterceptor } from './fetch-interceptor';
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FetchInterceptor = exports.HttpInterceptor = exports.NetworkProfiler = exports.AbstractProfiler = void 0;
4
+ var abstract_profiler_1 = require("./abstract-profiler");
5
+ Object.defineProperty(exports, "AbstractProfiler", { enumerable: true, get: function () { return abstract_profiler_1.AbstractProfiler; } });
6
+ var network_profiler_1 = require("./network-profiler");
7
+ Object.defineProperty(exports, "NetworkProfiler", { enumerable: true, get: function () { return network_profiler_1.NetworkProfiler; } });
8
+ var http_interceptor_1 = require("./http-interceptor");
9
+ Object.defineProperty(exports, "HttpInterceptor", { enumerable: true, get: function () { return http_interceptor_1.HttpInterceptor; } });
10
+ var fetch_interceptor_1 = require("./fetch-interceptor");
11
+ Object.defineProperty(exports, "FetchInterceptor", { enumerable: true, get: function () { return fetch_interceptor_1.FetchInterceptor; } });
@@ -0,0 +1,61 @@
1
+ import { AbstractProfiler } from './abstract-profiler';
2
+ import { TestStepType } from '../models';
3
+ export interface NetworkProfilerOptions {
4
+ skipDomains?: string[] | undefined;
5
+ trackOnFail?: boolean | undefined;
6
+ }
7
+ export declare class NetworkProfiler extends AbstractProfiler {
8
+ private readonly skipDomains;
9
+ private readonly _trackOnFail;
10
+ /**
11
+ * Fallback accumulator for steps captured outside any run() context.
12
+ * Used by frameworks where test bodies cannot be wrapped in run() (Jest, Vitest, WDIO).
13
+ * Public so interceptors can push to it via ProfilerRef.
14
+ */
15
+ readonly fallbackSteps: TestStepType[];
16
+ private httpInterceptor;
17
+ private fetchInterceptor;
18
+ constructor(options?: NetworkProfilerOptions);
19
+ get trackOnFail(): boolean;
20
+ shouldSkip(url: string): boolean;
21
+ /**
22
+ * Install HTTP/HTTPS monkey-patches and subscribe to undici diagnostics_channel
23
+ * so that outgoing requests inside a `run()` context are captured as REQUEST steps.
24
+ */
25
+ enable(): void;
26
+ /**
27
+ * No-op per v1.3 design decision (flag-based activation model).
28
+ * Satisfies the AbstractProfiler contract.
29
+ */
30
+ disable(): void;
31
+ /**
32
+ * Uninstall HTTP/HTTPS monkey-patches and unsubscribe from undici diagnostics_channel —
33
+ * permanently removes interception.
34
+ */
35
+ restore(): void;
36
+ /**
37
+ * Wraps `fn` in an AsyncLocalStorage context so that any HTTP requests
38
+ * made inside `fn` are attributed to the step accumulator for this call.
39
+ *
40
+ * Returns `{ result, steps }` where `result` is the return value of `fn`
41
+ * and `steps` is the array of REQUEST steps captured during execution.
42
+ */
43
+ run<T>(fn: () => Promise<T>): Promise<{
44
+ result: T;
45
+ steps: TestStepType[];
46
+ }>;
47
+ /**
48
+ * Returns the current ALS store when called inside a `run()` context.
49
+ * Returns an empty array when called outside (no active context).
50
+ */
51
+ getSteps(): TestStepType[];
52
+ /**
53
+ * Returns a copy of steps accumulated outside any run() context (fallback accumulator).
54
+ * Used by frameworks where test bodies cannot be wrapped in run() (Jest, Vitest, WDIO).
55
+ */
56
+ getAllSteps(): TestStepType[];
57
+ /**
58
+ * Clears the fallback step accumulator. Call after collecting steps per test.
59
+ */
60
+ clearSteps(): void;
61
+ }
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NetworkProfiler = void 0;
4
+ const node_async_hooks_1 = require("node:async_hooks");
5
+ const abstract_profiler_1 = require("./abstract-profiler");
6
+ const http_interceptor_1 = require("./http-interceptor");
7
+ const fetch_interceptor_1 = require("./fetch-interceptor");
8
+ const QASE_API_HOST = 'qase.io';
9
+ // Module-level ALS store — shared across all NetworkProfiler instances to
10
+ // allow nested run() calls to see the same context.
11
+ const store = new node_async_hooks_1.AsyncLocalStorage();
12
+ class NetworkProfiler extends abstract_profiler_1.AbstractProfiler {
13
+ skipDomains;
14
+ // Stored for Phase 13 use (controls whether to capture network steps on test failure)
15
+ _trackOnFail;
16
+ /**
17
+ * Fallback accumulator for steps captured outside any run() context.
18
+ * Used by frameworks where test bodies cannot be wrapped in run() (Jest, Vitest, WDIO).
19
+ * Public so interceptors can push to it via ProfilerRef.
20
+ */
21
+ fallbackSteps = [];
22
+ httpInterceptor = null;
23
+ fetchInterceptor = null;
24
+ constructor(options = {}) {
25
+ super();
26
+ this.skipDomains = options.skipDomains ?? [];
27
+ this._trackOnFail = options.trackOnFail ?? true;
28
+ }
29
+ get trackOnFail() {
30
+ return this._trackOnFail;
31
+ }
32
+ shouldSkip(url) {
33
+ if (url.includes(QASE_API_HOST)) {
34
+ return true;
35
+ }
36
+ return this.skipDomains.some((domain) => url.includes(domain));
37
+ }
38
+ /**
39
+ * Install HTTP/HTTPS monkey-patches and subscribe to undici diagnostics_channel
40
+ * so that outgoing requests inside a `run()` context are captured as REQUEST steps.
41
+ */
42
+ enable() {
43
+ this.httpInterceptor = new http_interceptor_1.HttpInterceptor(store, this);
44
+ this.httpInterceptor.install();
45
+ this.fetchInterceptor = new fetch_interceptor_1.FetchInterceptor(store, this);
46
+ this.fetchInterceptor.subscribe();
47
+ }
48
+ /**
49
+ * No-op per v1.3 design decision (flag-based activation model).
50
+ * Satisfies the AbstractProfiler contract.
51
+ */
52
+ disable() {
53
+ // Intentional no-op
54
+ }
55
+ /**
56
+ * Uninstall HTTP/HTTPS monkey-patches and unsubscribe from undici diagnostics_channel —
57
+ * permanently removes interception.
58
+ */
59
+ restore() {
60
+ this.fetchInterceptor?.unsubscribe();
61
+ this.fetchInterceptor = null;
62
+ this.httpInterceptor?.uninstall();
63
+ this.httpInterceptor = null;
64
+ }
65
+ /**
66
+ * Wraps `fn` in an AsyncLocalStorage context so that any HTTP requests
67
+ * made inside `fn` are attributed to the step accumulator for this call.
68
+ *
69
+ * Returns `{ result, steps }` where `result` is the return value of `fn`
70
+ * and `steps` is the array of REQUEST steps captured during execution.
71
+ */
72
+ async run(fn) {
73
+ const accumulator = [];
74
+ const result = await store.run(accumulator, fn);
75
+ return { result, steps: accumulator };
76
+ }
77
+ /**
78
+ * Returns the current ALS store when called inside a `run()` context.
79
+ * Returns an empty array when called outside (no active context).
80
+ */
81
+ getSteps() {
82
+ return store.getStore() ?? [];
83
+ }
84
+ /**
85
+ * Returns a copy of steps accumulated outside any run() context (fallback accumulator).
86
+ * Used by frameworks where test bodies cannot be wrapped in run() (Jest, Vitest, WDIO).
87
+ */
88
+ getAllSteps() {
89
+ return [...this.fallbackSteps];
90
+ }
91
+ /**
92
+ * Clears the fallback step accumulator. Call after collecting steps per test.
93
+ */
94
+ clearSteps() {
95
+ this.fallbackSteps.length = 0;
96
+ }
97
+ }
98
+ exports.NetworkProfiler = NetworkProfiler;
package/dist/qase.js CHANGED
@@ -97,13 +97,7 @@ class QaseReporter {
97
97
  this.logger.logDebug(`Config: ${JSON.stringify(this.sanitizeOptions(composedOptions))}`);
98
98
  const effectiveMode = composedOptions.mode || options_1.ModeEnum.off;
99
99
  const effectiveFallback = composedOptions.fallback || options_1.ModeEnum.off;
100
- const needsHostData = effectiveMode === options_1.ModeEnum.testops ||
101
- effectiveMode === options_1.ModeEnum.testops_multi ||
102
- effectiveFallback === options_1.ModeEnum.testops ||
103
- effectiveFallback === options_1.ModeEnum.testops_multi;
104
- this.hostData = needsHostData
105
- ? (0, hostData_1.getHostInfo)(options.frameworkPackage, options.reporterName)
106
- : (0, hostData_1.getMinimalHostData)();
100
+ this.hostData = (0, hostData_1.getHostInfo)(options.frameworkPackage, options.reporterName);
107
101
  this.logger.logDebug(`Host data: ${JSON.stringify(this.hostData)}`);
108
102
  this.captureLogs = composedOptions.captureLogs;
109
103
  try {
@@ -474,7 +468,7 @@ class QaseReporter {
474
468
  case options_1.ModeEnum.report: {
475
469
  const localOptions = options.report?.connections?.[writer_1.DriverEnum.local];
476
470
  const writer = new writer_1.FsWriter(localOptions);
477
- return new reporters_1.ReportReporter(this.logger, writer, options.frameworkPackage, options.reporterName, options.environment, options.rootSuite, options.testops?.run?.id);
471
+ return new reporters_1.ReportReporter(this.logger, writer, options.frameworkPackage, options.reporterName, options.environment, options.rootSuite, options.testops?.run?.id, this.hostData);
478
472
  }
479
473
  case options_1.ModeEnum.off:
480
474
  throw new disabled_exception_1.DisabledException();
@@ -2,6 +2,7 @@ import { AbstractReporter } from './abstract-reporter';
2
2
  import { Attachment } from '../models';
3
3
  import { WriterInterface } from '../writer';
4
4
  import { LoggerInterface } from '../utils/logger';
5
+ import { HostData } from '../models/host-data';
5
6
  /**
6
7
  * @class ReportReporter
7
8
  * @extends AbstractReporter
@@ -13,6 +14,7 @@ export declare class ReportReporter extends AbstractReporter {
13
14
  private readonly environment;
14
15
  private readonly runId;
15
16
  private readonly rootSuite;
17
+ private readonly hostData;
16
18
  private startTime;
17
19
  /**
18
20
  * @param {LoggerInterface} logger
@@ -22,8 +24,9 @@ export declare class ReportReporter extends AbstractReporter {
22
24
  * @param {string | undefined} environment
23
25
  * @param {string | undefined} rootSuite
24
26
  * @param {number | undefined} runId
27
+ * @param {HostData | undefined} hostData
25
28
  */
26
- constructor(logger: LoggerInterface, writer: WriterInterface, frameworkName: string, reporterName: string, environment?: string, rootSuite?: string, runId?: number);
29
+ constructor(logger: LoggerInterface, writer: WriterInterface, frameworkName: string, reporterName: string, environment?: string, rootSuite?: string, runId?: number, hostData?: HostData);
27
30
  /**
28
31
  * @returns {Promise<void>}
29
32
  */
@@ -15,6 +15,7 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
15
15
  environment;
16
16
  runId;
17
17
  rootSuite;
18
+ hostData;
18
19
  startTime = Date.now();
19
20
  /**
20
21
  * @param {LoggerInterface} logger
@@ -24,8 +25,9 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
24
25
  * @param {string | undefined} environment
25
26
  * @param {string | undefined} rootSuite
26
27
  * @param {number | undefined} runId
28
+ * @param {HostData | undefined} hostData
27
29
  */
28
- constructor(logger, writer, frameworkName, reporterName, environment, rootSuite, runId) {
30
+ constructor(logger, writer, frameworkName, reporterName, environment, rootSuite, runId, hostData) {
29
31
  super(logger);
30
32
  this.writer = writer;
31
33
  this.frameworkName = frameworkName;
@@ -33,6 +35,7 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
33
35
  this.environment = environment;
34
36
  this.runId = runId;
35
37
  this.rootSuite = rootSuite;
38
+ this.hostData = hostData;
36
39
  }
37
40
  /**
38
41
  * @returns {Promise<void>}
@@ -103,7 +106,7 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
103
106
  threads: [],
104
107
  suites: [],
105
108
  environment: this.environment ?? '',
106
- host_data: (0, hostData_1.getHostInfo)(this.frameworkName, this.reporterName),
109
+ host_data: this.hostData ?? (0, hostData_1.getHostInfo)(this.frameworkName, this.reporterName),
107
110
  };
108
111
  for (const result of this.results) {
109
112
  report.stats.total++;
@@ -276,6 +279,10 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
276
279
  input_data: null, // JS GherkinData has no data field
277
280
  };
278
281
  }
282
+ // For request steps, pass raw fields through (all 7 StepRequestData fields are preserved)
283
+ if (step.step_type === models_1.StepType.REQUEST && 'request_method' in data) {
284
+ return data;
285
+ }
279
286
  return data;
280
287
  }
281
288
  }
@@ -1,10 +1,4 @@
1
1
  import { HostData } from '../models/host-data';
2
- /**
3
- * Returns minimal host data without slow operations (no npm list, no execSync for node/npm).
4
- * Use when reporter mode is "off" to avoid startup delay.
5
- * @returns {HostData} Minimal host information object
6
- */
7
- export declare function getMinimalHostData(): HostData;
8
2
  /**
9
3
  * Gets information about the current host environment
10
4
  * @param {string} framework The framework name to check version for