@playq/core 0.2.77

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 (225) hide show
  1. package/README.md +41 -0
  2. package/bin/playq.js +175 -0
  3. package/cucumber.js +10 -0
  4. package/dist/exec/featureFileCache.d.ts +21 -0
  5. package/dist/exec/featureFileCache.js +124 -0
  6. package/dist/exec/featureFilePreProcess.d.ts +12 -0
  7. package/dist/exec/featureFilePreProcess.js +208 -0
  8. package/dist/exec/preLoader.d.ts +1 -0
  9. package/dist/exec/preLoader.js +72 -0
  10. package/dist/exec/preProcessEntry.d.ts +1 -0
  11. package/dist/exec/preProcessEntry.js +83 -0
  12. package/dist/exec/preProcess_old_todelete.d.ts +1 -0
  13. package/dist/exec/preProcess_old_todelete.js +258 -0
  14. package/dist/exec/runner.d.ts +1 -0
  15. package/dist/exec/runner.js +221 -0
  16. package/dist/exec/runner_orchestrator.d.ts +1 -0
  17. package/dist/exec/runner_orchestrator.js +85 -0
  18. package/dist/exec/sgGenerator.d.ts +11 -0
  19. package/dist/exec/sgGenerator.js +310 -0
  20. package/dist/global.d.ts +15 -0
  21. package/dist/global.js +185 -0
  22. package/dist/helper/actions/api/apiRequestActions.d.ts +117 -0
  23. package/dist/helper/actions/api/apiRequestActions.js +374 -0
  24. package/dist/helper/actions/api/apiValidationActions.d.ts +119 -0
  25. package/dist/helper/actions/api/apiValidationActions.js +615 -0
  26. package/dist/helper/actions/apiActions.d.ts +18 -0
  27. package/dist/helper/actions/apiActions.js +34 -0
  28. package/dist/helper/actions/apiStepDefs.d.ts +1 -0
  29. package/dist/helper/actions/apiStepDefs.js +64 -0
  30. package/dist/helper/actions/comm/commonActions.d.ts +58 -0
  31. package/dist/helper/actions/comm/commonActions.js +198 -0
  32. package/dist/helper/actions/comm/utilityActions.d.ts +131 -0
  33. package/dist/helper/actions/comm/utilityActions.js +351 -0
  34. package/dist/helper/actions/commActions.d.ts +18 -0
  35. package/dist/helper/actions/commActions.js +34 -0
  36. package/dist/helper/actions/commStepDefs.d.ts +1 -0
  37. package/dist/helper/actions/commStepDefs.js +57 -0
  38. package/dist/helper/actions/stepGroupStepDefs.d.ts +1 -0
  39. package/dist/helper/actions/stepGroupStepDefs.js +15 -0
  40. package/dist/helper/actions/web/alertActions.d.ts +61 -0
  41. package/dist/helper/actions/web/alertActions.js +224 -0
  42. package/dist/helper/actions/web/cookieActions.d.ts +45 -0
  43. package/dist/helper/actions/web/cookieActions.js +186 -0
  44. package/dist/helper/actions/web/downloadActions.d.ts +40 -0
  45. package/dist/helper/actions/web/downloadActions.js +153 -0
  46. package/dist/helper/actions/web/elementReaderActions.d.ts +95 -0
  47. package/dist/helper/actions/web/elementReaderActions.js +326 -0
  48. package/dist/helper/actions/web/formActions.d.ts +122 -0
  49. package/dist/helper/actions/web/formActions.js +423 -0
  50. package/dist/helper/actions/web/iframeActions.d.ts +23 -0
  51. package/dist/helper/actions/web/iframeActions.js +108 -0
  52. package/dist/helper/actions/web/javascriptActions.d.ts +14 -0
  53. package/dist/helper/actions/web/javascriptActions.js +77 -0
  54. package/dist/helper/actions/web/keyboardActions.d.ts +35 -0
  55. package/dist/helper/actions/web/keyboardActions.js +118 -0
  56. package/dist/helper/actions/web/localStorageActions.d.ts +51 -0
  57. package/dist/helper/actions/web/localStorageActions.js +163 -0
  58. package/dist/helper/actions/web/mouseActions.d.ts +240 -0
  59. package/dist/helper/actions/web/mouseActions.js +609 -0
  60. package/dist/helper/actions/web/reportingActions.d.ts +34 -0
  61. package/dist/helper/actions/web/reportingActions.js +58 -0
  62. package/dist/helper/actions/web/screenshotActions.d.ts +34 -0
  63. package/dist/helper/actions/web/screenshotActions.js +151 -0
  64. package/dist/helper/actions/web/testDataActions.d.ts +21 -0
  65. package/dist/helper/actions/web/testDataActions.js +211 -0
  66. package/dist/helper/actions/web/validationActions.d.ts +547 -0
  67. package/dist/helper/actions/web/validationActions.js +1754 -0
  68. package/dist/helper/actions/web/waitActions.d.ts +191 -0
  69. package/dist/helper/actions/web/waitActions.js +589 -0
  70. package/dist/helper/actions/web/webNavigation.d.ts +104 -0
  71. package/dist/helper/actions/web/webNavigation.js +288 -0
  72. package/dist/helper/actions/webActions.d.ts +32 -0
  73. package/dist/helper/actions/webActions.js +48 -0
  74. package/dist/helper/actions/webStepDefs.d.ts +1 -0
  75. package/dist/helper/actions/webStepDefs.js +455 -0
  76. package/dist/helper/browsers/browserManager.d.ts +1 -0
  77. package/dist/helper/browsers/browserManager.js +56 -0
  78. package/dist/helper/bundle/defaultEntries.d.ts +6 -0
  79. package/dist/helper/bundle/defaultEntries.js +200 -0
  80. package/dist/helper/bundle/env.d.ts +1 -0
  81. package/dist/helper/bundle/env.js +157 -0
  82. package/dist/helper/bundle/vars.d.ts +9 -0
  83. package/dist/helper/bundle/vars.js +375 -0
  84. package/dist/helper/faker/customFaker.d.ts +55 -0
  85. package/dist/helper/faker/customFaker.js +45 -0
  86. package/dist/helper/faker/modules/data/postcodes_valid_sg.json +17 -0
  87. package/dist/helper/faker/modules/dateTime.d.ts +18 -0
  88. package/dist/helper/faker/modules/dateTime.js +106 -0
  89. package/dist/helper/faker/modules/mobile.d.ts +4 -0
  90. package/dist/helper/faker/modules/mobile.js +59 -0
  91. package/dist/helper/faker/modules/nric.d.ts +32 -0
  92. package/dist/helper/faker/modules/nric.js +84 -0
  93. package/dist/helper/faker/modules/passport.d.ts +3 -0
  94. package/dist/helper/faker/modules/passport.js +36 -0
  95. package/dist/helper/faker/modules/person.d.ts +14 -0
  96. package/dist/helper/faker/modules/person.js +73 -0
  97. package/dist/helper/faker/modules/postcode.d.ts +6 -0
  98. package/dist/helper/faker/modules/postcode.js +47 -0
  99. package/dist/helper/fixtures/locAggregate.d.ts +7 -0
  100. package/dist/helper/fixtures/locAggregate.js +94 -0
  101. package/dist/helper/fixtures/logFixture.d.ts +8 -0
  102. package/dist/helper/fixtures/logFixture.js +56 -0
  103. package/dist/helper/fixtures/webFixture.d.ts +19 -0
  104. package/dist/helper/fixtures/webFixture.js +186 -0
  105. package/dist/helper/fixtures/webLocFixture.d.ts +2 -0
  106. package/dist/helper/fixtures/webLocFixture.js +144 -0
  107. package/dist/helper/report/allureStepLogger.d.ts +0 -0
  108. package/dist/helper/report/allureStepLogger.js +25 -0
  109. package/dist/helper/report/customiseReport.d.ts +1 -0
  110. package/dist/helper/report/customiseReport.js +55 -0
  111. package/dist/helper/report/init.d.ts +1 -0
  112. package/dist/helper/report/init.js +14 -0
  113. package/dist/helper/report/report.d.ts +1 -0
  114. package/dist/helper/report/report.js +102 -0
  115. package/dist/helper/util/dataLoader.d.ts +10 -0
  116. package/dist/helper/util/dataLoader.js +73 -0
  117. package/dist/helper/util/logger.d.ts +4 -0
  118. package/dist/helper/util/logger.js +61 -0
  119. package/dist/helper/util/session/sessionUtil.d.ts +26 -0
  120. package/dist/helper/util/session/sessionUtil.js +729 -0
  121. package/dist/helper/util/stepHelpers.d.ts +2 -0
  122. package/dist/helper/util/stepHelpers.js +16 -0
  123. package/dist/helper/util/test-data/dataLoader.d.ts +7 -0
  124. package/dist/helper/util/test-data/dataLoader.js +145 -0
  125. package/dist/helper/util/test-data/dataTest.d.ts +10 -0
  126. package/dist/helper/util/test-data/dataTest.js +216 -0
  127. package/dist/helper/util/totp/totpHelper.d.ts +38 -0
  128. package/dist/helper/util/totp/totpHelper.js +117 -0
  129. package/dist/helper/util/utilities/cryptoUtil.d.ts +2 -0
  130. package/dist/helper/util/utilities/cryptoUtil.js +53 -0
  131. package/dist/helper/util/utilities/schemaGeneratorUtil.d.ts +2 -0
  132. package/dist/helper/util/utilities/schemaGeneratorUtil.js +129 -0
  133. package/dist/helper/util/utils.d.ts +2 -0
  134. package/dist/helper/util/utils.js +22 -0
  135. package/dist/helper/wrapper/PlaywrightWrappers.d.ts +8 -0
  136. package/dist/helper/wrapper/PlaywrightWrappers.js +26 -0
  137. package/dist/helper/wrapper/assert.d.ts +9 -0
  138. package/dist/helper/wrapper/assert.js +23 -0
  139. package/dist/index.d.ts +7 -0
  140. package/dist/index.js +57 -0
  141. package/dist/scripts/get-versions.d.ts +1 -0
  142. package/dist/scripts/get-versions.js +98 -0
  143. package/dist/scripts/posttest.d.ts +1 -0
  144. package/dist/scripts/posttest.js +29 -0
  145. package/dist/scripts/pretest.d.ts +1 -0
  146. package/dist/scripts/pretest.js +57 -0
  147. package/dist/scripts/util.d.ts +1 -0
  148. package/dist/scripts/util.js +376 -0
  149. package/package.json +68 -0
  150. package/src/exec/featureFileCache.ts +80 -0
  151. package/src/exec/featureFilePreProcess.ts +239 -0
  152. package/src/exec/preLoader.ts +72 -0
  153. package/src/exec/preProcessEntry.ts +59 -0
  154. package/src/exec/preProcess_old_todelete.ts +289 -0
  155. package/src/exec/runner.ts +241 -0
  156. package/src/exec/runnerCuke.js +90 -0
  157. package/src/exec/runner_orchestrator.ts +91 -0
  158. package/src/exec/sgGenerator.ts +373 -0
  159. package/src/global.ts +130 -0
  160. package/src/helper/actions/api/apiRequestActions.ts +362 -0
  161. package/src/helper/actions/api/apiValidationActions.ts +594 -0
  162. package/src/helper/actions/apiActions.ts +18 -0
  163. package/src/helper/actions/apiStepDefs.ts +80 -0
  164. package/src/helper/actions/comm/commonActions.ts +165 -0
  165. package/src/helper/actions/comm/utilityActions.ts +344 -0
  166. package/src/helper/actions/commActions.ts +18 -0
  167. package/src/helper/actions/commStepDefs.ts +72 -0
  168. package/src/helper/actions/stepGroupStepDefs.ts +17 -0
  169. package/src/helper/actions/web/alertActions.ts +179 -0
  170. package/src/helper/actions/web/cookieActions.ts +124 -0
  171. package/src/helper/actions/web/downloadActions.ts +129 -0
  172. package/src/helper/actions/web/elementReaderActions.ts +323 -0
  173. package/src/helper/actions/web/formActions.ts +469 -0
  174. package/src/helper/actions/web/iframeActions.ts +67 -0
  175. package/src/helper/actions/web/javascriptActions.ts +38 -0
  176. package/src/helper/actions/web/keyboardActions.ts +101 -0
  177. package/src/helper/actions/web/localStorageActions.ts +109 -0
  178. package/src/helper/actions/web/mouseActions.ts +864 -0
  179. package/src/helper/actions/web/reportingActions.ts +53 -0
  180. package/src/helper/actions/web/screenshotActions.ts +124 -0
  181. package/src/helper/actions/web/testDataActions.ts +162 -0
  182. package/src/helper/actions/web/validationActions.ts +2287 -0
  183. package/src/helper/actions/web/waitActions.ts +757 -0
  184. package/src/helper/actions/web/webNavigation.ts +313 -0
  185. package/src/helper/actions/webActions.ts +33 -0
  186. package/src/helper/actions/webStepDefs.ts +505 -0
  187. package/src/helper/browsers/browserManager.ts +23 -0
  188. package/src/helper/bundle/defaultEntries.ts +208 -0
  189. package/src/helper/bundle/env.ts +119 -0
  190. package/src/helper/bundle/vars.ts +368 -0
  191. package/src/helper/faker/customFaker.ts +107 -0
  192. package/src/helper/faker/modules/data/postcodes_valid_sg.json +17 -0
  193. package/src/helper/faker/modules/dateTime.ts +121 -0
  194. package/src/helper/faker/modules/mobile.ts +58 -0
  195. package/src/helper/faker/modules/nric.ts +109 -0
  196. package/src/helper/faker/modules/passport.ts +34 -0
  197. package/src/helper/faker/modules/person.ts +93 -0
  198. package/src/helper/faker/modules/postcode.ts +57 -0
  199. package/src/helper/fixtures/locAggregate.ts +61 -0
  200. package/src/helper/fixtures/logFixture.ts +57 -0
  201. package/src/helper/fixtures/webFixture.ts +206 -0
  202. package/src/helper/fixtures/webLocFixture.ts +143 -0
  203. package/src/helper/report/allureStepLogger.ts +26 -0
  204. package/src/helper/report/customiseReport.ts +61 -0
  205. package/src/helper/report/init.ts +18 -0
  206. package/src/helper/report/report.ts +72 -0
  207. package/src/helper/util/dataLoader.ts +42 -0
  208. package/src/helper/util/logger.ts +32 -0
  209. package/src/helper/util/session/sessionUtil.ts +839 -0
  210. package/src/helper/util/stepHelpers.ts +14 -0
  211. package/src/helper/util/test-data/dataLoader.ts +108 -0
  212. package/src/helper/util/test-data/dataTest.ts +191 -0
  213. package/src/helper/util/test-data/registerUser.json +7 -0
  214. package/src/helper/util/totp/totpHelper.ts +102 -0
  215. package/src/helper/util/utilities/cryptoUtil.ts +53 -0
  216. package/src/helper/util/utilities/schemaGeneratorUtil.ts +143 -0
  217. package/src/helper/util/utils.ts +28 -0
  218. package/src/helper/wrapper/PlaywrightWrappers.ts +28 -0
  219. package/src/helper/wrapper/assert.ts +25 -0
  220. package/src/index.ts +17 -0
  221. package/src/scripts/get-versions.ts +68 -0
  222. package/src/scripts/posttest.ts +32 -0
  223. package/src/scripts/pretest.ts +48 -0
  224. package/src/scripts/util.ts +406 -0
  225. package/tsconfig.json +30 -0
