@capillarytech/cap-ui-utils 3.1.1 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/e2e/constants/common.js +22 -0
  2. package/e2e/index.js +65 -0
  3. package/e2e/pages/common/base.page.js +13 -0
  4. package/e2e/pages/common/constant.js +5 -0
  5. package/e2e/pages/common/login.page.js +95 -0
  6. package/e2e/services/{lockService.ts → lockService.js} +63 -67
  7. package/e2e/utils/{antdVersionUtil.ts → antdVersionUtil.js} +24 -32
  8. package/e2e/utils/{automationBypassUtil.ts → automationBypassUtil.js} +11 -8
  9. package/e2e/utils/{debugModeUtil.ts → debugModeUtil.js} +9 -7
  10. package/e2e/utils/deletionRegistry.js +16 -0
  11. package/e2e/utils/{elementUtil.ts → elementUtil.js} +224 -296
  12. package/e2e/utils/expectUtil.js +40 -0
  13. package/e2e/utils/featureFlagUtil.js +25 -0
  14. package/e2e/utils/garudaDropdownUtil.js +34 -0
  15. package/e2e/utils/htmlEditorUtil.js +59 -0
  16. package/e2e/utils/logCollectorUtil.js +65 -0
  17. package/e2e/utils/{mockResponse.ts → mockResponse.js} +5 -2
  18. package/e2e/utils/{reportUploader.ts → reportUploader.js} +69 -66
  19. package/e2e/utils/{requestRecorderUtil.ts → requestRecorderUtil.js} +69 -97
  20. package/e2e/utils/{screenshotRecorderUtil.ts → screenshotRecorderUtil.js} +61 -82
  21. package/e2e/utils/setupMock.js +18 -0
  22. package/e2e/utils/unmatchedBracesUtil.js +87 -0
  23. package/e2e/utils/uploaders/fileServiceUploader.js +113 -0
  24. package/e2e/utils/uploaders/uploader.js +2 -0
  25. package/e2e/utils/virtualListUtil.js +74 -0
  26. package/package.json +1 -1
  27. package/e2e/constants/common.ts +0 -23
  28. package/e2e/index.ts +0 -30
  29. package/e2e/pages/common/base.page.ts +0 -11
  30. package/e2e/pages/common/constant.ts +0 -2
  31. package/e2e/pages/common/login.page.ts +0 -95
  32. package/e2e/utils/deletionRegistry.ts +0 -15
  33. package/e2e/utils/expectUtil.ts +0 -19
  34. package/e2e/utils/featureFlagUtil.ts +0 -30
  35. package/e2e/utils/garudaDropdownUtil.ts +0 -60
  36. package/e2e/utils/htmlEditorUtil.ts +0 -56
  37. package/e2e/utils/logCollectorUtil.ts +0 -59
  38. package/e2e/utils/setupMock.ts +0 -29
  39. package/e2e/utils/unmatchedBracesUtil.ts +0 -101
  40. package/e2e/utils/uploaders/fileServiceUploader.ts +0 -84
  41. package/e2e/utils/uploaders/uploader.ts +0 -20
  42. package/e2e/utils/virtualListUtil.ts +0 -115
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.maxLoginAttempts = exports.common = exports.WAIT_120S = exports.WAIT_90S = exports.WAIT_60S = exports.WAIT_30S = exports.FAILURE = exports.SUCCESS = exports.INFO = exports.SG_CLUSTERS = void 0;
4
+ // Singapore clusters that require special handling
5
+ exports.SG_CLUSTERS = ['sgcrm', 'sgcrmasia'];
6
+ exports.INFO = "🟡";
7
+ exports.SUCCESS = "🟢";
8
+ exports.FAILURE = "🔴";
9
+ exports.WAIT_30S = 30000;
10
+ exports.WAIT_60S = 60000;
11
+ exports.WAIT_90S = 90000;
12
+ exports.WAIT_120S = 120000;
13
+ exports.common = {
14
+ "cluster": process.env.cluster,
15
+ "validatorUrl": "http://127.0.0.1:4000",
16
+ "maxLoginAttempts": 3,
17
+ "sgClusters": exports.SG_CLUSTERS,
18
+ "isLoyaltyMigration": process.env.module === 'loyalty_migration',
19
+ };
20
+ // login.page.ts (shared) imports this by name; re-exported here so the toolkit
21
+ // stays free of the data-heavy constants/constants aggregator (org/auth/endpoints).
22
+ exports.maxLoginAttempts = exports.common.maxLoginAttempts;
package/e2e/index.js ADDED
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.loginPage = exports.BasePage = exports.lockService = exports.setupMock = exports.screenshotRecorderUtil = exports.requestRecorderUtil = exports.reportUploader = exports.logCollectorUtil = exports.dropdownUtil = exports.deletionRegistry = exports.debugMode = exports.automationBypass = exports.expectUtil = exports.antdVersion = exports.elementUtil = void 0;
21
+ /**
22
+ * Shared WDIO E2E toolkit — the framework-agnostic, data-free helpers that were
23
+ * duplicated byte-for-byte across the product repos (wdio-ui-automation,
24
+ * garuda-ui, and every future migrated repo).
25
+ *
26
+ * Two ways to consume:
27
+ * import { elementUtil, loginPage } from '@capillarytech/cap-ui-utils/e2e';
28
+ * import elementUtil from '@capillarytech/cap-ui-utils/e2e/utils/elementUtil';
29
+ *
30
+ * NOT included yet (data-coupled — they read the per-repo `cons` org/auth/endpoint
31
+ * data via constants/constants): apiUtil, helperUtil, cookieUtil, navigationUtil,
32
+ * appNavigationUtil, requestRecorderService, home.page, testAndPreviewFlow.page.
33
+ * These need a small config-injection seam before they can move — see README.
34
+ */
35
+ var elementUtil_1 = require("./utils/elementUtil");
36
+ Object.defineProperty(exports, "elementUtil", { enumerable: true, get: function () { return __importDefault(elementUtil_1).default; } });
37
+ var antdVersionUtil_1 = require("./utils/antdVersionUtil");
38
+ Object.defineProperty(exports, "antdVersion", { enumerable: true, get: function () { return __importDefault(antdVersionUtil_1).default; } });
39
+ var expectUtil_1 = require("./utils/expectUtil");
40
+ Object.defineProperty(exports, "expectUtil", { enumerable: true, get: function () { return __importDefault(expectUtil_1).default; } });
41
+ var automationBypassUtil_1 = require("./utils/automationBypassUtil");
42
+ Object.defineProperty(exports, "automationBypass", { enumerable: true, get: function () { return __importDefault(automationBypassUtil_1).default; } });
43
+ var debugModeUtil_1 = require("./utils/debugModeUtil");
44
+ Object.defineProperty(exports, "debugMode", { enumerable: true, get: function () { return __importDefault(debugModeUtil_1).default; } });
45
+ var deletionRegistry_1 = require("./utils/deletionRegistry");
46
+ Object.defineProperty(exports, "deletionRegistry", { enumerable: true, get: function () { return __importDefault(deletionRegistry_1).default; } });
47
+ var garudaDropdownUtil_1 = require("./utils/garudaDropdownUtil");
48
+ Object.defineProperty(exports, "dropdownUtil", { enumerable: true, get: function () { return __importDefault(garudaDropdownUtil_1).default; } });
49
+ var logCollectorUtil_1 = require("./utils/logCollectorUtil");
50
+ Object.defineProperty(exports, "logCollectorUtil", { enumerable: true, get: function () { return __importDefault(logCollectorUtil_1).default; } });
51
+ var reportUploader_1 = require("./utils/reportUploader");
52
+ Object.defineProperty(exports, "reportUploader", { enumerable: true, get: function () { return __importDefault(reportUploader_1).default; } });
53
+ var requestRecorderUtil_1 = require("./utils/requestRecorderUtil");
54
+ Object.defineProperty(exports, "requestRecorderUtil", { enumerable: true, get: function () { return __importDefault(requestRecorderUtil_1).default; } });
55
+ var screenshotRecorderUtil_1 = require("./utils/screenshotRecorderUtil");
56
+ Object.defineProperty(exports, "screenshotRecorderUtil", { enumerable: true, get: function () { return __importDefault(screenshotRecorderUtil_1).default; } });
57
+ var setupMock_1 = require("./utils/setupMock");
58
+ Object.defineProperty(exports, "setupMock", { enumerable: true, get: function () { return __importDefault(setupMock_1).default; } });
59
+ var lockService_1 = require("./services/lockService");
60
+ Object.defineProperty(exports, "lockService", { enumerable: true, get: function () { return __importDefault(lockService_1).default; } });
61
+ var base_page_1 = require("./pages/common/base.page");
62
+ Object.defineProperty(exports, "BasePage", { enumerable: true, get: function () { return __importDefault(base_page_1).default; } });
63
+ var login_page_1 = require("./pages/common/login.page");
64
+ Object.defineProperty(exports, "loginPage", { enumerable: true, get: function () { return __importDefault(login_page_1).default; } });
65
+ __exportStar(require("./constants/common"), exports);
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class BasePage {
4
+ async launchApplication(url) {
5
+ console.log('Browser Maximize');
6
+ browser.maximizeWindow();
7
+ console.log('Triggered navigate to', url);
8
+ await browser.navigateTo(url);
9
+ console.log('Navigated to URL: ', url);
10
+ }
11
+ async quitApplication() { await browser.closeWindow(); }
12
+ }
13
+ exports.default = BasePage;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WEBPUSH = exports.EMAIL = void 0;
4
+ exports.EMAIL = 'email';
5
+ exports.WEBPUSH = 'webpush';
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const base_page_1 = __importDefault(require("./base.page"));
7
+ const elementUtil_1 = __importDefault(require("../../utils/elementUtil"));
8
+ const common_1 = require("../../constants/common");
9
+ const wdio_commands_1 = __importDefault(require("@rpii/wdio-commands"));
10
+ class LoginPage extends base_page_1.default {
11
+ get isV2Login() { return process.env.V2_LOGIN === 'true'; }
12
+ get inputUserName() { return this.isV2Login ? $('#login-username') : $('#login_user'); }
13
+ get inputPassword() { return this.isV2Login ? $('#login-password') : $('#login_cred'); }
14
+ get buttonLogin() { return this.isV2Login ? $('//button[.="Sign in"]') : $('#c-login-btn'); }
15
+ get continueButton() { return $('//button[.="Continue"]'); }
16
+ // Post-login readiness marker. The MFE Navigation Host replaced the old
17
+ // Dashboard (which rendered ExplorePosts → "Explore features") with NewDashboard,
18
+ // so the sidebar shell testid is the reliable signal the home shell has mounted.
19
+ // Old text kept as a backward-compatible fallback for pre-MFE environments.
20
+ get exploreFeatures() { return $('//*[@data-testid="cap-navigation-spa-sidebar"] | //*[text()="Explore features"]'); }
21
+ async doLogin(url, username, password, loginTryCount = 0) {
22
+ try {
23
+ await browser.refresh();
24
+ await browser.pause(3000);
25
+ console.log('Browser launched');
26
+ await super.launchApplication(url);
27
+ if (loginTryCount > 0) {
28
+ let currentUrl = await browser.getUrl();
29
+ const isHome = currentUrl.includes("/home/ui");
30
+ if (isHome) {
31
+ try {
32
+ await (await this.exploreFeatures).waitForDisplayed({ timeout: 60000, interval: 5000 });
33
+ console.log('-----Logged In-----');
34
+ console.log('On retry login flow, /home/ui url and explore features text appeared after 60s probably, hence skipping entering username and password');
35
+ return;
36
+ }
37
+ catch (e) {
38
+ console.log('proceeding with retry');
39
+ }
40
+ }
41
+ }
42
+ for (let i = 0; i <= 2; i++) {
43
+ try {
44
+ await (await this.inputUserName).waitForDisplayed({ timeout: 60000 });
45
+ await this.inputUserName.waitForEnabled({ timeout: 30000, timeoutMsg: "thrown from dologin", interval: 1000 });
46
+ break;
47
+ }
48
+ catch (error) {
49
+ console.log('Failed to load login page- Refreshing browser');
50
+ await browser.refresh();
51
+ }
52
+ }
53
+ await elementUtil_1.default.enterText(await this.inputUserName, username);
54
+ if (this.isV2Login) {
55
+ await this.continueButton.waitForEnabled({ timeout: 10000 });
56
+ await elementUtil_1.default.elementClick(await this.continueButton);
57
+ await this.inputPassword.waitForDisplayed({ timeout: 30000 });
58
+ }
59
+ await elementUtil_1.default.enterText(await this.inputPassword, password);
60
+ await this.buttonLogin.waitForEnabled({ timeout: 10000 });
61
+ await elementUtil_1.default.elementClick(await this.buttonLogin);
62
+ await browser.waitUntil(async () => {
63
+ let currentUrl = await browser.getUrl();
64
+ return currentUrl.includes("/home/ui");
65
+ }, {
66
+ timeout: 60000,
67
+ timeoutMsg: 'expected url to include /home/ui',
68
+ interval: 1000,
69
+ });
70
+ console.log('-----Browsers current url contains home/ui after login-----');
71
+ await (await this.exploreFeatures).waitForDisplayed({ timeout: 60000, interval: 5000 });
72
+ console.log('-----Logged In-----');
73
+ }
74
+ catch (error) {
75
+ console.log('Throwed error from dologin, attempt #: ', loginTryCount, ', Error: ', error);
76
+ wdio_commands_1.default.logScreenshot("Failed to get the user logged in: " + error);
77
+ if (loginTryCount >= (common_1.maxLoginAttempts - 1)) {
78
+ console.log('Since login retry count exceeds 2. Exiting now.');
79
+ throw new Error("Failed to get the user logged in: " + error);
80
+ }
81
+ console.log('Retrying Login.. ');
82
+ await this.doLogin(url, username, password, ++loginTryCount);
83
+ }
84
+ }
85
+ async redirectHome(url) {
86
+ try {
87
+ await super.launchApplication(url);
88
+ }
89
+ catch (error) {
90
+ wdio_commands_1.default.logScreenshot("Exception logged: " + error);
91
+ throw new Error(error);
92
+ }
93
+ }
94
+ }
95
+ exports.default = new LoginPage();
@@ -1,13 +1,34 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
-
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ const fs = __importStar(require("fs"));
27
+ const path = __importStar(require("path"));
4
28
  // A publish section holds the lock for ~1-3 minutes; anything older than this
