lexxit-automation-framework 2.0.20 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +246 -57
- package/npmignore +8 -0
- package/package.json +32 -1
- package/public/dashboard.html +591 -0
- package/src/actions/baseHandler.ts +351 -0
- package/src/actions/browserManager.ts +432 -0
- package/src/actions/checkboxHandler.ts +276 -0
- package/src/actions/clickHandler.ts +513 -0
- package/src/actions/customcodehandler.ts +251 -0
- package/src/actions/dropdownHandler.ts +501 -0
- package/src/actions/radiobuttonHandler.ts +286 -0
- package/src/actions/textHandler.ts +498 -0
- package/src/api/server.ts +210 -0
- package/src/config.ts +7 -0
- package/src/executor/functionMap.ts +153 -0
- package/src/executor/mapping.ts +70 -0
- package/src/executor/scriptExecutor.ts +162 -0
- package/src/executor/stepExecutor.ts +289 -0
- package/src/runner/testRunner.ts +78 -0
- package/src/sse/sseManager.ts +130 -0
- package/src/store/executionStore.ts +152 -0
- package/src/store/testDataStore.ts +46 -0
- package/src/types/types.ts +159 -0
- package/src/utils/healingService.ts +210 -0
- package/src/utils/locatorService.ts +73 -0
- package/src/utils/logger.ts +27 -0
- package/src/utils/metadataService.ts +137 -0
- package/src/utils/waitConditions.ts +141 -0
- package/src/validator/validator.ts +140 -0
- package/tsconfig.json +16 -0
- package/bin/lexxit-automation-framework.js +0 -224
- package/dist-obf/app.js +0 -1
- package/dist-obf/controllers/controller.js +0 -1
- package/dist-obf/core/BrowserManager.js +0 -1
- package/dist-obf/core/PlaywrightEngine.js +0 -1
- package/dist-obf/core/ScreenshotManager.js +0 -1
- package/dist-obf/core/TestData.js +0 -1
- package/dist-obf/core/TestExecutor.js +0 -1
- package/dist-obf/core/handlers/AllHandlers.js +0 -1
- package/dist-obf/core/handlers/BaseHandler.js +0 -1
- package/dist-obf/core/handlers/ClickHandler.js +0 -1
- package/dist-obf/core/handlers/CustomCodeHandler.js +0 -1
- package/dist-obf/core/handlers/DropdownHandler.js +0 -1
- package/dist-obf/core/handlers/InputHandler.js +0 -1
- package/dist-obf/core/registry/ActionRegistry.js +0 -1
- package/dist-obf/installer/frameworkLauncher.js +0 -1
- package/dist-obf/public/dashboard.html +0 -591
- package/dist-obf/public/execution.html +0 -510
- package/dist-obf/public/queue-monitor.html +0 -408
- package/dist-obf/queue/ExecutionQueue.js +0 -1
- package/dist-obf/routes/api.routes.js +0 -1
- package/dist-obf/server.js +0 -1
- package/dist-obf/types/types.js +0 -1
- package/dist-obf/utils/chromefinder.js +0 -1
- package/dist-obf/utils/elementHighlight.js +0 -1
- package/dist-obf/utils/locatorHelper.js +0 -1
- package/dist-obf/utils/logger.js +0 -1
- package/dist-obf/utils/response.js +0 -1
- package/dist-obf/utils/responseFormatter.js +0 -1
- package/dist-obf/utils/sseManager.js +0 -1
- package/scripts/postinstall.js +0 -52
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { Page } from 'playwright';
|
|
2
|
+
import { ScreenshotMode, StepResult } from '../types/types';
|
|
3
|
+
import { LocatorService } from '../utils/locatorService';
|
|
4
|
+
import { BaseHandler } from './baseHandler';
|
|
5
|
+
import { DEFAULT_SCREENSHOT_MODE } from '../config';
|
|
6
|
+
|
|
7
|
+
export class RadiobuttonHandler extends BaseHandler {
|
|
8
|
+
protected page: Page;
|
|
9
|
+
|
|
10
|
+
constructor(page: Page, screenshotMode: ScreenshotMode = DEFAULT_SCREENSHOT_MODE) {
|
|
11
|
+
super();
|
|
12
|
+
this.page = page;
|
|
13
|
+
this.screenshotMode = screenshotMode;
|
|
14
|
+
this.setupNetworkCapture(page);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ─── Helpers ───────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
// private getStartTime(): string {
|
|
20
|
+
// return new Date().toISOString();
|
|
21
|
+
// }
|
|
22
|
+
|
|
23
|
+
// private getDuration(startMs: number): number {
|
|
24
|
+
// return Date.now() - startMs;
|
|
25
|
+
// }
|
|
26
|
+
|
|
27
|
+
// private buildResult(
|
|
28
|
+
// status: 'PASS' | 'FAIL',
|
|
29
|
+
// expected_result: string,
|
|
30
|
+
// comments: string,
|
|
31
|
+
// startMs: number,
|
|
32
|
+
// startTime: string,
|
|
33
|
+
// step_name: string = '',
|
|
34
|
+
// uid: string | null = null,
|
|
35
|
+
// obj_uid: string | null = null,
|
|
36
|
+
// page_uid: string | null = null
|
|
37
|
+
// ): StepResult {
|
|
38
|
+
// return {
|
|
39
|
+
// uid,
|
|
40
|
+
// obj_uid,
|
|
41
|
+
// page_uid,
|
|
42
|
+
// step_name,
|
|
43
|
+
// step_script: '',
|
|
44
|
+
// status,
|
|
45
|
+
// expected_result,
|
|
46
|
+
// comments,
|
|
47
|
+
// duration: this.getDuration(startMs),
|
|
48
|
+
// start_time: startTime
|
|
49
|
+
// };
|
|
50
|
+
// }
|
|
51
|
+
|
|
52
|
+
// private async captureScreenshot(isFailure: boolean): Promise<string | undefined> {
|
|
53
|
+
// if (this.screenshotMode === 'never') return undefined;
|
|
54
|
+
// if (this.screenshotMode === 'on_failure' && !isFailure) return undefined;
|
|
55
|
+
// try {
|
|
56
|
+
// if (!this.page || this.page.isClosed()) return undefined;
|
|
57
|
+
// const buf = await this.page.screenshot({ type: 'png', fullPage: false });
|
|
58
|
+
// return buf.toString('base64');
|
|
59
|
+
// } catch {
|
|
60
|
+
// return undefined;
|
|
61
|
+
// }
|
|
62
|
+
// }
|
|
63
|
+
|
|
64
|
+
// private classifyError(error: any): string {
|
|
65
|
+
// const msg: string = error?.message || String(error);
|
|
66
|
+
|
|
67
|
+
// if (msg.includes('Element not found') || msg.includes('not visible')) {
|
|
68
|
+
// return `Element not found or not visible: ${msg}`;
|
|
69
|
+
// }
|
|
70
|
+
// if (msg.includes('Timeout') || msg.includes('timeout')) {
|
|
71
|
+
// return `Timeout error - element took too long to respond: ${msg}`;
|
|
72
|
+
// }
|
|
73
|
+
// if (msg.includes('not enabled')) {
|
|
74
|
+
// return `Element is disabled: ${msg}`;
|
|
75
|
+
// }
|
|
76
|
+
// if (msg.includes('detached')) {
|
|
77
|
+
// return `Element got detached from DOM: ${msg}`;
|
|
78
|
+
// }
|
|
79
|
+
// if (msg.includes('Target closed') || msg.includes('has been closed')) {
|
|
80
|
+
// return `Browser or page closed unexpectedly: ${msg}`;
|
|
81
|
+
// }
|
|
82
|
+
|
|
83
|
+
// return `Unexpected error: ${msg}`;
|
|
84
|
+
// }
|
|
85
|
+
|
|
86
|
+
// private appendMatchedLocatorInfo(comments: string, matchedIndex: number, matchedXPath: string): string {
|
|
87
|
+
// if (matchedIndex > 0) {
|
|
88
|
+
// return `${comments} (Matched locator at position ${matchedIndex}: ${matchedXPath})`;
|
|
89
|
+
// }
|
|
90
|
+
// return comments;
|
|
91
|
+
// }
|
|
92
|
+
|
|
93
|
+
// ─── select ────────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
async select(label: string, locators: string[], step_name: string = 'Select radio button'): Promise<StepResult> {
|
|
96
|
+
const startMs = Date.now();
|
|
97
|
+
const startTime = this.getStartTime();
|
|
98
|
+
const expected = `'${label}' selected successfully`;
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const resolved = await LocatorService.resolveElement(this.page, locators);
|
|
102
|
+
await resolved.element.check();
|
|
103
|
+
|
|
104
|
+
const comments = this.appendMatchedLocatorInfo(expected, resolved.matchedIndex, resolved.matchedXPath);
|
|
105
|
+
return this.buildResult('PASS', expected, comments, startMs, startTime, step_name);
|
|
106
|
+
} catch (error: any) {
|
|
107
|
+
// const sc = await this.captureScreenshot(true);
|
|
108
|
+
return this.buildResult('FAIL', expected, this.classifyError(error), startMs, startTime, step_name);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ─── selectByValue ─────────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
async selectByValue(label: string, locators: string[], value: string, step_name: string = 'Select radio button by value'): Promise<StepResult> {
|
|
115
|
+
const startMs = Date.now();
|
|
116
|
+
const startTime = this.getStartTime();
|
|
117
|
+
const expected = `'${label}' option with value '${value}' selected successfully`;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const combinedXPath = locators.join(' | ');
|
|
121
|
+
const allRadios = this.page.locator(`xpath=${combinedXPath}`);
|
|
122
|
+
const count = await allRadios.count();
|
|
123
|
+
|
|
124
|
+
if (count <= 1) {
|
|
125
|
+
const resolved = await LocatorService.resolveElement(this.page, locators);
|
|
126
|
+
await resolved.element.check();
|
|
127
|
+
const comments = this.appendMatchedLocatorInfo(expected, resolved.matchedIndex, resolved.matchedXPath);
|
|
128
|
+
return this.buildResult('PASS', expected, comments, startMs, startTime, step_name);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const targetByAttr = this.page.locator(`xpath=(${combinedXPath})[@value="${value}"]`).first();
|
|
132
|
+
|
|
133
|
+
const isVisible = await targetByAttr.isVisible().catch(() => false);
|
|
134
|
+
if (!isVisible) {
|
|
135
|
+
// const sc = await this.captureScreenshot(true);
|
|
136
|
+
return this.buildResult('FAIL', expected, `No radio option found with value '${value}' under label '${label}'`, startMs, startTime, step_name);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
await targetByAttr.check();
|
|
140
|
+
return this.buildResult('PASS', expected, expected, startMs, startTime, step_name);
|
|
141
|
+
} catch (error: any) {
|
|
142
|
+
// const sc = await this.captureScreenshot(true);
|
|
143
|
+
return this.buildResult('FAIL', expected, this.classifyError(error), startMs, startTime, step_name);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ─── selectByLabel ─────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
async selectByLabel(label: string, locators: string[], optionLabel: string, step_name: string = 'Select radio button by label'): Promise<StepResult> {
|
|
150
|
+
const startMs = Date.now();
|
|
151
|
+
const startTime = this.getStartTime();
|
|
152
|
+
const expected = `'${label}' option '${optionLabel}' selected successfully`;
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const combinedXPath = locators.join(' | ');
|
|
156
|
+
const allRadios = this.page.locator(`xpath=${combinedXPath}`);
|
|
157
|
+
const count = await allRadios.count();
|
|
158
|
+
|
|
159
|
+
if (count <= 1) {
|
|
160
|
+
const resolved = await LocatorService.resolveElement(this.page, locators);
|
|
161
|
+
await resolved.element.check();
|
|
162
|
+
const comments = this.appendMatchedLocatorInfo(expected, resolved.matchedIndex, resolved.matchedXPath);
|
|
163
|
+
return this.buildResult('PASS', expected, comments, startMs, startTime, step_name);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const targetByForId = this.page.locator(
|
|
167
|
+
`xpath=(${combinedXPath})[@id=//label[normalize-space(text())="${optionLabel}"]/@for]`
|
|
168
|
+
).first();
|
|
169
|
+
|
|
170
|
+
let isVisible = await targetByForId.isVisible().catch(() => false);
|
|
171
|
+
let target = targetByForId;
|
|
172
|
+
|
|
173
|
+
if (!isVisible) {
|
|
174
|
+
const targetByWrapping = this.page.locator(
|
|
175
|
+
`xpath=(${combinedXPath})[ancestor::label[normalize-space(text())="${optionLabel}"]]`
|
|
176
|
+
).first();
|
|
177
|
+
isVisible = await targetByWrapping.isVisible().catch(() => false);
|
|
178
|
+
target = targetByWrapping;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!isVisible) {
|
|
182
|
+
// const sc = await this.captureScreenshot(true);
|
|
183
|
+
return this.buildResult('FAIL', expected, `No radio option found with label text '${optionLabel}' under '${label}'`, startMs, startTime, step_name);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
await target.check();
|
|
187
|
+
return this.buildResult('PASS', expected, expected, startMs, startTime, step_name);
|
|
188
|
+
} catch (error: any) {
|
|
189
|
+
// const sc = await this.captureScreenshot(true);
|
|
190
|
+
return this.buildResult('FAIL', expected, this.classifyError(error), startMs, startTime, step_name);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ─── isSelected ────────────────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
async isSelected(label: string, locators: string[], step_name: string = 'Check if radio button is selected'): Promise<StepResult> {
|
|
197
|
+
const startMs = Date.now();
|
|
198
|
+
const startTime = this.getStartTime();
|
|
199
|
+
const expected = `'${label}' selected state retrieved successfully`;
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
const resolved = await LocatorService.resolveElement(this.page, locators);
|
|
203
|
+
const checked = await resolved.element.isChecked();
|
|
204
|
+
|
|
205
|
+
const comments = this.appendMatchedLocatorInfo(
|
|
206
|
+
`'${label}' is ${checked ? 'selected' : 'not selected'}`,
|
|
207
|
+
resolved.matchedIndex,
|
|
208
|
+
resolved.matchedXPath
|
|
209
|
+
);
|
|
210
|
+
return this.buildResult('PASS', expected, comments, startMs, startTime, step_name);
|
|
211
|
+
} catch (error: any) {
|
|
212
|
+
// const sc = await this.captureScreenshot(true);
|
|
213
|
+
return this.buildResult('FAIL', expected, this.classifyError(error), startMs, startTime, step_name);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ─── verifySelected ────────────────────────────────────────────────────────
|
|
218
|
+
|
|
219
|
+
async verifySelected(label: string, locators: string[], step_name: string = 'Verify radio button is selected'): Promise<StepResult> {
|
|
220
|
+
const startMs = Date.now();
|
|
221
|
+
const startTime = this.getStartTime();
|
|
222
|
+
const expected = `'${label}' is selected`;
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
const resolved = await LocatorService.resolveElement(this.page, locators);
|
|
226
|
+
const checked = await resolved.element.isChecked();
|
|
227
|
+
|
|
228
|
+
if (!checked) {
|
|
229
|
+
const failComments = this.appendMatchedLocatorInfo(`'${label}' is not selected`, resolved.matchedIndex, resolved.matchedXPath);
|
|
230
|
+
return this.buildResult('FAIL', expected, failComments, startMs, startTime, step_name);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const comments = this.appendMatchedLocatorInfo(expected, resolved.matchedIndex, resolved.matchedXPath);
|
|
234
|
+
return this.buildResult('PASS', expected, comments, startMs, startTime, step_name);
|
|
235
|
+
} catch (error: any) {
|
|
236
|
+
// const sc = await this.captureScreenshot(true);
|
|
237
|
+
return this.buildResult('FAIL', expected, this.classifyError(error), startMs, startTime, step_name);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ─── isEnabled ─────────────────────────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
async isEnabled(label: string, locators: string[], step_name: string = 'Check if radio button is enabled'): Promise<StepResult> {
|
|
244
|
+
const startMs = Date.now();
|
|
245
|
+
const startTime = this.getStartTime();
|
|
246
|
+
const expected = `'${label}' enabled state retrieved successfully`;
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
const resolved = await LocatorService.resolveElement(this.page, locators);
|
|
250
|
+
const enabled = await resolved.element.isEnabled();
|
|
251
|
+
|
|
252
|
+
const comments = this.appendMatchedLocatorInfo(
|
|
253
|
+
`'${label}' is ${enabled ? 'enabled' : 'disabled'}`,
|
|
254
|
+
resolved.matchedIndex,
|
|
255
|
+
resolved.matchedXPath
|
|
256
|
+
);
|
|
257
|
+
return this.buildResult('PASS', expected, comments, startMs, startTime, step_name);
|
|
258
|
+
} catch (error: any) {
|
|
259
|
+
// const sc = await this.captureScreenshot(true);
|
|
260
|
+
return this.buildResult('FAIL', expected, this.classifyError(error), startMs, startTime, step_name);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ─── isVisible ─────────────────────────────────────────────────────────────
|
|
265
|
+
|
|
266
|
+
async isVisible(label: string, locators: string[], step_name: string = 'Check if radio button is visible'): Promise<StepResult> {
|
|
267
|
+
const startMs = Date.now();
|
|
268
|
+
const startTime = this.getStartTime();
|
|
269
|
+
const expected = `'${label}' visibility state retrieved successfully`;
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
const resolved = await LocatorService.resolveElement(this.page, locators);
|
|
273
|
+
const visible = await resolved.element.isVisible();
|
|
274
|
+
|
|
275
|
+
const comments = this.appendMatchedLocatorInfo(
|
|
276
|
+
`'${label}' is ${visible ? 'visible' : 'not visible'}`,
|
|
277
|
+
resolved.matchedIndex,
|
|
278
|
+
resolved.matchedXPath
|
|
279
|
+
);
|
|
280
|
+
return this.buildResult('PASS', expected, comments, startMs, startTime, step_name);
|
|
281
|
+
} catch (error: any) {
|
|
282
|
+
// const sc = await this.captureScreenshot(true);
|
|
283
|
+
return this.buildResult('FAIL', expected, this.classifyError(error), startMs, startTime, step_name);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|