@@ -0,0 +1,757 @@
1
+ /**
2
+ * @file waitActions.ts
3
+ *
4
+ * Wait utilities for PlayQ web actions.
5
+ * Provides time-based waits, page load settling, element visibility/state
6
+ * waits, header/text waits, and URL waits with runner-aware step wrappers.
7
+ *
8
+ * Authors: PlayQ Team
9
+ * Version: v1.0.0
10
+ */
11
+ import * as allure from "allure-js-commons";
12
+ import { Page, Locator, expect } from "@playwright/test";
13
+ import { vars, comm } from "../../../global";
14
+ import { webLocResolver } from "../../fixtures/webLocFixture";
15
+ import { processScreenshot } from "./screenshotActions";
16
+ import { attachLog } from "../comm/commonActions";
17
+ import { parseLooseJson } from '../../bundle/vars';
18
+
19
+ function isPlaywrightRunner() { return process.env.TEST_RUNNER === 'playwright'; }
20
+ const __allureAny_wait: any = allure as any;
21
+ if (typeof __allureAny_wait.step !== 'function') { __allureAny_wait.step = async (_n: string, f: any) => await f(); }
22
+
23
+ /**
24
+ * Comm: Wait-In-Milli-Seconds -seconds: {param}
25
+ *
26
+ * Pauses execution for the given number of milliseconds.
27
+ * Delegates to `comm.waitInMilliSeconds`.
28
+ *
29
+ * @param ms Milliseconds to wait
30
+ */
31
+ export async function wait(ms: number) {
32
+ return comm.waitInMilliSeconds(ms);
33
+ }
34
+
35
+ /**
36
+ * Web: Wait For Condition -timeout: {param}
37
+ *
38
+ * Polls a predicate until it returns true or times out.
39
+ *
40
+ * @param page Playwright Page instance
41
+ * @param predicate Async function returning a boolean
42
+ * @param options Optional JSON string or object ({ timeout, interval })
43
+ */
44
+ export async function waitForCondition(page: Page, predicate: (page: Page) => Promise<boolean>, options?: string | Record<string, any>) {
45
+ const options_json = typeof options === 'string' ? vars.parseLooseJson(options) : options || {};
46
+ const { timeout = Number(vars.getConfigValue('testExecution.actionTimeout')) || 10000, interval = 250 } = options_json;
47
+ const stepName = `Web: Wait For Condition -timeout: ${timeout}`;
48
+ const start = Date.now();
49
+ async function loop() {
50
+ while (Date.now() - start < timeout) {
51
+ if (await predicate(page)) return true;
52
+ await comm.waitInMilliSeconds(interval);
53
+ }
54
+ throw new Error('Condition not met within timeout');
55
+ }
56
+ if (isPlaywrightRunner()) { await __allureAny_wait.step(stepName, loop); } else { await loop(); }
57
+ }
58
+
59
+ /**
60
+ * Waits for the page to fully load by checking multiple browser states:
61
+ * - `domcontentloaded`: Ensures DOM is parsed and ready.
62
+ * - `load`: Waits for all resources like images and scripts to load.
63
+ * - `requestIdleCallback`: Ensures the browser is idle before proceeding.
64
+ *
65
+ * This function is useful after navigation, form submission, or any page transition
66
+ * to ensure stable element interaction.
67
+ *
68
+ * @param page - The Playwright Page instance.
69
+ * @param actionTimeout - Optional timeout (in ms) to wait for each load state. Default: 10000.
70
+ *
71
+ */
72
+ export async function waitForPageToLoad(
73
+ page: Page,
74
+ actionTimeout: number = 10000
75
+ ): Promise<void> {
76
+ const wait = (ms: number) => new Promise((res) => setTimeout(res, ms));
77
+
78
+ console.log("⏳ Waiting for DOMContentLoaded...");
79
+ try {
80
+ await page.waitForLoadState("domcontentloaded", { timeout: actionTimeout });
81
+ console.log("✅ DOMContentLoaded");
82
+ } catch {
83
+ console.warn("⚠️ DOMContentLoaded not detected within timeout");
84
+ }
85
+
86
+ console.log("🔄 Waiting for load event...");
87
+ try {
88
+ await page.waitForLoadState("load", { timeout: actionTimeout });
89
+ console.log("✅ Load event");
90
+ } catch {
91
+ console.warn("⚠️ Page load event not triggered within timeout");
92
+ }
93
+
94
+ console.log("🕓 Waiting for browser idle callback...");
95
+ try {
96
+ await page.evaluate(() => {
97
+ return new Promise((resolve) => {
98
+ window.requestIdleCallback(() => resolve(true));
99
+ });
100
+ });
101
+ console.log("✅ requestIdleCallback done");
102
+ } catch {
103
+ console.warn("⚠️ requestIdleCallback failed or delayed");
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Waits for a given locator (element) to become enabled within the specified timeout.
109
+ * Useful before interacting with inputs, buttons, or other dynamic UI elements.
110
+ *
111
+ * @param locator - The Playwright Locator to wait for (e.g., input field, button).
112
+ * @param actionTimeout - Optional timeout (in ms). Default: 5000.
113
+ *
114
+ * @throws Error if the locator does not become enabled within the timeout.
115
+ *
116
+ */
117
+ export async function waitForEnabled(
118
+ locator: Locator,
119
+ actionTimeout: number = 5000
120
+ ): Promise<void> {
121
+ await expect(locator).toBeEnabled({ timeout: actionTimeout });
122
+ }
123
+
124
+ /**
125
+ * Web: Wait for displayed -field: {param} -options: {param}
126
+ *
127
+ * Waits for an element to become visible on the page.
128
+ *
129
+ * @param page - Playwright Page instance
130
+ * @param field - The label, id, name, or selector of the element to wait for
131
+ * @param options - Optional JSON string or object:
132
+ * - actionTimeout: [number] Optional timeout in milliseconds. Default: Configured timeout.
133
+ * - pattern: [string] Optional pattern to refine element search.
134
+ * - fieldType: [string] Type of field (e.g., input, button, etc.)
135
+ * - screenshot: [boolean] Capture screenshot after waiting. Default: false.
136
+ * - screenshotText: [string] Description for screenshot.
137
+ * - screenshotFullPage: [boolean] Full page screenshot. Default: true.
138
+ */
139
+ export async function waitForDisplayed(page: Page, field: string | Locator, options?: string | Record<string, any>) {
140
+ const options_json = typeof options === "string" ? vars.parseLooseJson(options) : options || {};
141
+ const {
142
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")) || 30000,
143
+ pattern,
144
+ fieldType,
145
+ screenshot = false,
146
+ screenshotText = "",
147
+ screenshotFullPage = true
148
+ } = options_json;
149
+
150
+ const stepName = `Web: Wait for displayed -field: ${String(field)} -options: ${JSON.stringify(options_json)}`;
151
+ async function run() {
152
+ if (!page) throw new Error("Page not initialized");
153
+ const target = typeof field === "string" ? await webLocResolver(fieldType, field, page, pattern, actionTimeout) : field;
154
+ try {
155
+ await (target as Locator).waitFor({ state: "visible", timeout: actionTimeout });
156
+ await attachLog(`✅ Element "${String(field)}" is now visible`, "text/plain", "Log");
157
+ } catch {
158
+ throw new Error(`❌ Element "${String(field)}" did not become visible within ${actionTimeout}ms`);
159
+ }
160
+ await processScreenshot(page, screenshot, screenshotText || `Waited for element to be displayed: ${String(field)}`, screenshotFullPage);
161
+ }
162
+ if (isPlaywrightRunner()) { await __allureAny_wait.step(stepName, run); } else { await run(); }
163
+ }
164
+
165
+ /**
166
+ * Web: Wait for disappear -field: {param} -options: {param}
167
+ *
168
+ * Waits for an element to disappear (become hidden or removed) from the page.
169
+ *
170
+ * @param page - Playwright Page instance
171
+ * @param field - The label, id, name, or selector of the element to wait for
172
+ * @param options - Optional JSON string or object:
173
+ * - actionTimeout: [number] Optional timeout in milliseconds. Default: Configured timeout.
174
+ * - pattern: [string] Optional pattern to refine element search.
175
+ * - fieldType: [string] Type of field (e.g., input, button, etc.)
176
+ * - screenshot: [boolean] Capture screenshot after waiting. Default: false.
177
+ * - screenshotText: [string] Description for screenshot.
178
+ * - screenshotFullPage: [boolean] Full page screenshot. Default: true.
179
+ */
180
+ export async function waitForDisappear(page: Page, field: string | Locator, options?: string | Record<string, any>) {
181
+ const options_json = typeof options === "string" ? vars.parseLooseJson(options) : options || {};
182
+ const {
183
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")) || 30000,
184
+ pattern,
185
+ fieldType,
186
+ screenshot = false,
187
+ screenshotText = "",
188
+ screenshotFullPage = true
189
+ } = options_json;
190
+ const stepName = `Web: Wait for disappear -field: ${String(field)} -options: ${JSON.stringify(options_json)}`;
191
+ async function run() {
192
+ if (!page) throw new Error("Page not initialized");
193
+ const target = typeof field === "string" ? await webLocResolver(fieldType, field, page, pattern, actionTimeout) : field;
194
+ try {
195
+ await (target as Locator).waitFor({ state: "hidden", timeout: actionTimeout });
196
+ await attachLog(`✅ Element "${String(field)}" has disappeared`, "text/plain", "Log");
197
+ } catch {
198
+ throw new Error(`❌ Element "${String(field)}" did not disappear within ${actionTimeout}ms`);
199
+ }
200
+ await processScreenshot(page, screenshot, screenshotText || `Waited for element to disappear: ${String(field)}`, screenshotFullPage);
201
+ }
202
+ if (isPlaywrightRunner()) { await __allureAny_wait.step(stepName, run); } else { await run(); }
203
+ }
204
+
205
+
206
+ /**
207
+ * Web: Wait for Text at Location -field: {param} -text: {param} -options: {param}
208
+ *
209
+ * Waits until the specified text appears at the given field/locator.
210
+ *
211
+ * @param page - Playwright Page instance
212
+ * @param field - The selector or Locator where the text should appear
213
+ * @param expectedText - The text to wait for
214
+ * @param options - Optional string or object:
215
+ * - actionTimeout: [number] Timeout in ms (default: 30000)
216
+ * - partialMatch: [boolean] If true, waits for substring match (default: false)
217
+ * - caseSensitive: [boolean] If true, match is case-sensitive (default: true)
218
+ * - screenshot: [boolean] Capture screenshot after waiting (default: false)
219
+ * - screenshotText: [string] Description for screenshot
220
+ * - screenshotFullPage: [boolean] Full page screenshot (default: true)
221
+ *
222
+ * @example
223
+ * await waitForTextAtLocation(page, 'h1', 'Welcome', { actionTimeout: 10000, partialMatch: true });
224
+ */
225
+ export async function waitForTextAtLocation(
226
+ page: Page,
227
+ field: string | Locator,
228
+ expectedText: string,
229
+ options?: string | Record<string, any>
230
+ ) {
231
+ const options_json =
232
+ typeof options === "string" ? parseLooseJson(options) : options || {};
233
+ const {
234
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
235
+ partialMatch = false,
236
+ ignoreCase = true,
237
+ screenshot = false,
238
+ screenshotText = "",
239
+ screenshotFullPage = true,
240
+ pattern,
241
+ } = options_json;
242
+
243
+ if (!page) throw new Error("Page not initialized");
244
+
245
+ if (isPlaywrightRunner()) {
246
+ await __allureAny_wait.step(
247
+ `Web: Wait for Text at Location -field: ${field} -text: ${expectedText} -options: ${JSON.stringify(
248
+ options_json
249
+ )}`,
250
+ async () => {
251
+ await doWaitForTextAtLocation();
252
+ }
253
+ );
254
+ } else {
255
+ await doWaitForTextAtLocation();
256
+ }
257
+
258
+ async function doWaitForTextAtLocation() {
259
+ const target =
260
+ typeof field === "string"
261
+ ? await webLocResolver("text", field, page, pattern, actionTimeout)
262
+ : field;
263
+
264
+ const start = Date.now();
265
+ let found = false;
266
+ let lastActual = "";
267
+
268
+ while (Date.now() - start < actionTimeout) {
269
+ const actualText = ((await target.innerText()) || "").trim();
270
+ lastActual = actualText;
271
+ let expected = expectedText;
272
+ let actual = actualText;
273
+ if (!ignoreCase) {
274
+ expected = expected.toLowerCase();
275
+ actual = actual.toLowerCase();
276
+ }
277
+ if (partialMatch ? actual.includes(expected) : actual === expected) {
278
+ found = true;
279
+ break;
280
+ }
281
+ await new Promise((res) => setTimeout(res, 300));
282
+ }
283
+
284
+ if (!found) {
285
+ const msg = `❌ Text "${expectedText}" did not appear at location "${field}" within ${actionTimeout}ms. Last actual: "${lastActual}"`;
286
+ await attachLog(msg, "text/plain");
287
+ throw new Error(msg);
288
+ } else {
289
+ await attachLog(
290
+ `✅ Text "${expectedText}" appeared at location "${field}"`,
291
+ "text/plain"
292
+ );
293
+ }
294
+
295
+ await processScreenshot(
296
+ page,
297
+ screenshot,
298
+ screenshotText || `Waited for text at location: ${expectedText}`,
299
+ screenshotFullPage
300
+ );
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Web: Wait for Text to Disappear at Location -field: {param} -text: {param} -options: {param}
306
+ *
307
+ * Waits until the specified text disappears from the given field/locator.
308
+ *
309
+ * @param page - Playwright Page instance
310
+ * @param field - The selector or Locator where the text should disappear
311
+ * @param textToDisappear - The text to wait for disappearance
312
+ * @param options - Optional string or object:
313
+ * - actionTimeout: [number] Timeout in ms (default: 30000)
314
+ * - partialMatch: [boolean] If true, waits for substring match (default: false)
315
+ * - ignoreCase: [boolean] If true, match is case-insensitive (default: true)
316
+ * - screenshot: [boolean] Capture screenshot after waiting (default: false)
317
+ * - screenshotText: [string] Description for screenshot
318
+ * - screenshotFullPage: [boolean] Full page screenshot (default: true)
319
+ */
320
+ export async function waitForTextToDisappearAtLocation(
321
+ page: Page,
322
+ field: string | Locator,
323
+ textToDisappear: string,
324
+ options?: string | Record<string, any>
325
+ ) {
326
+ const options_json =
327
+ typeof options === "string" ? parseLooseJson(options) : options || {};
328
+
329
+ const {
330
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")) || 30000,
331
+ partialMatch = true,
332
+ ignoreCase = true,
333
+ screenshot = false,
334
+ screenshotText = "",
335
+ screenshotFullPage = true,
336
+ } = options_json;
337
+
338
+ if (!page) throw new Error("Page not initialized");
339
+
340
+ let locator: Locator;
341
+
342
+ if (typeof field === "string") {
343
+ // ✅ Build regex safely
344
+ const escapedText = textToDisappear.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
345
+
346
+ const pattern = partialMatch
347
+ ? `.*${escapedText}.*`
348
+ : `^${escapedText}$`;
349
+
350
+ const regex = new RegExp(pattern, ignoreCase ? "i" : undefined);
351
+
352
+ locator = page.getByText(regex);
353
+ } else {
354
+ locator = field;
355
+ }
356
+
357
+ await locator.waitFor({
358
+ state: "hidden",
359
+ timeout: actionTimeout,
360
+ });
361
+
362
+ await attachLog(
363
+ `✅ Text "${textToDisappear}" disappeared (partialMatch=${partialMatch}, ignoreCase=${ignoreCase})`,
364
+ "text/plain"
365
+ );
366
+
367
+ await processScreenshot(
368
+ page,
369
+ screenshot,
370
+ screenshotText || `Waited for text to disappear: ${textToDisappear}`,
371
+ screenshotFullPage
372
+ );
373
+ }
374
+
375
+ /**
376
+ * Web: Wait for Selector -field: {param} -options: {param}
377
+ *
378
+ * Waits for a selector or locator to reach a specific state.
379
+ *
380
+ * @param page - Playwright Page instance
381
+ * @param field - The selector string or Locator to wait for
382
+ * @param options - Optional string or object:
383
+ * - fieldType: [string] Type of field (e.g., 'button', 'input') for pattern resolution
384
+ * - state: [string] 'attached' | 'detached' | 'visible' | 'hidden' (default: 'visible')
385
+ * - actionTimeout: [number] Timeout in ms (default: 30000)
386
+ * - pattern: [string] Optional pattern for locator resolution
387
+ * - screenshot: [boolean] Capture screenshot after waiting (default: false)
388
+ * - screenshotText: [string] Description for screenshot
389
+ * - screenshotFullPage: [boolean] Full page screenshot (default: true)
390
+ *
391
+ * @example
392
+ * await waitForSelector(page, 'img[alt="~"]', { state: 'hidden', actionTimeout: 10000 });
393
+ */
394
+ export async function waitForSelector(
395
+ page: Page,
396
+ field: string | Locator,
397
+ options?: string | Record<string, any>
398
+ ) {
399
+ const options_json =
400
+ typeof options === "string" ? parseLooseJson(options) : options || {};
401
+ const {
402
+ fieldType,
403
+ state = "visible",
404
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")) || 30000,
405
+ pattern,
406
+ screenshot = false,
407
+ screenshotText = "",
408
+ screenshotFullPage = true,
409
+ } = options_json;
410
+
411
+ if (!page) throw new Error("Page not initialized");
412
+
413
+ if (isPlaywrightRunner()) {
414
+ await __allureAny_wait.step(
415
+ `Web: Wait for Selector -field: ${field} -state: ${state} -options: ${JSON.stringify(options_json)}`,
416
+ async () => {
417
+ await doWaitForSelector();
418
+ }
419
+ );
420
+ } else {
421
+ await doWaitForSelector();
422
+ }
423
+
424
+ async function doWaitForSelector() {
425
+ if (typeof field === "string" && !fieldType) {
426
+ throw new Error(
427
+ `fieldType is required for string selectors. Received field="${field}"`
428
+ );
429
+ }
430
+
431
+ let target: Locator;
432
+
433
+ if (typeof field === "string") {
434
+ if (state === "hidden" || state === "detached") {
435
+ if (fieldType === "text") {
436
+ target = page.getByText(field, { exact: false });
437
+ } else {
438
+ target = page.locator(`//*[normalize-space()='${field}']`);
439
+ }
440
+ } else {
441
+ target = await webLocResolver(
442
+ fieldType,
443
+ field,
444
+ page,
445
+ pattern,
446
+ actionTimeout
447
+ );
448
+ }
449
+ } else {
450
+ target = field;
451
+ }
452
+
453
+ await target.waitFor({ state, timeout: actionTimeout });
454
+
455
+ await attachLog(
456
+ `✅ Selector "${field}" reached state "${state}".`,
457
+ "text/plain"
458
+ );
459
+
460
+ await processScreenshot(
461
+ page,
462
+ screenshot,
463
+ screenshotText || `Waited for selector: ${field} state: ${state}`,
464
+ screenshotFullPage
465
+ );
466
+ }
467
+
468
+
469
+ }
470
+
471
+ /**
472
+ * Web: Wait for Header -header: {param} -text: {param} -options: {param}
473
+ *
474
+ * Waits until a specific header element contains the expected text.
475
+ * The header parameter should be a locator or will use pattern resolution.
476
+ *
477
+ * @param page - Playwright Page instance
478
+ * @param header - The locator of the header element (e.g., "h1", "xpath=//h1[@class='title']", or Locator object)
479
+ * @param headerText - The expected header text to wait for (e.g., "Welcome", "Dashboard")
480
+ * @param options - Optional string or object:
481
+ * - actionTimeout: [number] Timeout in ms (default: 30000)
482
+ * - partialMatch: [boolean] If true, waits for substring match (default: false)
483
+ * - ignoreCase: [boolean] If true, case-insensitive match (default: true)
484
+ * - pattern: [string] Optional pattern to refine element search
485
+ * - screenshot: [boolean] Capture screenshot after waiting (default: false)
486
+ * - screenshotText: [string] Description for screenshot
487
+ * - screenshotFullPage: [boolean] Full page screenshot (default: true)
488
+ *
489
+ * @example
490
+ * await waitForHeader(page, 'h1', 'Welcome Back!', {
491
+ * partialMatch: true,
492
+ * screenshot: true
493
+ * });
494
+ */
495
+ export async function waitForHeader(
496
+ page: Page,
497
+ header: string | Locator,
498
+ headerText: string,
499
+ options?: string | Record<string, any>
500
+ ) {
501
+ const resolvedHeaderText = vars.replaceVariables(headerText);
502
+ const options_json =
503
+ typeof options === "string" ? parseLooseJson(options) : options || {};
504
+
505
+ const {
506
+ actionTimeout = Number(
507
+ vars.getConfigValue("testExecution.actionTimeout")
508
+ ) || 30000,
509
+ partialMatch = false,
510
+ ignoreCase = true,
511
+ pattern,
512
+ screenshot = false,
513
+ screenshotText = "",
514
+ screenshotFullPage = true,
515
+ } = options_json;
516
+
517
+ if (!page) throw new Error("Page not initialized");
518
+
519
+ if (isPlaywrightRunner()) {
520
+ await __allureAny_wait.step(
521
+ `Web: Wait for Header -header: ${header} -text: ${resolvedHeaderText} -options: ${JSON.stringify(
522
+ options_json
523
+ )}`,
524
+ async () => {
525
+ await doWaitForHeader();
526
+ }
527
+ );
528
+ } else {
529
+ await doWaitForHeader();
530
+ }
531
+
532
+ async function doWaitForHeader() {
533
+ // Use webLocResolver for header locator resolution or direct Locator
534
+ const target =
535
+ typeof header === "string"
536
+ ? await webLocResolver(
537
+ "header",
538
+ header,
539
+ page,
540
+ pattern,
541
+ actionTimeout
542
+ )
543
+ : header;
544
+
545
+ const start = Date.now();
546
+ let found = false;
547
+ let lastActualText = "";
548
+
549
+ while (Date.now() - start < actionTimeout) {
550
+ try {
551
+ // Check if header is visible and get its text
552
+ if (await target.isVisible()) {
553
+ const actualText = (await target.innerText()).trim();
554
+ lastActualText = actualText;
555
+
556
+ let expected = resolvedHeaderText;
557
+ let actual = actualText;
558
+
559
+ // Apply case sensitivity
560
+ if (ignoreCase) {
561
+ expected = expected.toLowerCase();
562
+ actual = actual.toLowerCase();
563
+ }
564
+
565
+ // Check for match
566
+ const isMatch = partialMatch
567
+ ? actual.includes(expected)
568
+ : actual === expected;
569
+
570
+ if (isMatch) {
571
+ found = true;
572
+ await attachLog(
573
+ `✅ Header found: "${actualText}" matches expected "${resolvedHeaderText}"`,
574
+ "text/plain"
575
+ );
576
+ break;
577
+ }
578
+ }
579
+
580
+ // Wait before next check
581
+ await new Promise((resolve) => setTimeout(resolve, 300));
582
+ } catch (error) {
583
+ // Continue waiting if there's an error
584
+ await new Promise((resolve) => setTimeout(resolve, 300));
585
+ }
586
+ }
587
+
588
+ if (!found) {
589
+ const msg = `❌ Header text "${resolvedHeaderText}" did not appear at locator "${header}" within ${actionTimeout}ms. Last seen: "${lastActualText}"`;
590
+ await attachLog(msg, "text/plain");
591
+ throw new Error(msg);
592
+ }
593
+
594
+ await processScreenshot(
595
+ page,
596
+ screenshot,
597
+ screenshotText || `Waited for header: ${resolvedHeaderText}`,
598
+ screenshotFullPage
599
+ );
600
+ }
601
+ }
602
+
603
+ /**
604
+ * Web: Wait for Input -field: {param} -state: {param} (enabled or disabled) -options: {param}
605
+ *
606
+ * Waits for an input field to become 'enabled' or 'disabled'.
607
+ *
608
+ * @param page - Playwright Page instance
609
+ * @param field - Locator or label of the input field
610
+ * @param state - Desired state: 'enabled' or 'disabled'
611
+ * @param options - Optional string or object:
612
+ * - actionTimeout: [number] Optional timeout in milliseconds for waiting. Default: from [config] or 30000.
613
+ * - pattern: [string] Optional pattern to refine element search.
614
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
615
+ * - screenshotText: [string] Description for the screenshot.
616
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
617
+ * - smartIQ_refreshLoc: optional override for locator refresh key
618
+ */
619
+ export async function waitForInputState(
620
+ page: Page,
621
+ field: string | Locator,
622
+ state: "enabled" | "disabled",
623
+ options?: string | Record<string, any>
624
+ ) {
625
+ const options_json =
626
+ typeof options === "string" ? parseLooseJson(options) : options || {};
627
+
628
+ const {
629
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
630
+ pattern,
631
+ screenshot = false,
632
+ screenshotText = "",
633
+ screenshotFullPage = true,
634
+ smartIQ_refreshLoc = "",
635
+ } = options_json;
636
+
637
+ if (isPlaywrightRunner()) {
638
+ await __allureAny_wait.step(
639
+ `Web: Wait for Input -field: ${field} -state: ${state} (enabled or disabled) -options: ${JSON.stringify(
640
+ options_json
641
+ )}`,
642
+ async () => {
643
+ await doWaitForInputState();
644
+ }
645
+ );
646
+ } else {
647
+ await doWaitForInputState();
648
+ }
649
+
650
+ async function doWaitForInputState() {
651
+ if (!page) throw new Error("Page not initialized");
652
+
653
+ const target =
654
+ typeof field === "string"
655
+ ? await webLocResolver(
656
+ "input",
657
+ field,
658
+ page,
659
+ pattern,
660
+ actionTimeout,
661
+ smartIQ_refreshLoc
662
+ )
663
+ : field;
664
+
665
+ try {
666
+ if (state === "enabled") {
667
+ await expect(target).toBeEnabled({ timeout: actionTimeout });
668
+ } else if (state === "disabled") {
669
+ await expect(target).toBeDisabled({ timeout: actionTimeout });
670
+ } else {
671
+ throw new Error(
672
+ `Invalid state "${state}". Use "enabled" or "disabled".`
673
+ );
674
+ }
675
+ } catch (e) {
676
+ throw new Error(
677
+ `❌ Input "${field}" did not reach state "${state}" within ${actionTimeout}ms. ${e}`
678
+ );
679
+ }
680
+
681
+ await processScreenshot(
682
+ page,
683
+ screenshot,
684
+ screenshotText || `Waited for input state: ${state}`,
685
+ screenshotFullPage
686
+ );
687
+ }
688
+ }
689
+
690
+ /**
691
+ * Web: Wait for URL -url: {param} -options: {param}
692
+ *
693
+ * Waits until the current page URL matches or contains the specified string or regex.
694
+ *
695
+ * @param url - The expected URL or substring/regex to match (e.g., "/dashboard", "https://example.com/page").
696
+ * @param options - Optional JSON string or object:
697
+ * - actionTimeout: [number] Timeout in ms to wait for URL. Default: 30000.
698
+ * - match: [string] "exact" | "contains" . Default: "contains".
699
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
700
+ * - screenshot: [boolean] If true, captures screenshot. Default: false.
701
+ * - screenshotText: [string] Custom screenshot label.
702
+ * - screenshotFullPage: [boolean] Full page screenshot. Default: true.
703
+ */
704
+ export async function waitForUrl(
705
+ page: Page,
706
+ url: string,
707
+ options?: string | Record<string, any>
708
+ ): Promise<void> {
709
+ const options_json =
710
+ typeof options === "string" ? parseLooseJson(options) : options || {};
711
+ const {
712
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
713
+ match = "contains",
714
+ screenshot = false,
715
+ screenshotText,
716
+ screenshotFullPage = true,
717
+ } = options_json;
718
+
719
+ if (isPlaywrightRunner()) {
720
+ await __allureAny_wait.step(
721
+ `Web: Wait for URL -url: ${url} -options: ${JSON.stringify(
722
+ options_json
723
+ )}`,
724
+ async () => {
725
+ await doWaitForUrl();
726
+ }
727
+ );
728
+ } else {
729
+ await doWaitForUrl();
730
+ }
731
+
732
+ async function doWaitForUrl() {
733
+ try {
734
+ if (match === "exact") {
735
+ await page.waitForURL(url.toString(), { timeout: actionTimeout });
736
+ } else if (match === "contains") {
737
+ const cleanUrl = url.replace(/^\/|\/$/g, "");
738
+ const regexUrl = new RegExp(escapeRegExp(cleanUrl), "i");
739
+ await expect(page).toHaveURL(regexUrl, { timeout: actionTimeout });
740
+ }
741
+ } catch (error) {
742
+ throw new Error(`⚠️ waitForUrl failed: ${error.message}`);
743
+ }
744
+ await waitForPageToLoad(page);
745
+ await processScreenshot(
746
+ page,
747
+ screenshot,
748
+ screenshotText || "",
749
+ screenshotFullPage
750
+ );
751
+ }
752
+ }
753
+
754
+
755
+ function escapeRegExp(str: string): string {
756
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
757
+ }