@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,2287 @@
1
+ /**
2
+ * @file validationActions.ts
3
+ *
4
+ * Validation helpers for PlayQ web actions.
5
+ * Provides see/don't see, page title checks, header/text verifications,
6
+ * input value checks, tab field validations, toast checks, and more with
7
+ * runner-aware step wrappers and screenshot options.
8
+ *
9
+ * Authors: PlayQ Team
10
+ * Version: v1.0.0
11
+ */
12
+ import * as allure from "allure-js-commons";
13
+ import { Page, Locator } from "@playwright/test";
14
+ import { vars, webLocResolver, comm, logFixture } from "../../../global";
15
+ import { processScreenshot } from "./screenshotActions";
16
+ import { attachLog, waitInMilliSeconds } from '../comm/commonActions';
17
+ import { waitForEnabled, waitForPageToLoad } from './waitActions';
18
+ import { expect } from '@playwright/test';
19
+ import { parseLooseJson } from '../../bundle/vars';
20
+ import { Verify } from 'node:crypto';
21
+
22
+ const config: any = {};
23
+
24
+
25
+ function isPlaywrightRunner() { return process.env.TEST_RUNNER === 'playwright'; }
26
+ const __allureAny_val: any = allure as any;
27
+ if (typeof __allureAny_val.step !== 'function') { __allureAny_val.step = async (_n: string, f: any) => await f(); }
28
+ // Allure compatibility shim: if step is unavailable, just run the body
29
+ const __allureAny_web: any = allure as any;
30
+ if (typeof __allureAny_web.step !== 'function') {
31
+ __allureAny_web.step = async (_name: string, fn: any) => await fn();
32
+ }
33
+
34
+ export async function see(page: Page, text: string, options?: string | Record<string, any>) {
35
+ const resolvedText = vars.replaceVariables(text);
36
+ const options_json = typeof options === "string" ? vars.parseLooseJson(options) : options || {};
37
+ const { actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")) || 30000, partialMatch = false, ignoreCase = false, assert = true, screenshot = true, screenshotText = "", screenshotFullPage = true } = options_json;
38
+ const stepName = `Web: Verify text on page -text: ${resolvedText} -options: ${JSON.stringify(options_json)}`;
39
+ async function run() {
40
+ if (!page) throw new Error("Page not initialized");
41
+ let matched = false;
42
+ let allText = await page.textContent("body");
43
+ let actual = allText || "";
44
+ let expected = resolvedText;
45
+ if (ignoreCase) { actual = actual.toLowerCase(); expected = expected.toLowerCase(); }
46
+ matched = partialMatch ? actual.includes(expected) : actual.includes(expected);
47
+ if (!matched) {
48
+ const message = `❌ Text "${resolvedText}" not found in page content.`;
49
+ await attachLog(message, "text/plain");
50
+ if (assert !== false) throw new Error(message);
51
+ } else {
52
+ await attachLog(`✅ Text "${resolvedText}" found in page.`, "text/plain");
53
+ }
54
+ await processScreenshot(page, screenshot, screenshotText || `Verify text in page: ${resolvedText}`, screenshotFullPage);
55
+ }
56
+ if (isPlaywrightRunner()) { await __allureAny_val.step(stepName, run); } else { await run(); }
57
+ }
58
+
59
+ export async function dontSee(page: Page, text: string, options?: string | Record<string, any>) {
60
+ const options_json = typeof options === 'string' ? vars.parseLooseJson(options) : options || {};
61
+ const stepName = `Web: Dont See -text: ${text}`;
62
+ if (isPlaywrightRunner()) {
63
+ await __allureAny_val.step(stepName, async () => { await doDontSee(); });
64
+ } else {
65
+ await doDontSee();
66
+ }
67
+ async function doDontSee() {
68
+ const content = await page.content();
69
+ if (content.includes(text)) {
70
+ await attachLog(`❌ Text found but expected to be absent: ${text}`, "text/plain");
71
+ throw new Error(`Text '${text}' should not be visible`);
72
+ }
73
+ await attachLog(`✅ Text not visible as expected: ${text}`, "text/plain");
74
+ }
75
+ }
76
+
77
+ export async function count(page: Page, field: string | Locator, options?: string | Record<string, any>) {
78
+ const options_json = typeof options === 'string' ? vars.parseLooseJson(options) : options || {};
79
+ const locator = typeof field === 'string'
80
+ ? await webLocResolver(options_json?.fieldType || '', field, page, options_json?.pattern, typeof options_json?.actionTimeout === 'number' ? options_json.actionTimeout : undefined, options_json?.smartAiRefresh || '')
81
+ : field;
82
+ const stepName = `Web: Count Elements -field: ${typeof field === 'string' ? field : '<locator>'}`;
83
+ let result = 0;
84
+ if (isPlaywrightRunner()) {
85
+ await __allureAny_val.step(stepName, async () => { result = await locator.count(); });
86
+ } else {
87
+ result = await locator.count();
88
+ }
89
+ return result;
90
+ }
91
+
92
+ /**
93
+ * Web: See Page Title -text: {param} -options: {param}
94
+ *
95
+ * Verifies the page title matches the expected text.
96
+ * Supports exact and partial match, case sensitivity, and screenshot capture.
97
+ *
98
+ * @param page Playwright Page instance
99
+ * @param expectedTitle The expected page title to match (e.g., "Your store").
100
+ * @param options Optional JSON string or object:
101
+ * - partialMatch: [boolean] Perform partial match on title (default: false)
102
+ * - ignoreCase: [boolean] Case-insensitive comparison (default: false)
103
+ * - assert: [boolean] If false, logs failure but does not throw (default: true)
104
+ * - screenshot: [boolean] Capture a screenshot (default: true)
105
+ * - screenshotText: [string] Description for screenshot
106
+ * - screenshotFullPage: [boolean] Full page screenshot (default: true)
107
+ *
108
+ * @example
109
+ * Web: See Page Title -text: "Your store" -options: "{partialMatch: true, ignoreCase: false, assert: true}"
110
+ */
111
+ export async function seePageTitle(page: Page, expectedTitle: string, options?: string | Record<string, any>) {
112
+ const options_json = typeof options === "string" ? vars.parseLooseJson(options) : options || {};
113
+ const { partialMatch = false, ignoreCase = false, assert = true, screenshot = true, screenshotText = "", screenshotFullPage = true } = options_json;
114
+ const stepName = `Web: See Page Title -text: ${expectedTitle} -options: ${JSON.stringify(options_json)}`;
115
+ async function run() {
116
+ let actualTitle = await page.title();
117
+ let expected = vars.replaceVariables(expectedTitle);
118
+ let actual = actualTitle;
119
+ if (ignoreCase) { expected = expected.toLowerCase(); actual = actual.toLowerCase(); }
120
+ if (partialMatch ? actual.includes(expected) : actual === expected) {
121
+ await comm.attachLog(`✅ Page title matched: expected: "${expectedTitle}", found: "${actualTitle}"`, "text/plain", "Validation");
122
+ } else {
123
+ await comm.attachLog(`❌ Page title mismatch: expected: "${expectedTitle}", found: "${actualTitle}"`, "text/plain", "Validation");
124
+ if (assert !== false) throw new Error(`❌ Page title verification failed`);
125
+ }
126
+ await processScreenshot(page, screenshot, screenshotText || `Verify page title: ${expectedTitle}`, screenshotFullPage);
127
+ }
128
+ if (isPlaywrightRunner()) { await __allureAny_val.step(stepName, run); } else { await run(); }
129
+ }
130
+
131
+ /**
132
+ * Web: Verify page title -text: {param} -options: {param}
133
+ *
134
+ * Verifies the page title matches the expected text.
135
+ *
136
+ * @param expectedTitle - The expected page title to match (e.g., "Your store").
137
+ * @param options - Optional JSON string or object, supporting fields:
138
+ * - partial_check: [boolean] Perform partial match (default: false).
139
+ * - ignoreCase: [boolean] Case-sensitive match (default: true).
140
+ * - assert: [boolean] If false, continues the test even if the verification fails. Default: true.
141
+ * - screenshot: [boolean] Capture screenshot after verification (default: true).
142
+ * - screenshotText: [string] Description for screenshot attachment.
143
+ * - screenshotFullPage: [boolean] Full page screenshot (default: true).
144
+ *
145
+ * Example usage:
146
+ * * Web: Verify page title -text: "Your store" -options: "{partial_check: true, ignoreCase: false, assert: true}"
147
+ */
148
+ export async function verifyPageTitle(
149
+ page: Page,
150
+ expectedTitle: string,
151
+ options?: string | Record<string, any>
152
+ ) {
153
+ const options_json =
154
+ typeof options === "string" ? parseLooseJson(options) : options || {};
155
+ const {
156
+ partialMatch = false,
157
+ ignoreCase = false,
158
+ assert = true,
159
+ screenshot = true,
160
+ screenshotText = "",
161
+ screenshotFullPage = true,
162
+ } = options_json;
163
+
164
+ if (isPlaywrightRunner()) {
165
+ await __allureAny_val.step(
166
+ `Web: Verify page title -text: ${expectedTitle} -options: ${JSON.stringify(
167
+ options_json
168
+ )}`,
169
+ async () => {
170
+ await doVerifyPageTitle();
171
+ }
172
+ );
173
+ } else {
174
+ await doVerifyPageTitle();
175
+ }
176
+
177
+ async function doVerifyPageTitle() {
178
+ await waitForPageToLoad(page);
179
+ let actualTitle = await page.title();
180
+ let expected = vars.replaceVariables(expectedTitle);
181
+ let actual = actualTitle;
182
+
183
+ if (!ignoreCase) {
184
+ expected = expected.toLowerCase();
185
+ actual = actual.toLowerCase();
186
+ }
187
+
188
+ if (partialMatch ? actual.includes(expected) : actual === expected) {
189
+ await attachLog(
190
+ `✅ Page title matched: expected: "${expectedTitle}", found: "${actualTitle}"`,
191
+ "text/plain"
192
+ );
193
+ } else {
194
+ await attachLog(
195
+ `❌ Page title mismatch: expected: "${expectedTitle}", found: "${actualTitle}"`,
196
+ "text/plain"
197
+ );
198
+ if (assert !== false) {
199
+ throw new Error(`❌ Page title verification failed`);
200
+ }
201
+ }
202
+
203
+ await processScreenshot(
204
+ page,
205
+ screenshot,
206
+ screenshotText,
207
+ screenshotFullPage
208
+ );
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Web: Verify header text -field: {param} -options: {param}
214
+ *
215
+ * Verifies that a header element's text (e.g., h1, h2, h3) matches the expected text. Supports partial or exact match, case sensitivity, and optional screenshot capture. The field parameter is the expected text, while pattern refines element selection if needed.
216
+ *
217
+ * @param field - The expected header text to match (e.g., "Welcome", "Dashboard").
218
+ * @param options - Optional JSON string or object:
219
+ * - actionTimeout: [number] Optional Action timeout in milliseconds for waiting. Default: Configured timeout.
220
+ * - navigationTimeout: [number] Optional Navigation timeout in milliseconds for waiting. Default: Configured timeout.
221
+ * - partialMatch: [boolean] Perform a partial match instead of an exact match. Default: false.
222
+ * - pattern: [string] Override the default pattern from config for element resolution. Default: Configured pattern in config.
223
+ * - ignoreCase: [boolean] Perform a case-sensitive match. Default: false.
224
+ * - assert: [boolean] If false, continues the test even if the verification fails. Default: true.
225
+ * - locator: [string] Optional locator to refine element search. Default: "". Eg:locator: locator: "xpath=(//h3[@class='module-title'])[1]"
226
+ * - headerType: [string] Specify a header level (e.g., "h1", "h2", "h3"). Default: Checks all headers from h1 to h4.
227
+ * - screenshot: [boolean] Capture a screenshot during verification. Default: true.
228
+ * - screenshotText: [string] Text description for the screenshot. Default: "".
229
+ * - screenshotFullPage: [boolean] Capture a full page screenshot. Default: true.
230
+ *
231
+ * @example
232
+ * Web: Verify header text -field: "Your Account Has Been Created!" -options: "{partialMatch: true, screenshot: true, screenshotText: 'After account creation', locator: "xpath=(//h3[@class='module-title'])[1]" }"
233
+ */
234
+ export async function verifyHeaderText(
235
+ page: Page,
236
+ expectedText: string,
237
+ options?: string | Record<string, any>
238
+ ) {
239
+ let resolved_expectedText = vars.replaceVariables(expectedText);
240
+ const options_json =
241
+ typeof options === "string" ? parseLooseJson(options) : options || {};
242
+ const {
243
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
244
+ navigationTimeout = Number(vars.getConfigValue("testExecution.navigationTimeout")),
245
+ partialMatch = false,
246
+ ignoreCase = false,
247
+ assert = true,
248
+ screenshot = true,
249
+ screenshotText = "",
250
+ screenshotFullPage = true,
251
+ } = options_json;
252
+
253
+ if (isPlaywrightRunner()) {
254
+ await __allureAny_val.step(
255
+ `Web: Verify header -text: ${resolved_expectedText} -options: ${JSON.stringify(
256
+ options_json
257
+ )}`,
258
+ async () => {
259
+ await doverifyHeaderText();
260
+ }
261
+ );
262
+ } else {
263
+ await doverifyHeaderText();
264
+ }
265
+ async function doverifyHeaderText() {
266
+ if (!page) throw new Error("Page not initialized");
267
+ await waitForPageToLoad(page, navigationTimeout);
268
+ await page.waitForSelector("h1, h2, h3, h4, h5, h6", {
269
+ timeout: actionTimeout,
270
+ });
271
+
272
+ const startTime = Date.now();
273
+ let matchFound = false;
274
+
275
+ while (Date.now() - startTime < actionTimeout) {
276
+ const target = page.locator("h1, h2, h3, h4, h5, h6");
277
+ const count = await target.count();
278
+ for (let i = 0; i < count; i++) {
279
+ let actualText = await target.nth(i).innerText();
280
+ let expected = resolved_expectedText;
281
+ let actual = actualText;
282
+ if (!ignoreCase) {
283
+ expected = expected.toLowerCase();
284
+ actual = actual.toLowerCase();
285
+ }
286
+ if (partialMatch ? actual.includes(expected) : actual === expected) {
287
+ await attachLog(`✅ Header matched: "${actualText}"`, "text/plain");
288
+ matchFound = true;
289
+ break;
290
+ }
291
+ }
292
+ if (matchFound) break;
293
+ await waitInMilliSeconds(500); // Wait for 500ms before retrying
294
+ }
295
+
296
+ if (!matchFound) {
297
+ const msg = `❌ Header text verification failed for "${resolved_expectedText}"`;
298
+ await attachLog(msg, "text/plain");
299
+ if (assert !== false) throw new Error(msg);
300
+ }
301
+ await processScreenshot(
302
+ page,
303
+ screenshot,
304
+ screenshotText,
305
+ screenshotFullPage
306
+ );
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Web: Verify text on page -text: {param} -options: {param}
312
+ *
313
+ * Verifies that the provided text is present somewhere in the page.
314
+ *
315
+ * @param text - The expected text to search for on the page.
316
+ * @param options - Optional JSON string or object:
317
+ * - actionTimeout: [number] Optional timeout in milliseconds for waiting. Default: Configured timeout.
318
+ * - partialMatch: [boolean] Perform a partial match instead of an exact match. Default: false.
319
+ * - ignoreCase: [boolean] Perform a case-sensitive match. Default: false.
320
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
321
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
322
+ * - screenshotText: [string] Description for the screenshot.
323
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
324
+ *
325
+ * @example
326
+ * Web: Verify text on page -text: "Welcome back!" -options: "{partialMatch: true, screenshot: true, screenshotText: 'Verifying greeting'}"
327
+ */
328
+ export async function verifyTextOnPage(
329
+ page: Page,
330
+ text: string,
331
+ options?: string | Record<string, any>
332
+ ) {
333
+ const resolvedText = vars.replaceVariables(text);
334
+ const options_json =
335
+ typeof options === "string" ? parseLooseJson(options) : options || {};
336
+ const {
337
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
338
+ partialMatch = false,
339
+ ignoreCase = false,
340
+ assert = true,
341
+ screenshot = true,
342
+ screenshotText = "",
343
+ screenshotFullPage = true,
344
+ } = options_json;
345
+
346
+ if (isPlaywrightRunner()) {
347
+ await __allureAny_val.step(
348
+ `Web: Verify text on page -text: ${resolvedText} -options: ${JSON.stringify(
349
+ options_json
350
+ )}`,
351
+ async () => {
352
+ await doVerifyTextOnPage();
353
+ }
354
+ );
355
+ } else {
356
+ await doVerifyTextOnPage();
357
+ }
358
+
359
+ async function doVerifyTextOnPage() {
360
+ if (!page) throw new Error("Page not initialized");
361
+ await waitForPageToLoad(page, actionTimeout);
362
+ let matched = false;
363
+ let allText = await page.textContent("body");
364
+ let actual = allText || "";
365
+ let expected = resolvedText;
366
+ if (!ignoreCase) {
367
+ actual = actual.toLowerCase();
368
+ expected = expected.toLowerCase();
369
+ }
370
+ matched = partialMatch
371
+ ? actual.includes(expected)
372
+ : actual.includes(expected);
373
+ if (!matched) {
374
+ const message = `❌ Text "${resolvedText}" not found in page content.`;
375
+ await attachLog(message, "text/plain");
376
+ if (assert !== false) throw new Error(message);
377
+ } else {
378
+ await attachLog(`✅ Text "${resolvedText}" found in page.`, "text/plain");
379
+ }
380
+ await processScreenshot(
381
+ page,
382
+ screenshot,
383
+ screenshotText || `Verify text in page: ${resolvedText}`,
384
+ screenshotFullPage
385
+ );
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Web: Verify text not empty at location -field: {param} -options: {param}
391
+ *
392
+ * Verifies that the text content of an element is not empty.
393
+ *
394
+ * @param page - Playwright Page instance
395
+ * @param field - The label, id, name, or selector of the element to verify
396
+ * @param options - Optional JSON string or object:
397
+ * - actionTimeout: [number] Optional timeout in milliseconds. Default: Configured timeout.
398
+ * - pattern: [string] Optional pattern to refine element search.
399
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
400
+ * - screenshot: [boolean] Capture screenshot. Default: true.
401
+ * - screenshotText: [string] Screenshot description.
402
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
403
+ */
404
+ export async function verifyTextNotEmptyAtLocation(
405
+ page: Page,
406
+ field: string | Locator,
407
+ options?: string | Record<string, any>
408
+ ) {
409
+ const options_json =
410
+ typeof options === "string" ? parseLooseJson(options) : options || {};
411
+ const {
412
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
413
+ pattern,
414
+ assert = true,
415
+ screenshot = true,
416
+ screenshotText = "",
417
+ screenshotFullPage = true,
418
+ } = options_json;
419
+
420
+ if (isPlaywrightRunner()) {
421
+ await __allureAny_val.step(
422
+ `Web: Verify text not empty at location -field: ${field} -options: ${JSON.stringify(
423
+ options_json
424
+ )}`,
425
+ async () => {
426
+ await doVerifyTextNotEmptyAtLocation();
427
+ }
428
+ );
429
+ } else {
430
+ await doVerifyTextNotEmptyAtLocation();
431
+ }
432
+
433
+ async function doVerifyTextNotEmptyAtLocation() {
434
+ if (!page) throw new Error("Page not initialized");
435
+ await waitForPageToLoad(page, actionTimeout);
436
+
437
+ const target =
438
+ typeof field === "string"
439
+ ? await webLocResolver("text", field, page, pattern, actionTimeout)
440
+ : field;
441
+
442
+ await target.waitFor({ state: "visible", timeout: actionTimeout });
443
+
444
+ const actualText = (await target.innerText()).trim();
445
+
446
+ if (!actualText) {
447
+ await attachLog(`❌ Text at location "${field}" is empty!`, "text/plain");
448
+ if (assert !== false) {
449
+ throw new Error(`❌ Text at location "${field}" is empty.`);
450
+ }
451
+ } else {
452
+ await attachLog(
453
+ `✅ Text at location "${field}" is not empty: "${actualText}"`,
454
+ "text/plain"
455
+ );
456
+ }
457
+
458
+ await processScreenshot(
459
+ page,
460
+ screenshot,
461
+ screenshotText,
462
+ screenshotFullPage
463
+ );
464
+ }
465
+ }
466
+
467
+ /**
468
+ * Web: Verify text at location -field: {param} -value: {param} -options: {param}
469
+ *
470
+ * Verifies that the text content of an element matches the expected value.
471
+ *
472
+ * @param field - The label, id, name, or selector of the element to verify.
473
+ * @param expectedText - The expected text content.
474
+ * @param options - Optional JSON string or object:
475
+ * - actionTimeout: [number] Optional timeout in milliseconds. Default: Configured timeout.
476
+ * - partialMatch: [boolean] If true, performs substring match. Default: false.
477
+ * - pattern: [string] Optional pattern to refine element search.
478
+ * - ignoreCase: [boolean] Whether the match is case-sensitive. Default: true.
479
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
480
+ * - screenshot: [boolean] Capture screenshot. Default: true.
481
+ * - screenshotText: [string] Screenshot description.
482
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
483
+ */
484
+ export async function verifyTextAtLocation(
485
+ page: Page,
486
+ field: string | Locator,
487
+ expectedText: string,
488
+ options?: string | Record<string, any>
489
+ ) {
490
+ const options_json =
491
+ typeof options === "string" ? parseLooseJson(options) : options || {};
492
+
493
+ const {
494
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
495
+ partialMatch = false,
496
+ pattern,
497
+ ignoreCase = true,
498
+ assert = true,
499
+ screenshot = true,
500
+ screenshotText = "",
501
+ screenshotFullPage = true,
502
+ } = options_json;
503
+ const resolvedExpectedValue = vars.replaceVariables(expectedText);
504
+ if (isPlaywrightRunner()) {
505
+ await __allureAny_val.step(
506
+ `Web: Verify text at location -field: ${field} -value: ${resolvedExpectedValue} -options: ${JSON.stringify(
507
+ options_json
508
+ )}`,
509
+ async () => {
510
+ await doVerifyTextAtLocation();
511
+ }
512
+ );
513
+ } else {
514
+ await doVerifyTextAtLocation();
515
+ }
516
+
517
+ async function doVerifyTextAtLocation() {
518
+ if (!page) throw new Error("Page not initialized");
519
+ await waitForPageToLoad(page, actionTimeout);
520
+
521
+ const target =
522
+ typeof field === "string"
523
+ ? await webLocResolver("text", field, page, pattern, actionTimeout)
524
+ : field;
525
+
526
+ await target.waitFor({ state: "visible", timeout: actionTimeout });
527
+
528
+ const tagName = await target.evaluate(el => el.tagName.toLowerCase());
529
+ let actualText: string;
530
+ if (tagName === 'textarea' || tagName === 'input') {
531
+ actualText = (await target.inputValue()).trim();
532
+ } else {
533
+ actualText = (await target.innerText()).trim();
534
+ }
535
+
536
+ const expected = vars.getValue(resolvedExpectedValue).trim();
537
+
538
+ let match = false;
539
+ if (ignoreCase) {
540
+ match = partialMatch
541
+ ? actualText.includes(expected)
542
+ : actualText === expected;
543
+ } else {
544
+ match = partialMatch
545
+ ? actualText.toLowerCase().includes(expected.toLowerCase())
546
+ : actualText.toLowerCase() === expected.toLowerCase();
547
+ }
548
+
549
+ if (match) {
550
+ await attachLog(`✅ Text matched: "${actualText}"`, "text/plain");
551
+ } else {
552
+ await attachLog(
553
+ `❌ Text mismatch: expected "${expected}", got "${actualText}"`,
554
+ "text/plain"
555
+ );
556
+ if (assert !== false) {
557
+ throw new Error(`❌ Text mismatch for field "${field}"`);
558
+ }
559
+ }
560
+
561
+ await processScreenshot(
562
+ page,
563
+ screenshot,
564
+ screenshotText,
565
+ screenshotFullPage
566
+ );
567
+ }
568
+ }
569
+
570
+ /**
571
+ * Web: Verify input field is blank -field: {param} -options: {param}
572
+ *
573
+ * Verifies that an input field is blank (empty value).
574
+ *
575
+ * @param page - Playwright Page instance
576
+ * @param field - The label, id, name, selector, or Locator for the input field
577
+ * @param options - Optional JSON string or object:
578
+ * - actionTimeout: [number] Optional timeout in milliseconds. Default: Configured timeout.
579
+ * - pattern: [string] Optional pattern to refine element search.
580
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
581
+ * - screenshot: [boolean] Capture screenshot. Default: true.
582
+ * - screenshotText: [string] Screenshot description.
583
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
584
+ */
585
+ export async function verifyInputFieldIsBlank(
586
+ page: Page,
587
+ field: string | Locator,
588
+ options?: string | Record<string, any>
589
+ ) {
590
+ const options_json =
591
+ typeof options === "string" ? parseLooseJson(options) : options || {};
592
+ const {
593
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
594
+ pattern,
595
+ assert = true,
596
+ screenshot = true,
597
+ screenshotText = "",
598
+ screenshotFullPage = true,
599
+ } = options_json;
600
+
601
+ if (isPlaywrightRunner()) {
602
+ await __allureAny_val.step(
603
+ `Web: Verify input field is blank -field: ${field} -options: ${JSON.stringify(
604
+ options_json
605
+ )}`,
606
+ async () => {
607
+ await doVerifyInputFieldIsBlank();
608
+ }
609
+ );
610
+ } else {
611
+ await doVerifyInputFieldIsBlank();
612
+ }
613
+
614
+ async function doVerifyInputFieldIsBlank() {
615
+ if (!page) throw new Error("Page not initialized");
616
+ await waitForPageToLoad(page, actionTimeout);
617
+
618
+ const target =
619
+ typeof field === "string"
620
+ ? await webLocResolver("input", field, page, pattern, actionTimeout)
621
+ : field;
622
+
623
+ await target.waitFor({ state: "visible", timeout: actionTimeout });
624
+
625
+ const actualValue = (await target.inputValue()).trim();
626
+
627
+ if (actualValue === "") {
628
+ await attachLog(
629
+ `✅ Input field "${field}" is blank.`,
630
+ "text/plain"
631
+ );
632
+ } else {
633
+ await attachLog(
634
+ `❌ Input field "${field}" is not blank. Value: "${actualValue}"`,
635
+ "text/plain"
636
+ );
637
+ if (assert !== false) {
638
+ throw new Error(`❌ Input field "${field}" is not blank.`);
639
+ }
640
+ }
641
+
642
+ await processScreenshot(
643
+ page,
644
+ screenshot,
645
+ screenshotText,
646
+ screenshotFullPage
647
+ );
648
+ }
649
+ }
650
+
651
+ /**
652
+ * Web: Verify input field is present -field: {param} -options: {param}
653
+ *
654
+ * Verifies that an input field is present on the page, identified by label, text, id, name, or pattern.
655
+ *
656
+ * @param field - The label, text, id, name, or selector of the input field to verify (e.g., "Email", "Password").
657
+ * @param options - Optional JSON string or object:
658
+ * - actionTimeout: [number] Optional timeout in milliseconds for waiting. Default: Configured timeout.
659
+ * - pattern: [string] Optional pattern to refine element search. Default: Configured pattern.
660
+ * - assert: [boolean] If false, continues the test even if the verification fails. Default: true.
661
+ * - screenshot: [boolean] Capture a screenshot during verification. Default: true.
662
+ * - screenshotText: [string] Text description for the screenshot. Default: "".
663
+ * - screenshotFullPage: [boolean] Capture a full page screenshot. Default: true.
664
+ *
665
+ * @example
666
+ * Web: Verify input field is present -field: "Email" -options: "{screenshot: true, screenshotText: 'Verifying Email input field'}"
667
+ */
668
+ export async function verifyInputFieldPresent(
669
+ page: Page,
670
+ field: string | Locator,
671
+ options?: string | Record<string, any>
672
+ ) {
673
+ const options_json =
674
+ typeof options === "string" ? parseLooseJson(options) : options || {};
675
+ const {
676
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
677
+ pattern,
678
+ assert = true,
679
+ screenshot = true,
680
+ screenshotText = "",
681
+ screenshotFullPage = true,
682
+ } = options_json;
683
+
684
+ if (!page) throw new Error("Page not initialized");
685
+ await waitForPageToLoad(page, actionTimeout);
686
+
687
+ const target =
688
+ typeof field === "string"
689
+ ? await webLocResolver("input", field, page, pattern, actionTimeout)
690
+ : field;
691
+
692
+ const isVisible = await target.isVisible();
693
+
694
+ if (isVisible) {
695
+ await attachLog(
696
+ `✅ Input field "${field}" is present and visible.`,
697
+ "text/plain"
698
+ );
699
+ } else {
700
+ await attachLog(`❌ Input field "${field}" is not visible.`, "text/plain");
701
+ if (assert !== false) {
702
+ throw new Error(`❌ Input field "${field}" is not visible.`);
703
+ }
704
+ }
705
+ await processScreenshot(page, screenshot, screenshotText, screenshotFullPage);
706
+ }
707
+
708
+ /**
709
+ * Web: Verify input field value -field: {param} -value: {param} -options: {param}
710
+ *
711
+ * Verifies that the value of an input field matches the expected value.
712
+ *
713
+ * @param field - The label, id, name, or selector of the input field to verify.
714
+ * @param expectedValue - The expected value of the input field.
715
+ * @param options - Optional JSON string or object:
716
+ * - actionTimeout: [number] Optional timeout in milliseconds. Default: Configured timeout.
717
+ * - partialMatch: [boolean] If true, performs substring match. Default: false.
718
+ * - pattern: [string] Optional pattern to refine element search.
719
+ * - ignoreCase: [boolean] Whether the match is case-sensitive. Default: true.
720
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
721
+ * - screenshot: [boolean] Capture screenshot. Default: true.
722
+ * - screenshotText: [string] Screenshot description.
723
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
724
+ */
725
+ export async function verifyInputFieldValue(
726
+ page: Page,
727
+ field: string | Locator,
728
+ expectedValue: string,
729
+ options?: string | Record<string, any>
730
+ ) {
731
+ const options_json =
732
+ typeof options === "string" ? parseLooseJson(options) : options || {};
733
+ const {
734
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
735
+ partialMatch = false,
736
+ pattern,
737
+ ignoreCase = true,
738
+ assert = true,
739
+ screenshot = true,
740
+ screenshotText = "",
741
+ screenshotFullPage = true,
742
+ } = options_json;
743
+ const resolvedExpectedValue = vars.replaceVariables(expectedValue);
744
+
745
+ if (isPlaywrightRunner()) {
746
+ await __allureAny_val.step(
747
+ `Web: Verify input field value -field: ${field} -value: ${resolvedExpectedValue} -options: ${JSON.stringify(
748
+ options_json
749
+ )}`,
750
+ async () => {
751
+ await doVerifyInputFieldValue();
752
+ }
753
+ );
754
+ } else {
755
+ await doVerifyInputFieldValue();
756
+ }
757
+
758
+ async function doVerifyInputFieldValue() {
759
+ if (!page) throw new Error("Page not initialized");
760
+ await waitForPageToLoad(page, actionTimeout);
761
+
762
+ const target =
763
+ typeof field === "string"
764
+ ? await webLocResolver("input", field, page, pattern, actionTimeout)
765
+ : field;
766
+
767
+ await target.waitFor({ state: "visible", timeout: actionTimeout });
768
+
769
+ const tagName = await target.evaluate(el => el.tagName.toLowerCase());
770
+ let actualValue: string;
771
+ if (tagName === 'textarea' || tagName === 'input') {
772
+ actualValue = (await target.inputValue()).trim();
773
+ } else {
774
+ actualValue = (await target.innerText()).trim();
775
+ }
776
+
777
+ const expected = vars.getValue(resolvedExpectedValue).trim();
778
+
779
+ let match = false;
780
+ if (ignoreCase) {
781
+ match = partialMatch
782
+ ? actualValue.includes(expected)
783
+ : actualValue === expected;
784
+ } else {
785
+ match = partialMatch
786
+ ? actualValue.toLowerCase().includes(expected.toLowerCase())
787
+ : actualValue.toLowerCase() === expected.toLowerCase();
788
+ }
789
+
790
+ if (match) {
791
+ await attachLog(`✅ Input value matched: "${actualValue}"`, "text/plain");
792
+ } else {
793
+ await attachLog(
794
+ `❌ Input value mismatch: expected "${expected}", got "${actualValue}"`,
795
+ "text/plain"
796
+ );
797
+ if (assert !== false) {
798
+ throw new Error(`❌ Input value mismatch for field "${field}"`);
799
+ }
800
+ }
801
+
802
+ await processScreenshot(
803
+ page,
804
+ screenshot,
805
+ screenshotText,
806
+ screenshotFullPage
807
+ );
808
+ }
809
+ }
810
+
811
+ /**
812
+ * Web: Verify locked input field value -field: {param} -value: {param} -options: {param}
813
+ *
814
+ * Verifies that the value of an locked input field matches the expected value.
815
+ *
816
+ * @param field - The label, id, name, or selector of the input field to verify.
817
+ * @param expectedValue - The expected value of the input field.
818
+ * @param options - Optional JSON string or object:
819
+ * - actionTimeout: [number] Optional timeout in milliseconds. Default: Configured timeout.
820
+ * - partialMatch: [boolean] If true, performs substring match. Default: false.
821
+ * - pattern: [string] Optional pattern to refine element search.
822
+ * - ignoreCase: [boolean] Whether the match is case-sensitive. Default: true.
823
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
824
+ * - screenshot: [boolean] Capture screenshot. Default: true.
825
+ * - screenshotText: [string] Screenshot description.
826
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
827
+ */
828
+ export async function verifyLockedInputFieldValue(
829
+ page: Page,
830
+ field: string | Locator,
831
+ expectedValue: string,
832
+ options?: string | Record<string, any>
833
+ ) {
834
+ const options_json =
835
+ typeof options === "string" ? vars.parseLooseJson(options) : options || {};
836
+ const {
837
+ actionTimeout = config?.testExecution?.actionTimeout || Number(
838
+ vars.getConfigValue("testExecution.actionTimeout")
839
+ ) || 30000,
840
+ partialMatch = false,
841
+ pattern,
842
+ ignoreCase = false,
843
+ assert = true,
844
+ screenshot = true,
845
+ screenshotText = "",
846
+ screenshotFullPage = true,
847
+ } = options_json;
848
+ const resolvedExpectedValue = vars.replaceVariables(expectedValue);
849
+
850
+ if (isPlaywrightRunner()) {
851
+ await __allureAny_web.step(
852
+ `Web: Verify input field value -field: ${field} -value: ${resolvedExpectedValue} -options: ${JSON.stringify(
853
+ options_json
854
+ )}`,
855
+ async () => {
856
+ await doVerifyInputFieldValue();
857
+ }
858
+ );
859
+ } else {
860
+ await doVerifyInputFieldValue();
861
+ }
862
+
863
+ async function doVerifyInputFieldValue() {
864
+ if (!page) throw new Error("Page not initialized");
865
+ await waitForPageToLoad(page, actionTimeout);
866
+
867
+ const target =
868
+ typeof field === "string"
869
+ ? await webLocResolver("input", field, page, pattern, actionTimeout)
870
+ : field;
871
+
872
+ await target.waitFor({ state: "visible", timeout: actionTimeout });
873
+
874
+ const actualValue = (await target.inputValue()).trim();
875
+ const expected = vars.getValue(resolvedExpectedValue).trim();
876
+
877
+ let match = false;
878
+ if (ignoreCase) {
879
+ match = partialMatch
880
+ ? actualValue.includes(expected)
881
+ : actualValue === expected;
882
+ } else {
883
+ match = partialMatch
884
+ ? actualValue.toLowerCase().includes(expected.toLowerCase())
885
+ : actualValue.toLowerCase() === expected.toLowerCase();
886
+ }
887
+
888
+ if (match) {
889
+ await attachLog(`✅ Input value matched: "${actualValue}"`, "text/plain");
890
+ } else {
891
+ await attachLog(
892
+ `❌ Locked input value mismatch: expected "${expected}", got "${actualValue}"`,
893
+ "text/plain"
894
+ );
895
+ if (assert !== false) {
896
+ throw new Error(`❌ Locked input value mismatch for field "${field}"`);
897
+ }
898
+ }
899
+
900
+ await processScreenshot(
901
+ page,
902
+ screenshot,
903
+ screenshotText,
904
+ screenshotFullPage
905
+ );
906
+ }
907
+ }
908
+
909
+ /**
910
+ * Web: Verify Tab field Present -field: {param} -options: {param}
911
+ *
912
+ * Verifies that a "Tab" field is present on the page, identified by label, text, id, name, or pattern.
913
+ *
914
+ * @param field - The label, text, id, name, or selector of the tab to verify (e.g., "Overview", "Settings").
915
+ * @param options - Optional JSON string or object:
916
+ * - actionTimeout: [number] Optional timeout in milliseconds for waiting. Default: from [config] or 30000.
917
+ * - pattern: [string] Optional pattern to refine element search.
918
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
919
+ * - isPresent: [boolean] Check Tab is present on the page. Default: true.
920
+ * - isEnabled: [boolean] Check if Tab is enabled. Default: false.
921
+ * - isSelected: [boolean] Check if Tab is selected. Default: false.
922
+ * - isNotSelected: [boolean] Check if Tab is not selected. Default: false.
923
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
924
+ * - screenshotText: [string] Description for the screenshot.
925
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
926
+ */
927
+ export async function verifyTabField(
928
+ page: Page,
929
+ field: string | Locator,
930
+ options?: string | Record<string, any>
931
+ ) {
932
+ const options_json =
933
+ typeof options === "string" ? parseLooseJson(options) : options || {};
934
+ const {
935
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
936
+ pattern,
937
+ assert = true,
938
+ isPresent = true,
939
+ isEnabled = false,
940
+ isSelected = false,
941
+ isNotSelected = false,
942
+ screenshot = true,
943
+ screenshotText = "",
944
+ screenshotFullPage = true,
945
+ } = options_json;
946
+
947
+ if (isPlaywrightRunner()) {
948
+ await __allureAny_val.step(
949
+ ` Web: Verify Tab field Present -field: ${field} -options: ${JSON.stringify(
950
+ options_json
951
+ )}`,
952
+ async () => {
953
+ await doVerifyTabField();
954
+ }
955
+ );
956
+ } else {
957
+ await doVerifyTabField();
958
+ }
959
+
960
+ async function doVerifyTabField() {
961
+ if (!page) throw new Error("Page not initialized");
962
+ await waitForPageToLoad(page);
963
+
964
+ const target =
965
+ typeof field === "string"
966
+ ? await webLocResolver("tab", field, page, pattern, actionTimeout)
967
+ : field;
968
+ await waitForEnabled(target, actionTimeout);
969
+ let failureReason = "";
970
+
971
+ if (isPresent) {
972
+ const isVisible = await target.isVisible();
973
+ if (!isVisible) {
974
+ failureReason += `❌ Tab "${field}" is not visible.\n`;
975
+ } else {
976
+ await attachLog(`✅ Tab "${field}" is visible.`, "text/plain");
977
+ }
978
+ }
979
+
980
+ if (isEnabled) {
981
+ const isEnabled = await target.isEnabled();
982
+ if (!isEnabled) {
983
+ failureReason += `❌ Tab "${field}" is disabled.\n`;
984
+ } else {
985
+ await attachLog(`✅ Tab "${field}" is enabled.`, "text/plain");
986
+ }
987
+ }
988
+
989
+ if (isSelected) {
990
+ const ariaSelected = await target.getAttribute("aria-selected");
991
+ const tabIndex = await target.getAttribute("tabindex");
992
+ if (ariaSelected !== "true" || tabIndex !== "0") {
993
+ failureReason += `❌ Tab "${field}" is not selected (aria-selected != true).\n`;
994
+ } else {
995
+ await attachLog(
996
+ `✅ Tab "${field}" is selected (aria-selected = true).`,
997
+ "text/plain"
998
+ );
999
+ }
1000
+ }
1001
+
1002
+ if (isNotSelected) {
1003
+ const ariaSelected = await target.getAttribute("aria-selected");
1004
+ const tabIndex = await target.getAttribute("tabindex");
1005
+ if (ariaSelected !== "false" || tabIndex !== "-1") {
1006
+ failureReason += `❌ Tab "${field}" is focused (expected not focused).\n`;
1007
+ } else {
1008
+ await attachLog(`✅ Tab "${field}" is not focused.`, "text/plain");
1009
+ }
1010
+ }
1011
+
1012
+ if (failureReason) {
1013
+ await attachLog(failureReason.trim(), "text/plain");
1014
+ if (assert !== false) {
1015
+ throw new Error(failureReason.trim());
1016
+ }
1017
+ }
1018
+
1019
+ await processScreenshot(
1020
+ page,
1021
+ screenshot,
1022
+ screenshotText,
1023
+ screenshotFullPage
1024
+ );
1025
+ }
1026
+ }
1027
+
1028
+ /**
1029
+ * Web: Verify Toast Text Contains -text: {param} -options: {param}
1030
+ *
1031
+ * Verifies that a toast (notification) element appears on the page and contains the expected text.
1032
+ * Throws an error (or logs a warning if `assert: false`) if the text is not found.
1033
+ *
1034
+ * @param page - Playwright Page instance.
1035
+ * @param text - The expected substring to match within the toast notification (e.g., "Saved successfully").
1036
+ * @param options - Optional JSON string or object:
1037
+ * - actionTimeout: [number] Timeout in milliseconds to wait for toast visibility. Default: 30000.
1038
+ * - pattern: [string] Optional pattern to refine toast element search (e.g., class name or attribute).
1039
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
1040
+ * - screenshot: [boolean] Capture a screenshot after verification. Default: true.
1041
+ * - screenshotText: [string] Description for screenshot attachment.
1042
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
1043
+ *
1044
+ * @example
1045
+ * Web: Verify Toast Text Contains -text: "Saved successfully"
1046
+ */
1047
+ export async function verifyToastTextContains(
1048
+ page: Page,
1049
+ text: string,
1050
+ options?: any
1051
+ ) {
1052
+ const options_json =
1053
+ typeof options === "string" ? parseLooseJson(options) : options || {};
1054
+ const {
1055
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
1056
+ pattern,
1057
+ assert = true,
1058
+ screenshot = true,
1059
+ screenshotText = "",
1060
+ screenshotFullPage = true,
1061
+ } = options_json;
1062
+
1063
+ const target =
1064
+ typeof text === "string"
1065
+ ? await webLocResolver(
1066
+ "text",
1067
+ text,
1068
+ page,
1069
+ pattern,
1070
+ actionTimeout,
1071
+ "before"
1072
+ )
1073
+ : text;
1074
+
1075
+ await target.waitFor({ state: "visible", timeout: actionTimeout });
1076
+
1077
+ const actual = await target.textContent();
1078
+ if (!actual?.includes(text)) {
1079
+ await attachLog(
1080
+ `❌ Expected toast to contain "${text}", but got "${actual}"`,
1081
+ "text/plain"
1082
+ );
1083
+ if (assert) {
1084
+ throw new Error(
1085
+ `❌ Expected toast to contain "${text}", but got "${actual}"`
1086
+ );
1087
+ } else {
1088
+ await logFixture
1089
+ .getLogger()
1090
+ ?.warn(`❌ Expected toast to contain "${text}", but got "${actual}"`);
1091
+ }
1092
+ } else {
1093
+ await attachLog(`✅ Toast contains expected text: "${text}"`, "text/plain");
1094
+ }
1095
+ await processScreenshot(page, screenshot, screenshotText, screenshotFullPage);
1096
+ }
1097
+
1098
+ /**
1099
+ * Web: Verify text to disappear at location -field: {param} -text: {param} -options: {param}
1100
+ *
1101
+ * Verifies that the specified text disappears from the given field/locator within a timeout.
1102
+ *
1103
+ * @param page - Playwright Page instance
1104
+ * @param field - The selector or Locator where the text should disappear
1105
+ * @param textToDisappear - The text to wait for disappearance
1106
+ * @param options - Optional string or object:
1107
+ * - actionTimeout: [number] Timeout in ms (default: 30000)
1108
+ * - partialMatch: [boolean] If true, waits for substring match (default: false)
1109
+ * - ignoreCase: [boolean] If true, match is case-insensitive (default: true)
1110
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
1111
+ * - screenshot: [boolean] Capture screenshot after waiting (default: true)
1112
+ * - screenshotText: [string] Description for screenshot
1113
+ * - screenshotFullPage: [boolean] Full page screenshot (default: true)
1114
+ *
1115
+ * @example
1116
+ * await verifyTextToDisappearAtLocation(page, 'h1', 'Loading...', { actionTimeout: 10000, partialMatch: true });
1117
+ */
1118
+ export async function verifyTextToDisappearAtLocation(
1119
+ page: Page,
1120
+ field: string | Locator,
1121
+ textToDisappear: string,
1122
+ options?: string | Record<string, any>
1123
+ ) {
1124
+ const options_json =
1125
+ typeof options === "string" ? parseLooseJson(options) : options || {};
1126
+ const {
1127
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
1128
+ partialMatch = false,
1129
+ ignoreCase = true,
1130
+ assert = true,
1131
+ screenshot = true,
1132
+ screenshotText = "",
1133
+ screenshotFullPage = true,
1134
+ pattern,
1135
+ } = options_json;
1136
+
1137
+ if (isPlaywrightRunner()) {
1138
+ await __allureAny_val.step(
1139
+ `Web: Verify text to disappear at location -field: ${field} -text: ${textToDisappear} -options: ${JSON.stringify(
1140
+ options_json
1141
+ )}`,
1142
+ async () => {
1143
+ await doVerifyTextToDisappearAtLocation();
1144
+ }
1145
+ );
1146
+ } else {
1147
+ await doVerifyTextToDisappearAtLocation();
1148
+ }
1149
+
1150
+ async function doVerifyTextToDisappearAtLocation() {
1151
+ if (!page) throw new Error("Page not initialized");
1152
+ await waitForPageToLoad(page, actionTimeout);
1153
+
1154
+ const target =
1155
+ typeof field === "string"
1156
+ ? await webLocResolver("text", field, page, pattern, actionTimeout)
1157
+ : field;
1158
+
1159
+ const start = Date.now();
1160
+ let disappeared = false;
1161
+ let lastActual = "";
1162
+
1163
+ while (Date.now() - start < actionTimeout) {
1164
+ if (!(await target.isVisible().catch(() => false))) {
1165
+ disappeared = true;
1166
+ break;
1167
+ }
1168
+ const actualText = ((await target.innerText().catch(() => "")) || "").trim();
1169
+ lastActual = actualText;
1170
+ let expected = textToDisappear;
1171
+ let actual = actualText;
1172
+ if (ignoreCase) {
1173
+ expected = expected.toLowerCase();
1174
+ actual = actual.toLowerCase();
1175
+ }
1176
+ if (partialMatch ? !actual.includes(expected) : actual !== expected) {
1177
+ disappeared = true;
1178
+ break;
1179
+ }
1180
+ await new Promise((res) => setTimeout(res, 300));
1181
+ }
1182
+
1183
+ if (!disappeared) {
1184
+ const msg = `❌ Text "${textToDisappear}" did not disappear from location "${field}" within ${actionTimeout}ms. Last actual: "${lastActual}"`;
1185
+ await attachLog(msg, "text/plain");
1186
+ if (assert !== false) throw new Error(msg);
1187
+ } else {
1188
+ await attachLog(
1189
+ `✅ Text "${textToDisappear}" disappeared from location "${field}"`,
1190
+ "text/plain"
1191
+ );
1192
+ }
1193
+
1194
+ await processScreenshot(
1195
+ page,
1196
+ screenshot,
1197
+ screenshotText || `Verified text disappeared at location: ${textToDisappear}`,
1198
+ screenshotFullPage
1199
+ );
1200
+ }
1201
+ }
1202
+
1203
+ /**
1204
+ * Web: Verify field is locked -field: {param} -options: {param}
1205
+ * Verifies that the specified field on the page is locked (read-only).
1206
+ * Designed for use in Cucumber step definitions.
1207
+ *
1208
+ * @param page - Playwright Page instance.
1209
+ * @param field - The label, text, id, name, or selector of the field.
1210
+ * @param options - Optional settings for the verification action. Can be a JSON string or an object:
1211
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
1212
+ * - screenshotText: [string] Description for the screenshot.
1213
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
1214
+ *
1215
+ */
1216
+ export async function verifyFieldIsLocked(
1217
+ page: Page,
1218
+ field: string | Locator,
1219
+ options?: string | Record<string, any>
1220
+ ) {
1221
+ const options_json =
1222
+ typeof options === "string" ? vars.parseLooseJson(options) : options || {};
1223
+ const {
1224
+ iframe = "",
1225
+ actionTimeout = config?.testExecution?.actionTimeout || Number(
1226
+ vars.getConfigValue("testExecution.actionTimeout")
1227
+ ) || 30000, // Default timeout
1228
+ pattern,
1229
+ fieldType,
1230
+ screenshot = false,
1231
+ screenshotText = "",
1232
+ screenshotFullPage = true,
1233
+ screenshotField = false,
1234
+ smartIQ_refreshLoc = "",
1235
+ } = options_json || {};
1236
+
1237
+ if (isPlaywrightRunner()) {
1238
+ await __allureAny_web.step(
1239
+ `Web: Verify field is locked -field: ${field} -options: ${JSON.stringify(options_json)}`,
1240
+ async () => {
1241
+ await doverifyFieldLocked();
1242
+ }
1243
+ );
1244
+ } else {
1245
+ await doverifyFieldLocked();
1246
+ }
1247
+
1248
+ async function doverifyFieldLocked() {
1249
+ const target =
1250
+ typeof field === "string"
1251
+ ? await webLocResolver(
1252
+ fieldType || "input",
1253
+ field,
1254
+ page,
1255
+ pattern,
1256
+ smartIQ_refreshLoc
1257
+ )
1258
+ : field;
1259
+ if (iframe) {
1260
+ await waitForEnabled(
1261
+ page.frameLocator(iframe).locator(target),
1262
+ actionTimeout
1263
+ );
1264
+ await expect(target).toBeVisible({ timeout: actionTimeout });
1265
+ } else {
1266
+ await waitForEnabled(target, actionTimeout);
1267
+ await expect(target).toBeVisible({ timeout: actionTimeout });
1268
+ }
1269
+ const isFieldScreenshot = screenshotField === true;
1270
+ await processScreenshot(
1271
+ page,
1272
+ screenshot,
1273
+ screenshotText,
1274
+ !isFieldScreenshot,
1275
+ isFieldScreenshot ? target : undefined
1276
+ );
1277
+ }
1278
+ }
1279
+
1280
+ /**
1281
+ * Web: Verify field is mandatory -field: {param} -options: {param}
1282
+ * Verifies that the specified field on the page is locked (read-only).
1283
+ * Designed for use in Cucumber step definitions.
1284
+ *
1285
+ * @param page - Playwright Page instance.
1286
+ * @param field - The label, text, id, name, or selector of the field.
1287
+ * @param options - Optional settings for the verification action. Can be a JSON string or an object:
1288
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
1289
+ * - screenshotText: [string] Description for the screenshot.
1290
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
1291
+ *
1292
+ */
1293
+ export async function verifyFieldIsMandatory(
1294
+ page: Page,
1295
+ field: string | Locator,
1296
+ options?: string | Record<string, any>
1297
+ ) {
1298
+ const options_json =
1299
+ typeof options === "string" ? vars.parseLooseJson(options) : options || {};
1300
+ const {
1301
+ iframe = "",
1302
+ actionTimeout = config?.testExecution?.actionTimeout || Number(
1303
+ vars.getConfigValue("testExecution.actionTimeout")
1304
+ ) || 30000, // Default timeout
1305
+ pattern,
1306
+ fieldType,
1307
+ screenshot = false,
1308
+ screenshotText = "",
1309
+ screenshotFullPage = true,
1310
+ screenshotField = false,
1311
+ smartIQ_refreshLoc = "",
1312
+ } = options_json || {};
1313
+
1314
+ if (isPlaywrightRunner()) {
1315
+ await __allureAny_web.step(
1316
+ `Web: Verify field is mandatory -field: ${field} -options: ${JSON.stringify(options_json)}`,
1317
+ async () => {
1318
+ await doverifyFieldIsMandatory();
1319
+ }
1320
+ );
1321
+ } else {
1322
+ await doverifyFieldIsMandatory();
1323
+ }
1324
+
1325
+ async function doverifyFieldIsMandatory() {
1326
+ const target =
1327
+ typeof field === "string"
1328
+ ? await webLocResolver(
1329
+ fieldType || "input",
1330
+ field,
1331
+ page,
1332
+ pattern,
1333
+ smartIQ_refreshLoc
1334
+ )
1335
+ : field;
1336
+ if (iframe) {
1337
+ await waitForEnabled(
1338
+ page.frameLocator(iframe).locator(target),
1339
+ actionTimeout
1340
+ );
1341
+ await expect(target).toBeVisible({ timeout: actionTimeout });
1342
+ } else {
1343
+ await waitForEnabled(target, actionTimeout);
1344
+ await expect(target).toBeVisible({ timeout: actionTimeout });
1345
+ }
1346
+ const isFieldScreenshot = screenshotField === true;
1347
+ await processScreenshot(
1348
+ page,
1349
+ screenshot,
1350
+ screenshotText,
1351
+ !isFieldScreenshot,
1352
+ isFieldScreenshot ? target : undefined
1353
+ );
1354
+ }
1355
+ }
1356
+
1357
+ /**
1358
+ * Web: Verify field is secured -field: {param} -options: {param}
1359
+ * Verifies that the specified field on the page is secured.
1360
+ * Designed for use in Cucumber step definitions.
1361
+ *
1362
+ * @param page - Playwright Page instance.
1363
+ * @param field - The label, text, id, name, or selector of the field.
1364
+ * @param options - Optional settings for the verification action. Can be a JSON string or an object:
1365
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
1366
+ * - screenshotText: [string] Description for the screenshot.
1367
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
1368
+ *
1369
+ */
1370
+ export async function verifyFieldIsSecured(
1371
+ page: Page,
1372
+ field: string | Locator,
1373
+ options?: string | Record<string, any>
1374
+ ) {
1375
+ const options_json =
1376
+ typeof options === "string" ? vars.parseLooseJson(options) : options || {};
1377
+ const {
1378
+ iframe = "",
1379
+ actionTimeout = config?.testExecution?.actionTimeout || Number(
1380
+ vars.getConfigValue("testExecution.actionTimeout")
1381
+ ) || 30000, // Default timeout
1382
+ pattern,
1383
+ fieldType,
1384
+ screenshot = false,
1385
+ screenshotText = "",
1386
+ screenshotFullPage = true,
1387
+ screenshotField = false,
1388
+ smartIQ_refreshLoc = "",
1389
+ } = options_json || {};
1390
+
1391
+ if (isPlaywrightRunner()) {
1392
+ await __allureAny_web.step(
1393
+ `Web: Verify field is secured -field: ${field} -options: ${JSON.stringify(options_json)}`,
1394
+ async () => {
1395
+ await doverifyFieldIsSecured();
1396
+ }
1397
+ );
1398
+ } else {
1399
+ await doverifyFieldIsSecured();
1400
+ }
1401
+
1402
+ async function doverifyFieldIsSecured() {
1403
+ const target =
1404
+ typeof field === "string"
1405
+ ? await webLocResolver(
1406
+ fieldType || "input",
1407
+ field,
1408
+ page,
1409
+ pattern,
1410
+ smartIQ_refreshLoc
1411
+ )
1412
+ : field;
1413
+ if (iframe) {
1414
+ await waitForEnabled(
1415
+ page.frameLocator(iframe).locator(target),
1416
+ actionTimeout
1417
+ );
1418
+ await expect(target).toBeVisible({ timeout: actionTimeout });
1419
+ } else {
1420
+ await waitForEnabled(target, actionTimeout);
1421
+ await expect(target).toBeVisible({ timeout: actionTimeout });
1422
+ }
1423
+ const isFieldScreenshot = screenshotField === true;
1424
+ await processScreenshot(
1425
+ page,
1426
+ screenshot,
1427
+ screenshotText,
1428
+ !isFieldScreenshot,
1429
+ isFieldScreenshot ? target : undefined
1430
+ );
1431
+ }
1432
+ }
1433
+
1434
+ /**
1435
+ * Web: Verify select field value -field: {param} -value: {param} -options: {param}
1436
+ *
1437
+ * Verifies that the value of a select field matches the expected value.
1438
+ *
1439
+ * @param field - The label, id, name, or selector of the select field to verify.
1440
+ * @param expectedValue - The expected value of the select field.
1441
+ * @param options - Optional JSON string or object:
1442
+ * - actionTimeout: [number] Optional timeout in milliseconds. Default: Configured timeout.
1443
+ * - partialMatch: [boolean] If true, performs substring match. Default: false.
1444
+ * - pattern: [string] Optional pattern to refine element search.
1445
+ * - ignoreCase: [boolean] Whether the match is case-sensitive. Default: true.
1446
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
1447
+ * - screenshot: [boolean] Capture screenshot. Default: true.
1448
+ * - screenshotText: [string] Screenshot description.
1449
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
1450
+ */
1451
+ export async function verifySelectDropdownValue(
1452
+ page: Page,
1453
+ field: string | Locator,
1454
+ expectedValue: string,
1455
+ options?: string | Record<string, any>
1456
+ ) {
1457
+ const options_json =
1458
+ typeof options === "string" ? vars.parseLooseJson(options) : options || {};
1459
+ const {
1460
+ actionTimeout = config?.testExecution?.actionTimeout || Number(
1461
+ vars.getConfigValue("testExecution.actionTimeout")
1462
+ ) || 30000,
1463
+ partialMatch = false,
1464
+ pattern,
1465
+ ignoreCase = false,
1466
+ assert = true,
1467
+ screenshot = true,
1468
+ screenshotText = "",
1469
+ screenshotFullPage = true,
1470
+ } = options_json;
1471
+ const resolvedExpectedValue = vars.replaceVariables(expectedValue);
1472
+
1473
+ if (isPlaywrightRunner()) {
1474
+ await __allureAny_web.step(
1475
+ `Web: Verify select dropdown value -field: ${field} -value: ${resolvedExpectedValue} -options: ${JSON.stringify(
1476
+ options_json
1477
+ )}`,
1478
+ async () => {
1479
+ await doVerifySelectDropdownValue();
1480
+ }
1481
+ );
1482
+ } else {
1483
+ await doVerifySelectDropdownValue();
1484
+ }
1485
+
1486
+ async function doVerifySelectDropdownValue() {
1487
+ if (!page) throw new Error("Page not initialized");
1488
+ await waitForPageToLoad(page, actionTimeout);
1489
+
1490
+ const target =
1491
+ typeof field === "string"
1492
+ ? await webLocResolver("dropdown", field, page, pattern, actionTimeout)
1493
+ : field;
1494
+
1495
+ await target.waitFor({ state: "visible", timeout: actionTimeout });
1496
+
1497
+ const actualValue = (await target.getAttribute("value")).trim();
1498
+ const expected = vars.getValue(resolvedExpectedValue).trim();
1499
+
1500
+ let match = false;
1501
+ if (ignoreCase) {
1502
+ match = partialMatch
1503
+ ? actualValue.includes(expected)
1504
+ : actualValue === expected;
1505
+ } else {
1506
+ match = partialMatch
1507
+ ? actualValue.toLowerCase().includes(expected.toLowerCase())
1508
+ : actualValue.toLowerCase() === expected.toLowerCase();
1509
+ }
1510
+
1511
+ if (match) {
1512
+ await attachLog(`✅ Select value matched: "${actualValue}"`, "text/plain");
1513
+ } else {
1514
+ await attachLog(
1515
+ `❌ Select value mismatch: expected "${expected}", got "${actualValue}"`,
1516
+ "text/plain"
1517
+ );
1518
+ if (assert !== false) {
1519
+ throw new Error(`❌ Select value mismatch for field "${field}"`);
1520
+ }
1521
+ }
1522
+
1523
+ await processScreenshot(
1524
+ page,
1525
+ screenshot,
1526
+ screenshotText,
1527
+ screenshotFullPage
1528
+ );
1529
+ }
1530
+ }
1531
+
1532
+ /**
1533
+ * Web: Verify select list does not have given value -field: {param} -value: {param} -options: {param}
1534
+ *
1535
+ * Verifies that a select dropdown does NOT contain the specified value in its options list.
1536
+ *
1537
+ * @param page - Playwright Page instance
1538
+ * @param field - The label, id, name, or selector of the select field to verify.
1539
+ * @param excludedValue - The value that should NOT be present in the dropdown options.
1540
+ * @param options - Optional JSON string or object:
1541
+ * - actionTimeout: [number] Optional timeout in milliseconds. Default: Configured timeout.
1542
+ * - pattern: [string] Optional pattern to refine element search.
1543
+ * - ignoreCase: [boolean] Whether the match is case-sensitive. Default: true.
1544
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
1545
+ * - screenshot: [boolean] Capture screenshot. Default: true.
1546
+ * - screenshotText: [string] Screenshot description.
1547
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
1548
+ *
1549
+ * @example
1550
+ * Web: Verify select list does not have given value -field: "Country" -value: "Antarctica" -options: "{screenshot: true, screenshotText: 'Verified Antarctica not in list'}"
1551
+ */
1552
+ export async function verifySelectListNotHaveGivenValue(
1553
+ page: Page,
1554
+ field: string | Locator,
1555
+ excludedValue: string,
1556
+ options?: string | Record<string, any>
1557
+ ) {
1558
+ const options_json =
1559
+ typeof options === "string" ? vars.parseLooseJson(options) : options || {};
1560
+ const {
1561
+ actionTimeout = config?.testExecution?.actionTimeout || Number(
1562
+ vars.getConfigValue("testExecution.actionTimeout")
1563
+ ) || 30000,
1564
+ pattern,
1565
+ ignoreCase = false,
1566
+ assert = true,
1567
+ screenshot = true,
1568
+ screenshotText = "",
1569
+ screenshotFullPage = true,
1570
+ } = options_json;
1571
+ const resolvedExcludedValue = vars.replaceVariables(excludedValue);
1572
+
1573
+ if (isPlaywrightRunner()) {
1574
+ await __allureAny_web.step(
1575
+ `Web: Verify select list does not have given value -field: ${field} -value: ${resolvedExcludedValue} -options: ${JSON.stringify(
1576
+ options_json
1577
+ )}`,
1578
+ async () => {
1579
+ await doVerifySelectListNotHaveGivenValue();
1580
+ }
1581
+ );
1582
+ } else {
1583
+ await doVerifySelectListNotHaveGivenValue();
1584
+ }
1585
+
1586
+ async function doVerifySelectListNotHaveGivenValue() {
1587
+ if (!page) throw new Error("Page not initialized");
1588
+ await waitForPageToLoad(page, actionTimeout);
1589
+
1590
+ const target =
1591
+ typeof field === "string"
1592
+ ? await webLocResolver("dropdown", field, page, pattern, actionTimeout)
1593
+ : field;
1594
+
1595
+ await target.waitFor({ state: "visible", timeout: actionTimeout });
1596
+ await target.click();
1597
+ await page.waitForTimeout(2000); // Wait for options to render
1598
+
1599
+ const optionLocator = target.locator('xpath=following::div[@role="option"]');
1600
+ const size = await optionLocator.count();
1601
+ if (size === 0) {
1602
+ throw new Error(`❌ No options found for the select field "${field}".`);
1603
+ }
1604
+
1605
+ // Collect all option texts and check for the excluded value
1606
+ const optionTexts: string[] = [];
1607
+ for (let i = 0; i < size; i++) {
1608
+ optionTexts.push((await optionLocator.nth(i).innerText()).trim());
1609
+ }
1610
+
1611
+ // Case handling: when ignoreCase=true, compare in lowercase; else compare exact
1612
+ const found = optionTexts.some((optText) => {
1613
+ const current = ignoreCase ? optText.toLowerCase() : optText;
1614
+ const compare = ignoreCase
1615
+ ? vars.replaceVariables(resolvedExcludedValue).trim().toLowerCase()
1616
+ : vars.replaceVariables(resolvedExcludedValue).trim();
1617
+ return current === compare;
1618
+ });
1619
+
1620
+ if (found) {
1621
+ const msg = `❌ Select list should NOT contain "${resolvedExcludedValue}", but it was found.`;
1622
+ await attachLog(msg, "text/plain");
1623
+ if (assert !== false) {
1624
+ throw new Error(msg);
1625
+ }
1626
+ } else {
1627
+ await attachLog(
1628
+ `✅ Select list does not contain "${resolvedExcludedValue}" as expected.`,
1629
+ "text/plain"
1630
+ );
1631
+ }
1632
+
1633
+ await processScreenshot(
1634
+ page,
1635
+ screenshot,
1636
+ screenshotText || `Verified select list does not contain: ${resolvedExcludedValue}`,
1637
+ screenshotFullPage
1638
+ );
1639
+ }
1640
+ }
1641
+
1642
+
1643
+ /**
1644
+ * Extracts the most relevant text/value/label for a given Playwright Locator element.
1645
+ * Handles input, textarea, innerText, aria-label, title, and placeholder.
1646
+ * Returns a trimmed string for robust matching.
1647
+ */
1648
+ async function extractElementText(el: Locator): Promise<string> {
1649
+ try {
1650
+ const tag = await el.evaluate(e => e.tagName.toLowerCase());
1651
+ if (tag === 'input' || tag === 'textarea') {
1652
+ const value = await el.inputValue().catch(() => "");
1653
+ const placeholder = await el.getAttribute('placeholder').catch(() => "");
1654
+ const ariaLabel = await el.getAttribute('aria-label').catch(() => "");
1655
+ const title = await el.getAttribute('title').catch(() => "");
1656
+ return (value || placeholder || ariaLabel || title || "").trim();
1657
+ } else {
1658
+ const innerText = await el.innerText().catch(() => "");
1659
+ const ariaLabel = await el.getAttribute('aria-label').catch(() => "");
1660
+ const title = await el.getAttribute('title').catch(() => "");
1661
+ return (innerText || ariaLabel || title || "").trim();
1662
+ }
1663
+ } catch {
1664
+ return "";
1665
+ }
1666
+ }
1667
+
1668
+ /**
1669
+ * Web: Verify element is present/visible -field: {param} -options: {param}
1670
+ *
1671
+ * Generic presence/visibility check for any element type (button, input, dropdown, text, etc.)
1672
+ * using PlayQ locator system and fieldType, with support for exact/partial match.
1673
+ *
1674
+ * @param page - Playwright Page instance
1675
+ * @param field - The label, text, id, name, or selector of the element to verify
1676
+ * @param options - Optional JSON string or object:
1677
+ * - fieldType: [string] Type of element (e.g., "button", "input", "dropdown", "text", etc.)
1678
+ * - actionTimeout: [number] Optional timeout in milliseconds. Default: Configured timeout.
1679
+ * - pattern: [string] Optional pattern to refine element search.
1680
+ * - partialMatch: [boolean] If true, allows substring match. Default: false (exact match).
1681
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
1682
+ * - screenshot: [boolean] Capture screenshot. Default: true.
1683
+ * - screenshotText: [string] Screenshot description.
1684
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
1685
+ * - unique: [boolean] If true, require exactly one match. Default: false.
1686
+ *
1687
+ * @example
1688
+ * await web.verifyElementPresent(page, "My Button", { fieldType: "button" });
1689
+ * await web.verifyElementPresent(page, "Search", { fieldType: "input", partialMatch: true });
1690
+ */
1691
+ export async function verifyElementPresent(
1692
+ page: Page,
1693
+ field: string | Locator,
1694
+ options?: string | Record<string, any>
1695
+ ) {
1696
+ const options_json = typeof options === "string" ? parseLooseJson(options) : options || {};
1697
+ const {
1698
+ fieldType = "",
1699
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
1700
+ pattern,
1701
+ partialMatch = false,
1702
+ assert = true,
1703
+ screenshot = true,
1704
+ screenshotText = "",
1705
+ screenshotFullPage = true,
1706
+ unique = false,
1707
+ } = options_json;
1708
+
1709
+ if (!page) throw new Error("Page not initialized");
1710
+ await waitForPageToLoad(page, actionTimeout);
1711
+
1712
+ const target =
1713
+ typeof field === "string"
1714
+ ? await webLocResolver(fieldType, field, page, pattern, actionTimeout)
1715
+ : field;
1716
+
1717
+
1718
+ // Wait for at least one element to be visible and matching (by text or value)
1719
+ const pollInterval = 200;
1720
+ const maxWait = actionTimeout || 5000;
1721
+ let foundIndexes: number[] = [];
1722
+ let elapsed = 0;
1723
+ while (elapsed < maxWait) {
1724
+ foundIndexes = [];
1725
+ const count = await target.count();
1726
+ for (let i = 0; i < count; i++) {
1727
+ const el = target.nth(i);
1728
+ if (await el.isVisible() && typeof field === 'string') {
1729
+ let text = await extractElementText(el);
1730
+ if (
1731
+ (partialMatch && text.includes(field)) ||
1732
+ (!partialMatch && text === field)
1733
+ ) {
1734
+ foundIndexes.push(i);
1735
+ }
1736
+ }
1737
+ }
1738
+ if ((unique && foundIndexes.length === 1) || (!unique && foundIndexes.length > 0)) {
1739
+ break;
1740
+ }
1741
+ await new Promise(res => setTimeout(res, pollInterval));
1742
+ elapsed += pollInterval;
1743
+ }
1744
+
1745
+ let pass = false;
1746
+ if (unique) {
1747
+ pass = foundIndexes.length === 1;
1748
+ } else {
1749
+ pass = foundIndexes.length > 0;
1750
+ }
1751
+
1752
+ if (pass) {
1753
+ await attachLog(
1754
+ `✅ Element (type: "${fieldType}", field: "${field}", partialMatch: ${partialMatch}) is present and visible. Matches: ${foundIndexes.length}`,
1755
+ "text/plain"
1756
+ );
1757
+ } else {
1758
+ await attachLog(
1759
+ `❌ Element (type: "${fieldType}", field: "${field}", partialMatch: ${partialMatch}) is not visible. Matches: ${foundIndexes.length}`,
1760
+ "text/plain"
1761
+ );
1762
+ if (assert !== false) {
1763
+ throw new Error(`❌ Element (type: "${fieldType}", field: "${field}", partialMatch: ${partialMatch}) is not visible. Matches: ${foundIndexes.length}`);
1764
+ }
1765
+ }
1766
+ await processScreenshot(page, screenshot, screenshotText, screenshotFullPage);
1767
+ }
1768
+
1769
+ /**
1770
+ * Web: Verify element is NOT present/visible -field: {param} -options: {param}
1771
+ *
1772
+ * Generic absence/invisibility check for any element type (button, input, dropdown, text, etc.)
1773
+ * using PlayQ locator system and fieldType, with support for exact/partial match.
1774
+ *
1775
+ * @param page - Playwright Page instance
1776
+ * @param field - The label, text, id, name, or selector of the element to verify
1777
+ * @param options - Optional JSON string or object:
1778
+ * - fieldType: [string] Type of element (e.g., "button", "input", "dropdown", "text", etc.)
1779
+ * - actionTimeout: [number] Optional timeout in milliseconds. Default: Configured timeout.
1780
+ * - pattern: [string] Optional pattern to refine element search.
1781
+ * - partialMatch: [boolean] If true, allows substring match. Default: false (exact match).
1782
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
1783
+ * - screenshot: [boolean] Capture screenshot. Default: true.
1784
+ * - screenshotText: [string] Screenshot description.
1785
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
1786
+ * - unique: [boolean] If true, require exactly one match. Default: false.
1787
+ *
1788
+ * @example
1789
+ * await web.verifyElementNotPresent(page, "My Button", { fieldType: "button" });
1790
+ * await web.verifyElementNotPresent(page, "Search", { fieldType: "input", partialMatch: true });
1791
+ */
1792
+ export async function verifyElementNotPresent(
1793
+ page: Page,
1794
+ field: string | Locator,
1795
+ options?: string | Record<string, any>
1796
+ ) {
1797
+ const options_json = typeof options === "string" ? parseLooseJson(options) : options || {};
1798
+ const {
1799
+ fieldType = "",
1800
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
1801
+ pattern,
1802
+ partialMatch = false,
1803
+ assert = true,
1804
+ screenshot = true,
1805
+ screenshotText = "",
1806
+ screenshotFullPage = true,
1807
+ unique = false,
1808
+ } = options_json;
1809
+
1810
+ if (!page) throw new Error("Page not initialized");
1811
+
1812
+ await waitForPageToLoad(page, actionTimeout);
1813
+
1814
+ const target =
1815
+ typeof field === "string"
1816
+ ? await webLocResolver(fieldType, field, page, pattern, actionTimeout)
1817
+ : field;
1818
+
1819
+ // Wait for all matching elements to be absent or invisible (with polling)
1820
+ const pollInterval = 200;
1821
+ const maxWait = actionTimeout || 5000;
1822
+ let foundIndexes: number[] = [];
1823
+ let elapsed = 0;
1824
+ let pass = false;
1825
+ while (elapsed < maxWait) {
1826
+ foundIndexes = [];
1827
+ const count = await target.count();
1828
+ for (let i = 0; i < count; i++) {
1829
+ const el = target.nth(i);
1830
+ if (await el.isVisible() && typeof field === 'string') {
1831
+ let text = await extractElementText(el);
1832
+ if (
1833
+ (partialMatch && text.includes(field)) ||
1834
+ (!partialMatch && text === field)
1835
+ ) {
1836
+ foundIndexes.push(i);
1837
+ }
1838
+ }
1839
+ }
1840
+ // Only assign pass here, not redeclare
1841
+ if (unique) {
1842
+ pass = foundIndexes.length === 0;
1843
+ } else {
1844
+ pass = foundIndexes.length === 0;
1845
+ }
1846
+ if (pass) break;
1847
+ await new Promise(res => setTimeout(res, pollInterval));
1848
+ elapsed += pollInterval;
1849
+ }
1850
+
1851
+ if (pass) {
1852
+ await attachLog(
1853
+ `✅ Element (type: "${fieldType}", field: "${field}", partialMatch: ${partialMatch}) is NOT present/visible. Matches: 0`,
1854
+ "text/plain"
1855
+ );
1856
+ } else {
1857
+ await attachLog(
1858
+ `❌ Element (type: "${fieldType}", field: "${field}", partialMatch: ${partialMatch}) IS present/visible. Matches: ${foundIndexes.length}`,
1859
+ "text/plain"
1860
+ );
1861
+ if (assert !== false) {
1862
+ throw new Error(`❌ Element (type: "${fieldType}", field: "${field}", partialMatch: ${partialMatch}) IS present/visible. Matches: ${foundIndexes.length}`);
1863
+ }
1864
+ }
1865
+ await processScreenshot(page, screenshot, screenshotText, screenshotFullPage);
1866
+ }
1867
+
1868
+ /**
1869
+ * Web: Verify element is enabled -field: {param} -options: {param}
1870
+ *
1871
+ * Verifies that the specified element on the page is enabled (not disabled).
1872
+ *
1873
+ * @param page - Playwright Page instance.
1874
+ * @param field - The label, text, id, name, or selector of the element to verify.
1875
+ * @param options - Optional settings for the verification action. Can be a JSON string or an object:
1876
+ * - fieldType: [string] Type of element (e.g., "button", "input", "dropdown", etc.)
1877
+ * - actionTimeout: [number] Timeout in milliseconds to wait for page load. Default: Configured timeout.
1878
+ * - pattern: [string] Optional pattern to refine element search.
1879
+ * - partialMatch: [boolean] If true, allows substring match. Default: false (exact match).
1880
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
1881
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
1882
+ * - screenshotText: [string] Description for the screenshot.
1883
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
1884
+ *
1885
+ * @example
1886
+ * await web.verifyElementEnabled(page, "Submit Button", { fieldType: "button" });
1887
+ *
1888
+ */
1889
+ export async function verifyElementEnabled(
1890
+ page: Page,
1891
+ field: string | Locator,
1892
+ options?: string | Record<string, any>
1893
+ ) {
1894
+ const options_json = typeof options === "string" ? parseLooseJson(options) : options || {};
1895
+ const { fieldType = "", actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")), pattern, partialMatch = false, assert = true, screenshot = true, screenshotText = "", screenshotFullPage = true } = options_json;
1896
+ if (!page) throw new Error("Page not initialized");
1897
+ await waitForPageToLoad(page, actionTimeout);
1898
+ const target = typeof field === "string" ? await webLocResolver(fieldType, field, page, pattern, actionTimeout) : field;
1899
+ const count = await target.count();
1900
+ let enabled = false;
1901
+ for (let i = 0; i < count; i++) {
1902
+ const el = target.nth(i);
1903
+ if (await el.isVisible() && typeof field === 'string') {
1904
+ let text = await extractElementText(el);
1905
+ if ((partialMatch && text.includes(field)) || (!partialMatch && text === field)) {
1906
+ enabled = await el.isEnabled();
1907
+ if (enabled) break;
1908
+ }
1909
+ }
1910
+ }
1911
+ if (enabled) {
1912
+ await attachLog(`✅ Element (type: "${fieldType}", field: "${field}") is enabled.`, "text/plain");
1913
+ } else {
1914
+ await attachLog(`❌ Element (type: "${fieldType}", field: "${field}") is not enabled.`, "text/plain");
1915
+ if (assert !== false) throw new Error(`❌ Element (type: "${fieldType}", field: "${field}") is not enabled.`);
1916
+ }
1917
+ await processScreenshot(page, screenshot, screenshotText, screenshotFullPage);
1918
+ }
1919
+
1920
+ /**
1921
+ * Web: Verify element is disabled -field: {param} -options: {param}
1922
+ *
1923
+ * Verifies that the specified element on the page is disabled (not enabled).
1924
+ *
1925
+ * @param page - Playwright Page instance.
1926
+ * @param field - The label, text, id, name, or selector of the element to verify.
1927
+ * @param options - Optional settings for the verification action. Can be a JSON string or an object:
1928
+ * - fieldType: [string] Type of element (e.g., "button", "input", "dropdown", etc.)
1929
+ * - actionTimeout: [number] Timeout in milliseconds to wait for page load. Default: Configured timeout.
1930
+ * - pattern: [string] Optional pattern to refine element search.
1931
+ * - partialMatch: [boolean] If true, allows substring match. Default: false (exact match).
1932
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
1933
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
1934
+ * - screenshotText: [string] Description for the screenshot.
1935
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
1936
+ *
1937
+ * @example
1938
+ * await web.verifyElementDisabled(page, "Submit Button", { fieldType: "button" });
1939
+ *
1940
+ */
1941
+ export async function verifyElementDisabled(
1942
+ page: Page,
1943
+ field: string | Locator,
1944
+ options?: string | Record<string, any>
1945
+ ) {
1946
+ const options_json = typeof options === "string" ? parseLooseJson(options) : options || {};
1947
+ const { fieldType = "", actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")), pattern, partialMatch = false, assert = true, screenshot = true, screenshotText = "", screenshotFullPage = true } = options_json;
1948
+ if (!page) throw new Error("Page not initialized");
1949
+ await waitForPageToLoad(page, actionTimeout);
1950
+ const target = typeof field === "string" ? await webLocResolver(fieldType, field, page, pattern, actionTimeout) : field;
1951
+ const count = await target.count();
1952
+ let disabled = false;
1953
+ for (let i = 0; i < count; i++) {
1954
+ const el = target.nth(i);
1955
+ if (await el.isVisible() && typeof field === 'string') {
1956
+ let text = await extractElementText(el);
1957
+ if ((partialMatch && text.includes(field)) || (!partialMatch && text === field)) {
1958
+ disabled = !(await el.isEnabled());
1959
+ if (disabled) break;
1960
+ }
1961
+ }
1962
+ }
1963
+ if (disabled) {
1964
+ await attachLog(`✅ Element (type: "${fieldType}", field: "${field}") is disabled.`, "text/plain");
1965
+ } else {
1966
+ await attachLog(`❌ Element (type: "${fieldType}", field: "${field}") is not disabled.`, "text/plain");
1967
+ if (assert !== false) throw new Error(`❌ Element (type: "${fieldType}", field: "${field}") is not disabled.`);
1968
+ }
1969
+ await processScreenshot(page, screenshot, screenshotText, screenshotFullPage);
1970
+ }
1971
+
1972
+ /**
1973
+ * Web: Verify element is selected -field: {param} -options: {param}
1974
+ *
1975
+ * Verifies that the specified element on the page is selected (e.g., checkbox, radio button, option).
1976
+ *
1977
+ * @param page - Playwright Page instance.
1978
+ * @param field - The label, text, id, name, or selector of the element to verify.
1979
+ * @param options - Optional settings for the verification action. Can be a JSON string or an object:
1980
+ * - fieldType: [string] Type of element (e.g., "checkbox", "radio", "option", etc.)
1981
+ * - actionTimeout: [number] Timeout in milliseconds to wait for page load. Default: Configured timeout.
1982
+ * - pattern: [string] Optional pattern to refine element search.
1983
+ * - partialMatch: [boolean] If true, allows substring match. Default: false (exact match).
1984
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
1985
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
1986
+ * - screenshotText: [string] Description for the screenshot.
1987
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
1988
+ *
1989
+ *
1990
+ * @example
1991
+ * await web.verifyElementSelected(page, "Accept Terms", { fieldType: "checkbox" });
1992
+ *
1993
+ */
1994
+ export async function verifyElementSelected(
1995
+ page: Page,
1996
+ field: string | Locator,
1997
+ options?: string | Record<string, any>
1998
+ ) {
1999
+ const options_json = typeof options === "string" ? parseLooseJson(options) : options || {};
2000
+ const { fieldType = "", actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")), pattern, partialMatch = false, assert = true, screenshot = true, screenshotText = "", screenshotFullPage = true } = options_json;
2001
+ if (!page) throw new Error("Page not initialized");
2002
+ await waitForPageToLoad(page, actionTimeout);
2003
+ const target = typeof field === "string" ? await webLocResolver(fieldType, field, page, pattern, actionTimeout) : field;
2004
+ const count = await target.count();
2005
+ let selected = false;
2006
+ for (let i = 0; i < count; i++) {
2007
+ const el = target.nth(i);
2008
+ if (await el.isVisible() && typeof field === 'string') {
2009
+ let text = await extractElementText(el);
2010
+ if ((partialMatch && text.includes(field)) || (!partialMatch && text === field)) {
2011
+ // Try aria-selected, checked, or selected attribute
2012
+ const ariaSelected = await el.getAttribute("aria-selected");
2013
+ const checked = await el.getAttribute("checked");
2014
+ const selectedAttr = await el.getAttribute("selected");
2015
+ if (ariaSelected === "true" || checked === "true" || selectedAttr === "true") {
2016
+ selected = true;
2017
+ break;
2018
+ }
2019
+ }
2020
+ }
2021
+ }
2022
+ if (selected) {
2023
+ await attachLog(`✅ Element (type: "${fieldType}", field: "${field}") is selected.`, "text/plain");
2024
+ } else {
2025
+ await attachLog(`❌ Element (type: "${fieldType}", field: "${field}") is not selected.`, "text/plain");
2026
+ if (assert !== false) throw new Error(`❌ Element (type: "${fieldType}", field: "${field}") is not selected.`);
2027
+ }
2028
+ await processScreenshot(page, screenshot, screenshotText, screenshotFullPage);
2029
+ }
2030
+
2031
+
2032
+ /**
2033
+ *
2034
+ * Web: Verify element has attribute -field: {param} -attribute: {param} -value: {param} -options: {param}
2035
+ *
2036
+ * Verifies that the specified element on the page has the given attribute with the expected value.
2037
+ *
2038
+ * @param page - Playwright Page instance.
2039
+ * @param field - The label, text, id, name, or selector of the element to verify.
2040
+ * @param attribute - The attribute name to check (e.g., "href", "src", "alt", etc.).
2041
+ * @param expectedValue - The expected value of the attribute.
2042
+ * @param options - Optional settings for the verification action. Can be a JSON string or an object:
2043
+ * - fieldType: [string] Type of element (e.g., "button", "input", "dropdown", etc.)
2044
+ * - actionTimeout: [number] Timeout in milliseconds to wait for page load. Default: Configured timeout.
2045
+ * - pattern: [string] Optional pattern to refine element search.
2046
+ * - partialMatch: [boolean] If true, allows substring match. Default: false (exact match).
2047
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
2048
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
2049
+ * - screenshotText: [string] Description for the screenshot.
2050
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
2051
+ *
2052
+ * @example
2053
+ * await web.verifyElementHasAttribute(page, "My Link", "href", "https://example.com", { fieldType: "link" });
2054
+ *
2055
+ */
2056
+ export async function verifyElementHasAttribute(
2057
+ page: Page,
2058
+ field: string | Locator,
2059
+ attribute: string,
2060
+ expectedValue: string,
2061
+ options?: string | Record<string, any>
2062
+ ) {
2063
+ const options_json = typeof options === "string" ? parseLooseJson(options) : options || {};
2064
+ const { fieldType = "", actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")), pattern, partialMatch = false, assert = true, screenshot = true, screenshotText = "", screenshotFullPage = true } = options_json;
2065
+ if (!page) throw new Error("Page not initialized");
2066
+ await waitForPageToLoad(page, actionTimeout);
2067
+ const target = typeof field === "string" ? await webLocResolver(fieldType, field, page, pattern, actionTimeout) : field;
2068
+ const count = await target.count();
2069
+ let found = false;
2070
+ for (let i = 0; i < count; i++) {
2071
+ const el = target.nth(i);
2072
+ if (await el.isVisible() && typeof field === 'string') {
2073
+ let text = await extractElementText(el);
2074
+ if ((partialMatch && text.includes(field)) || (!partialMatch && text === field)) {
2075
+ const attrVal = await el.getAttribute(attribute);
2076
+ if (attrVal === expectedValue) {
2077
+ found = true;
2078
+ break;
2079
+ }
2080
+ }
2081
+ }
2082
+ }
2083
+ if (found) {
2084
+ await attachLog(`✅ Element (type: "${fieldType}", field: "${field}") has attribute ${attribute}="${expectedValue}".`, "text/plain");
2085
+ } else {
2086
+ await attachLog(`❌ Element (type: "${fieldType}", field: "${field}") does not have attribute ${attribute}="${expectedValue}".`, "text/plain");
2087
+ if (assert !== false) throw new Error(`❌ Element (type: "${fieldType}", field: "${field}") does not have attribute ${attribute}="${expectedValue}".`);
2088
+ }
2089
+ await processScreenshot(page, screenshot, screenshotText, screenshotFullPage);
2090
+ }
2091
+
2092
+ /**
2093
+ * Web: Verify element count -field: {param} -expectedCount: {param} -options: {param}
2094
+ *
2095
+ * Verifies that the count of specified elements on the page matches the expected count.
2096
+ *
2097
+ * @param page - Playwright Page instance.
2098
+ * @param field - The label, text, id, name, or selector of the element to verify.
2099
+ * @param expectedCount - The expected number of elements to be present.
2100
+ * @param options - Optional settings for the verification action. Can be a JSON string or an object:
2101
+ * - fieldType: [string] Type of element (e.g., "button", "input", "dropdown", etc.)
2102
+ * - actionTimeout: [number] Timeout in milliseconds to wait for page load. Default: Configured timeout.
2103
+ * - pattern: [string] Optional pattern to refine element search.
2104
+ * - partialMatch: [boolean] If true, allows substring match. Default: false (exact match).
2105
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
2106
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
2107
+ * - screenshotText: [string] Description for the screenshot.
2108
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
2109
+ *
2110
+ *
2111
+ * @example
2112
+ * await web.verifyElementCount(page, "Item", 5, { fieldType: "text" });
2113
+ *
2114
+ *
2115
+ */
2116
+ export async function verifyElementCount(
2117
+ page: Page,
2118
+ field: string | Locator,
2119
+ expectedCount: number,
2120
+ options?: string | Record<string, any>
2121
+ ) {
2122
+ const options_json = typeof options === "string" ? parseLooseJson(options) : options || {};
2123
+ const { fieldType = "", actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")), pattern, partialMatch = false, assert = true, screenshot = true, screenshotText = "", screenshotFullPage = true } = options_json;
2124
+ if (!page) throw new Error("Page not initialized");
2125
+ await waitForPageToLoad(page, actionTimeout);
2126
+ const target = typeof field === "string" ? await webLocResolver(fieldType, field, page, pattern, actionTimeout) : field;
2127
+ const count = await target.count();
2128
+ let matchCount = 0;
2129
+ for (let i = 0; i < count; i++) {
2130
+ const el = target.nth(i);
2131
+ if (await el.isVisible() && typeof field === 'string') {
2132
+ let text = await extractElementText(el);
2133
+ if ((partialMatch && text.includes(field)) || (!partialMatch && text === field)) {
2134
+ matchCount++;
2135
+ }
2136
+ }
2137
+ }
2138
+ if (matchCount === expectedCount) {
2139
+ await attachLog(`✅ Element (type: "${fieldType}", field: "${field}") count matches expected: ${expectedCount}.`, "text/plain");
2140
+ } else {
2141
+ await attachLog(`❌ Element (type: "${fieldType}", field: "${field}") count ${matchCount} does not match expected: ${expectedCount}.`, "text/plain");
2142
+ if (assert !== false) throw new Error(`❌ Element (type: "${fieldType}", field: "${field}") count ${matchCount} does not match expected: ${expectedCount}.`);
2143
+ }
2144
+ await processScreenshot(page, screenshot, screenshotText, screenshotFullPage);
2145
+ }
2146
+
2147
+ /**
2148
+ *
2149
+
2150
+ /**
2151
+ * Web: Verify element order -field: {param} -expectedOrder: {param} -options: {param}
2152
+ *
2153
+ * Verifies that the order of elements of a specified type on the page matches the expected order.
2154
+ *
2155
+ * @param page - Playwright Page instance.
2156
+ * @param field - The locator, label, text, id, name, or selector of the elements to verify (string or Locator).
2157
+ * @param expectedOrder - An array of expected element texts in the correct order.
2158
+ * @param options - Optional settings for the verification action. Can be a JSON string or an object:
2159
+ * - fieldType: [string] The type of elements to verify (e.g., "text", "button", "item", etc.).
2160
+ * - actionTimeout: [number] Timeout in milliseconds to wait for page load. Default: Configured timeout.
2161
+ * - pattern: [string] Optional pattern to refine element search.
2162
+ * - partialMatch: [boolean] If true, allows substring match. Default: false (exact match).
2163
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
2164
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
2165
+ * - screenshotText: [string] Description for the screenshot.
2166
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
2167
+ *
2168
+ * @example
2169
+ * // Using a string field (label or logical type)
2170
+ * await web.verifyElementOrder(page, "Phase", ["Draft", "Review", "Approved"], { fieldType: "phase", partialMatch: false });
2171
+ *
2172
+ * // Using a Playwright Locator (e.g., all visible rows in a table)
2173
+ * const rows = page.locator('.my-table-row');
2174
+ * await web.verifyElementOrder(page, rows, ["Row 1", "Row 2", "Row 3"]);
2175
+ */
2176
+ export async function verifyElementOrder(
2177
+ page: Page,
2178
+ field: string | Locator,
2179
+ expectedOrder: string[],
2180
+ options?: string | Record<string, any>
2181
+ ) {
2182
+ const options_json = typeof options === "string" ? parseLooseJson(options) : options || {};
2183
+ const {
2184
+ fieldType = "",
2185
+ actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")),
2186
+ pattern,
2187
+ partialMatch = false,
2188
+ assert = true,
2189
+ screenshot = true,
2190
+ screenshotText = "",
2191
+ screenshotFullPage = true
2192
+ } = options_json;
2193
+ if (!page) throw new Error("Page not initialized");
2194
+ await waitForPageToLoad(page, actionTimeout);
2195
+ const target =
2196
+ typeof field === "string"
2197
+ ? await webLocResolver(fieldType, field, page, pattern, actionTimeout)
2198
+ : field;
2199
+ const count = await target.count();
2200
+ let actualOrder: string[] = [];
2201
+ for (let i = 0; i < count; i++) {
2202
+ const el = target.nth(i);
2203
+ if (await el.isVisible()) {
2204
+ let text = await extractElementText(el);
2205
+ actualOrder.push(text);
2206
+ }
2207
+ }
2208
+ let matches = true;
2209
+ if (partialMatch) {
2210
+ for (let i = 0; i < expectedOrder.length; i++) {
2211
+ if (!actualOrder[i]?.includes(expectedOrder[i])) {
2212
+ matches = false;
2213
+ break;
2214
+ }
2215
+ }
2216
+ } else {
2217
+ matches = JSON.stringify(actualOrder) === JSON.stringify(expectedOrder);
2218
+ }
2219
+ if (matches) {
2220
+ await attachLog(`✅ Element order matches expected.`, "text/plain");
2221
+ } else {
2222
+ await attachLog(`❌ Element order does not match expected. Actual: ${JSON.stringify(actualOrder)}, Expected: ${JSON.stringify(expectedOrder)}`, "text/plain");
2223
+ if (assert !== false) throw new Error(`❌ Element order does not match expected.`);
2224
+ }
2225
+ await processScreenshot(page, screenshot, screenshotText, screenshotFullPage);
2226
+ }
2227
+
2228
+ /**
2229
+ * Web: Verify element is in viewport -field: {param} -options: {param}
2230
+ *
2231
+ * Verifies that the specified element on the page is within the visible viewport.
2232
+ *
2233
+ * @param page - Playwright Page instance.
2234
+ * @param field - The label, text, id, name, or selector of the element to verify.
2235
+ * @param options - Optional settings for the verification action. Can be a JSON string or an object:
2236
+ * - fieldType: [string] Type of element (e.g., "button", "input", "dropdown", etc.)
2237
+ * - actionTimeout: [number] Timeout in milliseconds to wait for page load. Default: Configured timeout.
2238
+ * - pattern: [string] Optional pattern to refine element search.
2239
+ * - partialMatch: [boolean] If true, allows substring match. Default: false (exact match).
2240
+ * - assert: [boolean] If false, logs the failure but does not throw. Default: true.
2241
+ * - screenshot: [boolean] Capture a screenshot. Default: true.
2242
+ * - screenshotText: [string] Description for the screenshot.
2243
+ * - screenshotFullPage: [boolean] Capture full page screenshot. Default: true.
2244
+ *
2245
+ * @example
2246
+ * await web.verifyElementInViewport(page, "Submit Button", { fieldType: "button" });
2247
+ *
2248
+ *
2249
+ */
2250
+ export async function verifyElementInViewport(
2251
+ page: Page,
2252
+ field: string | Locator,
2253
+ options?: string | Record<string, any>
2254
+ ) {
2255
+ const options_json = typeof options === "string" ? parseLooseJson(options) : options || {};
2256
+ const { fieldType = "", actionTimeout = Number(vars.getConfigValue("testExecution.actionTimeout")), pattern, partialMatch = false, assert = true, screenshot = true, screenshotText = "", screenshotFullPage = true } = options_json;
2257
+ if (!page) throw new Error("Page not initialized");
2258
+ await waitForPageToLoad(page, actionTimeout);
2259
+ const target = typeof field === "string" ? await webLocResolver(fieldType, field, page, pattern, actionTimeout) : field;
2260
+ const count = await target.count();
2261
+ let inViewport = false;
2262
+ for (let i = 0; i < count; i++) {
2263
+ const el = target.nth(i);
2264
+ if (await el.isVisible() && typeof field === 'string') {
2265
+ let text = await extractElementText(el);
2266
+ if ((partialMatch && text.includes(field)) || (!partialMatch && text === field)) {
2267
+ inViewport = await el.evaluate((node: any) => {
2268
+ const rect = node.getBoundingClientRect();
2269
+ return (
2270
+ rect.top >= 0 &&
2271
+ rect.left >= 0 &&
2272
+ rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
2273
+ rect.right <= (window.innerWidth || document.documentElement.clientWidth)
2274
+ );
2275
+ });
2276
+ if (inViewport) break;
2277
+ }
2278
+ }
2279
+ }
2280
+ if (inViewport) {
2281
+ await attachLog(`✅ Element (type: "${fieldType}", field: "${field}") is in viewport.`, "text/plain");
2282
+ } else {
2283
+ await attachLog(`❌ Element (type: "${fieldType}", field: "${field}") is not in viewport.`, "text/plain");
2284
+ if (assert !== false) throw new Error(`❌ Element (type: "${fieldType}", field: "${field}") is not in viewport.`);
2285
+ }
2286
+ await processScreenshot(page, screenshot, screenshotText, screenshotFullPage);
2287
+ }