@wdio/browserstack-service 9.23.2 → 9.24.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/accessibility-handler.d.ts.map +1 -1
- package/build/ai-handler.d.ts.map +1 -1
- package/build/cleanup.js +1162 -162
- package/build/cli/cliUtils.d.ts +18 -0
- package/build/cli/cliUtils.d.ts.map +1 -1
- package/build/cli/grpcClient.d.ts +11 -3
- package/build/cli/grpcClient.d.ts.map +1 -1
- package/build/constants.d.ts +27 -0
- package/build/constants.d.ts.map +1 -1
- package/build/index.js +4186 -3538
- package/build/insights-handler.d.ts.map +1 -1
- package/build/instrumentation/funnelInstrumentation.d.ts.map +1 -1
- package/build/instrumentation/performance/constants.d.ts +64 -0
- package/build/instrumentation/performance/constants.d.ts.map +1 -1
- package/build/instrumentation/performance/performance-tester.d.ts +11 -0
- package/build/instrumentation/performance/performance-tester.d.ts.map +1 -1
- package/build/launcher.d.ts.map +1 -1
- package/build/module-hook-tracker.d.ts +47 -0
- package/build/module-hook-tracker.d.ts.map +1 -0
- package/build/service.d.ts +2 -0
- package/build/service.d.ts.map +1 -1
- package/build/testorchestration/helpers.d.ts.map +1 -1
- package/package.json +6 -6
package/build/cleanup.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// src/util.ts
|
|
2
|
-
import { hostname as hostname2, platform as
|
|
2
|
+
import { hostname as hostname2, platform as platform3, type as type2, version as version2, arch as arch3, tmpdir } from "node:os";
|
|
3
3
|
import crypto from "node:crypto";
|
|
4
|
-
import
|
|
4
|
+
import fs7 from "node:fs";
|
|
5
5
|
import zlib from "node:zlib";
|
|
6
|
-
import { format, promisify } from "node:util";
|
|
7
|
-
import
|
|
8
|
-
import
|
|
6
|
+
import { format, promisify as promisify2 } from "node:util";
|
|
7
|
+
import path6 from "node:path";
|
|
8
|
+
import util3 from "node:util";
|
|
9
9
|
import gitRepoInfo from "git-repo-info";
|
|
10
10
|
import gitconfig from "gitconfiglocal";
|
|
11
11
|
import { FormData } from "formdata-node";
|
|
@@ -54,13 +54,13 @@ var logPatcher_default = logPatcher;
|
|
|
54
54
|
|
|
55
55
|
// src/instrumentation/performance/performance-tester.ts
|
|
56
56
|
import { createObjectCsvWriter } from "csv-writer";
|
|
57
|
-
import
|
|
57
|
+
import fs4 from "node:fs";
|
|
58
58
|
import fsPromise from "node:fs/promises";
|
|
59
59
|
import { performance, PerformanceObserver } from "node:perf_hooks";
|
|
60
|
-
import
|
|
60
|
+
import util2 from "node:util";
|
|
61
61
|
import worker from "node:worker_threads";
|
|
62
|
-
import
|
|
63
|
-
import { arch, hostname, platform, type, version } from "node:os";
|
|
62
|
+
import path4 from "node:path";
|
|
63
|
+
import { arch as arch2, hostname, platform as platform2, type, version } from "node:os";
|
|
64
64
|
|
|
65
65
|
// src/bstackLogger.ts
|
|
66
66
|
import path from "node:path";
|
|
@@ -71,7 +71,7 @@ import logger from "@wdio/logger";
|
|
|
71
71
|
// package.json
|
|
72
72
|
var package_default = {
|
|
73
73
|
name: "@wdio/browserstack-service",
|
|
74
|
-
version: "9.23.
|
|
74
|
+
version: "9.23.3",
|
|
75
75
|
description: "WebdriverIO service for better Browserstack integration",
|
|
76
76
|
author: "Adam Bjerstedt <abjerstedt@gmail.com>",
|
|
77
77
|
homepage: "https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-browserstack-service",
|
|
@@ -122,7 +122,7 @@ var package_default = {
|
|
|
122
122
|
"git-repo-info": "^2.1.1",
|
|
123
123
|
gitconfiglocal: "^2.1.0",
|
|
124
124
|
glob: "^11.0.0",
|
|
125
|
-
tar: "^
|
|
125
|
+
tar: "^7.5.7",
|
|
126
126
|
undici: "^6.21.3",
|
|
127
127
|
uuid: "^11.1.0",
|
|
128
128
|
webdriverio: "workspace:*",
|
|
@@ -163,7 +163,7 @@ var BROWSERSTACK_OBSERVABILITY = "BROWSERSTACK_OBSERVABILITY";
|
|
|
163
163
|
var MAX_GIT_META_DATA_SIZE_IN_BYTES = 64 * 1024;
|
|
164
164
|
var GIT_META_DATA_TRUNCATED = "...[TRUNCATED]";
|
|
165
165
|
var WDIO_NAMING_PREFIX = "WebdriverIO-";
|
|
166
|
-
var
|
|
166
|
+
var UPDATED_CLI_ENDPOINT = "sdk/v1/update_cli";
|
|
167
167
|
|
|
168
168
|
// src/bstackLogger.ts
|
|
169
169
|
var log = logger("@wdio/browserstack-service");
|
|
@@ -256,6 +256,17 @@ var APIUtils = class {
|
|
|
256
256
|
}
|
|
257
257
|
};
|
|
258
258
|
|
|
259
|
+
// src/cli/cliUtils.ts
|
|
260
|
+
import fs3 from "node:fs";
|
|
261
|
+
import fsp from "node:fs/promises";
|
|
262
|
+
import { platform, arch, homedir } from "node:os";
|
|
263
|
+
import path3 from "node:path";
|
|
264
|
+
import util, { promisify } from "node:util";
|
|
265
|
+
import { exec } from "node:child_process";
|
|
266
|
+
import { Readable } from "node:stream";
|
|
267
|
+
import yauzl from "yauzl";
|
|
268
|
+
import { threadId } from "node:worker_threads";
|
|
269
|
+
|
|
259
270
|
// src/fetchWrapper.ts
|
|
260
271
|
import { fetch as undiciFetch, ProxyAgent } from "undici";
|
|
261
272
|
var ResponseError = class extends Error {
|
|
@@ -288,29 +299,963 @@ function _fetch(input, init) {
|
|
|
288
299
|
return fetch(input, init);
|
|
289
300
|
}
|
|
290
301
|
|
|
302
|
+
// src/instrumentation/performance/constants.ts
|
|
303
|
+
var EVENTS = {
|
|
304
|
+
SDK_SETUP: "sdk:setup",
|
|
305
|
+
SDK_CLEANUP: "sdk:cleanup",
|
|
306
|
+
SDK_PRE_TEST: "sdk:pre-test",
|
|
307
|
+
SDK_TEST: "sdk:test",
|
|
308
|
+
SDK_POST_TEST: "sdk:post-test",
|
|
309
|
+
SDK_HOOK: "sdk:hook",
|
|
310
|
+
SDK_DRIVER: "sdk:driver",
|
|
311
|
+
SDK_A11Y: "sdk:a11y",
|
|
312
|
+
SDK_O11Y: "sdk:o11y",
|
|
313
|
+
SDK_AUTO_CAPTURE: "sdk:auto-capture",
|
|
314
|
+
SDK_PROXY_SETUP: "sdk:proxy-setup",
|
|
315
|
+
SDK_TESTHUB: "sdk:testhub",
|
|
316
|
+
SDK_AUTOMATE: "sdk:automate",
|
|
317
|
+
SDK_APP_AUTOMATE: "sdk:app-automate",
|
|
318
|
+
SDK_TURBOSCALE: "sdk:turboscale",
|
|
319
|
+
SDK_PERCY: "sdk:percy",
|
|
320
|
+
SDK_PRE_INITIALIZE: "sdk:driver:pre-initialization",
|
|
321
|
+
SDK_POST_INITIALIZE: "sdk:driver:post-initialization",
|
|
322
|
+
SDK_CLI_CHECK_UPDATE: "sdk:cli:check-update",
|
|
323
|
+
SDK_CLI_DOWNLOAD: "sdk:cli:download",
|
|
324
|
+
SDK_CLI_ON_BOOTSTRAP: "sdk:cli:on-bootstrap",
|
|
325
|
+
SDK_CLI_ON_CONNECT: "sdk:cli:on-connect",
|
|
326
|
+
SDK_CLI_START: "sdk:cli:start",
|
|
327
|
+
SDK_CLI_ON_STOP: "sdk:cli:on-stop",
|
|
328
|
+
SDK_CONNECT_BIN_SESSION: "sdk:connectBinSession",
|
|
329
|
+
SDK_START_BIN_SESSION: "sdk:startBinSession",
|
|
330
|
+
// New events from Python SDK
|
|
331
|
+
SDK_DRIVER_INIT: "sdk:driverInit",
|
|
332
|
+
SDK_FIND_NEAREST_HUB: "sdk:findNearestHub",
|
|
333
|
+
SDK_AUTOMATION_FRAMEWORK_INIT: "sdk:automationFrameworkInit",
|
|
334
|
+
SDK_AUTOMATION_FRAMEWORK_START: "sdk:automationFrameworkStart",
|
|
335
|
+
SDK_AUTOMATION_FRAMEWORK_STOP: "sdk:automationFrameworkStop",
|
|
336
|
+
SDK_ACCESSIBILITY_CONFIG: "sdk:accessibilityConfig",
|
|
337
|
+
SDK_OBSERVABILITY_CONFIG: "sdk:observabilityConfig",
|
|
338
|
+
SDK_AI_SELF_HEAL_STEP: "sdk:aiSelfHealStep",
|
|
339
|
+
SDK_AI_SELF_HEAL_GET_RESULT: "sdk:aiSelfHealGetResult",
|
|
340
|
+
SDK_TEST_FRAMEWORK_EVENT: "sdk:testFrameworkEvent",
|
|
341
|
+
SDK_TEST_SESSION_EVENT: "sdk:testSessionEvent",
|
|
342
|
+
SDK_CLI_LOG_CREATED_EVENT: "sdk:cli:logCreatedEvent",
|
|
343
|
+
SDK_CLI_ENQUEUE_TEST_EVENT: "sdk:cli:enqueueTestEvent",
|
|
344
|
+
SDK_ON_STOP: "sdk:onStop",
|
|
345
|
+
SDK_SEND_LOGS: "sdk:sendlogs",
|
|
346
|
+
// Funnel Events
|
|
347
|
+
SDK_FUNNEL_TEST_ATTEMPTED: "sdk:funnel:test-attempted",
|
|
348
|
+
SDK_FUNNEL_TEST_SUCCESSFUL: "sdk:funnel:test-successful",
|
|
349
|
+
// Log Upload Events
|
|
350
|
+
SDK_UPLOAD_LOGS: "sdk:upload-logs",
|
|
351
|
+
// Key Metrics Events
|
|
352
|
+
SDK_SEND_KEY_METRICS: "sdk:send-key-metrics",
|
|
353
|
+
SDK_KEY_METRICS_PREPARATION: "sdk:key-metrics:preparation",
|
|
354
|
+
SDK_KEY_METRICS_UPLOAD: "sdk:key-metrics:upload",
|
|
355
|
+
// CLI Binary Events
|
|
356
|
+
SDK_CLI_DOWNLOAD_BINARY: "sdk:cli:download-binary",
|
|
357
|
+
SDK_CLI_BINARY_VERIFICATION: "sdk:cli:binary-verification",
|
|
358
|
+
// Cleanup & Shutdown Events (tracking gap between driver:quit and funnel:test-successful)
|
|
359
|
+
SDK_LISTENER_WORKER_END: "sdk:listener:worker-end",
|
|
360
|
+
SDK_PERCY_TEARDOWN: "sdk:percy:teardown",
|
|
361
|
+
SDK_WORKER_SAVE_DATA: "sdk:worker:save-data",
|
|
362
|
+
SDK_PERFORMANCE_REPORT_GEN: "sdk:performance:report-generation",
|
|
363
|
+
SDK_PERFORMANCE_JSON_WRITE: "sdk:performance:json-write",
|
|
364
|
+
SDK_PERFORMANCE_HTML_GEN: "sdk:performance:html-generation",
|
|
365
|
+
// Device Allocation Event (tracking gap between beforeSession and before hooks)
|
|
366
|
+
SDK_DEVICE_ALLOCATION: "sdk:device-allocation"
|
|
367
|
+
};
|
|
368
|
+
var TESTHUB_EVENTS = {
|
|
369
|
+
START: `${EVENTS.SDK_TESTHUB}:start`,
|
|
370
|
+
STOP: `${EVENTS.SDK_TESTHUB}:stop`
|
|
371
|
+
};
|
|
372
|
+
var AUTOMATE_EVENTS = {
|
|
373
|
+
KEEP_ALIVE: `${EVENTS.SDK_AUTOMATE}:keep-alive`,
|
|
374
|
+
HUB_MANAGEMENT: `${EVENTS.SDK_AUTOMATE}:hub-management`,
|
|
375
|
+
LOCAL_START: `${EVENTS.SDK_AUTOMATE}:local-start`,
|
|
376
|
+
LOCAL_STOP: `${EVENTS.SDK_AUTOMATE}:local-stop`,
|
|
377
|
+
DRIVER_MANAGE: `${EVENTS.SDK_AUTOMATE}:driver-manage`,
|
|
378
|
+
SESSION_NAME: `${EVENTS.SDK_AUTOMATE}:session-name`,
|
|
379
|
+
SESSION_STATUS: `${EVENTS.SDK_AUTOMATE}:session-status`,
|
|
380
|
+
SESSION_ANNOTATION: `${EVENTS.SDK_AUTOMATE}:session-annotation`,
|
|
381
|
+
IDLE_TIMEOUT: `${EVENTS.SDK_AUTOMATE}:idle-timeout`,
|
|
382
|
+
GENERATE_CI_ARTIFACT: `${EVENTS.SDK_AUTOMATE}:ci-artifacts`,
|
|
383
|
+
PRINT_BUILDLINK: `${EVENTS.SDK_AUTOMATE}:print-buildlink`
|
|
384
|
+
};
|
|
385
|
+
var A11Y_EVENTS = {
|
|
386
|
+
PERFORM_SCAN: `${EVENTS.SDK_A11Y}:driver-performscan`,
|
|
387
|
+
SAVE_RESULTS: `${EVENTS.SDK_A11Y}:save-results`,
|
|
388
|
+
GET_RESULTS: `${EVENTS.SDK_A11Y}:get-accessibility-results`,
|
|
389
|
+
GET_RESULTS_SUMMARY: `${EVENTS.SDK_A11Y}:get-accessibility-results-summary`
|
|
390
|
+
};
|
|
391
|
+
var PERCY_EVENTS = {
|
|
392
|
+
DOWNLOAD: `${EVENTS.SDK_PERCY}:download`,
|
|
393
|
+
SCREENSHOT: `${EVENTS.SDK_PERCY}:screenshot`,
|
|
394
|
+
START: `${EVENTS.SDK_PERCY}:start`,
|
|
395
|
+
STOP: `${EVENTS.SDK_PERCY}:stop`,
|
|
396
|
+
AUTO_CAPTURE: `${EVENTS.SDK_PERCY}:auto-capture`,
|
|
397
|
+
SNAPSHOT: `${EVENTS.SDK_PERCY}:snapshot`,
|
|
398
|
+
SCREENSHOT_APP: `${EVENTS.SDK_PERCY}:screenshot-app`
|
|
399
|
+
};
|
|
400
|
+
var O11Y_EVENTS = {
|
|
401
|
+
SYNC: `${EVENTS.SDK_O11Y}:sync`,
|
|
402
|
+
TAKE_SCREENSHOT: `${EVENTS.SDK_O11Y}:driver-takeScreenShot`,
|
|
403
|
+
PRINT_BUILDLINK: `${EVENTS.SDK_O11Y}:print-buildlink`
|
|
404
|
+
};
|
|
405
|
+
var HOOK_EVENTS = {
|
|
406
|
+
BEFORE_EACH: `${EVENTS.SDK_HOOK}:before-each`,
|
|
407
|
+
AFTER_EACH: `${EVENTS.SDK_HOOK}:after-each`,
|
|
408
|
+
AFTER_ALL: `${EVENTS.SDK_HOOK}:after-all`,
|
|
409
|
+
BEFORE_ALL: `${EVENTS.SDK_HOOK}:before-all`,
|
|
410
|
+
BEFORE: `${EVENTS.SDK_HOOK}:before`,
|
|
411
|
+
AFTER: `${EVENTS.SDK_HOOK}:after`
|
|
412
|
+
};
|
|
413
|
+
var TURBOSCALE_EVENTS = {
|
|
414
|
+
HUB_MANAGEMENT: `${EVENTS.SDK_TURBOSCALE}:hub-management`,
|
|
415
|
+
PRINT_BUILDLINK: `${EVENTS.SDK_TURBOSCALE}:print-buildlink`
|
|
416
|
+
};
|
|
417
|
+
var APP_AUTOMATE_EVENTS = {
|
|
418
|
+
APP_UPLOAD: `${EVENTS.SDK_APP_AUTOMATE}:app-upload`
|
|
419
|
+
};
|
|
420
|
+
var DRIVER_EVENT = {
|
|
421
|
+
QUIT: `${EVENTS.SDK_DRIVER}:quit`,
|
|
422
|
+
GET: `${EVENTS.SDK_DRIVER}:get`,
|
|
423
|
+
PRE_EXECUTE: `${EVENTS.SDK_DRIVER}:pre-execute`,
|
|
424
|
+
POST_EXECUTE: `${EVENTS.SDK_DRIVER}:post-execute`,
|
|
425
|
+
INIT: EVENTS.SDK_DRIVER_INIT,
|
|
426
|
+
PRE_INITIALIZE: EVENTS.SDK_PRE_INITIALIZE,
|
|
427
|
+
POST_INITIALIZE: EVENTS.SDK_POST_INITIALIZE
|
|
428
|
+
};
|
|
429
|
+
var FRAMEWORK_EVENTS = {
|
|
430
|
+
INIT: EVENTS.SDK_AUTOMATION_FRAMEWORK_INIT,
|
|
431
|
+
START: EVENTS.SDK_AUTOMATION_FRAMEWORK_START,
|
|
432
|
+
STOP: EVENTS.SDK_AUTOMATION_FRAMEWORK_STOP
|
|
433
|
+
};
|
|
434
|
+
var CONFIG_EVENTS = {
|
|
435
|
+
ACCESSIBILITY: EVENTS.SDK_ACCESSIBILITY_CONFIG,
|
|
436
|
+
OBSERVABILITY: EVENTS.SDK_OBSERVABILITY_CONFIG
|
|
437
|
+
};
|
|
438
|
+
var AI_EVENTS = {
|
|
439
|
+
SELF_HEAL_STEP: EVENTS.SDK_AI_SELF_HEAL_STEP,
|
|
440
|
+
SELF_HEAL_GET_RESULT: EVENTS.SDK_AI_SELF_HEAL_GET_RESULT
|
|
441
|
+
};
|
|
442
|
+
var DISPATCHER_EVENTS = {
|
|
443
|
+
TEST_FRAMEWORK: EVENTS.SDK_TEST_FRAMEWORK_EVENT,
|
|
444
|
+
TEST_SESSION: EVENTS.SDK_TEST_SESSION_EVENT,
|
|
445
|
+
LOG_CREATED: EVENTS.SDK_CLI_LOG_CREATED_EVENT,
|
|
446
|
+
ENQUEUE_TEST: EVENTS.SDK_CLI_ENQUEUE_TEST_EVENT
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
// src/cli/cliLogger.ts
|
|
450
|
+
import path2 from "node:path";
|
|
451
|
+
import fs2 from "node:fs";
|
|
452
|
+
import chalk2 from "chalk";
|
|
453
|
+
import logger2 from "@wdio/logger";
|
|
454
|
+
var log2 = logger2("@wdio/browserstack-service/cli");
|
|
455
|
+
var BStackLogger2 = class {
|
|
456
|
+
static logFilePath = path2.join(process.cwd(), LOGS_FILE);
|
|
457
|
+
static logFolderPath = path2.join(process.cwd(), "logs");
|
|
458
|
+
static logFileStream;
|
|
459
|
+
static logToFile(logMessage, logLevel) {
|
|
460
|
+
try {
|
|
461
|
+
if (!this.logFileStream) {
|
|
462
|
+
this.ensureLogsFolder();
|
|
463
|
+
this.logFileStream = fs2.createWriteStream(this.logFilePath, { flags: "a" });
|
|
464
|
+
}
|
|
465
|
+
if (this.logFileStream && this.logFileStream.writable) {
|
|
466
|
+
this.logFileStream.write(this.formatLog(logMessage, logLevel));
|
|
467
|
+
}
|
|
468
|
+
} catch (error) {
|
|
469
|
+
log2.debug(`Failed to log to file. Error ${error}`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
static formatLog(logMessage, level) {
|
|
473
|
+
return `${chalk2.gray((/* @__PURE__ */ new Date()).toISOString())} ${chalk2[COLORS[level]](level.toUpperCase())} ${chalk2.whiteBright("@wdio/browserstack-service")} ${logMessage}
|
|
474
|
+
`;
|
|
475
|
+
}
|
|
476
|
+
static info(message) {
|
|
477
|
+
this.logToFile(message, "info");
|
|
478
|
+
log2.info(message);
|
|
479
|
+
}
|
|
480
|
+
static error(message) {
|
|
481
|
+
this.logToFile(message, "error");
|
|
482
|
+
log2.error(message);
|
|
483
|
+
}
|
|
484
|
+
static debug(message, param) {
|
|
485
|
+
this.logToFile(message, "debug");
|
|
486
|
+
if (param) {
|
|
487
|
+
log2.debug(message, param);
|
|
488
|
+
} else {
|
|
489
|
+
log2.debug(message);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
static warn(message) {
|
|
493
|
+
this.logToFile(message, "warn");
|
|
494
|
+
log2.warn(message);
|
|
495
|
+
}
|
|
496
|
+
static trace(message) {
|
|
497
|
+
this.logToFile(message, "trace");
|
|
498
|
+
log2.trace(message);
|
|
499
|
+
}
|
|
500
|
+
static clearLogger() {
|
|
501
|
+
if (this.logFileStream) {
|
|
502
|
+
this.logFileStream.end();
|
|
503
|
+
}
|
|
504
|
+
this.logFileStream = null;
|
|
505
|
+
}
|
|
506
|
+
static clearLogFile() {
|
|
507
|
+
if (fs2.existsSync(this.logFilePath)) {
|
|
508
|
+
fs2.truncateSync(this.logFilePath);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
static ensureLogsFolder() {
|
|
512
|
+
if (!fs2.existsSync(this.logFolderPath)) {
|
|
513
|
+
fs2.mkdirSync(this.logFolderPath);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
// src/cli/frameworks/constants/testFrameworkConstants.ts
|
|
519
|
+
var TestFrameworkConstants = {
|
|
520
|
+
KEY_TEST_UUID: "test_uuid",
|
|
521
|
+
KEY_TEST_ID: "test_id",
|
|
522
|
+
KEY_TEST_NAME: "test_name",
|
|
523
|
+
KEY_TEST_FILE_PATH: "test_file_path",
|
|
524
|
+
KEY_TEST_TAGS: "test_tags",
|
|
525
|
+
KEY_TEST_RESULT: "test_result",
|
|
526
|
+
KEY_TEST_RESULT_AT: "test_result_at",
|
|
527
|
+
KEY_TEST_STARTED_AT: "test_started_at",
|
|
528
|
+
KEY_TEST_ENDED_AT: "test_ended_at",
|
|
529
|
+
KEY_TEST_LOCATION: "test_location",
|
|
530
|
+
KEY_TEST_SCOPE: "test_scope",
|
|
531
|
+
KEY_TEST_SCOPES: "test_scopes",
|
|
532
|
+
KEY_TEST_FRAMEWORK_NAME: "test_framework_name",
|
|
533
|
+
KEY_TEST_FRAMEWORK_VERSION: "test_framework_version",
|
|
534
|
+
KEY_TEST_CODE: "test_code",
|
|
535
|
+
KEY_TEST_RERUN_NAME: "test_rerun_name",
|
|
536
|
+
KEY_PLATFORM_INDEX: "platform_index",
|
|
537
|
+
KEY_TEST_FAILURE: "test_failure",
|
|
538
|
+
KEY_TEST_FAILURE_TYPE: "test_failure_type",
|
|
539
|
+
KEY_TEST_FAILURE_REASON: "test_failure_reason",
|
|
540
|
+
KEY_TEST_LOGS: "test_logs",
|
|
541
|
+
KEY_TEST_META: "test_meta",
|
|
542
|
+
KEY_TEST_DEFERRED: "test_deferred",
|
|
543
|
+
KEY_SESSION_NAME: "test_session_name",
|
|
544
|
+
KEY_AUTOMATE_SESSION_NAME: "automate_session_name",
|
|
545
|
+
KEY_AUTOMATE_SESSION_STATUS: "automate_session_status",
|
|
546
|
+
KEY_AUTOMATE_SESSION_REASON: "automate_session_reason",
|
|
547
|
+
KEY_EVENT_STARTED_AT: "event_started_at",
|
|
548
|
+
KEY_EVENT_ENDED_AT: "event_ended_at",
|
|
549
|
+
KEY_HOOK_ID: "hook_id",
|
|
550
|
+
KEY_HOOK_RESULT: "hook_result",
|
|
551
|
+
KEY_HOOK_LOGS: "hook_logs",
|
|
552
|
+
KEY_HOOK_NAME: "hook_name",
|
|
553
|
+
KEY_HOOKS_STARTED: "test_hooks_started",
|
|
554
|
+
KEY_HOOKS_FINISHED: "test_hooks_finished",
|
|
555
|
+
DEFAULT_TEST_RESULT: "pending",
|
|
556
|
+
DEFAULT_HOOK_RESULT: "pending",
|
|
557
|
+
KIND_SCREENSHOT: "TEST_SCREENSHOT",
|
|
558
|
+
KIND_LOG: "TEST_LOG",
|
|
559
|
+
HOOK_REGEX: "^(BEFORE_|AFTER_)"
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// src/cli/cliUtils.ts
|
|
563
|
+
var CLI_LOCK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
564
|
+
var CLI_LOCK_POLL_MS = 1e3;
|
|
565
|
+
var CLI_DOWNLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
566
|
+
var CLI_DOWNLOAD_TMP_PREFIX = "downloaded_file_";
|
|
567
|
+
var CLI_DOWNLOAD_TMP_SUFFIX = ".zip";
|
|
568
|
+
var CLIUtils = class _CLIUtils {
|
|
569
|
+
static automationFrameworkDetail = {};
|
|
570
|
+
static testFrameworkDetail = {};
|
|
571
|
+
static CLISupportedFrameworks = ["mocha"];
|
|
572
|
+
static isDevelopmentEnv() {
|
|
573
|
+
return process.env.BROWSERSTACK_CLI_ENV === "development";
|
|
574
|
+
}
|
|
575
|
+
static getCLIParamsForDevEnv() {
|
|
576
|
+
return {
|
|
577
|
+
id: process.env.BROWSERSTACK_CLI_ENV || "",
|
|
578
|
+
listen: `unix:/tmp/sdk-platform-${process.env.BROWSERSTACK_CLI_ENV}.sock`
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Build config object for binary session request
|
|
583
|
+
* @returns {string}
|
|
584
|
+
* @throws {Error}
|
|
585
|
+
*/
|
|
586
|
+
static getBinConfig(config, capabilities, options, buildTag) {
|
|
587
|
+
const modifiedOpts = { ...options };
|
|
588
|
+
if (modifiedOpts.opts) {
|
|
589
|
+
modifiedOpts.browserStackLocalOptions = modifiedOpts.opts;
|
|
590
|
+
delete modifiedOpts.opts;
|
|
591
|
+
}
|
|
592
|
+
modifiedOpts.testContextOptions = {
|
|
593
|
+
skipSessionName: isFalse(modifiedOpts.setSessionName),
|
|
594
|
+
skipSessionStatus: isFalse(modifiedOpts.setSessionStatus),
|
|
595
|
+
sessionNameOmitTestTitle: modifiedOpts.sessionNameOmitTestTitle || false,
|
|
596
|
+
sessionNamePrependTopLevelSuiteTitle: modifiedOpts.sessionNamePrependTopLevelSuiteTitle || false,
|
|
597
|
+
sessionNameFormat: modifiedOpts.sessionNameFormat || ""
|
|
598
|
+
};
|
|
599
|
+
const commonBstackOptions = (() => {
|
|
600
|
+
if (capabilities && !Array.isArray(capabilities) && typeof capabilities === "object" && "bstack:options" in capabilities) {
|
|
601
|
+
return capabilities["bstack:options"] || {};
|
|
602
|
+
}
|
|
603
|
+
return {};
|
|
604
|
+
})();
|
|
605
|
+
const isNonBstackA11y = isTurboScale(options) || !shouldAddServiceVersion(
|
|
606
|
+
config,
|
|
607
|
+
options.testObservability
|
|
608
|
+
);
|
|
609
|
+
const observabilityOptions = options.testObservabilityOptions || {};
|
|
610
|
+
const binconfig = {
|
|
611
|
+
userName: observabilityOptions.user || config.user,
|
|
612
|
+
accessKey: observabilityOptions.key || config.key,
|
|
613
|
+
platforms: [],
|
|
614
|
+
isNonBstackA11yWDIO: isNonBstackA11y,
|
|
615
|
+
...modifiedOpts,
|
|
616
|
+
...commonBstackOptions
|
|
617
|
+
};
|
|
618
|
+
binconfig.buildName = observabilityOptions.buildName || binconfig.buildName;
|
|
619
|
+
binconfig.projectName = observabilityOptions.projectName || binconfig.projectName;
|
|
620
|
+
binconfig.buildTag = this.getObservabilityBuildTags(observabilityOptions, buildTag) || [];
|
|
621
|
+
let caps = capabilities;
|
|
622
|
+
if (capabilities && !Array.isArray(capabilities)) {
|
|
623
|
+
caps = [capabilities];
|
|
624
|
+
}
|
|
625
|
+
if (Array.isArray(caps)) {
|
|
626
|
+
for (const cap of caps) {
|
|
627
|
+
const platform4 = {};
|
|
628
|
+
const capability = cap;
|
|
629
|
+
Object.keys(capability).filter((key) => key !== "bstack:options").forEach((key) => {
|
|
630
|
+
platform4[key] = capability[key];
|
|
631
|
+
});
|
|
632
|
+
if (capability["bstack:options"]) {
|
|
633
|
+
Object.keys(
|
|
634
|
+
capability["bstack:options"]
|
|
635
|
+
).forEach((key) => {
|
|
636
|
+
platform4[key] = capability["bstack:options"][key];
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
binconfig.platforms.push(platform4);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return JSON.stringify(binconfig);
|
|
643
|
+
}
|
|
644
|
+
static getSdkVersion() {
|
|
645
|
+
return BSTACK_SERVICE_VERSION;
|
|
646
|
+
}
|
|
647
|
+
static getSdkLanguage() {
|
|
648
|
+
return "ECMAScript";
|
|
649
|
+
}
|
|
650
|
+
static async setupCliPath(config) {
|
|
651
|
+
BStackLogger2.debug("Configuring Cli path.");
|
|
652
|
+
const developmentBinaryPath = process.env.SDK_CLI_BIN_PATH || null;
|
|
653
|
+
if (!isNullOrEmpty(developmentBinaryPath)) {
|
|
654
|
+
BStackLogger2.debug(`Development Cli Path: ${developmentBinaryPath}`);
|
|
655
|
+
return developmentBinaryPath;
|
|
656
|
+
}
|
|
657
|
+
try {
|
|
658
|
+
const cliDir = this.getCliDir();
|
|
659
|
+
if (isNullOrEmpty(cliDir)) {
|
|
660
|
+
throw new Error("No writable directory available for the CLI");
|
|
661
|
+
}
|
|
662
|
+
const existingCliPath = this.getExistingCliPath(cliDir);
|
|
663
|
+
const finalBinaryPath = await this.checkAndUpdateCli(
|
|
664
|
+
existingCliPath,
|
|
665
|
+
cliDir,
|
|
666
|
+
config
|
|
667
|
+
);
|
|
668
|
+
BStackLogger2.debug(`Resolved binary path: ${finalBinaryPath}`);
|
|
669
|
+
return finalBinaryPath;
|
|
670
|
+
} catch (err) {
|
|
671
|
+
BStackLogger2.debug(
|
|
672
|
+
`Error in setting up cli path directory, Exception: ${util.format(err)}`
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
return null;
|
|
676
|
+
}
|
|
677
|
+
static async checkAndUpdateCli(existingCliPath, cliDir, config) {
|
|
678
|
+
if (process.env.BROWSERSTACK_TESTHUB_JWT) {
|
|
679
|
+
BStackLogger2.debug(
|
|
680
|
+
`Worker process detected, skipping CLI update. Using existing: ${existingCliPath}`
|
|
681
|
+
);
|
|
682
|
+
if (existingCliPath && fs3.existsSync(existingCliPath)) {
|
|
683
|
+
return existingCliPath;
|
|
684
|
+
}
|
|
685
|
+
BStackLogger2.warn(
|
|
686
|
+
"Worker process has no existing CLI binary, attempting download as fallback."
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
PerformanceTester.start(EVENTS.SDK_CLI_CHECK_UPDATE);
|
|
690
|
+
BStackLogger2.info(`Current CLI Path Found: ${existingCliPath}`);
|
|
691
|
+
const queryParams = {
|
|
692
|
+
sdk_version: _CLIUtils.getSdkVersion(),
|
|
693
|
+
os: platform(),
|
|
694
|
+
os_arch: arch(),
|
|
695
|
+
cli_version: "0",
|
|
696
|
+
sdk_language: this.getSdkLanguage()
|
|
697
|
+
};
|
|
698
|
+
if (!isNullOrEmpty(existingCliPath)) {
|
|
699
|
+
queryParams.cli_version = await this.runShellCommand(
|
|
700
|
+
`${existingCliPath} version`
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
const response = await this.requestToUpdateCLI(queryParams, config);
|
|
704
|
+
if (nestedKeyValue(response, ["updated_cli_version"])) {
|
|
705
|
+
BStackLogger2.debug(
|
|
706
|
+
`Need to update binary, current binary version: ${queryParams.cli_version}`
|
|
707
|
+
);
|
|
708
|
+
const browserStackBinaryUrl = process.env.BROWSERSTACK_BINARY_URL || null;
|
|
709
|
+
if (!isNullOrEmpty(browserStackBinaryUrl)) {
|
|
710
|
+
BStackLogger2.debug(
|
|
711
|
+
`Using BROWSERSTACK_BINARY_URL: ${browserStackBinaryUrl}`
|
|
712
|
+
);
|
|
713
|
+
response.url = browserStackBinaryUrl;
|
|
714
|
+
}
|
|
715
|
+
const finalBinaryPath = await this.downloadLatestBinary(
|
|
716
|
+
nestedKeyValue(response, ["url"]),
|
|
717
|
+
cliDir
|
|
718
|
+
);
|
|
719
|
+
PerformanceTester.end(EVENTS.SDK_CLI_CHECK_UPDATE);
|
|
720
|
+
return finalBinaryPath;
|
|
721
|
+
}
|
|
722
|
+
PerformanceTester.end(EVENTS.SDK_CLI_CHECK_UPDATE);
|
|
723
|
+
return existingCliPath;
|
|
724
|
+
}
|
|
725
|
+
static getCliDir() {
|
|
726
|
+
const writableDir = this.getWritableDir();
|
|
727
|
+
try {
|
|
728
|
+
if (isNullOrEmpty(writableDir)) {
|
|
729
|
+
throw new Error("No writable directory available for the CLI");
|
|
730
|
+
}
|
|
731
|
+
const cliDirPath = path3.join(writableDir, "cli");
|
|
732
|
+
if (!fs3.existsSync(cliDirPath)) {
|
|
733
|
+
createDir(cliDirPath);
|
|
734
|
+
}
|
|
735
|
+
return cliDirPath;
|
|
736
|
+
} catch (err) {
|
|
737
|
+
BStackLogger2.error(
|
|
738
|
+
`Error in getting writable directory, writableDir=${util.format(err)}`
|
|
739
|
+
);
|
|
740
|
+
return "";
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
static getWritableDir() {
|
|
744
|
+
const writableDirOptions = [
|
|
745
|
+
process.env.BROWSERSTACK_FILES_DIR,
|
|
746
|
+
path3.join(homedir(), ".browserstack"),
|
|
747
|
+
path3.join("tmp", ".browserstack")
|
|
748
|
+
];
|
|
749
|
+
for (const path9 of writableDirOptions) {
|
|
750
|
+
if (isNullOrEmpty(path9)) {
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
try {
|
|
754
|
+
if (fs3.existsSync(path9)) {
|
|
755
|
+
BStackLogger2.debug(`File ${path9} already exist`);
|
|
756
|
+
if (!isWritable(path9)) {
|
|
757
|
+
BStackLogger2.debug(`Giving write permission to ${path9}`);
|
|
758
|
+
const success = setReadWriteAccess(path9);
|
|
759
|
+
if (!isTrue(success)) {
|
|
760
|
+
BStackLogger2.warn(
|
|
761
|
+
`Unable to provide write permission to ${path9}`
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
} else {
|
|
766
|
+
BStackLogger2.debug(`File does not exist: ${path9}`);
|
|
767
|
+
createDir(path9);
|
|
768
|
+
BStackLogger2.debug(`Giving write permission to ${path9}`);
|
|
769
|
+
const success = setReadWriteAccess(path9);
|
|
770
|
+
if (!isTrue(success)) {
|
|
771
|
+
BStackLogger2.warn(
|
|
772
|
+
`Unable to provide write permission to ${path9}`
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return path9;
|
|
777
|
+
} catch (err) {
|
|
778
|
+
BStackLogger2.error(
|
|
779
|
+
`Unable to get writable directory, exception ${util.format(err)}`
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
return null;
|
|
784
|
+
}
|
|
785
|
+
static getExistingCliPath(cliDir) {
|
|
786
|
+
try {
|
|
787
|
+
if (!fs3.existsSync(cliDir) || !fs3.statSync(cliDir).isDirectory()) {
|
|
788
|
+
return "";
|
|
789
|
+
}
|
|
790
|
+
const allBinaries = fs3.readdirSync(cliDir).map((file) => path3.join(cliDir, file)).filter(
|
|
791
|
+
(filePath) => fs3.statSync(filePath).isFile() && path3.basename(filePath).startsWith("binary-")
|
|
792
|
+
);
|
|
793
|
+
if (allBinaries.length > 0) {
|
|
794
|
+
const latestBinary = allBinaries.map((filePath) => ({
|
|
795
|
+
filePath,
|
|
796
|
+
mtime: fs3.statSync(filePath).mtime
|
|
797
|
+
})).reduce(
|
|
798
|
+
(latest, current) => {
|
|
799
|
+
if (!latest || !latest.mtime) {
|
|
800
|
+
return current;
|
|
801
|
+
}
|
|
802
|
+
if (current.mtime > latest.mtime) {
|
|
803
|
+
return current;
|
|
804
|
+
}
|
|
805
|
+
return latest;
|
|
806
|
+
},
|
|
807
|
+
null
|
|
808
|
+
);
|
|
809
|
+
return latestBinary ? latestBinary.filePath : "";
|
|
810
|
+
}
|
|
811
|
+
return "";
|
|
812
|
+
} catch (err) {
|
|
813
|
+
BStackLogger2.error(`Error while reading CLI path: ${util.format(err)}`);
|
|
814
|
+
return "";
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
static requestToUpdateCLI = async (queryParams, config) => {
|
|
818
|
+
const params = new URLSearchParams(queryParams);
|
|
819
|
+
const requestInit = {
|
|
820
|
+
method: "GET",
|
|
821
|
+
headers: {
|
|
822
|
+
Authorization: `Basic ${Buffer.from(`${getBrowserStackUser(config)}:${getBrowserStackKey(config)}`).toString("base64")}`
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
const response = await _fetch(
|
|
826
|
+
`${APIUtils.BROWSERSTACK_AUTOMATE_API_URL}/${UPDATED_CLI_ENDPOINT}?${params.toString()}`,
|
|
827
|
+
requestInit
|
|
828
|
+
);
|
|
829
|
+
const jsonResponse = await response.json();
|
|
830
|
+
BStackLogger2.debug(`response ${JSON.stringify(jsonResponse)}`);
|
|
831
|
+
return jsonResponse;
|
|
832
|
+
};
|
|
833
|
+
static runShellCommand(cmdCommand, workingDir = "") {
|
|
834
|
+
return new Promise((resolve) => {
|
|
835
|
+
const process2 = exec(
|
|
836
|
+
cmdCommand,
|
|
837
|
+
{ cwd: workingDir, timeout: 5e3 },
|
|
838
|
+
(error, stdout, stderr) => {
|
|
839
|
+
if (error) {
|
|
840
|
+
resolve(stderr.trim() || "SHELL_EXECUTE_ERROR");
|
|
841
|
+
} else {
|
|
842
|
+
resolve(stdout.trim());
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
);
|
|
846
|
+
process2.on("error", () => {
|
|
847
|
+
resolve("SHELL_EXECUTE_ERROR");
|
|
848
|
+
});
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
static downloadLatestBinary = async (binDownloadUrl, cliDir) => {
|
|
852
|
+
const lockPath = path3.join(cliDir, "download.lock");
|
|
853
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
854
|
+
const parseLockFile = () => {
|
|
855
|
+
try {
|
|
856
|
+
const content = fs3.readFileSync(lockPath, "utf8").trim();
|
|
857
|
+
const [pidLine, timestampLine] = content.split("\n");
|
|
858
|
+
const pid = Number.parseInt(pidLine, 10);
|
|
859
|
+
const timestamp = Number.parseInt(timestampLine, 10);
|
|
860
|
+
if (!Number.isFinite(pid) || !Number.isFinite(timestamp)) {
|
|
861
|
+
return null;
|
|
862
|
+
}
|
|
863
|
+
return { pid, timestamp };
|
|
864
|
+
} catch {
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
const isProcessRunning = (pid) => {
|
|
869
|
+
try {
|
|
870
|
+
process.kill(pid, 0);
|
|
871
|
+
return true;
|
|
872
|
+
} catch {
|
|
873
|
+
return false;
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
const acquireLock = async (timeoutMs = CLI_LOCK_TIMEOUT_MS, pollMs = CLI_LOCK_POLL_MS) => {
|
|
877
|
+
const start = Date.now();
|
|
878
|
+
while (true) {
|
|
879
|
+
try {
|
|
880
|
+
const fd = fs3.openSync(lockPath, "wx");
|
|
881
|
+
try {
|
|
882
|
+
fs3.writeFileSync(fd, `${process.pid}
|
|
883
|
+
${Date.now()}
|
|
884
|
+
`);
|
|
885
|
+
} catch {
|
|
886
|
+
}
|
|
887
|
+
return () => {
|
|
888
|
+
try {
|
|
889
|
+
fs3.closeSync(fd);
|
|
890
|
+
} catch {
|
|
891
|
+
}
|
|
892
|
+
try {
|
|
893
|
+
fs3.unlinkSync(lockPath);
|
|
894
|
+
} catch {
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
} catch (e) {
|
|
898
|
+
const error = e;
|
|
899
|
+
if (error.code === "EEXIST") {
|
|
900
|
+
const lockMeta = parseLockFile();
|
|
901
|
+
if (lockMeta) {
|
|
902
|
+
const lockAge = Date.now() - lockMeta.timestamp;
|
|
903
|
+
const running = isProcessRunning(lockMeta.pid);
|
|
904
|
+
if (!running || lockAge > timeoutMs) {
|
|
905
|
+
BStackLogger2.warn(
|
|
906
|
+
`Stale CLI download lock detected (pid=${lockMeta.pid}, age=${lockAge}ms). Removing lock.`
|
|
907
|
+
);
|
|
908
|
+
try {
|
|
909
|
+
fs3.unlinkSync(lockPath);
|
|
910
|
+
} catch {
|
|
911
|
+
}
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
const existingBinary = _CLIUtils.getExistingCliPath(cliDir);
|
|
916
|
+
if (existingBinary && fs3.existsSync(existingBinary) && fs3.statSync(existingBinary).size > 0) {
|
|
917
|
+
BStackLogger2.debug(
|
|
918
|
+
`Binary appeared while waiting for lock: ${existingBinary}`
|
|
919
|
+
);
|
|
920
|
+
return { alreadyExists: existingBinary };
|
|
921
|
+
}
|
|
922
|
+
if (Date.now() - start > timeoutMs) {
|
|
923
|
+
throw new Error(
|
|
924
|
+
`Timeout waiting for lock: ${lockPath}`
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
await sleep(pollMs);
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
throw e;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
const cleanupTemporaryDownloads = (maxAgeMs = CLI_LOCK_TIMEOUT_MS) => {
|
|
935
|
+
try {
|
|
936
|
+
const now = Date.now();
|
|
937
|
+
for (const entry of fs3.readdirSync(cliDir)) {
|
|
938
|
+
if (!entry.startsWith(CLI_DOWNLOAD_TMP_PREFIX) || !entry.endsWith(CLI_DOWNLOAD_TMP_SUFFIX)) {
|
|
939
|
+
continue;
|
|
940
|
+
}
|
|
941
|
+
const filePath = path3.join(cliDir, entry);
|
|
942
|
+
let stats;
|
|
943
|
+
try {
|
|
944
|
+
stats = fs3.statSync(filePath);
|
|
945
|
+
} catch {
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
948
|
+
if (now - stats.mtimeMs < maxAgeMs) {
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
try {
|
|
952
|
+
fs3.unlinkSync(filePath);
|
|
953
|
+
} catch (err) {
|
|
954
|
+
BStackLogger2.debug(
|
|
955
|
+
`Failed to delete temp CLI zip file ${filePath}: ${util.format(err)}`
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
} catch (err) {
|
|
960
|
+
BStackLogger2.debug(
|
|
961
|
+
`Failed to scan temp CLI downloads in ${cliDir}: ${util.format(err)}`
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
PerformanceTester.start(EVENTS.SDK_CLI_DOWNLOAD);
|
|
966
|
+
BStackLogger2.debug(`Downloading SDK binary from: ${binDownloadUrl}`);
|
|
967
|
+
let downloadEnded = false;
|
|
968
|
+
const endDownload = (success = true, errMsg) => {
|
|
969
|
+
if (downloadEnded) {
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
downloadEnded = true;
|
|
973
|
+
if (success) {
|
|
974
|
+
PerformanceTester.end(EVENTS.SDK_CLI_DOWNLOAD);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
PerformanceTester.end(
|
|
978
|
+
EVENTS.SDK_CLI_DOWNLOAD,
|
|
979
|
+
false,
|
|
980
|
+
errMsg
|
|
981
|
+
);
|
|
982
|
+
};
|
|
983
|
+
let releaseLock;
|
|
984
|
+
try {
|
|
985
|
+
const lockResult = await acquireLock();
|
|
986
|
+
if (typeof lockResult !== "function") {
|
|
987
|
+
endDownload();
|
|
988
|
+
return lockResult.alreadyExists;
|
|
989
|
+
}
|
|
990
|
+
releaseLock = lockResult;
|
|
991
|
+
const existingBinary = _CLIUtils.getExistingCliPath(cliDir);
|
|
992
|
+
if (existingBinary && fs3.existsSync(existingBinary) && fs3.statSync(existingBinary).size > 0) {
|
|
993
|
+
BStackLogger2.debug(
|
|
994
|
+
`Binary already exists after acquiring lock: ${existingBinary}`
|
|
995
|
+
);
|
|
996
|
+
endDownload();
|
|
997
|
+
releaseLock();
|
|
998
|
+
return existingBinary;
|
|
999
|
+
}
|
|
1000
|
+
cleanupTemporaryDownloads();
|
|
1001
|
+
const zipFilePath = path3.join(
|
|
1002
|
+
cliDir,
|
|
1003
|
+
`${CLI_DOWNLOAD_TMP_PREFIX}${process.pid}_${Date.now()}${CLI_DOWNLOAD_TMP_SUFFIX}`
|
|
1004
|
+
);
|
|
1005
|
+
const downloadedFileStream = fs3.createWriteStream(zipFilePath);
|
|
1006
|
+
return new Promise((resolve, reject) => {
|
|
1007
|
+
const binaryName = null;
|
|
1008
|
+
const processDownload = async () => {
|
|
1009
|
+
const abortController = new AbortController();
|
|
1010
|
+
const timeout = setTimeout(
|
|
1011
|
+
() => abortController.abort(),
|
|
1012
|
+
CLI_DOWNLOAD_TIMEOUT_MS
|
|
1013
|
+
);
|
|
1014
|
+
let response;
|
|
1015
|
+
try {
|
|
1016
|
+
response = await _fetch(binDownloadUrl, {
|
|
1017
|
+
signal: abortController.signal
|
|
1018
|
+
});
|
|
1019
|
+
} finally {
|
|
1020
|
+
clearTimeout(timeout);
|
|
1021
|
+
}
|
|
1022
|
+
if (!response.body) {
|
|
1023
|
+
throw new Error("No response body received");
|
|
1024
|
+
}
|
|
1025
|
+
downloadedFileStream.on("error", function(err) {
|
|
1026
|
+
BStackLogger2.error(
|
|
1027
|
+
`Got Error while downloading cli binary file: ${err}`
|
|
1028
|
+
);
|
|
1029
|
+
endDownload(false, util.format(err));
|
|
1030
|
+
releaseLock?.();
|
|
1031
|
+
reject(err);
|
|
1032
|
+
});
|
|
1033
|
+
try {
|
|
1034
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
1035
|
+
const nodeStream = Readable.from([
|
|
1036
|
+
new Uint8Array(arrayBuffer)
|
|
1037
|
+
]);
|
|
1038
|
+
nodeStream.pipe(downloadedFileStream);
|
|
1039
|
+
_CLIUtils.downloadFileStream(
|
|
1040
|
+
downloadedFileStream,
|
|
1041
|
+
binaryName,
|
|
1042
|
+
zipFilePath,
|
|
1043
|
+
cliDir,
|
|
1044
|
+
(result) => {
|
|
1045
|
+
endDownload();
|
|
1046
|
+
releaseLock?.();
|
|
1047
|
+
resolve(result);
|
|
1048
|
+
},
|
|
1049
|
+
(err) => {
|
|
1050
|
+
endDownload(false, util.format(err));
|
|
1051
|
+
releaseLock?.();
|
|
1052
|
+
reject(err);
|
|
1053
|
+
}
|
|
1054
|
+
);
|
|
1055
|
+
} catch (err) {
|
|
1056
|
+
BStackLogger2.error(
|
|
1057
|
+
`Got Error in cli binary downloading request ${util.format(err)}`
|
|
1058
|
+
);
|
|
1059
|
+
endDownload(false, util.format(err));
|
|
1060
|
+
releaseLock?.();
|
|
1061
|
+
reject(err);
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
processDownload();
|
|
1065
|
+
});
|
|
1066
|
+
} catch (err) {
|
|
1067
|
+
releaseLock?.();
|
|
1068
|
+
endDownload(false, util.format(err));
|
|
1069
|
+
BStackLogger2.debug(
|
|
1070
|
+
`Failed to download binary, Exception: ${util.format(err)}`
|
|
1071
|
+
);
|
|
1072
|
+
return null;
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
static downloadFileStream(downloadedFileStream, binaryName, zipFilePath, cliDir, resolve, reject) {
|
|
1076
|
+
downloadedFileStream.on("close", async function() {
|
|
1077
|
+
const yauzlOpenPromise = promisify(yauzl.open);
|
|
1078
|
+
try {
|
|
1079
|
+
const zipfile = await yauzlOpenPromise(zipFilePath, {
|
|
1080
|
+
lazyEntries: true
|
|
1081
|
+
});
|
|
1082
|
+
zipfile.readEntry();
|
|
1083
|
+
zipfile.on("entry", async (entry) => {
|
|
1084
|
+
if (!binaryName) {
|
|
1085
|
+
binaryName = entry.fileName;
|
|
1086
|
+
}
|
|
1087
|
+
if (/\/$/.test(entry.fileName)) {
|
|
1088
|
+
zipfile.readEntry();
|
|
1089
|
+
} else {
|
|
1090
|
+
const writeStream = fs3.createWriteStream(
|
|
1091
|
+
path3.join(cliDir, entry.fileName)
|
|
1092
|
+
);
|
|
1093
|
+
const openReadStreamPromise = promisify(
|
|
1094
|
+
zipfile.openReadStream
|
|
1095
|
+
).bind(zipfile);
|
|
1096
|
+
try {
|
|
1097
|
+
const readStream = await openReadStreamPromise(entry);
|
|
1098
|
+
readStream.on("end", function() {
|
|
1099
|
+
writeStream.close();
|
|
1100
|
+
zipfile.readEntry();
|
|
1101
|
+
});
|
|
1102
|
+
readStream.pipe(writeStream);
|
|
1103
|
+
} catch (zipErr) {
|
|
1104
|
+
reject(zipErr);
|
|
1105
|
+
}
|
|
1106
|
+
if (entry.fileName === binaryName) {
|
|
1107
|
+
zipfile.close();
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
});
|
|
1111
|
+
zipfile.on("error", (zipErr) => {
|
|
1112
|
+
reject(zipErr);
|
|
1113
|
+
});
|
|
1114
|
+
zipfile.once("end", () => {
|
|
1115
|
+
fsp.unlink(zipFilePath).catch(() => {
|
|
1116
|
+
BStackLogger2.warn(
|
|
1117
|
+
`Failed to delete zip file: ${zipFilePath}`
|
|
1118
|
+
);
|
|
1119
|
+
});
|
|
1120
|
+
fsp.chmod(`${cliDir}/${binaryName}`, "0755").then(() => {
|
|
1121
|
+
resolve(`${cliDir}/${binaryName}`);
|
|
1122
|
+
}).catch((err) => {
|
|
1123
|
+
reject(err);
|
|
1124
|
+
});
|
|
1125
|
+
zipfile.close();
|
|
1126
|
+
});
|
|
1127
|
+
} catch (err) {
|
|
1128
|
+
reject(err);
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
static getTestFrameworkDetail() {
|
|
1133
|
+
if (process.env.BROWSERSTACK_TEST_FRAMEWORK_DETAIL) {
|
|
1134
|
+
return JSON.parse(process.env.BROWSERSTACK_TEST_FRAMEWORK_DETAIL);
|
|
1135
|
+
}
|
|
1136
|
+
return this.testFrameworkDetail;
|
|
1137
|
+
}
|
|
1138
|
+
static getAutomationFrameworkDetail() {
|
|
1139
|
+
if (process.env.BROWSERSTACK_AUTOMATION_FRAMEWORK_DETAIL) {
|
|
1140
|
+
return JSON.parse(
|
|
1141
|
+
process.env.BROWSERSTACK_AUTOMATION_FRAMEWORK_DETAIL
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
return this.automationFrameworkDetail;
|
|
1145
|
+
}
|
|
1146
|
+
static setFrameworkDetail(testFramework, automationFramework) {
|
|
1147
|
+
if (!testFramework || !automationFramework) {
|
|
1148
|
+
BStackLogger2.debug(
|
|
1149
|
+
`Test or Automation framework not provided testFramework=${testFramework}, automationFramework=${automationFramework}`
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
this.testFrameworkDetail = {
|
|
1153
|
+
name: testFramework,
|
|
1154
|
+
version: { [testFramework]: _CLIUtils.getSdkVersion() }
|
|
1155
|
+
};
|
|
1156
|
+
this.automationFrameworkDetail = {
|
|
1157
|
+
name: automationFramework,
|
|
1158
|
+
version: { [automationFramework]: _CLIUtils.getSdkVersion() }
|
|
1159
|
+
};
|
|
1160
|
+
process.env.BROWSERSTACK_AUTOMATION_FRAMEWORK_DETAIL = JSON.stringify(
|
|
1161
|
+
this.automationFrameworkDetail
|
|
1162
|
+
);
|
|
1163
|
+
process.env.BROWSERSTACK_TEST_FRAMEWORK_DETAIL = JSON.stringify(
|
|
1164
|
+
this.testFrameworkDetail
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Get the current instance name using thread id and processId
|
|
1169
|
+
* @returns {string}
|
|
1170
|
+
*/
|
|
1171
|
+
static getCurrentInstanceName() {
|
|
1172
|
+
return `${process.pid}:${threadId}`;
|
|
1173
|
+
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Generate a unique client worker identifier combining thread ID and process ID.
|
|
1176
|
+
* This identifier is used to track worker-specific events and performance metrics
|
|
1177
|
+
* across distributed test execution. Format matches the Python SDK implementation
|
|
1178
|
+
* for consistency across SDKs.
|
|
1179
|
+
*
|
|
1180
|
+
* Format: "threadId-processId"
|
|
1181
|
+
*
|
|
1182
|
+
* @param context - Optional execution context with threadId and processId
|
|
1183
|
+
* @returns Worker ID string in format "threadId-processId"
|
|
1184
|
+
* @example
|
|
1185
|
+
* const workerId = CLIUtils.getClientWorkerId() // Returns "1-12345"
|
|
1186
|
+
* const workerId = CLIUtils.getClientWorkerId({ threadId: 123, processId: 456 }) // Returns "123-456"
|
|
1187
|
+
*/
|
|
1188
|
+
static getClientWorkerId(context) {
|
|
1189
|
+
const workerThreadId = context?.threadId?.toString() || threadId.toString();
|
|
1190
|
+
const workerProcessId = context?.processId?.toString() || process.pid.toString();
|
|
1191
|
+
return `${workerThreadId}-${workerProcessId}`;
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
*
|
|
1195
|
+
* @param {TestFrameworkState | AutomationFrameworkState} frameworkState
|
|
1196
|
+
* @param {HookState} hookState
|
|
1197
|
+
* @returns {string}
|
|
1198
|
+
*/
|
|
1199
|
+
static getHookRegistryKey(frameworkState, hookState) {
|
|
1200
|
+
return `${frameworkState}:${hookState}`;
|
|
1201
|
+
}
|
|
1202
|
+
static matchHookRegex(hookState) {
|
|
1203
|
+
const pattern = new RegExp(TestFrameworkConstants.HOOK_REGEX);
|
|
1204
|
+
return pattern.test(hookState);
|
|
1205
|
+
}
|
|
1206
|
+
static getObservabilityBuildTags(observabilityOptions, bstackBuildTag) {
|
|
1207
|
+
if (process.env.TEST_OBSERVABILITY_BUILD_TAG) {
|
|
1208
|
+
return process.env.TEST_OBSERVABILITY_BUILD_TAG.split(",");
|
|
1209
|
+
}
|
|
1210
|
+
if (observabilityOptions.buildTag) {
|
|
1211
|
+
return observabilityOptions.buildTag;
|
|
1212
|
+
}
|
|
1213
|
+
if (bstackBuildTag) {
|
|
1214
|
+
return [bstackBuildTag];
|
|
1215
|
+
}
|
|
1216
|
+
return [];
|
|
1217
|
+
}
|
|
1218
|
+
static checkCLISupportedFrameworks(framework) {
|
|
1219
|
+
if (framework === void 0) {
|
|
1220
|
+
return false;
|
|
1221
|
+
}
|
|
1222
|
+
return this.CLISupportedFrameworks.includes(framework);
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
|
|
291
1226
|
// src/instrumentation/performance/performance-tester.ts
|
|
292
1227
|
var PerformanceTester = class _PerformanceTester {
|
|
293
1228
|
static _observer;
|
|
294
1229
|
static _csvWriter;
|
|
295
1230
|
static _events = [];
|
|
296
1231
|
static _measuredEvents = [];
|
|
1232
|
+
static _hasStoppedGeneration = false;
|
|
1233
|
+
static _stopGenerateCallCount = 0;
|
|
297
1234
|
static started = false;
|
|
298
1235
|
static details = {};
|
|
299
1236
|
static eventsMap = {};
|
|
300
1237
|
static browser;
|
|
301
1238
|
static scenarioThatRan;
|
|
302
1239
|
static jsonReportDirName = "performance-report";
|
|
303
|
-
static jsonReportDirPath =
|
|
1240
|
+
static jsonReportDirPath = path4.join(process.cwd(), "logs", this.jsonReportDirName);
|
|
304
1241
|
static jsonReportFileName = `${this.jsonReportDirPath}/performance-report-${_PerformanceTester.getProcessId()}.json`;
|
|
305
1242
|
static startMonitoring(csvName = "performance-report.csv") {
|
|
306
|
-
if (!
|
|
307
|
-
|
|
1243
|
+
if (!fs4.existsSync(this.jsonReportDirPath)) {
|
|
1244
|
+
fs4.mkdirSync(this.jsonReportDirPath, { recursive: true });
|
|
308
1245
|
}
|
|
309
1246
|
this._observer = new PerformanceObserver((list) => {
|
|
310
1247
|
list.getEntries().filter((entry) => entry.entryType === "measure").forEach(
|
|
311
1248
|
(entry) => {
|
|
312
|
-
let finalEntry = entry;
|
|
313
|
-
|
|
1249
|
+
let finalEntry = entry.toJSON();
|
|
1250
|
+
try {
|
|
1251
|
+
if (typeof finalEntry.startTime === "number" && typeof performance.timeOrigin === "number") {
|
|
1252
|
+
const originalStartTime = finalEntry.startTime;
|
|
1253
|
+
finalEntry.startTime = performance.timeOrigin + finalEntry.startTime;
|
|
1254
|
+
BStackLogger.debug(`Timestamp conversion for ${entry.name}: ${originalStartTime} -> ${finalEntry.startTime} (timeOrigin: ${performance.timeOrigin})`);
|
|
1255
|
+
}
|
|
1256
|
+
} catch (e) {
|
|
1257
|
+
BStackLogger.debug(`Error converting startTime to epoch: ${util2.format(e)}`);
|
|
1258
|
+
}
|
|
314
1259
|
if (this.details[entry.name]) {
|
|
315
1260
|
finalEntry = Object.assign(finalEntry, this.details[entry.name]);
|
|
316
1261
|
}
|
|
@@ -356,28 +1301,26 @@ var PerformanceTester = class _PerformanceTester {
|
|
|
356
1301
|
if (!this.started) {
|
|
357
1302
|
return;
|
|
358
1303
|
}
|
|
359
|
-
await _PerformanceTester.sleep(PERF_METRICS_WAIT_TIME);
|
|
360
1304
|
try {
|
|
361
1305
|
const eventsJson = JSON.stringify(this._measuredEvents);
|
|
362
1306
|
const finalJSONStr = eventsJson.slice(1, -1) + ",";
|
|
363
1307
|
await fsPromise.appendFile(this.jsonReportFileName, finalJSONStr);
|
|
364
1308
|
} catch (er) {
|
|
365
|
-
BStackLogger.debug(`Failed to write events of the worker to ${this.jsonReportFileName}: ${
|
|
1309
|
+
BStackLogger.debug(`Failed to write events of the worker to ${this.jsonReportFileName}: ${util2.format(er)}`);
|
|
366
1310
|
}
|
|
367
1311
|
this._observer.disconnect();
|
|
368
1312
|
if (!process.env[PERF_MEASUREMENT_ENV]) {
|
|
369
1313
|
return;
|
|
370
1314
|
}
|
|
371
|
-
await _PerformanceTester.sleep(PERF_METRICS_WAIT_TIME);
|
|
372
1315
|
this.started = false;
|
|
373
1316
|
this.generateCSV(this._events);
|
|
374
1317
|
const content = this.generateReport(this._events);
|
|
375
|
-
const dir =
|
|
1318
|
+
const dir = path4.join(process.cwd(), filename);
|
|
376
1319
|
try {
|
|
377
1320
|
await fsPromise.writeFile(dir, content);
|
|
378
|
-
BStackLogger.info(`Performance report is at ${
|
|
1321
|
+
BStackLogger.info(`Performance report is at ${path4}`);
|
|
379
1322
|
} catch (err) {
|
|
380
|
-
BStackLogger.error(`Error in writing html ${
|
|
1323
|
+
BStackLogger.error(`Error in writing html ${util2.format(err)}`);
|
|
381
1324
|
}
|
|
382
1325
|
}
|
|
383
1326
|
static generateReport(entries) {
|
|
@@ -437,27 +1380,71 @@ var PerformanceTester = class _PerformanceTester {
|
|
|
437
1380
|
if (!this.started || !this.isEnabled()) {
|
|
438
1381
|
return fn.apply(thisArg, args);
|
|
439
1382
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
1383
|
+
const uniqueId = `${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
1384
|
+
const startMark = `${label}-start-${uniqueId}`;
|
|
1385
|
+
const endMark = `${label}-end-${uniqueId}`;
|
|
1386
|
+
performance.mark(startMark);
|
|
1387
|
+
const detailsWithContext = {
|
|
1388
|
+
...details,
|
|
1389
|
+
measurementId: uniqueId
|
|
1390
|
+
};
|
|
444
1391
|
try {
|
|
445
1392
|
const returnVal = fn.apply(thisArg, args);
|
|
446
1393
|
if (returnVal instanceof Promise) {
|
|
447
1394
|
return new Promise((resolve, reject) => {
|
|
448
1395
|
returnVal.then((v) => {
|
|
449
|
-
|
|
1396
|
+
performance.mark(endMark);
|
|
1397
|
+
performance.measure(label, startMark, endMark);
|
|
1398
|
+
this.details[label] = Object.assign({
|
|
1399
|
+
success: true,
|
|
1400
|
+
failure: void 0
|
|
1401
|
+
}, Object.assign(Object.assign({
|
|
1402
|
+
clientWorkerId: _PerformanceTester.getClientWorkerId(),
|
|
1403
|
+
worker: _PerformanceTester.getProcessId(),
|
|
1404
|
+
platform: _PerformanceTester.browser?.sessionId,
|
|
1405
|
+
testName: _PerformanceTester.scenarioThatRan?.pop()
|
|
1406
|
+
}, detailsWithContext), this.details[label] || {}));
|
|
450
1407
|
resolve(v);
|
|
451
1408
|
}).catch((e) => {
|
|
452
|
-
|
|
1409
|
+
performance.mark(endMark);
|
|
1410
|
+
performance.measure(label, startMark, endMark);
|
|
1411
|
+
this.details[label] = Object.assign({
|
|
1412
|
+
success: false,
|
|
1413
|
+
failure: util2.format(e)
|
|
1414
|
+
}, Object.assign(Object.assign({
|
|
1415
|
+
clientWorkerId: _PerformanceTester.getClientWorkerId(),
|
|
1416
|
+
worker: _PerformanceTester.getProcessId(),
|
|
1417
|
+
platform: _PerformanceTester.browser?.sessionId,
|
|
1418
|
+
testName: _PerformanceTester.scenarioThatRan?.pop()
|
|
1419
|
+
}, detailsWithContext), this.details[label] || {}));
|
|
453
1420
|
reject(e);
|
|
454
1421
|
});
|
|
455
1422
|
});
|
|
456
1423
|
}
|
|
457
|
-
|
|
1424
|
+
performance.mark(endMark);
|
|
1425
|
+
performance.measure(label, startMark, endMark);
|
|
1426
|
+
this.details[label] = Object.assign({
|
|
1427
|
+
success: true,
|
|
1428
|
+
failure: void 0
|
|
1429
|
+
}, Object.assign(Object.assign({
|
|
1430
|
+
clientWorkerId: _PerformanceTester.getClientWorkerId(),
|
|
1431
|
+
worker: _PerformanceTester.getProcessId(),
|
|
1432
|
+
platform: _PerformanceTester.browser?.sessionId,
|
|
1433
|
+
testName: _PerformanceTester.scenarioThatRan?.pop()
|
|
1434
|
+
}, detailsWithContext), this.details[label] || {}));
|
|
458
1435
|
return returnVal;
|
|
459
1436
|
} catch (er) {
|
|
460
|
-
|
|
1437
|
+
performance.mark(endMark);
|
|
1438
|
+
performance.measure(label, startMark, endMark);
|
|
1439
|
+
this.details[label] = Object.assign({
|
|
1440
|
+
success: false,
|
|
1441
|
+
failure: util2.format(er)
|
|
1442
|
+
}, Object.assign(Object.assign({
|
|
1443
|
+
clientWorkerId: _PerformanceTester.getClientWorkerId(),
|
|
1444
|
+
worker: _PerformanceTester.getProcessId(),
|
|
1445
|
+
platform: _PerformanceTester.browser?.sessionId,
|
|
1446
|
+
testName: _PerformanceTester.scenarioThatRan?.pop()
|
|
1447
|
+
}, detailsWithContext), this.details[label] || {}));
|
|
461
1448
|
throw er;
|
|
462
1449
|
}
|
|
463
1450
|
}
|
|
@@ -472,26 +1459,62 @@ var PerformanceTester = class _PerformanceTester {
|
|
|
472
1459
|
static end(event, success = true, failure, details = {}) {
|
|
473
1460
|
performance.mark(event + "-end");
|
|
474
1461
|
performance.measure(event, event + "-start", event + "-end");
|
|
475
|
-
this.details[event] = Object.assign({ success, failure:
|
|
1462
|
+
this.details[event] = Object.assign({ success, failure: util2.format(failure) }, Object.assign(Object.assign({
|
|
1463
|
+
clientWorkerId: _PerformanceTester.getClientWorkerId(),
|
|
476
1464
|
worker: _PerformanceTester.getProcessId(),
|
|
477
1465
|
platform: _PerformanceTester.browser?.sessionId,
|
|
478
1466
|
testName: _PerformanceTester.scenarioThatRan && _PerformanceTester.scenarioThatRan[_PerformanceTester.scenarioThatRan.length - 1]
|
|
479
1467
|
}, details), this.details[event] || {}));
|
|
480
1468
|
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Get client worker ID in format "threadId-processId".
|
|
1471
|
+
* This method provides a consistent identifier across the SDK for tracking
|
|
1472
|
+
* worker-specific events and performance metrics.
|
|
1473
|
+
*
|
|
1474
|
+
* @returns Worker ID string in format "threadId-processId"
|
|
1475
|
+
*/
|
|
1476
|
+
static getClientWorkerId() {
|
|
1477
|
+
return CLIUtils.getClientWorkerId();
|
|
1478
|
+
}
|
|
481
1479
|
static getProcessId() {
|
|
482
1480
|
return `${process.pid}-${worker.threadId}`;
|
|
483
1481
|
}
|
|
484
1482
|
static sleep = (ms = 100) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
485
1483
|
static async uploadEventsData() {
|
|
1484
|
+
this.start(EVENTS.SDK_SEND_KEY_METRICS);
|
|
486
1485
|
try {
|
|
1486
|
+
const workerId = `${process.pid}`;
|
|
1487
|
+
BStackLogger.debug(`[Performance Upload] Starting upload for worker ${workerId}`);
|
|
1488
|
+
this.start(EVENTS.SDK_KEY_METRICS_PREPARATION);
|
|
487
1489
|
let measures = [];
|
|
488
1490
|
if (await fsPromise.access(this.jsonReportDirPath).then(() => true).catch(() => false)) {
|
|
489
|
-
const files = (await fsPromise.readdir(this.jsonReportDirPath)).map((file) =>
|
|
1491
|
+
const files = (await fsPromise.readdir(this.jsonReportDirPath)).map((file) => path4.resolve(this.jsonReportDirPath, file));
|
|
490
1492
|
measures = (await Promise.all(files.map((file) => fsPromise.readFile(file, "utf-8")))).map((el) => `[${el.slice(0, -1)}]`).map((el) => JSON.parse(el)).flat();
|
|
491
1493
|
}
|
|
1494
|
+
BStackLogger.debug(`[Performance Upload] Total events from files: ${measures.length}`);
|
|
492
1495
|
if (this._measuredEvents.length > 0) {
|
|
493
|
-
|
|
1496
|
+
BStackLogger.debug(`[Performance Upload] Adding ${this._measuredEvents.length} in-memory events`);
|
|
1497
|
+
measures = measures.concat(
|
|
1498
|
+
this._measuredEvents.map((e) => {
|
|
1499
|
+
if (typeof e.toJSON === "function") {
|
|
1500
|
+
return e.toJSON();
|
|
1501
|
+
}
|
|
1502
|
+
return e;
|
|
1503
|
+
})
|
|
1504
|
+
);
|
|
494
1505
|
}
|
|
1506
|
+
const ensureEpochTimes = (arr) => {
|
|
1507
|
+
const now = Date.now();
|
|
1508
|
+
const cutoff = 1e12;
|
|
1509
|
+
const timeOrigin = typeof performance !== "undefined" && typeof performance.timeOrigin === "number" ? performance.timeOrigin : now - process.uptime() * 1e3;
|
|
1510
|
+
return arr.map((entry) => {
|
|
1511
|
+
if (typeof entry.startTime === "number" && entry.startTime < cutoff) {
|
|
1512
|
+
entry.startTime = timeOrigin + entry.startTime;
|
|
1513
|
+
}
|
|
1514
|
+
return entry;
|
|
1515
|
+
});
|
|
1516
|
+
};
|
|
1517
|
+
measures = ensureEpochTimes(measures);
|
|
495
1518
|
const date = /* @__PURE__ */ new Date();
|
|
496
1519
|
const options = {
|
|
497
1520
|
timeZone: "UTC",
|
|
@@ -506,6 +1529,7 @@ var PerformanceTester = class _PerformanceTester {
|
|
|
506
1529
|
hour12: false
|
|
507
1530
|
};
|
|
508
1531
|
const formattedDate = new Intl.DateTimeFormat("en-GB", options).formatToParts(date).map(({ type: type3, value }) => type3 === "timeZoneName" ? "Z" : value).join("").replace(",", "T");
|
|
1532
|
+
this.end(EVENTS.SDK_KEY_METRICS_PREPARATION, true);
|
|
509
1533
|
const payload = {
|
|
510
1534
|
event_type: "sdk_events",
|
|
511
1535
|
data: {
|
|
@@ -515,10 +1539,10 @@ var PerformanceTester = class _PerformanceTester {
|
|
|
515
1539
|
user_data: process.env.PERF_USER_NAME,
|
|
516
1540
|
host_info: JSON.stringify({
|
|
517
1541
|
hostname: hostname(),
|
|
518
|
-
platform:
|
|
1542
|
+
platform: platform2(),
|
|
519
1543
|
type: type(),
|
|
520
1544
|
version: version(),
|
|
521
|
-
arch:
|
|
1545
|
+
arch: arch2()
|
|
522
1546
|
}),
|
|
523
1547
|
event_json: { measures, sdkRunId: process.env.SDK_RUN_ID }
|
|
524
1548
|
}
|
|
@@ -530,111 +1554,27 @@ var PerformanceTester = class _PerformanceTester {
|
|
|
530
1554
|
},
|
|
531
1555
|
body: JSON.stringify(payload)
|
|
532
1556
|
});
|
|
533
|
-
BStackLogger.debug(`Successfully uploaded
|
|
1557
|
+
BStackLogger.debug(`[Performance Upload] Successfully uploaded to EDS: ${util2.format(await result.text())}`);
|
|
1558
|
+
this.end(EVENTS.SDK_SEND_KEY_METRICS, true);
|
|
534
1559
|
} catch (er) {
|
|
535
|
-
BStackLogger.debug(`Failed to upload
|
|
1560
|
+
BStackLogger.debug(`[Performance Upload] Failed to upload events: ${util2.format(er)}`);
|
|
1561
|
+
this.end(EVENTS.SDK_KEY_METRICS_PREPARATION, false, er);
|
|
1562
|
+
this.end(EVENTS.SDK_SEND_KEY_METRICS, false, er);
|
|
536
1563
|
}
|
|
537
1564
|
try {
|
|
538
1565
|
if (await fsPromise.access(this.jsonReportDirPath).then(() => true, () => false)) {
|
|
539
1566
|
const files = await fsPromise.readdir(this.jsonReportDirPath);
|
|
540
1567
|
for (const file of files) {
|
|
541
|
-
await fsPromise.unlink(
|
|
1568
|
+
await fsPromise.unlink(path4.join(this.jsonReportDirPath, file));
|
|
542
1569
|
}
|
|
1570
|
+
BStackLogger.debug(`[Performance Upload] Cleaned up ${files.length} temporary report files`);
|
|
543
1571
|
}
|
|
544
1572
|
} catch (er) {
|
|
545
|
-
BStackLogger.debug(`Failed to delete
|
|
1573
|
+
BStackLogger.debug(`[Performance Upload] Failed to delete temporary files: ${util2.format(er)}`);
|
|
546
1574
|
}
|
|
547
1575
|
}
|
|
548
1576
|
};
|
|
549
1577
|
|
|
550
|
-
// src/instrumentation/performance/constants.ts
|
|
551
|
-
var EVENTS = {
|
|
552
|
-
SDK_SETUP: "sdk:setup",
|
|
553
|
-
SDK_CLEANUP: "sdk:cleanup",
|
|
554
|
-
SDK_PRE_TEST: "sdk:pre-test",
|
|
555
|
-
SDK_TEST: "sdk:test",
|
|
556
|
-
SDK_POST_TEST: "sdk:post-test",
|
|
557
|
-
SDK_HOOK: "sdk:hook",
|
|
558
|
-
SDK_DRIVER: "sdk:driver",
|
|
559
|
-
SDK_A11Y: "sdk:a11y",
|
|
560
|
-
SDK_O11Y: "sdk:o11y",
|
|
561
|
-
SDK_AUTO_CAPTURE: "sdk:auto-capture",
|
|
562
|
-
SDK_PROXY_SETUP: "sdk:proxy-setup",
|
|
563
|
-
SDK_TESTHUB: "sdk:testhub",
|
|
564
|
-
SDK_AUTOMATE: "sdk:automate",
|
|
565
|
-
SDK_APP_AUTOMATE: "sdk:app-automate",
|
|
566
|
-
SDK_TURBOSCALE: "sdk:turboscale",
|
|
567
|
-
SDK_PERCY: "sdk:percy",
|
|
568
|
-
SDK_PRE_INITIALIZE: "sdk:driver:pre-initialization",
|
|
569
|
-
SDK_POST_INITIALIZE: "sdk:driver:post-initialization",
|
|
570
|
-
SDK_CLI_CHECK_UPDATE: "sdk:cli:check-update",
|
|
571
|
-
SDK_CLI_DOWNLOAD: "sdk:cli:download",
|
|
572
|
-
SDK_CLI_ON_BOOTSTRAP: "sdk:cli:on-bootstrap",
|
|
573
|
-
SDK_CLI_ON_CONNECT: "sdk:cli:on-connect",
|
|
574
|
-
SDK_CLI_START: "sdk:cli:start",
|
|
575
|
-
SDK_CLI_ON_STOP: "sdk:cli:on-stop",
|
|
576
|
-
SDK_CONNECT_BIN_SESSION: "sdk:connectBinSession",
|
|
577
|
-
SDK_START_BIN_SESSION: "sdk:startBinSession"
|
|
578
|
-
};
|
|
579
|
-
var TESTHUB_EVENTS = {
|
|
580
|
-
START: `${EVENTS.SDK_TESTHUB}:start`,
|
|
581
|
-
STOP: `${EVENTS.SDK_TESTHUB}:stop`
|
|
582
|
-
};
|
|
583
|
-
var AUTOMATE_EVENTS = {
|
|
584
|
-
KEEP_ALIVE: `${EVENTS.SDK_AUTOMATE}:keep-alive`,
|
|
585
|
-
HUB_MANAGEMENT: `${EVENTS.SDK_AUTOMATE}:hub-management`,
|
|
586
|
-
LOCAL_START: `${EVENTS.SDK_AUTOMATE}:local-start`,
|
|
587
|
-
LOCAL_STOP: `${EVENTS.SDK_AUTOMATE}:local-stop`,
|
|
588
|
-
DRIVER_MANAGE: `${EVENTS.SDK_AUTOMATE}:driver-manage`,
|
|
589
|
-
SESSION_NAME: `${EVENTS.SDK_AUTOMATE}:session-name`,
|
|
590
|
-
SESSION_STATUS: `${EVENTS.SDK_AUTOMATE}:session-status`,
|
|
591
|
-
SESSION_ANNOTATION: `${EVENTS.SDK_AUTOMATE}:session-annotation`,
|
|
592
|
-
IDLE_TIMEOUT: `${EVENTS.SDK_AUTOMATE}:idle-timeout`,
|
|
593
|
-
GENERATE_CI_ARTIFACT: `${EVENTS.SDK_AUTOMATE}:ci-artifacts`,
|
|
594
|
-
PRINT_BUILDLINK: `${EVENTS.SDK_AUTOMATE}:print-buildlink`
|
|
595
|
-
};
|
|
596
|
-
var A11Y_EVENTS = {
|
|
597
|
-
PERFORM_SCAN: `${EVENTS.SDK_A11Y}:driver-performscan`,
|
|
598
|
-
SAVE_RESULTS: `${EVENTS.SDK_A11Y}:save-results`,
|
|
599
|
-
GET_RESULTS: `${EVENTS.SDK_A11Y}:get-accessibility-results`,
|
|
600
|
-
GET_RESULTS_SUMMARY: `${EVENTS.SDK_A11Y}:get-accessibility-results-summary`
|
|
601
|
-
};
|
|
602
|
-
var PERCY_EVENTS = {
|
|
603
|
-
DOWNLOAD: `${EVENTS.SDK_PERCY}:download`,
|
|
604
|
-
SCREENSHOT: `${EVENTS.SDK_PERCY}:screenshot`,
|
|
605
|
-
START: `${EVENTS.SDK_PERCY}:start`,
|
|
606
|
-
STOP: `${EVENTS.SDK_PERCY}:stop`,
|
|
607
|
-
AUTO_CAPTURE: `${EVENTS.SDK_PERCY}:auto-capture`,
|
|
608
|
-
SNAPSHOT: `${EVENTS.SDK_PERCY}:snapshot`,
|
|
609
|
-
SCREENSHOT_APP: `${EVENTS.SDK_PERCY}:screenshot-app`
|
|
610
|
-
};
|
|
611
|
-
var O11Y_EVENTS = {
|
|
612
|
-
SYNC: `${EVENTS.SDK_O11Y}:sync`,
|
|
613
|
-
TAKE_SCREENSHOT: `${EVENTS.SDK_O11Y}:driver-takeScreenShot`,
|
|
614
|
-
PRINT_BUILDLINK: `${EVENTS.SDK_O11Y}:print-buildlink`
|
|
615
|
-
};
|
|
616
|
-
var HOOK_EVENTS = {
|
|
617
|
-
BEFORE_EACH: `${EVENTS.SDK_HOOK}:before-each`,
|
|
618
|
-
AFTER_EACH: `${EVENTS.SDK_HOOK}:after-each`,
|
|
619
|
-
AFTER_ALL: `${EVENTS.SDK_HOOK}:after-all`,
|
|
620
|
-
BEFORE_ALL: `${EVENTS.SDK_HOOK}:before-all`,
|
|
621
|
-
BEFORE: `${EVENTS.SDK_HOOK}:before`,
|
|
622
|
-
AFTER: `${EVENTS.SDK_HOOK}:after`
|
|
623
|
-
};
|
|
624
|
-
var TURBOSCALE_EVENTS = {
|
|
625
|
-
HUB_MANAGEMENT: `${EVENTS.SDK_TURBOSCALE}:hub-management`,
|
|
626
|
-
PRINT_BUILDLINK: `${EVENTS.SDK_TURBOSCALE}:print-buildlink`
|
|
627
|
-
};
|
|
628
|
-
var APP_AUTOMATE_EVENTS = {
|
|
629
|
-
APP_UPLOAD: `${EVENTS.SDK_APP_AUTOMATE}:app-upload`
|
|
630
|
-
};
|
|
631
|
-
var DRIVER_EVENT = {
|
|
632
|
-
QUIT: `${EVENTS.SDK_DRIVER}:quit`,
|
|
633
|
-
GET: `${EVENTS.SDK_DRIVER}:get`,
|
|
634
|
-
PRE_EXECUTE: `${EVENTS.SDK_DRIVER}:pre-execute`,
|
|
635
|
-
POST_EXECUTE: `${EVENTS.SDK_DRIVER}:post-execute`
|
|
636
|
-
};
|
|
637
|
-
|
|
638
1578
|
// src/testHub/utils.ts
|
|
639
1579
|
var handleErrorForObservability = (error) => {
|
|
640
1580
|
process.env[BROWSERSTACK_OBSERVABILITY] = "false";
|
|
@@ -680,7 +1620,7 @@ var getProductMapForBuildStartCall = (config, accessibilityAutomation) => {
|
|
|
680
1620
|
};
|
|
681
1621
|
|
|
682
1622
|
// src/testorchestration/testorcherstrationutils.ts
|
|
683
|
-
import
|
|
1623
|
+
import fs5 from "node:fs";
|
|
684
1624
|
var RUN_SMART_SELECTION = "runSmartSelection";
|
|
685
1625
|
var ALLOWED_ORCHESTRATION_KEYS = [
|
|
686
1626
|
RUN_SMART_SELECTION
|
|
@@ -910,13 +1850,13 @@ var OrchestrationUtils = class _OrchestrationUtils {
|
|
|
910
1850
|
* @returns Formatted list of repository configurations
|
|
911
1851
|
*/
|
|
912
1852
|
_loadSourceFromFile(filePath) {
|
|
913
|
-
if (!
|
|
1853
|
+
if (!fs5.existsSync(filePath)) {
|
|
914
1854
|
BStackLogger.error(`Source file '${filePath}' does not exist.`);
|
|
915
1855
|
return [];
|
|
916
1856
|
}
|
|
917
1857
|
let data = null;
|
|
918
1858
|
try {
|
|
919
|
-
const fileContent =
|
|
1859
|
+
const fileContent = fs5.readFileSync(filePath, "utf8");
|
|
920
1860
|
data = JSON.parse(fileContent);
|
|
921
1861
|
} catch (error) {
|
|
922
1862
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1489,12 +2429,12 @@ var UsageStats = class _UsageStats {
|
|
|
1489
2429
|
var usageStats_default = UsageStats;
|
|
1490
2430
|
|
|
1491
2431
|
// src/util.ts
|
|
1492
|
-
import
|
|
2432
|
+
import { create } from "tar";
|
|
1493
2433
|
import { fileFromPath } from "formdata-node/file-from-path";
|
|
1494
2434
|
|
|
1495
2435
|
// src/scripts/accessibility-scripts.ts
|
|
1496
|
-
import
|
|
1497
|
-
import
|
|
2436
|
+
import path5 from "node:path";
|
|
2437
|
+
import fs6 from "node:fs";
|
|
1498
2438
|
import os from "node:os";
|
|
1499
2439
|
var AccessibilityScripts = class _AccessibilityScripts {
|
|
1500
2440
|
static instance = null;
|
|
@@ -1509,7 +2449,7 @@ var AccessibilityScripts = class _AccessibilityScripts {
|
|
|
1509
2449
|
// don't allow to create instances from it other than through `checkAndGetInstance`
|
|
1510
2450
|
constructor() {
|
|
1511
2451
|
this.browserstackFolderPath = this.getWritableDir();
|
|
1512
|
-
this.commandsPath =
|
|
2452
|
+
this.commandsPath = path5.join(this.browserstackFolderPath, "commands.json");
|
|
1513
2453
|
}
|
|
1514
2454
|
static checkAndGetInstance() {
|
|
1515
2455
|
if (!_AccessibilityScripts.instance) {
|
|
@@ -1521,17 +2461,17 @@ var AccessibilityScripts = class _AccessibilityScripts {
|
|
|
1521
2461
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
1522
2462
|
getWritableDir() {
|
|
1523
2463
|
const orderedPaths = [
|
|
1524
|
-
|
|
2464
|
+
path5.join(os.homedir(), ".browserstack"),
|
|
1525
2465
|
process.cwd(),
|
|
1526
2466
|
os.tmpdir()
|
|
1527
2467
|
];
|
|
1528
2468
|
for (const orderedPath of orderedPaths) {
|
|
1529
2469
|
try {
|
|
1530
|
-
if (
|
|
1531
|
-
|
|
2470
|
+
if (fs6.existsSync(orderedPath)) {
|
|
2471
|
+
fs6.accessSync(orderedPath);
|
|
1532
2472
|
return orderedPath;
|
|
1533
2473
|
}
|
|
1534
|
-
|
|
2474
|
+
fs6.mkdirSync(orderedPath, { recursive: true });
|
|
1535
2475
|
return orderedPath;
|
|
1536
2476
|
} catch (error) {
|
|
1537
2477
|
}
|
|
@@ -1540,8 +2480,8 @@ var AccessibilityScripts = class _AccessibilityScripts {
|
|
|
1540
2480
|
}
|
|
1541
2481
|
readFromExistingFile() {
|
|
1542
2482
|
try {
|
|
1543
|
-
if (
|
|
1544
|
-
const data =
|
|
2483
|
+
if (fs6.existsSync(this.commandsPath)) {
|
|
2484
|
+
const data = fs6.readFileSync(this.commandsPath, "utf8");
|
|
1545
2485
|
if (data) {
|
|
1546
2486
|
this.update(JSON.parse(data));
|
|
1547
2487
|
}
|
|
@@ -1564,10 +2504,10 @@ var AccessibilityScripts = class _AccessibilityScripts {
|
|
|
1564
2504
|
}
|
|
1565
2505
|
}
|
|
1566
2506
|
store() {
|
|
1567
|
-
if (!
|
|
1568
|
-
|
|
2507
|
+
if (!fs6.existsSync(this.browserstackFolderPath)) {
|
|
2508
|
+
fs6.mkdirSync(this.browserstackFolderPath);
|
|
1569
2509
|
}
|
|
1570
|
-
|
|
2510
|
+
fs6.writeFileSync(this.commandsPath, JSON.stringify({
|
|
1571
2511
|
commands: this.commandsToWrap,
|
|
1572
2512
|
scripts: {
|
|
1573
2513
|
scan: this.performScan,
|
|
@@ -1582,7 +2522,7 @@ var AccessibilityScripts = class _AccessibilityScripts {
|
|
|
1582
2522
|
var accessibility_scripts_default = AccessibilityScripts.checkAndGetInstance();
|
|
1583
2523
|
|
|
1584
2524
|
// src/util.ts
|
|
1585
|
-
var pGitconfig =
|
|
2525
|
+
var pGitconfig = promisify2(gitconfig);
|
|
1586
2526
|
var DEFAULT_REQUEST_CONFIG = {
|
|
1587
2527
|
headers: {
|
|
1588
2528
|
"Content-Type": "application/json",
|
|
@@ -1603,7 +2543,7 @@ function processError(error, fn, args) {
|
|
|
1603
2543
|
try {
|
|
1604
2544
|
argsString = JSON.stringify(args);
|
|
1605
2545
|
} catch {
|
|
1606
|
-
argsString =
|
|
2546
|
+
argsString = util3.inspect(args, { depth: 2 });
|
|
1607
2547
|
}
|
|
1608
2548
|
CrashReporter.uploadCrashReport(`Error in executing ${fn.name} with args ${argsString} : ${error}`, error && error.stack || "unknown error");
|
|
1609
2549
|
}
|
|
@@ -1699,10 +2639,10 @@ var launchTestSession = PerformanceTester.measureWrapper(TESTHUB_EVENTS.START, o
|
|
|
1699
2639
|
tags: getObservabilityBuildTags(options, bsConfig.buildTag),
|
|
1700
2640
|
host_info: {
|
|
1701
2641
|
hostname: hostname2(),
|
|
1702
|
-
platform:
|
|
2642
|
+
platform: platform3(),
|
|
1703
2643
|
type: type2(),
|
|
1704
2644
|
version: version2(),
|
|
1705
|
-
arch:
|
|
2645
|
+
arch: arch3()
|
|
1706
2646
|
},
|
|
1707
2647
|
ci_info: getCiInfo(),
|
|
1708
2648
|
build_run_identifier: process.env.BROWSERSTACK_BUILD_RUN_IDENTIFIER,
|
|
@@ -1815,7 +2755,7 @@ var performA11yScan = async (isAppAutomate, browser, isBrowserStackSession, isAc
|
|
|
1815
2755
|
try {
|
|
1816
2756
|
if (isAppAccessibilityAutomationSession(isAccessibility, isAppAutomate)) {
|
|
1817
2757
|
const results = await browser.execute(formatString(accessibility_scripts_default.performScan, JSON.stringify(_getParamsForAppAccessibility(commandName))), {});
|
|
1818
|
-
BStackLogger.debug(
|
|
2758
|
+
BStackLogger.debug(util3.format(results));
|
|
1819
2759
|
return results;
|
|
1820
2760
|
}
|
|
1821
2761
|
if (accessibility_scripts_default.performScan) {
|
|
@@ -2261,7 +3201,7 @@ function getObservabilityBuild(options, bstackBuildName) {
|
|
|
2261
3201
|
if (options.testObservabilityOptions && options.testObservabilityOptions.buildName) {
|
|
2262
3202
|
return options.testObservabilityOptions.buildName;
|
|
2263
3203
|
}
|
|
2264
|
-
return bstackBuildName ||
|
|
3204
|
+
return bstackBuildName || path6.basename(path6.resolve(process.cwd()));
|
|
2265
3205
|
}
|
|
2266
3206
|
function getObservabilityBuildTags(options, bstackBuildTag) {
|
|
2267
3207
|
if (process.env.TEST_OBSERVABILITY_BUILD_TAG) {
|
|
@@ -2275,9 +3215,24 @@ function getObservabilityBuildTags(options, bstackBuildTag) {
|
|
|
2275
3215
|
}
|
|
2276
3216
|
return [];
|
|
2277
3217
|
}
|
|
3218
|
+
function getBrowserStackUser(config) {
|
|
3219
|
+
if (process.env.BROWSERSTACK_USERNAME) {
|
|
3220
|
+
return process.env.BROWSERSTACK_USERNAME;
|
|
3221
|
+
}
|
|
3222
|
+
return config.user;
|
|
3223
|
+
}
|
|
3224
|
+
function getBrowserStackKey(config) {
|
|
3225
|
+
if (process.env.BROWSERSTACK_ACCESS_KEY) {
|
|
3226
|
+
return process.env.BROWSERSTACK_ACCESS_KEY;
|
|
3227
|
+
}
|
|
3228
|
+
return config.key;
|
|
3229
|
+
}
|
|
2278
3230
|
function isTrue(value) {
|
|
2279
3231
|
return (value + "").toLowerCase() === "true";
|
|
2280
3232
|
}
|
|
3233
|
+
function isFalse(value) {
|
|
3234
|
+
return (value + "").toLowerCase() === "false";
|
|
3235
|
+
}
|
|
2281
3236
|
var patchConsoleLogs = o11yErrorHandler(() => {
|
|
2282
3237
|
const BSTestOpsPatcher = new logPatcher_default({});
|
|
2283
3238
|
Object.keys(consoleHolder).forEach((method) => {
|
|
@@ -2472,21 +3427,66 @@ function isValidEnabledValue(value) {
|
|
|
2472
3427
|
}
|
|
2473
3428
|
return false;
|
|
2474
3429
|
}
|
|
3430
|
+
function isNullOrEmpty(string) {
|
|
3431
|
+
return !string || string.trim() === "";
|
|
3432
|
+
}
|
|
3433
|
+
function isHash(entity) {
|
|
3434
|
+
return Boolean(entity && typeof entity === "object" && !Array.isArray(entity));
|
|
3435
|
+
}
|
|
3436
|
+
function nestedKeyValue(hash, keys) {
|
|
3437
|
+
return keys.reduce((hash2, key) => isHash(hash2) ? hash2[key] : void 0, hash);
|
|
3438
|
+
}
|
|
3439
|
+
function removeDir(dir) {
|
|
3440
|
+
const list = fs7.readdirSync(dir);
|
|
3441
|
+
for (let i = 0; i < list.length; i++) {
|
|
3442
|
+
const filename = path6.join(dir, list[i]);
|
|
3443
|
+
const stat = fs7.statSync(filename);
|
|
3444
|
+
if (filename === "." || filename === "..") {
|
|
3445
|
+
} else if (stat.isDirectory()) {
|
|
3446
|
+
removeDir(filename);
|
|
3447
|
+
} else {
|
|
3448
|
+
fs7.unlinkSync(filename);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
fs7.rmdirSync(dir);
|
|
3452
|
+
}
|
|
3453
|
+
function createDir(dir) {
|
|
3454
|
+
if (fs7.existsSync(dir)) {
|
|
3455
|
+
removeDir(dir);
|
|
3456
|
+
}
|
|
3457
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
3458
|
+
}
|
|
3459
|
+
function isWritable(dirPath) {
|
|
3460
|
+
try {
|
|
3461
|
+
fs7.accessSync(dirPath, fs7.constants.W_OK);
|
|
3462
|
+
return true;
|
|
3463
|
+
} catch {
|
|
3464
|
+
return false;
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
function setReadWriteAccess(dirPath) {
|
|
3468
|
+
try {
|
|
3469
|
+
fs7.chmodSync(dirPath, 438);
|
|
3470
|
+
BStackLogger.debug(`Directory ${dirPath} is now read/write accessible.`);
|
|
3471
|
+
} catch (err) {
|
|
3472
|
+
BStackLogger.error(`Failed to set directory access: ${err.stack}`);
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
2475
3475
|
|
|
2476
3476
|
// src/cleanup.ts
|
|
2477
|
-
import
|
|
2478
|
-
import
|
|
3477
|
+
import fs10 from "node:fs";
|
|
3478
|
+
import util5 from "node:util";
|
|
2479
3479
|
|
|
2480
3480
|
// src/instrumentation/funnelInstrumentation.ts
|
|
2481
3481
|
import os2 from "node:os";
|
|
2482
|
-
import
|
|
2483
|
-
import
|
|
2484
|
-
import
|
|
3482
|
+
import util4, { format as format2 } from "node:util";
|
|
3483
|
+
import path8 from "node:path";
|
|
3484
|
+
import fs9 from "node:fs";
|
|
2485
3485
|
|
|
2486
3486
|
// src/data-store.ts
|
|
2487
|
-
import
|
|
2488
|
-
import
|
|
2489
|
-
var workersDataDirPath =
|
|
3487
|
+
import path7 from "node:path";
|
|
3488
|
+
import fs8 from "node:fs";
|
|
3489
|
+
var workersDataDirPath = path7.join(process.cwd(), "logs", "worker_data");
|
|
2490
3490
|
|
|
2491
3491
|
// src/instrumentation/funnelInstrumentation.ts
|
|
2492
3492
|
function redactCredentialsFromFunnelData(data) {
|
|
@@ -2503,7 +3503,7 @@ function redactCredentialsFromFunnelData(data) {
|
|
|
2503
3503
|
async function fireFunnelRequest(data) {
|
|
2504
3504
|
const { userName, accessKey } = data;
|
|
2505
3505
|
redactCredentialsFromFunnelData(data);
|
|
2506
|
-
BStackLogger.debug("Sending SDK event with data " +
|
|
3506
|
+
BStackLogger.debug("Sending SDK event with data " + util4.inspect(data, { depth: 6 }));
|
|
2507
3507
|
const encodedAuth = Buffer.from(`${userName}:${accessKey}`, "utf8").toString("base64");
|
|
2508
3508
|
const response = await fetchWrap(APIUtils.FUNNEL_INSTRUMENTATION_URL, {
|
|
2509
3509
|
method: "POST",
|
|
@@ -2542,7 +3542,7 @@ var BStackCleanup = class _BStackCleanup {
|
|
|
2542
3542
|
await PerformanceTester.uploadEventsData();
|
|
2543
3543
|
}
|
|
2544
3544
|
} catch (er) {
|
|
2545
|
-
BStackLogger.debug(`Error in sending events data ${
|
|
3545
|
+
BStackLogger.debug(`Error in sending events data ${util5.format(er)}`);
|
|
2546
3546
|
}
|
|
2547
3547
|
}
|
|
2548
3548
|
static async executeObservabilityCleanup(funnelData) {
|
|
@@ -2592,7 +3592,7 @@ Visit https://automation.browserstack.com/builds/${process.env[BROWSERSTACK_TEST
|
|
|
2592
3592
|
if (!filePath) {
|
|
2593
3593
|
return null;
|
|
2594
3594
|
}
|
|
2595
|
-
const content =
|
|
3595
|
+
const content = fs10.readFileSync(filePath, "utf8");
|
|
2596
3596
|
const data = JSON.parse(content);
|
|
2597
3597
|
this.removeFunnelDataFile(filePath);
|
|
2598
3598
|
return data;
|
|
@@ -2601,7 +3601,7 @@ Visit https://automation.browserstack.com/builds/${process.env[BROWSERSTACK_TEST
|
|
|
2601
3601
|
if (!filePath) {
|
|
2602
3602
|
return;
|
|
2603
3603
|
}
|
|
2604
|
-
|
|
3604
|
+
fs10.rmSync(filePath, { force: true });
|
|
2605
3605
|
}
|
|
2606
3606
|
};
|
|
2607
3607
|
void BStackCleanup.startCleanup();
|