@wdio/browserstack-service 9.27.1 → 9.28.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/build/caCert.d.ts +7 -0
- package/build/caCert.d.ts.map +1 -0
- package/build/cleanup.js +406 -164
- package/build/cli/cliUtils.d.ts +10 -2
- package/build/cli/cliUtils.d.ts.map +1 -1
- package/build/cli/frameworks/wdioMochaTestFramework.d.ts.map +1 -1
- package/build/cli/grpcClient.d.ts.map +1 -1
- package/build/cli/index.d.ts +13 -0
- package/build/cli/index.d.ts.map +1 -1
- package/build/config.d.ts +4 -4
- package/build/config.d.ts.map +1 -1
- package/build/constants.d.ts +15 -0
- package/build/constants.d.ts.map +1 -1
- package/build/fetchWrapper.d.ts +18 -2
- package/build/fetchWrapper.d.ts.map +1 -1
- package/build/index.js +1289 -714
- package/build/insights-handler.d.ts.map +1 -1
- package/build/instrumentation/performance/performance-tester.d.ts.map +1 -1
- package/build/launcher.d.ts +1 -0
- package/build/launcher.d.ts.map +1 -1
- package/build/reporter.d.ts.map +1 -1
- package/build/service.d.ts.map +1 -1
- package/build/testHub/utils.d.ts +2 -2
- package/build/testHub/utils.d.ts.map +1 -1
- package/build/types.d.ts +16 -0
- package/build/types.d.ts.map +1 -1
- package/build/util.d.ts +23 -0
- package/build/util.d.ts.map +1 -1
- package/package.json +6 -7
package/build/index.js
CHANGED
|
@@ -23,7 +23,7 @@ var init_package = __esm({
|
|
|
23
23
|
"package.json"() {
|
|
24
24
|
package_default = {
|
|
25
25
|
name: "@wdio/browserstack-service",
|
|
26
|
-
version: "9.27.
|
|
26
|
+
version: "9.27.2",
|
|
27
27
|
description: "WebdriverIO service for better Browserstack integration",
|
|
28
28
|
author: "Adam Bjerstedt <abjerstedt@gmail.com>",
|
|
29
29
|
homepage: "https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-browserstack-service",
|
|
@@ -70,7 +70,6 @@ var init_package = __esm({
|
|
|
70
70
|
"browserstack-local": "^1.5.1",
|
|
71
71
|
chalk: "^5.3.0",
|
|
72
72
|
"csv-writer": "^1.6.0",
|
|
73
|
-
"formdata-node": "5.0.1",
|
|
74
73
|
"git-repo-info": "^2.1.1",
|
|
75
74
|
gitconfiglocal: "^2.1.0",
|
|
76
75
|
glob: "^11.0.0",
|
|
@@ -98,7 +97,7 @@ var init_package = __esm({
|
|
|
98
97
|
});
|
|
99
98
|
|
|
100
99
|
// src/constants.ts
|
|
101
|
-
var bstackServiceVersion, BROWSER_DESCRIPTION, VALID_APP_EXTENSION, DEFAULT_OPTIONS, consoleHolder, APP_ALLY_ISSUES_ENDPOINT, APP_ALLY_ISSUES_SUMMARY_ENDPOINT, DATA_EVENT_ENDPOINT, DATA_BATCH_ENDPOINT, DATA_SCREENSHOT_ENDPOINT, DATA_BATCH_SIZE, DATA_BATCH_INTERVAL, DEFAULT_WAIT_TIMEOUT_FOR_PENDING_UPLOADS, DEFAULT_WAIT_INTERVAL_FOR_PENDING_UPLOADS, BSTACK_SERVICE_VERSION, NOT_ALLOWED_KEYS_IN_CAPS, LOGS_FILE, CLI_DEBUG_LOGS_FILE, UPLOAD_LOGS_ENDPOINT, PERCY_LOGS_FILE, PERCY_DOM_CHANGING_COMMANDS_ENDPOINTS, CAPTURE_MODES, LOG_KIND_USAGE_MAP, SUPPORTED_BROWSERS_FOR_AI, TCG_URL, TCG_INFO, SMART_SELECTION_MODE_RELEVANT_FIRST, SMART_SELECTION_MODE_RELEVANT_ONLY, BROWSERSTACK_TESTHUB_JWT, BSTACK_TCG_AUTH_RESULT, TESTOPS_SCREENSHOT_ENV, BROWSERSTACK_TESTHUB_UUID, TEST_ANALYTICS_ID, PERF_MEASUREMENT_ENV, RERUN_TESTS_ENV, RERUN_ENV, TESTOPS_BUILD_COMPLETED_ENV, BROWSERSTACK_PERCY, BROWSERSTACK_ACCESSIBILITY, BROWSERSTACK_OBSERVABILITY, BROWSERSTACK_TEST_REPORTING, TEST_REPORTING_PROJECT_NAME, MAX_GIT_META_DATA_SIZE_IN_BYTES, GIT_META_DATA_TRUNCATED, CLI_STOP_TIMEOUT, WDIO_NAMING_PREFIX, UPDATED_CLI_ENDPOINT;
|
|
100
|
+
var bstackServiceVersion, BROWSER_DESCRIPTION, VALID_APP_EXTENSION, DEFAULT_OPTIONS, consoleHolder, APP_ALLY_ISSUES_ENDPOINT, APP_ALLY_ISSUES_SUMMARY_ENDPOINT, DATA_EVENT_ENDPOINT, DATA_BATCH_ENDPOINT, DATA_SCREENSHOT_ENDPOINT, DATA_BATCH_SIZE, DATA_BATCH_INTERVAL, DEFAULT_WAIT_TIMEOUT_FOR_PENDING_UPLOADS, DEFAULT_WAIT_INTERVAL_FOR_PENDING_UPLOADS, BSTACK_SERVICE_VERSION, NOT_ALLOWED_KEYS_IN_CAPS, BROWSERSTACK_TEST_PLAN_ID, LOGS_FILE, CLI_DEBUG_LOGS_FILE, UPLOAD_LOGS_ENDPOINT, PERCY_LOGS_FILE, PERCY_DOM_CHANGING_COMMANDS_ENDPOINTS, CAPTURE_MODES, LOG_KIND_USAGE_MAP, SUPPORTED_BROWSERS_FOR_AI, SUPPORTED_BROWSERS_FOR_ACCESSIBILITY, MIN_BROWSER_VERSIONS_A11Y, MIN_BROWSER_VERSIONS_A11Y_NON_BSTACK, TCG_URL, TCG_INFO, SMART_SELECTION_MODE_RELEVANT_FIRST, SMART_SELECTION_MODE_RELEVANT_ONLY, BROWSERSTACK_TESTHUB_JWT, BSTACK_TCG_AUTH_RESULT, TESTOPS_SCREENSHOT_ENV, BROWSERSTACK_TESTHUB_UUID, TEST_ANALYTICS_ID, PERF_MEASUREMENT_ENV, RERUN_TESTS_ENV, RERUN_ENV, TESTOPS_BUILD_COMPLETED_ENV, BROWSERSTACK_PERCY, BROWSERSTACK_ACCESSIBILITY, BROWSERSTACK_OBSERVABILITY, BROWSERSTACK_TEST_REPORTING, TEST_REPORTING_PROJECT_NAME, MAX_GIT_META_DATA_SIZE_IN_BYTES, GIT_META_DATA_TRUNCATED, CLI_STOP_TIMEOUT, BINARY_BUSY_ERROR_CODES, MAX_SPAWN_RETRIES, SPAWN_RETRY_DELAY_MS, WDIO_NAMING_PREFIX, UPDATED_CLI_ENDPOINT;
|
|
102
101
|
var init_constants = __esm({
|
|
103
102
|
"src/constants.ts"() {
|
|
104
103
|
"use strict";
|
|
@@ -122,8 +121,7 @@ var init_constants = __esm({
|
|
|
122
121
|
DEFAULT_OPTIONS = {
|
|
123
122
|
setSessionName: true,
|
|
124
123
|
setSessionStatus: true,
|
|
125
|
-
testObservability: true
|
|
126
|
-
accessibility: false
|
|
124
|
+
testObservability: true
|
|
127
125
|
};
|
|
128
126
|
consoleHolder = Object.assign({}, console);
|
|
129
127
|
APP_ALLY_ISSUES_ENDPOINT = "api/v1/issues";
|
|
@@ -136,7 +134,8 @@ var init_constants = __esm({
|
|
|
136
134
|
DEFAULT_WAIT_TIMEOUT_FOR_PENDING_UPLOADS = 5e3;
|
|
137
135
|
DEFAULT_WAIT_INTERVAL_FOR_PENDING_UPLOADS = 100;
|
|
138
136
|
BSTACK_SERVICE_VERSION = bstackServiceVersion;
|
|
139
|
-
NOT_ALLOWED_KEYS_IN_CAPS = ["includeTagsInTestingScope", "excludeTagsInTestingScope"];
|
|
137
|
+
NOT_ALLOWED_KEYS_IN_CAPS = ["includeTagsInTestingScope", "excludeTagsInTestingScope", "testManagementOptions"];
|
|
138
|
+
BROWSERSTACK_TEST_PLAN_ID = "BROWSERSTACK_TEST_PLAN_ID";
|
|
140
139
|
LOGS_FILE = "logs/bstack-wdio-service.log";
|
|
141
140
|
CLI_DEBUG_LOGS_FILE = "log/sdk-cli-debug.log";
|
|
142
141
|
UPLOAD_LOGS_ENDPOINT = "client-logs/upload";
|
|
@@ -158,6 +157,17 @@ var init_constants = __esm({
|
|
|
158
157
|
"HTTP": "http"
|
|
159
158
|
};
|
|
160
159
|
SUPPORTED_BROWSERS_FOR_AI = ["chrome", "microsoftedge", "firefox"];
|
|
160
|
+
SUPPORTED_BROWSERS_FOR_ACCESSIBILITY = ["chrome", "chromefortesting", "safari"];
|
|
161
|
+
MIN_BROWSER_VERSIONS_A11Y = {
|
|
162
|
+
chrome: 95,
|
|
163
|
+
chromefortesting: 141,
|
|
164
|
+
safari: 18.4
|
|
165
|
+
};
|
|
166
|
+
MIN_BROWSER_VERSIONS_A11Y_NON_BSTACK = {
|
|
167
|
+
chrome: 100,
|
|
168
|
+
chromefortesting: 141,
|
|
169
|
+
safari: 18.4
|
|
170
|
+
};
|
|
161
171
|
TCG_URL = "https://tcg.browserstack.com";
|
|
162
172
|
TCG_INFO = {
|
|
163
173
|
tcgRegion: "use",
|
|
@@ -182,6 +192,9 @@ var init_constants = __esm({
|
|
|
182
192
|
MAX_GIT_META_DATA_SIZE_IN_BYTES = 64 * 1024;
|
|
183
193
|
GIT_META_DATA_TRUNCATED = "...[TRUNCATED]";
|
|
184
194
|
CLI_STOP_TIMEOUT = 5e3;
|
|
195
|
+
BINARY_BUSY_ERROR_CODES = ["ETXTBSY", "EBUSY"];
|
|
196
|
+
MAX_SPAWN_RETRIES = 3;
|
|
197
|
+
SPAWN_RETRY_DELAY_MS = 1e3;
|
|
185
198
|
WDIO_NAMING_PREFIX = "WebdriverIO-";
|
|
186
199
|
UPDATED_CLI_ENDPOINT = "sdk/v1/update_cli";
|
|
187
200
|
}
|
|
@@ -266,34 +279,155 @@ var init_apiUtils = __esm({
|
|
|
266
279
|
}
|
|
267
280
|
});
|
|
268
281
|
|
|
282
|
+
// src/caCert.ts
|
|
283
|
+
import { setGlobalDispatcher, Agent } from "undici";
|
|
284
|
+
import tls from "node:tls";
|
|
285
|
+
import fs from "node:fs";
|
|
286
|
+
import os from "node:os";
|
|
287
|
+
import path from "node:path";
|
|
288
|
+
function derToPem(der) {
|
|
289
|
+
const b64 = der.toString("base64").replace(/(.{64})/g, "$1\n");
|
|
290
|
+
return `-----BEGIN CERTIFICATE-----
|
|
291
|
+
${b64}${b64.endsWith("\n") ? "" : "\n"}-----END CERTIFICATE-----
|
|
292
|
+
`;
|
|
293
|
+
}
|
|
294
|
+
function loadCaCertsAsPem(buf) {
|
|
295
|
+
if (buf.includes("-----BEGIN CERTIFICATE-----")) {
|
|
296
|
+
return buf.toString("utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g) || [];
|
|
297
|
+
}
|
|
298
|
+
return [derToPem(buf)];
|
|
299
|
+
}
|
|
300
|
+
function resolveCaCertPath(options) {
|
|
301
|
+
let p = process.env.BROWSERSTACK_EXTRA_CA_CERTS;
|
|
302
|
+
if ((!p || !p.trim()) && options?.proxyCaCertificate) {
|
|
303
|
+
p = options.proxyCaCertificate;
|
|
304
|
+
}
|
|
305
|
+
if (!p || !String(p).trim()) {
|
|
306
|
+
return void 0;
|
|
307
|
+
}
|
|
308
|
+
p = String(p).trim();
|
|
309
|
+
try {
|
|
310
|
+
if (fs.existsSync(p) && fs.statSync(p).isFile()) {
|
|
311
|
+
return p;
|
|
312
|
+
}
|
|
313
|
+
BStackLogger.warn(`proxyCaCertificate: path does not exist or is not a file, falling back to system trust store: ${p}`);
|
|
314
|
+
} catch (e) {
|
|
315
|
+
BStackLogger.warn(`proxyCaCertificate: failed to stat cert path ${p}: ${e.message}`);
|
|
316
|
+
}
|
|
317
|
+
return void 0;
|
|
318
|
+
}
|
|
319
|
+
function getMergedCa() {
|
|
320
|
+
return mergedCa;
|
|
321
|
+
}
|
|
322
|
+
function configureCaCertificate(options) {
|
|
323
|
+
if (configured) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
const certPath = resolveCaCertPath(options);
|
|
328
|
+
if (!certPath) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const buf = fs.readFileSync(certPath);
|
|
332
|
+
const isPem = buf.includes("-----BEGIN CERTIFICATE-----");
|
|
333
|
+
const pemCerts = loadCaCertsAsPem(buf);
|
|
334
|
+
if (!pemCerts.length) {
|
|
335
|
+
BStackLogger.warn(`proxyCaCertificate: no certificate found in ${certPath}; falling back to system trust store.`);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
mergedCa = [...tls.rootCertificates, ...pemCerts];
|
|
339
|
+
setGlobalDispatcher(new Agent({ connect: { ca: mergedCa } }));
|
|
340
|
+
configured = true;
|
|
341
|
+
BStackLogger.info(`proxyCaCertificate: trusting custom CA from ${certPath} (merged with system roots).`);
|
|
342
|
+
try {
|
|
343
|
+
if (!process.env.NODE_EXTRA_CA_CERTS) {
|
|
344
|
+
let nodeExtra = certPath;
|
|
345
|
+
if (!isPem) {
|
|
346
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "browserstack_sdk_ca_"));
|
|
347
|
+
nodeExtra = path.join(tmpDir, "ca.pem");
|
|
348
|
+
const fd = fs.openSync(nodeExtra, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | (fs.constants.O_NOFOLLOW || 0), 384);
|
|
349
|
+
try {
|
|
350
|
+
fs.writeFileSync(fd, pemCerts.join(""));
|
|
351
|
+
} finally {
|
|
352
|
+
fs.closeSync(fd);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
process.env.NODE_EXTRA_CA_CERTS = nodeExtra;
|
|
356
|
+
}
|
|
357
|
+
} catch (e) {
|
|
358
|
+
BStackLogger.warn(`proxyCaCertificate: CA is trusted for this process, but exporting NODE_EXTRA_CA_CERTS for detached child processes failed (children may not trust the custom CA): ${e.message}`);
|
|
359
|
+
}
|
|
360
|
+
} catch (e) {
|
|
361
|
+
BStackLogger.warn(`proxyCaCertificate: setup failed, falling back to system trust store: ${e.message}`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
var mergedCa, configured;
|
|
365
|
+
var init_caCert = __esm({
|
|
366
|
+
"src/caCert.ts"() {
|
|
367
|
+
"use strict";
|
|
368
|
+
init_bstackLogger();
|
|
369
|
+
configured = false;
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
269
373
|
// src/fetchWrapper.ts
|
|
270
|
-
import { fetch as undiciFetch, ProxyAgent } from "undici";
|
|
271
|
-
|
|
272
|
-
const
|
|
374
|
+
import { fetch as undiciFetch, ProxyAgent, Agent as Agent2 } from "undici";
|
|
375
|
+
function getDispatcher(proxyUrl, connectTimeoutMs) {
|
|
376
|
+
const ca = getMergedCa();
|
|
377
|
+
const key = `${proxyUrl ?? "direct"}:${connectTimeoutMs ?? "default"}:${ca ? "ca" : "noca"}`;
|
|
378
|
+
let dispatcher = dispatcherCache.get(key);
|
|
379
|
+
if (!dispatcher) {
|
|
380
|
+
const connect = {};
|
|
381
|
+
if (connectTimeoutMs) {
|
|
382
|
+
connect.timeout = connectTimeoutMs;
|
|
383
|
+
}
|
|
384
|
+
if (proxyUrl) {
|
|
385
|
+
dispatcher = new ProxyAgent({ uri: proxyUrl, connect, ...ca ? { requestTls: { ca } } : {} });
|
|
386
|
+
} else {
|
|
387
|
+
if (ca) {
|
|
388
|
+
connect.ca = ca;
|
|
389
|
+
}
|
|
390
|
+
dispatcher = new Agent2({ connect });
|
|
391
|
+
}
|
|
392
|
+
dispatcherCache.set(key, dispatcher);
|
|
393
|
+
}
|
|
394
|
+
return dispatcher;
|
|
395
|
+
}
|
|
396
|
+
async function fetchWrap(input, init, options) {
|
|
397
|
+
const res = await _fetch(input, init, options);
|
|
273
398
|
if (!res.ok) {
|
|
274
399
|
throw new ResponseError(`Error response from server ${res.status}: ${await res.text()}`, res);
|
|
275
400
|
}
|
|
276
401
|
return res;
|
|
277
402
|
}
|
|
278
|
-
function _fetch(input, init) {
|
|
403
|
+
function _fetch(input, init, options) {
|
|
404
|
+
const connectTimeoutMs = options?.connectTimeoutMs;
|
|
279
405
|
const proxyUrl = process.env.HTTP_PROXY || process.env.HTTPS_PROXY;
|
|
280
406
|
if (proxyUrl) {
|
|
281
407
|
const noProxy = process.env.NO_PROXY && process.env.NO_PROXY.trim() ? process.env.NO_PROXY.trim().split(/[\s,;]+/) : [];
|
|
282
408
|
const request = new Request(input);
|
|
283
409
|
const url3 = new URL(request.url);
|
|
284
410
|
if (!noProxy.some((str) => url3.hostname.endsWith(str))) {
|
|
411
|
+
const dispatcher = getDispatcher(proxyUrl, connectTimeoutMs);
|
|
285
412
|
return undiciFetch(
|
|
286
413
|
request.url,
|
|
287
|
-
{ ...init, dispatcher
|
|
414
|
+
{ ...init, dispatcher }
|
|
288
415
|
);
|
|
289
416
|
}
|
|
290
417
|
}
|
|
418
|
+
if (connectTimeoutMs) {
|
|
419
|
+
return undiciFetch(
|
|
420
|
+
new Request(input).url,
|
|
421
|
+
{ ...init, dispatcher: getDispatcher(void 0, connectTimeoutMs) }
|
|
422
|
+
);
|
|
423
|
+
}
|
|
291
424
|
return fetch(input, init);
|
|
292
425
|
}
|
|
293
|
-
var ResponseError;
|
|
426
|
+
var ResponseError, dispatcherCache;
|
|
294
427
|
var init_fetchWrapper = __esm({
|
|
295
428
|
"src/fetchWrapper.ts"() {
|
|
296
429
|
"use strict";
|
|
430
|
+
init_caCert();
|
|
297
431
|
ResponseError = class extends Error {
|
|
298
432
|
response;
|
|
299
433
|
constructor(message, res) {
|
|
@@ -301,6 +435,7 @@ var init_fetchWrapper = __esm({
|
|
|
301
435
|
this.response = res;
|
|
302
436
|
}
|
|
303
437
|
};
|
|
438
|
+
dispatcherCache = /* @__PURE__ */ new Map();
|
|
304
439
|
}
|
|
305
440
|
});
|
|
306
441
|
|
|
@@ -458,26 +593,26 @@ var init_constants2 = __esm({
|
|
|
458
593
|
});
|
|
459
594
|
|
|
460
595
|
// src/cli/cliLogger.ts
|
|
461
|
-
import
|
|
462
|
-
import
|
|
596
|
+
import path2 from "node:path";
|
|
597
|
+
import fs2 from "node:fs";
|
|
463
598
|
import chalk from "chalk";
|
|
464
599
|
import logger from "@wdio/logger";
|
|
465
|
-
var log,
|
|
600
|
+
var log, BStackLogger2;
|
|
466
601
|
var init_cliLogger = __esm({
|
|
467
602
|
"src/cli/cliLogger.ts"() {
|
|
468
603
|
"use strict";
|
|
469
604
|
init_constants();
|
|
470
605
|
init_util();
|
|
471
606
|
log = logger("@wdio/browserstack-service/cli");
|
|
472
|
-
|
|
473
|
-
static logFilePath =
|
|
474
|
-
static logFolderPath =
|
|
607
|
+
BStackLogger2 = class {
|
|
608
|
+
static logFilePath = path2.join(process.cwd(), LOGS_FILE);
|
|
609
|
+
static logFolderPath = path2.join(process.cwd(), "logs");
|
|
475
610
|
static logFileStream;
|
|
476
611
|
static logToFile(logMessage, logLevel) {
|
|
477
612
|
try {
|
|
478
613
|
if (!this.logFileStream) {
|
|
479
614
|
this.ensureLogsFolder();
|
|
480
|
-
this.logFileStream =
|
|
615
|
+
this.logFileStream = fs2.createWriteStream(this.logFilePath, { flags: "a" });
|
|
481
616
|
}
|
|
482
617
|
if (this.logFileStream && this.logFileStream.writable) {
|
|
483
618
|
this.logFileStream.write(this.formatLog(logMessage, logLevel));
|
|
@@ -521,13 +656,13 @@ var init_cliLogger = __esm({
|
|
|
521
656
|
this.logFileStream = null;
|
|
522
657
|
}
|
|
523
658
|
static clearLogFile() {
|
|
524
|
-
if (
|
|
525
|
-
|
|
659
|
+
if (fs2.existsSync(this.logFilePath)) {
|
|
660
|
+
fs2.truncateSync(this.logFilePath);
|
|
526
661
|
}
|
|
527
662
|
}
|
|
528
663
|
static ensureLogsFolder() {
|
|
529
|
-
if (!
|
|
530
|
-
|
|
664
|
+
if (!fs2.existsSync(this.logFolderPath)) {
|
|
665
|
+
fs2.mkdirSync(this.logFolderPath);
|
|
531
666
|
}
|
|
532
667
|
}
|
|
533
668
|
};
|
|
@@ -585,16 +720,16 @@ var init_testFrameworkConstants = __esm({
|
|
|
585
720
|
});
|
|
586
721
|
|
|
587
722
|
// src/cli/cliUtils.ts
|
|
588
|
-
import
|
|
723
|
+
import fs3 from "node:fs";
|
|
589
724
|
import fsp from "node:fs/promises";
|
|
590
725
|
import { platform, arch, homedir } from "node:os";
|
|
591
|
-
import
|
|
726
|
+
import path3 from "node:path";
|
|
592
727
|
import util, { promisify } from "node:util";
|
|
593
728
|
import { exec } from "node:child_process";
|
|
594
729
|
import { Readable } from "node:stream";
|
|
595
730
|
import yauzl from "yauzl";
|
|
596
731
|
import { threadId } from "node:worker_threads";
|
|
597
|
-
var CLI_LOCK_TIMEOUT_MS, CLI_LOCK_POLL_MS, CLI_DOWNLOAD_TIMEOUT_MS, CLI_DOWNLOAD_TMP_PREFIX, CLI_DOWNLOAD_TMP_SUFFIX, CLIUtils;
|
|
732
|
+
var CLI_LOCK_TIMEOUT_MS, CLI_LOCK_POLL_MS, CLI_DOWNLOAD_TIMEOUT_MS, CLI_DOWNLOAD_TMP_PREFIX, CLI_DOWNLOAD_TMP_SUFFIX, CLI_CONNECT_TIMEOUT_MS, CLI_FETCH_MAX_ATTEMPTS, CLI_FETCH_RETRY_BASE_DELAY_MS, CLIUtils;
|
|
598
733
|
var init_cliUtils = __esm({
|
|
599
734
|
"src/cli/cliUtils.ts"() {
|
|
600
735
|
"use strict";
|
|
@@ -611,6 +746,9 @@ var init_cliUtils = __esm({
|
|
|
611
746
|
CLI_DOWNLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
612
747
|
CLI_DOWNLOAD_TMP_PREFIX = "downloaded_file_";
|
|
613
748
|
CLI_DOWNLOAD_TMP_SUFFIX = ".zip";
|
|
749
|
+
CLI_CONNECT_TIMEOUT_MS = Number(process.env.BROWSERSTACK_CLI_CONNECT_TIMEOUT_MS) || 60 * 1e3;
|
|
750
|
+
CLI_FETCH_MAX_ATTEMPTS = 3;
|
|
751
|
+
CLI_FETCH_RETRY_BASE_DELAY_MS = 500;
|
|
614
752
|
CLIUtils = class _CLIUtils {
|
|
615
753
|
static automationFrameworkDetail = {};
|
|
616
754
|
static testFrameworkDetail = {};
|
|
@@ -635,6 +773,7 @@ var init_cliUtils = __esm({
|
|
|
635
773
|
modifiedOpts.browserStackLocalOptions = modifiedOpts.opts;
|
|
636
774
|
delete modifiedOpts.opts;
|
|
637
775
|
}
|
|
776
|
+
delete modifiedOpts.testManagementOptions;
|
|
638
777
|
modifiedOpts.testContextOptions = {
|
|
639
778
|
skipSessionName: isFalse(modifiedOpts.setSessionName),
|
|
640
779
|
skipSessionStatus: isFalse(modifiedOpts.setSessionStatus),
|
|
@@ -653,6 +792,8 @@ var init_cliUtils = __esm({
|
|
|
653
792
|
options.testObservability
|
|
654
793
|
);
|
|
655
794
|
const observabilityOptions = options.testObservabilityOptions || {};
|
|
795
|
+
const testManagementOptions = options.testManagementOptions || {};
|
|
796
|
+
const testPlanId = typeof testManagementOptions.testPlanId === "string" ? testManagementOptions.testPlanId.trim() : "";
|
|
656
797
|
const binconfig = {
|
|
657
798
|
userName: observabilityOptions.user || config.user,
|
|
658
799
|
accessKey: observabilityOptions.key || config.key,
|
|
@@ -664,26 +805,26 @@ var init_cliUtils = __esm({
|
|
|
664
805
|
binconfig.buildName = observabilityOptions.buildName || binconfig.buildName;
|
|
665
806
|
binconfig.projectName = observabilityOptions.projectName || binconfig.projectName;
|
|
666
807
|
binconfig.buildTag = this.getObservabilityBuildTags(observabilityOptions, buildTag) || [];
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
808
|
+
if (testPlanId.length > 0) {
|
|
809
|
+
binconfig.testManagementOptions = {
|
|
810
|
+
testPlanId
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
const caps = Array.isArray(capabilities) ? capabilities : [capabilities];
|
|
814
|
+
for (const cap of caps) {
|
|
815
|
+
const platform4 = {};
|
|
816
|
+
const capability = cap;
|
|
817
|
+
Object.keys(capability).filter((key) => key !== "bstack:options").forEach((key) => {
|
|
818
|
+
platform4[key] = capability[key];
|
|
819
|
+
});
|
|
820
|
+
if (capability["bstack:options"]) {
|
|
821
|
+
Object.keys(
|
|
822
|
+
capability["bstack:options"]
|
|
823
|
+
).forEach((key) => {
|
|
824
|
+
platform4[key] = capability["bstack:options"][key];
|
|
677
825
|
});
|
|
678
|
-
if (capability["bstack:options"]) {
|
|
679
|
-
Object.keys(
|
|
680
|
-
capability["bstack:options"]
|
|
681
|
-
).forEach((key) => {
|
|
682
|
-
platform4[key] = capability["bstack:options"][key];
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
binconfig.platforms.push(platform4);
|
|
686
826
|
}
|
|
827
|
+
binconfig.platforms.push(platform4);
|
|
687
828
|
}
|
|
688
829
|
return JSON.stringify(binconfig);
|
|
689
830
|
}
|
|
@@ -694,10 +835,10 @@ var init_cliUtils = __esm({
|
|
|
694
835
|
return "ECMAScript";
|
|
695
836
|
}
|
|
696
837
|
static async setupCliPath(config) {
|
|
697
|
-
|
|
838
|
+
BStackLogger2.debug("Configuring Cli path.");
|
|
698
839
|
const developmentBinaryPath = process.env.SDK_CLI_BIN_PATH || null;
|
|
699
840
|
if (!isNullOrEmpty(developmentBinaryPath)) {
|
|
700
|
-
|
|
841
|
+
BStackLogger2.debug(`Development Cli Path: ${developmentBinaryPath}`);
|
|
701
842
|
return developmentBinaryPath;
|
|
702
843
|
}
|
|
703
844
|
try {
|
|
@@ -711,10 +852,10 @@ var init_cliUtils = __esm({
|
|
|
711
852
|
cliDir,
|
|
712
853
|
config
|
|
713
854
|
);
|
|
714
|
-
|
|
855
|
+
BStackLogger2.debug(`Resolved binary path: ${finalBinaryPath}`);
|
|
715
856
|
return finalBinaryPath;
|
|
716
857
|
} catch (err) {
|
|
717
|
-
|
|
858
|
+
BStackLogger2.warn(
|
|
718
859
|
`Error in setting up cli path directory, Exception: ${util.format(err)}`
|
|
719
860
|
);
|
|
720
861
|
}
|
|
@@ -722,18 +863,18 @@ var init_cliUtils = __esm({
|
|
|
722
863
|
}
|
|
723
864
|
static async checkAndUpdateCli(existingCliPath, cliDir, config) {
|
|
724
865
|
if (process.env.BROWSERSTACK_TESTHUB_JWT) {
|
|
725
|
-
|
|
866
|
+
BStackLogger2.debug(
|
|
726
867
|
`Worker process detected, skipping CLI update. Using existing: ${existingCliPath}`
|
|
727
868
|
);
|
|
728
|
-
if (existingCliPath &&
|
|
869
|
+
if (existingCliPath && fs3.existsSync(existingCliPath)) {
|
|
729
870
|
return existingCliPath;
|
|
730
871
|
}
|
|
731
|
-
|
|
872
|
+
BStackLogger2.warn(
|
|
732
873
|
"Worker process has no existing CLI binary, attempting download as fallback."
|
|
733
874
|
);
|
|
734
875
|
}
|
|
735
876
|
PerformanceTester.start(EVENTS.SDK_CLI_CHECK_UPDATE);
|
|
736
|
-
|
|
877
|
+
BStackLogger2.info(`Current CLI Path Found: ${existingCliPath}`);
|
|
737
878
|
const queryParams = {
|
|
738
879
|
sdk_version: _CLIUtils.getSdkVersion(),
|
|
739
880
|
os: platform(),
|
|
@@ -742,18 +883,29 @@ var init_cliUtils = __esm({
|
|
|
742
883
|
sdk_language: this.getSdkLanguage()
|
|
743
884
|
};
|
|
744
885
|
if (!isNullOrEmpty(existingCliPath)) {
|
|
745
|
-
|
|
886
|
+
if (this.isBinaryBusy(existingCliPath)) {
|
|
887
|
+
BStackLogger2.warn(`Existing binary is currently in use, skipping update: ${existingCliPath}`);
|
|
888
|
+
PerformanceTester.end(EVENTS.SDK_CLI_CHECK_UPDATE);
|
|
889
|
+
return existingCliPath;
|
|
890
|
+
}
|
|
891
|
+
const version3 = await this.runShellCommand(
|
|
746
892
|
`${existingCliPath} version`
|
|
747
893
|
);
|
|
894
|
+
if (version3.toLowerCase().includes("text file busy")) {
|
|
895
|
+
BStackLogger2.warn(`Binary busy during version check, skipping update: ${existingCliPath}`);
|
|
896
|
+
PerformanceTester.end(EVENTS.SDK_CLI_CHECK_UPDATE);
|
|
897
|
+
return existingCliPath;
|
|
898
|
+
}
|
|
899
|
+
queryParams.cli_version = version3;
|
|
748
900
|
}
|
|
749
901
|
const response = await this.requestToUpdateCLI(queryParams, config);
|
|
750
902
|
if (nestedKeyValue(response, ["updated_cli_version"])) {
|
|
751
|
-
|
|
903
|
+
BStackLogger2.debug(
|
|
752
904
|
`Need to update binary, current binary version: ${queryParams.cli_version}`
|
|
753
905
|
);
|
|
754
906
|
const browserStackBinaryUrl = process.env.BROWSERSTACK_BINARY_URL || null;
|
|
755
907
|
if (!isNullOrEmpty(browserStackBinaryUrl)) {
|
|
756
|
-
|
|
908
|
+
BStackLogger2.debug(
|
|
757
909
|
`Using BROWSERSTACK_BINARY_URL: ${browserStackBinaryUrl}`
|
|
758
910
|
);
|
|
759
911
|
response.url = browserStackBinaryUrl;
|
|
@@ -774,13 +926,13 @@ var init_cliUtils = __esm({
|
|
|
774
926
|
if (isNullOrEmpty(writableDir)) {
|
|
775
927
|
throw new Error("No writable directory available for the CLI");
|
|
776
928
|
}
|
|
777
|
-
const cliDirPath =
|
|
778
|
-
if (!
|
|
929
|
+
const cliDirPath = path3.join(writableDir, "cli");
|
|
930
|
+
if (!fs3.existsSync(cliDirPath)) {
|
|
779
931
|
createDir(cliDirPath);
|
|
780
932
|
}
|
|
781
933
|
return cliDirPath;
|
|
782
934
|
} catch (err) {
|
|
783
|
-
|
|
935
|
+
BStackLogger2.error(
|
|
784
936
|
`Error in getting writable directory, writableDir=${util.format(err)}`
|
|
785
937
|
);
|
|
786
938
|
return "";
|
|
@@ -789,39 +941,39 @@ var init_cliUtils = __esm({
|
|
|
789
941
|
static getWritableDir() {
|
|
790
942
|
const writableDirOptions = [
|
|
791
943
|
process.env.BROWSERSTACK_FILES_DIR,
|
|
792
|
-
|
|
793
|
-
|
|
944
|
+
path3.join(homedir(), ".browserstack"),
|
|
945
|
+
path3.join("tmp", ".browserstack")
|
|
794
946
|
];
|
|
795
|
-
for (const
|
|
796
|
-
if (isNullOrEmpty(
|
|
947
|
+
for (const path22 of writableDirOptions) {
|
|
948
|
+
if (isNullOrEmpty(path22)) {
|
|
797
949
|
continue;
|
|
798
950
|
}
|
|
799
951
|
try {
|
|
800
|
-
if (
|
|
801
|
-
|
|
802
|
-
if (!isWritable(
|
|
803
|
-
|
|
804
|
-
const success = setReadWriteAccess(
|
|
952
|
+
if (fs3.existsSync(path22)) {
|
|
953
|
+
BStackLogger2.debug(`File ${path22} already exist`);
|
|
954
|
+
if (!isWritable(path22)) {
|
|
955
|
+
BStackLogger2.debug(`Giving write permission to ${path22}`);
|
|
956
|
+
const success = setReadWriteAccess(path22);
|
|
805
957
|
if (!isTrue(success)) {
|
|
806
|
-
|
|
807
|
-
`Unable to provide write permission to ${
|
|
958
|
+
BStackLogger2.warn(
|
|
959
|
+
`Unable to provide write permission to ${path22}`
|
|
808
960
|
);
|
|
809
961
|
}
|
|
810
962
|
}
|
|
811
963
|
} else {
|
|
812
|
-
|
|
813
|
-
createDir(
|
|
814
|
-
|
|
815
|
-
const success = setReadWriteAccess(
|
|
964
|
+
BStackLogger2.debug(`File does not exist: ${path22}`);
|
|
965
|
+
createDir(path22);
|
|
966
|
+
BStackLogger2.debug(`Giving write permission to ${path22}`);
|
|
967
|
+
const success = setReadWriteAccess(path22);
|
|
816
968
|
if (!isTrue(success)) {
|
|
817
|
-
|
|
818
|
-
`Unable to provide write permission to ${
|
|
969
|
+
BStackLogger2.warn(
|
|
970
|
+
`Unable to provide write permission to ${path22}`
|
|
819
971
|
);
|
|
820
972
|
}
|
|
821
973
|
}
|
|
822
|
-
return
|
|
974
|
+
return path22;
|
|
823
975
|
} catch (err) {
|
|
824
|
-
|
|
976
|
+
BStackLogger2.error(
|
|
825
977
|
`Unable to get writable directory, exception ${util.format(err)}`
|
|
826
978
|
);
|
|
827
979
|
}
|
|
@@ -830,16 +982,16 @@ var init_cliUtils = __esm({
|
|
|
830
982
|
}
|
|
831
983
|
static getExistingCliPath(cliDir) {
|
|
832
984
|
try {
|
|
833
|
-
if (!
|
|
985
|
+
if (!fs3.existsSync(cliDir) || !fs3.statSync(cliDir).isDirectory()) {
|
|
834
986
|
return "";
|
|
835
987
|
}
|
|
836
|
-
const allBinaries =
|
|
837
|
-
(filePath) =>
|
|
988
|
+
const allBinaries = fs3.readdirSync(cliDir).map((file) => path3.join(cliDir, file)).filter(
|
|
989
|
+
(filePath) => fs3.statSync(filePath).isFile() && path3.basename(filePath).startsWith("binary-")
|
|
838
990
|
);
|
|
839
991
|
if (allBinaries.length > 0) {
|
|
840
992
|
const latestBinary = allBinaries.map((filePath) => ({
|
|
841
993
|
filePath,
|
|
842
|
-
mtime:
|
|
994
|
+
mtime: fs3.statSync(filePath).mtime
|
|
843
995
|
})).reduce(
|
|
844
996
|
(latest, current) => {
|
|
845
997
|
if (!latest || !latest.mtime) {
|
|
@@ -856,10 +1008,62 @@ var init_cliUtils = __esm({
|
|
|
856
1008
|
}
|
|
857
1009
|
return "";
|
|
858
1010
|
} catch (err) {
|
|
859
|
-
|
|
1011
|
+
BStackLogger2.error(`Error while reading CLI path: ${util.format(err)}`);
|
|
860
1012
|
return "";
|
|
861
1013
|
}
|
|
862
1014
|
}
|
|
1015
|
+
static isBinaryBusy(binaryPath) {
|
|
1016
|
+
if (isNullOrEmpty(binaryPath)) {
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
if (platform() === "darwin") {
|
|
1020
|
+
return false;
|
|
1021
|
+
}
|
|
1022
|
+
if (!fs3.existsSync(binaryPath)) {
|
|
1023
|
+
return false;
|
|
1024
|
+
}
|
|
1025
|
+
try {
|
|
1026
|
+
const fd = fs3.openSync(binaryPath, "r+");
|
|
1027
|
+
fs3.closeSync(fd);
|
|
1028
|
+
return false;
|
|
1029
|
+
} catch (err) {
|
|
1030
|
+
if (BINARY_BUSY_ERROR_CODES.includes(err.code)) {
|
|
1031
|
+
BStackLogger2.debug(`Binary is busy: ${binaryPath}`);
|
|
1032
|
+
return true;
|
|
1033
|
+
}
|
|
1034
|
+
BStackLogger2.debug(`Error checking if binary is busy: ${err.message}`);
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* fetch wrapper for CLI network calls that (a) raises undici's connect timeout
|
|
1040
|
+
* above the non-overridable 10s default of global fetch and (b) retries a few
|
|
1041
|
+
* times with linear backoff on transient connection failures. Both are needed
|
|
1042
|
+
* for reliability in constrained CI/containerised networks (SDK-6152).
|
|
1043
|
+
*/
|
|
1044
|
+
static fetchWithRetry = async (input, init, label) => {
|
|
1045
|
+
let lastError;
|
|
1046
|
+
for (let attempt = 1; attempt <= CLI_FETCH_MAX_ATTEMPTS; attempt++) {
|
|
1047
|
+
try {
|
|
1048
|
+
return await _fetch(input, init, { connectTimeoutMs: CLI_CONNECT_TIMEOUT_MS });
|
|
1049
|
+
} catch (err) {
|
|
1050
|
+
lastError = err;
|
|
1051
|
+
const reason = (
|
|
1052
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1053
|
+
err?.cause?.code || err?.code || err?.message
|
|
1054
|
+
);
|
|
1055
|
+
BStackLogger2.warn(
|
|
1056
|
+
`${label} attempt ${attempt}/${CLI_FETCH_MAX_ATTEMPTS} failed: ${reason}`
|
|
1057
|
+
);
|
|
1058
|
+
if (attempt < CLI_FETCH_MAX_ATTEMPTS) {
|
|
1059
|
+
await new Promise(
|
|
1060
|
+
(resolve) => setTimeout(resolve, CLI_FETCH_RETRY_BASE_DELAY_MS * attempt)
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
throw lastError;
|
|
1066
|
+
};
|
|
863
1067
|
static requestToUpdateCLI = async (queryParams, config) => {
|
|
864
1068
|
const params = new URLSearchParams(queryParams);
|
|
865
1069
|
const requestInit = {
|
|
@@ -868,12 +1072,13 @@ var init_cliUtils = __esm({
|
|
|
868
1072
|
Authorization: `Basic ${Buffer.from(`${getBrowserStackUser(config)}:${getBrowserStackKey(config)}`).toString("base64")}`
|
|
869
1073
|
}
|
|
870
1074
|
};
|
|
871
|
-
const response = await
|
|
1075
|
+
const response = await this.fetchWithRetry(
|
|
872
1076
|
`${APIUtils.BROWSERSTACK_AUTOMATE_API_URL}/${UPDATED_CLI_ENDPOINT}?${params.toString()}`,
|
|
873
|
-
requestInit
|
|
1077
|
+
requestInit,
|
|
1078
|
+
"update_cli request"
|
|
874
1079
|
);
|
|
875
1080
|
const jsonResponse = await response.json();
|
|
876
|
-
|
|
1081
|
+
BStackLogger2.debug(`response ${JSON.stringify(jsonResponse)}`);
|
|
877
1082
|
return jsonResponse;
|
|
878
1083
|
};
|
|
879
1084
|
static runShellCommand(cmdCommand, workingDir = "") {
|
|
@@ -895,11 +1100,11 @@ var init_cliUtils = __esm({
|
|
|
895
1100
|
});
|
|
896
1101
|
}
|
|
897
1102
|
static downloadLatestBinary = async (binDownloadUrl, cliDir) => {
|
|
898
|
-
const lockPath =
|
|
1103
|
+
const lockPath = path3.join(cliDir, "download.lock");
|
|
899
1104
|
const sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
900
1105
|
const parseLockFile = () => {
|
|
901
1106
|
try {
|
|
902
|
-
const content =
|
|
1107
|
+
const content = fs3.readFileSync(lockPath, "utf8").trim();
|
|
903
1108
|
const [pidLine, timestampLine] = content.split("\n");
|
|
904
1109
|
const pid = Number.parseInt(pidLine, 10);
|
|
905
1110
|
const timestamp = Number.parseInt(timestampLine, 10);
|
|
@@ -923,20 +1128,20 @@ var init_cliUtils = __esm({
|
|
|
923
1128
|
const start = Date.now();
|
|
924
1129
|
while (true) {
|
|
925
1130
|
try {
|
|
926
|
-
const fd =
|
|
1131
|
+
const fd = fs3.openSync(lockPath, "wx");
|
|
927
1132
|
try {
|
|
928
|
-
|
|
1133
|
+
fs3.writeFileSync(fd, `${process.pid}
|
|
929
1134
|
${Date.now()}
|
|
930
1135
|
`);
|
|
931
1136
|
} catch {
|
|
932
1137
|
}
|
|
933
1138
|
return () => {
|
|
934
1139
|
try {
|
|
935
|
-
|
|
1140
|
+
fs3.closeSync(fd);
|
|
936
1141
|
} catch {
|
|
937
1142
|
}
|
|
938
1143
|
try {
|
|
939
|
-
|
|
1144
|
+
fs3.unlinkSync(lockPath);
|
|
940
1145
|
} catch {
|
|
941
1146
|
}
|
|
942
1147
|
};
|
|
@@ -948,19 +1153,19 @@ ${Date.now()}
|
|
|
948
1153
|
const lockAge = Date.now() - lockMeta.timestamp;
|
|
949
1154
|
const running = isProcessRunning(lockMeta.pid);
|
|
950
1155
|
if (!running || lockAge > timeoutMs) {
|
|
951
|
-
|
|
1156
|
+
BStackLogger2.warn(
|
|
952
1157
|
`Stale CLI download lock detected (pid=${lockMeta.pid}, age=${lockAge}ms). Removing lock.`
|
|
953
1158
|
);
|
|
954
1159
|
try {
|
|
955
|
-
|
|
1160
|
+
fs3.unlinkSync(lockPath);
|
|
956
1161
|
} catch {
|
|
957
1162
|
}
|
|
958
1163
|
continue;
|
|
959
1164
|
}
|
|
960
1165
|
}
|
|
961
1166
|
const existingBinary = _CLIUtils.getExistingCliPath(cliDir);
|
|
962
|
-
if (existingBinary &&
|
|
963
|
-
|
|
1167
|
+
if (existingBinary && fs3.existsSync(existingBinary) && fs3.statSync(existingBinary).size > 0) {
|
|
1168
|
+
BStackLogger2.debug(
|
|
964
1169
|
`Binary appeared while waiting for lock: ${existingBinary}`
|
|
965
1170
|
);
|
|
966
1171
|
return { alreadyExists: existingBinary };
|
|
@@ -980,14 +1185,17 @@ ${Date.now()}
|
|
|
980
1185
|
const cleanupTemporaryDownloads = (maxAgeMs = CLI_LOCK_TIMEOUT_MS) => {
|
|
981
1186
|
try {
|
|
982
1187
|
const now2 = Date.now();
|
|
983
|
-
|
|
984
|
-
|
|
1188
|
+
const tmpExtractRe = /\.tmp\.\d+$/;
|
|
1189
|
+
for (const entry of fs3.readdirSync(cliDir)) {
|
|
1190
|
+
const isDownloadZip = entry.startsWith(CLI_DOWNLOAD_TMP_PREFIX) && entry.endsWith(CLI_DOWNLOAD_TMP_SUFFIX);
|
|
1191
|
+
const isExtractTmp = tmpExtractRe.test(entry);
|
|
1192
|
+
if (!isDownloadZip && !isExtractTmp) {
|
|
985
1193
|
continue;
|
|
986
1194
|
}
|
|
987
|
-
const filePath =
|
|
1195
|
+
const filePath = path3.join(cliDir, entry);
|
|
988
1196
|
let stats;
|
|
989
1197
|
try {
|
|
990
|
-
stats =
|
|
1198
|
+
stats = fs3.statSync(filePath);
|
|
991
1199
|
} catch {
|
|
992
1200
|
continue;
|
|
993
1201
|
}
|
|
@@ -995,21 +1203,21 @@ ${Date.now()}
|
|
|
995
1203
|
continue;
|
|
996
1204
|
}
|
|
997
1205
|
try {
|
|
998
|
-
|
|
1206
|
+
fs3.unlinkSync(filePath);
|
|
999
1207
|
} catch (err) {
|
|
1000
|
-
|
|
1001
|
-
`Failed to delete temp CLI
|
|
1208
|
+
BStackLogger2.debug(
|
|
1209
|
+
`Failed to delete temp CLI file ${filePath}: ${util.format(err)}`
|
|
1002
1210
|
);
|
|
1003
1211
|
}
|
|
1004
1212
|
}
|
|
1005
1213
|
} catch (err) {
|
|
1006
|
-
|
|
1007
|
-
`Failed to scan temp CLI
|
|
1214
|
+
BStackLogger2.debug(
|
|
1215
|
+
`Failed to scan temp CLI files in ${cliDir}: ${util.format(err)}`
|
|
1008
1216
|
);
|
|
1009
1217
|
}
|
|
1010
1218
|
};
|
|
1011
1219
|
PerformanceTester.start(EVENTS.SDK_CLI_DOWNLOAD);
|
|
1012
|
-
|
|
1220
|
+
BStackLogger2.debug(`Downloading SDK binary from: ${binDownloadUrl}`);
|
|
1013
1221
|
let downloadEnded = false;
|
|
1014
1222
|
const endDownload = (success = true, errMsg) => {
|
|
1015
1223
|
if (downloadEnded) {
|
|
@@ -1035,8 +1243,8 @@ ${Date.now()}
|
|
|
1035
1243
|
}
|
|
1036
1244
|
releaseLock = lockResult;
|
|
1037
1245
|
const existingBinary = _CLIUtils.getExistingCliPath(cliDir);
|
|
1038
|
-
if (existingBinary &&
|
|
1039
|
-
|
|
1246
|
+
if (existingBinary && fs3.existsSync(existingBinary) && fs3.statSync(existingBinary).size > 0) {
|
|
1247
|
+
BStackLogger2.debug(
|
|
1040
1248
|
`Binary already exists after acquiring lock: ${existingBinary}`
|
|
1041
1249
|
);
|
|
1042
1250
|
endDownload();
|
|
@@ -1044,13 +1252,12 @@ ${Date.now()}
|
|
|
1044
1252
|
return existingBinary;
|
|
1045
1253
|
}
|
|
1046
1254
|
cleanupTemporaryDownloads();
|
|
1047
|
-
const zipFilePath =
|
|
1255
|
+
const zipFilePath = path3.join(
|
|
1048
1256
|
cliDir,
|
|
1049
1257
|
`${CLI_DOWNLOAD_TMP_PREFIX}${process.pid}_${Date.now()}${CLI_DOWNLOAD_TMP_SUFFIX}`
|
|
1050
1258
|
);
|
|
1051
|
-
const downloadedFileStream =
|
|
1259
|
+
const downloadedFileStream = fs3.createWriteStream(zipFilePath);
|
|
1052
1260
|
return new Promise((resolve, reject) => {
|
|
1053
|
-
const binaryName = null;
|
|
1054
1261
|
const processDownload = async () => {
|
|
1055
1262
|
const abortController = new AbortController();
|
|
1056
1263
|
const timeout = setTimeout(
|
|
@@ -1059,9 +1266,11 @@ ${Date.now()}
|
|
|
1059
1266
|
);
|
|
1060
1267
|
let response;
|
|
1061
1268
|
try {
|
|
1062
|
-
response = await _fetch(
|
|
1063
|
-
|
|
1064
|
-
|
|
1269
|
+
response = await _fetch(
|
|
1270
|
+
binDownloadUrl,
|
|
1271
|
+
{ signal: abortController.signal },
|
|
1272
|
+
{ connectTimeoutMs: CLI_CONNECT_TIMEOUT_MS }
|
|
1273
|
+
);
|
|
1065
1274
|
} finally {
|
|
1066
1275
|
clearTimeout(timeout);
|
|
1067
1276
|
}
|
|
@@ -1069,7 +1278,7 @@ ${Date.now()}
|
|
|
1069
1278
|
throw new Error("No response body received");
|
|
1070
1279
|
}
|
|
1071
1280
|
downloadedFileStream.on("error", function(err) {
|
|
1072
|
-
|
|
1281
|
+
BStackLogger2.error(
|
|
1073
1282
|
`Got Error while downloading cli binary file: ${err}`
|
|
1074
1283
|
);
|
|
1075
1284
|
endDownload(false, util.format(err));
|
|
@@ -1084,7 +1293,6 @@ ${Date.now()}
|
|
|
1084
1293
|
nodeStream.pipe(downloadedFileStream);
|
|
1085
1294
|
_CLIUtils.downloadFileStream(
|
|
1086
1295
|
downloadedFileStream,
|
|
1087
|
-
binaryName,
|
|
1088
1296
|
zipFilePath,
|
|
1089
1297
|
cliDir,
|
|
1090
1298
|
(result) => {
|
|
@@ -1099,7 +1307,7 @@ ${Date.now()}
|
|
|
1099
1307
|
}
|
|
1100
1308
|
);
|
|
1101
1309
|
} catch (err) {
|
|
1102
|
-
|
|
1310
|
+
BStackLogger2.error(
|
|
1103
1311
|
`Got Error in cli binary downloading request ${util.format(err)}`
|
|
1104
1312
|
);
|
|
1105
1313
|
endDownload(false, util.format(err));
|
|
@@ -1112,46 +1320,110 @@ ${Date.now()}
|
|
|
1112
1320
|
} catch (err) {
|
|
1113
1321
|
releaseLock?.();
|
|
1114
1322
|
endDownload(false, util.format(err));
|
|
1115
|
-
|
|
1323
|
+
BStackLogger2.debug(
|
|
1116
1324
|
`Failed to download binary, Exception: ${util.format(err)}`
|
|
1117
1325
|
);
|
|
1118
1326
|
return null;
|
|
1119
1327
|
}
|
|
1120
1328
|
};
|
|
1121
|
-
static downloadFileStream(downloadedFileStream,
|
|
1329
|
+
static downloadFileStream(downloadedFileStream, zipFilePath, cliDir, resolve, reject) {
|
|
1122
1330
|
downloadedFileStream.on("close", async function() {
|
|
1123
1331
|
const yauzlOpenPromise = promisify(yauzl.open);
|
|
1124
1332
|
try {
|
|
1125
1333
|
const zipfile = await yauzlOpenPromise(zipFilePath, {
|
|
1126
1334
|
lazyEntries: true
|
|
1127
1335
|
});
|
|
1336
|
+
let resolvedBinaryPath = null;
|
|
1128
1337
|
zipfile.readEntry();
|
|
1129
1338
|
zipfile.on("entry", async (entry) => {
|
|
1130
|
-
if (!binaryName) {
|
|
1131
|
-
binaryName = entry.fileName;
|
|
1132
|
-
}
|
|
1133
1339
|
if (/\/$/.test(entry.fileName)) {
|
|
1134
1340
|
zipfile.readEntry();
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
const candidatePath = path3.join(cliDir, entry.fileName);
|
|
1344
|
+
const resolvedCandidate = path3.resolve(candidatePath);
|
|
1345
|
+
const resolvedDir = path3.resolve(cliDir) + path3.sep;
|
|
1346
|
+
if (!resolvedCandidate.startsWith(resolvedDir)) {
|
|
1347
|
+
zipfile.close();
|
|
1348
|
+
reject(new Error(`Zip-slip detected: entry "${entry.fileName}" resolves outside ${cliDir}`));
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
const isBinaryEntry = path3.basename(entry.fileName).startsWith("binary-");
|
|
1352
|
+
if (!isBinaryEntry) {
|
|
1353
|
+
const directStream = fs3.createWriteStream(candidatePath);
|
|
1354
|
+
directStream.on("error", (writeErr) => {
|
|
1355
|
+
zipfile.close();
|
|
1356
|
+
reject(writeErr);
|
|
1357
|
+
});
|
|
1358
|
+
const openReadStreamPromise2 = promisify(
|
|
1140
1359
|
zipfile.openReadStream
|
|
1141
1360
|
).bind(zipfile);
|
|
1142
1361
|
try {
|
|
1143
|
-
const readStream = await
|
|
1362
|
+
const readStream = await openReadStreamPromise2(entry);
|
|
1144
1363
|
readStream.on("end", function() {
|
|
1145
|
-
|
|
1146
|
-
zipfile.readEntry();
|
|
1364
|
+
directStream.end();
|
|
1365
|
+
directStream.on("close", () => zipfile.readEntry());
|
|
1147
1366
|
});
|
|
1148
|
-
readStream.pipe(
|
|
1367
|
+
readStream.pipe(directStream);
|
|
1149
1368
|
} catch (zipErr) {
|
|
1369
|
+
zipfile.close();
|
|
1150
1370
|
reject(zipErr);
|
|
1151
1371
|
}
|
|
1152
|
-
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
const finalPath = candidatePath;
|
|
1375
|
+
const tempPath = path3.join(cliDir, `${entry.fileName}.tmp.${process.pid}`);
|
|
1376
|
+
const writeStream = fs3.createWriteStream(tempPath);
|
|
1377
|
+
let writeStreamErrored = false;
|
|
1378
|
+
writeStream.on("error", (writeErr) => {
|
|
1379
|
+
writeStreamErrored = true;
|
|
1380
|
+
fsp.unlink(tempPath).catch(() => {
|
|
1381
|
+
});
|
|
1382
|
+
zipfile.close();
|
|
1383
|
+
reject(writeErr);
|
|
1384
|
+
});
|
|
1385
|
+
writeStream.on("close", async () => {
|
|
1386
|
+
if (writeStreamErrored) {
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
try {
|
|
1390
|
+
await fsp.chmod(tempPath, "0755");
|
|
1391
|
+
try {
|
|
1392
|
+
await fsp.rename(tempPath, finalPath);
|
|
1393
|
+
} catch (renameErr) {
|
|
1394
|
+
if (renameErr.code !== "EXDEV") {
|
|
1395
|
+
throw renameErr;
|
|
1396
|
+
}
|
|
1397
|
+
BStackLogger2.warn(`Atomic rename failed (cross-device), falling back to copy: ${renameErr.message}`);
|
|
1398
|
+
await fsp.copyFile(tempPath, finalPath);
|
|
1399
|
+
await fsp.unlink(tempPath).catch(() => {
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
if (!resolvedBinaryPath) {
|
|
1403
|
+
resolvedBinaryPath = finalPath;
|
|
1404
|
+
}
|
|
1405
|
+
zipfile.readEntry();
|
|
1406
|
+
} catch (err) {
|
|
1407
|
+
await fsp.unlink(tempPath).catch(() => {
|
|
1408
|
+
});
|
|
1153
1409
|
zipfile.close();
|
|
1410
|
+
reject(err);
|
|
1154
1411
|
}
|
|
1412
|
+
});
|
|
1413
|
+
const openReadStreamPromise = promisify(
|
|
1414
|
+
zipfile.openReadStream
|
|
1415
|
+
).bind(zipfile);
|
|
1416
|
+
try {
|
|
1417
|
+
const readStream = await openReadStreamPromise(entry);
|
|
1418
|
+
readStream.on("end", function() {
|
|
1419
|
+
writeStream.end();
|
|
1420
|
+
});
|
|
1421
|
+
readStream.pipe(writeStream);
|
|
1422
|
+
} catch (zipErr) {
|
|
1423
|
+
fsp.unlink(tempPath).catch(() => {
|
|
1424
|
+
});
|
|
1425
|
+
zipfile.close();
|
|
1426
|
+
reject(zipErr);
|
|
1155
1427
|
}
|
|
1156
1428
|
});
|
|
1157
1429
|
zipfile.on("error", (zipErr) => {
|
|
@@ -1159,16 +1431,15 @@ ${Date.now()}
|
|
|
1159
1431
|
});
|
|
1160
1432
|
zipfile.once("end", () => {
|
|
1161
1433
|
fsp.unlink(zipFilePath).catch(() => {
|
|
1162
|
-
|
|
1163
|
-
`Failed to delete zip file: ${zipFilePath}`
|
|
1164
|
-
);
|
|
1165
|
-
});
|
|
1166
|
-
fsp.chmod(`${cliDir}/${binaryName}`, "0755").then(() => {
|
|
1167
|
-
resolve(`${cliDir}/${binaryName}`);
|
|
1168
|
-
}).catch((err) => {
|
|
1169
|
-
reject(err);
|
|
1434
|
+
BStackLogger2.warn(`Failed to delete zip file: ${zipFilePath}`);
|
|
1170
1435
|
});
|
|
1436
|
+
if (!resolvedBinaryPath) {
|
|
1437
|
+
zipfile.close();
|
|
1438
|
+
reject(new Error("No binary-* entry found in zip; cannot complete CLI binary extraction"));
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1171
1441
|
zipfile.close();
|
|
1442
|
+
resolve(resolvedBinaryPath);
|
|
1172
1443
|
});
|
|
1173
1444
|
} catch (err) {
|
|
1174
1445
|
reject(err);
|
|
@@ -1191,7 +1462,7 @@ ${Date.now()}
|
|
|
1191
1462
|
}
|
|
1192
1463
|
static setFrameworkDetail(testFramework, automationFramework) {
|
|
1193
1464
|
if (!testFramework || !automationFramework) {
|
|
1194
|
-
|
|
1465
|
+
BStackLogger2.debug(
|
|
1195
1466
|
`Test or Automation framework not provided testFramework=${testFramework}, automationFramework=${automationFramework}`
|
|
1196
1467
|
);
|
|
1197
1468
|
}
|
|
@@ -1273,12 +1544,12 @@ ${Date.now()}
|
|
|
1273
1544
|
|
|
1274
1545
|
// src/instrumentation/performance/performance-tester.ts
|
|
1275
1546
|
import { createObjectCsvWriter } from "csv-writer";
|
|
1276
|
-
import
|
|
1547
|
+
import fs4 from "node:fs";
|
|
1277
1548
|
import fsPromise from "node:fs/promises";
|
|
1278
1549
|
import { performance, PerformanceObserver } from "node:perf_hooks";
|
|
1279
1550
|
import util2 from "node:util";
|
|
1280
1551
|
import worker from "node:worker_threads";
|
|
1281
|
-
import
|
|
1552
|
+
import path4 from "node:path";
|
|
1282
1553
|
import { arch as arch2, hostname, platform as platform2, type, version } from "node:os";
|
|
1283
1554
|
var PerformanceTester;
|
|
1284
1555
|
var init_performance_tester = __esm({
|
|
@@ -1303,11 +1574,11 @@ var init_performance_tester = __esm({
|
|
|
1303
1574
|
static browser;
|
|
1304
1575
|
static scenarioThatRan;
|
|
1305
1576
|
static jsonReportDirName = "performance-report";
|
|
1306
|
-
static jsonReportDirPath =
|
|
1577
|
+
static jsonReportDirPath = path4.join(process.cwd(), "logs", this.jsonReportDirName);
|
|
1307
1578
|
static jsonReportFileName = `${this.jsonReportDirPath}/performance-report-${_PerformanceTester.getProcessId()}.json`;
|
|
1308
1579
|
static startMonitoring(csvName = "performance-report.csv") {
|
|
1309
|
-
if (!
|
|
1310
|
-
|
|
1580
|
+
if (!fs4.existsSync(this.jsonReportDirPath)) {
|
|
1581
|
+
fs4.mkdirSync(this.jsonReportDirPath, { recursive: true });
|
|
1311
1582
|
}
|
|
1312
1583
|
this._observer = new PerformanceObserver((list) => {
|
|
1313
1584
|
list.getEntries().filter((entry) => entry.entryType === "measure").forEach(
|
|
@@ -1317,10 +1588,10 @@ var init_performance_tester = __esm({
|
|
|
1317
1588
|
if (typeof finalEntry.startTime === "number" && typeof performance.timeOrigin === "number") {
|
|
1318
1589
|
const originalStartTime = finalEntry.startTime;
|
|
1319
1590
|
finalEntry.startTime = performance.timeOrigin + finalEntry.startTime;
|
|
1320
|
-
|
|
1591
|
+
BStackLogger.debug(`Timestamp conversion for ${entry.name}: ${originalStartTime} -> ${finalEntry.startTime} (timeOrigin: ${performance.timeOrigin})`);
|
|
1321
1592
|
}
|
|
1322
1593
|
} catch (e) {
|
|
1323
|
-
|
|
1594
|
+
BStackLogger.debug(`Error converting startTime to epoch: ${util2.format(e)}`);
|
|
1324
1595
|
}
|
|
1325
1596
|
if (this.details[entry.name]) {
|
|
1326
1597
|
finalEntry = Object.assign(finalEntry, this.details[entry.name]);
|
|
@@ -1360,7 +1631,7 @@ var init_performance_tester = __esm({
|
|
|
1360
1631
|
const timeTaken = methods.reduce((a, c) => {
|
|
1361
1632
|
return times[c] + (a || 0);
|
|
1362
1633
|
}, 0);
|
|
1363
|
-
|
|
1634
|
+
BStackLogger.debug(`Time for ${methods} is ${timeTaken}`);
|
|
1364
1635
|
return timeTaken;
|
|
1365
1636
|
}
|
|
1366
1637
|
static async stopAndGenerate(filename = "performance-own.html") {
|
|
@@ -1372,7 +1643,7 @@ var init_performance_tester = __esm({
|
|
|
1372
1643
|
const finalJSONStr = eventsJson.slice(1, -1) + ",";
|
|
1373
1644
|
await fsPromise.appendFile(this.jsonReportFileName, finalJSONStr);
|
|
1374
1645
|
} catch (er) {
|
|
1375
|
-
|
|
1646
|
+
BStackLogger.debug(`Failed to write events of the worker to ${this.jsonReportFileName}: ${util2.format(er)}`);
|
|
1376
1647
|
}
|
|
1377
1648
|
this._observer.disconnect();
|
|
1378
1649
|
if (!process.env[PERF_MEASUREMENT_ENV]) {
|
|
@@ -1381,12 +1652,12 @@ var init_performance_tester = __esm({
|
|
|
1381
1652
|
this.started = false;
|
|
1382
1653
|
this.generateCSV(this._events);
|
|
1383
1654
|
const content = this.generateReport(this._events);
|
|
1384
|
-
const dir =
|
|
1655
|
+
const dir = path4.join(process.cwd(), filename);
|
|
1385
1656
|
try {
|
|
1386
1657
|
await fsPromise.writeFile(dir, content);
|
|
1387
|
-
|
|
1658
|
+
BStackLogger.info(`Performance report is at ${path4}`);
|
|
1388
1659
|
} catch (err) {
|
|
1389
|
-
|
|
1660
|
+
BStackLogger.error(`Error in writing html ${util2.format(err)}`);
|
|
1390
1661
|
}
|
|
1391
1662
|
}
|
|
1392
1663
|
static generateReport(entries) {
|
|
@@ -1417,7 +1688,7 @@ var init_performance_tester = __esm({
|
|
|
1417
1688
|
time: value
|
|
1418
1689
|
};
|
|
1419
1690
|
});
|
|
1420
|
-
this._csvWriter.writeRecords(dat).then(() =>
|
|
1691
|
+
this._csvWriter.writeRecords(dat).then(() => BStackLogger.info("Performance CSV report generated successfully")).catch((error) => console.error(error));
|
|
1421
1692
|
}
|
|
1422
1693
|
static Measure(label, details = {}) {
|
|
1423
1694
|
const self = this;
|
|
@@ -1525,6 +1796,7 @@ var init_performance_tester = __esm({
|
|
|
1525
1796
|
static end(event, success = true, failure, details = {}) {
|
|
1526
1797
|
performance.mark(event + "-end");
|
|
1527
1798
|
performance.measure(event, event + "-start", event + "-end");
|
|
1799
|
+
delete this.eventsMap[event + "-start"];
|
|
1528
1800
|
this.details[event] = Object.assign({ success, failure: util2.format(failure) }, Object.assign(Object.assign({
|
|
1529
1801
|
clientWorkerId: _PerformanceTester.getClientWorkerId(),
|
|
1530
1802
|
worker: _PerformanceTester.getProcessId(),
|
|
@@ -1550,16 +1822,16 @@ var init_performance_tester = __esm({
|
|
|
1550
1822
|
this.start(EVENTS.SDK_SEND_KEY_METRICS);
|
|
1551
1823
|
try {
|
|
1552
1824
|
const workerId = `${process.pid}`;
|
|
1553
|
-
|
|
1825
|
+
BStackLogger.debug(`[Performance Upload] Starting upload for worker ${workerId}`);
|
|
1554
1826
|
this.start(EVENTS.SDK_KEY_METRICS_PREPARATION);
|
|
1555
1827
|
let measures = [];
|
|
1556
1828
|
if (await fsPromise.access(this.jsonReportDirPath).then(() => true).catch(() => false)) {
|
|
1557
|
-
const files = (await fsPromise.readdir(this.jsonReportDirPath)).map((file) =>
|
|
1829
|
+
const files = (await fsPromise.readdir(this.jsonReportDirPath)).map((file) => path4.resolve(this.jsonReportDirPath, file));
|
|
1558
1830
|
measures = (await Promise.all(files.map((file) => fsPromise.readFile(file, "utf-8")))).map((el) => `[${el.slice(0, -1)}]`).map((el) => JSON.parse(el)).flat();
|
|
1559
1831
|
}
|
|
1560
|
-
|
|
1832
|
+
BStackLogger.debug(`[Performance Upload] Total events from files: ${measures.length}`);
|
|
1561
1833
|
if (this._measuredEvents.length > 0) {
|
|
1562
|
-
|
|
1834
|
+
BStackLogger.debug(`[Performance Upload] Adding ${this._measuredEvents.length} in-memory events`);
|
|
1563
1835
|
measures = measures.concat(
|
|
1564
1836
|
this._measuredEvents.map((e) => {
|
|
1565
1837
|
if (typeof e.toJSON === "function") {
|
|
@@ -1620,10 +1892,10 @@ var init_performance_tester = __esm({
|
|
|
1620
1892
|
},
|
|
1621
1893
|
body: JSON.stringify(payload)
|
|
1622
1894
|
});
|
|
1623
|
-
|
|
1895
|
+
BStackLogger.debug(`[Performance Upload] Successfully uploaded to EDS: ${util2.format(await result.text())}`);
|
|
1624
1896
|
this.end(EVENTS.SDK_SEND_KEY_METRICS, true);
|
|
1625
1897
|
} catch (er) {
|
|
1626
|
-
|
|
1898
|
+
BStackLogger.debug(`[Performance Upload] Failed to upload events: ${util2.format(er)}`);
|
|
1627
1899
|
this.end(EVENTS.SDK_KEY_METRICS_PREPARATION, false, er);
|
|
1628
1900
|
this.end(EVENTS.SDK_SEND_KEY_METRICS, false, er);
|
|
1629
1901
|
}
|
|
@@ -1631,12 +1903,12 @@ var init_performance_tester = __esm({
|
|
|
1631
1903
|
if (await fsPromise.access(this.jsonReportDirPath).then(() => true, () => false)) {
|
|
1632
1904
|
const files = await fsPromise.readdir(this.jsonReportDirPath);
|
|
1633
1905
|
for (const file of files) {
|
|
1634
|
-
await fsPromise.unlink(
|
|
1906
|
+
await fsPromise.unlink(path4.join(this.jsonReportDirPath, file));
|
|
1635
1907
|
}
|
|
1636
|
-
|
|
1908
|
+
BStackLogger.debug(`[Performance Upload] Cleaned up ${files.length} temporary report files`);
|
|
1637
1909
|
}
|
|
1638
1910
|
} catch (er) {
|
|
1639
|
-
|
|
1911
|
+
BStackLogger.debug(`[Performance Upload] Failed to delete temporary files: ${util2.format(er)}`);
|
|
1640
1912
|
}
|
|
1641
1913
|
}
|
|
1642
1914
|
};
|
|
@@ -1644,7 +1916,7 @@ var init_performance_tester = __esm({
|
|
|
1644
1916
|
});
|
|
1645
1917
|
|
|
1646
1918
|
// src/testorchestration/testorcherstrationutils.ts
|
|
1647
|
-
import
|
|
1919
|
+
import fs5 from "node:fs";
|
|
1648
1920
|
var RUN_SMART_SELECTION, ALLOWED_ORCHESTRATION_KEYS, isPlainObject, asString, TestOrdering, OrchestrationUtils;
|
|
1649
1921
|
var init_testorcherstrationutils = __esm({
|
|
1650
1922
|
"src/testorchestration/testorcherstrationutils.ts"() {
|
|
@@ -1712,7 +1984,7 @@ var init_testorcherstrationutils = __esm({
|
|
|
1712
1984
|
for (const service of services) {
|
|
1713
1985
|
if (Array.isArray(service) && service[0] === "browserstack" && isPlainObject(service[1]) && isPlainObject(service[1].testOrchestrationOptions)) {
|
|
1714
1986
|
testOrchOptions = service[1].testOrchestrationOptions;
|
|
1715
|
-
|
|
1987
|
+
BStackLogger.debug("[constructor] Found testOrchestrationOptions in browserstack service config");
|
|
1716
1988
|
break;
|
|
1717
1989
|
}
|
|
1718
1990
|
}
|
|
@@ -1741,9 +2013,9 @@ var init_testorcherstrationutils = __esm({
|
|
|
1741
2013
|
}
|
|
1742
2014
|
});
|
|
1743
2015
|
}
|
|
1744
|
-
|
|
2016
|
+
BStackLogger.debug(`[_extractBuildDetails] Extracted - projectName: ${this.projectName}, buildName: ${this.buildName}, buildIdentifier: ${this.buildIdentifier}`);
|
|
1745
2017
|
} catch (error) {
|
|
1746
|
-
|
|
2018
|
+
BStackLogger.error(`[_extractBuildDetails] ${error}`);
|
|
1747
2019
|
}
|
|
1748
2020
|
}
|
|
1749
2021
|
_parseCapability(capability) {
|
|
@@ -1841,7 +2113,7 @@ var init_testorcherstrationutils = __esm({
|
|
|
1841
2113
|
this.projectName = projectName;
|
|
1842
2114
|
this.buildName = buildName;
|
|
1843
2115
|
this.buildIdentifier = buildIdentifier;
|
|
1844
|
-
|
|
2116
|
+
BStackLogger.debug(`[setBuildDetails] Set - projectName: ${this.projectName}, buildName: ${this.buildName}, buildIdentifier: ${this.buildIdentifier}`);
|
|
1845
2117
|
}
|
|
1846
2118
|
/**
|
|
1847
2119
|
* Set run smart selection
|
|
@@ -1851,27 +2123,27 @@ var init_testorcherstrationutils = __esm({
|
|
|
1851
2123
|
this.runSmartSelection = isValidEnabledValue(enabled);
|
|
1852
2124
|
this.smartSelectionMode = mode;
|
|
1853
2125
|
this.smartSelectionSource = [];
|
|
1854
|
-
|
|
2126
|
+
BStackLogger.debug(`Setting runSmartSelection: enabled=${this.runSmartSelection}, mode=${this.smartSelectionMode}`);
|
|
1855
2127
|
if (this.runSmartSelection) {
|
|
1856
2128
|
const validModes = [SMART_SELECTION_MODE_RELEVANT_FIRST, SMART_SELECTION_MODE_RELEVANT_ONLY];
|
|
1857
2129
|
if (!validModes.includes(this.smartSelectionMode)) {
|
|
1858
|
-
|
|
2130
|
+
BStackLogger.warn(`Invalid smart selection mode '${this.smartSelectionMode}' provided. Defaulting to '${SMART_SELECTION_MODE_RELEVANT_FIRST}'.`);
|
|
1859
2131
|
this.smartSelectionMode = SMART_SELECTION_MODE_RELEVANT_FIRST;
|
|
1860
2132
|
}
|
|
1861
2133
|
if (source === null) {
|
|
1862
2134
|
this.smartSelectionSource = null;
|
|
1863
|
-
|
|
2135
|
+
BStackLogger.debug("No source provided for smart selection; defaulting to null.");
|
|
1864
2136
|
} else if (Array.isArray(source)) {
|
|
1865
2137
|
this.smartSelectionSource = source;
|
|
1866
|
-
|
|
2138
|
+
BStackLogger.debug(`Smart selection source set to array: ${JSON.stringify(source)}`);
|
|
1867
2139
|
} else if (typeof source === "string" && source.endsWith(".json")) {
|
|
1868
2140
|
this.smartSelectionSource = this._loadSourceFromFile(source) || [];
|
|
1869
|
-
|
|
2141
|
+
BStackLogger.debug(`Smart selection source loaded from file: ${source}`);
|
|
1870
2142
|
}
|
|
1871
2143
|
this._setTestOrdering();
|
|
1872
2144
|
}
|
|
1873
2145
|
} catch (e) {
|
|
1874
|
-
|
|
2146
|
+
BStackLogger.error(`[_setRunSmartSelection] ${e}`);
|
|
1875
2147
|
}
|
|
1876
2148
|
}
|
|
1877
2149
|
/**
|
|
@@ -1881,17 +2153,17 @@ var init_testorcherstrationutils = __esm({
|
|
|
1881
2153
|
* @returns Formatted list of repository configurations
|
|
1882
2154
|
*/
|
|
1883
2155
|
_loadSourceFromFile(filePath) {
|
|
1884
|
-
if (!
|
|
1885
|
-
|
|
2156
|
+
if (!fs5.existsSync(filePath)) {
|
|
2157
|
+
BStackLogger.error(`Source file '${filePath}' does not exist.`);
|
|
1886
2158
|
return [];
|
|
1887
2159
|
}
|
|
1888
2160
|
let data = null;
|
|
1889
2161
|
try {
|
|
1890
|
-
const fileContent =
|
|
2162
|
+
const fileContent = fs5.readFileSync(filePath, "utf8");
|
|
1891
2163
|
data = JSON.parse(fileContent);
|
|
1892
2164
|
} catch (error) {
|
|
1893
2165
|
const message = error instanceof Error ? error.message : String(error);
|
|
1894
|
-
|
|
2166
|
+
BStackLogger.error(`Error parsing JSON from source file '${filePath}': ${message}`);
|
|
1895
2167
|
return [];
|
|
1896
2168
|
}
|
|
1897
2169
|
let featureBranchEnvMap = null;
|
|
@@ -1907,9 +2179,9 @@ var init_testorcherstrationutils = __esm({
|
|
|
1907
2179
|
return acc;
|
|
1908
2180
|
}, {});
|
|
1909
2181
|
} catch (error) {
|
|
1910
|
-
|
|
2182
|
+
BStackLogger.error(`Error parsing feature branch mappings: ${error}`);
|
|
1911
2183
|
}
|
|
1912
|
-
|
|
2184
|
+
BStackLogger.debug(`Feature branch mappings from env: ${JSON.stringify(envMap)}`);
|
|
1913
2185
|
return envMap;
|
|
1914
2186
|
};
|
|
1915
2187
|
featureBranchEnvMap = loadFeatureBranchMaps();
|
|
@@ -1931,23 +2203,23 @@ var init_testorcherstrationutils = __esm({
|
|
|
1931
2203
|
}
|
|
1932
2204
|
const typedRepoInfo = repoInfo;
|
|
1933
2205
|
if (!typedRepoInfo.url || typeof typedRepoInfo.url !== "string") {
|
|
1934
|
-
|
|
2206
|
+
BStackLogger.warn(`Repository URL is missing or invalid for source '${name}': ${JSON.stringify(repoInfo)}`);
|
|
1935
2207
|
continue;
|
|
1936
2208
|
}
|
|
1937
2209
|
if (typedRepoInfo.baseBranch !== void 0 && typeof typedRepoInfo.baseBranch !== "string") {
|
|
1938
|
-
|
|
2210
|
+
BStackLogger.warn(`Base branch must be a string for source '${name}': ${JSON.stringify(repoInfo)}`);
|
|
1939
2211
|
continue;
|
|
1940
2212
|
}
|
|
1941
2213
|
if (typedRepoInfo.featureBranch !== void 0 && typeof typedRepoInfo.featureBranch !== "string") {
|
|
1942
|
-
|
|
2214
|
+
BStackLogger.warn(`Feature branch must be a string for source '${name}': ${JSON.stringify(repoInfo)}`);
|
|
1943
2215
|
continue;
|
|
1944
2216
|
}
|
|
1945
2217
|
if (!namePattern.test(name)) {
|
|
1946
|
-
|
|
2218
|
+
BStackLogger.warn(`Invalid source identifier format for '${name}': ${JSON.stringify(repoInfo)}`);
|
|
1947
2219
|
continue;
|
|
1948
2220
|
}
|
|
1949
2221
|
if (name.length > 30 || name.length < 1) {
|
|
1950
|
-
|
|
2222
|
+
BStackLogger.warn(`Source identifier '${name}' must have a length between 1 and 30 characters.`);
|
|
1951
2223
|
continue;
|
|
1952
2224
|
}
|
|
1953
2225
|
const featureBranch = getFeatureBranch(name, typedRepoInfo);
|
|
@@ -1960,11 +2232,11 @@ var init_testorcherstrationutils = __esm({
|
|
|
1960
2232
|
filteredRepoInfo.baseBranch = typedRepoInfo.baseBranch;
|
|
1961
2233
|
}
|
|
1962
2234
|
if (!filteredRepoInfo.featureBranch || filteredRepoInfo.featureBranch === "") {
|
|
1963
|
-
|
|
2235
|
+
BStackLogger.warn(`Feature branch not specified for source '${name}': ${JSON.stringify(repoInfo)}`);
|
|
1964
2236
|
continue;
|
|
1965
2237
|
}
|
|
1966
2238
|
if (filteredRepoInfo.baseBranch && filteredRepoInfo.baseBranch === filteredRepoInfo.featureBranch) {
|
|
1967
|
-
|
|
2239
|
+
BStackLogger.warn(`Feature branch and base branch cannot be the same for source '${name}': ${JSON.stringify(repoInfo)}`);
|
|
1968
2240
|
continue;
|
|
1969
2241
|
}
|
|
1970
2242
|
formattedData.push(filteredRepoInfo);
|
|
@@ -2074,17 +2346,17 @@ var init_crash_reporter = __esm({
|
|
|
2074
2346
|
this.credentialsForCrashReportUpload = process.env.CREDENTIALS_FOR_CRASH_REPORTING !== void 0 ? JSON.parse(process.env.CREDENTIALS_FOR_CRASH_REPORTING) : this.credentialsForCrashReportUpload;
|
|
2075
2347
|
}
|
|
2076
2348
|
} catch (error) {
|
|
2077
|
-
return
|
|
2349
|
+
return BStackLogger.error(`[Crash_Report_Upload] Failed to parse user credentials while reporting crash due to ${error}`);
|
|
2078
2350
|
}
|
|
2079
2351
|
if (!this.credentialsForCrashReportUpload.username || !this.credentialsForCrashReportUpload.password) {
|
|
2080
|
-
return
|
|
2352
|
+
return BStackLogger.error("[Crash_Report_Upload] Failed to parse user credentials while reporting crash");
|
|
2081
2353
|
}
|
|
2082
2354
|
try {
|
|
2083
2355
|
if (Object.keys(this.userConfigForReporting).length === 0) {
|
|
2084
2356
|
this.userConfigForReporting = process.env.USER_CONFIG_FOR_REPORTING !== void 0 ? JSON.parse(process.env.USER_CONFIG_FOR_REPORTING) : {};
|
|
2085
2357
|
}
|
|
2086
2358
|
} catch (error) {
|
|
2087
|
-
|
|
2359
|
+
BStackLogger.error(`[Crash_Report_Upload] Failed to parse user config while reporting crash due to ${error}`);
|
|
2088
2360
|
this.userConfigForReporting = {};
|
|
2089
2361
|
}
|
|
2090
2362
|
const data = {
|
|
@@ -2116,9 +2388,9 @@ var init_crash_reporter = __esm({
|
|
|
2116
2388
|
body = JSON.stringify(JSON.parse(body));
|
|
2117
2389
|
} catch {
|
|
2118
2390
|
}
|
|
2119
|
-
|
|
2391
|
+
BStackLogger.debug(`[Crash_Report_Upload] Success response: ${body}`);
|
|
2120
2392
|
} else {
|
|
2121
|
-
|
|
2393
|
+
BStackLogger.error(`[Crash_Report_Upload] Failed due to ${response.body}`);
|
|
2122
2394
|
}
|
|
2123
2395
|
}
|
|
2124
2396
|
static recursivelyRedactKeysFromObject(obj, keys) {
|
|
@@ -2178,7 +2450,7 @@ var init_crash_reporter = __esm({
|
|
|
2178
2450
|
}
|
|
2179
2451
|
}
|
|
2180
2452
|
} catch (err) {
|
|
2181
|
-
|
|
2453
|
+
BStackLogger.error(`Error in parsing user config PII with error ${err ? err.stack || err : err}`);
|
|
2182
2454
|
return configWithoutPII;
|
|
2183
2455
|
}
|
|
2184
2456
|
configWithoutPII.services = finalServices;
|
|
@@ -2213,7 +2485,7 @@ var init_featureStats = __esm({
|
|
|
2213
2485
|
this.failed(groupId);
|
|
2214
2486
|
break;
|
|
2215
2487
|
default:
|
|
2216
|
-
|
|
2488
|
+
BStackLogger.debug("Request to mark usage for unknown status - " + status);
|
|
2217
2489
|
break;
|
|
2218
2490
|
}
|
|
2219
2491
|
}
|
|
@@ -2449,7 +2721,7 @@ var init_usageStats = __esm({
|
|
|
2449
2721
|
try {
|
|
2450
2722
|
usage.events = this.getEventsData();
|
|
2451
2723
|
} catch (e) {
|
|
2452
|
-
|
|
2724
|
+
BStackLogger.debug("exception in getFormattedData: " + e);
|
|
2453
2725
|
}
|
|
2454
2726
|
return usage;
|
|
2455
2727
|
}
|
|
@@ -2459,7 +2731,7 @@ var init_usageStats = __esm({
|
|
|
2459
2731
|
const usageStatsForWorker = _UsageStats.fromJSON(workerData.usageStats);
|
|
2460
2732
|
this.add(usageStatsForWorker);
|
|
2461
2733
|
} catch (e) {
|
|
2462
|
-
|
|
2734
|
+
BStackLogger.debug("Exception in adding workerData: " + e);
|
|
2463
2735
|
}
|
|
2464
2736
|
});
|
|
2465
2737
|
}
|
|
@@ -2513,9 +2785,9 @@ var init_usageStats = __esm({
|
|
|
2513
2785
|
});
|
|
2514
2786
|
|
|
2515
2787
|
// src/scripts/accessibility-scripts.ts
|
|
2516
|
-
import
|
|
2517
|
-
import
|
|
2518
|
-
import
|
|
2788
|
+
import path5 from "node:path";
|
|
2789
|
+
import fs6 from "node:fs";
|
|
2790
|
+
import os2 from "node:os";
|
|
2519
2791
|
var AccessibilityScripts, accessibility_scripts_default;
|
|
2520
2792
|
var init_accessibility_scripts = __esm({
|
|
2521
2793
|
"src/scripts/accessibility-scripts.ts"() {
|
|
@@ -2533,7 +2805,7 @@ var init_accessibility_scripts = __esm({
|
|
|
2533
2805
|
// don't allow to create instances from it other than through `checkAndGetInstance`
|
|
2534
2806
|
constructor() {
|
|
2535
2807
|
this.browserstackFolderPath = this.getWritableDir();
|
|
2536
|
-
this.commandsPath =
|
|
2808
|
+
this.commandsPath = path5.join(this.browserstackFolderPath, "commands.json");
|
|
2537
2809
|
}
|
|
2538
2810
|
static checkAndGetInstance() {
|
|
2539
2811
|
if (!_AccessibilityScripts.instance) {
|
|
@@ -2545,17 +2817,17 @@ var init_accessibility_scripts = __esm({
|
|
|
2545
2817
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2546
2818
|
getWritableDir() {
|
|
2547
2819
|
const orderedPaths = [
|
|
2548
|
-
|
|
2820
|
+
path5.join(os2.homedir(), ".browserstack"),
|
|
2549
2821
|
process.cwd(),
|
|
2550
|
-
|
|
2822
|
+
os2.tmpdir()
|
|
2551
2823
|
];
|
|
2552
2824
|
for (const orderedPath of orderedPaths) {
|
|
2553
2825
|
try {
|
|
2554
|
-
if (
|
|
2555
|
-
|
|
2826
|
+
if (fs6.existsSync(orderedPath)) {
|
|
2827
|
+
fs6.accessSync(orderedPath);
|
|
2556
2828
|
return orderedPath;
|
|
2557
2829
|
}
|
|
2558
|
-
|
|
2830
|
+
fs6.mkdirSync(orderedPath, { recursive: true });
|
|
2559
2831
|
return orderedPath;
|
|
2560
2832
|
} catch (error) {
|
|
2561
2833
|
}
|
|
@@ -2564,8 +2836,8 @@ var init_accessibility_scripts = __esm({
|
|
|
2564
2836
|
}
|
|
2565
2837
|
readFromExistingFile() {
|
|
2566
2838
|
try {
|
|
2567
|
-
if (
|
|
2568
|
-
const data =
|
|
2839
|
+
if (fs6.existsSync(this.commandsPath)) {
|
|
2840
|
+
const data = fs6.readFileSync(this.commandsPath, "utf8");
|
|
2569
2841
|
if (data) {
|
|
2570
2842
|
this.update(JSON.parse(data));
|
|
2571
2843
|
}
|
|
@@ -2588,10 +2860,10 @@ var init_accessibility_scripts = __esm({
|
|
|
2588
2860
|
}
|
|
2589
2861
|
}
|
|
2590
2862
|
store() {
|
|
2591
|
-
if (!
|
|
2592
|
-
|
|
2863
|
+
if (!fs6.existsSync(this.browserstackFolderPath)) {
|
|
2864
|
+
fs6.mkdirSync(this.browserstackFolderPath);
|
|
2593
2865
|
}
|
|
2594
|
-
|
|
2866
|
+
fs6.writeFileSync(this.commandsPath, JSON.stringify({
|
|
2595
2867
|
commands: this.commandsToWrap,
|
|
2596
2868
|
scripts: {
|
|
2597
2869
|
scan: this.performScan,
|
|
@@ -2610,17 +2882,15 @@ var init_accessibility_scripts = __esm({
|
|
|
2610
2882
|
// src/util.ts
|
|
2611
2883
|
import { hostname as hostname2, platform as platform3, type as type2, version as version2, arch as arch3, tmpdir } from "node:os";
|
|
2612
2884
|
import crypto from "node:crypto";
|
|
2613
|
-
import
|
|
2885
|
+
import fs7 from "node:fs";
|
|
2614
2886
|
import zlib from "node:zlib";
|
|
2615
2887
|
import { format, promisify as promisify2 } from "node:util";
|
|
2616
|
-
import
|
|
2888
|
+
import path6 from "node:path";
|
|
2617
2889
|
import util3 from "node:util";
|
|
2618
2890
|
import gitRepoInfo from "git-repo-info";
|
|
2619
2891
|
import gitconfig from "gitconfiglocal";
|
|
2620
|
-
import { FormData as FormData2 } from "formdata-node";
|
|
2621
2892
|
import { performance as performance2 } from "node:perf_hooks";
|
|
2622
2893
|
import { create } from "tar";
|
|
2623
|
-
import { fileFromPath } from "formdata-node/file-from-path";
|
|
2624
2894
|
function getBrowserDescription(cap) {
|
|
2625
2895
|
cap = cap || {};
|
|
2626
2896
|
if (cap["bstack:options"]) {
|
|
@@ -2656,7 +2926,7 @@ function getParentSuiteName(fullTitle, testSuiteTitle) {
|
|
|
2656
2926
|
return parentSuiteName.trim();
|
|
2657
2927
|
}
|
|
2658
2928
|
function processError(error, fn, args) {
|
|
2659
|
-
|
|
2929
|
+
BStackLogger.error(`Error in executing ${fn.name} with args ${args}: ${error}`);
|
|
2660
2930
|
let argsString;
|
|
2661
2931
|
try {
|
|
2662
2932
|
argsString = JSON.stringify(args);
|
|
@@ -2694,7 +2964,7 @@ async function nodeRequest(requestType, apiEndpoint, options, apiUrl, timeout =
|
|
|
2694
2964
|
clearTimeout(timeoutId);
|
|
2695
2965
|
return await response.json();
|
|
2696
2966
|
} catch (error) {
|
|
2697
|
-
|
|
2967
|
+
BStackLogger.debug(`Error in firing request ${apiUrl}/${apiEndpoint}: ${format(error)}`);
|
|
2698
2968
|
const isLogUpload = apiEndpoint === UPLOAD_LOGS_ENDPOINT;
|
|
2699
2969
|
if (error && error.response) {
|
|
2700
2970
|
const errorMessageJson = error.response.body ? JSON.parse(error.response.body.toString()) : null;
|
|
@@ -2702,9 +2972,9 @@ async function nodeRequest(requestType, apiEndpoint, options, apiUrl, timeout =
|
|
|
2702
2972
|
if (errorMessage) {
|
|
2703
2973
|
const message = `${errorMessage} - ${error.stack}`;
|
|
2704
2974
|
if (isLogUpload) {
|
|
2705
|
-
|
|
2975
|
+
BStackLogger.debug(message);
|
|
2706
2976
|
} else {
|
|
2707
|
-
|
|
2977
|
+
BStackLogger.error(message);
|
|
2708
2978
|
}
|
|
2709
2979
|
}
|
|
2710
2980
|
if (isLogUpload) {
|
|
@@ -2713,10 +2983,10 @@ async function nodeRequest(requestType, apiEndpoint, options, apiUrl, timeout =
|
|
|
2713
2983
|
throw error;
|
|
2714
2984
|
} else {
|
|
2715
2985
|
if (isLogUpload) {
|
|
2716
|
-
|
|
2986
|
+
BStackLogger.debug(`Failed to fire api request due to ${error} - ${error.stack}`);
|
|
2717
2987
|
return;
|
|
2718
2988
|
}
|
|
2719
|
-
|
|
2989
|
+
BStackLogger.debug(`Failed to fire api request due to ${error} - ${error.stack}`);
|
|
2720
2990
|
throw error;
|
|
2721
2991
|
}
|
|
2722
2992
|
}
|
|
@@ -3136,9 +3406,9 @@ async function batchAndPostEvents(eventUrl, kind, data) {
|
|
|
3136
3406
|
},
|
|
3137
3407
|
body: JSON.stringify(data)
|
|
3138
3408
|
});
|
|
3139
|
-
|
|
3409
|
+
BStackLogger.debug(`[${kind}] Success response: ${JSON.stringify(await response.json())}`);
|
|
3140
3410
|
} catch (error) {
|
|
3141
|
-
|
|
3411
|
+
BStackLogger.debug(`[${kind}] EXCEPTION IN ${kind} REQUEST TO TEST REPORTING AND ANALYTICS : ${error}`);
|
|
3142
3412
|
throw new Error("Exception in request " + error);
|
|
3143
3413
|
}
|
|
3144
3414
|
}
|
|
@@ -3176,7 +3446,7 @@ function getObservabilityBuild(options, bstackBuildName) {
|
|
|
3176
3446
|
if (options.testObservabilityOptions && options.testObservabilityOptions.buildName) {
|
|
3177
3447
|
return options.testObservabilityOptions.buildName;
|
|
3178
3448
|
}
|
|
3179
|
-
return bstackBuildName ||
|
|
3449
|
+
return bstackBuildName || path6.basename(path6.resolve(process.cwd()));
|
|
3180
3450
|
}
|
|
3181
3451
|
function getObservabilityBuildTags(options, bstackBuildTag) {
|
|
3182
3452
|
if (process.env.TEST_OBSERVABILITY_BUILD_TAG) {
|
|
@@ -3190,6 +3460,25 @@ function getObservabilityBuildTags(options, bstackBuildTag) {
|
|
|
3190
3460
|
}
|
|
3191
3461
|
return [];
|
|
3192
3462
|
}
|
|
3463
|
+
function getTestPlanId(options) {
|
|
3464
|
+
if (process.env[BROWSERSTACK_TEST_PLAN_ID]) {
|
|
3465
|
+
return process.env[BROWSERSTACK_TEST_PLAN_ID];
|
|
3466
|
+
}
|
|
3467
|
+
const CLI_ARG = "--browserstack.testManagementOptions.testPlanId";
|
|
3468
|
+
const argIndex = process.argv.indexOf(CLI_ARG);
|
|
3469
|
+
if (argIndex !== -1 && process.argv[argIndex + 1]) {
|
|
3470
|
+
return process.argv[argIndex + 1];
|
|
3471
|
+
}
|
|
3472
|
+
const argWithEquals = process.argv.find((arg) => arg.startsWith(`${CLI_ARG}=`));
|
|
3473
|
+
if (argWithEquals) {
|
|
3474
|
+
return argWithEquals.split("=")[1];
|
|
3475
|
+
}
|
|
3476
|
+
const testPlanId = options.testManagementOptions?.testPlanId;
|
|
3477
|
+
if (typeof testPlanId === "string" && testPlanId.trim().length > 0) {
|
|
3478
|
+
return testPlanId.trim();
|
|
3479
|
+
}
|
|
3480
|
+
return void 0;
|
|
3481
|
+
}
|
|
3193
3482
|
function getBrowserStackUser(config) {
|
|
3194
3483
|
if (process.env.BROWSERSTACK_USERNAME) {
|
|
3195
3484
|
return process.env.BROWSERSTACK_USERNAME;
|
|
@@ -3235,23 +3524,39 @@ function getFailureObject(error) {
|
|
|
3235
3524
|
};
|
|
3236
3525
|
}
|
|
3237
3526
|
async function uploadLogs(user, key, clientBuildUuid) {
|
|
3527
|
+
const eventName = EVENTS.SDK_UPLOAD_LOGS;
|
|
3528
|
+
let success = true;
|
|
3529
|
+
let failure;
|
|
3530
|
+
PerformanceTester.start(eventName);
|
|
3238
3531
|
try {
|
|
3239
3532
|
if (!user || !key) {
|
|
3240
|
-
|
|
3533
|
+
success = false;
|
|
3534
|
+
failure = "skipped: missing_credentials";
|
|
3535
|
+
BStackLogger.debug("Uploading logs failed due to no credentials");
|
|
3241
3536
|
return;
|
|
3242
3537
|
}
|
|
3243
3538
|
const tmpDir = tmpdir();
|
|
3244
|
-
const tarPath =
|
|
3245
|
-
const tarGzPath =
|
|
3539
|
+
const tarPath = path6.join(tmpDir, "logs.tar");
|
|
3540
|
+
const tarGzPath = path6.join(tmpDir, "logs.tar.gz");
|
|
3246
3541
|
const filesToArchive = [
|
|
3247
|
-
|
|
3542
|
+
BStackLogger.logFilePath,
|
|
3248
3543
|
CLI_DEBUG_LOGS_FILE
|
|
3249
|
-
].filter((f) =>
|
|
3544
|
+
].filter((f) => fs7.existsSync(f));
|
|
3250
3545
|
const copiedFileNames = [];
|
|
3546
|
+
const archiveAddFailures = [];
|
|
3251
3547
|
for (const f of filesToArchive) {
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3548
|
+
try {
|
|
3549
|
+
const dest = path6.join(tmpDir, path6.basename(f));
|
|
3550
|
+
fs7.copyFileSync(f, dest);
|
|
3551
|
+
copiedFileNames.push(path6.basename(f));
|
|
3552
|
+
} catch (copyErr) {
|
|
3553
|
+
const msg = copyErr?.message || String(copyErr);
|
|
3554
|
+
archiveAddFailures.push(`${path6.basename(f)}: ${msg}`);
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
if (archiveAddFailures.length > 0 && failure === void 0) {
|
|
3558
|
+
success = false;
|
|
3559
|
+
failure = `archive_add_failed [${archiveAddFailures.length}]: ${archiveAddFailures.join("; ")}`.substring(0, 300);
|
|
3255
3560
|
}
|
|
3256
3561
|
await create(
|
|
3257
3562
|
{
|
|
@@ -3263,15 +3568,15 @@ async function uploadLogs(user, key, clientBuildUuid) {
|
|
|
3263
3568
|
copiedFileNames
|
|
3264
3569
|
);
|
|
3265
3570
|
await new Promise((resolve, reject) => {
|
|
3266
|
-
const source =
|
|
3267
|
-
const dest =
|
|
3571
|
+
const source = fs7.createReadStream(tarPath);
|
|
3572
|
+
const dest = fs7.createWriteStream(tarGzPath);
|
|
3268
3573
|
const gzip = zlib.createGzip({ level: 1 });
|
|
3269
3574
|
source.pipe(gzip).pipe(dest);
|
|
3270
3575
|
dest.on("finish", resolve);
|
|
3271
3576
|
dest.on("error", reject);
|
|
3272
3577
|
});
|
|
3273
|
-
const formData = new
|
|
3274
|
-
const file = await
|
|
3578
|
+
const formData = new FormData();
|
|
3579
|
+
const file = await fs7.openAsBlob(tarGzPath, { type: "application/x-gzip" });
|
|
3275
3580
|
formData.append("data", file, "logs.tar.gz");
|
|
3276
3581
|
formData.append("clientBuildUuid", clientBuildUuid);
|
|
3277
3582
|
const auth = Buffer.from(`${user}:${key}`).toString("base64");
|
|
@@ -3287,21 +3592,32 @@ async function uploadLogs(user, key, clientBuildUuid) {
|
|
|
3287
3592
|
requestOptions,
|
|
3288
3593
|
APIUtils.UPLOAD_LOGS_ADDRESS
|
|
3289
3594
|
);
|
|
3290
|
-
|
|
3291
|
-
|
|
3595
|
+
fs7.unlinkSync(tarPath);
|
|
3596
|
+
fs7.unlinkSync(tarGzPath);
|
|
3292
3597
|
for (const f of copiedFileNames) {
|
|
3293
|
-
const filePath =
|
|
3294
|
-
if (
|
|
3295
|
-
|
|
3598
|
+
const filePath = path6.join(tmpDir, f);
|
|
3599
|
+
if (fs7.existsSync(filePath)) {
|
|
3600
|
+
fs7.unlinkSync(filePath);
|
|
3296
3601
|
}
|
|
3297
3602
|
}
|
|
3298
|
-
if (
|
|
3299
|
-
|
|
3603
|
+
if (fs7.existsSync(CLI_DEBUG_LOGS_FILE)) {
|
|
3604
|
+
fs7.unlinkSync(CLI_DEBUG_LOGS_FILE);
|
|
3605
|
+
}
|
|
3606
|
+
if (!response) {
|
|
3607
|
+
success = false;
|
|
3608
|
+
failure = "upload_no_response";
|
|
3609
|
+
} else if (response.status && response.status !== "success") {
|
|
3610
|
+
success = false;
|
|
3611
|
+
failure = `upload_status: ${response.status}${response.message ? " - " + String(response.message).slice(0, 200) : ""}`;
|
|
3300
3612
|
}
|
|
3301
3613
|
return response;
|
|
3302
3614
|
} catch (error) {
|
|
3303
|
-
|
|
3615
|
+
success = false;
|
|
3616
|
+
failure = `uploadLogs exception: ${getErrorString(error)}`;
|
|
3617
|
+
BStackLogger.error(`Error while uploading logs: ${getErrorString(error)}`);
|
|
3304
3618
|
return null;
|
|
3619
|
+
} finally {
|
|
3620
|
+
PerformanceTester.end(eventName, success, failure);
|
|
3305
3621
|
}
|
|
3306
3622
|
}
|
|
3307
3623
|
function truncateString(field, truncateSizeInBytes) {
|
|
@@ -3315,7 +3631,7 @@ function truncateString(field, truncateSizeInBytes) {
|
|
|
3315
3631
|
return truncatedString;
|
|
3316
3632
|
}
|
|
3317
3633
|
} catch (error) {
|
|
3318
|
-
|
|
3634
|
+
BStackLogger.debug(`Error while truncating field, nothing was truncated here: ${error}`);
|
|
3319
3635
|
}
|
|
3320
3636
|
return field;
|
|
3321
3637
|
}
|
|
@@ -3324,7 +3640,7 @@ function getSizeOfJsonObjectInBytes(jsonData) {
|
|
|
3324
3640
|
const buffer = Buffer.from(JSON.stringify(jsonData));
|
|
3325
3641
|
return buffer.length;
|
|
3326
3642
|
} catch (error) {
|
|
3327
|
-
|
|
3643
|
+
BStackLogger.debug(`Something went wrong while calculating size of JSON object: ${error}`);
|
|
3328
3644
|
}
|
|
3329
3645
|
return -1;
|
|
3330
3646
|
}
|
|
@@ -3334,7 +3650,7 @@ function checkAndTruncateVCSInfo(gitMetaData) {
|
|
|
3334
3650
|
const truncateSize = gitMetaDataSizeInBytes - MAX_GIT_META_DATA_SIZE_IN_BYTES;
|
|
3335
3651
|
const truncatedCommitMessage = truncateString(gitMetaData.commit_message, truncateSize);
|
|
3336
3652
|
gitMetaData.commit_message = truncatedCommitMessage;
|
|
3337
|
-
|
|
3653
|
+
BStackLogger.info(`The commit has been truncated. Size of commit after truncation is ${getSizeOfJsonObjectInBytes(gitMetaData) / 1024} KB`);
|
|
3338
3654
|
}
|
|
3339
3655
|
return gitMetaData;
|
|
3340
3656
|
}
|
|
@@ -3346,7 +3662,7 @@ function getObservabilityProduct(options, isAppAutomate) {
|
|
|
3346
3662
|
}
|
|
3347
3663
|
async function pollApi(url3, params, headers, upperLimit, startTime = Date.now()) {
|
|
3348
3664
|
params.timestamp = Math.round(Date.now() / 1e3);
|
|
3349
|
-
|
|
3665
|
+
BStackLogger.debug(`current timestamp ${params.timestamp}`);
|
|
3350
3666
|
try {
|
|
3351
3667
|
const response = await makeGetRequest(url3, params, headers);
|
|
3352
3668
|
const responseData = await response.json();
|
|
@@ -3358,9 +3674,9 @@ async function pollApi(url3, params, headers, upperLimit, startTime = Date.now()
|
|
|
3358
3674
|
} catch (error) {
|
|
3359
3675
|
if (error.response && error.response.status === 404) {
|
|
3360
3676
|
const nextPollTime = parseInt(error.response.headers.get("next_poll_time"), 10) * 1e3;
|
|
3361
|
-
|
|
3677
|
+
BStackLogger.debug(`timeInMillis ${nextPollTime}`);
|
|
3362
3678
|
if (isNaN(nextPollTime)) {
|
|
3363
|
-
|
|
3679
|
+
BStackLogger.warn("Invalid or missing `nextPollTime` header. Stopping polling.");
|
|
3364
3680
|
return {
|
|
3365
3681
|
data: {},
|
|
3366
3682
|
headers: error.response.headers,
|
|
@@ -3368,18 +3684,18 @@ async function pollApi(url3, params, headers, upperLimit, startTime = Date.now()
|
|
|
3368
3684
|
};
|
|
3369
3685
|
}
|
|
3370
3686
|
const elapsedTime = nextPollTime - Date.now();
|
|
3371
|
-
|
|
3687
|
+
BStackLogger.debug(
|
|
3372
3688
|
`elapsedTime ${elapsedTime} timeInMillis ${nextPollTime} upperLimit ${upperLimit}`
|
|
3373
3689
|
);
|
|
3374
3690
|
if (nextPollTime > upperLimit) {
|
|
3375
|
-
|
|
3691
|
+
BStackLogger.warn("Polling stopped due to upper time limit.");
|
|
3376
3692
|
return {
|
|
3377
3693
|
data: {},
|
|
3378
3694
|
headers: error.response.headers,
|
|
3379
3695
|
message: "Polling stopped due to upper time limit."
|
|
3380
3696
|
};
|
|
3381
3697
|
}
|
|
3382
|
-
|
|
3698
|
+
BStackLogger.debug(`Polling again in ${elapsedTime}ms with params:`, params);
|
|
3383
3699
|
await new Promise((resolve) => setTimeout(resolve, elapsedTime));
|
|
3384
3700
|
return pollApi(url3, params, headers, upperLimit, startTime);
|
|
3385
3701
|
} else if (error.response) {
|
|
@@ -3388,7 +3704,7 @@ async function pollApi(url3, params, headers, upperLimit, startTime = Date.now()
|
|
|
3388
3704
|
const parsedError = JSON.parse(error.response.json());
|
|
3389
3705
|
errorMessage = parsedError.message;
|
|
3390
3706
|
} catch {
|
|
3391
|
-
|
|
3707
|
+
BStackLogger.debug(`Error parsing pollApi request body ${error.response.body}`);
|
|
3392
3708
|
errorMessage = "Unknown error";
|
|
3393
3709
|
}
|
|
3394
3710
|
throw {
|
|
@@ -3397,7 +3713,7 @@ async function pollApi(url3, params, headers, upperLimit, startTime = Date.now()
|
|
|
3397
3713
|
message: errorMessage
|
|
3398
3714
|
};
|
|
3399
3715
|
} else {
|
|
3400
|
-
|
|
3716
|
+
BStackLogger.error(`Unexpected error occurred: ${error}`);
|
|
3401
3717
|
return { data: {}, headers: {}, message: "Unexpected error occurred." };
|
|
3402
3718
|
}
|
|
3403
3719
|
}
|
|
@@ -3506,28 +3822,28 @@ function nestedKeyValue(hash, keys) {
|
|
|
3506
3822
|
return keys.reduce((hash2, key) => isHash(hash2) ? hash2[key] : void 0, hash);
|
|
3507
3823
|
}
|
|
3508
3824
|
function removeDir(dir) {
|
|
3509
|
-
const list =
|
|
3825
|
+
const list = fs7.readdirSync(dir);
|
|
3510
3826
|
for (let i = 0; i < list.length; i++) {
|
|
3511
|
-
const filename =
|
|
3512
|
-
const stat =
|
|
3827
|
+
const filename = path6.join(dir, list[i]);
|
|
3828
|
+
const stat = fs7.statSync(filename);
|
|
3513
3829
|
if (filename === "." || filename === "..") {
|
|
3514
3830
|
} else if (stat.isDirectory()) {
|
|
3515
3831
|
removeDir(filename);
|
|
3516
3832
|
} else {
|
|
3517
|
-
|
|
3833
|
+
fs7.unlinkSync(filename);
|
|
3518
3834
|
}
|
|
3519
3835
|
}
|
|
3520
|
-
|
|
3836
|
+
fs7.rmdirSync(dir);
|
|
3521
3837
|
}
|
|
3522
3838
|
function createDir(dir) {
|
|
3523
|
-
if (
|
|
3839
|
+
if (fs7.existsSync(dir)) {
|
|
3524
3840
|
removeDir(dir);
|
|
3525
3841
|
}
|
|
3526
|
-
|
|
3842
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
3527
3843
|
}
|
|
3528
3844
|
function isWritable(dirPath) {
|
|
3529
3845
|
try {
|
|
3530
|
-
|
|
3846
|
+
fs7.accessSync(dirPath, fs7.constants.W_OK);
|
|
3531
3847
|
return true;
|
|
3532
3848
|
} catch {
|
|
3533
3849
|
return false;
|
|
@@ -3535,10 +3851,10 @@ function isWritable(dirPath) {
|
|
|
3535
3851
|
}
|
|
3536
3852
|
function setReadWriteAccess(dirPath) {
|
|
3537
3853
|
try {
|
|
3538
|
-
|
|
3539
|
-
|
|
3854
|
+
fs7.chmodSync(dirPath, 438);
|
|
3855
|
+
BStackLogger.debug(`Directory ${dirPath} is now read/write accessible.`);
|
|
3540
3856
|
} catch (err) {
|
|
3541
|
-
|
|
3857
|
+
BStackLogger.error(`Failed to set directory access: ${err.stack}`);
|
|
3542
3858
|
}
|
|
3543
3859
|
}
|
|
3544
3860
|
function getMochaTestHierarchy(test) {
|
|
@@ -3566,7 +3882,7 @@ function isMultiRemoteCaps(capabilities) {
|
|
|
3566
3882
|
(cap) => Object.values(cap).length > 0 && Object.values(cap).every((c) => c !== null && typeof c === "object" && c.capabilities)
|
|
3567
3883
|
);
|
|
3568
3884
|
}
|
|
3569
|
-
var pGitconfig, DEFAULT_REQUEST_CONFIG, COLORS, processTestObservabilityResponse, jsonifyAccessibilityArray, processAccessibilityResponse, processLaunchBuildResponse, launchTestSession, validateCapsWithAppA11y, validateCapsWithA11y, validateCapsWithNonBstackA11y, shouldScanTestForAccessibility, isAccessibilityAutomationSession, isAppAccessibilityAutomationSession, formatString, _getParamsForAppAccessibility, performA11yScan, getA11yResults, getAppA11yResults, getAppA11yResultsSummary, getAppA11yResultResponse, getA11yResultsSummary, stopBuildUpstream, patchConsoleLogs, sleep, isObject, ObjectsAreEqual, getPlatformVersion, getBasicAuthHeader, isObjectEmpty, getErrorString, hasBrowserName, isValidCapsForHealing, performO11ySync;
|
|
3885
|
+
var pGitconfig, DEFAULT_REQUEST_CONFIG, COLORS, processTestObservabilityResponse, jsonifyAccessibilityArray, processAccessibilityResponse, processLaunchBuildResponse, launchTestSession, validateCapsWithAppA11y, validateCapsWithA11y, validateCapsWithNonBstackA11y, shouldScanTestForAccessibility, isAccessibilityAutomationSession, isAppAccessibilityAutomationSession, formatString, _getParamsForAppAccessibility, performA11yScan, getA11yResults, getAppA11yResults, getAppA11yResultsSummary, getAppA11yResultResponse, getA11yResultsSummary, stopBuildUpstream, patchConsoleLogs, sleep, isObject, ObjectsAreEqual, getPlatformVersion, getResolvedDeviceName, getBasicAuthHeader, isObjectEmpty, getErrorString, hasBrowserName, isValidCapsForHealing, performO11ySync;
|
|
3570
3886
|
var init_util = __esm({
|
|
3571
3887
|
"src/util.ts"() {
|
|
3572
3888
|
"use strict";
|
|
@@ -3640,7 +3956,7 @@ var init_util = __esm({
|
|
|
3640
3956
|
};
|
|
3641
3957
|
if (scannerVersion) {
|
|
3642
3958
|
process.env.BSTACK_A11Y_SCANNER_VERSION = scannerVersion;
|
|
3643
|
-
|
|
3959
|
+
BStackLogger.debug(`Accessibility scannerVersion ${scannerVersion}`);
|
|
3644
3960
|
}
|
|
3645
3961
|
if (accessibilityToken) {
|
|
3646
3962
|
process.env.BSTACK_A11Y_JWT = accessibilityToken;
|
|
@@ -3698,7 +4014,10 @@ var init_util = __esm({
|
|
|
3698
4014
|
},
|
|
3699
4015
|
product_map: getProductMapForBuildStartCall(bStackConfig, accessibilityAutomation),
|
|
3700
4016
|
config: {},
|
|
3701
|
-
test_orchestration: OrchestrationUtils.getInstance(config)?.getBuildStartData() || {}
|
|
4017
|
+
test_orchestration: OrchestrationUtils.getInstance(config)?.getBuildStartData() || {},
|
|
4018
|
+
test_management: {
|
|
4019
|
+
test_plan_id: getTestPlanId(options)
|
|
4020
|
+
}
|
|
3702
4021
|
};
|
|
3703
4022
|
if (accessibilityAutomation && (isTurboScale(options) || data.browserstackAutomation === false)) {
|
|
3704
4023
|
data.accessibility.settings ??= {};
|
|
@@ -3709,7 +4028,7 @@ var init_util = __esm({
|
|
|
3709
4028
|
CrashReporter.userConfigForReporting = process.env.USER_CONFIG_FOR_REPORTING !== void 0 ? JSON.parse(process.env.USER_CONFIG_FOR_REPORTING) : {};
|
|
3710
4029
|
}
|
|
3711
4030
|
} catch (error) {
|
|
3712
|
-
return
|
|
4031
|
+
return BStackLogger.error(`[Crash_Report_Upload] Failed to parse user config while sending build start event due to ${error}`);
|
|
3713
4032
|
}
|
|
3714
4033
|
data.config = CrashReporter.userConfigForReporting;
|
|
3715
4034
|
try {
|
|
@@ -3726,7 +4045,8 @@ var init_util = __esm({
|
|
|
3726
4045
|
});
|
|
3727
4046
|
const jsonResponse = await response.json();
|
|
3728
4047
|
delete data?.accessibility?.settings?.includeEncodedExtension;
|
|
3729
|
-
|
|
4048
|
+
BStackLogger.debug(`[Start_Build] Success response: ${JSON.stringify(jsonResponse)}`);
|
|
4049
|
+
BStackLogger.debug(`Test Plan Id sent in request: ${getTestPlanId(options)}`);
|
|
3730
4050
|
process.env[TESTOPS_BUILD_COMPLETED_ENV] = "true";
|
|
3731
4051
|
if (jsonResponse.jwt) {
|
|
3732
4052
|
process.env[BROWSERSTACK_TESTHUB_JWT] = jsonResponse.jwt;
|
|
@@ -3734,13 +4054,13 @@ var init_util = __esm({
|
|
|
3734
4054
|
if (jsonResponse.build_hashed_id) {
|
|
3735
4055
|
process.env[BROWSERSTACK_TESTHUB_UUID] = jsonResponse.build_hashed_id;
|
|
3736
4056
|
testOpsConfig_default.getInstance().buildHashedId = jsonResponse.build_hashed_id;
|
|
3737
|
-
|
|
4057
|
+
BStackLogger.info(`Testhub started with id: ${testOpsConfig_default.getInstance()?.buildHashedId}`);
|
|
3738
4058
|
}
|
|
3739
4059
|
processLaunchBuildResponse(jsonResponse, options);
|
|
3740
4060
|
launchBuildUsage.success();
|
|
3741
4061
|
return jsonResponse;
|
|
3742
4062
|
} catch (error) {
|
|
3743
|
-
|
|
4063
|
+
BStackLogger.debug(`TestHub build start failed: ${format(error)}`);
|
|
3744
4064
|
if (!error.success) {
|
|
3745
4065
|
launchBuildUsage.failed(error);
|
|
3746
4066
|
logBuildError(error);
|
|
@@ -3750,7 +4070,7 @@ var init_util = __esm({
|
|
|
3750
4070
|
}));
|
|
3751
4071
|
validateCapsWithAppA11y = (platformMeta) => {
|
|
3752
4072
|
if (platformMeta?.platform_name && String(platformMeta?.platform_name).toLowerCase() === "android" && (platformMeta?.platform_version && parseInt(platformMeta?.platform_version?.toString()) < 11)) {
|
|
3753
|
-
|
|
4073
|
+
BStackLogger.warn("App Accessibility Automation tests are supported on OS version 11 and above for Android devices.");
|
|
3754
4074
|
return false;
|
|
3755
4075
|
}
|
|
3756
4076
|
return true;
|
|
@@ -3758,38 +4078,79 @@ var init_util = __esm({
|
|
|
3758
4078
|
validateCapsWithA11y = (deviceName, platformMeta, chromeOptions) => {
|
|
3759
4079
|
try {
|
|
3760
4080
|
if (deviceName) {
|
|
3761
|
-
|
|
3762
|
-
return false;
|
|
3763
|
-
}
|
|
3764
|
-
if (platformMeta?.browser_name?.toLowerCase() !== "chrome") {
|
|
3765
|
-
BStackLogger2.warn("Accessibility Automation will run only on Chrome browsers.");
|
|
4081
|
+
BStackLogger.warn("Accessibility Automation will run only on Desktop browsers.");
|
|
3766
4082
|
return false;
|
|
3767
4083
|
}
|
|
4084
|
+
const browserName = platformMeta?.browser_name?.toLowerCase();
|
|
3768
4085
|
const browserVersion = platformMeta?.browser_version;
|
|
3769
|
-
|
|
3770
|
-
|
|
4086
|
+
const validBrowsers = SUPPORTED_BROWSERS_FOR_ACCESSIBILITY;
|
|
4087
|
+
if (!browserName || !validBrowsers.includes(browserName)) {
|
|
4088
|
+
BStackLogger.warn(`Accessibility Automation supports Chrome 95+, Chrome for Testing 141+, and Safari 18.4+. Current browser: ${browserName}`);
|
|
3771
4089
|
return false;
|
|
3772
4090
|
}
|
|
3773
|
-
if (
|
|
3774
|
-
|
|
3775
|
-
|
|
4091
|
+
if (browserName === "chrome" || browserName === "chromefortesting") {
|
|
4092
|
+
const minVersion = MIN_BROWSER_VERSIONS_A11Y[browserName];
|
|
4093
|
+
if (browserVersion && browserVersion !== "latest") {
|
|
4094
|
+
const version3 = parseInt(browserVersion.toString().split(".")[0] || "0", 10);
|
|
4095
|
+
if (version3 < minVersion) {
|
|
4096
|
+
BStackLogger.warn(`Accessibility Automation requires ${browserName === "chrome" ? "Chrome" : "Chrome for Testing"} version ${minVersion} or higher.`);
|
|
4097
|
+
return false;
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
if (chromeOptions?.args?.includes("--headless")) {
|
|
4101
|
+
BStackLogger.warn("Accessibility Automation will not run on legacy headless mode. Switch to new headless mode or avoid using headless mode.");
|
|
4102
|
+
return false;
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
if (browserName === "safari") {
|
|
4106
|
+
if (browserVersion && browserVersion !== "latest") {
|
|
4107
|
+
const [currentMajor = 0, currentMinor = 0] = browserVersion.toString().split(".").map(Number);
|
|
4108
|
+
const [requiredMajor = 0, requiredMinor = 0] = MIN_BROWSER_VERSIONS_A11Y.safari.toString().split(".").map(Number);
|
|
4109
|
+
if (currentMajor < requiredMajor || currentMajor === requiredMajor && currentMinor < requiredMinor) {
|
|
4110
|
+
BStackLogger.warn(`Accessibility Automation requires Safari version ${MIN_BROWSER_VERSIONS_A11Y.safari} or higher.`);
|
|
4111
|
+
return false;
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
3776
4114
|
}
|
|
3777
4115
|
return true;
|
|
3778
4116
|
} catch (error) {
|
|
3779
|
-
|
|
4117
|
+
BStackLogger.debug(`Exception in checking capabilities compatibility with Accessibility. Error: ${error}`);
|
|
3780
4118
|
}
|
|
3781
4119
|
return false;
|
|
3782
4120
|
};
|
|
3783
4121
|
validateCapsWithNonBstackA11y = (browserName, browserVersion) => {
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
4122
|
+
try {
|
|
4123
|
+
const browser = browserName?.toLowerCase();
|
|
4124
|
+
const validBrowsers = ["chrome", "chromefortesting", "safari"];
|
|
4125
|
+
if (!browser || !validBrowsers.includes(browser)) {
|
|
4126
|
+
BStackLogger.warn("Accessibility Automation on non-BrowserStack infrastructure supports Chrome 100+, Chrome for Testing 141+, and Safari 18.4+.");
|
|
4127
|
+
return false;
|
|
4128
|
+
}
|
|
4129
|
+
if (browser === "chrome" || browser === "chromefortesting") {
|
|
4130
|
+
const minVersion = MIN_BROWSER_VERSIONS_A11Y_NON_BSTACK[browser];
|
|
4131
|
+
if (browserVersion && browserVersion !== "latest") {
|
|
4132
|
+
const version3 = parseInt(browserVersion.toString().split(".")[0] || "0", 10);
|
|
4133
|
+
if (version3 < minVersion) {
|
|
4134
|
+
BStackLogger.warn(`Accessibility Automation requires ${browser === "chrome" ? "Chrome" : "Chrome for Testing"} version ${minVersion}+ on non-BrowserStack infrastructure.`);
|
|
4135
|
+
return false;
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4139
|
+
if (browser === "safari") {
|
|
4140
|
+
if (browserVersion && browserVersion !== "latest") {
|
|
4141
|
+
const [currentMajor = 0, currentMinor = 0] = browserVersion.toString().split(".").map(Number);
|
|
4142
|
+
const [requiredMajor = 0, requiredMinor = 0] = MIN_BROWSER_VERSIONS_A11Y_NON_BSTACK.safari.toString().split(".").map(Number);
|
|
4143
|
+
if (currentMajor < requiredMajor || currentMajor === requiredMajor && currentMinor < requiredMinor) {
|
|
4144
|
+
BStackLogger.warn(`Accessibility Automation requires Safari version ${MIN_BROWSER_VERSIONS_A11Y_NON_BSTACK.safari}+ on non-BrowserStack infrastructure.`);
|
|
4145
|
+
return false;
|
|
4146
|
+
}
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
return true;
|
|
4150
|
+
} catch (error) {
|
|
4151
|
+
BStackLogger.debug(`Exception in checking capabilities compatibility with Accessibility. Error: ${error}`);
|
|
3791
4152
|
}
|
|
3792
|
-
return
|
|
4153
|
+
return false;
|
|
3793
4154
|
};
|
|
3794
4155
|
shouldScanTestForAccessibility = (suiteTitle, testTitle, accessibilityOptions, world, isCucumber) => {
|
|
3795
4156
|
try {
|
|
@@ -3807,7 +4168,7 @@ var init_util = __esm({
|
|
|
3807
4168
|
const included = includeTags?.length === 0 || includeTags?.some((include) => fullTestName.includes(include));
|
|
3808
4169
|
return !excluded && included;
|
|
3809
4170
|
} catch (error) {
|
|
3810
|
-
|
|
4171
|
+
BStackLogger.debug(`Error while validating test case for accessibility before scanning. Error : ${error}`);
|
|
3811
4172
|
}
|
|
3812
4173
|
return false;
|
|
3813
4174
|
};
|
|
@@ -3816,7 +4177,7 @@ var init_util = __esm({
|
|
|
3816
4177
|
const hasA11yJwtToken = typeof process.env.BSTACK_A11Y_JWT === "string" && process.env.BSTACK_A11Y_JWT.length > 0 && process.env.BSTACK_A11Y_JWT !== "null" && process.env.BSTACK_A11Y_JWT !== "undefined";
|
|
3817
4178
|
return accessibilityFlag && hasA11yJwtToken;
|
|
3818
4179
|
} catch (error) {
|
|
3819
|
-
|
|
4180
|
+
BStackLogger.debug(`Exception in verifying the Accessibility session with error : ${error}`);
|
|
3820
4181
|
}
|
|
3821
4182
|
return false;
|
|
3822
4183
|
};
|
|
@@ -3847,43 +4208,43 @@ var init_util = __esm({
|
|
|
3847
4208
|
};
|
|
3848
4209
|
performA11yScan = async (isAppAutomate, browser, isBrowserStackSession, isAccessibility, commandName, testName) => {
|
|
3849
4210
|
if (!isAccessibilityAutomationSession(isAccessibility)) {
|
|
3850
|
-
|
|
4211
|
+
BStackLogger.warn("Not an Accessibility Automation session, cannot perform Accessibility scan.");
|
|
3851
4212
|
return;
|
|
3852
4213
|
}
|
|
3853
4214
|
try {
|
|
3854
4215
|
if (isAppAccessibilityAutomationSession(isAccessibility, isAppAutomate)) {
|
|
3855
4216
|
const results = await browser.execute(formatString(accessibility_scripts_default.performScan, JSON.stringify(_getParamsForAppAccessibility(commandName, testName))), {});
|
|
3856
|
-
|
|
4217
|
+
BStackLogger.debug(util3.format(results));
|
|
3857
4218
|
return results;
|
|
3858
4219
|
}
|
|
3859
4220
|
if (accessibility_scripts_default.performScan) {
|
|
3860
4221
|
const results = await executeAccessibilityScript(browser, accessibility_scripts_default.performScan, { method: commandName || "" });
|
|
3861
4222
|
return results;
|
|
3862
4223
|
}
|
|
3863
|
-
|
|
4224
|
+
BStackLogger.error("AccessibilityScripts.performScan is null");
|
|
3864
4225
|
return;
|
|
3865
4226
|
} catch (err) {
|
|
3866
|
-
|
|
4227
|
+
BStackLogger.error("Accessibility Scan could not be performed : " + err);
|
|
3867
4228
|
return;
|
|
3868
4229
|
}
|
|
3869
4230
|
};
|
|
3870
4231
|
getA11yResults = PerformanceTester.measureWrapper(A11Y_EVENTS.GET_RESULTS, async (isAppAutomate, browser, isBrowserStackSession, isAccessibility) => {
|
|
3871
4232
|
if (!isAccessibilityAutomationSession(isAccessibility)) {
|
|
3872
|
-
|
|
4233
|
+
BStackLogger.warn("Not an Accessibility Automation session, cannot retrieve Accessibility results.");
|
|
3873
4234
|
return [];
|
|
3874
4235
|
}
|
|
3875
4236
|
try {
|
|
3876
|
-
|
|
4237
|
+
BStackLogger.debug("Performing scan before getting results");
|
|
3877
4238
|
await performA11yScan(isAppAutomate, browser, isBrowserStackSession, isAccessibility);
|
|
3878
4239
|
if (accessibility_scripts_default.getResults) {
|
|
3879
4240
|
const results = await executeAccessibilityScript(browser, accessibility_scripts_default.getResults);
|
|
3880
4241
|
return results;
|
|
3881
4242
|
}
|
|
3882
|
-
|
|
4243
|
+
BStackLogger.error("AccessibilityScripts.getResults is null");
|
|
3883
4244
|
return [];
|
|
3884
4245
|
} catch (error) {
|
|
3885
|
-
|
|
3886
|
-
|
|
4246
|
+
BStackLogger.error("No accessibility results were found.");
|
|
4247
|
+
BStackLogger.debug(`getA11yResults Failed. Error: ${error}`);
|
|
3887
4248
|
return [];
|
|
3888
4249
|
}
|
|
3889
4250
|
});
|
|
@@ -3892,18 +4253,18 @@ var init_util = __esm({
|
|
|
3892
4253
|
return [];
|
|
3893
4254
|
}
|
|
3894
4255
|
if (!isAppAccessibilityAutomationSession(isAccessibility, isAppAutomate)) {
|
|
3895
|
-
|
|
4256
|
+
BStackLogger.warn("Not an Accessibility Automation session, cannot retrieve Accessibility results summary.");
|
|
3896
4257
|
return [];
|
|
3897
4258
|
}
|
|
3898
4259
|
try {
|
|
3899
4260
|
const apiUrl = `${APIUtils.APP_ALLY_ENDPOINT}/${APP_ALLY_ISSUES_ENDPOINT}`;
|
|
3900
4261
|
const apiRespone = await getAppA11yResultResponse(apiUrl, isAppAutomate, browser, testName, isBrowserStackSession, isAccessibility, sessionId);
|
|
3901
4262
|
const result = apiRespone?.data?.data?.issues;
|
|
3902
|
-
|
|
4263
|
+
BStackLogger.debug(`Polling Result: ${JSON.stringify(result)}`);
|
|
3903
4264
|
return result;
|
|
3904
4265
|
} catch (error) {
|
|
3905
|
-
|
|
3906
|
-
|
|
4266
|
+
BStackLogger.error("No accessibility summary was found.");
|
|
4267
|
+
BStackLogger.debug(`getAppA11yResults Failed. Error: ${error}`);
|
|
3907
4268
|
return [];
|
|
3908
4269
|
}
|
|
3909
4270
|
});
|
|
@@ -3912,46 +4273,46 @@ var init_util = __esm({
|
|
|
3912
4273
|
return {};
|
|
3913
4274
|
}
|
|
3914
4275
|
if (!isAppAccessibilityAutomationSession(isAccessibility, isAppAutomate)) {
|
|
3915
|
-
|
|
4276
|
+
BStackLogger.warn("Not an Accessibility Automation session, cannot retrieve Accessibility results summary.");
|
|
3916
4277
|
return {};
|
|
3917
4278
|
}
|
|
3918
4279
|
try {
|
|
3919
4280
|
const apiUrl = `${APIUtils.APP_ALLY_ENDPOINT}/${APP_ALLY_ISSUES_SUMMARY_ENDPOINT}`;
|
|
3920
4281
|
const apiRespone = await getAppA11yResultResponse(apiUrl, isAppAutomate, browser, testName, isBrowserStackSession, isAccessibility, sessionId);
|
|
3921
4282
|
const result = apiRespone?.data?.data?.summary;
|
|
3922
|
-
|
|
4283
|
+
BStackLogger.debug(`Polling Result: ${JSON.stringify(result)}`);
|
|
3923
4284
|
return result;
|
|
3924
4285
|
} catch {
|
|
3925
|
-
|
|
4286
|
+
BStackLogger.error("No accessibility summary was found.");
|
|
3926
4287
|
return {};
|
|
3927
4288
|
}
|
|
3928
4289
|
});
|
|
3929
4290
|
getAppA11yResultResponse = async (apiUrl, isAppAutomate, browser, testName, isBrowserStackSession, isAccessibility, sessionId) => {
|
|
3930
|
-
|
|
4291
|
+
BStackLogger.debug("Performing scan before getting results summary");
|
|
3931
4292
|
await performA11yScan(isAppAutomate, browser, isBrowserStackSession, isAccessibility, void 0, testName);
|
|
3932
4293
|
const upperTimeLimit = process.env.BSTACK_A11Y_POLLING_TIMEOUT ? Date.now() + parseInt(process.env.BSTACK_A11Y_POLLING_TIMEOUT) * 1e3 : Date.now() + 3e4;
|
|
3933
4294
|
const params = { test_run_uuid: process.env.TEST_ANALYTICS_ID, session_id: sessionId, timestamp: Date.now() };
|
|
3934
4295
|
const header = { Authorization: `Bearer ${process.env.BSTACK_A11Y_JWT}` };
|
|
3935
4296
|
const apiRespone = await pollApi(apiUrl, params, header, upperTimeLimit);
|
|
3936
|
-
|
|
4297
|
+
BStackLogger.debug(`Polling Result: ${JSON.stringify(apiRespone)}`);
|
|
3937
4298
|
return apiRespone;
|
|
3938
4299
|
};
|
|
3939
4300
|
getA11yResultsSummary = PerformanceTester.measureWrapper(A11Y_EVENTS.GET_RESULTS_SUMMARY, async (isAppAutomate, browser, isBrowserStackSession, isAccessibility) => {
|
|
3940
4301
|
if (!isAccessibilityAutomationSession(isAccessibility)) {
|
|
3941
|
-
|
|
4302
|
+
BStackLogger.warn("Not an Accessibility Automation session, cannot retrieve Accessibility results summary.");
|
|
3942
4303
|
return {};
|
|
3943
4304
|
}
|
|
3944
4305
|
try {
|
|
3945
|
-
|
|
4306
|
+
BStackLogger.debug("Performing scan before getting results summary");
|
|
3946
4307
|
await performA11yScan(isAppAutomate, browser, isBrowserStackSession, isAccessibility);
|
|
3947
4308
|
if (accessibility_scripts_default.getResultsSummary) {
|
|
3948
4309
|
const summaryResults = await executeAccessibilityScript(browser, accessibility_scripts_default.getResultsSummary);
|
|
3949
4310
|
return summaryResults;
|
|
3950
4311
|
}
|
|
3951
|
-
|
|
4312
|
+
BStackLogger.error("AccessibilityScripts.getResultsSummary is null");
|
|
3952
4313
|
return {};
|
|
3953
4314
|
} catch {
|
|
3954
|
-
|
|
4315
|
+
BStackLogger.error("No accessibility summary was found.");
|
|
3955
4316
|
return {};
|
|
3956
4317
|
}
|
|
3957
4318
|
});
|
|
@@ -3967,7 +4328,7 @@ var init_util = __esm({
|
|
|
3967
4328
|
}
|
|
3968
4329
|
if (!process.env[BROWSERSTACK_TESTHUB_JWT]) {
|
|
3969
4330
|
stopBuildUsage.failed("Token/buildID is undefined, build creation might have failed");
|
|
3970
|
-
|
|
4331
|
+
BStackLogger.debug("[STOP_BUILD] Missing Authentication Token/ Build ID");
|
|
3971
4332
|
return {
|
|
3972
4333
|
status: "error",
|
|
3973
4334
|
message: "Token/buildID is undefined, build creation might have failed"
|
|
@@ -3986,7 +4347,7 @@ var init_util = __esm({
|
|
|
3986
4347
|
},
|
|
3987
4348
|
body: JSON.stringify(data)
|
|
3988
4349
|
});
|
|
3989
|
-
|
|
4350
|
+
BStackLogger.debug(`[STOP_BUILD] Success response: ${await response.text()}`);
|
|
3990
4351
|
stopBuildUsage.success();
|
|
3991
4352
|
return {
|
|
3992
4353
|
status: "success",
|
|
@@ -3994,7 +4355,7 @@ var init_util = __esm({
|
|
|
3994
4355
|
};
|
|
3995
4356
|
} catch (error) {
|
|
3996
4357
|
stopBuildUsage.failed(error);
|
|
3997
|
-
|
|
4358
|
+
BStackLogger.debug(`[STOP_BUILD] Failed. Error: ${error}`);
|
|
3998
4359
|
return {
|
|
3999
4360
|
status: "error",
|
|
4000
4361
|
message: error.message
|
|
@@ -4005,7 +4366,7 @@ var init_util = __esm({
|
|
|
4005
4366
|
const BSTestOpsPatcher = new logPatcher_default({});
|
|
4006
4367
|
Object.keys(consoleHolder).forEach((method) => {
|
|
4007
4368
|
if (!(method in console) || method === "Console" || typeof console[method] !== "function") {
|
|
4008
|
-
|
|
4369
|
+
BStackLogger.debug(`Skipping method: ${method}, exists: ${method in console}, type: ${typeof console[method]}`);
|
|
4009
4370
|
return;
|
|
4010
4371
|
}
|
|
4011
4372
|
const origMethod = console[method].bind(console);
|
|
@@ -4018,7 +4379,7 @@ var init_util = __esm({
|
|
|
4018
4379
|
BSTestOpsPatcher[method](...args);
|
|
4019
4380
|
}
|
|
4020
4381
|
} catch (error) {
|
|
4021
|
-
|
|
4382
|
+
BStackLogger.debug(`Error while patching console logs : ${error}`);
|
|
4022
4383
|
origMethod(...args);
|
|
4023
4384
|
}
|
|
4024
4385
|
};
|
|
@@ -4052,18 +4413,57 @@ var init_util = __esm({
|
|
|
4052
4413
|
const keys = ["platformVersion", "platform_version", "osVersion", "os_version"];
|
|
4053
4414
|
for (const key of keys) {
|
|
4054
4415
|
if (caps?.[key]) {
|
|
4055
|
-
|
|
4416
|
+
BStackLogger.debug(`Got ${key} from driver caps`);
|
|
4056
4417
|
return String(caps?.[key]);
|
|
4057
4418
|
} else if (bstackOptions && bstackOptions?.[key]) {
|
|
4058
|
-
|
|
4419
|
+
BStackLogger.debug(`Got ${key} from user bstack options`);
|
|
4059
4420
|
return String(bstackOptions?.[key]);
|
|
4060
4421
|
} else if (userCaps[key]) {
|
|
4061
|
-
|
|
4422
|
+
BStackLogger.debug(`Got ${key} from user caps`);
|
|
4062
4423
|
return String(userCaps[key]);
|
|
4063
4424
|
}
|
|
4064
4425
|
}
|
|
4065
4426
|
return void 0;
|
|
4066
4427
|
});
|
|
4428
|
+
getResolvedDeviceName = o11yErrorHandler(function getResolvedDeviceName2(driverCaps, requestedCaps) {
|
|
4429
|
+
const flattenMultiremote = (caps) => {
|
|
4430
|
+
if (!caps) {
|
|
4431
|
+
return [];
|
|
4432
|
+
}
|
|
4433
|
+
const obj = caps;
|
|
4434
|
+
if (obj["deviceModel"] || obj["appium:deviceModel"] || obj["deviceName"] || obj["bstack:options"]) {
|
|
4435
|
+
return [caps];
|
|
4436
|
+
}
|
|
4437
|
+
return Object.values(obj).filter((v) => v !== null && typeof v === "object" && "capabilities" in v).map((v) => v.capabilities).filter(Boolean);
|
|
4438
|
+
};
|
|
4439
|
+
const sources = [
|
|
4440
|
+
...flattenMultiremote(driverCaps),
|
|
4441
|
+
...flattenMultiremote(requestedCaps)
|
|
4442
|
+
];
|
|
4443
|
+
if (!sources.length) {
|
|
4444
|
+
return void 0;
|
|
4445
|
+
}
|
|
4446
|
+
const pickString = (obj, key) => {
|
|
4447
|
+
const v = obj?.[key];
|
|
4448
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
4449
|
+
};
|
|
4450
|
+
const paths = [
|
|
4451
|
+
(c) => pickString(c, "deviceModel"),
|
|
4452
|
+
(c) => pickString(c, "appium:deviceModel"),
|
|
4453
|
+
(c) => pickString(c["bstack:options"], "deviceName"),
|
|
4454
|
+
(c) => pickString(c, "appium:deviceName"),
|
|
4455
|
+
(c) => pickString(c, "deviceName")
|
|
4456
|
+
];
|
|
4457
|
+
for (const path22 of paths) {
|
|
4458
|
+
for (const src of sources) {
|
|
4459
|
+
const v = path22(src);
|
|
4460
|
+
if (v) {
|
|
4461
|
+
return v;
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
return void 0;
|
|
4466
|
+
});
|
|
4067
4467
|
getBasicAuthHeader = (username, password) => {
|
|
4068
4468
|
const encodedAuth = Buffer.from(`${username}:${password}`, "utf8").toString("base64");
|
|
4069
4469
|
return `Basic ${encodedAuth}`;
|
|
@@ -4107,20 +4507,20 @@ var init_util = __esm({
|
|
|
4107
4507
|
});
|
|
4108
4508
|
|
|
4109
4509
|
// src/bstackLogger.ts
|
|
4110
|
-
import
|
|
4111
|
-
import
|
|
4510
|
+
import path7 from "node:path";
|
|
4511
|
+
import fs8 from "node:fs";
|
|
4112
4512
|
import chalk2 from "chalk";
|
|
4113
4513
|
import logger2 from "@wdio/logger";
|
|
4114
|
-
var log2,
|
|
4514
|
+
var log2, BStackLogger;
|
|
4115
4515
|
var init_bstackLogger = __esm({
|
|
4116
4516
|
"src/bstackLogger.ts"() {
|
|
4117
4517
|
"use strict";
|
|
4118
4518
|
init_constants();
|
|
4119
4519
|
init_util();
|
|
4120
4520
|
log2 = logger2("@wdio/browserstack-service");
|
|
4121
|
-
|
|
4122
|
-
static logFilePath =
|
|
4123
|
-
static logFolderPath =
|
|
4521
|
+
BStackLogger = class {
|
|
4522
|
+
static logFilePath = path7.join(process.cwd(), LOGS_FILE);
|
|
4523
|
+
static logFolderPath = path7.join(process.cwd(), "logs");
|
|
4124
4524
|
static logFileStream;
|
|
4125
4525
|
static redactCredentials(logMessage) {
|
|
4126
4526
|
return logMessage.replace(/(["']?(?:username|userName|accesskey|accessKey|user|key)["']?\s*[:=]\s*["']?)([^"'\s,}]+)/gi, "$1").replace(/([?&](?:username|userName|access_key|accesskey|accessKey|user|key)=)([^&#\s]+)/gi, "$1");
|
|
@@ -4130,7 +4530,7 @@ var init_bstackLogger = __esm({
|
|
|
4130
4530
|
const redactedMessage = this.redactCredentials(logMessage);
|
|
4131
4531
|
if (!this.logFileStream) {
|
|
4132
4532
|
this.ensureLogsFolder();
|
|
4133
|
-
this.logFileStream =
|
|
4533
|
+
this.logFileStream = fs8.createWriteStream(this.logFilePath, { flags: "a" });
|
|
4134
4534
|
}
|
|
4135
4535
|
if (this.logFileStream && this.logFileStream.writable) {
|
|
4136
4536
|
this.logFileStream.write(this.formatLog(redactedMessage, logLevel));
|
|
@@ -4179,13 +4579,13 @@ var init_bstackLogger = __esm({
|
|
|
4179
4579
|
this.logFileStream = null;
|
|
4180
4580
|
}
|
|
4181
4581
|
static clearLogFile() {
|
|
4182
|
-
if (
|
|
4183
|
-
|
|
4582
|
+
if (fs8.existsSync(this.logFilePath)) {
|
|
4583
|
+
fs8.truncateSync(this.logFilePath);
|
|
4184
4584
|
}
|
|
4185
4585
|
}
|
|
4186
4586
|
static ensureLogsFolder() {
|
|
4187
|
-
if (!
|
|
4188
|
-
|
|
4587
|
+
if (!fs8.existsSync(this.logFolderPath)) {
|
|
4588
|
+
fs8.mkdirSync(this.logFolderPath);
|
|
4189
4589
|
}
|
|
4190
4590
|
}
|
|
4191
4591
|
};
|
|
@@ -4201,13 +4601,14 @@ var init_utils = __esm({
|
|
|
4201
4601
|
init_bstackLogger();
|
|
4202
4602
|
init_util();
|
|
4203
4603
|
getProductMap = (config) => {
|
|
4204
|
-
|
|
4205
|
-
observability
|
|
4206
|
-
accessibility
|
|
4207
|
-
percy
|
|
4208
|
-
automate
|
|
4209
|
-
app_automate
|
|
4210
|
-
|
|
4604
|
+
const entries = [
|
|
4605
|
+
["observability", config.testObservability.enabled],
|
|
4606
|
+
["accessibility", !!config.accessibility],
|
|
4607
|
+
["percy", config.percy],
|
|
4608
|
+
["automate", config.automate],
|
|
4609
|
+
["app_automate", config.appAutomate]
|
|
4610
|
+
];
|
|
4611
|
+
return Object.fromEntries(entries.filter(([, v]) => v !== null));
|
|
4211
4612
|
};
|
|
4212
4613
|
shouldProcessEventForTesthub = (eventType) => {
|
|
4213
4614
|
if (isTrue(process.env[BROWSERSTACK_OBSERVABILITY])) {
|
|
@@ -4231,7 +4632,7 @@ var init_utils = __esm({
|
|
|
4231
4632
|
};
|
|
4232
4633
|
logBuildError = (error, product = "") => {
|
|
4233
4634
|
if (!error || !error.errors) {
|
|
4234
|
-
|
|
4635
|
+
BStackLogger.error(`${product.toUpperCase()} Build creation failed ${error}`);
|
|
4235
4636
|
return;
|
|
4236
4637
|
}
|
|
4237
4638
|
for (const errorJson of error.errors) {
|
|
@@ -4240,35 +4641,36 @@ var init_utils = __esm({
|
|
|
4240
4641
|
if (errorMessage) {
|
|
4241
4642
|
switch (errorType) {
|
|
4242
4643
|
case "ERROR_INVALID_CREDENTIALS":
|
|
4243
|
-
|
|
4644
|
+
BStackLogger.error(errorMessage);
|
|
4244
4645
|
break;
|
|
4245
4646
|
case "ERROR_ACCESS_DENIED":
|
|
4246
|
-
|
|
4647
|
+
BStackLogger.info(errorMessage);
|
|
4247
4648
|
break;
|
|
4248
4649
|
case "ERROR_SDK_DEPRECATED":
|
|
4249
|
-
|
|
4650
|
+
BStackLogger.error(errorMessage);
|
|
4250
4651
|
break;
|
|
4251
4652
|
default:
|
|
4252
|
-
|
|
4653
|
+
BStackLogger.error(errorMessage);
|
|
4253
4654
|
}
|
|
4254
4655
|
}
|
|
4255
4656
|
}
|
|
4256
4657
|
};
|
|
4257
4658
|
getProductMapForBuildStartCall = (config, accessibilityAutomation) => {
|
|
4258
|
-
|
|
4259
|
-
observability
|
|
4260
|
-
accessibility
|
|
4261
|
-
percy
|
|
4262
|
-
automate
|
|
4263
|
-
app_automate
|
|
4264
|
-
|
|
4659
|
+
const entries = [
|
|
4660
|
+
["observability", config.testObservability.enabled],
|
|
4661
|
+
["accessibility", accessibilityAutomation],
|
|
4662
|
+
["percy", config.percy],
|
|
4663
|
+
["automate", config.automate],
|
|
4664
|
+
["app_automate", config.appAutomate]
|
|
4665
|
+
];
|
|
4666
|
+
return Object.fromEntries(entries.filter(([, v]) => v !== null));
|
|
4265
4667
|
};
|
|
4266
4668
|
}
|
|
4267
4669
|
});
|
|
4268
4670
|
|
|
4269
4671
|
// src/Percy/PercyLogger.ts
|
|
4270
|
-
import
|
|
4271
|
-
import
|
|
4672
|
+
import path8 from "node:path";
|
|
4673
|
+
import fs9 from "node:fs";
|
|
4272
4674
|
import chalk3 from "chalk";
|
|
4273
4675
|
import logger3 from "@wdio/logger";
|
|
4274
4676
|
var log3, PercyLogger;
|
|
@@ -4279,16 +4681,16 @@ var init_PercyLogger = __esm({
|
|
|
4279
4681
|
init_util();
|
|
4280
4682
|
log3 = logger3("@wdio/browserstack-service");
|
|
4281
4683
|
PercyLogger = class {
|
|
4282
|
-
static logFilePath =
|
|
4283
|
-
static logFolderPath =
|
|
4684
|
+
static logFilePath = path8.join(process.cwd(), PERCY_LOGS_FILE);
|
|
4685
|
+
static logFolderPath = path8.join(process.cwd(), "logs");
|
|
4284
4686
|
static logFileStream;
|
|
4285
4687
|
static logToFile(logMessage, logLevel) {
|
|
4286
4688
|
try {
|
|
4287
4689
|
if (!this.logFileStream) {
|
|
4288
|
-
if (!
|
|
4289
|
-
|
|
4690
|
+
if (!fs9.existsSync(this.logFolderPath)) {
|
|
4691
|
+
fs9.mkdirSync(this.logFolderPath);
|
|
4290
4692
|
}
|
|
4291
|
-
this.logFileStream =
|
|
4693
|
+
this.logFileStream = fs9.createWriteStream(this.logFilePath, { flags: "a" });
|
|
4292
4694
|
}
|
|
4293
4695
|
if (this.logFileStream && this.logFileStream.writable) {
|
|
4294
4696
|
this.logFileStream.write(this.formatLog(logMessage, logLevel));
|
|
@@ -4332,8 +4734,8 @@ var init_PercyLogger = __esm({
|
|
|
4332
4734
|
this.logFileStream = null;
|
|
4333
4735
|
}
|
|
4334
4736
|
static clearLogFile() {
|
|
4335
|
-
if (
|
|
4336
|
-
|
|
4737
|
+
if (fs9.existsSync(this.logFilePath)) {
|
|
4738
|
+
fs9.truncateSync(this.logFilePath);
|
|
4337
4739
|
}
|
|
4338
4740
|
}
|
|
4339
4741
|
};
|
|
@@ -4341,7 +4743,7 @@ var init_PercyLogger = __esm({
|
|
|
4341
4743
|
});
|
|
4342
4744
|
|
|
4343
4745
|
// src/cli/grpcClient.ts
|
|
4344
|
-
import
|
|
4746
|
+
import path13 from "node:path";
|
|
4345
4747
|
import util5, { promisify as promisify3 } from "node:util";
|
|
4346
4748
|
import {
|
|
4347
4749
|
SDKClient,
|
|
@@ -4359,7 +4761,7 @@ import {
|
|
|
4359
4761
|
DriverInitRequestConstructor,
|
|
4360
4762
|
FetchDriverExecuteParamsEventRequestConstructor
|
|
4361
4763
|
} from "@browserstack/wdio-browserstack-service";
|
|
4362
|
-
var GrpcClient;
|
|
4764
|
+
var GRPC_MESSAGE_LIMIT, GrpcClient;
|
|
4363
4765
|
var init_grpcClient = __esm({
|
|
4364
4766
|
"src/cli/grpcClient.ts"() {
|
|
4365
4767
|
"use strict";
|
|
@@ -4367,13 +4769,14 @@ var init_grpcClient = __esm({
|
|
|
4367
4769
|
init_performance_tester();
|
|
4368
4770
|
init_constants2();
|
|
4369
4771
|
init_cliLogger();
|
|
4772
|
+
GRPC_MESSAGE_LIMIT = 20 * 1024 * 1024;
|
|
4370
4773
|
GrpcClient = class _GrpcClient {
|
|
4371
4774
|
static #instance = null;
|
|
4372
4775
|
binSessionId;
|
|
4373
4776
|
listenAddress;
|
|
4374
4777
|
channel = null;
|
|
4375
4778
|
client = null;
|
|
4376
|
-
logger =
|
|
4779
|
+
logger = BStackLogger2;
|
|
4377
4780
|
constructor() {
|
|
4378
4781
|
}
|
|
4379
4782
|
/**
|
|
@@ -4428,17 +4831,20 @@ var init_grpcClient = __esm({
|
|
|
4428
4831
|
if (!listenAddress) {
|
|
4429
4832
|
throw new Error("Unable to determine gRPC server listen address");
|
|
4430
4833
|
}
|
|
4834
|
+
const channelOptions = {
|
|
4835
|
+
"grpc.keepalive_time_ms": 1e4,
|
|
4836
|
+
"grpc.max_send_message_length": GRPC_MESSAGE_LIMIT,
|
|
4837
|
+
"grpc.max_receive_message_length": GRPC_MESSAGE_LIMIT
|
|
4838
|
+
};
|
|
4431
4839
|
this.channel = new grpcChannel(
|
|
4432
4840
|
listenAddress,
|
|
4433
4841
|
grpcCredentials.createInsecure(),
|
|
4434
|
-
|
|
4435
|
-
"grpc.keepalive_time_ms": 1e4
|
|
4436
|
-
}
|
|
4842
|
+
channelOptions
|
|
4437
4843
|
);
|
|
4438
4844
|
this.client = new SDKClient(
|
|
4439
4845
|
listenAddress,
|
|
4440
4846
|
grpcCredentials.createInsecure(),
|
|
4441
|
-
|
|
4847
|
+
channelOptions
|
|
4442
4848
|
);
|
|
4443
4849
|
this.logger.info(`Connected to gRPC server at ${listenAddress}`);
|
|
4444
4850
|
}
|
|
@@ -4462,7 +4868,7 @@ var init_grpcClient = __esm({
|
|
|
4462
4868
|
sdkLanguage: CLIUtils.getSdkLanguage(),
|
|
4463
4869
|
sdkVersion: packageVersion,
|
|
4464
4870
|
pathProject: process.cwd(),
|
|
4465
|
-
pathConfig:
|
|
4871
|
+
pathConfig: path13.resolve(process.cwd(), "browserstack.yml"),
|
|
4466
4872
|
cliArgs: process.argv.slice(2),
|
|
4467
4873
|
frameworks: [automationFrameworkDetail.name, testFrameworkDetail.name],
|
|
4468
4874
|
frameworkVersions,
|
|
@@ -4908,7 +5314,7 @@ var init_baseModule = __esm({
|
|
|
4908
5314
|
this.platformIndex = platformIndex;
|
|
4909
5315
|
this.client = client;
|
|
4910
5316
|
this.config = config;
|
|
4911
|
-
|
|
5317
|
+
BStackLogger2.debug(`Configured module ${this.getModuleName()} with binSessionId=${binSessionId}, platformIndex=${platformIndex}`);
|
|
4912
5318
|
}
|
|
4913
5319
|
};
|
|
4914
5320
|
}
|
|
@@ -5179,7 +5585,7 @@ var init_testFramework = __esm({
|
|
|
5179
5585
|
* @returns {void}
|
|
5180
5586
|
*/
|
|
5181
5587
|
trackEvent(testFrameworkState, hookState, args = {}) {
|
|
5182
|
-
|
|
5588
|
+
BStackLogger2.info(`trackEvent: testFrameworkState=${testFrameworkState}; hookState=${hookState}; args=${args}`);
|
|
5183
5589
|
}
|
|
5184
5590
|
/**
|
|
5185
5591
|
* run test hooks
|
|
@@ -5189,7 +5595,7 @@ var init_testFramework = __esm({
|
|
|
5189
5595
|
* @param {*} args
|
|
5190
5596
|
*/
|
|
5191
5597
|
async runHooks(instance, testFrameworkState, hookState, args = {}) {
|
|
5192
|
-
|
|
5598
|
+
BStackLogger2.info(`runHooks: instance=${instance} automationFrameworkState=${testFrameworkState} hookState=${hookState}`);
|
|
5193
5599
|
const hookRegistryKey = CLIUtils.getHookRegistryKey(testFrameworkState, hookState);
|
|
5194
5600
|
await eventDispatcher.notifyObserver(hookRegistryKey, args);
|
|
5195
5601
|
}
|
|
@@ -5453,7 +5859,7 @@ var init_automationFramework = __esm({
|
|
|
5453
5859
|
* @returns {void}
|
|
5454
5860
|
*/
|
|
5455
5861
|
async trackEvent(automationFrameworkState, hookState, args = {}) {
|
|
5456
|
-
|
|
5862
|
+
BStackLogger2.info(`trackEvent: automationFrameworkState=${automationFrameworkState} hookState=${hookState} args=${args}`);
|
|
5457
5863
|
}
|
|
5458
5864
|
/**
|
|
5459
5865
|
*
|
|
@@ -5463,7 +5869,7 @@ var init_automationFramework = __esm({
|
|
|
5463
5869
|
* @param {*} args
|
|
5464
5870
|
*/
|
|
5465
5871
|
async runHooks(instance, automationFrameworkState, hookState, args = {}) {
|
|
5466
|
-
|
|
5872
|
+
BStackLogger2.info(`runHooks: automationFrameworkState=${automationFrameworkState} hookState=${hookState}`);
|
|
5467
5873
|
const hookRegistryKey = CLIUtils.getHookRegistryKey(automationFrameworkState, hookState);
|
|
5468
5874
|
await eventDispatcher.notifyObserver(hookRegistryKey, args);
|
|
5469
5875
|
}
|
|
@@ -5481,7 +5887,7 @@ var init_automationFramework = __esm({
|
|
|
5481
5887
|
* @returns {void}
|
|
5482
5888
|
*/
|
|
5483
5889
|
static setTrackedInstance(context, instance) {
|
|
5484
|
-
|
|
5890
|
+
BStackLogger2.debug(`setTrackedInstance: ${context.getId()}`);
|
|
5485
5891
|
_AutomationFramework.instances.set(context.getId(), instance);
|
|
5486
5892
|
}
|
|
5487
5893
|
/**
|
|
@@ -5489,7 +5895,7 @@ var init_automationFramework = __esm({
|
|
|
5489
5895
|
* @returns {TrackedInstance} The tracked instance
|
|
5490
5896
|
*/
|
|
5491
5897
|
static getTrackedInstance() {
|
|
5492
|
-
|
|
5898
|
+
BStackLogger2.debug(`getTrackedInstance: ${CLIUtils.getCurrentInstanceName()}`);
|
|
5493
5899
|
const context = TrackedInstance.createContext(CLIUtils.getCurrentInstanceName());
|
|
5494
5900
|
return _AutomationFramework.instances.get(context.getId());
|
|
5495
5901
|
}
|
|
@@ -5617,7 +6023,7 @@ var init_automateModule = __esm({
|
|
|
5617
6023
|
init_automationFrameworkState();
|
|
5618
6024
|
init_fetchWrapper();
|
|
5619
6025
|
AutomateModule = class _AutomateModule extends BaseModule {
|
|
5620
|
-
logger =
|
|
6026
|
+
logger = BStackLogger2;
|
|
5621
6027
|
browserStackConfig;
|
|
5622
6028
|
sessionMap = /* @__PURE__ */ new Map();
|
|
5623
6029
|
static MODULE_NAME = "AutomateModule";
|
|
@@ -5678,12 +6084,12 @@ var init_automateModule = __esm({
|
|
|
5678
6084
|
async onAfterTest(args) {
|
|
5679
6085
|
this.logger.debug("onAfterTest: inside automate module after test hook!");
|
|
5680
6086
|
const instace = args.instance;
|
|
5681
|
-
const { error, passed } = args.result;
|
|
6087
|
+
const { error, passed, skipped } = args.result;
|
|
5682
6088
|
const _failReasons = [];
|
|
5683
|
-
if (!passed) {
|
|
6089
|
+
if (!passed && !skipped) {
|
|
5684
6090
|
_failReasons.push(error && error.message || "Unknown Error");
|
|
5685
6091
|
}
|
|
5686
|
-
const status = passed ? "passed" : "failed";
|
|
6092
|
+
const status = passed || skipped ? "passed" : "failed";
|
|
5687
6093
|
const reason = _failReasons.length > 0 ? _failReasons.join("\n") : void 0;
|
|
5688
6094
|
const autoInstance = AutomationFramework.getTrackedInstance();
|
|
5689
6095
|
const sessionId = AutomationFramework.getState(autoInstance, AutomationFrameworkConstants.KEY_FRAMEWORK_SESSION_ID);
|
|
@@ -5933,7 +6339,7 @@ var init_testFrameworkInstance = __esm({
|
|
|
5933
6339
|
|
|
5934
6340
|
// src/cli/frameworks/wdioMochaTestFramework.ts
|
|
5935
6341
|
import { v4 as uuidv42 } from "uuid";
|
|
5936
|
-
import
|
|
6342
|
+
import path14 from "node:path";
|
|
5937
6343
|
var WdioMochaTestFramework;
|
|
5938
6344
|
var init_wdioMochaTestFramework = __esm({
|
|
5939
6345
|
"src/cli/frameworks/wdioMochaTestFramework.ts"() {
|
|
@@ -5967,11 +6373,11 @@ var init_wdioMochaTestFramework = __esm({
|
|
|
5967
6373
|
* @param {*} args
|
|
5968
6374
|
*/
|
|
5969
6375
|
async trackEvent(testFrameworkState, hookState, args = {}) {
|
|
5970
|
-
|
|
6376
|
+
BStackLogger2.info(`trackEvent: testFrameworkState=${testFrameworkState} hookState=${hookState}`);
|
|
5971
6377
|
await super.trackEvent(testFrameworkState, hookState, args);
|
|
5972
6378
|
const instance = this.resolveInstance(testFrameworkState, hookState, args);
|
|
5973
6379
|
if (instance === null) {
|
|
5974
|
-
|
|
6380
|
+
BStackLogger2.error(`trackEvent: instance not found for testFrameworkState=${testFrameworkState} hookState=${hookState}`);
|
|
5975
6381
|
return;
|
|
5976
6382
|
}
|
|
5977
6383
|
try {
|
|
@@ -5983,7 +6389,7 @@ var init_wdioMochaTestFramework = __esm({
|
|
|
5983
6389
|
if (!TestFramework.getState(instance, TestFrameworkConstants.KEY_TEST_ID) && hookState === HookState.PRE && testFrameworkState === TestFrameworkState.TEST) {
|
|
5984
6390
|
const test = args.test;
|
|
5985
6391
|
const testData = await this.getTestData(instance, test);
|
|
5986
|
-
|
|
6392
|
+
BStackLogger2.info(`trackEvent: instanceData=${JSON.stringify(Object.fromEntries(instance.getAllData()))}`);
|
|
5987
6393
|
instance.updateMultipleEntries(testData);
|
|
5988
6394
|
}
|
|
5989
6395
|
if (testFrameworkState === TestFrameworkState.TEST) {
|
|
@@ -6001,13 +6407,13 @@ var init_wdioMochaTestFramework = __esm({
|
|
|
6001
6407
|
logEntry.uuid = TestFramework.getState(instance, TestFrameworkConstants.KEY_HOOK_ID);
|
|
6002
6408
|
this.loadLogEntries(instance, testFrameworkState, hookState, logEntry);
|
|
6003
6409
|
} else if (testFrameworkState === TestFrameworkState.LOG_REPORT && hookState === HookState.POST) {
|
|
6004
|
-
|
|
6410
|
+
BStackLogger2.info("trackEvent: load test results");
|
|
6005
6411
|
this.loadTestResult(instance, args);
|
|
6006
6412
|
}
|
|
6007
6413
|
await this.trackHookEvents(instance, testFrameworkState, hookState, args);
|
|
6008
|
-
|
|
6414
|
+
BStackLogger2.debug(`trackEvent: tracked instance data=${JSON.stringify(Object.fromEntries(instance.getAllData()))}`);
|
|
6009
6415
|
} catch (error) {
|
|
6010
|
-
|
|
6416
|
+
BStackLogger2.error(`trackEvent: Error in tracking events: ${error} hookState=${hookState} testFrameworkState=${testFrameworkState}`);
|
|
6011
6417
|
}
|
|
6012
6418
|
args.instance = instance;
|
|
6013
6419
|
await this.runHooks(instance, testFrameworkState, hookState, args);
|
|
@@ -6021,7 +6427,7 @@ var init_wdioMochaTestFramework = __esm({
|
|
|
6021
6427
|
*/
|
|
6022
6428
|
resolveInstance(testFrameworkState, hookState, args = {}) {
|
|
6023
6429
|
let instance = null;
|
|
6024
|
-
|
|
6430
|
+
BStackLogger2.info(`resolveInstance: resolving instance for testFrameworkState=${testFrameworkState} hookState=${hookState}`);
|
|
6025
6431
|
if (testFrameworkState === TestFrameworkState.INIT_TEST || testFrameworkState === TestFrameworkState.NONE) {
|
|
6026
6432
|
this.trackWdioMochaInstance(testFrameworkState, args);
|
|
6027
6433
|
}
|
|
@@ -6038,7 +6444,7 @@ var init_wdioMochaTestFramework = __esm({
|
|
|
6038
6444
|
const target = CLIUtils.getCurrentInstanceName();
|
|
6039
6445
|
const trackedContext = TrackedInstance.createContext(target);
|
|
6040
6446
|
let instance = null;
|
|
6041
|
-
|
|
6447
|
+
BStackLogger2.info(`trackWdioMochaInstance: created instance for target=${target}, state=${testFrameworkState}, args=${args}`);
|
|
6042
6448
|
instance = new TestFrameworkInstance(
|
|
6043
6449
|
trackedContext,
|
|
6044
6450
|
this.getTestFrameworks(),
|
|
@@ -6061,7 +6467,7 @@ var init_wdioMochaTestFramework = __esm({
|
|
|
6061
6467
|
process.env[TEST_ANALYTICS_ID] = instanceEntries[TestFrameworkConstants.KEY_TEST_UUID];
|
|
6062
6468
|
instance.updateMultipleEntries(instanceEntries);
|
|
6063
6469
|
TestFramework.setTrackedInstance(trackedContext, instance);
|
|
6064
|
-
|
|
6470
|
+
BStackLogger2.info(`trackWdioMochaInstance: saved instance contextId=${trackedContext.getId()} target=${target}`);
|
|
6065
6471
|
}
|
|
6066
6472
|
async getTestData(instance, test) {
|
|
6067
6473
|
const framework = TestFramework.getState(instance, TestFrameworkConstants.KEY_TEST_FRAMEWORK_NAME);
|
|
@@ -6072,8 +6478,8 @@ var init_wdioMochaTestFramework = __esm({
|
|
|
6072
6478
|
[TestFrameworkConstants.KEY_TEST_ID]: getUniqueIdentifier(test, framework),
|
|
6073
6479
|
[TestFrameworkConstants.KEY_TEST_NAME]: test.title || test.description,
|
|
6074
6480
|
[TestFrameworkConstants.KEY_TEST_CODE]: test.body || "",
|
|
6075
|
-
[TestFrameworkConstants.KEY_TEST_FILE_PATH]: gitConfig?.root && filename ?
|
|
6076
|
-
[TestFrameworkConstants.KEY_TEST_LOCATION]: filename ?
|
|
6481
|
+
[TestFrameworkConstants.KEY_TEST_FILE_PATH]: gitConfig?.root && filename ? path14.relative(gitConfig.root, filename) : void 0,
|
|
6482
|
+
[TestFrameworkConstants.KEY_TEST_LOCATION]: filename ? path14.relative(process.cwd(), filename) : void 0,
|
|
6077
6483
|
[TestFrameworkConstants.KEY_TEST_SCOPE]: fullTitle,
|
|
6078
6484
|
[TestFrameworkConstants.KEY_TEST_SCOPES]: getMochaTestHierarchy(test)
|
|
6079
6485
|
};
|
|
@@ -6081,13 +6487,17 @@ var init_wdioMochaTestFramework = __esm({
|
|
|
6081
6487
|
}
|
|
6082
6488
|
loadTestResult(instance, args) {
|
|
6083
6489
|
const results = args.result;
|
|
6084
|
-
const { error, passed } = results;
|
|
6490
|
+
const { error, passed, skipped } = results;
|
|
6085
6491
|
let result = "passed";
|
|
6086
6492
|
let failure = null;
|
|
6087
6493
|
let failureReason = null;
|
|
6088
6494
|
let failureType = null;
|
|
6089
6495
|
if (!passed) {
|
|
6090
|
-
|
|
6496
|
+
if (skipped) {
|
|
6497
|
+
result = "skipped";
|
|
6498
|
+
} else {
|
|
6499
|
+
result = error && error.message && error.message.includes("sync skip; aborting execution") ? "ignore" : "failed";
|
|
6500
|
+
}
|
|
6091
6501
|
if (error && result !== "skipped") {
|
|
6092
6502
|
failure = [{ backtrace: [removeAnsiColors(error.message), removeAnsiColors(error.stack || "")] }];
|
|
6093
6503
|
failureReason = removeAnsiColors(error.message);
|
|
@@ -6122,7 +6532,7 @@ var init_wdioMochaTestFramework = __esm({
|
|
|
6122
6532
|
if (lastActiveHook) {
|
|
6123
6533
|
const hookLogs = lastActiveHook[TestFrameworkConstants.KEY_HOOK_LOGS];
|
|
6124
6534
|
hookLogs.push(logRecord);
|
|
6125
|
-
|
|
6535
|
+
BStackLogger2.debug(`hooks after update logs ${TestFramework.getState(instance, TestFrameworkConstants.KEY_HOOKS_STARTED)} ${TestFramework.getState(instance, TestFrameworkConstants.KEY_HOOKS_FINISHED)}`);
|
|
6126
6536
|
return;
|
|
6127
6537
|
}
|
|
6128
6538
|
const entries = TestFramework.getState(instance, TestFrameworkConstants.KEY_TEST_LOGS);
|
|
@@ -6223,15 +6633,15 @@ var init_wdioMochaTestFramework = __esm({
|
|
|
6223
6633
|
[TestFrameworkConstants.KEY_EVENT_STARTED_AT]: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6224
6634
|
[TestFrameworkConstants.KEY_HOOK_LOGS]: [],
|
|
6225
6635
|
[TestFrameworkConstants.KEY_HOOK_NAME]: test.title || test.description,
|
|
6226
|
-
[TestFrameworkConstants.KEY_TEST_FILE_PATH]: gitConfig?.root && filename ?
|
|
6227
|
-
[TestFrameworkConstants.KEY_TEST_LOCATION]: filename ?
|
|
6636
|
+
[TestFrameworkConstants.KEY_TEST_FILE_PATH]: gitConfig?.root && filename ? path14.relative(gitConfig.root, filename) : void 0,
|
|
6637
|
+
[TestFrameworkConstants.KEY_TEST_LOCATION]: filename ? path14.relative(process.cwd(), filename) : void 0
|
|
6228
6638
|
};
|
|
6229
6639
|
hooksStarted.get(key)?.push(hook);
|
|
6230
6640
|
updates[_WdioMochaTestFramework.KEY_HOOK_LAST_STARTED] = key;
|
|
6231
|
-
|
|
6641
|
+
BStackLogger2.info(`Hook Started in PRE key = ${key} & hook = ${JSON.stringify(hook)}`);
|
|
6232
6642
|
} else if (hookState === HookState.POST) {
|
|
6233
6643
|
const hooksList = hooksStarted.get(key) || [];
|
|
6234
|
-
|
|
6644
|
+
BStackLogger2.info(`Hook List in Post ${JSON.stringify(hooksList)}`);
|
|
6235
6645
|
if (hooksList.length > 0) {
|
|
6236
6646
|
const hook = hooksList.pop();
|
|
6237
6647
|
const result = testResult.status;
|
|
@@ -6244,7 +6654,7 @@ var init_wdioMochaTestFramework = __esm({
|
|
|
6244
6654
|
}
|
|
6245
6655
|
}
|
|
6246
6656
|
instance.updateMultipleEntries(updates);
|
|
6247
|
-
|
|
6657
|
+
BStackLogger2.info(`trackHookEvents: hook state=${key}.${hookState}, hooks started=${JSON.stringify(hooksStarted)}, hooks finished=${JSON.stringify(hooksFinished)}`);
|
|
6248
6658
|
}
|
|
6249
6659
|
};
|
|
6250
6660
|
}
|
|
@@ -6268,7 +6678,7 @@ var init_testHubModule = __esm({
|
|
|
6268
6678
|
init_automationFramework();
|
|
6269
6679
|
init_automationFrameworkConstants();
|
|
6270
6680
|
TestHubModule = class _TestHubModule extends BaseModule {
|
|
6271
|
-
logger =
|
|
6681
|
+
logger = BStackLogger2;
|
|
6272
6682
|
testhubConfig;
|
|
6273
6683
|
name;
|
|
6274
6684
|
static MODULE_NAME = "TestHubModule";
|
|
@@ -6541,11 +6951,11 @@ var init_wdioAutomationFramework = __esm({
|
|
|
6541
6951
|
* @param {*} args
|
|
6542
6952
|
*/
|
|
6543
6953
|
async trackEvent(automationFrameworkState, hookState, args = {}) {
|
|
6544
|
-
|
|
6954
|
+
BStackLogger2.info(`trackEvent: automationFrameworkState=${automationFrameworkState} hookState=${hookState}`);
|
|
6545
6955
|
await super.trackEvent(automationFrameworkState, hookState, args);
|
|
6546
6956
|
const instance = this.resolveInstance(automationFrameworkState, hookState, args);
|
|
6547
6957
|
if (instance === null) {
|
|
6548
|
-
|
|
6958
|
+
BStackLogger2.error(`trackEvent: instance not found for automationFrameworkState=${automationFrameworkState} hookState=${hookState}`);
|
|
6549
6959
|
return;
|
|
6550
6960
|
}
|
|
6551
6961
|
args.instance = instance;
|
|
@@ -6560,7 +6970,7 @@ var init_wdioAutomationFramework = __esm({
|
|
|
6560
6970
|
*/
|
|
6561
6971
|
resolveInstance(automationFrameworkState, hookState, args = {}) {
|
|
6562
6972
|
let instance = null;
|
|
6563
|
-
|
|
6973
|
+
BStackLogger2.info(`resolveInstance: resolving instance for automationFrameworkState=${automationFrameworkState} hookState=${hookState}`);
|
|
6564
6974
|
if (automationFrameworkState === AutomationFrameworkState.CREATE || automationFrameworkState === AutomationFrameworkState.NONE) {
|
|
6565
6975
|
this.trackWebdriverIOInstance(automationFrameworkState, args);
|
|
6566
6976
|
}
|
|
@@ -6576,13 +6986,13 @@ var init_wdioAutomationFramework = __esm({
|
|
|
6576
6986
|
// !args.browser &&
|
|
6577
6987
|
AutomationFramework.getTrackedInstance()
|
|
6578
6988
|
) {
|
|
6579
|
-
|
|
6989
|
+
BStackLogger2.info("trackWebdriverIOInstance: instance already exists");
|
|
6580
6990
|
return;
|
|
6581
6991
|
}
|
|
6582
6992
|
const target = CLIUtils.getCurrentInstanceName();
|
|
6583
6993
|
const trackedContext = TrackedInstance.createContext(target);
|
|
6584
6994
|
let instance = null;
|
|
6585
|
-
|
|
6995
|
+
BStackLogger2.info(`trackWebdriverIOInstance: created instance for target=${target}, state=${automationFrameworkState}, args=${args}`);
|
|
6586
6996
|
instance = new AutomationFrameworkInstance(
|
|
6587
6997
|
trackedContext,
|
|
6588
6998
|
this.getAutomationFrameworkName(),
|
|
@@ -6590,7 +7000,7 @@ var init_wdioAutomationFramework = __esm({
|
|
|
6590
7000
|
automationFrameworkState
|
|
6591
7001
|
);
|
|
6592
7002
|
AutomationFramework.setTrackedInstance(trackedContext, instance);
|
|
6593
|
-
|
|
7003
|
+
BStackLogger2.info(`trackWebdriverIOInstance: saved instance contextId=${trackedContext.getId()} target=${target}`);
|
|
6594
7004
|
}
|
|
6595
7005
|
};
|
|
6596
7006
|
}
|
|
@@ -6616,7 +7026,7 @@ var init_webdriverIOModule = __esm({
|
|
|
6616
7026
|
browserVersion;
|
|
6617
7027
|
platforms;
|
|
6618
7028
|
testRunId;
|
|
6619
|
-
logger =
|
|
7029
|
+
logger = BStackLogger2;
|
|
6620
7030
|
static MODULE_NAME = "WebdriverIOModule";
|
|
6621
7031
|
/**
|
|
6622
7032
|
* Create a new WebdriverIOModule
|
|
@@ -6744,7 +7154,7 @@ var init_accessibilityModule = __esm({
|
|
|
6744
7154
|
init_constants2();
|
|
6745
7155
|
init_grpcClient();
|
|
6746
7156
|
AccessibilityModule = class extends BaseModule {
|
|
6747
|
-
logger =
|
|
7157
|
+
logger = BStackLogger2;
|
|
6748
7158
|
name;
|
|
6749
7159
|
scriptInstance;
|
|
6750
7160
|
accessibility = false;
|
|
@@ -7028,7 +7438,7 @@ var init_accessibilityModule = __esm({
|
|
|
7028
7438
|
formatString(this.scriptInstance.performScan, JSON.stringify(_getParamsForAppAccessibility(commandName, testName))),
|
|
7029
7439
|
{}
|
|
7030
7440
|
);
|
|
7031
|
-
|
|
7441
|
+
BStackLogger2.debug(util9.format(results2));
|
|
7032
7442
|
return results2;
|
|
7033
7443
|
}
|
|
7034
7444
|
const results = await browser.executeAsync(
|
|
@@ -7151,7 +7561,7 @@ var init_observabilityModule = __esm({
|
|
|
7151
7561
|
init_cliLogger();
|
|
7152
7562
|
init_util();
|
|
7153
7563
|
ObservabilityModule = class _ObservabilityModule extends BaseModule {
|
|
7154
|
-
logger =
|
|
7564
|
+
logger = BStackLogger2;
|
|
7155
7565
|
observabilityConfig;
|
|
7156
7566
|
name;
|
|
7157
7567
|
static MODULE_NAME = "ObservabilityModule";
|
|
@@ -7254,22 +7664,22 @@ var init_request_handler = __esm({
|
|
|
7254
7664
|
throw new Error("Test Reporting and Analytics build start not completed yet.");
|
|
7255
7665
|
}
|
|
7256
7666
|
this.queue.push(event);
|
|
7257
|
-
|
|
7667
|
+
BStackLogger.debug(`Added data to request queue. Queue length = ${this.queue.length}`);
|
|
7258
7668
|
const shouldProceed = this.shouldProceed();
|
|
7259
7669
|
if (shouldProceed) {
|
|
7260
7670
|
this.sendBatch().catch((e) => {
|
|
7261
|
-
|
|
7671
|
+
BStackLogger.debug("Exception in sending batch: " + e);
|
|
7262
7672
|
});
|
|
7263
7673
|
}
|
|
7264
7674
|
}
|
|
7265
7675
|
async shutdown() {
|
|
7266
|
-
|
|
7676
|
+
BStackLogger.debug("shutdown started");
|
|
7267
7677
|
this.removeEventBatchPolling("Shutting down");
|
|
7268
7678
|
while (this.queue.length > 0) {
|
|
7269
7679
|
const data = this.queue.splice(0, DATA_BATCH_SIZE);
|
|
7270
7680
|
await this.callCallback(data, "SHUTDOWN_QUEUE");
|
|
7271
7681
|
}
|
|
7272
|
-
|
|
7682
|
+
BStackLogger.debug("shutdown ended");
|
|
7273
7683
|
}
|
|
7274
7684
|
startEventBatchPolling() {
|
|
7275
7685
|
this.pollEventBatchInterval = setInterval(this.sendBatch.bind(this), DATA_BATCH_INTERVAL);
|
|
@@ -7279,11 +7689,11 @@ var init_request_handler = __esm({
|
|
|
7279
7689
|
if (data.length === 0) {
|
|
7280
7690
|
return;
|
|
7281
7691
|
}
|
|
7282
|
-
|
|
7692
|
+
BStackLogger.debug(`Sending data from request queue. Data length = ${data.length}, Queue length after removal = ${this.queue.length}`);
|
|
7283
7693
|
await this.callCallback(data, "INTERVAL_QUEUE");
|
|
7284
7694
|
}
|
|
7285
7695
|
callCallback = async (data, kind) => {
|
|
7286
|
-
|
|
7696
|
+
BStackLogger.debug("calling callback with kind " + kind);
|
|
7287
7697
|
await this.callback?.(data);
|
|
7288
7698
|
};
|
|
7289
7699
|
resetEventBatchPolling() {
|
|
@@ -7292,13 +7702,13 @@ var init_request_handler = __esm({
|
|
|
7292
7702
|
}
|
|
7293
7703
|
removeEventBatchPolling(tag) {
|
|
7294
7704
|
if (this.pollEventBatchInterval) {
|
|
7295
|
-
|
|
7705
|
+
BStackLogger.debug(`${tag} request queue`);
|
|
7296
7706
|
clearInterval(this.pollEventBatchInterval);
|
|
7297
7707
|
}
|
|
7298
7708
|
}
|
|
7299
7709
|
shouldProceed() {
|
|
7300
7710
|
if (_RequestQueueHandler.tearDownInvoked) {
|
|
7301
|
-
|
|
7711
|
+
BStackLogger.debug("Force request-queue shutdown, as test run event is received after teardown");
|
|
7302
7712
|
return true;
|
|
7303
7713
|
}
|
|
7304
7714
|
return this.queue.length >= DATA_BATCH_SIZE;
|
|
@@ -7321,7 +7731,7 @@ async function uploadEventData(eventData, eventUrl = DATA_EVENT_ENDPOINT) {
|
|
|
7321
7731
|
throw new Error("Build start not completed yet");
|
|
7322
7732
|
}
|
|
7323
7733
|
if (!process.env[BROWSERSTACK_TESTHUB_JWT]) {
|
|
7324
|
-
|
|
7734
|
+
BStackLogger.debug(`[${logTag}] Missing Authentication Token/ Build ID`);
|
|
7325
7735
|
throw new Error("Token/buildID is undefined, build creation might have failed");
|
|
7326
7736
|
}
|
|
7327
7737
|
try {
|
|
@@ -7334,9 +7744,9 @@ async function uploadEventData(eventData, eventUrl = DATA_EVENT_ENDPOINT) {
|
|
|
7334
7744
|
},
|
|
7335
7745
|
body: JSON.stringify(eventData)
|
|
7336
7746
|
});
|
|
7337
|
-
|
|
7747
|
+
BStackLogger.debug(`[${logTag}] Success response: ${JSON.stringify(await data.json())}`);
|
|
7338
7748
|
} catch (error) {
|
|
7339
|
-
|
|
7749
|
+
BStackLogger.debug(`[${logTag}] Failed. Error: ${format3(error)}`);
|
|
7340
7750
|
throw error;
|
|
7341
7751
|
}
|
|
7342
7752
|
}
|
|
@@ -7399,7 +7809,7 @@ var init_listener = __esm({
|
|
|
7399
7809
|
await this.uploadPending();
|
|
7400
7810
|
await this.teardown();
|
|
7401
7811
|
} catch (e) {
|
|
7402
|
-
|
|
7812
|
+
BStackLogger.debug("Exception in onWorkerEnd: " + e);
|
|
7403
7813
|
}
|
|
7404
7814
|
}
|
|
7405
7815
|
async uploadPending(waitTimeout = DEFAULT_WAIT_TIMEOUT_FOR_PENDING_UPLOADS, waitInterval = DEFAULT_WAIT_INTERVAL_FOR_PENDING_UPLOADS) {
|
|
@@ -7410,10 +7820,10 @@ var init_listener = __esm({
|
|
|
7410
7820
|
return this.uploadPending(waitTimeout - waitInterval);
|
|
7411
7821
|
}
|
|
7412
7822
|
async teardown() {
|
|
7413
|
-
|
|
7823
|
+
BStackLogger.debug("teardown started");
|
|
7414
7824
|
RequestQueueHandler.tearDownInvoked = true;
|
|
7415
7825
|
await this.requestBatcher?.shutdown();
|
|
7416
|
-
|
|
7826
|
+
BStackLogger.debug("teardown ended");
|
|
7417
7827
|
}
|
|
7418
7828
|
hookStarted(hookData) {
|
|
7419
7829
|
try {
|
|
@@ -7521,7 +7931,7 @@ var init_listener = __esm({
|
|
|
7521
7931
|
}
|
|
7522
7932
|
markLogs(status, data) {
|
|
7523
7933
|
if (!data) {
|
|
7524
|
-
|
|
7934
|
+
BStackLogger.debug("No log data");
|
|
7525
7935
|
return;
|
|
7526
7936
|
}
|
|
7527
7937
|
try {
|
|
@@ -7530,7 +7940,7 @@ var init_listener = __esm({
|
|
|
7530
7940
|
this.logEvents.mark(status, LOG_KIND_USAGE_MAP[kind] || kind);
|
|
7531
7941
|
}
|
|
7532
7942
|
} catch (e) {
|
|
7533
|
-
|
|
7943
|
+
BStackLogger.debug("Exception in marking logs status " + e);
|
|
7534
7944
|
throw e;
|
|
7535
7945
|
}
|
|
7536
7946
|
}
|
|
@@ -7548,14 +7958,14 @@ var init_listener = __esm({
|
|
|
7548
7958
|
}
|
|
7549
7959
|
if (!this.requestBatcher) {
|
|
7550
7960
|
this.requestBatcher = RequestQueueHandler.getInstance(async (data) => {
|
|
7551
|
-
|
|
7961
|
+
BStackLogger.debug("callback: called with events " + data.length);
|
|
7552
7962
|
try {
|
|
7553
7963
|
this.pendingUploads += 1;
|
|
7554
7964
|
await batchAndPostEvents(DATA_BATCH_ENDPOINT, "BATCH_DATA", data);
|
|
7555
|
-
|
|
7965
|
+
BStackLogger.debug("callback: marking events success " + data.length);
|
|
7556
7966
|
this.eventsSuccess(data);
|
|
7557
7967
|
} catch {
|
|
7558
|
-
|
|
7968
|
+
BStackLogger.debug("callback: marking events failed " + data.length);
|
|
7559
7969
|
this.eventsFailed(data);
|
|
7560
7970
|
} finally {
|
|
7561
7971
|
this.pendingUploads -= 1;
|
|
@@ -7612,7 +8022,7 @@ var init_listener = __esm({
|
|
|
7612
8022
|
});
|
|
7613
8023
|
|
|
7614
8024
|
// src/reporter.ts
|
|
7615
|
-
import
|
|
8025
|
+
import path15 from "node:path";
|
|
7616
8026
|
import WDIOReporter from "@wdio/reporter";
|
|
7617
8027
|
import * as url from "node:url";
|
|
7618
8028
|
import { v4 as uuidv43 } from "uuid";
|
|
@@ -7705,7 +8115,7 @@ var init_reporter = __esm({
|
|
|
7705
8115
|
filename = this._suiteName || suiteStats.file;
|
|
7706
8116
|
}
|
|
7707
8117
|
} catch {
|
|
7708
|
-
|
|
8118
|
+
BStackLogger.debug("Error in decoding file name of suite");
|
|
7709
8119
|
}
|
|
7710
8120
|
}
|
|
7711
8121
|
this._suiteName = filename;
|
|
@@ -7835,9 +8245,9 @@ var init_reporter = __esm({
|
|
|
7835
8245
|
scope,
|
|
7836
8246
|
scopes,
|
|
7837
8247
|
identifier,
|
|
7838
|
-
file_name: suiteFileName ?
|
|
7839
|
-
location: suiteFileName ?
|
|
7840
|
-
vc_filepath: this._gitConfigPath && suiteFileName ?
|
|
8248
|
+
file_name: suiteFileName ? path15.relative(process.cwd(), suiteFileName) : void 0,
|
|
8249
|
+
location: suiteFileName ? path15.relative(process.cwd(), suiteFileName) : void 0,
|
|
8250
|
+
vc_filepath: this._gitConfigPath && suiteFileName ? path15.relative(this._gitConfigPath, suiteFileName) : void 0,
|
|
7841
8251
|
started_at: testStats.start && testStats.start.toISOString(),
|
|
7842
8252
|
finished_at: testStats.end && testStats.end.toISOString(),
|
|
7843
8253
|
framework,
|
|
@@ -7850,13 +8260,15 @@ var init_reporter = __esm({
|
|
|
7850
8260
|
if (eventType.startsWith("TestRun") || eventType === "HookRunStarted") {
|
|
7851
8261
|
const cloudProvider = getCloudProvider({ options: { hostname: this._config?.hostname } });
|
|
7852
8262
|
testData.integrations = {};
|
|
8263
|
+
const liveBrowserCaps = globalThis?.browser?.capabilities;
|
|
7853
8264
|
testData.integrations[cloudProvider] = {
|
|
7854
8265
|
capabilities: this._capabilities,
|
|
7855
8266
|
session_id: this._sessionId,
|
|
7856
8267
|
browser: this._capabilities?.browserName,
|
|
7857
8268
|
browser_version: this._capabilities?.browserVersion,
|
|
7858
8269
|
platform: this._capabilities?.platformName,
|
|
7859
|
-
platform_version: getPlatformVersion(this._capabilities, this._userCaps)
|
|
8270
|
+
platform_version: getPlatformVersion(this._capabilities, this._userCaps),
|
|
8271
|
+
device: getResolvedDeviceName(liveBrowserCaps, this._userCaps)
|
|
7860
8272
|
};
|
|
7861
8273
|
}
|
|
7862
8274
|
if (eventType === "TestRunFinished" || eventType === "HookRunFinished") {
|
|
@@ -7886,7 +8298,7 @@ var init_reporter = __esm({
|
|
|
7886
8298
|
});
|
|
7887
8299
|
|
|
7888
8300
|
// src/insights-handler.ts
|
|
7889
|
-
import
|
|
8301
|
+
import path16 from "node:path";
|
|
7890
8302
|
import { v4 as uuidv44 } from "uuid";
|
|
7891
8303
|
var _InsightsHandler, InsightsHandler, insights_handler_default;
|
|
7892
8304
|
var init_insights_handler = __esm({
|
|
@@ -8166,9 +8578,9 @@ var init_insights_handler = __esm({
|
|
|
8166
8578
|
test_run_id: hookData.testRunId,
|
|
8167
8579
|
scope: feature?.name,
|
|
8168
8580
|
scopes: [feature?.name || ""],
|
|
8169
|
-
file_name: uri ?
|
|
8170
|
-
location: uri ?
|
|
8171
|
-
vc_filepath: this._gitConfigPath && uri ?
|
|
8581
|
+
file_name: uri ? path16.relative(process.cwd(), uri) : void 0,
|
|
8582
|
+
location: uri ? path16.relative(process.cwd(), uri) : void 0,
|
|
8583
|
+
vc_filepath: this._gitConfigPath && uri ? path16.relative(this._gitConfigPath, uri) : void 0,
|
|
8172
8584
|
result: "pending",
|
|
8173
8585
|
framework: this._framework
|
|
8174
8586
|
};
|
|
@@ -8333,7 +8745,7 @@ var init_insights_handler = __esm({
|
|
|
8333
8745
|
this.listener.logCreated([stdLog]);
|
|
8334
8746
|
}
|
|
8335
8747
|
} catch (error) {
|
|
8336
|
-
|
|
8748
|
+
BStackLogger.debug(`Exception in uploading log data to Test Reporting and Analytics with error : ${error}`);
|
|
8337
8749
|
}
|
|
8338
8750
|
};
|
|
8339
8751
|
async browserCommand(commandType, args, test) {
|
|
@@ -8466,9 +8878,9 @@ var init_insights_handler = __esm({
|
|
|
8466
8878
|
scope: fullTitle,
|
|
8467
8879
|
scopes: this.getHierarchy(test),
|
|
8468
8880
|
identifier: fullTitle,
|
|
8469
|
-
file_name: filename ?
|
|
8470
|
-
location: filename ?
|
|
8471
|
-
vc_filepath: this._gitConfigPath && filename ?
|
|
8881
|
+
file_name: filename ? path16.relative(process.cwd(), filename) : void 0,
|
|
8882
|
+
location: filename ? path16.relative(process.cwd(), filename) : void 0,
|
|
8883
|
+
vc_filepath: this._gitConfigPath && filename ? path16.relative(this._gitConfigPath, filename) : void 0,
|
|
8472
8884
|
started_at: testMetaData.startedAt,
|
|
8473
8885
|
finished_at: testMetaData.finishedAt,
|
|
8474
8886
|
result: "pending",
|
|
@@ -8578,9 +8990,9 @@ var init_insights_handler = __esm({
|
|
|
8578
8990
|
scope: fullNameWithExamples,
|
|
8579
8991
|
scopes: [feature?.name || ""],
|
|
8580
8992
|
identifier: scenario?.name,
|
|
8581
|
-
file_name: feature && feature.path ?
|
|
8582
|
-
location: feature && feature.path ?
|
|
8583
|
-
vc_filepath: this._gitConfigPath && feature?.path ?
|
|
8993
|
+
file_name: feature && feature.path ? path16.relative(process.cwd(), feature.path) : void 0,
|
|
8994
|
+
location: feature && feature.path ? path16.relative(process.cwd(), feature.path) : void 0,
|
|
8995
|
+
vc_filepath: this._gitConfigPath && feature?.path ? path16.relative(this._gitConfigPath, feature?.path) : void 0,
|
|
8584
8996
|
framework: this._framework,
|
|
8585
8997
|
result: "pending",
|
|
8586
8998
|
meta: {
|
|
@@ -8633,7 +9045,7 @@ var init_insights_handler = __esm({
|
|
|
8633
9045
|
return testData;
|
|
8634
9046
|
}
|
|
8635
9047
|
async flushCBTDataQueue() {
|
|
8636
|
-
|
|
9048
|
+
BStackLogger.debug(`Flushing CBT Data Queue ${this.currentTestId}`);
|
|
8637
9049
|
if (isUndefined(this.currentTestId)) {
|
|
8638
9050
|
return;
|
|
8639
9051
|
}
|
|
@@ -8653,7 +9065,7 @@ var init_insights_handler = __esm({
|
|
|
8653
9065
|
uuid: "",
|
|
8654
9066
|
integrations: integrationsData
|
|
8655
9067
|
};
|
|
8656
|
-
|
|
9068
|
+
BStackLogger.debug(`Sending CBT Data ${this.currentTestId} ${JSON.stringify(cbtData)}`);
|
|
8657
9069
|
if (this.currentTestId !== void 0) {
|
|
8658
9070
|
cbtData.uuid = this.currentTestId;
|
|
8659
9071
|
this.listener.cbtSessionCreated(cbtData);
|
|
@@ -8664,8 +9076,8 @@ var init_insights_handler = __esm({
|
|
|
8664
9076
|
getIntegrationsObject() {
|
|
8665
9077
|
const caps = this._browser?.capabilities;
|
|
8666
9078
|
const sessionId = this._browser?.sessionId;
|
|
8667
|
-
|
|
8668
|
-
|
|
9079
|
+
BStackLogger.debug(`Driver capabilities used for integration object: ${JSON.stringify(caps)}`);
|
|
9080
|
+
BStackLogger.debug(`User capabilities used for integration object: ${JSON.stringify(this._userCaps)}`);
|
|
8669
9081
|
return {
|
|
8670
9082
|
capabilities: caps,
|
|
8671
9083
|
session_id: sessionId,
|
|
@@ -8673,7 +9085,8 @@ var init_insights_handler = __esm({
|
|
|
8673
9085
|
browser_version: caps?.browserVersion,
|
|
8674
9086
|
platform: caps?.platformName,
|
|
8675
9087
|
product: this._platformMeta?.product,
|
|
8676
|
-
platform_version: getPlatformVersion(caps, this._userCaps)
|
|
9088
|
+
platform_version: getPlatformVersion(caps, this._userCaps),
|
|
9089
|
+
device: getResolvedDeviceName(caps, this._userCaps)
|
|
8677
9090
|
};
|
|
8678
9091
|
}
|
|
8679
9092
|
getIdentifier(test) {
|
|
@@ -8966,7 +9379,7 @@ var init_percyModule = __esm({
|
|
|
8966
9379
|
init_Percy_Handler();
|
|
8967
9380
|
init_testFrameworkConstants();
|
|
8968
9381
|
PercyModule = class _PercyModule extends BaseModule {
|
|
8969
|
-
logger =
|
|
9382
|
+
logger = BStackLogger2;
|
|
8970
9383
|
browser;
|
|
8971
9384
|
static MODULE_NAME = "PercyModule";
|
|
8972
9385
|
percyHandler;
|
|
@@ -9087,7 +9500,7 @@ var init_cli = __esm({
|
|
|
9087
9500
|
cliParams = null;
|
|
9088
9501
|
automationFramework = null;
|
|
9089
9502
|
SDK_CLI_BIN_PATH = null;
|
|
9090
|
-
logger =
|
|
9503
|
+
logger = BStackLogger2;
|
|
9091
9504
|
options;
|
|
9092
9505
|
constructor() {
|
|
9093
9506
|
this.initialized = false;
|
|
@@ -9146,7 +9559,7 @@ var init_cli = __esm({
|
|
|
9146
9559
|
const response = await GrpcClient.getInstance().startBinSession(this.wdioConfig);
|
|
9147
9560
|
const redactedStartResponse = JSON.parse(JSON.stringify(response));
|
|
9148
9561
|
CrashReporter.recursivelyRedactKeysFromObject(redactedStartResponse, ["user", "username", "key", "accesskey", "password"]);
|
|
9149
|
-
|
|
9562
|
+
BStackLogger2.debug(`start: startBinSession response=${JSON.stringify(redactedStartResponse)}`);
|
|
9150
9563
|
this.loadModules(response);
|
|
9151
9564
|
this.isMainConnected = true;
|
|
9152
9565
|
}
|
|
@@ -9158,6 +9571,7 @@ var init_cli = __esm({
|
|
|
9158
9571
|
this.binSessionId = startBinResponse.binSessionId;
|
|
9159
9572
|
this.logger.info(`loadModules: binSessionId=${this.binSessionId}`);
|
|
9160
9573
|
this.setConfig(startBinResponse);
|
|
9574
|
+
this.logBuildErrors(startBinResponse);
|
|
9161
9575
|
APIUtils.updateURLSForGRR(this.config.apis);
|
|
9162
9576
|
this.setupTestFramework();
|
|
9163
9577
|
this.setupAutomationFramework();
|
|
@@ -9193,6 +9607,28 @@ var init_cli = __esm({
|
|
|
9193
9607
|
}
|
|
9194
9608
|
this.configureModules();
|
|
9195
9609
|
}
|
|
9610
|
+
/**
|
|
9611
|
+
* Log any build errors the binary reported via testhub.errors. The
|
|
9612
|
+
* field is a JSON-encoded { [errorKey]: { message, type } } map. Called
|
|
9613
|
+
* early in loadModules so the user sees the actionable cause (e.g.
|
|
9614
|
+
* invalid credentials) before any downstream bootstrap step that
|
|
9615
|
+
* depends on a fully populated config.
|
|
9616
|
+
*/
|
|
9617
|
+
logBuildErrors(startBinResponse) {
|
|
9618
|
+
const rawErrors = startBinResponse.testhub?.errors;
|
|
9619
|
+
if (!rawErrors || !rawErrors.length) {
|
|
9620
|
+
return;
|
|
9621
|
+
}
|
|
9622
|
+
try {
|
|
9623
|
+
const errors = JSON.parse(Buffer.from(rawErrors).toString());
|
|
9624
|
+
for (const [code, detail] of Object.entries(errors)) {
|
|
9625
|
+
const { message } = detail;
|
|
9626
|
+
BStackLogger2.error(`[Build] ${code}: ${message}`);
|
|
9627
|
+
}
|
|
9628
|
+
} catch (e) {
|
|
9629
|
+
BStackLogger2.debug(`Failed to parse testhub errors: ${e}`);
|
|
9630
|
+
}
|
|
9631
|
+
}
|
|
9196
9632
|
/**
|
|
9197
9633
|
* Configure modules
|
|
9198
9634
|
* @returns {Promise<void>}
|
|
@@ -9223,12 +9659,61 @@ var init_cli = __esm({
|
|
|
9223
9659
|
return Promise.resolve();
|
|
9224
9660
|
}
|
|
9225
9661
|
const SDK_CLI_BIN_PATH = await this.getCliBinPath();
|
|
9662
|
+
if (!SDK_CLI_BIN_PATH) {
|
|
9663
|
+
throw new Error("Failed to get CLI binary path. CLI setup failed.");
|
|
9664
|
+
}
|
|
9226
9665
|
const cmd = [SDK_CLI_BIN_PATH, "sdk"];
|
|
9227
9666
|
this.logger.debug(`spawning command='${cmd}'`);
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9667
|
+
for (let attempt = 1; attempt <= MAX_SPAWN_RETRIES; attempt++) {
|
|
9668
|
+
try {
|
|
9669
|
+
await this._spawnAndAwaitReady(cmd);
|
|
9670
|
+
if (attempt > 1) {
|
|
9671
|
+
this.logger.info(`Spawn succeeded on attempt ${attempt}/${MAX_SPAWN_RETRIES}`);
|
|
9672
|
+
}
|
|
9673
|
+
PerformanceTester.end(EVENTS.SDK_CLI_START);
|
|
9674
|
+
return;
|
|
9675
|
+
} catch (err) {
|
|
9676
|
+
const isBusy = BINARY_BUSY_ERROR_CODES.includes(err.code) || err.message && (err.message.toLowerCase().includes("text file busy") || err.message.toLowerCase().includes("being used by another process"));
|
|
9677
|
+
if (isBusy && attempt < MAX_SPAWN_RETRIES) {
|
|
9678
|
+
this.logger.warn(
|
|
9679
|
+
`Spawn attempt ${attempt}/${MAX_SPAWN_RETRIES} failed with busy error, retrying in ${SPAWN_RETRY_DELAY_MS}ms: ${err.message}`
|
|
9680
|
+
);
|
|
9681
|
+
if (this.process) {
|
|
9682
|
+
this.process.removeAllListeners();
|
|
9683
|
+
this.process.stdout?.removeAllListeners();
|
|
9684
|
+
this.process.stderr?.removeAllListeners();
|
|
9685
|
+
this.process = null;
|
|
9686
|
+
}
|
|
9687
|
+
await new Promise((r) => setTimeout(r, SPAWN_RETRY_DELAY_MS));
|
|
9688
|
+
} else {
|
|
9689
|
+
PerformanceTester.end(EVENTS.SDK_CLI_START, false, err);
|
|
9690
|
+
throw err;
|
|
9691
|
+
}
|
|
9692
|
+
}
|
|
9693
|
+
}
|
|
9694
|
+
}
|
|
9695
|
+
/**
|
|
9696
|
+
* Spawn the CLI process and wait until it emits "ready".
|
|
9697
|
+
* Handles both synchronous spawn errors and async error/close events.
|
|
9698
|
+
*/
|
|
9699
|
+
_spawnAndAwaitReady(cmd) {
|
|
9231
9700
|
return new Promise((resolve, reject) => {
|
|
9701
|
+
let settled = false;
|
|
9702
|
+
const settle = (fn, val) => {
|
|
9703
|
+
if (settled) {
|
|
9704
|
+
return;
|
|
9705
|
+
}
|
|
9706
|
+
settled = true;
|
|
9707
|
+
fn(val);
|
|
9708
|
+
};
|
|
9709
|
+
try {
|
|
9710
|
+
this.process = spawn3(cmd[0], cmd.slice(1), {
|
|
9711
|
+
env: process.env
|
|
9712
|
+
});
|
|
9713
|
+
} catch (syncErr) {
|
|
9714
|
+
settle(reject, syncErr);
|
|
9715
|
+
return;
|
|
9716
|
+
}
|
|
9232
9717
|
const cliOut = {};
|
|
9233
9718
|
this.process.stdout.on("data", (data) => {
|
|
9234
9719
|
const lines = data.toString().trim().split("\n");
|
|
@@ -9241,8 +9726,7 @@ var init_cli = __esm({
|
|
|
9241
9726
|
}
|
|
9242
9727
|
if (line.toLowerCase().includes("ready")) {
|
|
9243
9728
|
this.loadCliParams(cliOut);
|
|
9244
|
-
|
|
9245
|
-
resolve();
|
|
9729
|
+
settle(resolve);
|
|
9246
9730
|
return;
|
|
9247
9731
|
}
|
|
9248
9732
|
}
|
|
@@ -9251,14 +9735,11 @@ var init_cli = __esm({
|
|
|
9251
9735
|
this.logger.error(`CLI stderr: ${data.toString().trim()}`);
|
|
9252
9736
|
});
|
|
9253
9737
|
this.process.on("error", (err) => {
|
|
9254
|
-
|
|
9255
|
-
PerformanceTester.end(EVENTS.SDK_CLI_START, false, err);
|
|
9256
|
-
reject(new Error(`Failed to start CLI process: ${err.message}`));
|
|
9738
|
+
settle(reject, err);
|
|
9257
9739
|
});
|
|
9258
9740
|
this.process.on("close", (code) => {
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
}
|
|
9741
|
+
const msg = code !== 0 ? `CLI process exited with code ${code}` : "CLI process exited cleanly before emitting ready";
|
|
9742
|
+
settle(reject, new Error(msg));
|
|
9262
9743
|
});
|
|
9263
9744
|
});
|
|
9264
9745
|
}
|
|
@@ -9272,7 +9753,7 @@ var init_cli = __esm({
|
|
|
9272
9753
|
try {
|
|
9273
9754
|
if (this.isMainConnected) {
|
|
9274
9755
|
const response = await GrpcClient.getInstance().stopBinSession();
|
|
9275
|
-
|
|
9756
|
+
BStackLogger2.debug(`stop: stopBinSession response=${JSON.stringify(response)}`);
|
|
9276
9757
|
}
|
|
9277
9758
|
await this.unConfigureModules();
|
|
9278
9759
|
if (this.process && this.process.pid) {
|
|
@@ -9454,8 +9935,8 @@ var init_cli = __esm({
|
|
|
9454
9935
|
});
|
|
9455
9936
|
|
|
9456
9937
|
// src/testorchestration/helpers.ts
|
|
9457
|
-
import
|
|
9458
|
-
import
|
|
9938
|
+
import os6 from "node:os";
|
|
9939
|
+
import path19 from "node:path";
|
|
9459
9940
|
import { spawnSync } from "node:child_process";
|
|
9460
9941
|
import logger4 from "@wdio/logger";
|
|
9461
9942
|
function isValidGitRef(ref) {
|
|
@@ -9481,11 +9962,11 @@ function safeGitCommand(args, cwd) {
|
|
|
9481
9962
|
}
|
|
9482
9963
|
function getHostInfo() {
|
|
9483
9964
|
return {
|
|
9484
|
-
hostname:
|
|
9965
|
+
hostname: os6.hostname(),
|
|
9485
9966
|
platform: process.platform,
|
|
9486
9967
|
architecture: process.arch,
|
|
9487
|
-
release:
|
|
9488
|
-
username:
|
|
9968
|
+
release: os6.release(),
|
|
9969
|
+
username: os6.userInfo().username
|
|
9489
9970
|
};
|
|
9490
9971
|
}
|
|
9491
9972
|
function isValidGitResult(result) {
|
|
@@ -9576,7 +10057,7 @@ function getGitMetadataForAISelection(folders = []) {
|
|
|
9576
10057
|
if (folders === null) {
|
|
9577
10058
|
folders = [process.cwd()];
|
|
9578
10059
|
}
|
|
9579
|
-
const uniqueFolders = [...new Set(folders.map((f) =>
|
|
10060
|
+
const uniqueFolders = [...new Set(folders.map((f) => path19.resolve(f)))];
|
|
9580
10061
|
log4.debug(`Processing ${uniqueFolders.length} unique folders out of ${folders.length} total`);
|
|
9581
10062
|
const results = [];
|
|
9582
10063
|
for (const folder of uniqueFolders) {
|
|
@@ -9733,14 +10214,14 @@ var init_request_utils = __esm({
|
|
|
9733
10214
|
* Makes a request to the test orchestration split tests endpoint
|
|
9734
10215
|
*/
|
|
9735
10216
|
static async testOrchestrationSplitTests(reqEndpoint, data) {
|
|
9736
|
-
|
|
10217
|
+
BStackLogger.debug("Processing Request for testOrchestrationSplitTests");
|
|
9737
10218
|
return _RequestUtils.makeOrchestrationRequest("POST", reqEndpoint, { data });
|
|
9738
10219
|
}
|
|
9739
10220
|
/**
|
|
9740
10221
|
* Gets ordered tests from the test orchestration
|
|
9741
10222
|
*/
|
|
9742
10223
|
static async getTestOrchestrationOrderedTests(reqEndpoint) {
|
|
9743
|
-
|
|
10224
|
+
BStackLogger.debug("Processing Request for getTestOrchestrationOrderedTests");
|
|
9744
10225
|
return _RequestUtils.makeOrchestrationRequest("GET", reqEndpoint, {});
|
|
9745
10226
|
}
|
|
9746
10227
|
/**
|
|
@@ -9777,13 +10258,13 @@ var init_request_utils = __esm({
|
|
|
9777
10258
|
throw new Error(`Unsupported HTTP method: ${method}`);
|
|
9778
10259
|
}
|
|
9779
10260
|
const response = await fetchWrap(urlObject.toString(), requestInit);
|
|
9780
|
-
|
|
10261
|
+
BStackLogger.debug(`Orchestration request made to URL: ${urlObject.toString()} with method: ${method}`);
|
|
9781
10262
|
const rawBody = await response.text();
|
|
9782
10263
|
let responseObj = rawBody;
|
|
9783
10264
|
try {
|
|
9784
10265
|
responseObj = rawBody ? JSON.parse(rawBody) : {};
|
|
9785
10266
|
} catch (error) {
|
|
9786
|
-
|
|
10267
|
+
BStackLogger.debug(`Failed to parse JSON response: ${error} - ${rawBody}`);
|
|
9787
10268
|
}
|
|
9788
10269
|
if (responseObj && typeof responseObj === "object" && !Array.isArray(responseObj)) {
|
|
9789
10270
|
return {
|
|
@@ -9794,7 +10275,7 @@ var init_request_utils = __esm({
|
|
|
9794
10275
|
}
|
|
9795
10276
|
return typeof responseObj === "string" ? responseObj : rawBody;
|
|
9796
10277
|
} catch (error) {
|
|
9797
|
-
|
|
10278
|
+
BStackLogger.error(`Orchestration request failed: ${error} - ${url3}`);
|
|
9798
10279
|
return null;
|
|
9799
10280
|
}
|
|
9800
10281
|
}
|
|
@@ -9803,7 +10284,7 @@ var init_request_utils = __esm({
|
|
|
9803
10284
|
});
|
|
9804
10285
|
|
|
9805
10286
|
// src/testorchestration/test-ordering-server.ts
|
|
9806
|
-
import
|
|
10287
|
+
import path20 from "node:path";
|
|
9807
10288
|
function isSplitTestsResponse(value) {
|
|
9808
10289
|
return typeof value === "object" && value !== null;
|
|
9809
10290
|
}
|
|
@@ -9837,7 +10318,7 @@ var init_test_ordering_server = __esm({
|
|
|
9837
10318
|
* Initiates the split tests request and stores the response data for polling.
|
|
9838
10319
|
*/
|
|
9839
10320
|
async splitTests(testFiles, orchestrationStrategy, orchestrationMetadata = "{}") {
|
|
9840
|
-
|
|
10321
|
+
BStackLogger.debug(`[splitTests] Initiating split tests with strategy: ${orchestrationStrategy}`);
|
|
9841
10322
|
try {
|
|
9842
10323
|
let prDetails = [];
|
|
9843
10324
|
const parsedMetadata = JSON.parse(orchestrationMetadata);
|
|
@@ -9847,7 +10328,7 @@ var init_test_ordering_server = __esm({
|
|
|
9847
10328
|
const multiRepoSource = parsedMetadata.run_smart_selection?.source;
|
|
9848
10329
|
prDetails = getGitMetadataForAISelection(multiRepoSource);
|
|
9849
10330
|
}
|
|
9850
|
-
|
|
10331
|
+
BStackLogger.debug(`PR Details for AI Selection: ${JSON.stringify(prDetails)}`);
|
|
9851
10332
|
const payload = {
|
|
9852
10333
|
tests: testFiles.map((f) => ({ filePath: f })),
|
|
9853
10334
|
orchestrationStrategy,
|
|
@@ -9855,23 +10336,23 @@ var init_test_ordering_server = __esm({
|
|
|
9855
10336
|
nodeIndex: parseInt(process.env.BROWSERSTACK_NODE_INDEX || "0"),
|
|
9856
10337
|
totalNodes: parseInt(process.env.BROWSERSTACK_TOTAL_NODE_COUNT || "1"),
|
|
9857
10338
|
projectName: this.config.testObservabilityOptions?.projectName || "",
|
|
9858
|
-
buildName: this.config.testObservabilityOptions?.buildName ||
|
|
10339
|
+
buildName: this.config.testObservabilityOptions?.buildName || path20.basename(process.cwd()),
|
|
9859
10340
|
buildRunIdentifier: process.env.BROWSERSTACK_BUILD_RUN_IDENTIFIER || "",
|
|
9860
10341
|
hostInfo: getHostInfo(),
|
|
9861
10342
|
prDetails
|
|
9862
10343
|
};
|
|
9863
|
-
|
|
10344
|
+
BStackLogger.info(`[splitTests] Split tests payload: ${JSON.stringify(payload)}`);
|
|
9864
10345
|
const response = await RequestUtils.testOrchestrationSplitTests(this.ORDERING_ENDPOINT, payload);
|
|
9865
10346
|
if (isSplitTestsResponse(response)) {
|
|
9866
10347
|
this.requestData = this._processSplitTestsResponse(response);
|
|
9867
|
-
|
|
10348
|
+
BStackLogger.debug(`[splitTests] Split tests response: ${JSON.stringify(this.requestData)}`);
|
|
9868
10349
|
} else if (response) {
|
|
9869
|
-
|
|
10350
|
+
BStackLogger.error("[splitTests] Received unexpected response format from split tests request.");
|
|
9870
10351
|
} else {
|
|
9871
|
-
|
|
10352
|
+
BStackLogger.error("[splitTests] Failed to get split tests response.");
|
|
9872
10353
|
}
|
|
9873
10354
|
} catch (error) {
|
|
9874
|
-
|
|
10355
|
+
BStackLogger.error(`[splitTests] Exception in sending test files:: ${error}`);
|
|
9875
10356
|
}
|
|
9876
10357
|
}
|
|
9877
10358
|
/**
|
|
@@ -9889,7 +10370,7 @@ var init_test_ordering_server = __esm({
|
|
|
9889
10370
|
const resultUrl = normalizeUrl(response.resultUrl);
|
|
9890
10371
|
const timeoutUrl = normalizeUrl(response.timeoutUrl);
|
|
9891
10372
|
if (response.timeout === void 0 || response.timeoutInterval === void 0 || response.timeoutUrl === void 0 || response.resultUrl === void 0) {
|
|
9892
|
-
|
|
10373
|
+
BStackLogger.debug("[process_split_tests_response] Received null value(s) for some attributes in split tests API response");
|
|
9893
10374
|
}
|
|
9894
10375
|
return {
|
|
9895
10376
|
timeout,
|
|
@@ -9903,7 +10384,7 @@ var init_test_ordering_server = __esm({
|
|
|
9903
10384
|
*/
|
|
9904
10385
|
async getOrderedTestFiles() {
|
|
9905
10386
|
if (!this.requestData) {
|
|
9906
|
-
|
|
10387
|
+
BStackLogger.error("[getOrderedTestFiles] No request data available to fetch ordered test files.");
|
|
9907
10388
|
return null;
|
|
9908
10389
|
}
|
|
9909
10390
|
let testFilesJsonList = null;
|
|
@@ -9927,10 +10408,10 @@ var init_test_ordering_server = __esm({
|
|
|
9927
10408
|
break;
|
|
9928
10409
|
}
|
|
9929
10410
|
await new Promise((resolve) => setTimeout(resolve, timeoutInterval * 1e3));
|
|
9930
|
-
|
|
10411
|
+
BStackLogger.debug(`[getOrderedTestFiles] Fetching ordered tests from result URL after waiting for ${timeoutInterval} seconds.`);
|
|
9931
10412
|
}
|
|
9932
10413
|
if (timeoutUrl && !testFilesJsonList) {
|
|
9933
|
-
|
|
10414
|
+
BStackLogger.debug("[getOrderedTestFiles] Fetching ordered tests from timeout URL");
|
|
9934
10415
|
const response = await RequestUtils.getTestOrchestrationOrderedTests(timeoutUrl);
|
|
9935
10416
|
if (isSplitTestsResponse(response) && Array.isArray(response.tests)) {
|
|
9936
10417
|
testFilesJsonList = response.tests;
|
|
@@ -9947,10 +10428,10 @@ var init_test_ordering_server = __esm({
|
|
|
9947
10428
|
if (!testFilesJsonList) {
|
|
9948
10429
|
return null;
|
|
9949
10430
|
}
|
|
9950
|
-
|
|
10431
|
+
BStackLogger.debug(`[getOrderedTestFiles] Ordered test files received: ${JSON.stringify(testFiles)}`);
|
|
9951
10432
|
return testFiles;
|
|
9952
10433
|
} catch (error) {
|
|
9953
|
-
|
|
10434
|
+
BStackLogger.error(`[getOrderedTestFiles] Exception in fetching ordered test files: ${error}`);
|
|
9954
10435
|
return null;
|
|
9955
10436
|
}
|
|
9956
10437
|
}
|
|
@@ -10042,10 +10523,10 @@ var init_testorcherstrationhandler = __esm({
|
|
|
10042
10523
|
return;
|
|
10043
10524
|
}
|
|
10044
10525
|
if (this.config.projectName === void 0 || this.config.buildName === void 0) {
|
|
10045
|
-
|
|
10526
|
+
BStackLogger.info("Test Reordering can't work as buildName or projectName is null. Please set a non-null value.");
|
|
10046
10527
|
}
|
|
10047
10528
|
if (!this._isObservabilityEnabled()) {
|
|
10048
|
-
|
|
10529
|
+
BStackLogger.info("Test Reordering can't work as testReporting is disabled. Please enable it from browserstack.yml file.");
|
|
10049
10530
|
}
|
|
10050
10531
|
}
|
|
10051
10532
|
/**
|
|
@@ -10054,7 +10535,7 @@ var init_testorcherstrationhandler = __esm({
|
|
|
10054
10535
|
async reorderTestFiles(testFiles) {
|
|
10055
10536
|
try {
|
|
10056
10537
|
if (!testFiles || testFiles.length === 0) {
|
|
10057
|
-
|
|
10538
|
+
BStackLogger.debug("[reorderTestFiles] No test files provided for ordering.");
|
|
10058
10539
|
return null;
|
|
10059
10540
|
}
|
|
10060
10541
|
let orchestrationStrategy = null;
|
|
@@ -10063,16 +10544,16 @@ var init_testorcherstrationhandler = __esm({
|
|
|
10063
10544
|
orchestrationStrategy = this.orchestrationUtils.getTestOrderingName();
|
|
10064
10545
|
}
|
|
10065
10546
|
if (orchestrationStrategy === null) {
|
|
10066
|
-
|
|
10547
|
+
BStackLogger.error("Orchestration strategy is None. Cannot proceed with test orchestration session.");
|
|
10067
10548
|
return null;
|
|
10068
10549
|
}
|
|
10069
|
-
|
|
10550
|
+
BStackLogger.info(`Reordering test files with orchestration strategy: ${orchestrationStrategy}`);
|
|
10070
10551
|
let orderedTestFiles = [];
|
|
10071
10552
|
if (BrowserstackCLI.getInstance().isRunning()) {
|
|
10072
|
-
|
|
10553
|
+
BStackLogger.info("Using CLI flow for test files orchestration.");
|
|
10073
10554
|
orderedTestFiles = await GrpcClient.getInstance().testOrchestrationSession(testFiles, orchestrationStrategy, JSON.stringify(orchestrationMetadata)) || [];
|
|
10074
10555
|
} else {
|
|
10075
|
-
|
|
10556
|
+
BStackLogger.info("Using SDK flow for test files orchestration.");
|
|
10076
10557
|
await this.testOrderingServerHandler.splitTests(testFiles, orchestrationStrategy, JSON.stringify(orchestrationMetadata));
|
|
10077
10558
|
orderedTestFiles = await this.testOrderingServerHandler.getOrderedTestFiles() || [];
|
|
10078
10559
|
}
|
|
@@ -10083,7 +10564,7 @@ var init_testorcherstrationhandler = __esm({
|
|
|
10083
10564
|
this.addToOrderingInstrumentationData("splitTestsAPICallCount", this.testOrderingServerHandler.getSplitTestsApiCallCount());
|
|
10084
10565
|
return orderedTestFiles;
|
|
10085
10566
|
} catch (error) {
|
|
10086
|
-
|
|
10567
|
+
BStackLogger.debug(`[reorderTestFiles] Error in ordering test classes: ${error}`);
|
|
10087
10568
|
}
|
|
10088
10569
|
return null;
|
|
10089
10570
|
}
|
|
@@ -10113,22 +10594,22 @@ import { performance as performance3 } from "node:perf_hooks";
|
|
|
10113
10594
|
async function applyOrchestrationIfEnabled(specs, config) {
|
|
10114
10595
|
const orchestrationHandler = TestOrchestrationHandler.getInstance(config);
|
|
10115
10596
|
if (!orchestrationHandler) {
|
|
10116
|
-
|
|
10597
|
+
BStackLogger.debug("Orchestration handler is not initialized. Skipping orchestration.");
|
|
10117
10598
|
return specs;
|
|
10118
10599
|
}
|
|
10119
10600
|
const runSmartSelectionEnabled = isValidEnabledValue(config?.testOrchestrationOptions?.runSmartSelection?.enabled);
|
|
10120
10601
|
if (!runSmartSelectionEnabled) {
|
|
10121
|
-
|
|
10602
|
+
BStackLogger.info("runSmartSelection is not enabled in config. Skipping orchestration.");
|
|
10122
10603
|
return specs;
|
|
10123
10604
|
}
|
|
10124
10605
|
orchestrationHandler.addToOrderingInstrumentationData("enabled", orchestrationHandler.testOrderingEnabled());
|
|
10125
10606
|
const startTime = performance3.now();
|
|
10126
|
-
|
|
10607
|
+
BStackLogger.info("Test orchestration is enabled. Attempting to reorder test files.");
|
|
10127
10608
|
const testFiles = specs;
|
|
10128
|
-
|
|
10609
|
+
BStackLogger.info(`Test files to be reordered: ${testFiles.join(", ")}`);
|
|
10129
10610
|
const orderedFiles = await orchestrationHandler.reorderTestFiles(testFiles);
|
|
10130
10611
|
if (orderedFiles && orderedFiles.length > 0) {
|
|
10131
|
-
|
|
10612
|
+
BStackLogger.info(`Tests reordered using orchestration: ${orderedFiles.join(", ")}`);
|
|
10132
10613
|
orchestrationHandler.addToOrderingInstrumentationData(
|
|
10133
10614
|
"timeTakenToApply",
|
|
10134
10615
|
Math.floor(performance3.now() - startTime)
|
|
@@ -10136,7 +10617,7 @@ async function applyOrchestrationIfEnabled(specs, config) {
|
|
|
10136
10617
|
);
|
|
10137
10618
|
return orderedFiles;
|
|
10138
10619
|
}
|
|
10139
|
-
|
|
10620
|
+
BStackLogger.info("No test files were reordered by orchestration.");
|
|
10140
10621
|
orchestrationHandler.addToOrderingInstrumentationData(
|
|
10141
10622
|
"timeTakenToApply",
|
|
10142
10623
|
Math.floor(performance3.now() - startTime)
|
|
@@ -10158,12 +10639,12 @@ var init_apply_orchestration = __esm({
|
|
|
10158
10639
|
// src/launcher.ts
|
|
10159
10640
|
init_utils();
|
|
10160
10641
|
init_testOpsConfig();
|
|
10161
|
-
import
|
|
10642
|
+
import fs15 from "node:fs";
|
|
10162
10643
|
import { readFile } from "node:fs/promises";
|
|
10163
|
-
import
|
|
10644
|
+
import path21 from "node:path";
|
|
10164
10645
|
import { promisify as promisify4, format as format4 } from "node:util";
|
|
10165
10646
|
import { performance as performance4, PerformanceObserver as PerformanceObserver2 } from "node:perf_hooks";
|
|
10166
|
-
import
|
|
10647
|
+
import os7 from "node:os";
|
|
10167
10648
|
import { SevereServiceError } from "webdriverio";
|
|
10168
10649
|
import * as BrowserstackLocalLauncher from "browserstack-local";
|
|
10169
10650
|
|
|
@@ -10173,9 +10654,9 @@ init_PercyLogger();
|
|
|
10173
10654
|
// src/Percy/Percy.ts
|
|
10174
10655
|
init_util();
|
|
10175
10656
|
init_PercyLogger();
|
|
10176
|
-
import
|
|
10177
|
-
import
|
|
10178
|
-
import
|
|
10657
|
+
import fs11 from "node:fs";
|
|
10658
|
+
import path10 from "node:path";
|
|
10659
|
+
import os4 from "node:os";
|
|
10179
10660
|
import { spawn as spawn2 } from "node:child_process";
|
|
10180
10661
|
|
|
10181
10662
|
// src/Percy/PercyBinary.ts
|
|
@@ -10185,20 +10666,20 @@ init_constants2();
|
|
|
10185
10666
|
init_bstackLogger();
|
|
10186
10667
|
init_fetchWrapper();
|
|
10187
10668
|
import yauzl2 from "yauzl";
|
|
10188
|
-
import
|
|
10669
|
+
import fs10 from "node:fs";
|
|
10189
10670
|
import fsp2 from "node:fs/promises";
|
|
10190
10671
|
import { pipeline } from "node:stream/promises";
|
|
10191
|
-
import
|
|
10192
|
-
import
|
|
10672
|
+
import path9 from "node:path";
|
|
10673
|
+
import os3 from "node:os";
|
|
10193
10674
|
import { spawn } from "node:child_process";
|
|
10194
10675
|
var PercyBinary = class {
|
|
10195
10676
|
#hostOS = process.platform;
|
|
10196
10677
|
#httpPath = null;
|
|
10197
10678
|
#binaryName = "percy";
|
|
10198
10679
|
#orderedPaths = [
|
|
10199
|
-
|
|
10680
|
+
path9.join(os3.homedir(), ".browserstack"),
|
|
10200
10681
|
process.cwd(),
|
|
10201
|
-
|
|
10682
|
+
os3.tmpdir()
|
|
10202
10683
|
];
|
|
10203
10684
|
constructor() {
|
|
10204
10685
|
const base = "https://github.com/percy/cli/releases/latest/download";
|
|
@@ -10211,15 +10692,15 @@ var PercyBinary = class {
|
|
|
10211
10692
|
this.#httpPath = base + "/percy-linux.zip";
|
|
10212
10693
|
}
|
|
10213
10694
|
}
|
|
10214
|
-
async #makePath(
|
|
10215
|
-
if (await this.#checkPath(
|
|
10695
|
+
async #makePath(path22) {
|
|
10696
|
+
if (await this.#checkPath(path22)) {
|
|
10216
10697
|
return true;
|
|
10217
10698
|
}
|
|
10218
|
-
return fsp2.mkdir(
|
|
10699
|
+
return fsp2.mkdir(path22).then(() => true).catch(() => false);
|
|
10219
10700
|
}
|
|
10220
|
-
async #checkPath(
|
|
10701
|
+
async #checkPath(path22) {
|
|
10221
10702
|
try {
|
|
10222
|
-
const hasDir = await fsp2.access(
|
|
10703
|
+
const hasDir = await fsp2.access(path22).then(() => true, () => false);
|
|
10223
10704
|
if (hasDir) {
|
|
10224
10705
|
return true;
|
|
10225
10706
|
}
|
|
@@ -10229,7 +10710,7 @@ var PercyBinary = class {
|
|
|
10229
10710
|
}
|
|
10230
10711
|
// Get the path for storing the ETag
|
|
10231
10712
|
#getETagPath(destParentDir) {
|
|
10232
|
-
return
|
|
10713
|
+
return path9.join(destParentDir, `${this.#binaryName}.etag`);
|
|
10233
10714
|
}
|
|
10234
10715
|
// Load the stored ETag if it exists
|
|
10235
10716
|
async #loadETag(destParentDir) {
|
|
@@ -10239,7 +10720,7 @@ var PercyBinary = class {
|
|
|
10239
10720
|
const data = await fsp2.readFile(etagPath, "utf8");
|
|
10240
10721
|
return data.trim();
|
|
10241
10722
|
} catch (err) {
|
|
10242
|
-
|
|
10723
|
+
BStackLogger.warn(`Failed to read ETag file ${err}`);
|
|
10243
10724
|
}
|
|
10244
10725
|
}
|
|
10245
10726
|
return null;
|
|
@@ -10252,23 +10733,23 @@ var PercyBinary = class {
|
|
|
10252
10733
|
try {
|
|
10253
10734
|
const etagPath = this.#getETagPath(destParentDir);
|
|
10254
10735
|
await fsp2.writeFile(etagPath, etag);
|
|
10255
|
-
|
|
10736
|
+
BStackLogger.debug("Saved new ETag for percy binary");
|
|
10256
10737
|
} catch (err) {
|
|
10257
|
-
|
|
10738
|
+
BStackLogger.error(`Failed to save ETag file ${err}`);
|
|
10258
10739
|
}
|
|
10259
10740
|
}
|
|
10260
10741
|
async #getAvailableDirs() {
|
|
10261
10742
|
for (let i = 0; i < this.#orderedPaths.length; i++) {
|
|
10262
|
-
const
|
|
10263
|
-
if (await this.#makePath(
|
|
10264
|
-
return
|
|
10743
|
+
const path22 = this.#orderedPaths[i];
|
|
10744
|
+
if (await this.#makePath(path22)) {
|
|
10745
|
+
return path22;
|
|
10265
10746
|
}
|
|
10266
10747
|
}
|
|
10267
10748
|
throw new Error("Error trying to download percy binary");
|
|
10268
10749
|
}
|
|
10269
10750
|
async getBinaryPath() {
|
|
10270
10751
|
const destParentDir = await this.#getAvailableDirs();
|
|
10271
|
-
const binaryPath =
|
|
10752
|
+
const binaryPath = path9.join(destParentDir, this.#binaryName);
|
|
10272
10753
|
let response;
|
|
10273
10754
|
if (await this.#checkPath(binaryPath)) {
|
|
10274
10755
|
const currentETag = await this.#loadETag(destParentDir);
|
|
@@ -10276,13 +10757,13 @@ var PercyBinary = class {
|
|
|
10276
10757
|
try {
|
|
10277
10758
|
const result = await this.#checkForUpdate(currentETag);
|
|
10278
10759
|
if (!result.needsUpdate) {
|
|
10279
|
-
|
|
10760
|
+
BStackLogger.debug("Percy binary is up to date (ETag unchanged)");
|
|
10280
10761
|
return binaryPath;
|
|
10281
10762
|
}
|
|
10282
10763
|
response = result.response;
|
|
10283
|
-
|
|
10764
|
+
BStackLogger.debug("New Percy binary version available, downloading update");
|
|
10284
10765
|
} catch (err) {
|
|
10285
|
-
|
|
10766
|
+
BStackLogger.warn(`Failed to check for binary updates, using existing binary ${err}`);
|
|
10286
10767
|
return binaryPath;
|
|
10287
10768
|
}
|
|
10288
10769
|
}
|
|
@@ -10310,11 +10791,11 @@ var PercyBinary = class {
|
|
|
10310
10791
|
}
|
|
10311
10792
|
const newETag = response.headers.get("eTag");
|
|
10312
10793
|
if (newETag) {
|
|
10313
|
-
await this.#saveETag(
|
|
10794
|
+
await this.#saveETag(path9.dirname(this.#getETagPath(await this.#getAvailableDirs())), newETag);
|
|
10314
10795
|
}
|
|
10315
10796
|
return { needsUpdate: true, response };
|
|
10316
10797
|
} catch (error) {
|
|
10317
|
-
|
|
10798
|
+
BStackLogger.warn(`Error checking for Percy binary updates: ${error}`);
|
|
10318
10799
|
throw error;
|
|
10319
10800
|
}
|
|
10320
10801
|
}
|
|
@@ -10337,9 +10818,9 @@ var PercyBinary = class {
|
|
|
10337
10818
|
await fsp2.mkdir(destParentDir);
|
|
10338
10819
|
}
|
|
10339
10820
|
const binaryName = this.#binaryName;
|
|
10340
|
-
const zipFilePath =
|
|
10341
|
-
const binaryPath =
|
|
10342
|
-
const downloadedFileStream =
|
|
10821
|
+
const zipFilePath = path9.join(destParentDir, binaryName + ".zip");
|
|
10822
|
+
const binaryPath = path9.join(destParentDir, binaryName);
|
|
10823
|
+
const downloadedFileStream = fs10.createWriteStream(zipFilePath);
|
|
10343
10824
|
if (!response) {
|
|
10344
10825
|
response = await _fetch(this.#httpPath);
|
|
10345
10826
|
}
|
|
@@ -10358,8 +10839,8 @@ var PercyBinary = class {
|
|
|
10358
10839
|
if (/\/$/.test(entry.fileName)) {
|
|
10359
10840
|
zipfile.readEntry();
|
|
10360
10841
|
} else {
|
|
10361
|
-
const writeStream =
|
|
10362
|
-
|
|
10842
|
+
const writeStream = fs10.createWriteStream(
|
|
10843
|
+
path9.join(destParentDir, entry.fileName)
|
|
10363
10844
|
);
|
|
10364
10845
|
zipfile.openReadStream(entry, function(zipErr, readStream) {
|
|
10365
10846
|
if (zipErr) {
|
|
@@ -10380,7 +10861,7 @@ var PercyBinary = class {
|
|
|
10380
10861
|
reject(zipErr);
|
|
10381
10862
|
});
|
|
10382
10863
|
zipfile.once("end", () => {
|
|
10383
|
-
|
|
10864
|
+
fs10.chmod(binaryPath, "0755", function(zipErr) {
|
|
10384
10865
|
if (zipErr) {
|
|
10385
10866
|
reject(zipErr);
|
|
10386
10867
|
}
|
|
@@ -10404,7 +10885,7 @@ init_constants2();
|
|
|
10404
10885
|
init_apiUtils();
|
|
10405
10886
|
var logDir = "logs";
|
|
10406
10887
|
var Percy = class {
|
|
10407
|
-
#logfile =
|
|
10888
|
+
#logfile = path10.join(logDir, "percy.log");
|
|
10408
10889
|
#address = process.env.PERCY_SERVER_ADDRESS || "http://127.0.0.1:5338";
|
|
10409
10890
|
#binaryPath = null;
|
|
10410
10891
|
#options;
|
|
@@ -10445,7 +10926,7 @@ var Percy = class {
|
|
|
10445
10926
|
}
|
|
10446
10927
|
async start() {
|
|
10447
10928
|
const binaryPath = await this.#getBinaryPath();
|
|
10448
|
-
const logStream =
|
|
10929
|
+
const logStream = fs11.createWriteStream(this.#logfile, { flags: "a" });
|
|
10449
10930
|
const token = await this.fetchPercyToken();
|
|
10450
10931
|
const configPath = await this.createPercyConfig();
|
|
10451
10932
|
if (!token) {
|
|
@@ -10532,13 +11013,13 @@ var Percy = class {
|
|
|
10532
11013
|
if (!this.#options.percyOptions) {
|
|
10533
11014
|
return null;
|
|
10534
11015
|
}
|
|
10535
|
-
const configPath =
|
|
11016
|
+
const configPath = path10.join(os4.tmpdir(), "percy.json");
|
|
10536
11017
|
const percyOptions = this.#options.percyOptions;
|
|
10537
11018
|
if (!percyOptions.version) {
|
|
10538
11019
|
percyOptions.version = "2";
|
|
10539
11020
|
}
|
|
10540
11021
|
return new Promise((resolve) => {
|
|
10541
|
-
|
|
11022
|
+
fs11.writeFile(
|
|
10542
11023
|
configPath,
|
|
10543
11024
|
JSON.stringify(
|
|
10544
11025
|
percyOptions
|
|
@@ -10632,6 +11113,7 @@ init_constants();
|
|
|
10632
11113
|
init_util();
|
|
10633
11114
|
init_crash_reporter();
|
|
10634
11115
|
init_bstackLogger();
|
|
11116
|
+
init_caCert();
|
|
10635
11117
|
init_PercyLogger();
|
|
10636
11118
|
|
|
10637
11119
|
// src/config.ts
|
|
@@ -10639,10 +11121,62 @@ init_testOpsConfig();
|
|
|
10639
11121
|
init_util();
|
|
10640
11122
|
init_bstackLogger();
|
|
10641
11123
|
import { v4 as uuidv4 } from "uuid";
|
|
11124
|
+
var APP_AUTOMATE_CAP_KEYS = ["appium:app", "appium:bundleId", "appium:appPackage", "appium:appActivity"];
|
|
11125
|
+
function hasAppCap(cap) {
|
|
11126
|
+
if (!cap || typeof cap !== "object") {
|
|
11127
|
+
return false;
|
|
11128
|
+
}
|
|
11129
|
+
const record = cap;
|
|
11130
|
+
if (APP_AUTOMATE_CAP_KEYS.some((key) => !isUndefined(record[key]))) {
|
|
11131
|
+
return true;
|
|
11132
|
+
}
|
|
11133
|
+
const appiumOptions = record["appium:options"];
|
|
11134
|
+
return !!(appiumOptions && !isUndefined(appiumOptions.app));
|
|
11135
|
+
}
|
|
11136
|
+
function detectAppAutomate(capabilities) {
|
|
11137
|
+
if (!capabilities) {
|
|
11138
|
+
return false;
|
|
11139
|
+
}
|
|
11140
|
+
const flat = [];
|
|
11141
|
+
if (Array.isArray(capabilities)) {
|
|
11142
|
+
for (const entry of capabilities) {
|
|
11143
|
+
if (!entry || typeof entry !== "object") {
|
|
11144
|
+
continue;
|
|
11145
|
+
}
|
|
11146
|
+
if ("alwaysMatch" in entry) {
|
|
11147
|
+
const w3c = entry;
|
|
11148
|
+
flat.push(w3c.alwaysMatch);
|
|
11149
|
+
if (Array.isArray(w3c.firstMatch)) {
|
|
11150
|
+
flat.push(...w3c.firstMatch);
|
|
11151
|
+
}
|
|
11152
|
+
continue;
|
|
11153
|
+
}
|
|
11154
|
+
const values = Object.values(entry);
|
|
11155
|
+
const isParallelMultiremote = values.length > 0 && values.every(
|
|
11156
|
+
(v) => v !== null && typeof v === "object" && v.capabilities
|
|
11157
|
+
);
|
|
11158
|
+
if (isParallelMultiremote) {
|
|
11159
|
+
for (const v of values) {
|
|
11160
|
+
flat.push(v.capabilities);
|
|
11161
|
+
}
|
|
11162
|
+
} else {
|
|
11163
|
+
flat.push(entry);
|
|
11164
|
+
}
|
|
11165
|
+
}
|
|
11166
|
+
} else {
|
|
11167
|
+
for (const v of Object.values(capabilities)) {
|
|
11168
|
+
const inner = v.capabilities;
|
|
11169
|
+
if (inner) {
|
|
11170
|
+
flat.push(inner);
|
|
11171
|
+
}
|
|
11172
|
+
}
|
|
11173
|
+
}
|
|
11174
|
+
return flat.some(hasAppCap);
|
|
11175
|
+
}
|
|
10642
11176
|
var BrowserStackConfig = class _BrowserStackConfig {
|
|
10643
|
-
static getInstance(options, config) {
|
|
11177
|
+
static getInstance(options, config, capabilities, isBrowserStackInfra) {
|
|
10644
11178
|
if (!this._instance && options && config) {
|
|
10645
|
-
this._instance = new _BrowserStackConfig(options, config);
|
|
11179
|
+
this._instance = new _BrowserStackConfig(options, config, capabilities, isBrowserStackInfra);
|
|
10646
11180
|
}
|
|
10647
11181
|
return this._instance;
|
|
10648
11182
|
}
|
|
@@ -10663,19 +11197,19 @@ var BrowserStackConfig = class _BrowserStackConfig {
|
|
|
10663
11197
|
percyBuildId;
|
|
10664
11198
|
isPercyAutoEnabled = false;
|
|
10665
11199
|
sdkRunID;
|
|
10666
|
-
constructor(options, config) {
|
|
11200
|
+
constructor(options, config, capabilities, isBrowserStackInfra = true) {
|
|
10667
11201
|
this.framework = config.framework;
|
|
10668
11202
|
this.userName = config.user;
|
|
10669
11203
|
this.accessKey = config.key;
|
|
10670
11204
|
this.testObservability = new testOpsConfig_default(options.testObservability !== false, !isUndefined(options.testObservability));
|
|
10671
11205
|
this.percy = options.percy || false;
|
|
10672
|
-
this.accessibility = options.accessibility
|
|
11206
|
+
this.accessibility = options.accessibility;
|
|
10673
11207
|
this.app = options.app;
|
|
10674
|
-
this.appAutomate = !isUndefined(options.app);
|
|
10675
|
-
this.automate = !this.appAutomate;
|
|
11208
|
+
this.appAutomate = isBrowserStackInfra && (!isUndefined(options.app) || detectAppAutomate(capabilities));
|
|
11209
|
+
this.automate = isBrowserStackInfra && !this.appAutomate;
|
|
10676
11210
|
this.buildIdentifier = options.buildIdentifier;
|
|
10677
11211
|
this.sdkRunID = uuidv4();
|
|
10678
|
-
|
|
11212
|
+
BStackLogger.info(`BrowserStack service started with id: ${this.sdkRunID}`);
|
|
10679
11213
|
}
|
|
10680
11214
|
sentFunnelData() {
|
|
10681
11215
|
this.funnelDataSent = true;
|
|
@@ -10685,32 +11219,32 @@ var config_default = BrowserStackConfig;
|
|
|
10685
11219
|
|
|
10686
11220
|
// src/exitHandler.ts
|
|
10687
11221
|
import { spawn as spawn4 } from "node:child_process";
|
|
10688
|
-
import
|
|
11222
|
+
import path17 from "node:path";
|
|
10689
11223
|
|
|
10690
11224
|
// src/instrumentation/funnelInstrumentation.ts
|
|
10691
11225
|
init_usageStats();
|
|
10692
11226
|
init_bstackLogger();
|
|
10693
11227
|
init_constants();
|
|
10694
|
-
import
|
|
11228
|
+
import os5 from "node:os";
|
|
10695
11229
|
import util4, { format as format2 } from "node:util";
|
|
10696
|
-
import
|
|
10697
|
-
import
|
|
11230
|
+
import path12 from "node:path";
|
|
11231
|
+
import fs13 from "node:fs";
|
|
10698
11232
|
|
|
10699
11233
|
// src/data-store.ts
|
|
10700
11234
|
init_bstackLogger();
|
|
10701
|
-
import
|
|
10702
|
-
import
|
|
10703
|
-
var workersDataDirPath =
|
|
11235
|
+
import path11 from "node:path";
|
|
11236
|
+
import fs12 from "node:fs";
|
|
11237
|
+
var workersDataDirPath = path11.join(process.cwd(), "logs", "worker_data");
|
|
10704
11238
|
function getDataFromWorkers() {
|
|
10705
11239
|
const workersData = [];
|
|
10706
|
-
if (!
|
|
11240
|
+
if (!fs12.existsSync(workersDataDirPath)) {
|
|
10707
11241
|
return workersData;
|
|
10708
11242
|
}
|
|
10709
|
-
const files =
|
|
11243
|
+
const files = fs12.readdirSync(workersDataDirPath);
|
|
10710
11244
|
files.forEach((file) => {
|
|
10711
|
-
|
|
10712
|
-
const filePath =
|
|
10713
|
-
const fileContent =
|
|
11245
|
+
BStackLogger.debug("Reading worker file " + file);
|
|
11246
|
+
const filePath = path11.join(workersDataDirPath, file);
|
|
11247
|
+
const fileContent = fs12.readFileSync(filePath, "utf8");
|
|
10714
11248
|
const workerData = JSON.parse(fileContent);
|
|
10715
11249
|
workersData.push(workerData);
|
|
10716
11250
|
});
|
|
@@ -10718,21 +11252,21 @@ function getDataFromWorkers() {
|
|
|
10718
11252
|
return workersData;
|
|
10719
11253
|
}
|
|
10720
11254
|
function saveWorkerData(data) {
|
|
10721
|
-
const filePath =
|
|
11255
|
+
const filePath = path11.join(workersDataDirPath, "worker-data-" + process.pid + ".json");
|
|
10722
11256
|
try {
|
|
10723
11257
|
createWorkersDataDir();
|
|
10724
|
-
|
|
11258
|
+
fs12.writeFileSync(filePath, JSON.stringify(data));
|
|
10725
11259
|
} catch (e) {
|
|
10726
|
-
|
|
11260
|
+
BStackLogger.debug("Exception in saving worker data: " + e);
|
|
10727
11261
|
}
|
|
10728
11262
|
}
|
|
10729
11263
|
function removeWorkersDataDir() {
|
|
10730
|
-
|
|
11264
|
+
fs12.rmSync(workersDataDirPath, { recursive: true, force: true });
|
|
10731
11265
|
return true;
|
|
10732
11266
|
}
|
|
10733
11267
|
function createWorkersDataDir() {
|
|
10734
|
-
if (!
|
|
10735
|
-
|
|
11268
|
+
if (!fs12.existsSync(workersDataDirPath)) {
|
|
11269
|
+
fs12.mkdirSync(workersDataDirPath, { recursive: true });
|
|
10736
11270
|
}
|
|
10737
11271
|
return true;
|
|
10738
11272
|
}
|
|
@@ -10746,16 +11280,16 @@ init_performance_tester();
|
|
|
10746
11280
|
init_constants2();
|
|
10747
11281
|
async function fireFunnelTestEvent(eventType, config, isCLIEnabled = false) {
|
|
10748
11282
|
if (!config.userName || !config.accessKey) {
|
|
10749
|
-
|
|
11283
|
+
BStackLogger.debug("username/accesskey not passed");
|
|
10750
11284
|
return;
|
|
10751
11285
|
}
|
|
10752
11286
|
try {
|
|
10753
11287
|
const data = buildEventData(eventType, config, isCLIEnabled);
|
|
10754
11288
|
await fireFunnelRequest(data);
|
|
10755
|
-
|
|
11289
|
+
BStackLogger.debug("Funnel event success");
|
|
10756
11290
|
config.sentFunnelData();
|
|
10757
11291
|
} catch (error) {
|
|
10758
|
-
|
|
11292
|
+
BStackLogger.debug(`Exception in sending funnel data: ${format2(error)}`);
|
|
10759
11293
|
}
|
|
10760
11294
|
}
|
|
10761
11295
|
async function sendStart(config) {
|
|
@@ -10780,9 +11314,9 @@ async function sendFinish(config, isCLIEnabled = false) {
|
|
|
10780
11314
|
}
|
|
10781
11315
|
function saveFunnelData(eventType, config, isCLIEnabled = false) {
|
|
10782
11316
|
const data = buildEventData(eventType, config, isCLIEnabled);
|
|
10783
|
-
|
|
10784
|
-
const filePath =
|
|
10785
|
-
|
|
11317
|
+
BStackLogger.ensureLogsFolder();
|
|
11318
|
+
const filePath = path12.join(BStackLogger.logFolderPath, "funnelData.json");
|
|
11319
|
+
fs13.writeFileSync(filePath, JSON.stringify(data));
|
|
10786
11320
|
return filePath;
|
|
10787
11321
|
}
|
|
10788
11322
|
function redactCredentialsFromFunnelData(data) {
|
|
@@ -10799,7 +11333,7 @@ function redactCredentialsFromFunnelData(data) {
|
|
|
10799
11333
|
async function fireFunnelRequest(data) {
|
|
10800
11334
|
const { userName, accessKey } = data;
|
|
10801
11335
|
redactCredentialsFromFunnelData(data);
|
|
10802
|
-
|
|
11336
|
+
BStackLogger.debug("Sending SDK event with data " + util4.inspect(data, { depth: 6 }));
|
|
10803
11337
|
const encodedAuth = Buffer.from(`${userName}:${accessKey}`, "utf8").toString("base64");
|
|
10804
11338
|
const response = await fetchWrap(APIUtils.FUNNEL_INSTRUMENTATION_URL, {
|
|
10805
11339
|
method: "POST",
|
|
@@ -10809,7 +11343,7 @@ async function fireFunnelRequest(data) {
|
|
|
10809
11343
|
},
|
|
10810
11344
|
body: JSON.stringify(data)
|
|
10811
11345
|
});
|
|
10812
|
-
|
|
11346
|
+
BStackLogger.debug("Funnel Event Response: " + JSON.stringify(await response.text()));
|
|
10813
11347
|
}
|
|
10814
11348
|
function getProductList(config) {
|
|
10815
11349
|
const products = [];
|
|
@@ -10843,8 +11377,8 @@ function buildEventData(eventType, config, isCLIEnabled = false) {
|
|
|
10843
11377
|
buildName: config.buildName || "undefined",
|
|
10844
11378
|
buildIdentifier: String(config.buildIdentifier),
|
|
10845
11379
|
// Host details
|
|
10846
|
-
os:
|
|
10847
|
-
hostname:
|
|
11380
|
+
os: os5.type() || "unknown",
|
|
11381
|
+
hostname: os5.hostname() || "unknown",
|
|
10848
11382
|
// Product Details
|
|
10849
11383
|
productMap: getProductMap(config),
|
|
10850
11384
|
product: getProductList(config),
|
|
@@ -10894,30 +11428,30 @@ function isProxyError(authResult) {
|
|
|
10894
11428
|
function handleProxyError(config, isSelfHealEnabled) {
|
|
10895
11429
|
sendEvent.tcgProxyFailure(config);
|
|
10896
11430
|
if (isSelfHealEnabled) {
|
|
10897
|
-
|
|
11431
|
+
BStackLogger.warn("Proxy Error. Disabling Healing for this session.");
|
|
10898
11432
|
}
|
|
10899
11433
|
}
|
|
10900
11434
|
function handleUpgradeRequired(isSelfHealEnabled) {
|
|
10901
11435
|
if (isSelfHealEnabled) {
|
|
10902
|
-
|
|
11436
|
+
BStackLogger.warn("Please upgrade Browserstack Service to the latest version to use the self-healing feature.");
|
|
10903
11437
|
}
|
|
10904
11438
|
}
|
|
10905
11439
|
function handleAuthenticationFailure(status, config, isSelfHealEnabled) {
|
|
10906
11440
|
if (status >= 500) {
|
|
10907
11441
|
if (isSelfHealEnabled) {
|
|
10908
|
-
|
|
11442
|
+
BStackLogger.warn("Something went wrong. Disabling healing for this session. Please try again later.");
|
|
10909
11443
|
}
|
|
10910
11444
|
sendEvent.tcgDown(config);
|
|
10911
11445
|
} else {
|
|
10912
11446
|
if (isSelfHealEnabled) {
|
|
10913
|
-
|
|
11447
|
+
BStackLogger.warn("Authentication Failed. Disabling Healing for this session.");
|
|
10914
11448
|
}
|
|
10915
11449
|
sendEvent.tcgAuthFailure(config);
|
|
10916
11450
|
}
|
|
10917
11451
|
}
|
|
10918
11452
|
function handleAuthenticationSuccess(isHealingEnabledForUser, userId, config, isSelfHealEnabled) {
|
|
10919
11453
|
if (!isHealingEnabledForUser && isSelfHealEnabled) {
|
|
10920
|
-
|
|
11454
|
+
BStackLogger.warn("Healing is not enabled for your group, please contact the admin");
|
|
10921
11455
|
} else if (userId && isHealingEnabledForUser) {
|
|
10922
11456
|
sendEvent.tcgtInitSuccessful(config);
|
|
10923
11457
|
}
|
|
@@ -10929,7 +11463,7 @@ function handleInitializationFailure(status, config, isSelfHealEnabled) {
|
|
|
10929
11463
|
sendEvent.invalidTcgAuth(config);
|
|
10930
11464
|
}
|
|
10931
11465
|
if (isSelfHealEnabled) {
|
|
10932
|
-
|
|
11466
|
+
BStackLogger.warn("Authentication Failed. Healing will be disabled for this session.");
|
|
10933
11467
|
}
|
|
10934
11468
|
}
|
|
10935
11469
|
function handleHealingInstrumentation(authResult, config, isSelfHealEnabled) {
|
|
@@ -10956,7 +11490,7 @@ function handleHealingInstrumentation(authResult, config, isSelfHealEnabled) {
|
|
|
10956
11490
|
return;
|
|
10957
11491
|
}
|
|
10958
11492
|
} catch (err) {
|
|
10959
|
-
|
|
11493
|
+
BStackLogger.debug("Error in handling healing instrumentation: " + err);
|
|
10960
11494
|
}
|
|
10961
11495
|
}
|
|
10962
11496
|
|
|
@@ -10968,36 +11502,36 @@ init_bstackLogger();
|
|
|
10968
11502
|
init_cli();
|
|
10969
11503
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
10970
11504
|
var __filename = fileURLToPath2(import.meta.url);
|
|
10971
|
-
var __dirname =
|
|
11505
|
+
var __dirname = path17.dirname(__filename);
|
|
10972
11506
|
function setupExitHandlers() {
|
|
10973
11507
|
const handleCLICleanup = () => {
|
|
10974
|
-
|
|
11508
|
+
BStackLogger.debug("Handling CLI cleanup in exit handler");
|
|
10975
11509
|
try {
|
|
10976
11510
|
const cliProcess = BrowserstackCLI.getInstance()?.process;
|
|
10977
11511
|
if (cliProcess && cliProcess.pid && cliProcess.exitCode === null) {
|
|
10978
|
-
|
|
11512
|
+
BStackLogger.debug(`Found CLI process with PID ${cliProcess.pid}, terminating`);
|
|
10979
11513
|
try {
|
|
10980
11514
|
if (process.platform === "win32") {
|
|
10981
11515
|
cliProcess.kill("SIGTERM");
|
|
10982
|
-
|
|
11516
|
+
BStackLogger.debug("CLI process terminated successfully with SIGTERM (Windows)");
|
|
10983
11517
|
} else {
|
|
10984
11518
|
cliProcess.kill("SIGINT");
|
|
10985
|
-
|
|
11519
|
+
BStackLogger.debug("CLI process terminated successfully with SIGINT (Unix)");
|
|
10986
11520
|
}
|
|
10987
11521
|
} catch (processError2) {
|
|
10988
|
-
|
|
11522
|
+
BStackLogger.debug(`CLI process termination error: ${processError2}`);
|
|
10989
11523
|
try {
|
|
10990
11524
|
cliProcess.kill();
|
|
10991
|
-
|
|
11525
|
+
BStackLogger.debug("CLI process terminated with default signal (fallback)");
|
|
10992
11526
|
} catch (fallbackError) {
|
|
10993
|
-
|
|
11527
|
+
BStackLogger.debug(`CLI process fallback termination error: ${fallbackError}`);
|
|
10994
11528
|
}
|
|
10995
11529
|
}
|
|
10996
11530
|
} else {
|
|
10997
|
-
|
|
11531
|
+
BStackLogger.debug("No CLI process found to terminate");
|
|
10998
11532
|
}
|
|
10999
11533
|
} catch (error) {
|
|
11000
|
-
|
|
11534
|
+
BStackLogger.debug(`Error in CLI cleanup: ${error}`);
|
|
11001
11535
|
}
|
|
11002
11536
|
};
|
|
11003
11537
|
process.on("exit", () => {
|
|
@@ -11005,8 +11539,8 @@ function setupExitHandlers() {
|
|
|
11005
11539
|
handleCLICleanup();
|
|
11006
11540
|
const args = shouldCallCleanup(config_default.getInstance(), isCLIEnabled);
|
|
11007
11541
|
if (Array.isArray(args) && args.length) {
|
|
11008
|
-
|
|
11009
|
-
const childProcess = spawn4("node", [`${
|
|
11542
|
+
BStackLogger.debug(`Spawning cleanup.js with args: ${args.join(", ")}`);
|
|
11543
|
+
const childProcess = spawn4("node", [`${path17.join(__dirname, "cleanup.js")}`, ...args], { detached: true, stdio: "inherit", env: { ...process.env } });
|
|
11010
11544
|
childProcess.unref();
|
|
11011
11545
|
}
|
|
11012
11546
|
});
|
|
@@ -11032,8 +11566,8 @@ function shouldCallCleanup(config, isCLIEnabled = false) {
|
|
|
11032
11566
|
// src/ai-handler.ts
|
|
11033
11567
|
init_bstackLogger();
|
|
11034
11568
|
init_constants();
|
|
11035
|
-
import
|
|
11036
|
-
import
|
|
11569
|
+
import path18 from "node:path";
|
|
11570
|
+
import fs14 from "node:fs";
|
|
11037
11571
|
import url2 from "node:url";
|
|
11038
11572
|
import aiSDK from "@browserstack/ai-sdk-node";
|
|
11039
11573
|
init_util();
|
|
@@ -11060,7 +11594,7 @@ var AiHandler = class {
|
|
|
11060
11594
|
}
|
|
11061
11595
|
} else if (options.selfHeal === true) {
|
|
11062
11596
|
const healingWarnMessage = authResult.message;
|
|
11063
|
-
|
|
11597
|
+
BStackLogger.warn(`Healing Auth failed. Disabling healing for this session. Reason: ${healingWarnMessage}`);
|
|
11064
11598
|
}
|
|
11065
11599
|
return caps;
|
|
11066
11600
|
}
|
|
@@ -11069,8 +11603,8 @@ var AiHandler = class {
|
|
|
11069
11603
|
}
|
|
11070
11604
|
async installFirefoxExtension(browser) {
|
|
11071
11605
|
const __dirname2 = url2.fileURLToPath(new URL(".", import.meta.url));
|
|
11072
|
-
const extensionPath =
|
|
11073
|
-
const extFile =
|
|
11606
|
+
const extensionPath = path18.resolve(__dirname2, aiSDK.BrowserstackHealing.getFirefoxAddonPath());
|
|
11607
|
+
const extFile = fs14.readFileSync(extensionPath);
|
|
11074
11608
|
await browser.installAddOn(extFile.toString("base64"), true);
|
|
11075
11609
|
}
|
|
11076
11610
|
async handleHealing(orginalFunc, using, value, browser, options) {
|
|
@@ -11097,7 +11631,7 @@ var AiHandler = class {
|
|
|
11097
11631
|
return result;
|
|
11098
11632
|
}
|
|
11099
11633
|
if (options.selfHeal === true && this.authResult.isHealingEnabled) {
|
|
11100
|
-
|
|
11634
|
+
BStackLogger.info("findElement failed, trying to heal");
|
|
11101
11635
|
PerformanceTester.start(AI_EVENTS.SELF_HEAL_STEP);
|
|
11102
11636
|
const script = await aiSDK.BrowserstackHealing.healFailure(locatorType, locatorValue, void 0, void 0, this.authResult.userId, this.authResult.groupId, sessionId, void 0, void 0, this.authResult.isGroupAIEnabled, tcgDetails);
|
|
11103
11637
|
if (script) {
|
|
@@ -11107,7 +11641,7 @@ var AiHandler = class {
|
|
|
11107
11641
|
PerformanceTester.end(AI_EVENTS.SELF_HEAL_GET_RESULT);
|
|
11108
11642
|
if (tcgData && tcgData.selector && tcgData.value) {
|
|
11109
11643
|
const healedResult = await orginalFunc(tcgData.selector, tcgData.value);
|
|
11110
|
-
|
|
11644
|
+
BStackLogger.info("Healing worked, element found: " + tcgData.selector + ": " + tcgData.value);
|
|
11111
11645
|
PerformanceTester.end(AI_EVENTS.SELF_HEAL_STEP);
|
|
11112
11646
|
return healedResult.error ? result : healedResult;
|
|
11113
11647
|
}
|
|
@@ -11119,9 +11653,9 @@ var AiHandler = class {
|
|
|
11119
11653
|
} catch (err) {
|
|
11120
11654
|
PerformanceTester.end(AI_EVENTS.SELF_HEAL_STEP, false, String(err));
|
|
11121
11655
|
if (options.selfHeal === true) {
|
|
11122
|
-
|
|
11656
|
+
BStackLogger.warn("Something went wrong while healing. Disabling healing for this command");
|
|
11123
11657
|
} else {
|
|
11124
|
-
|
|
11658
|
+
BStackLogger.warn("Error in findElement: " + err + "using: " + using + "value: " + value);
|
|
11125
11659
|
}
|
|
11126
11660
|
}
|
|
11127
11661
|
return await orginalFunc(using, value);
|
|
@@ -11157,7 +11691,7 @@ var AiHandler = class {
|
|
|
11157
11691
|
}
|
|
11158
11692
|
} catch (err) {
|
|
11159
11693
|
if (options.selfHeal === true) {
|
|
11160
|
-
|
|
11694
|
+
BStackLogger.warn(`Error while initiliazing Browserstack healing Extension ${err}`);
|
|
11161
11695
|
}
|
|
11162
11696
|
}
|
|
11163
11697
|
return caps;
|
|
@@ -11166,7 +11700,7 @@ var AiHandler = class {
|
|
|
11166
11700
|
if (SUPPORTED_BROWSERS_FOR_AI.includes(browser.capabilities?.browserName?.toLowerCase())) {
|
|
11167
11701
|
const authInfo = this.authResult;
|
|
11168
11702
|
if (Object.keys(authInfo).length === 0 && options.selfHeal === true) {
|
|
11169
|
-
|
|
11703
|
+
BStackLogger.debug("TCG Auth result is empty");
|
|
11170
11704
|
return;
|
|
11171
11705
|
}
|
|
11172
11706
|
const { isAuthenticated, sessionToken, defaultLogDataEnabled } = authInfo;
|
|
@@ -11194,7 +11728,7 @@ var AiHandler = class {
|
|
|
11194
11728
|
}
|
|
11195
11729
|
} catch (err) {
|
|
11196
11730
|
if (options.selfHeal === true) {
|
|
11197
|
-
|
|
11731
|
+
BStackLogger.warn(`Error while setting up self-healing: ${err}. Disabling healing for this session.`);
|
|
11198
11732
|
}
|
|
11199
11733
|
}
|
|
11200
11734
|
}
|
|
@@ -11212,9 +11746,10 @@ var BrowserstackLauncherService = class {
|
|
|
11212
11746
|
constructor(_options, capabilities, _config) {
|
|
11213
11747
|
this._options = _options;
|
|
11214
11748
|
this._config = _config;
|
|
11215
|
-
|
|
11749
|
+
BStackLogger.clearLogFile();
|
|
11216
11750
|
PercyLogger.clearLogFile();
|
|
11217
11751
|
setupExitHandlers();
|
|
11752
|
+
configureCaCertificate(this._options);
|
|
11218
11753
|
if (!this._config) {
|
|
11219
11754
|
this._config = _options;
|
|
11220
11755
|
}
|
|
@@ -11236,12 +11771,13 @@ var BrowserstackLauncherService = class {
|
|
|
11236
11771
|
if (!isUndefined(process.env.TEST_REPORTING_BUILD_TAG)) {
|
|
11237
11772
|
process.env.TEST_OBSERVABILITY_BUILD_TAG = process.env.TEST_REPORTING_BUILD_TAG;
|
|
11238
11773
|
}
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11774
|
+
const isBrowserStackInfra = isBrowserstackInfra(_config, capabilities);
|
|
11775
|
+
this.browserStackConfig = config_default.getInstance(_options, _config, capabilities, isBrowserStackInfra);
|
|
11776
|
+
BStackLogger.debug(`_options data: ${JSON.stringify(_options)}`);
|
|
11777
|
+
BStackLogger.debug(`webdriver capabilities data: ${JSON.stringify(capabilities)}`);
|
|
11242
11778
|
const configCopy = JSON.parse(JSON.stringify(_config));
|
|
11243
11779
|
CrashReporter.recursivelyRedactKeysFromObject(configCopy, ["user", "username", "key", "accesskey", "password"]);
|
|
11244
|
-
|
|
11780
|
+
BStackLogger.debug(`_config data: ${JSON.stringify(configCopy)}`);
|
|
11245
11781
|
if (Array.isArray(capabilities)) {
|
|
11246
11782
|
capabilities.flatMap((c) => {
|
|
11247
11783
|
if ("alwaysMatch" in c) {
|
|
@@ -11328,7 +11864,7 @@ var BrowserstackLauncherService = class {
|
|
|
11328
11864
|
try {
|
|
11329
11865
|
CrashReporter.setConfigDetails(this._config, capabilities, this._options);
|
|
11330
11866
|
} catch (error) {
|
|
11331
|
-
|
|
11867
|
+
BStackLogger.error(`[Crash_Report_Upload] Config processing failed due to ${error}`);
|
|
11332
11868
|
}
|
|
11333
11869
|
}
|
|
11334
11870
|
browserstackLocal;
|
|
@@ -11336,7 +11872,7 @@ var BrowserstackLauncherService = class {
|
|
|
11336
11872
|
_projectName;
|
|
11337
11873
|
_buildTag;
|
|
11338
11874
|
_buildIdentifier;
|
|
11339
|
-
_accessibilityAutomation
|
|
11875
|
+
_accessibilityAutomation;
|
|
11340
11876
|
_percy;
|
|
11341
11877
|
_percyBestPlatformCaps;
|
|
11342
11878
|
browserStackConfig;
|
|
@@ -11358,7 +11894,7 @@ var BrowserstackLauncherService = class {
|
|
|
11358
11894
|
if (config.specs && Array.isArray(config.specs) && isValidEnabledValue(this._options.testOrchestrationOptions?.runSmartSelection?.enabled)) {
|
|
11359
11895
|
try {
|
|
11360
11896
|
const glob = (await import("glob")).sync;
|
|
11361
|
-
const
|
|
11897
|
+
const path22 = await import("node:path");
|
|
11362
11898
|
const expandedSpecs = [];
|
|
11363
11899
|
for (const specPattern of config.specs) {
|
|
11364
11900
|
if (typeof specPattern === "string") {
|
|
@@ -11375,21 +11911,21 @@ var BrowserstackLauncherService = class {
|
|
|
11375
11911
|
}) || [];
|
|
11376
11912
|
filenames.forEach((filename) => {
|
|
11377
11913
|
let absolutePath = filename;
|
|
11378
|
-
if (!
|
|
11379
|
-
absolutePath =
|
|
11914
|
+
if (!path22.isAbsolute(filename)) {
|
|
11915
|
+
absolutePath = path22.resolve(rootDir, filename);
|
|
11380
11916
|
}
|
|
11381
|
-
let relativePath =
|
|
11917
|
+
let relativePath = path22.relative(cwd, absolutePath);
|
|
11382
11918
|
relativePath = relativePath.replace(/\\/g, "/");
|
|
11383
11919
|
expandedSpecs.push(relativePath);
|
|
11384
11920
|
});
|
|
11385
11921
|
}
|
|
11386
11922
|
}
|
|
11387
11923
|
if (expandedSpecs.length > 0) {
|
|
11388
|
-
|
|
11924
|
+
BStackLogger.info(`Expanded specs from glob patterns to ${expandedSpecs.length} files`);
|
|
11389
11925
|
config.specs = expandedSpecs;
|
|
11390
11926
|
}
|
|
11391
11927
|
} catch (error) {
|
|
11392
|
-
|
|
11928
|
+
BStackLogger.error(`Failed to expand spec patterns: ${error}`);
|
|
11393
11929
|
}
|
|
11394
11930
|
}
|
|
11395
11931
|
try {
|
|
@@ -11400,11 +11936,11 @@ var BrowserstackLauncherService = class {
|
|
|
11400
11936
|
CLIUtils.setFrameworkDetail(WDIO_NAMING_PREFIX + config.framework, "WebdriverIO");
|
|
11401
11937
|
const binconfig = CLIUtils.getBinConfig(config, capabilities, this._options, this._buildTag);
|
|
11402
11938
|
await BrowserstackCLI.getInstance().bootstrap(this._options, config, binconfig);
|
|
11403
|
-
|
|
11939
|
+
BStackLogger.debug(`Is CLI running ${BrowserstackCLI.getInstance().isRunning()}`);
|
|
11404
11940
|
PerformanceTester.end(FRAMEWORK_EVENTS.START);
|
|
11405
11941
|
}
|
|
11406
11942
|
} catch (err) {
|
|
11407
|
-
|
|
11943
|
+
BStackLogger.error(`Error while starting CLI ${err}`);
|
|
11408
11944
|
PerformanceTester.end(FRAMEWORK_EVENTS.START, false, format4(err));
|
|
11409
11945
|
}
|
|
11410
11946
|
PerformanceTester.end(FRAMEWORK_EVENTS.INIT);
|
|
@@ -11423,13 +11959,13 @@ var BrowserstackLauncherService = class {
|
|
|
11423
11959
|
}
|
|
11424
11960
|
} catch (err) {
|
|
11425
11961
|
if (this._options.selfHeal === true) {
|
|
11426
|
-
|
|
11962
|
+
BStackLogger.warn(`Error while setting up Browserstack healing Extension ${err}. Disabling healing for this session.`);
|
|
11427
11963
|
}
|
|
11428
11964
|
}
|
|
11429
11965
|
}
|
|
11430
11966
|
if (!BrowserstackCLI.getInstance().isRunning()) {
|
|
11431
11967
|
if (!this._options.app) {
|
|
11432
|
-
|
|
11968
|
+
BStackLogger.debug("app is not defined in browserstack-service config, skipping ...");
|
|
11433
11969
|
} else {
|
|
11434
11970
|
let app = {};
|
|
11435
11971
|
const appConfig = this._options.app;
|
|
@@ -11438,10 +11974,10 @@ var BrowserstackLauncherService = class {
|
|
|
11438
11974
|
} catch (error) {
|
|
11439
11975
|
throw new SevereServiceError(error.message);
|
|
11440
11976
|
}
|
|
11441
|
-
if (VALID_APP_EXTENSION.includes(
|
|
11442
|
-
if (
|
|
11977
|
+
if (VALID_APP_EXTENSION.includes(path21.extname(app.app))) {
|
|
11978
|
+
if (fs15.existsSync(app.app)) {
|
|
11443
11979
|
const data = await this._uploadApp(app);
|
|
11444
|
-
|
|
11980
|
+
BStackLogger.info(`app upload completed: ${JSON.stringify(data)}`);
|
|
11445
11981
|
app.app = data.app_url;
|
|
11446
11982
|
} else if (app.customId) {
|
|
11447
11983
|
app.app = app.customId;
|
|
@@ -11449,7 +11985,7 @@ var BrowserstackLauncherService = class {
|
|
|
11449
11985
|
throw new SevereServiceError(`[Invalid app path] app path ${app.app} is not correct, Provide correct path to app under test`);
|
|
11450
11986
|
}
|
|
11451
11987
|
}
|
|
11452
|
-
|
|
11988
|
+
BStackLogger.info(`Using app: ${app.app}`);
|
|
11453
11989
|
this._updateCaps(capabilities, "app", app.app);
|
|
11454
11990
|
}
|
|
11455
11991
|
}
|
|
@@ -11462,7 +11998,7 @@ var BrowserstackLauncherService = class {
|
|
|
11462
11998
|
const shouldSetupPercy = this._options.percy || isUndefined(this._options.percy) && this._options.app;
|
|
11463
11999
|
let buildStartResponse = null;
|
|
11464
12000
|
if (!BrowserstackCLI.getInstance().isRunning() && (this._options.testObservability || this._accessibilityAutomation || shouldSetupPercy)) {
|
|
11465
|
-
|
|
12001
|
+
BStackLogger.debug("Sending launch start event");
|
|
11466
12002
|
buildStartResponse = await launchTestSession(this._options, this._config, {
|
|
11467
12003
|
projectName: this._projectName,
|
|
11468
12004
|
buildName: this._buildName,
|
|
@@ -11476,7 +12012,7 @@ var BrowserstackLauncherService = class {
|
|
|
11476
12012
|
this._updateObjectTypeCaps(capabilities, "goog:chromeOptions", overrideOptions);
|
|
11477
12013
|
}
|
|
11478
12014
|
if (buildStartResponse?.accessibility) {
|
|
11479
|
-
if (this._accessibilityAutomation
|
|
12015
|
+
if (isUndefined(this._accessibilityAutomation)) {
|
|
11480
12016
|
this.browserStackConfig.accessibility = buildStartResponse.accessibility.success;
|
|
11481
12017
|
this._accessibilityAutomation = buildStartResponse.accessibility.success;
|
|
11482
12018
|
this._options.accessibility = buildStartResponse.accessibility.success;
|
|
@@ -11497,6 +12033,7 @@ var BrowserstackLauncherService = class {
|
|
|
11497
12033
|
} else if (isAccessibilityAutomationSession(this._accessibilityAutomation)) {
|
|
11498
12034
|
this._updateObjectTypeCaps(capabilities, "accessibilityOptions", {});
|
|
11499
12035
|
}
|
|
12036
|
+
this._removeCliOnlyCapabilityOptions(capabilities);
|
|
11500
12037
|
if (shouldSetupPercy) {
|
|
11501
12038
|
try {
|
|
11502
12039
|
const bestPlatformPercyCaps = getBestPlatformForPercySnapshot(capabilities);
|
|
@@ -11520,15 +12057,15 @@ var BrowserstackLauncherService = class {
|
|
|
11520
12057
|
if (typeof spec !== "string") {
|
|
11521
12058
|
return spec;
|
|
11522
12059
|
}
|
|
11523
|
-
const absolutePath =
|
|
11524
|
-
const relativePath =
|
|
12060
|
+
const absolutePath = path21.isAbsolute(spec) ? spec : path21.resolve(cwd, spec);
|
|
12061
|
+
const relativePath = path21.relative(rootDir, absolutePath);
|
|
11525
12062
|
return relativePath.replace(/\\/g, "/");
|
|
11526
12063
|
});
|
|
11527
12064
|
};
|
|
11528
12065
|
try {
|
|
11529
12066
|
const { applyOrchestrationIfEnabled: applyOrchestrationIfEnabled2 } = await Promise.resolve().then(() => (init_apply_orchestration(), apply_orchestration_exports));
|
|
11530
12067
|
if (config.specs && config.specs.length > 0 && this._options.testObservability && isValidEnabledValue(this._options.testOrchestrationOptions?.runSmartSelection?.enabled)) {
|
|
11531
|
-
|
|
12068
|
+
BStackLogger.info("Applying test orchestration");
|
|
11532
12069
|
const specs = config.specs.filter((spec) => typeof spec === "string");
|
|
11533
12070
|
console.log(`Specs before orchestration: ${specs}`);
|
|
11534
12071
|
const orderedSpecs = await applyOrchestrationIfEnabled2(specs, this._options);
|
|
@@ -11536,14 +12073,14 @@ var BrowserstackLauncherService = class {
|
|
|
11536
12073
|
const specsToConvert = orderedSpecs && orderedSpecs.length > 0 ? orderedSpecs : specs;
|
|
11537
12074
|
config.specs = convertToRootDirRelative(specsToConvert);
|
|
11538
12075
|
console.log(`Specs after orchestration: ${config.specs}`);
|
|
11539
|
-
|
|
12076
|
+
BStackLogger.info("Test specs updated with orchestrated order");
|
|
11540
12077
|
}
|
|
11541
12078
|
} catch (error) {
|
|
11542
|
-
|
|
12079
|
+
BStackLogger.error(`Error applying test orchestration: ${error}`);
|
|
11543
12080
|
if (config.specs && config.specs.length > 0) {
|
|
11544
12081
|
const specs = config.specs.filter((spec) => typeof spec === "string");
|
|
11545
12082
|
config.specs = convertToRootDirRelative(specs);
|
|
11546
|
-
|
|
12083
|
+
BStackLogger.debug(`Specs converted back to rootDir-relative after error: ${config.specs}`);
|
|
11547
12084
|
}
|
|
11548
12085
|
}
|
|
11549
12086
|
}
|
|
@@ -11551,7 +12088,7 @@ var BrowserstackLauncherService = class {
|
|
|
11551
12088
|
return;
|
|
11552
12089
|
}
|
|
11553
12090
|
if (!this._options.browserstackLocal) {
|
|
11554
|
-
return
|
|
12091
|
+
return BStackLogger.info("browserstackLocal is not enabled - skipping...");
|
|
11555
12092
|
}
|
|
11556
12093
|
const opts = {
|
|
11557
12094
|
key: this._config.key,
|
|
@@ -11564,7 +12101,7 @@ var BrowserstackLauncherService = class {
|
|
|
11564
12101
|
}
|
|
11565
12102
|
const obs = new PerformanceObserver2((list) => {
|
|
11566
12103
|
const entry = list.getEntries()[0];
|
|
11567
|
-
|
|
12104
|
+
BStackLogger.info(`Browserstack Local successfully started after ${entry.duration}ms`);
|
|
11568
12105
|
});
|
|
11569
12106
|
obs.observe({ entryTypes: ["measure"] });
|
|
11570
12107
|
let timer;
|
|
@@ -11597,13 +12134,13 @@ var BrowserstackLauncherService = class {
|
|
|
11597
12134
|
PerformanceTester.start(FRAMEWORK_EVENTS.STOP);
|
|
11598
12135
|
try {
|
|
11599
12136
|
const isCLIEnabled = BrowserstackCLI.getInstance().isRunning();
|
|
11600
|
-
|
|
11601
|
-
|
|
12137
|
+
BStackLogger.debug("Inside OnComplete hook..");
|
|
12138
|
+
BStackLogger.debug("Sending stop launch event");
|
|
11602
12139
|
try {
|
|
11603
12140
|
await (isCLIEnabled ? BrowserstackCLI.getInstance().stop() : stopBuildUpstream());
|
|
11604
12141
|
PerformanceTester.end(FRAMEWORK_EVENTS.STOP);
|
|
11605
12142
|
} catch (err) {
|
|
11606
|
-
|
|
12143
|
+
BStackLogger.error(`Error while stopping CLI ${err}`);
|
|
11607
12144
|
PerformanceTester.end(FRAMEWORK_EVENTS.STOP, false, format4(err));
|
|
11608
12145
|
}
|
|
11609
12146
|
if (process.env[BROWSERSTACK_OBSERVABILITY] && process.env[BROWSERSTACK_TESTHUB_UUID]) {
|
|
@@ -11619,16 +12156,16 @@ Visit https://automation.browserstack.com/builds/${process.env[BROWSERSTACK_TEST
|
|
|
11619
12156
|
return;
|
|
11620
12157
|
}
|
|
11621
12158
|
const duration = (/* @__PURE__ */ new Date()).getTime() - new Date(process.env.START_TIME).getTime();
|
|
11622
|
-
|
|
12159
|
+
BStackLogger.info(`Total duration is ${duration / 1e3} s`);
|
|
11623
12160
|
}
|
|
11624
|
-
|
|
12161
|
+
BStackLogger.info(`BrowserStack service run ended for id: ${this.browserStackConfig?.sdkRunID} testhub id: ${testOpsConfig_default.getInstance()?.buildHashedId}`);
|
|
11625
12162
|
await sendFinish(this.browserStackConfig, isCLIEnabled);
|
|
11626
12163
|
try {
|
|
11627
12164
|
PerformanceTester.start(EVENTS.SDK_SEND_LOGS);
|
|
11628
12165
|
await this._uploadServiceLogs();
|
|
11629
12166
|
PerformanceTester.end(EVENTS.SDK_SEND_LOGS);
|
|
11630
12167
|
} catch (error) {
|
|
11631
|
-
|
|
12168
|
+
BStackLogger.debug(`Failed to upload BrowserStack WDIO Service logs ${error}`);
|
|
11632
12169
|
PerformanceTester.end(EVENTS.SDK_SEND_LOGS, false, format4(error));
|
|
11633
12170
|
}
|
|
11634
12171
|
PerformanceTester.end(EVENTS.SDK_ON_STOP);
|
|
@@ -11638,9 +12175,9 @@ Visit https://automation.browserstack.com/builds/${process.env[BROWSERSTACK_TEST
|
|
|
11638
12175
|
PerformanceTester.end(EVENTS.SDK_ON_STOP, false, format4(error));
|
|
11639
12176
|
PerformanceTester.end(EVENTS.SDK_CLEANUP, false, format4(error));
|
|
11640
12177
|
await PerformanceTester.stopAndGenerate("performance-launcher.html");
|
|
11641
|
-
|
|
12178
|
+
BStackLogger.error(`Error in onComplete hook: ${error}`);
|
|
11642
12179
|
}
|
|
11643
|
-
|
|
12180
|
+
BStackLogger.clearLogger();
|
|
11644
12181
|
if (this._options.percy) {
|
|
11645
12182
|
await this.stopPercy();
|
|
11646
12183
|
PercyLogger.clearLogger();
|
|
@@ -11724,10 +12261,10 @@ Visit https://automation.browserstack.com/builds/${process.env[BROWSERSTACK_TEST
|
|
|
11724
12261
|
}
|
|
11725
12262
|
}
|
|
11726
12263
|
async _uploadApp(app) {
|
|
11727
|
-
|
|
12264
|
+
BStackLogger.info(`uploading app ${app.app} ${app.customId ? `and custom_id: ${app.customId}` : ""} to browserstack`);
|
|
11728
12265
|
const form = new FormData();
|
|
11729
12266
|
if (app.app) {
|
|
11730
|
-
const fileName =
|
|
12267
|
+
const fileName = path21.basename(app.app);
|
|
11731
12268
|
const fileBuffer = await readFile(app.app);
|
|
11732
12269
|
const fileBlob = new Blob([new Uint8Array(fileBuffer)]);
|
|
11733
12270
|
form.append("file", fileBlob, fileName);
|
|
@@ -11774,13 +12311,40 @@ Visit https://automation.browserstack.com/builds/${process.env[BROWSERSTACK_TEST
|
|
|
11774
12311
|
}
|
|
11775
12312
|
async _uploadServiceLogs() {
|
|
11776
12313
|
const clientBuildUuid = this._getClientBuildUuid();
|
|
11777
|
-
await
|
|
11778
|
-
|
|
11779
|
-
|
|
11780
|
-
|
|
11781
|
-
|
|
12314
|
+
const response = await uploadLogs(getBrowserStackUser(this._config), getBrowserStackKey(this._config), clientBuildUuid);
|
|
12315
|
+
if (response) {
|
|
12316
|
+
BStackLogger.info(`Upload response: ${JSON.stringify(response, null, 2)}`);
|
|
12317
|
+
BStackLogger.logToFile(`Response - ${format4(response)}`, "debug");
|
|
12318
|
+
}
|
|
12319
|
+
}
|
|
12320
|
+
_removeCliOnlyCapabilityOptions(capabilities) {
|
|
12321
|
+
if (!capabilities || typeof capabilities !== "object") {
|
|
12322
|
+
return;
|
|
12323
|
+
}
|
|
12324
|
+
const strip = (capability) => {
|
|
12325
|
+
const capabilityRecord = capability;
|
|
12326
|
+
const bstackOptions = capabilityRecord["bstack:options"];
|
|
12327
|
+
if (bstackOptions && typeof bstackOptions === "object") {
|
|
12328
|
+
NOT_ALLOWED_KEYS_IN_CAPS.forEach((key) => delete bstackOptions[key]);
|
|
11782
12329
|
}
|
|
11783
|
-
|
|
12330
|
+
NOT_ALLOWED_KEYS_IN_CAPS.forEach((key) => delete capabilityRecord[`browserstack.${key}`]);
|
|
12331
|
+
const alwaysMatch = capability.alwaysMatch;
|
|
12332
|
+
if (alwaysMatch && typeof alwaysMatch === "object") {
|
|
12333
|
+
strip(alwaysMatch);
|
|
12334
|
+
}
|
|
12335
|
+
};
|
|
12336
|
+
if (Array.isArray(capabilities)) {
|
|
12337
|
+
capabilities.flatMap((c) => {
|
|
12338
|
+
if (Object.values(c).length > 0 && Object.values(c).every((c2) => typeof c2 === "object" && c2.capabilities)) {
|
|
12339
|
+
return Object.values(c).map((o) => o.capabilities);
|
|
12340
|
+
}
|
|
12341
|
+
return c;
|
|
12342
|
+
}).forEach(strip);
|
|
12343
|
+
} else {
|
|
12344
|
+
Object.entries(capabilities).forEach(([, caps]) => {
|
|
12345
|
+
strip(caps.capabilities);
|
|
12346
|
+
});
|
|
12347
|
+
}
|
|
11784
12348
|
}
|
|
11785
12349
|
_updateObjectTypeCaps(capabilities, capType, value) {
|
|
11786
12350
|
try {
|
|
@@ -11883,7 +12447,7 @@ Visit https://automation.browserstack.com/builds/${process.env[BROWSERSTACK_TEST
|
|
|
11883
12447
|
});
|
|
11884
12448
|
}
|
|
11885
12449
|
} catch (error) {
|
|
11886
|
-
|
|
12450
|
+
BStackLogger.debug(`Exception while retrieving capability value. Error - ${error}`);
|
|
11887
12451
|
}
|
|
11888
12452
|
}
|
|
11889
12453
|
_updateCaps(capabilities, capType, value) {
|
|
@@ -12019,7 +12583,7 @@ Visit https://automation.browserstack.com/builds/${process.env[BROWSERSTACK_TEST
|
|
|
12019
12583
|
}
|
|
12020
12584
|
if ((!this._buildName || process.env.BROWSERSTACK_BUILD_NAME) && this._buildIdentifier) {
|
|
12021
12585
|
this._updateCaps(capabilities, "buildIdentifier");
|
|
12022
|
-
|
|
12586
|
+
BStackLogger.warn("Skipping buildIdentifier as buildName is not passed.");
|
|
12023
12587
|
return;
|
|
12024
12588
|
}
|
|
12025
12589
|
if (this._buildIdentifier && this._buildIdentifier.includes("${DATE_TIME}")) {
|
|
@@ -12062,16 +12626,16 @@ Visit https://automation.browserstack.com/builds/${process.env[BROWSERSTACK_TEST
|
|
|
12062
12626
|
* else returns corresponding value in json file (e.g. { "wdio-build": { "identifier" : 2 } } => 2 in this case)
|
|
12063
12627
|
*/
|
|
12064
12628
|
_getLocalBuildNumber() {
|
|
12065
|
-
const browserstackFolderPath =
|
|
12629
|
+
const browserstackFolderPath = path21.join(os7.homedir(), ".browserstack");
|
|
12066
12630
|
try {
|
|
12067
|
-
if (!
|
|
12068
|
-
|
|
12631
|
+
if (!fs15.existsSync(browserstackFolderPath)) {
|
|
12632
|
+
fs15.mkdirSync(browserstackFolderPath);
|
|
12069
12633
|
}
|
|
12070
|
-
const filePath =
|
|
12071
|
-
if (!
|
|
12072
|
-
|
|
12634
|
+
const filePath = path21.join(browserstackFolderPath, ".build-name-cache.json");
|
|
12635
|
+
if (!fs15.existsSync(filePath)) {
|
|
12636
|
+
fs15.appendFileSync(filePath, JSON.stringify({}));
|
|
12073
12637
|
}
|
|
12074
|
-
const buildCacheFileData =
|
|
12638
|
+
const buildCacheFileData = fs15.readFileSync(filePath);
|
|
12075
12639
|
const parsedBuildCacheFileData = JSON.parse(buildCacheFileData.toString());
|
|
12076
12640
|
if (this._buildName && this._buildName in parsedBuildCacheFileData) {
|
|
12077
12641
|
const prevIdentifier = parseInt(parsedBuildCacheFileData[this._buildName].identifier);
|
|
@@ -12090,16 +12654,16 @@ Visit https://automation.browserstack.com/builds/${process.env[BROWSERSTACK_TEST
|
|
|
12090
12654
|
if (!buildName || !filePath) {
|
|
12091
12655
|
return;
|
|
12092
12656
|
}
|
|
12093
|
-
const jsonContent = JSON.parse(
|
|
12657
|
+
const jsonContent = JSON.parse(fs15.readFileSync(filePath).toString());
|
|
12094
12658
|
jsonContent[buildName] = { "identifier": buildIdentifier };
|
|
12095
|
-
|
|
12659
|
+
fs15.writeFileSync(filePath, JSON.stringify(jsonContent));
|
|
12096
12660
|
}
|
|
12097
12661
|
_getClientBuildUuid() {
|
|
12098
12662
|
if (process.env[BROWSERSTACK_TESTHUB_UUID]) {
|
|
12099
12663
|
return process.env[BROWSERSTACK_TESTHUB_UUID];
|
|
12100
12664
|
}
|
|
12101
12665
|
const uuid = this.browserStackConfig?.sdkRunID;
|
|
12102
|
-
|
|
12666
|
+
BStackLogger.logToFile(`If facing any issues, please contact BrowserStack support with the Build Run Id - ${uuid}`, "info");
|
|
12103
12667
|
return uuid;
|
|
12104
12668
|
}
|
|
12105
12669
|
};
|
|
@@ -12121,6 +12685,7 @@ init_util();
|
|
|
12121
12685
|
init_insights_handler();
|
|
12122
12686
|
init_reporter();
|
|
12123
12687
|
init_constants();
|
|
12688
|
+
init_caCert();
|
|
12124
12689
|
init_crash_reporter();
|
|
12125
12690
|
|
|
12126
12691
|
// src/accessibility-handler.ts
|
|
@@ -12244,7 +12809,7 @@ var _AccessibilityHandler = class {
|
|
|
12244
12809
|
};
|
|
12245
12810
|
browserWithA11y.startA11yScanning = async () => {
|
|
12246
12811
|
if (this._testIdentifier === null) {
|
|
12247
|
-
|
|
12812
|
+
BStackLogger.warn("Accessibility scanning cannot be started from outside the test");
|
|
12248
12813
|
return;
|
|
12249
12814
|
}
|
|
12250
12815
|
AccessibilityHandler._a11yScanSessionMap[sessionId] = true;
|
|
@@ -12256,7 +12821,7 @@ var _AccessibilityHandler = class {
|
|
|
12256
12821
|
};
|
|
12257
12822
|
browserWithA11y.stopA11yScanning = async () => {
|
|
12258
12823
|
if (this._testIdentifier === null) {
|
|
12259
|
-
|
|
12824
|
+
BStackLogger.warn("Accessibility scanning cannot be stopped from outside the test");
|
|
12260
12825
|
return;
|
|
12261
12826
|
}
|
|
12262
12827
|
AccessibilityHandler._a11yScanSessionMap[sessionId] = false;
|
|
@@ -12275,7 +12840,7 @@ var _AccessibilityHandler = class {
|
|
|
12275
12840
|
const prevImpl = orig ? orig.bind(browser) : void 0;
|
|
12276
12841
|
browser.overwriteCommand(command.name, this.commandWrapper.bind(this, command, prevImpl), command.class === "Element");
|
|
12277
12842
|
} catch (error) {
|
|
12278
|
-
|
|
12843
|
+
BStackLogger.debug(`Exception in overwrite command ${command.name} - ${error}`);
|
|
12279
12844
|
}
|
|
12280
12845
|
});
|
|
12281
12846
|
PerformanceTester.end(CONFIG_EVENTS.ACCESSIBILITY);
|
|
@@ -12299,14 +12864,14 @@ var _AccessibilityHandler = class {
|
|
|
12299
12864
|
};
|
|
12300
12865
|
this._testMetadata[testIdentifier].accessibilityScanStarted = shouldScanTest;
|
|
12301
12866
|
if (shouldScanTest) {
|
|
12302
|
-
|
|
12867
|
+
BStackLogger.info("Automate test case execution has started.");
|
|
12303
12868
|
}
|
|
12304
12869
|
} catch (error) {
|
|
12305
|
-
|
|
12870
|
+
BStackLogger.error(`Exception in starting accessibility automation scan for this test case ${error}`);
|
|
12306
12871
|
}
|
|
12307
12872
|
}
|
|
12308
12873
|
async afterTest(suiteTitle, test) {
|
|
12309
|
-
|
|
12874
|
+
BStackLogger.debug("Accessibility after test hook. Before sending test stop event");
|
|
12310
12875
|
if (this._framework !== "mocha" || !this.shouldRunTestHooks(this._browser, this._accessibility)) {
|
|
12311
12876
|
return;
|
|
12312
12877
|
}
|
|
@@ -12318,17 +12883,17 @@ var _AccessibilityHandler = class {
|
|
|
12318
12883
|
return;
|
|
12319
12884
|
}
|
|
12320
12885
|
if (shouldScanTestForAccessibility2) {
|
|
12321
|
-
|
|
12886
|
+
BStackLogger.info("Automate test case execution has ended. Processing for accessibility testing is underway. ");
|
|
12322
12887
|
const dataForExtension = {
|
|
12323
12888
|
"thTestRunUuid": process.env.TEST_ANALYTICS_ID,
|
|
12324
12889
|
"thBuildUuid": process.env.BROWSERSTACK_TESTHUB_UUID,
|
|
12325
12890
|
"thJwtToken": process.env.BROWSERSTACK_TESTHUB_JWT
|
|
12326
12891
|
};
|
|
12327
12892
|
await this.sendTestStopEvent(this._browser, dataForExtension);
|
|
12328
|
-
|
|
12893
|
+
BStackLogger.info("Accessibility testing for this test case has ended.");
|
|
12329
12894
|
}
|
|
12330
12895
|
} catch (error) {
|
|
12331
|
-
|
|
12896
|
+
BStackLogger.error(`Accessibility results could not be processed for the test case ${test.title}. Error : ${error}`);
|
|
12332
12897
|
}
|
|
12333
12898
|
}
|
|
12334
12899
|
/**
|
|
@@ -12356,14 +12921,14 @@ var _AccessibilityHandler = class {
|
|
|
12356
12921
|
}
|
|
12357
12922
|
listener_default.setTestRunAccessibilityVar(this._accessibility && shouldScanScenario);
|
|
12358
12923
|
if (shouldScanScenario) {
|
|
12359
|
-
|
|
12924
|
+
BStackLogger.info("Automate test case execution has started.");
|
|
12360
12925
|
}
|
|
12361
12926
|
} catch (error) {
|
|
12362
|
-
|
|
12927
|
+
BStackLogger.error(`Exception in starting accessibility automation scan for this test case ${error}`);
|
|
12363
12928
|
}
|
|
12364
12929
|
}
|
|
12365
12930
|
async afterScenario(world) {
|
|
12366
|
-
|
|
12931
|
+
BStackLogger.debug("Accessibility after scenario hook. Before sending test stop event");
|
|
12367
12932
|
if (!this.shouldRunTestHooks(this._browser, this._accessibility)) {
|
|
12368
12933
|
return;
|
|
12369
12934
|
}
|
|
@@ -12376,17 +12941,17 @@ var _AccessibilityHandler = class {
|
|
|
12376
12941
|
return;
|
|
12377
12942
|
}
|
|
12378
12943
|
if (shouldScanTestForAccessibility2) {
|
|
12379
|
-
|
|
12944
|
+
BStackLogger.info("Automate test case execution has ended. Processing for accessibility testing is underway. ");
|
|
12380
12945
|
const dataForExtension = {
|
|
12381
12946
|
"thTestRunUuid": process.env.TEST_ANALYTICS_ID,
|
|
12382
12947
|
"thBuildUuid": process.env.BROWSERSTACK_TESTHUB_UUID,
|
|
12383
12948
|
"thJwtToken": process.env.BROWSERSTACK_TESTHUB_JWT
|
|
12384
12949
|
};
|
|
12385
12950
|
await this.sendTestStopEvent(this._browser, dataForExtension);
|
|
12386
|
-
|
|
12951
|
+
BStackLogger.info("Accessibility testing for this test case has ended.");
|
|
12387
12952
|
}
|
|
12388
12953
|
} catch (error) {
|
|
12389
|
-
|
|
12954
|
+
BStackLogger.error(`Accessibility results could not be processed for the test case ${pickleData.name}. Error : ${error}`);
|
|
12390
12955
|
}
|
|
12391
12956
|
}
|
|
12392
12957
|
/*
|
|
@@ -12394,14 +12959,14 @@ var _AccessibilityHandler = class {
|
|
|
12394
12959
|
*/
|
|
12395
12960
|
async commandWrapper(command, prevImpl, origFunction, ...args) {
|
|
12396
12961
|
if (this._sessionId && AccessibilityHandler._a11yScanSessionMap[this._sessionId] && (!command.name.includes("execute") || !AccessibilityHandler.shouldPatchExecuteScript(args.length ? args[0] : null))) {
|
|
12397
|
-
|
|
12962
|
+
BStackLogger.debug(`Performing scan for ${command.class} ${command.name}`);
|
|
12398
12963
|
await performA11yScan(this.isAppAutomate, this._browser, true, true, command.name);
|
|
12399
12964
|
}
|
|
12400
12965
|
const impl = prevImpl || origFunction;
|
|
12401
12966
|
return impl(...args);
|
|
12402
12967
|
}
|
|
12403
12968
|
async sendTestStopEvent(browser, dataForExtension) {
|
|
12404
|
-
|
|
12969
|
+
BStackLogger.debug("Performing scan before saving results");
|
|
12405
12970
|
if (AccessibilityHandler._a11yScanSessionMap[this._sessionId]) {
|
|
12406
12971
|
await PerformanceTester.measureWrapper(A11Y_EVENTS.PERFORM_SCAN, async () => {
|
|
12407
12972
|
await performA11yScan(this.isAppAutomate, browser, true, true);
|
|
@@ -12413,9 +12978,9 @@ var _AccessibilityHandler = class {
|
|
|
12413
12978
|
await PerformanceTester.measureWrapper(A11Y_EVENTS.SAVE_RESULTS, async () => {
|
|
12414
12979
|
if (accessibility_scripts_default.saveTestResults) {
|
|
12415
12980
|
const results = await executeAccessibilityScript(browser, accessibility_scripts_default.saveTestResults, dataForExtension);
|
|
12416
|
-
|
|
12981
|
+
BStackLogger.debug(util11.format(results));
|
|
12417
12982
|
} else {
|
|
12418
|
-
|
|
12983
|
+
BStackLogger.error("saveTestResults script is null or undefined");
|
|
12419
12984
|
}
|
|
12420
12985
|
})();
|
|
12421
12986
|
}
|
|
@@ -12492,6 +13057,7 @@ var BrowserstackService = class {
|
|
|
12492
13057
|
this._caps = _caps;
|
|
12493
13058
|
this._config = _config;
|
|
12494
13059
|
this._options = { ...DEFAULT_OPTIONS, ...options };
|
|
13060
|
+
configureCaCertificate(this._options);
|
|
12495
13061
|
if (!this._config) {
|
|
12496
13062
|
this._config = this._options;
|
|
12497
13063
|
}
|
|
@@ -12571,24 +13137,33 @@ var BrowserstackService = class {
|
|
|
12571
13137
|
PerformanceTester.end(EVENTS.SDK_FIND_NEAREST_HUB, false, "Hub URL not found");
|
|
12572
13138
|
}
|
|
12573
13139
|
}
|
|
12574
|
-
|
|
12575
|
-
|
|
12576
|
-
|
|
12577
|
-
|
|
12578
|
-
|
|
13140
|
+
try {
|
|
13141
|
+
if (BrowserstackCLI.getInstance().isRunning()) {
|
|
13142
|
+
await BrowserstackCLI.getInstance().getAutomationFramework().trackEvent(AutomationFrameworkState.CREATE, HookState.PRE, { caps: capabilities });
|
|
13143
|
+
const instance = AutomationFramework.getTrackedInstance();
|
|
13144
|
+
const caps = AutomationFramework.getState(instance, AutomationFrameworkConstants.KEY_CAPABILITIES);
|
|
13145
|
+
Object.assign(capabilities, caps);
|
|
13146
|
+
const bstackOptions = capabilities["bstack:options"];
|
|
13147
|
+
if (bstackOptions && typeof bstackOptions === "object") {
|
|
13148
|
+
NOT_ALLOWED_KEYS_IN_CAPS.forEach((key) => delete bstackOptions[key]);
|
|
13149
|
+
}
|
|
13150
|
+
NOT_ALLOWED_KEYS_IN_CAPS.forEach((key) => delete capabilities[`browserstack.${key}`]);
|
|
13151
|
+
}
|
|
13152
|
+
} catch (err) {
|
|
13153
|
+
BStackLogger.error(`Error while tracking automation framework event: ${err}`);
|
|
12579
13154
|
}
|
|
12580
13155
|
} catch (err) {
|
|
12581
|
-
|
|
13156
|
+
BStackLogger.error(`Error while connecting to Browserstack CLI: ${err}`);
|
|
12582
13157
|
PerformanceTester.end(DRIVER_EVENT.INIT, false, util12.format(err));
|
|
12583
13158
|
throw err;
|
|
12584
13159
|
}
|
|
12585
13160
|
PerformanceTester.end(DRIVER_EVENT.INIT);
|
|
12586
13161
|
PerformanceTester.start(EVENTS.SDK_DEVICE_ALLOCATION);
|
|
12587
|
-
|
|
13162
|
+
BStackLogger.debug("Device allocation tracking started - waiting for WebDriverIO to create remote session");
|
|
12588
13163
|
}
|
|
12589
13164
|
async before(caps, specs, browser) {
|
|
12590
13165
|
PerformanceTester.end(EVENTS.SDK_DEVICE_ALLOCATION, true, "Device allocated and session created");
|
|
12591
|
-
|
|
13166
|
+
BStackLogger.debug("Device allocation tracking ended - remote session created successfully");
|
|
12592
13167
|
PerformanceTester.start(DRIVER_EVENT.PRE_INITIALIZE);
|
|
12593
13168
|
this._browser = browser ? browser : globalThis.browser;
|
|
12594
13169
|
PerformanceTester.browser = this._browser;
|
|
@@ -12597,7 +13172,7 @@ var BrowserstackService = class {
|
|
|
12597
13172
|
await ai_handler_default.selfHeal(this._options, caps, this._browser);
|
|
12598
13173
|
} catch (err) {
|
|
12599
13174
|
if (this._options.selfHeal === true) {
|
|
12600
|
-
|
|
13175
|
+
BStackLogger.warn(`Error while setting up self-healing: ${err}. Disabling healing for this session.`);
|
|
12601
13176
|
}
|
|
12602
13177
|
}
|
|
12603
13178
|
}
|
|
@@ -12627,13 +13202,13 @@ var BrowserstackService = class {
|
|
|
12627
13202
|
this._options.accessibilityOptions
|
|
12628
13203
|
);
|
|
12629
13204
|
if (isBrowserstackSession(this._browser) && BrowserstackCLI.getInstance().isRunning()) {
|
|
12630
|
-
|
|
13205
|
+
BStackLogger.info(`CLI is running, tracking accessibility event for before: ${sessionId}`);
|
|
12631
13206
|
} else {
|
|
12632
13207
|
await this._accessibilityHandler.before(sessionId);
|
|
12633
13208
|
}
|
|
12634
13209
|
listener_default.setAccessibilityOptions(this._options.accessibilityOptions);
|
|
12635
13210
|
} catch (err) {
|
|
12636
|
-
|
|
13211
|
+
BStackLogger.error(`[Accessibility Test Run] Error in service class before function: ${err}`);
|
|
12637
13212
|
}
|
|
12638
13213
|
if (shouldProcessEventForTesthub("")) {
|
|
12639
13214
|
patchConsoleLogs();
|
|
@@ -12644,7 +13219,7 @@ var BrowserstackService = class {
|
|
|
12644
13219
|
this._options
|
|
12645
13220
|
);
|
|
12646
13221
|
if (BrowserstackCLI.getInstance().isRunning()) {
|
|
12647
|
-
|
|
13222
|
+
BStackLogger.info(`CLI is running, tracking insights event for before: ${sessionId}`);
|
|
12648
13223
|
await BrowserstackCLI.getInstance().getAutomationFramework().trackEvent(AutomationFrameworkState.CREATE, HookState.POST, { browser: this._browser, hubUrl: this._config.hostname });
|
|
12649
13224
|
this._insightsHandler.setGitConfigPath();
|
|
12650
13225
|
PerformanceTester.end(DRIVER_EVENT.PRE_INITIALIZE);
|
|
@@ -12679,7 +13254,7 @@ var BrowserstackService = class {
|
|
|
12679
13254
|
});
|
|
12680
13255
|
}
|
|
12681
13256
|
} catch (err) {
|
|
12682
|
-
|
|
13257
|
+
BStackLogger.error(`Error in service class before function: ${err}`);
|
|
12683
13258
|
if (shouldProcessEventForTesthub("")) {
|
|
12684
13259
|
CrashReporter.uploadCrashReport(`Error in service class before function: ${err}`, err ? err.stack : "unknown error");
|
|
12685
13260
|
}
|
|
@@ -12752,8 +13327,8 @@ var BrowserstackService = class {
|
|
|
12752
13327
|
}
|
|
12753
13328
|
async afterTest(test, context, results) {
|
|
12754
13329
|
this._specsRan = true;
|
|
12755
|
-
const { error, passed } = results;
|
|
12756
|
-
if (!passed) {
|
|
13330
|
+
const { error, passed, skipped } = results;
|
|
13331
|
+
if (!passed && !skipped) {
|
|
12757
13332
|
const testError = error && error.message || "Unknown Error";
|
|
12758
13333
|
this._failReasons.push(testError);
|
|
12759
13334
|
this._pureTestFailReasons.push(testError);
|
|
@@ -12863,7 +13438,7 @@ var BrowserstackService = class {
|
|
|
12863
13438
|
return;
|
|
12864
13439
|
}
|
|
12865
13440
|
} catch (error) {
|
|
12866
|
-
|
|
13441
|
+
BStackLogger.error(`Error in after hook: ${error}`);
|
|
12867
13442
|
PerformanceTester.end(DRIVER_EVENT.QUIT, false, util12.format(error));
|
|
12868
13443
|
PerformanceTester.end(HOOK_EVENTS.AFTER, false, util12.format(error));
|
|
12869
13444
|
await PerformanceTester.stopAndGenerate("performance-service.html");
|
|
@@ -12934,14 +13509,14 @@ var BrowserstackService = class {
|
|
|
12934
13509
|
failureReason = hasReasons ? this._failReasons.join("\n") : void 0;
|
|
12935
13510
|
}
|
|
12936
13511
|
if (!this._browser.isMultiremote) {
|
|
12937
|
-
|
|
13512
|
+
BStackLogger.info(`Update (reloaded) job with sessionId ${oldSessionId}, ${sessionStatus}`);
|
|
12938
13513
|
} else {
|
|
12939
13514
|
const browserName = this._browser.instances.filter(
|
|
12940
13515
|
(browserName2) => this._browser && this._browser.getInstance(browserName2).sessionId === newSessionId
|
|
12941
13516
|
)[0];
|
|
12942
|
-
|
|
13517
|
+
BStackLogger.info(`Update (reloaded) multiremote job for browser "${browserName}" and sessionId ${oldSessionId}, ${sessionStatus}`);
|
|
12943
13518
|
}
|
|
12944
|
-
|
|
13519
|
+
BStackLogger.warn(`Session Reloaded: Old Session Id: ${oldSessionId}, New Session Id: ${newSessionId}`);
|
|
12945
13520
|
await this._insightsHandler?.sendCBTInfo();
|
|
12946
13521
|
if (setSessionStatus) {
|
|
12947
13522
|
await this._update(oldSessionId, {
|
|
@@ -12967,7 +13542,7 @@ var BrowserstackService = class {
|
|
|
12967
13542
|
}
|
|
12968
13543
|
_updateJob(requestBody) {
|
|
12969
13544
|
return this._multiRemoteAction((sessionId, browserName) => {
|
|
12970
|
-
|
|
13545
|
+
BStackLogger.info(
|
|
12971
13546
|
browserName ? `Update multiremote job for browser "${browserName}" and sessionId ${sessionId}` : `Update job with sessionId ${sessionId}`
|
|
12972
13547
|
);
|
|
12973
13548
|
return this._update(sessionId, requestBody);
|
|
@@ -12993,7 +13568,7 @@ var BrowserstackService = class {
|
|
|
12993
13568
|
return Promise.resolve();
|
|
12994
13569
|
}
|
|
12995
13570
|
const sessionUrl = `${this._sessionBaseUrl}/${sessionId}.json`;
|
|
12996
|
-
|
|
13571
|
+
BStackLogger.debug(`Updating Browserstack session at ${sessionUrl} with request body: `, requestBody);
|
|
12997
13572
|
const encodedAuth = Buffer.from(`${this._config.user}:${this._config.key}`, "utf8").toString("base64");
|
|
12998
13573
|
const headers = {
|
|
12999
13574
|
"Content-Type": "application/json; charset=utf-8",
|
|
@@ -13018,7 +13593,7 @@ var BrowserstackService = class {
|
|
|
13018
13593
|
}
|
|
13019
13594
|
await this._multiRemoteAction(async (sessionId, browserName) => {
|
|
13020
13595
|
const sessionUrl = `${this._sessionBaseUrl}/${sessionId}.json`;
|
|
13021
|
-
|
|
13596
|
+
BStackLogger.debug(`Requesting Browserstack session URL at ${sessionUrl}`);
|
|
13022
13597
|
let browserUrl;
|
|
13023
13598
|
const encodedAuth = Buffer.from(`${this._config.user}:${this._config.key}`, "utf8").toString("base64");
|
|
13024
13599
|
const headers = {
|
|
@@ -13045,7 +13620,7 @@ var BrowserstackService = class {
|
|
|
13045
13620
|
}
|
|
13046
13621
|
const capabilities = getBrowserCapabilities(this._browser, this._caps, browserName);
|
|
13047
13622
|
const browserString = getBrowserDescription(capabilities);
|
|
13048
|
-
|
|
13623
|
+
BStackLogger.info(`${browserString} session: ${browserUrl}`);
|
|
13049
13624
|
});
|
|
13050
13625
|
}
|
|
13051
13626
|
async _setSessionName(suiteTitle, test) {
|