@flash-ai-team/flash-test-framework 0.0.1
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 +110 -0
- package/dist/core/Keyword.d.ts +19 -0
- package/dist/core/Keyword.js +95 -0
- package/dist/core/ObjectRepository.d.ts +12 -0
- package/dist/core/ObjectRepository.js +20 -0
- package/dist/core/TestBase.d.ts +5 -0
- package/dist/core/TestBase.js +15 -0
- package/dist/core/TestDecorators.d.ts +2 -0
- package/dist/core/TestDecorators.js +81 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +13 -0
- package/dist/keywords/AIWebUI.d.ts +20 -0
- package/dist/keywords/AIWebUI.js +276 -0
- package/dist/keywords/WebUI.d.ts +166 -0
- package/dist/keywords/WebUI.js +502 -0
- package/dist/objects/LoginPage.d.ts +6 -0
- package/dist/objects/LoginPage.js +11 -0
- package/dist/reporting/CustomReporter.d.ts +11 -0
- package/dist/reporting/CustomReporter.js +129 -0
- package/dist/reporting/EmailReporter.d.ts +8 -0
- package/dist/reporting/EmailReporter.js +169 -0
- package/dist/reporting/HtmlReporter.d.ts +11 -0
- package/dist/reporting/HtmlReporter.js +190 -0
- package/dist/reporting/ReporterUtils.d.ts +3 -0
- package/dist/reporting/ReporterUtils.js +64 -0
- package/package.json +63 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.Web = void 0;
|
|
16
|
+
const test_1 = require("@playwright/test");
|
|
17
|
+
const Keyword_1 = require("../core/Keyword");
|
|
18
|
+
const ObjectRepository_1 = require("../core/ObjectRepository");
|
|
19
|
+
const fs_1 = __importDefault(require("fs"));
|
|
20
|
+
class Web {
|
|
21
|
+
static get page() {
|
|
22
|
+
return Keyword_1.KeywordContext.page;
|
|
23
|
+
}
|
|
24
|
+
static getLocator(to) {
|
|
25
|
+
return this.page.locator(to.selector);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Navigates to the specified URL.
|
|
29
|
+
* @param url - The URL to navigate to.
|
|
30
|
+
*/
|
|
31
|
+
static async navigateToUrl(url) {
|
|
32
|
+
await this.page.goto(url);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Clicks on the specified element.
|
|
36
|
+
* @param to - The TestObject representing the element.
|
|
37
|
+
*/
|
|
38
|
+
static async click(to) {
|
|
39
|
+
await this.getLocator(to).click();
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Sets the text of an input element.
|
|
43
|
+
* @param to - The TestObject representing the input element.
|
|
44
|
+
* @param text - The text to set.
|
|
45
|
+
*/
|
|
46
|
+
static async setText(to, text) {
|
|
47
|
+
await this.getLocator(to).fill(text);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Searches for the specified text using heuristic selectors.
|
|
51
|
+
* @param text - The text to search for.
|
|
52
|
+
*/
|
|
53
|
+
static async search(text) {
|
|
54
|
+
// Common search input selectors
|
|
55
|
+
const selectors = [
|
|
56
|
+
'input[name="q"]',
|
|
57
|
+
'textarea[name="q"]',
|
|
58
|
+
'input[type="search"]',
|
|
59
|
+
'input[aria-label*="Search" i]',
|
|
60
|
+
'input[placeholder*="Search" i]',
|
|
61
|
+
'button[aria-label*="Search" i]' // sometimes search is a button that expands? No, sticking to inputs for now.
|
|
62
|
+
];
|
|
63
|
+
// Combine into a single OR selector
|
|
64
|
+
const combinedSelector = selectors.join(', ');
|
|
65
|
+
const locator = this.page.locator(combinedSelector).first();
|
|
66
|
+
await locator.fill(text);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Verifies that the specified element is present (visible) on the page.
|
|
70
|
+
* @param to - The TestObject representing the element.
|
|
71
|
+
* @param timeout - The maximum time to wait in milliseconds (default: 5000).
|
|
72
|
+
*/
|
|
73
|
+
static async verifyElementPresent(to, timeout = 5000) {
|
|
74
|
+
await (0, test_1.expect)(this.getLocator(to)).toBeVisible({ timeout });
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Gets the visible text of the specified element.
|
|
78
|
+
* @param to - The TestObject representing the element.
|
|
79
|
+
* @returns The text content of the element.
|
|
80
|
+
*/
|
|
81
|
+
static async getText(to) {
|
|
82
|
+
return await this.getLocator(to).innerText();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Closes the current browser context/page.
|
|
86
|
+
*/
|
|
87
|
+
static async closeBrowser() {
|
|
88
|
+
await this.page.close();
|
|
89
|
+
}
|
|
90
|
+
// --- Interaction Keywords ---
|
|
91
|
+
/**
|
|
92
|
+
* Double clicks on the specified element.
|
|
93
|
+
* @param to - The TestObject representing the element.
|
|
94
|
+
*/
|
|
95
|
+
static async doubleClick(to) {
|
|
96
|
+
await this.getLocator(to).dblclick();
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Right clicks (context click) on the specified element.
|
|
100
|
+
* @param to - The TestObject representing the element.
|
|
101
|
+
*/
|
|
102
|
+
static async rightClick(to) {
|
|
103
|
+
await this.getLocator(to).click({ button: 'right' });
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Hovers over the specified element.
|
|
107
|
+
* @param to - The TestObject representing the element.
|
|
108
|
+
*/
|
|
109
|
+
static async mouseOver(to) {
|
|
110
|
+
await this.getLocator(to).hover();
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Drags the source element and drops it onto the target element.
|
|
114
|
+
* @param source - The TestObject representing the source element.
|
|
115
|
+
* @param target - The TestObject representing the target element.
|
|
116
|
+
*/
|
|
117
|
+
static async dragAndDrop(source, target) {
|
|
118
|
+
await this.getLocator(source).dragTo(this.getLocator(target));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Checks (selects) the specified checkbox or radio button.
|
|
122
|
+
* @param to - The TestObject representing the element.
|
|
123
|
+
*/
|
|
124
|
+
static async check(to) {
|
|
125
|
+
await this.getLocator(to).check();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Unchecks (deselects) the specified checkbox.
|
|
129
|
+
* @param to - The TestObject representing the element.
|
|
130
|
+
*/
|
|
131
|
+
static async uncheck(to) {
|
|
132
|
+
await this.getLocator(to).uncheck();
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Selects an option by its value.
|
|
136
|
+
* @param to - The TestObject representing the dropdown.
|
|
137
|
+
* @param value - The value of the option to select.
|
|
138
|
+
*/
|
|
139
|
+
static async selectOptionByValue(to, value) {
|
|
140
|
+
await this.getLocator(to).selectOption({ value: value });
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Selects an option by its label (visible text).
|
|
144
|
+
* @param to - The TestObject representing the dropdown.
|
|
145
|
+
* @param label - The label of the option to select.
|
|
146
|
+
*/
|
|
147
|
+
static async selectOptionByLabel(to, label) {
|
|
148
|
+
await this.getLocator(to).selectOption({ label: label });
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Sends keys to the specified element (sequentially).
|
|
152
|
+
* @param to - The TestObject representing the element.
|
|
153
|
+
* @param key - The key(s) to send.
|
|
154
|
+
*/
|
|
155
|
+
static async sendKeys(to, key) {
|
|
156
|
+
await this.getLocator(to).pressSequentially(key);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Presses a specific key on the keyboard.
|
|
160
|
+
* @param key - The name of the key to press (e.g., "Enter", "Tab", "ArrowDown").
|
|
161
|
+
*/
|
|
162
|
+
static async pressKey(key) {
|
|
163
|
+
await this.page.keyboard.press(key);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Uploads a file to the specified file input element.
|
|
167
|
+
* @param to - The TestObject representing the file input.
|
|
168
|
+
* @param absolutePath - The absolute path to the file.
|
|
169
|
+
*/
|
|
170
|
+
static async uploadFile(to, absolutePath) {
|
|
171
|
+
await this.getLocator(to).setInputFiles(absolutePath);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Clicks on an image template found on the screen.
|
|
175
|
+
* @param imagePath - The absolute path to the template image.
|
|
176
|
+
*/
|
|
177
|
+
static async clickImage(imagePath) {
|
|
178
|
+
if (!fs_1.default.existsSync(imagePath)) {
|
|
179
|
+
throw new Error(`Image not found at: ${imagePath}`);
|
|
180
|
+
}
|
|
181
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
182
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
183
|
+
const { Jimp, intToRGBA } = require('jimp');
|
|
184
|
+
const screenshotBuffer = await this.page.screenshot();
|
|
185
|
+
const screenshot = await Jimp.read(screenshotBuffer);
|
|
186
|
+
const template = await Jimp.read(imagePath);
|
|
187
|
+
const sWidth = screenshot.bitmap.width;
|
|
188
|
+
const sHeight = screenshot.bitmap.height;
|
|
189
|
+
const tWidth = template.bitmap.width;
|
|
190
|
+
const tHeight = template.bitmap.height;
|
|
191
|
+
let bestMatch = { x: -1, y: -1, score: 1.0 }; // Lower score is better (0 is perfect)
|
|
192
|
+
// Simple scan (optimization: step 5 pixels? or partial check?)
|
|
193
|
+
// For robustness, full scan or slightly optimized.
|
|
194
|
+
// Let's do a naive scan but check center pixel of template first?
|
|
195
|
+
// Or just straightforward pixel check.
|
|
196
|
+
// Optimization: Checking every pixel is slow.
|
|
197
|
+
// Let's assume exact match for now. Jimp diff?
|
|
198
|
+
// Manual sliding window with threshold
|
|
199
|
+
const threshold = 0.1; // 10% tolerance per pixel
|
|
200
|
+
for (let x = 0; x <= sWidth - tWidth; x++) {
|
|
201
|
+
for (let y = 0; y <= sHeight - tHeight; y++) {
|
|
202
|
+
// Quick check: Top-left corner
|
|
203
|
+
const sColor = intToRGBA(screenshot.getPixelColor(x, y));
|
|
204
|
+
const tColor = intToRGBA(template.getPixelColor(0, 0));
|
|
205
|
+
if (Math.abs(sColor.r - tColor.r) > 255 * threshold ||
|
|
206
|
+
Math.abs(sColor.g - tColor.g) > 255 * threshold ||
|
|
207
|
+
Math.abs(sColor.b - tColor.b) > 255 * threshold) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
// Full check
|
|
211
|
+
let diff = 0;
|
|
212
|
+
let match = true;
|
|
213
|
+
for (let tx = 0; tx < tWidth; tx += 5) { // Skip pixels for speed
|
|
214
|
+
for (let ty = 0; ty < tHeight; ty += 5) {
|
|
215
|
+
const sc = intToRGBA(screenshot.getPixelColor(x + tx, y + ty));
|
|
216
|
+
const tc = intToRGBA(template.getPixelColor(tx, ty));
|
|
217
|
+
const localDiff = (Math.abs(sc.r - tc.r) + Math.abs(sc.g - tc.g) + Math.abs(sc.b - tc.b)) / (255 * 3);
|
|
218
|
+
if (localDiff > threshold) {
|
|
219
|
+
match = false;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
diff += localDiff;
|
|
223
|
+
}
|
|
224
|
+
if (!match)
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
if (match) {
|
|
228
|
+
// Found!
|
|
229
|
+
bestMatch = { x, y, score: diff };
|
|
230
|
+
// Click center
|
|
231
|
+
console.log(`Image match found at (${x}, ${y})`);
|
|
232
|
+
await this.page.mouse.click(x + tWidth / 2, y + tHeight / 2);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
throw new Error(`Image template not found on screen: ${imagePath}`);
|
|
238
|
+
}
|
|
239
|
+
// --- Verification Keywords ---
|
|
240
|
+
/**
|
|
241
|
+
* Verifies that the specified element is NOT present (hidden) on the page.
|
|
242
|
+
* @param to - The TestObject representing the element.
|
|
243
|
+
* @param timeout - The maximum time to wait in milliseconds (default: 5000).
|
|
244
|
+
*/
|
|
245
|
+
static async verifyElementNotPresent(to, timeout = 5000) {
|
|
246
|
+
await (0, test_1.expect)(this.getLocator(to)).toBeHidden({ timeout });
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Verifies that the element's text matches the expected text.
|
|
250
|
+
* @param to - The TestObject representing the element.
|
|
251
|
+
* @param expectedText - The expected text.
|
|
252
|
+
*/
|
|
253
|
+
static async verifyElementText(to, expectedText) {
|
|
254
|
+
await (0, test_1.expect)(this.getLocator(to)).toHaveText(expectedText);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Verifies that the element's attribute matches the expected value.
|
|
258
|
+
* @param to - The TestObject representing the element.
|
|
259
|
+
* @param attribute - The attribute name.
|
|
260
|
+
* @param expectedValue - The expected attribute value.
|
|
261
|
+
*/
|
|
262
|
+
static async verifyElementAttributeValue(to, attribute, expectedValue) {
|
|
263
|
+
await (0, test_1.expect)(this.getLocator(to)).toHaveAttribute(attribute, expectedValue);
|
|
264
|
+
}
|
|
265
|
+
// --- Wait Keywords ---
|
|
266
|
+
/**
|
|
267
|
+
* Waits for the specified element to be visible.
|
|
268
|
+
* @param to - The TestObject representing the element.
|
|
269
|
+
* @param timeout - The maximum time to wait in milliseconds (default: 5000).
|
|
270
|
+
*/
|
|
271
|
+
static async waitForElementVisible(to, timeout = 5000) {
|
|
272
|
+
await this.getLocator(to).waitFor({ state: 'visible', timeout });
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Waits for the specified element to be clickable.
|
|
276
|
+
* @param to - The TestObject representing the element.
|
|
277
|
+
* @param timeout - The maximum time to wait in milliseconds (default: 5000).
|
|
278
|
+
*/
|
|
279
|
+
static async waitForElementClickable(to, timeout = 5000) {
|
|
280
|
+
// Playwright auto-waits for clickability on action, but we can explicit wait just for state if needed.
|
|
281
|
+
// There isn't a direct 'clickable' state in waitFor, typically we wait for visible + enabled.
|
|
282
|
+
const locator = this.getLocator(to);
|
|
283
|
+
await locator.waitFor({ state: 'visible', timeout });
|
|
284
|
+
await (0, test_1.expect)(locator).toBeEnabled({ timeout });
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Waits for the specified element to be not visible (hidden).
|
|
288
|
+
* @param to - The TestObject representing the element.
|
|
289
|
+
* @param timeout - The maximum time to wait in milliseconds (default: 5000).
|
|
290
|
+
*/
|
|
291
|
+
static async waitForElementNotVisible(to, timeout = 5000) {
|
|
292
|
+
await this.getLocator(to).waitFor({ state: 'hidden', timeout });
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Delays execution for a specified number of seconds.
|
|
296
|
+
* @param seconds - The number of seconds to wait.
|
|
297
|
+
*/
|
|
298
|
+
static async delay(seconds) {
|
|
299
|
+
//need a simple delay
|
|
300
|
+
await new Promise(r => setTimeout(r, seconds * 1000));
|
|
301
|
+
}
|
|
302
|
+
// --- Utility Keywords ---
|
|
303
|
+
/**
|
|
304
|
+
* Takes a screenshot and saves it to the reports folder.
|
|
305
|
+
* @param filename - The name of the screenshot file (optional).
|
|
306
|
+
*/
|
|
307
|
+
static async takeScreenshot(filename) {
|
|
308
|
+
const path = filename ? `reports/screenshots/${filename}.png` : undefined;
|
|
309
|
+
await this.page.screenshot({ path, fullPage: true });
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Gets the value of the specified attribute.
|
|
313
|
+
* @param to - The TestObject representing the element.
|
|
314
|
+
* @param attribute - The name of the attribute.
|
|
315
|
+
* @returns The attribute value or null.
|
|
316
|
+
*/
|
|
317
|
+
static async getAttribute(to, attribute) {
|
|
318
|
+
return await this.getLocator(to).getAttribute(attribute);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Scrolls the element into view.
|
|
322
|
+
* @param to - The TestObject representing the element.
|
|
323
|
+
*/
|
|
324
|
+
static async scrollToElement(to) {
|
|
325
|
+
await this.getLocator(to).scrollIntoViewIfNeeded();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
exports.Web = Web;
|
|
329
|
+
__decorate([
|
|
330
|
+
(0, Keyword_1.Keyword)("Navigate To Url"),
|
|
331
|
+
__metadata("design:type", Function),
|
|
332
|
+
__metadata("design:paramtypes", [String]),
|
|
333
|
+
__metadata("design:returntype", Promise)
|
|
334
|
+
], Web, "navigateToUrl", null);
|
|
335
|
+
__decorate([
|
|
336
|
+
(0, Keyword_1.Keyword)("Click"),
|
|
337
|
+
__metadata("design:type", Function),
|
|
338
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject]),
|
|
339
|
+
__metadata("design:returntype", Promise)
|
|
340
|
+
], Web, "click", null);
|
|
341
|
+
__decorate([
|
|
342
|
+
(0, Keyword_1.Keyword)("Set Text"),
|
|
343
|
+
__metadata("design:type", Function),
|
|
344
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, String]),
|
|
345
|
+
__metadata("design:returntype", Promise)
|
|
346
|
+
], Web, "setText", null);
|
|
347
|
+
__decorate([
|
|
348
|
+
(0, Keyword_1.Keyword)("Search"),
|
|
349
|
+
__metadata("design:type", Function),
|
|
350
|
+
__metadata("design:paramtypes", [String]),
|
|
351
|
+
__metadata("design:returntype", Promise)
|
|
352
|
+
], Web, "search", null);
|
|
353
|
+
__decorate([
|
|
354
|
+
(0, Keyword_1.Keyword)("Verify Element Present"),
|
|
355
|
+
__metadata("design:type", Function),
|
|
356
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, Number]),
|
|
357
|
+
__metadata("design:returntype", Promise)
|
|
358
|
+
], Web, "verifyElementPresent", null);
|
|
359
|
+
__decorate([
|
|
360
|
+
(0, Keyword_1.Keyword)("Get Text"),
|
|
361
|
+
__metadata("design:type", Function),
|
|
362
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject]),
|
|
363
|
+
__metadata("design:returntype", Promise)
|
|
364
|
+
], Web, "getText", null);
|
|
365
|
+
__decorate([
|
|
366
|
+
(0, Keyword_1.Keyword)("Close Browser"),
|
|
367
|
+
__metadata("design:type", Function),
|
|
368
|
+
__metadata("design:paramtypes", []),
|
|
369
|
+
__metadata("design:returntype", Promise)
|
|
370
|
+
], Web, "closeBrowser", null);
|
|
371
|
+
__decorate([
|
|
372
|
+
(0, Keyword_1.Keyword)("Double Click"),
|
|
373
|
+
__metadata("design:type", Function),
|
|
374
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject]),
|
|
375
|
+
__metadata("design:returntype", Promise)
|
|
376
|
+
], Web, "doubleClick", null);
|
|
377
|
+
__decorate([
|
|
378
|
+
(0, Keyword_1.Keyword)("Right Click"),
|
|
379
|
+
__metadata("design:type", Function),
|
|
380
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject]),
|
|
381
|
+
__metadata("design:returntype", Promise)
|
|
382
|
+
], Web, "rightClick", null);
|
|
383
|
+
__decorate([
|
|
384
|
+
(0, Keyword_1.Keyword)("Mouse Over"),
|
|
385
|
+
__metadata("design:type", Function),
|
|
386
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject]),
|
|
387
|
+
__metadata("design:returntype", Promise)
|
|
388
|
+
], Web, "mouseOver", null);
|
|
389
|
+
__decorate([
|
|
390
|
+
(0, Keyword_1.Keyword)("Drag And Drop"),
|
|
391
|
+
__metadata("design:type", Function),
|
|
392
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, ObjectRepository_1.TestObject]),
|
|
393
|
+
__metadata("design:returntype", Promise)
|
|
394
|
+
], Web, "dragAndDrop", null);
|
|
395
|
+
__decorate([
|
|
396
|
+
(0, Keyword_1.Keyword)("Check"),
|
|
397
|
+
__metadata("design:type", Function),
|
|
398
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject]),
|
|
399
|
+
__metadata("design:returntype", Promise)
|
|
400
|
+
], Web, "check", null);
|
|
401
|
+
__decorate([
|
|
402
|
+
(0, Keyword_1.Keyword)("Uncheck"),
|
|
403
|
+
__metadata("design:type", Function),
|
|
404
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject]),
|
|
405
|
+
__metadata("design:returntype", Promise)
|
|
406
|
+
], Web, "uncheck", null);
|
|
407
|
+
__decorate([
|
|
408
|
+
(0, Keyword_1.Keyword)("Select Option By Value"),
|
|
409
|
+
__metadata("design:type", Function),
|
|
410
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, String]),
|
|
411
|
+
__metadata("design:returntype", Promise)
|
|
412
|
+
], Web, "selectOptionByValue", null);
|
|
413
|
+
__decorate([
|
|
414
|
+
(0, Keyword_1.Keyword)("Select Option By Label"),
|
|
415
|
+
__metadata("design:type", Function),
|
|
416
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, String]),
|
|
417
|
+
__metadata("design:returntype", Promise)
|
|
418
|
+
], Web, "selectOptionByLabel", null);
|
|
419
|
+
__decorate([
|
|
420
|
+
(0, Keyword_1.Keyword)("Send Keys"),
|
|
421
|
+
__metadata("design:type", Function),
|
|
422
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, String]),
|
|
423
|
+
__metadata("design:returntype", Promise)
|
|
424
|
+
], Web, "sendKeys", null);
|
|
425
|
+
__decorate([
|
|
426
|
+
(0, Keyword_1.Keyword)("Press Key"),
|
|
427
|
+
__metadata("design:type", Function),
|
|
428
|
+
__metadata("design:paramtypes", [String]),
|
|
429
|
+
__metadata("design:returntype", Promise)
|
|
430
|
+
], Web, "pressKey", null);
|
|
431
|
+
__decorate([
|
|
432
|
+
(0, Keyword_1.Keyword)("Upload File"),
|
|
433
|
+
__metadata("design:type", Function),
|
|
434
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, String]),
|
|
435
|
+
__metadata("design:returntype", Promise)
|
|
436
|
+
], Web, "uploadFile", null);
|
|
437
|
+
__decorate([
|
|
438
|
+
(0, Keyword_1.Keyword)("Click Image"),
|
|
439
|
+
__metadata("design:type", Function),
|
|
440
|
+
__metadata("design:paramtypes", [String]),
|
|
441
|
+
__metadata("design:returntype", Promise)
|
|
442
|
+
], Web, "clickImage", null);
|
|
443
|
+
__decorate([
|
|
444
|
+
(0, Keyword_1.Keyword)("Verify Element Not Present"),
|
|
445
|
+
__metadata("design:type", Function),
|
|
446
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, Number]),
|
|
447
|
+
__metadata("design:returntype", Promise)
|
|
448
|
+
], Web, "verifyElementNotPresent", null);
|
|
449
|
+
__decorate([
|
|
450
|
+
(0, Keyword_1.Keyword)("Verify Element Text"),
|
|
451
|
+
__metadata("design:type", Function),
|
|
452
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, String]),
|
|
453
|
+
__metadata("design:returntype", Promise)
|
|
454
|
+
], Web, "verifyElementText", null);
|
|
455
|
+
__decorate([
|
|
456
|
+
(0, Keyword_1.Keyword)("Verify Element Attribute Value"),
|
|
457
|
+
__metadata("design:type", Function),
|
|
458
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, String, String]),
|
|
459
|
+
__metadata("design:returntype", Promise)
|
|
460
|
+
], Web, "verifyElementAttributeValue", null);
|
|
461
|
+
__decorate([
|
|
462
|
+
(0, Keyword_1.Keyword)("Wait For Element Visible"),
|
|
463
|
+
__metadata("design:type", Function),
|
|
464
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, Number]),
|
|
465
|
+
__metadata("design:returntype", Promise)
|
|
466
|
+
], Web, "waitForElementVisible", null);
|
|
467
|
+
__decorate([
|
|
468
|
+
(0, Keyword_1.Keyword)("Wait For Element Clickable"),
|
|
469
|
+
__metadata("design:type", Function),
|
|
470
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, Number]),
|
|
471
|
+
__metadata("design:returntype", Promise)
|
|
472
|
+
], Web, "waitForElementClickable", null);
|
|
473
|
+
__decorate([
|
|
474
|
+
(0, Keyword_1.Keyword)("Wait For Element Not Visible"),
|
|
475
|
+
__metadata("design:type", Function),
|
|
476
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, Number]),
|
|
477
|
+
__metadata("design:returntype", Promise)
|
|
478
|
+
], Web, "waitForElementNotVisible", null);
|
|
479
|
+
__decorate([
|
|
480
|
+
(0, Keyword_1.Keyword)("Delay"),
|
|
481
|
+
__metadata("design:type", Function),
|
|
482
|
+
__metadata("design:paramtypes", [Number]),
|
|
483
|
+
__metadata("design:returntype", Promise)
|
|
484
|
+
], Web, "delay", null);
|
|
485
|
+
__decorate([
|
|
486
|
+
(0, Keyword_1.Keyword)("Take Screenshot"),
|
|
487
|
+
__metadata("design:type", Function),
|
|
488
|
+
__metadata("design:paramtypes", [String]),
|
|
489
|
+
__metadata("design:returntype", Promise)
|
|
490
|
+
], Web, "takeScreenshot", null);
|
|
491
|
+
__decorate([
|
|
492
|
+
(0, Keyword_1.Keyword)("Get Attribute"),
|
|
493
|
+
__metadata("design:type", Function),
|
|
494
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, String]),
|
|
495
|
+
__metadata("design:returntype", Promise)
|
|
496
|
+
], Web, "getAttribute", null);
|
|
497
|
+
__decorate([
|
|
498
|
+
(0, Keyword_1.Keyword)("Scroll To Element"),
|
|
499
|
+
__metadata("design:type", Function),
|
|
500
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject]),
|
|
501
|
+
__metadata("design:returntype", Promise)
|
|
502
|
+
], Web, "scrollToElement", null);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare class LoginPage {
|
|
2
|
+
static readonly username: import("../core/ObjectRepository").TestObject;
|
|
3
|
+
static readonly password: import("../core/ObjectRepository").TestObject;
|
|
4
|
+
static readonly loginBtn: import("../core/ObjectRepository").TestObject;
|
|
5
|
+
static readonly successMessage: import("../core/ObjectRepository").TestObject;
|
|
6
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoginPage = void 0;
|
|
4
|
+
const ObjectRepository_1 = require("../core/ObjectRepository");
|
|
5
|
+
class LoginPage {
|
|
6
|
+
}
|
|
7
|
+
exports.LoginPage = LoginPage;
|
|
8
|
+
LoginPage.username = (0, ObjectRepository_1.el)("#username", "Username input field");
|
|
9
|
+
LoginPage.password = (0, ObjectRepository_1.el)("#password", "Password input field");
|
|
10
|
+
LoginPage.loginBtn = (0, ObjectRepository_1.el)("button[type='submit']", "Login button");
|
|
11
|
+
LoginPage.successMessage = (0, ObjectRepository_1.el)(".flash.success", "Success message");
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Reporter, FullConfig, Suite, TestCase, TestResult, FullResult } from '@playwright/test/reporter';
|
|
2
|
+
declare class CustomReporter implements Reporter {
|
|
3
|
+
private results;
|
|
4
|
+
private logBuffer;
|
|
5
|
+
private reportFolder;
|
|
6
|
+
private log;
|
|
7
|
+
onBegin(config: FullConfig, suite: Suite): void;
|
|
8
|
+
onTestEnd(test: TestCase, result: TestResult): void;
|
|
9
|
+
onEnd(result: FullResult): void;
|
|
10
|
+
}
|
|
11
|
+
export default CustomReporter;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const ReporterUtils_1 = require("./ReporterUtils");
|
|
39
|
+
class CustomReporter {
|
|
40
|
+
constructor() {
|
|
41
|
+
this.results = [];
|
|
42
|
+
this.logBuffer = [];
|
|
43
|
+
this.reportFolder = '';
|
|
44
|
+
}
|
|
45
|
+
log(message) {
|
|
46
|
+
console.log(message);
|
|
47
|
+
this.logBuffer.push(message);
|
|
48
|
+
}
|
|
49
|
+
onBegin(config, suite) {
|
|
50
|
+
this.reportFolder = (0, ReporterUtils_1.getReportFolder)(suite);
|
|
51
|
+
this.log(`\n[DEBUG] process.cwd: ${process.cwd()}`);
|
|
52
|
+
this.log(`[DEBUG] Calculated reportFolder: ${this.reportFolder}`);
|
|
53
|
+
this.log(`\nStarting test run with ${suite.allTests().length} tests...\n`);
|
|
54
|
+
}
|
|
55
|
+
onTestEnd(test, result) {
|
|
56
|
+
// Check for SourceFile annotation
|
|
57
|
+
const sourceAnnotation = test.annotations.find(a => a.type === 'SourceFile');
|
|
58
|
+
const file = sourceAnnotation ? sourceAnnotation.description : test.location.file;
|
|
59
|
+
// Check for MethodName annotation
|
|
60
|
+
const methodAnnotation = test.annotations.find(a => a.type === 'MethodName');
|
|
61
|
+
const method = methodAnnotation ? methodAnnotation.description : '';
|
|
62
|
+
// Capture step details
|
|
63
|
+
const steps = result.steps
|
|
64
|
+
.filter(step => step.category === 'test.step')
|
|
65
|
+
.map(step => ({
|
|
66
|
+
title: step.title,
|
|
67
|
+
status: step.error ? 'failed' : 'passed',
|
|
68
|
+
duration: step.duration,
|
|
69
|
+
error: step.error ? step.error.message : undefined
|
|
70
|
+
}));
|
|
71
|
+
this.results.push({
|
|
72
|
+
title: test.title,
|
|
73
|
+
file: file,
|
|
74
|
+
method: method,
|
|
75
|
+
status: result.status,
|
|
76
|
+
duration: result.duration,
|
|
77
|
+
error: result.error,
|
|
78
|
+
steps: steps
|
|
79
|
+
});
|
|
80
|
+
// Build individual test log
|
|
81
|
+
const lines = [];
|
|
82
|
+
lines.push(`Test: ${test.title} [${result.status.toUpperCase()}] (${result.duration}ms)`);
|
|
83
|
+
for (const step of result.steps) {
|
|
84
|
+
if (step.category === 'test.step') {
|
|
85
|
+
lines.push(` -> ${step.title} (${step.duration}ms)`);
|
|
86
|
+
if (step.error) {
|
|
87
|
+
lines.push(` Error: ${step.error.message}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
lines.push(''); // Empty line
|
|
92
|
+
// Log to valid console/main buffer
|
|
93
|
+
lines.forEach(line => this.log(line));
|
|
94
|
+
// Write to individual file
|
|
95
|
+
if (this.reportFolder) {
|
|
96
|
+
// Ensure folder exists (it normally should be created in onEnd, but we are in onTestEnd now.
|
|
97
|
+
// We might need to create it here if it doesn't exist yet, or wait?
|
|
98
|
+
// Safest is to ensure it exists.
|
|
99
|
+
if (!fs.existsSync(this.reportFolder)) {
|
|
100
|
+
try {
|
|
101
|
+
fs.mkdirSync(this.reportFolder, { recursive: true });
|
|
102
|
+
}
|
|
103
|
+
catch (e) { /* ignore race */ }
|
|
104
|
+
}
|
|
105
|
+
const sanitizedTitle = test.title.replace(/[^a-z0-9]/gi, '_');
|
|
106
|
+
const individualLogPath = path.join(this.reportFolder, `execution_${sanitizedTitle}.log`);
|
|
107
|
+
try {
|
|
108
|
+
fs.writeFileSync(individualLogPath, lines.join('\n'));
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
console.error(`Failed to write log for ${test.title}`, e);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
onEnd(result) {
|
|
116
|
+
if (!this.reportFolder)
|
|
117
|
+
return;
|
|
118
|
+
const reportPath = path.join(this.reportFolder, 'custom-report.json');
|
|
119
|
+
const logPath = path.join(this.reportFolder, 'execution.log');
|
|
120
|
+
if (!fs.existsSync(this.reportFolder)) {
|
|
121
|
+
fs.mkdirSync(this.reportFolder, { recursive: true });
|
|
122
|
+
}
|
|
123
|
+
fs.writeFileSync(reportPath, JSON.stringify(this.results, null, 2));
|
|
124
|
+
this.log(`Custom JSON Report generated at: ${reportPath}`);
|
|
125
|
+
fs.writeFileSync(logPath, this.logBuffer.join('\n'));
|
|
126
|
+
console.log(`Execution Log generated at: ${logPath}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
exports.default = CustomReporter;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Reporter, FullResult, FullConfig, Suite } from '@playwright/test/reporter';
|
|
2
|
+
export default class EmailReporter implements Reporter {
|
|
3
|
+
private suiteStartTime;
|
|
4
|
+
private rootSuite;
|
|
5
|
+
onBegin(config: FullConfig, suite: Suite): void;
|
|
6
|
+
onEnd(result: FullResult): Promise<void>;
|
|
7
|
+
private zipDirectory;
|
|
8
|
+
}
|