5
29
  // can only be a leftover from a run that died without releasing.
6
30
  const STALE_LOCK_TTL_MS = 15 * 60 * 1000;
7
-
8
31
  class LockService {
9
- private baseLockDir: string;
10
-
11
32
  constructor() {
12
33
  // Lock/signal files default to <this dir>/locks, but can be redirected via LOCK_DIR so that
13
34
  // independent suite runs (e.g. staging v3 + nightly v6 in parallel) don't share signal files
@@ -19,16 +40,13 @@ class LockService {
19
40
  fs.mkdirSync(this.baseLockDir, { recursive: true });
20
41
  }
21
42
  }
22
-
23
43
  initialize() {
24
44
  this.cleanStaleLocks();
25
45
  console.log("Lock service initialized.");
26
46
  }
27
-
28
- private getLockFilePath(lockName: string): string {
47
+ getLockFilePath(lockName) {
29
48
  return path.resolve(this.baseLockDir, `${lockName}.lock`);
30
49
  }
31
-
32
50
  /**
33
51
  * A lock is stale when its owner can no longer release it: the owning worker
34
52
  * process is dead (identifier format Test-<pid>-<timestamp>), or the file is
@@ -39,71 +57,62 @@ class LockService {
39
57
  * other. Locks owned by a live process are never touched, so a concurrently
40
58
  * running suite on this machine keeps its serialization.
41
59
  */
42
- private isLockStale(lockFilePath: string): boolean {
43
- let owner: string;
44
- let mtimeMs: number;
60
+ isLockStale(lockFilePath) {
61
+ let owner;
62
+ let mtimeMs;
45
63
  try {
46
64
  owner = fs.readFileSync(lockFilePath, 'utf8');
47
65
  mtimeMs = fs.statSync(lockFilePath).mtimeMs;
48
- } catch {
66
+ }
67
+ catch {
49
68
  return false; // vanished meanwhile — nothing to clean
50
69
  }
51
70
  // Probe the owner's liveness FIRST — a live owner must never lose its lock,
52
71
  // regardless of age (slow cluster, retried publish, debug SETTLE_MS pauses).
53
72
  // The TTL only applies when the owner is dead, unknown, or a reused pid.
54
73
  const match = /^Test-(\d+)-\d+$/.exec(owner.trim());
55
- if (!match) return Date.now() - mtimeMs > STALE_LOCK_TTL_MS; // unknown format — TTL only
74
+ if (!match)
75
+ return Date.now() - mtimeMs > STALE_LOCK_TTL_MS; // unknown format — TTL only
56
76
  try {
57
77
  process.kill(Number(match[1]), 0); // signal 0 = liveness probe, sends nothing
58
78
  return false; // owner alive — never steal
59
- } catch (error) {
79
+ }
80
+ catch (error) {
60
81
  // ESRCH: no such process → stale. EPERM: exists but not ours → TTL fallback (pid reuse).
61
- if ((error as NodeJS.ErrnoException).code === 'EPERM') {
82
+ if (error.code === 'EPERM') {
62
83
  return Date.now() - mtimeMs > STALE_LOCK_TTL_MS;
63
84
  }
64
85
  return true;
65
86
  }
66
87
  }
67
-
68
- private removeIfStale(lockFilePath: string): boolean {
69
- if (!this.isLockStale(lockFilePath)) return false;
88
+ removeIfStale(lockFilePath) {
89
+ if (!this.isLockStale(lockFilePath))
90
+ return false;
70
91
  try {
71
92
  fs.unlinkSync(lockFilePath);
72
93
  console.log(`Removed stale lock '${path.basename(lockFilePath)}' (owner dead or TTL expired).`);
73
94
  return true;
74
- } catch {
95
+ }
96
+ catch {
75
97
  return false; // another worker removed it first — fine
76
98
  }
77
99
  }
78
-
79
100
  /** Sweep all *.lock files in the lock dir, removing only stale ones. */
80
- cleanStaleLocks(): void {
81
- let lockFiles: string[];
101
+ cleanStaleLocks() {
102
+ let lockFiles;
82
103
  try {
83
104
  lockFiles = fs.readdirSync(this.baseLockDir).filter(file => file.endsWith('.lock'));
84
- } catch {
105
+ }
106
+ catch {
85
107
  return;
86
108
  }
87
109
  for (const file of lockFiles) {
88
110
  this.removeIfStale(path.resolve(this.baseLockDir, file));
89
111
  }
90
112
  }
91
-
92
- async acquireLock({
93
- testIdentifier,
94
- lockName,
95
- maxRetries = 10,
96
- retryInterval = 1000
97
- }: {
98
- testIdentifier: string;
99
- lockName: string;
100
- maxRetries?: number;
101
- retryInterval?: number;
102
- }): Promise<boolean> {
103
-
113
+ async acquireLock({ testIdentifier, lockName, maxRetries = 10, retryInterval = 1000 }) {
104
114
  const lockFilePath = this.getLockFilePath(lockName);
105
-
106
- for (let retries = 0; ; retries++) {
115
+ for (let retries = 0;; retries++) {
107
116
  // Self-heal locks left by crashed workers, then try an atomic
108
117
  // create-exclusive: 'wx' fails with EEXIST if the file exists, so two
109
118
  // waiters can never both believe they own the lock.
@@ -112,17 +121,18 @@ class LockService {
112
121
  fs.writeFileSync(lockFilePath, testIdentifier, { flag: 'wx' });
113
122
  console.log(`Lock for '${lockName}' acquired by ${testIdentifier}.`);
114
123
  return true;
115
- } catch (error) {
116
- if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {
124
+ }
125
+ catch (error) {
126
+ if (error.code !== 'EEXIST') {
117
127
  console.error(`Failed to acquire lock for '${lockName}':`, error);
118
128
  return false;
119
129
  }
120
130
  }
121
-
122
131
  let currentLockOwner = 'unknown';
123
132
  try {
124
133
  currentLockOwner = fs.readFileSync(lockFilePath, 'utf8');
125
- } catch {
134
+ }
135
+ catch {
126
136
  // released between attempts — next loop iteration will grab it
127
137
  }
128
138
  if (retries >= maxRetries) {
@@ -133,47 +143,40 @@ class LockService {
133
143
  await browser.pause(retryInterval);
134
144
  }
135
145
  }
136
-
137
- releaseLock(testIdentifier: string, lockName: string): boolean {
146
+ releaseLock(testIdentifier, lockName) {
138
147
  const lockFilePath = this.getLockFilePath(lockName);
139
-
140
148
  if (!fs.existsSync(lockFilePath)) {
141
149
  console.log(`Lock for '${lockName}' is not acquired, nothing to release.`);
142
150
  return false;
143
151
  }
144
-
145
152
  const currentLockOwner = fs.readFileSync(lockFilePath, 'utf8');
146
-
147
153
  if (currentLockOwner !== testIdentifier) {
148
154
  console.log(`Lock for '${lockName}' cannot be released. Current owner is ${currentLockOwner}, but ${testIdentifier} is trying to release it.`);
149
155
  return false;
150
156
  }
151
-
152
157
  try {
153
158
  fs.unlinkSync(lockFilePath);
154
159
  console.log(`Lock for '${lockName}' released by ${testIdentifier}.`);
155
160
  return true;
156
- } catch (error) {
161
+ }
162
+ catch (error) {
157
163
  console.error(`Failed to release lock for '${lockName}':`, error);
158
164
  return false;
159
165
  }
160
166
  }
161
-
162
- isLocked(lockName: string): boolean {
167
+ isLocked(lockName) {
163
168
  const lockFilePath = this.getLockFilePath(lockName);
164
169
  return fs.existsSync(lockFilePath);
165
170
  }
166
-
167
171
  /**
168
172
  * Writes a signal file so another worker can detect completion.
169
173
  * The file persists until the next test run overwrites it.
170
174
  */
171
- writeSignal(signalName: string, value: string = "ready"): void {
175
+ writeSignal(signalName, value = "ready") {
172
176
  const filePath = path.resolve(this.baseLockDir, `${signalName}.signal`);
173
177
  fs.writeFileSync(filePath, value);
174
178
  console.log(`Signal '${signalName}' written (value: ${value})`);
175
179
  }
176
-
177
180
  /**
178
181
  * Polls for a signal file written by another worker via writeSignal().
179
182
  * Skips "pending" values — waits until a terminal value (ready/skipped/failed) is written.
@@ -183,15 +186,10 @@ class LockService {
183
186
  * `ENOENT`) instead of a preceding `fs.existsSync` check, to avoid a TOCTOU
184
187
  * race condition.
185
188
  */
186
- async waitForSignal(
187
- signalName: string,
188
- timeoutMs: number = 300000,
189
- intervalMs: number = 5000,
190
- ): Promise<string> {
189
+ async waitForSignal(signalName, timeoutMs = 300000, intervalMs = 5000) {
191
190
  const filePath = path.resolve(this.baseLockDir, `${signalName}.signal`);
192
191
  const deadline = Date.now() + timeoutMs;
193
192
  let elapsed = 0;
194
-
195
193
  while (Date.now() < deadline) {
196
194
  if (fs.existsSync(filePath)) {
197
195
  const value = fs.readFileSync(filePath, 'utf8');
@@ -200,16 +198,14 @@ class LockService {
200
198
  return value;
201
199
  }
202
200
  console.log(`Signal '${signalName}' is pending — waiting for terminal value... (${elapsed}ms elapsed)`);
203
- } else {
201
+ }
202
+ else {
204
203
  console.log(`Waiting for signal '${signalName}'... (${elapsed}ms elapsed, timeout: ${timeoutMs}ms)`);
205
204
  }
206
205
  await browser.pause(intervalMs);
207
206
  elapsed += intervalMs;
208
207
  }
209
-
210
208
  throw new Error(`Signal '${signalName}' not received within ${timeoutMs}ms`);
211
209
  }
212
-
213
210
  }
214
-
215
- export default new LockService();
211
+ exports.default = new LockService();
@@ -1,3 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
1
3
  /**
2
4
  * antd-version detection for the antd v3/v5 -> v6 (new "unified" lib) migration.
3
5
  *
@@ -13,25 +15,7 @@
13
15
  * detect() below — change only that method if the marker ever moves.
14
16
  */
15
17
  class AntdVersionUtil {
16
- // undefined = not yet detected; true/false = cached result for this worker.
17
- private _isV6: boolean | undefined;
18
-
19
- /**
20
- * Detect whether the app under test is the new antd-v6 build, and cache it for
21
- * the rest of the worker. Apps do not use antd directly — they render through
22
- * the internal `cap-ui-library` CSS bundle, of which exactly ONE version (old
23
- * or new=v6) is loaded per page. So the loaded `cap-ui-library` stylesheet IS
24
- * the app's version. The new (v6) lib ships an `ant-row-legacy` selector that
25
- * the old lib never does, so a single same-origin CSS scan is an exact,
26
- * render-independent discriminator. The bundle is served from the same pod
27
- * (same origin), so `cssRules` is always readable (no CORS).
28
- */
29
- // Stylesheet whose presence means the internal UI lib (old OR new) has loaded.
30
- private static readonly LIB_HREF_MARKER = 'cap-ui-library';
31
- // Selector shipped only by the new (v6) lib; absent from the old lib's CSS.
32
- private static readonly V6_CSS_MARKER = 'ant-row-legacy';
33
-
34
- async detect(): Promise<boolean> {
18
+ async detect() {
35
19
  // Runtime detection is DISABLED. selector() now always emits a combined
36
20
  // v6-first selector, so we no longer need to know the version. This stub
37
21
  // stays callable (wdio.conf's navigateTo hook and a campaigns getter call
@@ -123,7 +107,6 @@ class AntdVersionUtil {
123
107
  return this._isV6;
124
108
  --------------------------------------------------------------------- */
125
109
  }
126
-
127
110
  /**
128
111
  * Navigation hook (wired into a global `navigateTo` override in wdio.conf).
129
112
  * The antd version is a property of the PAGE that is loaded, not of the worker
@@ -135,14 +118,13 @@ class AntdVersionUtil {
135
118
  * switcher), so one of the detection markers is always present and the probe
136
119
  * resolves quickly rather than waiting out the timeout.
137
120
  */
138
- async onNavigate(): Promise<void> {
121
+ async onNavigate() {
139
122
  // No-op while detection is disabled. selector() emits both variants, so
140
123
  // there is nothing to re-detect on navigation. Restore the two lines below
141
124
  // if runtime detection is re-enabled in detect().
142
125
  // this.reset();
143
126
  // await this.detect();
144
127
  }
145
-
146
128
  /**
147
129
  * Version flag for the FEW call sites that branch on the version directly
148
130
  * (different interaction model, not just a different locator) rather than
@@ -151,10 +133,9 @@ class AntdVersionUtil {
151
133
  * those branches take the v6 path. If the env is reverted to legacy, flip
152
134
  * this back (or restore detection in detect() and return this._isV6).
153
135
  */
154
- get isV6(): boolean | undefined {
136
+ get isV6() {
155
137
  return true;
156
138
  }
157
-
158
139
  /**
159
140
  * Build one combined selector that matches EITHER the v6 or the legacy
160
141
  * variant, with the v6 variant placed FIRST. Runtime version detection is
@@ -171,20 +152,31 @@ class AntdVersionUtil {
171
152
  * the `,` list. A page is either v6 or legacy, so only one side ever matches;
172
153
  * ordering v6 first just expresses the preference.
173
154
  */
174
- selector(legacy: string, v6: string): string {
175
- const isXPath = (s: string) => /^\s*(\/|\.\/|\.\.\/|\()/.test(s);
155
+ selector(legacy, v6) {
156
+ const isXPath = (s) => /^\s*(\/|\.\/|\.\.\/|\()/.test(s);
176
157
  return isXPath(v6) ? `${v6} | ${legacy}` : `${v6}, ${legacy}`;
177
158
  }
178
-
179
159
  /** Resolve a legacy/v6 selector pair to a live element using the picked one. */
180
- element(legacy: string, v6: string) {
160
+ element(legacy, v6) {
181
161
  return $(this.selector(legacy, v6));
182
162
  }
183
-
184
163
  /** Test hook: clear the cached detection (not used in normal runs). */
185
- reset(): void {
164
+ reset() {
186
165
  this._isV6 = undefined;
187
166
  }
188
167
  }
189
-
190
- export default new AntdVersionUtil();
168
+ /**
169
+ * Detect whether the app under test is the new antd-v6 build, and cache it for
170
+ * the rest of the worker. Apps do not use antd directly — they render through
171
+ * the internal `cap-ui-library` CSS bundle, of which exactly ONE version (old
172
+ * or new=v6) is loaded per page. So the loaded `cap-ui-library` stylesheet IS
173
+ * the app's version. The new (v6) lib ships an `ant-row-legacy` selector that
174
+ * the old lib never does, so a single same-origin CSS scan is an exact,
175
+ * render-independent discriminator. The bundle is served from the same pod
176
+ * (same origin), so `cssRules` is always readable (no CORS).
177
+ */
178
+ // Stylesheet whose presence means the internal UI lib (old OR new) has loaded.
179
+ AntdVersionUtil.LIB_HREF_MARKER = 'cap-ui-library';
180
+ // Selector shipped only by the new (v6) lib; absent from the old lib's CSS.
181
+ AntdVersionUtil.V6_CSS_MARKER = 'ant-row-legacy';
182
+ exports.default = new AntdVersionUtil();
@@ -1,3 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
1
3
  /**
2
4
  * Env-driven kill-switch that lets a module's automation be turned off without a
3
5
  * code change or a CI job edit. In prod the value is injected into the runner
@@ -20,9 +22,10 @@ class AutomationBypass {
20
22
  * must never break a run, so anything unparseable is logged and treated as
21
23
  * "nothing bypassed".
22
24
  */
23
- private getBypassedModules(): string[] {
25
+ getBypassedModules() {
24
26
  const raw = (process.env.AUTOMATION_BYPASS_MODULES || '').trim();
25
- if (!raw) return [];
27
+ if (!raw)
28
+ return [];
26
29
  try {
27
30
  const parsed = JSON.parse(raw);
28
31
  if (!Array.isArray(parsed)) {
@@ -30,17 +33,17 @@ class AutomationBypass {
30
33
  return [];
31
34
  }
32
35
  return parsed.map((m) => String(m).trim()).filter(Boolean);
33
- } catch (error) {
36
+ }
37
+ catch (error) {
34
38
  console.log(`[automation-bypass] Could not parse AUTOMATION_BYPASS_MODULES (${raw}): ${error}`);
35
39
  return [];
36
40
  }
37
41
  }
38
-
39
42
  /** True when the given module's automation should be bypassed. */
40
- isBypassed(module: string | undefined = process.env.module): boolean {
41
- if (!module) return false;
43
+ isBypassed(module = process.env.module) {
44
+ if (!module)
45
+ return false;
42
46
  return this.getBypassedModules().includes(module);
43
47
  }
44
48
  }
45
-
46
- export default new AutomationBypass();
49
+ exports.default = new AutomationBypass();
@@ -1,3 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
1
3
  /**
2
4
  * Central gate for the FULL_DEBUG capture feature (request-payload + screenshot
3
5
  * recording) and, indirectly, for whether the devtools automation protocol is
@@ -13,20 +15,20 @@
13
15
  */
14
16
  class DebugMode {
15
17
  /** Parse DEBUG_MODULES into a trimmed, non-empty module-name list. */
16
- private getDebugModules(): string[] {
18
+ getDebugModules() {
17
19
  return (process.env.DEBUG_MODULES || '')
18
20
  .split(',')
19
21
  .map((m) => m.trim())
20
22
  .filter(Boolean);
21
23
  }
22
-
23
24
  /** True when request/screenshot capture should run for the current module. */
24
- isCaptureEnabled(): boolean {
25
- if (process.env.FULL_DEBUG_MODE !== 'true') return false;
25
+ isCaptureEnabled() {
26
+ if (process.env.FULL_DEBUG_MODE !== 'true')
27
+ return false;
26
28
  const modules = this.getDebugModules();
27
- if (modules.length === 0) return true;
29
+ if (modules.length === 0)
30
+ return true;
28
31
  return modules.includes(process.env.module || '');
29
32
  }
30
33
  }
31
-
32
- export default new DebugMode();
34
+ exports.default = new DebugMode();